= base {
+ n = 0
+ err = strconv.ErrSyntax
+ goto Error
+ }
+
+ if n >= cutoff {
+ // n*base overflows
+ n = 1<<64 - 1
+ err = strconv.ErrRange
+ goto Error
+ }
+ n *= uint64(base)
+
+ n1 := n + uint64(v)
+ if n1 < n || n1 > maxVal {
+ // n+v overflows
+ n = 1<<64 - 1
+ err = strconv.ErrRange
+ goto Error
+ }
+ n = n1
+ }
+
+ return n, nil
+
+Error:
+ return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
+}
+
+// Return the first number n such that n*base >= 1<<64.
+func cutoff64(base int) uint64 {
+ if base < 2 {
+ return 0
+ }
+ return (1<<64-1)/uint64(base) + 1
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/gotrack_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/gotrack_test.go
new file mode 100644
index 000000000..3ff4957e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/gotrack_test.go
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+)
+
+func TestGoroutineLock(t *testing.T) {
+ DebugGoroutines = true
+ g := newGoroutineLock()
+ g.check()
+
+ sawPanic := make(chan interface{})
+ go func() {
+ defer func() { sawPanic <- recover() }()
+ g.check() // should panic
+ }()
+ e := <-sawPanic
+ if e == nil {
+ t.Fatal("did not see panic from check in other goroutine")
+ }
+ if !strings.Contains(fmt.Sprint(e), "wrong goroutine") {
+ t.Errorf("expected on see panic about running on the wrong goroutine; got %v", e)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/Makefile
new file mode 100644
index 000000000..3a77cf073
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/Makefile
@@ -0,0 +1,5 @@
+h2demo.linux: h2demo.go
+ GOOS=linux go build --tags=h2demo -o h2demo.linux .
+
+upload: h2demo.linux
+ cat h2demo.linux | go run launch.go --write_object=http2-demo-server-tls/h2demo --write_object_is_public
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/README b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/README
new file mode 100644
index 000000000..212a96f38
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/README
@@ -0,0 +1,16 @@
+
+Client:
+ -- Firefox nightly with about:config network.http.spdy.enabled.http2draft set true
+ -- Chrome: go to chrome://flags/#enable-spdy4, save and restart (button at bottom)
+
+Make CA:
+$ openssl genrsa -out rootCA.key 2048
+$ openssl req -x509 -new -nodes -key rootCA.key -days 1024 -out rootCA.pem
+... install that to Firefox
+
+Make cert:
+$ openssl genrsa -out server.key 2048
+$ openssl req -new -key server.key -out server.csr
+$ openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 500
+
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/h2demo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/h2demo.go
new file mode 100644
index 000000000..801592401
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/h2demo.go
@@ -0,0 +1,426 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+// +build h2demo
+
+package main
+
+import (
+ "bytes"
+ "crypto/tls"
+ "flag"
+ "fmt"
+ "hash/crc32"
+ "image"
+ "image/jpeg"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "os/exec"
+ "path"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "camlistore.org/pkg/googlestorage"
+ "camlistore.org/pkg/singleflight"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2"
+)
+
+var (
+ openFirefox = flag.Bool("openff", false, "Open Firefox")
+ addr = flag.String("addr", "localhost:4430", "TLS address to listen on")
+ httpAddr = flag.String("httpaddr", "", "If non-empty, address to listen for regular HTTP on")
+ prod = flag.Bool("prod", false, "Whether to configure itself to be the production http2.golang.org server.")
+)
+
+func homeOldHTTP(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, `
+
+Go + HTTP/2
+Welcome to the Go language 's HTTP/2 demo & interop server.
+Unfortunately, you're not using HTTP/2 right now. To do so:
+
+ Use Firefox Nightly or go to about:config and enable "network.http.spdy.enabled.http2draft"
+ Use Google Chrome Canary and/or go to chrome://flags/#enable-spdy4 to Enable SPDY/4 (Chrome's name for HTTP/2)
+
+See code & instructions for connecting at https://github.com/bradfitz/http2 .
+
+`)
+}
+
+func home(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != "/" {
+ http.NotFound(w, r)
+ return
+ }
+ io.WriteString(w, `
+
+Go + HTTP/2
+
+Welcome to the Go language 's HTTP/2 demo & interop server.
+
+Congratulations, you're using HTTP/2 right now .
+
+This server exists for others in the HTTP/2 community to test their HTTP/2 client implementations and point out flaws in our server.
+
+ The code is currently at github.com/bradfitz/http2
+but will move to the Go standard library at some point in the future
+(enabled by default, without users needing to change their code).
+
+Contact info: bradfitz@golang.org , or file a bug .
+
+Handlers for testing
+
+ GET /reqinfo to dump the request + headers received
+ GET /clockstream streams the current time every second
+ GET /gophertiles to see a page with a bunch of images
+ GET /file/gopher.png for a small file (does If-Modified-Since, Content-Range, etc)
+ GET /file/go.src.tar.gz for a larger file (~10 MB)
+ GET /redirect to redirect back to / (this page)
+ GET /goroutines to see all active goroutines in this server
+ PUT something to /crc32 to get a count of number of bytes and its CRC-32
+
+
+`)
+}
+
+func reqInfoHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ fmt.Fprintf(w, "Method: %s\n", r.Method)
+ fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
+ fmt.Fprintf(w, "Host: %s\n", r.Host)
+ fmt.Fprintf(w, "RemoteAddr: %s\n", r.RemoteAddr)
+ fmt.Fprintf(w, "RequestURI: %q\n", r.RequestURI)
+ fmt.Fprintf(w, "URL: %#v\n", r.URL)
+ fmt.Fprintf(w, "Body.ContentLength: %d (-1 means unknown)\n", r.ContentLength)
+ fmt.Fprintf(w, "Close: %v (relevant for HTTP/1 only)\n", r.Close)
+ fmt.Fprintf(w, "TLS: %#v\n", r.TLS)
+ fmt.Fprintf(w, "\nHeaders:\n")
+ r.Header.Write(w)
+}
+
+func crcHandler(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "PUT" {
+ http.Error(w, "PUT required.", 400)
+ return
+ }
+ crc := crc32.NewIEEE()
+ n, err := io.Copy(crc, r.Body)
+ if err == nil {
+ w.Header().Set("Content-Type", "text/plain")
+ fmt.Fprintf(w, "bytes=%d, CRC32=%x", n, crc.Sum(nil))
+ }
+}
+
+var (
+ fsGrp singleflight.Group
+ fsMu sync.Mutex // guards fsCache
+ fsCache = map[string]http.Handler{}
+)
+
+// fileServer returns a file-serving handler that proxies URL.
+// It lazily fetches URL on the first access and caches its contents forever.
+func fileServer(url string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ hi, err := fsGrp.Do(url, func() (interface{}, error) {
+ fsMu.Lock()
+ if h, ok := fsCache[url]; ok {
+ fsMu.Unlock()
+ return h, nil
+ }
+ fsMu.Unlock()
+
+ res, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer res.Body.Close()
+ slurp, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ modTime := time.Now()
+ var h http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.ServeContent(w, r, path.Base(url), modTime, bytes.NewReader(slurp))
+ })
+ fsMu.Lock()
+ fsCache[url] = h
+ fsMu.Unlock()
+ return h, nil
+ })
+ if err != nil {
+ http.Error(w, err.Error(), 500)
+ return
+ }
+ hi.(http.Handler).ServeHTTP(w, r)
+ })
+}
+
+func clockStreamHandler(w http.ResponseWriter, r *http.Request) {
+ clientGone := w.(http.CloseNotifier).CloseNotify()
+ w.Header().Set("Content-Type", "text/plain")
+ ticker := time.NewTicker(1 * time.Second)
+ defer ticker.Stop()
+ fmt.Fprintf(w, "# ~1KB of junk to force browsers to start rendering immediately: \n")
+ io.WriteString(w, strings.Repeat("# xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", 13))
+
+ for {
+ fmt.Fprintf(w, "%v\n", time.Now())
+ w.(http.Flusher).Flush()
+ select {
+ case <-ticker.C:
+ case <-clientGone:
+ log.Printf("Client %v disconnected from the clock", r.RemoteAddr)
+ return
+ }
+ }
+}
+
+func registerHandlers() {
+ tiles := newGopherTilesHandler()
+
+ mux2 := http.NewServeMux()
+ http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+ if r.TLS == nil {
+ if r.URL.Path == "/gophertiles" {
+ tiles.ServeHTTP(w, r)
+ return
+ }
+ http.Redirect(w, r, "https://http2.golang.org/", http.StatusFound)
+ return
+ }
+ if r.ProtoMajor == 1 {
+ if r.URL.Path == "/reqinfo" {
+ reqInfoHandler(w, r)
+ return
+ }
+ homeOldHTTP(w, r)
+ return
+ }
+ mux2.ServeHTTP(w, r)
+ })
+ mux2.HandleFunc("/", home)
+ mux2.Handle("/file/gopher.png", fileServer("https://golang.org/doc/gopher/frontpage.png"))
+ mux2.Handle("/file/go.src.tar.gz", fileServer("https://storage.googleapis.com/golang/go1.4.1.src.tar.gz"))
+ mux2.HandleFunc("/reqinfo", reqInfoHandler)
+ mux2.HandleFunc("/crc32", crcHandler)
+ mux2.HandleFunc("/clockstream", clockStreamHandler)
+ mux2.Handle("/gophertiles", tiles)
+ mux2.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "/", http.StatusFound)
+ })
+ stripHomedir := regexp.MustCompile(`/(Users|home)/\w+`)
+ mux2.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ buf := make([]byte, 2<<20)
+ w.Write(stripHomedir.ReplaceAll(buf[:runtime.Stack(buf, true)], nil))
+ })
+}
+
+func newGopherTilesHandler() http.Handler {
+ const gopherURL = "https://blog.golang.org/go-programming-language-turns-two_gophers.jpg"
+ res, err := http.Get(gopherURL)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if res.StatusCode != 200 {
+ log.Fatalf("Error fetching %s: %v", gopherURL, res.Status)
+ }
+ slurp, err := ioutil.ReadAll(res.Body)
+ res.Body.Close()
+ if err != nil {
+ log.Fatal(err)
+ }
+ im, err := jpeg.Decode(bytes.NewReader(slurp))
+ if err != nil {
+ if len(slurp) > 1024 {
+ slurp = slurp[:1024]
+ }
+ log.Fatalf("Failed to decode gopher image: %v (got %q)", err, slurp)
+ }
+
+ type subImager interface {
+ SubImage(image.Rectangle) image.Image
+ }
+ const tileSize = 32
+ xt := im.Bounds().Max.X / tileSize
+ yt := im.Bounds().Max.Y / tileSize
+ var tile [][][]byte // y -> x -> jpeg bytes
+ for yi := 0; yi < yt; yi++ {
+ var row [][]byte
+ for xi := 0; xi < xt; xi++ {
+ si := im.(subImager).SubImage(image.Rectangle{
+ Min: image.Point{xi * tileSize, yi * tileSize},
+ Max: image.Point{(xi + 1) * tileSize, (yi + 1) * tileSize},
+ })
+ buf := new(bytes.Buffer)
+ if err := jpeg.Encode(buf, si, &jpeg.Options{Quality: 90}); err != nil {
+ log.Fatal(err)
+ }
+ row = append(row, buf.Bytes())
+ }
+ tile = append(tile, row)
+ }
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ ms, _ := strconv.Atoi(r.FormValue("latency"))
+ const nanosPerMilli = 1e6
+ if r.FormValue("x") != "" {
+ x, _ := strconv.Atoi(r.FormValue("x"))
+ y, _ := strconv.Atoi(r.FormValue("y"))
+ if ms <= 1000 {
+ time.Sleep(time.Duration(ms) * nanosPerMilli)
+ }
+ if x >= 0 && x < xt && y >= 0 && y < yt {
+ http.ServeContent(w, r, "", time.Time{}, bytes.NewReader(tile[y][x]))
+ return
+ }
+ }
+ io.WriteString(w, "")
+ fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:", xt*yt)
+ for _, ms := range []int{0, 30, 200, 1000} {
+ d := time.Duration(ms) * nanosPerMilli
+ fmt.Fprintf(w, "[HTTP/2, %v latency ] [HTTP/1, %v latency ] \n",
+ httpsHost(), ms, d,
+ httpHost(), ms, d,
+ )
+ }
+ io.WriteString(w, "
\n")
+ cacheBust := time.Now().UnixNano()
+ for y := 0; y < yt; y++ {
+ for x := 0; x < xt; x++ {
+ fmt.Fprintf(w, " ",
+ tileSize, tileSize, x, y, cacheBust, ms)
+ }
+ io.WriteString(w, " \n")
+ }
+ io.WriteString(w, "
<< Back to Go HTTP/2 demo server ")
+ })
+}
+
+func httpsHost() string {
+ if *prod {
+ return "http2.golang.org"
+ }
+ if v := *addr; strings.HasPrefix(v, ":") {
+ return "localhost" + v
+ } else {
+ return v
+ }
+}
+
+func httpHost() string {
+ if *prod {
+ return "http2.golang.org"
+ }
+ if v := *httpAddr; strings.HasPrefix(v, ":") {
+ return "localhost" + v
+ } else {
+ return v
+ }
+}
+
+func serveProdTLS() error {
+ c, err := googlestorage.NewServiceClient()
+ if err != nil {
+ return err
+ }
+ slurp := func(key string) ([]byte, error) {
+ const bucket = "http2-demo-server-tls"
+ rc, _, err := c.GetObject(&googlestorage.Object{
+ Bucket: bucket,
+ Key: key,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("Error fetching GCS object %q in bucket %q: %v", key, bucket, err)
+ }
+ defer rc.Close()
+ return ioutil.ReadAll(rc)
+ }
+ certPem, err := slurp("http2.golang.org.chained.pem")
+ if err != nil {
+ return err
+ }
+ keyPem, err := slurp("http2.golang.org.key")
+ if err != nil {
+ return err
+ }
+ cert, err := tls.X509KeyPair(certPem, keyPem)
+ if err != nil {
+ return err
+ }
+ srv := &http.Server{
+ TLSConfig: &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ },
+ }
+ http2.ConfigureServer(srv, &http2.Server{})
+ ln, err := net.Listen("tcp", ":443")
+ if err != nil {
+ return err
+ }
+ return srv.Serve(tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, srv.TLSConfig))
+}
+
+type tcpKeepAliveListener struct {
+ *net.TCPListener
+}
+
+func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
+ tc, err := ln.AcceptTCP()
+ if err != nil {
+ return
+ }
+ tc.SetKeepAlive(true)
+ tc.SetKeepAlivePeriod(3 * time.Minute)
+ return tc, nil
+}
+
+func serveProd() error {
+ errc := make(chan error, 2)
+ go func() { errc <- http.ListenAndServe(":80", nil) }()
+ go func() { errc <- serveProdTLS() }()
+ return <-errc
+}
+
+func main() {
+ var srv http.Server
+ flag.BoolVar(&http2.VerboseLogs, "verbose", false, "Verbose HTTP/2 debugging.")
+ flag.Parse()
+ srv.Addr = *addr
+
+ registerHandlers()
+
+ if *prod {
+ *httpAddr = "http2.golang.org"
+ log.Fatal(serveProd())
+ }
+
+ url := "https://" + *addr + "/"
+ log.Printf("Listening on " + url)
+ http2.ConfigureServer(&srv, &http2.Server{})
+
+ if *httpAddr != "" {
+ go func() { log.Fatal(http.ListenAndServe(*httpAddr, nil)) }()
+ }
+
+ go func() {
+ log.Fatal(srv.ListenAndServeTLS("server.crt", "server.key"))
+ }()
+ if *openFirefox && runtime.GOOS == "darwin" {
+ time.Sleep(250 * time.Millisecond)
+ exec.Command("open", "-b", "org.mozilla.nightly", "https://localhost:4430/").Run()
+ }
+ select {}
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/launch.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/launch.go
new file mode 100644
index 000000000..2eb709bdd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/launch.go
@@ -0,0 +1,279 @@
+// Copyright 2014 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 ignore
+
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+ "strings"
+ "time"
+
+ "code.google.com/p/goauth2/oauth"
+ compute "code.google.com/p/google-api-go-client/compute/v1"
+)
+
+var (
+ proj = flag.String("project", "symbolic-datum-552", "name of Project")
+ zone = flag.String("zone", "us-central1-a", "GCE zone")
+ mach = flag.String("machinetype", "n1-standard-1", "Machine type")
+ instName = flag.String("instance_name", "http2-demo", "Name of VM instance.")
+ sshPub = flag.String("ssh_public_key", "", "ssh public key file to authorize. Can modify later in Google's web UI anyway.")
+ staticIP = flag.String("static_ip", "130.211.116.44", "Static IP to use. If empty, automatic.")
+
+ writeObject = flag.String("write_object", "", "If non-empty, a VM isn't created and the flag value is Google Cloud Storage bucket/object to write. The contents from stdin.")
+ publicObject = flag.Bool("write_object_is_public", false, "Whether the object created by --write_object should be public.")
+)
+
+func readFile(v string) string {
+ slurp, err := ioutil.ReadFile(v)
+ if err != nil {
+ log.Fatalf("Error reading %s: %v", v, err)
+ }
+ return strings.TrimSpace(string(slurp))
+}
+
+var config = &oauth.Config{
+ // The client-id and secret should be for an "Installed Application" when using
+ // the CLI. Later we'll use a web application with a callback.
+ ClientId: readFile("client-id.dat"),
+ ClientSecret: readFile("client-secret.dat"),
+ Scope: strings.Join([]string{
+ compute.DevstorageFull_controlScope,
+ compute.ComputeScope,
+ "https://www.googleapis.com/auth/sqlservice",
+ "https://www.googleapis.com/auth/sqlservice.admin",
+ }, " "),
+ AuthURL: "https://accounts.google.com/o/oauth2/auth",
+ TokenURL: "https://accounts.google.com/o/oauth2/token",
+ RedirectURL: "urn:ietf:wg:oauth:2.0:oob",
+}
+
+const baseConfig = `#cloud-config
+coreos:
+ units:
+ - name: h2demo.service
+ command: start
+ content: |
+ [Unit]
+ Description=HTTP2 Demo
+
+ [Service]
+ ExecStartPre=/bin/bash -c 'mkdir -p /opt/bin && curl -s -o /opt/bin/h2demo http://storage.googleapis.com/http2-demo-server-tls/h2demo && chmod +x /opt/bin/h2demo'
+ ExecStart=/opt/bin/h2demo --prod
+ RestartSec=5s
+ Restart=always
+ Type=simple
+
+ [Install]
+ WantedBy=multi-user.target
+`
+
+func main() {
+ flag.Parse()
+ if *proj == "" {
+ log.Fatalf("Missing --project flag")
+ }
+ prefix := "https://www.googleapis.com/compute/v1/projects/" + *proj
+ machType := prefix + "/zones/" + *zone + "/machineTypes/" + *mach
+
+ tr := &oauth.Transport{
+ Config: config,
+ }
+
+ tokenCache := oauth.CacheFile("token.dat")
+ token, err := tokenCache.Token()
+ if err != nil {
+ if *writeObject != "" {
+ log.Fatalf("Can't use --write_object without a valid token.dat file already cached.")
+ }
+ log.Printf("Error getting token from %s: %v", string(tokenCache), err)
+ log.Printf("Get auth code from %v", config.AuthCodeURL("my-state"))
+ fmt.Print("\nEnter auth code: ")
+ sc := bufio.NewScanner(os.Stdin)
+ sc.Scan()
+ authCode := strings.TrimSpace(sc.Text())
+ token, err = tr.Exchange(authCode)
+ if err != nil {
+ log.Fatalf("Error exchanging auth code for a token: %v", err)
+ }
+ tokenCache.PutToken(token)
+ }
+
+ tr.Token = token
+ oauthClient := &http.Client{Transport: tr}
+ if *writeObject != "" {
+ writeCloudStorageObject(oauthClient)
+ return
+ }
+
+ computeService, _ := compute.New(oauthClient)
+
+ natIP := *staticIP
+ if natIP == "" {
+ // Try to find it by name.
+ aggAddrList, err := computeService.Addresses.AggregatedList(*proj).Do()
+ if err != nil {
+ log.Fatal(err)
+ }
+ // http://godoc.org/code.google.com/p/google-api-go-client/compute/v1#AddressAggregatedList
+ IPLoop:
+ for _, asl := range aggAddrList.Items {
+ for _, addr := range asl.Addresses {
+ if addr.Name == *instName+"-ip" && addr.Status == "RESERVED" {
+ natIP = addr.Address
+ break IPLoop
+ }
+ }
+ }
+ }
+
+ cloudConfig := baseConfig
+ if *sshPub != "" {
+ key := strings.TrimSpace(readFile(*sshPub))
+ cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n - %s\n", key)
+ }
+ if os.Getenv("USER") == "bradfitz" {
+ cloudConfig += fmt.Sprintf("\nssh_authorized_keys:\n - %s\n", "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAwks9dwWKlRC+73gRbvYtVg0vdCwDSuIlyt4z6xa/YU/jTDynM4R4W10hm2tPjy8iR1k8XhDv4/qdxe6m07NjG/By1tkmGpm1mGwho4Pr5kbAAy/Qg+NLCSdAYnnE00FQEcFOC15GFVMOW2AzDGKisReohwH9eIzHPzdYQNPRWXE= bradfitz@papag.bradfitz.com")
+ }
+ const maxCloudConfig = 32 << 10 // per compute API docs
+ if len(cloudConfig) > maxCloudConfig {
+ log.Fatalf("cloud config length of %d bytes is over %d byte limit", len(cloudConfig), maxCloudConfig)
+ }
+
+ instance := &compute.Instance{
+ Name: *instName,
+ Description: "Go Builder",
+ MachineType: machType,
+ Disks: []*compute.AttachedDisk{instanceDisk(computeService)},
+ Tags: &compute.Tags{
+ Items: []string{"http-server", "https-server"},
+ },
+ Metadata: &compute.Metadata{
+ Items: []*compute.MetadataItems{
+ {
+ Key: "user-data",
+ Value: cloudConfig,
+ },
+ },
+ },
+ NetworkInterfaces: []*compute.NetworkInterface{
+ &compute.NetworkInterface{
+ AccessConfigs: []*compute.AccessConfig{
+ &compute.AccessConfig{
+ Type: "ONE_TO_ONE_NAT",
+ Name: "External NAT",
+ NatIP: natIP,
+ },
+ },
+ Network: prefix + "/global/networks/default",
+ },
+ },
+ ServiceAccounts: []*compute.ServiceAccount{
+ {
+ Email: "default",
+ Scopes: []string{
+ compute.DevstorageFull_controlScope,
+ compute.ComputeScope,
+ },
+ },
+ },
+ }
+
+ log.Printf("Creating instance...")
+ op, err := computeService.Instances.Insert(*proj, *zone, instance).Do()
+ if err != nil {
+ log.Fatalf("Failed to create instance: %v", err)
+ }
+ opName := op.Name
+ log.Printf("Created. Waiting on operation %v", opName)
+OpLoop:
+ for {
+ time.Sleep(2 * time.Second)
+ op, err := computeService.ZoneOperations.Get(*proj, *zone, opName).Do()
+ if err != nil {
+ log.Fatalf("Failed to get op %s: %v", opName, err)
+ }
+ switch op.Status {
+ case "PENDING", "RUNNING":
+ log.Printf("Waiting on operation %v", opName)
+ continue
+ case "DONE":
+ if op.Error != nil {
+ for _, operr := range op.Error.Errors {
+ log.Printf("Error: %+v", operr)
+ }
+ log.Fatalf("Failed to start.")
+ }
+ log.Printf("Success. %+v", op)
+ break OpLoop
+ default:
+ log.Fatalf("Unknown status %q: %+v", op.Status, op)
+ }
+ }
+
+ inst, err := computeService.Instances.Get(*proj, *zone, *instName).Do()
+ if err != nil {
+ log.Fatalf("Error getting instance after creation: %v", err)
+ }
+ ij, _ := json.MarshalIndent(inst, "", " ")
+ log.Printf("Instance: %s", ij)
+}
+
+func instanceDisk(svc *compute.Service) *compute.AttachedDisk {
+ const imageURL = "https://www.googleapis.com/compute/v1/projects/coreos-cloud/global/images/coreos-stable-444-5-0-v20141016"
+ diskName := *instName + "-disk"
+
+ return &compute.AttachedDisk{
+ AutoDelete: true,
+ Boot: true,
+ Type: "PERSISTENT",
+ InitializeParams: &compute.AttachedDiskInitializeParams{
+ DiskName: diskName,
+ SourceImage: imageURL,
+ DiskSizeGb: 50,
+ },
+ }
+}
+
+func writeCloudStorageObject(httpClient *http.Client) {
+ content := os.Stdin
+ const maxSlurp = 1 << 20
+ var buf bytes.Buffer
+ n, err := io.CopyN(&buf, content, maxSlurp)
+ if err != nil && err != io.EOF {
+ log.Fatalf("Error reading from stdin: %v, %v", n, err)
+ }
+ contentType := http.DetectContentType(buf.Bytes())
+
+ req, err := http.NewRequest("PUT", "https://storage.googleapis.com/"+*writeObject, io.MultiReader(&buf, content))
+ if err != nil {
+ log.Fatal(err)
+ }
+ req.Header.Set("x-goog-api-version", "2")
+ if *publicObject {
+ req.Header.Set("x-goog-acl", "public-read")
+ }
+ req.Header.Set("Content-Type", contentType)
+ res, err := httpClient.Do(req)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if res.StatusCode != 200 {
+ res.Write(os.Stderr)
+ log.Fatalf("Failed.")
+ }
+ log.Printf("Success.")
+ os.Exit(0)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.key
new file mode 100644
index 000000000..a15a6abaf
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAt5fAjp4fTcekWUTfzsp0kyih1OYbsGL0KX1eRbSSR8Od0+9Q
+62Hyny+GFwMTb4A/KU8mssoHvcceSAAbwfbxFK/+s51TobqUnORZrOoTZjkUygby
+XDSK99YBbcR1Pip8vwMTm4XKuLtCigeBBdjjAQdgUO28LENGlsMnmeYkJfODVGnV
+mr5Ltb9ANA8IKyTfsnHJ4iOCS/PlPbUj2q7YnoVLposUBMlgUb/CykX3mOoLb4yJ
+JQyA/iST6ZxiIEj36D4yWZ5lg7YJl+UiiBQHGCnPdGyipqV06ex0heYWcaiW8LWZ
+SUQ93jQ+WVCH8hT7DQO1dmsvUmXlq/JeAlwQ/QIDAQABAoIBAFFHV7JMAqPWnMYA
+nezY6J81v9+XN+7xABNWM2Q8uv4WdksbigGLTXR3/680Z2hXqJ7LMeC5XJACFT/e
+/Gr0vmpgOCygnCPfjGehGKpavtfksXV3edikUlnCXsOP1C//c1bFL+sMYmFCVgTx
+qYdDK8yKzXNGrKYT6q5YG7IglyRNV1rsQa8lM/5taFYiD1Ck/3tQi3YIq8Lcuser
+hrxsMABcQ6mi+EIvG6Xr4mfJug0dGJMHG4RG1UGFQn6RXrQq2+q53fC8ZbVUSi0j
+NQ918aKFzktwv+DouKU0ME4I9toks03gM860bAL7zCbKGmwR3hfgX/TqzVCWpG9E
+LDVfvekCgYEA8fk9N53jbBRmULUGEf4qWypcLGiZnNU0OeXWpbPV9aa3H0VDytA7
+8fCN2dPAVDPqlthMDdVe983NCNwp2Yo8ZimDgowyIAKhdC25s1kejuaiH9OAPj3c
+0f8KbriYX4n8zNHxFwK6Ae3pQ6EqOLJVCUsziUaZX9nyKY5aZlyX6xcCgYEAwjws
+K62PjC64U5wYddNLp+kNdJ4edx+a7qBb3mEgPvSFT2RO3/xafJyG8kQB30Mfstjd
+bRxyUV6N0vtX1zA7VQtRUAvfGCecpMo+VQZzcHXKzoRTnQ7eZg4Lmj5fQ9tOAKAo
+QCVBoSW/DI4PZL26CAMDcAba4Pa22ooLapoRIQsCgYA6pIfkkbxLNkpxpt2YwLtt
+Kr/590O7UaR9n6k8sW/aQBRDXNsILR1KDl2ifAIxpf9lnXgZJiwE7HiTfCAcW7c1
+nzwDCI0hWuHcMTS/NYsFYPnLsstyyjVZI3FY0h4DkYKV9Q9z3zJLQ2hz/nwoD3gy
+b2pHC7giFcTts1VPV4Nt8wKBgHeFn4ihHJweg76vZz3Z78w7VNRWGFklUalVdDK7
+gaQ7w2y/ROn/146mo0OhJaXFIFRlrpvdzVrU3GDf2YXJYDlM5ZRkObwbZADjksev
+WInzcgDy3KDg7WnPasRXbTfMU4t/AkW2p1QKbi3DnSVYuokDkbH2Beo45vxDxhKr
+C69RAoGBAIyo3+OJenoZmoNzNJl2WPW5MeBUzSh8T/bgyjFTdqFHF5WiYRD/lfHj
+x9Glyw2nutuT4hlOqHvKhgTYdDMsF2oQ72fe3v8Q5FU7FuKndNPEAyvKNXZaShVA
+hnlhv5DjXKb0wFWnt5PCCiQLtzG0yyHaITrrEme7FikkIcTxaX/Y
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.pem
new file mode 100644
index 000000000..3a323e774
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.pem
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE-----
+MIIEWjCCA0KgAwIBAgIJALfRlWsI8YQHMA0GCSqGSIb3DQEBBQUAMHsxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEUMBIG
+A1UEChMLQnJhZGZpdHppbmMxEjAQBgNVBAMTCWxvY2FsaG9zdDEdMBsGCSqGSIb3
+DQEJARYOYnJhZEBkYW5nYS5jb20wHhcNMTQwNzE1MjA0NjA1WhcNMTcwNTA0MjA0
+NjA1WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBG
+cmFuY2lzY28xFDASBgNVBAoTC0JyYWRmaXR6aW5jMRIwEAYDVQQDEwlsb2NhbGhv
+c3QxHTAbBgkqhkiG9w0BCQEWDmJyYWRAZGFuZ2EuY29tMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAt5fAjp4fTcekWUTfzsp0kyih1OYbsGL0KX1eRbSS
+R8Od0+9Q62Hyny+GFwMTb4A/KU8mssoHvcceSAAbwfbxFK/+s51TobqUnORZrOoT
+ZjkUygbyXDSK99YBbcR1Pip8vwMTm4XKuLtCigeBBdjjAQdgUO28LENGlsMnmeYk
+JfODVGnVmr5Ltb9ANA8IKyTfsnHJ4iOCS/PlPbUj2q7YnoVLposUBMlgUb/CykX3
+mOoLb4yJJQyA/iST6ZxiIEj36D4yWZ5lg7YJl+UiiBQHGCnPdGyipqV06ex0heYW
+caiW8LWZSUQ93jQ+WVCH8hT7DQO1dmsvUmXlq/JeAlwQ/QIDAQABo4HgMIHdMB0G
+A1UdDgQWBBRcAROthS4P4U7vTfjByC569R7E6DCBrQYDVR0jBIGlMIGigBRcAROt
+hS4P4U7vTfjByC569R7E6KF/pH0wezELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNB
+MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRQwEgYDVQQKEwtCcmFkZml0emluYzES
+MBAGA1UEAxMJbG9jYWxob3N0MR0wGwYJKoZIhvcNAQkBFg5icmFkQGRhbmdhLmNv
+bYIJALfRlWsI8YQHMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAG6h
+U9f9sNH0/6oBbGGy2EVU0UgITUQIrFWo9rFkrW5k/XkDjQm+3lzjT0iGR4IxE/Ao
+eU6sQhua7wrWeFEn47GL98lnCsJdD7oZNhFmQ95Tb/LnDUjs5Yj9brP0NWzXfYU4
+UK2ZnINJRcJpB8iRCaCxE8DdcUF0XqIEq6pA272snoLmiXLMvNl3kYEdm+je6voD
+58SNVEUsztzQyXmJEhCpwVI0A6QCjzXj+qvpmw3ZZHi8JwXei8ZZBLTSFBki8Z7n
+sH9BBH38/SzUmAN4QHSPy1gjqm00OAE8NaYDkh/bzE4d7mLGGMWp/WE3KPSu82HF
+kPe6XoSbiLm/kxk32T0=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.srl b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.srl
new file mode 100644
index 000000000..6db389188
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/rootCA.srl
@@ -0,0 +1 @@
+E2CE26BF3285059C
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/server.crt b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/server.crt
new file mode 100644
index 000000000..c59059bd6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/server.crt
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDPjCCAiYCCQDizia/MoUFnDANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJV
+UzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xFDASBgNVBAoT
+C0JyYWRmaXR6aW5jMRIwEAYDVQQDEwlsb2NhbGhvc3QxHTAbBgkqhkiG9w0BCQEW
+DmJyYWRAZGFuZ2EuY29tMB4XDTE0MDcxNTIwNTAyN1oXDTE1MTEyNzIwNTAyN1ow
+RzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMQswCQYDVQQHEwJTRjEeMBwGA1UE
+ChMVYnJhZGZpdHogaHR0cDIgc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAs1Y9CyLFrdL8VQWN1WaifDqaZFnoqjHhCMlc1TfG2zA+InDifx2l
+gZD3o8FeNnAcfM2sPlk3+ZleOYw9P/CklFVDlvqmpCv9ss/BEp/dDaWvy1LmJ4c2
+dbQJfmTxn7CV1H3TsVJvKdwFmdoABb41NoBp6+NNO7OtDyhbIMiCI0pL3Nefb3HL
+A7hIMo3DYbORTtJLTIH9W8YKrEWL0lwHLrYFx/UdutZnv+HjdmO6vCN4na55mjws
+/vjKQUmc7xeY7Xe20xDEG2oDKVkL2eD7FfyrYMS3rO1ExP2KSqlXYG/1S9I/fz88
+F0GK7HX55b5WjZCl2J3ERVdnv/0MQv+sYQIDAQABMA0GCSqGSIb3DQEBBQUAA4IB
+AQC0zL+n/YpRZOdulSu9tS8FxrstXqGWoxfe+vIUgqfMZ5+0MkjJ/vW0FqlLDl2R
+rn4XaR3e7FmWkwdDVbq/UB6lPmoAaFkCgh9/5oapMaclNVNnfF3fjCJfRr+qj/iD
+EmJStTIN0ZuUjAlpiACmfnpEU55PafT5Zx+i1yE4FGjw8bJpFoyD4Hnm54nGjX19
+KeCuvcYFUPnBm3lcL0FalF2AjqV02WTHYNQk7YF/oeO7NKBoEgvGvKG3x+xaOeBI
+dwvdq175ZsGul30h+QjrRlXhH/twcuaT3GSdoysDl9cCYE8f1Mk8PD6gan3uBCJU
+90p6/CbU71bGbfpM2PHot2fm
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/server.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/server.key
new file mode 100644
index 000000000..f329c1421
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/h2demo/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAs1Y9CyLFrdL8VQWN1WaifDqaZFnoqjHhCMlc1TfG2zA+InDi
+fx2lgZD3o8FeNnAcfM2sPlk3+ZleOYw9P/CklFVDlvqmpCv9ss/BEp/dDaWvy1Lm
+J4c2dbQJfmTxn7CV1H3TsVJvKdwFmdoABb41NoBp6+NNO7OtDyhbIMiCI0pL3Nef
+b3HLA7hIMo3DYbORTtJLTIH9W8YKrEWL0lwHLrYFx/UdutZnv+HjdmO6vCN4na55
+mjws/vjKQUmc7xeY7Xe20xDEG2oDKVkL2eD7FfyrYMS3rO1ExP2KSqlXYG/1S9I/
+fz88F0GK7HX55b5WjZCl2J3ERVdnv/0MQv+sYQIDAQABAoIBADQ2spUwbY+bcz4p
+3M66ECrNQTBggP40gYl2XyHxGGOu2xhZ94f9ELf1hjRWU2DUKWco1rJcdZClV6q3
+qwmXvcM2Q/SMS8JW0ImkNVl/0/NqPxGatEnj8zY30d/L8hGFb0orzFu/XYA5gCP4
+NbN2WrXgk3ZLeqwcNxHHtSiJWGJ/fPyeDWAu/apy75u9Xf2GlzBZmV6HYD9EfK80
+LTlI60f5FO487CrJnboL7ovPJrIHn+k05xRQqwma4orpz932rTXnTjs9Lg6KtbQN
+a7PrqfAntIISgr11a66Mng3IYH1lYqJsWJJwX/xHT4WLEy0EH4/0+PfYemJekz2+
+Co62drECgYEA6O9zVJZXrLSDsIi54cfxA7nEZWm5CAtkYWeAHa4EJ+IlZ7gIf9sL
+W8oFcEfFGpvwVqWZ+AsQ70dsjXAv3zXaG0tmg9FtqWp7pzRSMPidifZcQwWkKeTO
+gJnFmnVyed8h6GfjTEu4gxo1/S5U0V+mYSha01z5NTnN6ltKx1Or3b0CgYEAxRgm
+S30nZxnyg/V7ys61AZhst1DG2tkZXEMcA7dYhabMoXPJAP/EfhlWwpWYYUs/u0gS
+Wwmf5IivX5TlYScgmkvb/NYz0u4ZmOXkLTnLPtdKKFXhjXJcHjUP67jYmOxNlJLp
+V4vLRnFxTpffAV+OszzRxsXX6fvruwZBANYJeXUCgYBVouLFsFgfWGYp2rpr9XP4
+KK25kvrBqF6JKOIDB1zjxNJ3pUMKrl8oqccCFoCyXa4oTM2kUX0yWxHfleUjrMq4
+yimwQKiOZmV7fVLSSjSw6e/VfBd0h3gb82ygcplZkN0IclkwTY5SNKqwn/3y07V5
+drqdhkrgdJXtmQ6O5YYECQKBgATERcDToQ1USlI4sKrB/wyv1AlG8dg/IebiVJ4e
+ZAyvcQmClFzq0qS+FiQUnB/WQw9TeeYrwGs1hxBHuJh16srwhLyDrbMvQP06qh8R
+48F8UXXSRec22dV9MQphaROhu2qZdv1AC0WD3tqov6L33aqmEOi+xi8JgbT/PLk5
+c/c1AoGBAI1A/02ryksW6/wc7/6SP2M2rTy4m1sD/GnrTc67EHnRcVBdKO6qH2RY
+nqC8YcveC2ZghgPTDsA3VGuzuBXpwY6wTyV99q6jxQJ6/xcrD9/NUG6Uwv/xfCxl
+IJLeBYEqQundSSny3VtaAUK8Ul1nxpTvVRNwtcyWTo8RHAAyNPWd
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/headermap.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/headermap.go
new file mode 100644
index 000000000..67c7c4835
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/headermap.go
@@ -0,0 +1,80 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "net/http"
+ "strings"
+)
+
+var (
+ commonLowerHeader = map[string]string{} // Go-Canonical-Case -> lower-case
+ commonCanonHeader = map[string]string{} // lower-case -> Go-Canonical-Case
+)
+
+func init() {
+ for _, v := range []string{
+ "accept",
+ "accept-charset",
+ "accept-encoding",
+ "accept-language",
+ "accept-ranges",
+ "age",
+ "access-control-allow-origin",
+ "allow",
+ "authorization",
+ "cache-control",
+ "content-disposition",
+ "content-encoding",
+ "content-language",
+ "content-length",
+ "content-location",
+ "content-range",
+ "content-type",
+ "cookie",
+ "date",
+ "etag",
+ "expect",
+ "expires",
+ "from",
+ "host",
+ "if-match",
+ "if-modified-since",
+ "if-none-match",
+ "if-unmodified-since",
+ "last-modified",
+ "link",
+ "location",
+ "max-forwards",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "range",
+ "referer",
+ "refresh",
+ "retry-after",
+ "server",
+ "set-cookie",
+ "strict-transport-security",
+ "transfer-encoding",
+ "user-agent",
+ "vary",
+ "via",
+ "www-authenticate",
+ } {
+ chk := http.CanonicalHeaderKey(v)
+ commonLowerHeader[chk] = v
+ commonCanonHeader[v] = chk
+ }
+}
+
+func lowerHeader(v string) string {
+ if s, ok := commonLowerHeader[v]; ok {
+ return s
+ }
+ return strings.ToLower(v)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/encode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/encode.go
new file mode 100644
index 000000000..19bd9f4fc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/encode.go
@@ -0,0 +1,252 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package hpack
+
+import (
+ "io"
+)
+
+const (
+ uint32Max = ^uint32(0)
+ initialHeaderTableSize = 4096
+)
+
+type Encoder struct {
+ dynTab dynamicTable
+ // minSize is the minimum table size set by
+ // SetMaxDynamicTableSize after the previous Header Table Size
+ // Update.
+ minSize uint32
+ // maxSizeLimit is the maximum table size this encoder
+ // supports. This will protect the encoder from too large
+ // size.
+ maxSizeLimit uint32
+ // tableSizeUpdate indicates whether "Header Table Size
+ // Update" is required.
+ tableSizeUpdate bool
+ w io.Writer
+ buf []byte
+}
+
+// NewEncoder returns a new Encoder which performs HPACK encoding. An
+// encoded data is written to w.
+func NewEncoder(w io.Writer) *Encoder {
+ e := &Encoder{
+ minSize: uint32Max,
+ maxSizeLimit: initialHeaderTableSize,
+ tableSizeUpdate: false,
+ w: w,
+ }
+ e.dynTab.setMaxSize(initialHeaderTableSize)
+ return e
+}
+
+// WriteField encodes f into a single Write to e's underlying Writer.
+// This function may also produce bytes for "Header Table Size Update"
+// if necessary. If produced, it is done before encoding f.
+func (e *Encoder) WriteField(f HeaderField) error {
+ e.buf = e.buf[:0]
+
+ if e.tableSizeUpdate {
+ e.tableSizeUpdate = false
+ if e.minSize < e.dynTab.maxSize {
+ e.buf = appendTableSize(e.buf, e.minSize)
+ }
+ e.minSize = uint32Max
+ e.buf = appendTableSize(e.buf, e.dynTab.maxSize)
+ }
+
+ idx, nameValueMatch := e.searchTable(f)
+ if nameValueMatch {
+ e.buf = appendIndexed(e.buf, idx)
+ } else {
+ indexing := e.shouldIndex(f)
+ if indexing {
+ e.dynTab.add(f)
+ }
+
+ if idx == 0 {
+ e.buf = appendNewName(e.buf, f, indexing)
+ } else {
+ e.buf = appendIndexedName(e.buf, f, idx, indexing)
+ }
+ }
+ n, err := e.w.Write(e.buf)
+ if err == nil && n != len(e.buf) {
+ err = io.ErrShortWrite
+ }
+ return err
+}
+
+// searchTable searches f in both stable and dynamic header tables.
+// The static header table is searched first. Only when there is no
+// exact match for both name and value, the dynamic header table is
+// then searched. If there is no match, i is 0. If both name and value
+// match, i is the matched index and nameValueMatch becomes true. If
+// only name matches, i points to that index and nameValueMatch
+// becomes false.
+func (e *Encoder) searchTable(f HeaderField) (i uint64, nameValueMatch bool) {
+ for idx, hf := range staticTable {
+ if !constantTimeStringCompare(hf.Name, f.Name) {
+ continue
+ }
+ if i == 0 {
+ i = uint64(idx + 1)
+ }
+ if f.Sensitive {
+ continue
+ }
+ if !constantTimeStringCompare(hf.Value, f.Value) {
+ continue
+ }
+ i = uint64(idx + 1)
+ nameValueMatch = true
+ return
+ }
+
+ j, nameValueMatch := e.dynTab.search(f)
+ if nameValueMatch || (i == 0 && j != 0) {
+ i = j + uint64(len(staticTable))
+ }
+ return
+}
+
+// SetMaxDynamicTableSize changes the dynamic header table size to v.
+// The actual size is bounded by the value passed to
+// SetMaxDynamicTableSizeLimit.
+func (e *Encoder) SetMaxDynamicTableSize(v uint32) {
+ if v > e.maxSizeLimit {
+ v = e.maxSizeLimit
+ }
+ if v < e.minSize {
+ e.minSize = v
+ }
+ e.tableSizeUpdate = true
+ e.dynTab.setMaxSize(v)
+}
+
+// SetMaxDynamicTableSizeLimit changes the maximum value that can be
+// specified in SetMaxDynamicTableSize to v. By default, it is set to
+// 4096, which is the same size of the default dynamic header table
+// size described in HPACK specification. If the current maximum
+// dynamic header table size is strictly greater than v, "Header Table
+// Size Update" will be done in the next WriteField call and the
+// maximum dynamic header table size is truncated to v.
+func (e *Encoder) SetMaxDynamicTableSizeLimit(v uint32) {
+ e.maxSizeLimit = v
+ if e.dynTab.maxSize > v {
+ e.tableSizeUpdate = true
+ e.dynTab.setMaxSize(v)
+ }
+}
+
+// shouldIndex reports whether f should be indexed.
+func (e *Encoder) shouldIndex(f HeaderField) bool {
+ return !f.Sensitive && f.size() <= e.dynTab.maxSize
+}
+
+// appendIndexed appends index i, as encoded in "Indexed Header Field"
+// representation, to dst and returns the extended buffer.
+func appendIndexed(dst []byte, i uint64) []byte {
+ first := len(dst)
+ dst = appendVarInt(dst, 7, i)
+ dst[first] |= 0x80
+ return dst
+}
+
+// appendNewName appends f, as encoded in one of "Literal Header field
+// - New Name" representation variants, to dst and returns the
+// extended buffer.
+//
+// If f.Sensitive is true, "Never Indexed" representation is used. If
+// f.Sensitive is false and indexing is true, "Inremental Indexing"
+// representation is used.
+func appendNewName(dst []byte, f HeaderField, indexing bool) []byte {
+ dst = append(dst, encodeTypeByte(indexing, f.Sensitive))
+ dst = appendHpackString(dst, f.Name)
+ return appendHpackString(dst, f.Value)
+}
+
+// appendIndexedName appends f and index i referring indexed name
+// entry, as encoded in one of "Literal Header field - Indexed Name"
+// representation variants, to dst and returns the extended buffer.
+//
+// If f.Sensitive is true, "Never Indexed" representation is used. If
+// f.Sensitive is false and indexing is true, "Incremental Indexing"
+// representation is used.
+func appendIndexedName(dst []byte, f HeaderField, i uint64, indexing bool) []byte {
+ first := len(dst)
+ var n byte
+ if indexing {
+ n = 6
+ } else {
+ n = 4
+ }
+ dst = appendVarInt(dst, n, i)
+ dst[first] |= encodeTypeByte(indexing, f.Sensitive)
+ return appendHpackString(dst, f.Value)
+}
+
+// appendTableSize appends v, as encoded in "Header Table Size Update"
+// representation, to dst and returns the extended buffer.
+func appendTableSize(dst []byte, v uint32) []byte {
+ first := len(dst)
+ dst = appendVarInt(dst, 5, uint64(v))
+ dst[first] |= 0x20
+ return dst
+}
+
+// appendVarInt appends i, as encoded in variable integer form using n
+// bit prefix, to dst and returns the extended buffer.
+//
+// See
+// http://http2.github.io/http2-spec/compression.html#integer.representation
+func appendVarInt(dst []byte, n byte, i uint64) []byte {
+ k := uint64((1 << n) - 1)
+ if i < k {
+ return append(dst, byte(i))
+ }
+ dst = append(dst, byte(k))
+ i -= k
+ for ; i >= 128; i >>= 7 {
+ dst = append(dst, byte(0x80|(i&0x7f)))
+ }
+ return append(dst, byte(i))
+}
+
+// appendHpackString appends s, as encoded in "String Literal"
+// representation, to dst and returns the the extended buffer.
+//
+// s will be encoded in Huffman codes only when it produces strictly
+// shorter byte string.
+func appendHpackString(dst []byte, s string) []byte {
+ huffmanLength := HuffmanEncodeLength(s)
+ if huffmanLength < uint64(len(s)) {
+ first := len(dst)
+ dst = appendVarInt(dst, 7, huffmanLength)
+ dst = AppendHuffmanString(dst, s)
+ dst[first] |= 0x80
+ } else {
+ dst = appendVarInt(dst, 7, uint64(len(s)))
+ dst = append(dst, s...)
+ }
+ return dst
+}
+
+// encodeTypeByte returns type byte. If sensitive is true, type byte
+// for "Never Indexed" representation is returned. If sensitive is
+// false and indexing is true, type byte for "Incremental Indexing"
+// representation is returned. Otherwise, type byte for "Without
+// Indexing" is returned.
+func encodeTypeByte(indexing, sensitive bool) byte {
+ if sensitive {
+ return 0x10
+ }
+ if indexing {
+ return 0x40
+ }
+ return 0
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/encode_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/encode_test.go
new file mode 100644
index 000000000..dce66c90f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/encode_test.go
@@ -0,0 +1,331 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package hpack
+
+import (
+ "bytes"
+ "encoding/hex"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestEncoderTableSizeUpdate(t *testing.T) {
+ tests := []struct {
+ size1, size2 uint32
+ wantHex string
+ }{
+ // Should emit 2 table size updates (2048 and 4096)
+ {2048, 4096, "3fe10f 3fe11f 82"},
+
+ // Should emit 1 table size update (2048)
+ {16384, 2048, "3fe10f 82"},
+ }
+ for _, tt := range tests {
+ var buf bytes.Buffer
+ e := NewEncoder(&buf)
+ e.SetMaxDynamicTableSize(tt.size1)
+ e.SetMaxDynamicTableSize(tt.size2)
+ if err := e.WriteField(pair(":method", "GET")); err != nil {
+ t.Fatal(err)
+ }
+ want := removeSpace(tt.wantHex)
+ if got := hex.EncodeToString(buf.Bytes()); got != want {
+ t.Errorf("e.SetDynamicTableSize %v, %v = %q; want %q", tt.size1, tt.size2, got, want)
+ }
+ }
+}
+
+func TestEncoderWriteField(t *testing.T) {
+ var buf bytes.Buffer
+ e := NewEncoder(&buf)
+ var got []HeaderField
+ d := NewDecoder(4<<10, func(f HeaderField) {
+ got = append(got, f)
+ })
+
+ tests := []struct {
+ hdrs []HeaderField
+ }{
+ {[]HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "http"),
+ pair(":path", "/"),
+ pair(":authority", "www.example.com"),
+ }},
+ {[]HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "http"),
+ pair(":path", "/"),
+ pair(":authority", "www.example.com"),
+ pair("cache-control", "no-cache"),
+ }},
+ {[]HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "https"),
+ pair(":path", "/index.html"),
+ pair(":authority", "www.example.com"),
+ pair("custom-key", "custom-value"),
+ }},
+ }
+ for i, tt := range tests {
+ buf.Reset()
+ got = got[:0]
+ for _, hf := range tt.hdrs {
+ if err := e.WriteField(hf); err != nil {
+ t.Fatal(err)
+ }
+ }
+ _, err := d.Write(buf.Bytes())
+ if err != nil {
+ t.Errorf("%d. Decoder Write = %v", i, err)
+ }
+ if !reflect.DeepEqual(got, tt.hdrs) {
+ t.Errorf("%d. Decoded %+v; want %+v", i, got, tt.hdrs)
+ }
+ }
+}
+
+func TestEncoderSearchTable(t *testing.T) {
+ e := NewEncoder(nil)
+
+ e.dynTab.add(pair("foo", "bar"))
+ e.dynTab.add(pair("blake", "miz"))
+ e.dynTab.add(pair(":method", "GET"))
+
+ tests := []struct {
+ hf HeaderField
+ wantI uint64
+ wantMatch bool
+ }{
+ // Name and Value match
+ {pair("foo", "bar"), uint64(len(staticTable) + 3), true},
+ {pair("blake", "miz"), uint64(len(staticTable) + 2), true},
+ {pair(":method", "GET"), 2, true},
+
+ // Only name match because Sensitive == true
+ {HeaderField{":method", "GET", true}, 2, false},
+
+ // Only Name matches
+ {pair("foo", "..."), uint64(len(staticTable) + 3), false},
+ {pair("blake", "..."), uint64(len(staticTable) + 2), false},
+ {pair(":method", "..."), 2, false},
+
+ // None match
+ {pair("foo-", "bar"), 0, false},
+ }
+ for _, tt := range tests {
+ if gotI, gotMatch := e.searchTable(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch {
+ t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch)
+ }
+ }
+}
+
+func TestAppendVarInt(t *testing.T) {
+ tests := []struct {
+ n byte
+ i uint64
+ want []byte
+ }{
+ // Fits in a byte:
+ {1, 0, []byte{0}},
+ {2, 2, []byte{2}},
+ {3, 6, []byte{6}},
+ {4, 14, []byte{14}},
+ {5, 30, []byte{30}},
+ {6, 62, []byte{62}},
+ {7, 126, []byte{126}},
+ {8, 254, []byte{254}},
+
+ // Multiple bytes:
+ {5, 1337, []byte{31, 154, 10}},
+ }
+ for _, tt := range tests {
+ got := appendVarInt(nil, tt.n, tt.i)
+ if !bytes.Equal(got, tt.want) {
+ t.Errorf("appendVarInt(nil, %v, %v) = %v; want %v", tt.n, tt.i, got, tt.want)
+ }
+ }
+}
+
+func TestAppendHpackString(t *testing.T) {
+ tests := []struct {
+ s, wantHex string
+ }{
+ // Huffman encoded
+ {"www.example.com", "8c f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
+
+ // Not Huffman encoded
+ {"a", "01 61"},
+
+ // zero length
+ {"", "00"},
+ }
+ for _, tt := range tests {
+ want := removeSpace(tt.wantHex)
+ buf := appendHpackString(nil, tt.s)
+ if got := hex.EncodeToString(buf); want != got {
+ t.Errorf("appendHpackString(nil, %q) = %q; want %q", tt.s, got, want)
+ }
+ }
+}
+
+func TestAppendIndexed(t *testing.T) {
+ tests := []struct {
+ i uint64
+ wantHex string
+ }{
+ // 1 byte
+ {1, "81"},
+ {126, "fe"},
+
+ // 2 bytes
+ {127, "ff00"},
+ {128, "ff01"},
+ }
+ for _, tt := range tests {
+ want := removeSpace(tt.wantHex)
+ buf := appendIndexed(nil, tt.i)
+ if got := hex.EncodeToString(buf); want != got {
+ t.Errorf("appendIndex(nil, %v) = %q; want %q", tt.i, got, want)
+ }
+ }
+}
+
+func TestAppendNewName(t *testing.T) {
+ tests := []struct {
+ f HeaderField
+ indexing bool
+ wantHex string
+ }{
+ // Incremental indexing
+ {HeaderField{"custom-key", "custom-value", false}, true, "40 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
+
+ // Without indexing
+ {HeaderField{"custom-key", "custom-value", false}, false, "00 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
+
+ // Never indexed
+ {HeaderField{"custom-key", "custom-value", true}, true, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
+ {HeaderField{"custom-key", "custom-value", true}, false, "10 88 25a8 49e9 5ba9 7d7f 89 25a8 49e9 5bb8 e8b4 bf"},
+ }
+ for _, tt := range tests {
+ want := removeSpace(tt.wantHex)
+ buf := appendNewName(nil, tt.f, tt.indexing)
+ if got := hex.EncodeToString(buf); want != got {
+ t.Errorf("appendNewName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want)
+ }
+ }
+}
+
+func TestAppendIndexedName(t *testing.T) {
+ tests := []struct {
+ f HeaderField
+ i uint64
+ indexing bool
+ wantHex string
+ }{
+ // Incremental indexing
+ {HeaderField{":status", "302", false}, 8, true, "48 82 6402"},
+
+ // Without indexing
+ {HeaderField{":status", "302", false}, 8, false, "08 82 6402"},
+
+ // Never indexed
+ {HeaderField{":status", "302", true}, 8, true, "18 82 6402"},
+ {HeaderField{":status", "302", true}, 8, false, "18 82 6402"},
+ }
+ for _, tt := range tests {
+ want := removeSpace(tt.wantHex)
+ buf := appendIndexedName(nil, tt.f, tt.i, tt.indexing)
+ if got := hex.EncodeToString(buf); want != got {
+ t.Errorf("appendIndexedName(nil, %+v, %v) = %q; want %q", tt.f, tt.indexing, got, want)
+ }
+ }
+}
+
+func TestAppendTableSize(t *testing.T) {
+ tests := []struct {
+ i uint32
+ wantHex string
+ }{
+ // Fits into 1 byte
+ {30, "3e"},
+
+ // Extra byte
+ {31, "3f00"},
+ {32, "3f01"},
+ }
+ for _, tt := range tests {
+ want := removeSpace(tt.wantHex)
+ buf := appendTableSize(nil, tt.i)
+ if got := hex.EncodeToString(buf); want != got {
+ t.Errorf("appendTableSize(nil, %v) = %q; want %q", tt.i, got, want)
+ }
+ }
+}
+
+func TestEncoderSetMaxDynamicTableSize(t *testing.T) {
+ var buf bytes.Buffer
+ e := NewEncoder(&buf)
+ tests := []struct {
+ v uint32
+ wantUpdate bool
+ wantMinSize uint32
+ wantMaxSize uint32
+ }{
+ // Set new table size to 2048
+ {2048, true, 2048, 2048},
+
+ // Set new table size to 16384, but still limited to
+ // 4096
+ {16384, true, 2048, 4096},
+ }
+ for _, tt := range tests {
+ e.SetMaxDynamicTableSize(tt.v)
+ if got := e.tableSizeUpdate; tt.wantUpdate != got {
+ t.Errorf("e.tableSizeUpdate = %v; want %v", got, tt.wantUpdate)
+ }
+ if got := e.minSize; tt.wantMinSize != got {
+ t.Errorf("e.minSize = %v; want %v", got, tt.wantMinSize)
+ }
+ if got := e.dynTab.maxSize; tt.wantMaxSize != got {
+ t.Errorf("e.maxSize = %v; want %v", got, tt.wantMaxSize)
+ }
+ }
+}
+
+func TestEncoderSetMaxDynamicTableSizeLimit(t *testing.T) {
+ e := NewEncoder(nil)
+ // 4095 < initialHeaderTableSize means maxSize is truncated to
+ // 4095.
+ e.SetMaxDynamicTableSizeLimit(4095)
+ if got, want := e.dynTab.maxSize, uint32(4095); got != want {
+ t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
+ }
+ if got, want := e.maxSizeLimit, uint32(4095); got != want {
+ t.Errorf("e.maxSizeLimit = %v; want %v", got, want)
+ }
+ if got, want := e.tableSizeUpdate, true; got != want {
+ t.Errorf("e.tableSizeUpdate = %v; want %v", got, want)
+ }
+ // maxSize will be truncated to maxSizeLimit
+ e.SetMaxDynamicTableSize(16384)
+ if got, want := e.dynTab.maxSize, uint32(4095); got != want {
+ t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
+ }
+ // 8192 > current maxSizeLimit, so maxSize does not change.
+ e.SetMaxDynamicTableSizeLimit(8192)
+ if got, want := e.dynTab.maxSize, uint32(4095); got != want {
+ t.Errorf("e.dynTab.maxSize = %v; want %v", got, want)
+ }
+ if got, want := e.maxSizeLimit, uint32(8192); got != want {
+ t.Errorf("e.maxSizeLimit = %v; want %v", got, want)
+ }
+}
+
+func removeSpace(s string) string {
+ return strings.Replace(s, " ", "", -1)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/hpack.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/hpack.go
new file mode 100644
index 000000000..c9e36f742
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/hpack.go
@@ -0,0 +1,445 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+// Package hpack implements HPACK, a compression format for
+// efficiently representing HTTP header fields in the context of HTTP/2.
+//
+// See http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-09
+package hpack
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+)
+
+// A DecodingError is something the spec defines as a decoding error.
+type DecodingError struct {
+ Err error
+}
+
+func (de DecodingError) Error() string {
+ return fmt.Sprintf("decoding error: %v", de.Err)
+}
+
+// An InvalidIndexError is returned when an encoder references a table
+// entry before the static table or after the end of the dynamic table.
+type InvalidIndexError int
+
+func (e InvalidIndexError) Error() string {
+ return fmt.Sprintf("invalid indexed representation index %d", int(e))
+}
+
+// A HeaderField is a name-value pair. Both the name and value are
+// treated as opaque sequences of octets.
+type HeaderField struct {
+ Name, Value string
+
+ // Sensitive means that this header field should never be
+ // indexed.
+ Sensitive bool
+}
+
+func (hf *HeaderField) size() uint32 {
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.4.1
+ // "The size of the dynamic table is the sum of the size of
+ // its entries. The size of an entry is the sum of its name's
+ // length in octets (as defined in Section 5.2), its value's
+ // length in octets (see Section 5.2), plus 32. The size of
+ // an entry is calculated using the length of the name and
+ // value without any Huffman encoding applied."
+
+ // This can overflow if somebody makes a large HeaderField
+ // Name and/or Value by hand, but we don't care, because that
+ // won't happen on the wire because the encoding doesn't allow
+ // it.
+ return uint32(len(hf.Name) + len(hf.Value) + 32)
+}
+
+// A Decoder is the decoding context for incremental processing of
+// header blocks.
+type Decoder struct {
+ dynTab dynamicTable
+ emit func(f HeaderField)
+
+ // buf is the unparsed buffer. It's only written to
+ // saveBuf if it was truncated in the middle of a header
+ // block. Because it's usually not owned, we can only
+ // process it under Write.
+ buf []byte // usually not owned
+ saveBuf bytes.Buffer
+}
+
+func NewDecoder(maxSize uint32, emitFunc func(f HeaderField)) *Decoder {
+ d := &Decoder{
+ emit: emitFunc,
+ }
+ d.dynTab.allowedMaxSize = maxSize
+ d.dynTab.setMaxSize(maxSize)
+ return d
+}
+
+// TODO: add method *Decoder.Reset(maxSize, emitFunc) to let callers re-use Decoders and their
+// underlying buffers for garbage reasons.
+
+func (d *Decoder) SetMaxDynamicTableSize(v uint32) {
+ d.dynTab.setMaxSize(v)
+}
+
+// SetAllowedMaxDynamicTableSize sets the upper bound that the encoded
+// stream (via dynamic table size updates) may set the maximum size
+// to.
+func (d *Decoder) SetAllowedMaxDynamicTableSize(v uint32) {
+ d.dynTab.allowedMaxSize = v
+}
+
+type dynamicTable struct {
+ // ents is the FIFO described at
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.2.3.2
+ // The newest (low index) is append at the end, and items are
+ // evicted from the front.
+ ents []HeaderField
+ size uint32
+ maxSize uint32 // current maxSize
+ allowedMaxSize uint32 // maxSize may go up to this, inclusive
+}
+
+func (dt *dynamicTable) setMaxSize(v uint32) {
+ dt.maxSize = v
+ dt.evict()
+}
+
+// TODO: change dynamicTable to be a struct with a slice and a size int field,
+// per http://http2.github.io/http2-spec/compression.html#rfc.section.4.1:
+//
+//
+// Then make add increment the size. maybe the max size should move from Decoder to
+// dynamicTable and add should return an ok bool if there was enough space.
+//
+// Later we'll need a remove operation on dynamicTable.
+
+func (dt *dynamicTable) add(f HeaderField) {
+ dt.ents = append(dt.ents, f)
+ dt.size += f.size()
+ dt.evict()
+}
+
+// If we're too big, evict old stuff (front of the slice)
+func (dt *dynamicTable) evict() {
+ base := dt.ents // keep base pointer of slice
+ for dt.size > dt.maxSize {
+ dt.size -= dt.ents[0].size()
+ dt.ents = dt.ents[1:]
+ }
+
+ // Shift slice contents down if we evicted things.
+ if len(dt.ents) != len(base) {
+ copy(base, dt.ents)
+ dt.ents = base[:len(dt.ents)]
+ }
+}
+
+// constantTimeStringCompare compares string a and b in a constant
+// time manner.
+func constantTimeStringCompare(a, b string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+
+ c := byte(0)
+
+ for i := 0; i < len(a); i++ {
+ c |= a[i] ^ b[i]
+ }
+
+ return c == 0
+}
+
+// Search searches f in the table. The return value i is 0 if there is
+// no name match. If there is name match or name/value match, i is the
+// index of that entry (1-based). If both name and value match,
+// nameValueMatch becomes true.
+func (dt *dynamicTable) search(f HeaderField) (i uint64, nameValueMatch bool) {
+ l := len(dt.ents)
+ for j := l - 1; j >= 0; j-- {
+ ent := dt.ents[j]
+ if !constantTimeStringCompare(ent.Name, f.Name) {
+ continue
+ }
+ if i == 0 {
+ i = uint64(l - j)
+ }
+ if f.Sensitive {
+ continue
+ }
+ if !constantTimeStringCompare(ent.Value, f.Value) {
+ continue
+ }
+ i = uint64(l - j)
+ nameValueMatch = true
+ return
+ }
+ return
+}
+
+func (d *Decoder) maxTableIndex() int {
+ return len(d.dynTab.ents) + len(staticTable)
+}
+
+func (d *Decoder) at(i uint64) (hf HeaderField, ok bool) {
+ if i < 1 {
+ return
+ }
+ if i > uint64(d.maxTableIndex()) {
+ return
+ }
+ if i <= uint64(len(staticTable)) {
+ return staticTable[i-1], true
+ }
+ dents := d.dynTab.ents
+ return dents[len(dents)-(int(i)-len(staticTable))], true
+}
+
+// Decode decodes an entire block.
+//
+// TODO: remove this method and make it incremental later? This is
+// easier for debugging now.
+func (d *Decoder) DecodeFull(p []byte) ([]HeaderField, error) {
+ var hf []HeaderField
+ saveFunc := d.emit
+ defer func() { d.emit = saveFunc }()
+ d.emit = func(f HeaderField) { hf = append(hf, f) }
+ if _, err := d.Write(p); err != nil {
+ return nil, err
+ }
+ if err := d.Close(); err != nil {
+ return nil, err
+ }
+ return hf, nil
+}
+
+func (d *Decoder) Close() error {
+ if d.saveBuf.Len() > 0 {
+ d.saveBuf.Reset()
+ return DecodingError{errors.New("truncated headers")}
+ }
+ return nil
+}
+
+func (d *Decoder) Write(p []byte) (n int, err error) {
+ if len(p) == 0 {
+ // Prevent state machine CPU attacks (making us redo
+ // work up to the point of finding out we don't have
+ // enough data)
+ return
+ }
+ // Only copy the data if we have to. Optimistically assume
+ // that p will contain a complete header block.
+ if d.saveBuf.Len() == 0 {
+ d.buf = p
+ } else {
+ d.saveBuf.Write(p)
+ d.buf = d.saveBuf.Bytes()
+ d.saveBuf.Reset()
+ }
+
+ for len(d.buf) > 0 {
+ err = d.parseHeaderFieldRepr()
+ if err != nil {
+ if err == errNeedMore {
+ err = nil
+ d.saveBuf.Write(d.buf)
+ }
+ break
+ }
+ }
+
+ return len(p), err
+}
+
+// errNeedMore is an internal sentinel error value that means the
+// buffer is truncated and we need to read more data before we can
+// continue parsing.
+var errNeedMore = errors.New("need more data")
+
+type indexType int
+
+const (
+ indexedTrue indexType = iota
+ indexedFalse
+ indexedNever
+)
+
+func (v indexType) indexed() bool { return v == indexedTrue }
+func (v indexType) sensitive() bool { return v == indexedNever }
+
+// returns errNeedMore if there isn't enough data available.
+// any other error is fatal.
+// consumes d.buf iff it returns nil.
+// precondition: must be called with len(d.buf) > 0
+func (d *Decoder) parseHeaderFieldRepr() error {
+ b := d.buf[0]
+ switch {
+ case b&128 != 0:
+ // Indexed representation.
+ // High bit set?
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.6.1
+ return d.parseFieldIndexed()
+ case b&192 == 64:
+ // 6.2.1 Literal Header Field with Incremental Indexing
+ // 0b10xxxxxx: top two bits are 10
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.1
+ return d.parseFieldLiteral(6, indexedTrue)
+ case b&240 == 0:
+ // 6.2.2 Literal Header Field without Indexing
+ // 0b0000xxxx: top four bits are 0000
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.2
+ return d.parseFieldLiteral(4, indexedFalse)
+ case b&240 == 16:
+ // 6.2.3 Literal Header Field never Indexed
+ // 0b0001xxxx: top four bits are 0001
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.6.2.3
+ return d.parseFieldLiteral(4, indexedNever)
+ case b&224 == 32:
+ // 6.3 Dynamic Table Size Update
+ // Top three bits are '001'.
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.6.3
+ return d.parseDynamicTableSizeUpdate()
+ }
+
+ return DecodingError{errors.New("invalid encoding")}
+}
+
+// (same invariants and behavior as parseHeaderFieldRepr)
+func (d *Decoder) parseFieldIndexed() error {
+ buf := d.buf
+ idx, buf, err := readVarInt(7, buf)
+ if err != nil {
+ return err
+ }
+ hf, ok := d.at(idx)
+ if !ok {
+ return DecodingError{InvalidIndexError(idx)}
+ }
+ d.emit(HeaderField{Name: hf.Name, Value: hf.Value})
+ d.buf = buf
+ return nil
+}
+
+// (same invariants and behavior as parseHeaderFieldRepr)
+func (d *Decoder) parseFieldLiteral(n uint8, it indexType) error {
+ buf := d.buf
+ nameIdx, buf, err := readVarInt(n, buf)
+ if err != nil {
+ return err
+ }
+
+ var hf HeaderField
+ if nameIdx > 0 {
+ ihf, ok := d.at(nameIdx)
+ if !ok {
+ return DecodingError{InvalidIndexError(nameIdx)}
+ }
+ hf.Name = ihf.Name
+ } else {
+ hf.Name, buf, err = readString(buf)
+ if err != nil {
+ return err
+ }
+ }
+ hf.Value, buf, err = readString(buf)
+ if err != nil {
+ return err
+ }
+ d.buf = buf
+ if it.indexed() {
+ d.dynTab.add(hf)
+ }
+ hf.Sensitive = it.sensitive()
+ d.emit(hf)
+ return nil
+}
+
+// (same invariants and behavior as parseHeaderFieldRepr)
+func (d *Decoder) parseDynamicTableSizeUpdate() error {
+ buf := d.buf
+ size, buf, err := readVarInt(5, buf)
+ if err != nil {
+ return err
+ }
+ if size > uint64(d.dynTab.allowedMaxSize) {
+ return DecodingError{errors.New("dynamic table size update too large")}
+ }
+ d.dynTab.setMaxSize(uint32(size))
+ d.buf = buf
+ return nil
+}
+
+var errVarintOverflow = DecodingError{errors.New("varint integer overflow")}
+
+// readVarInt reads an unsigned variable length integer off the
+// beginning of p. n is the parameter as described in
+// http://http2.github.io/http2-spec/compression.html#rfc.section.5.1.
+//
+// n must always be between 1 and 8.
+//
+// The returned remain buffer is either a smaller suffix of p, or err != nil.
+// The error is errNeedMore if p doesn't contain a complete integer.
+func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
+ if n < 1 || n > 8 {
+ panic("bad n")
+ }
+ if len(p) == 0 {
+ return 0, p, errNeedMore
+ }
+ i = uint64(p[0])
+ if n < 8 {
+ i &= (1 << uint64(n)) - 1
+ }
+ if i < (1< 0 {
+ b := p[0]
+ p = p[1:]
+ i += uint64(b&127) << m
+ if b&128 == 0 {
+ return i, p, nil
+ }
+ m += 7
+ if m >= 63 { // TODO: proper overflow check. making this up.
+ return 0, origP, errVarintOverflow
+ }
+ }
+ return 0, origP, errNeedMore
+}
+
+func readString(p []byte) (s string, remain []byte, err error) {
+ if len(p) == 0 {
+ return "", p, errNeedMore
+ }
+ isHuff := p[0]&128 != 0
+ strLen, p, err := readVarInt(7, p)
+ if err != nil {
+ return "", p, err
+ }
+ if uint64(len(p)) < strLen {
+ return "", p, errNeedMore
+ }
+ if !isHuff {
+ return string(p[:strLen]), p[strLen:], nil
+ }
+
+ // TODO: optimize this garbage:
+ var buf bytes.Buffer
+ if _, err := HuffmanDecode(&buf, p[:strLen]); err != nil {
+ return "", nil, err
+ }
+ return buf.String(), p[strLen:], nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/hpack_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/hpack_test.go
new file mode 100644
index 000000000..08bee414f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/hpack_test.go
@@ -0,0 +1,648 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package hpack
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestStaticTable(t *testing.T) {
+ fromSpec := `
+ +-------+-----------------------------+---------------+
+ | 1 | :authority | |
+ | 2 | :method | GET |
+ | 3 | :method | POST |
+ | 4 | :path | / |
+ | 5 | :path | /index.html |
+ | 6 | :scheme | http |
+ | 7 | :scheme | https |
+ | 8 | :status | 200 |
+ | 9 | :status | 204 |
+ | 10 | :status | 206 |
+ | 11 | :status | 304 |
+ | 12 | :status | 400 |
+ | 13 | :status | 404 |
+ | 14 | :status | 500 |
+ | 15 | accept-charset | |
+ | 16 | accept-encoding | gzip, deflate |
+ | 17 | accept-language | |
+ | 18 | accept-ranges | |
+ | 19 | accept | |
+ | 20 | access-control-allow-origin | |
+ | 21 | age | |
+ | 22 | allow | |
+ | 23 | authorization | |
+ | 24 | cache-control | |
+ | 25 | content-disposition | |
+ | 26 | content-encoding | |
+ | 27 | content-language | |
+ | 28 | content-length | |
+ | 29 | content-location | |
+ | 30 | content-range | |
+ | 31 | content-type | |
+ | 32 | cookie | |
+ | 33 | date | |
+ | 34 | etag | |
+ | 35 | expect | |
+ | 36 | expires | |
+ | 37 | from | |
+ | 38 | host | |
+ | 39 | if-match | |
+ | 40 | if-modified-since | |
+ | 41 | if-none-match | |
+ | 42 | if-range | |
+ | 43 | if-unmodified-since | |
+ | 44 | last-modified | |
+ | 45 | link | |
+ | 46 | location | |
+ | 47 | max-forwards | |
+ | 48 | proxy-authenticate | |
+ | 49 | proxy-authorization | |
+ | 50 | range | |
+ | 51 | referer | |
+ | 52 | refresh | |
+ | 53 | retry-after | |
+ | 54 | server | |
+ | 55 | set-cookie | |
+ | 56 | strict-transport-security | |
+ | 57 | transfer-encoding | |
+ | 58 | user-agent | |
+ | 59 | vary | |
+ | 60 | via | |
+ | 61 | www-authenticate | |
+ +-------+-----------------------------+---------------+
+`
+ bs := bufio.NewScanner(strings.NewReader(fromSpec))
+ re := regexp.MustCompile(`\| (\d+)\s+\| (\S+)\s*\| (\S(.*\S)?)?\s+\|`)
+ for bs.Scan() {
+ l := bs.Text()
+ if !strings.Contains(l, "|") {
+ continue
+ }
+ m := re.FindStringSubmatch(l)
+ if m == nil {
+ continue
+ }
+ i, err := strconv.Atoi(m[1])
+ if err != nil {
+ t.Errorf("Bogus integer on line %q", l)
+ continue
+ }
+ if i < 1 || i > len(staticTable) {
+ t.Errorf("Bogus index %d on line %q", i, l)
+ continue
+ }
+ if got, want := staticTable[i-1].Name, m[2]; got != want {
+ t.Errorf("header index %d name = %q; want %q", i, got, want)
+ }
+ if got, want := staticTable[i-1].Value, m[3]; got != want {
+ t.Errorf("header index %d value = %q; want %q", i, got, want)
+ }
+ }
+ if err := bs.Err(); err != nil {
+ t.Error(err)
+ }
+}
+
+func (d *Decoder) mustAt(idx int) HeaderField {
+ if hf, ok := d.at(uint64(idx)); !ok {
+ panic(fmt.Sprintf("bogus index %d", idx))
+ } else {
+ return hf
+ }
+}
+
+func TestDynamicTableAt(t *testing.T) {
+ d := NewDecoder(4096, nil)
+ at := d.mustAt
+ if got, want := at(2), (pair(":method", "GET")); got != want {
+ t.Errorf("at(2) = %v; want %v", got, want)
+ }
+ d.dynTab.add(pair("foo", "bar"))
+ d.dynTab.add(pair("blake", "miz"))
+ if got, want := at(len(staticTable)+1), (pair("blake", "miz")); got != want {
+ t.Errorf("at(dyn 1) = %v; want %v", got, want)
+ }
+ if got, want := at(len(staticTable)+2), (pair("foo", "bar")); got != want {
+ t.Errorf("at(dyn 2) = %v; want %v", got, want)
+ }
+ if got, want := at(3), (pair(":method", "POST")); got != want {
+ t.Errorf("at(3) = %v; want %v", got, want)
+ }
+}
+
+func TestDynamicTableSearch(t *testing.T) {
+ dt := dynamicTable{}
+ dt.setMaxSize(4096)
+
+ dt.add(pair("foo", "bar"))
+ dt.add(pair("blake", "miz"))
+ dt.add(pair(":method", "GET"))
+
+ tests := []struct {
+ hf HeaderField
+ wantI uint64
+ wantMatch bool
+ }{
+ // Name and Value match
+ {pair("foo", "bar"), 3, true},
+ {pair(":method", "GET"), 1, true},
+
+ // Only name match because of Sensitive == true
+ {HeaderField{"blake", "miz", true}, 2, false},
+
+ // Only Name matches
+ {pair("foo", "..."), 3, false},
+ {pair("blake", "..."), 2, false},
+ {pair(":method", "..."), 1, false},
+
+ // None match
+ {pair("foo-", "bar"), 0, false},
+ }
+ for _, tt := range tests {
+ if gotI, gotMatch := dt.search(tt.hf); gotI != tt.wantI || gotMatch != tt.wantMatch {
+ t.Errorf("d.search(%+v) = %v, %v; want %v, %v", tt.hf, gotI, gotMatch, tt.wantI, tt.wantMatch)
+ }
+ }
+}
+
+func TestDynamicTableSizeEvict(t *testing.T) {
+ d := NewDecoder(4096, nil)
+ if want := uint32(0); d.dynTab.size != want {
+ t.Fatalf("size = %d; want %d", d.dynTab.size, want)
+ }
+ add := d.dynTab.add
+ add(pair("blake", "eats pizza"))
+ if want := uint32(15 + 32); d.dynTab.size != want {
+ t.Fatalf("after pizza, size = %d; want %d", d.dynTab.size, want)
+ }
+ add(pair("foo", "bar"))
+ if want := uint32(15 + 32 + 6 + 32); d.dynTab.size != want {
+ t.Fatalf("after foo bar, size = %d; want %d", d.dynTab.size, want)
+ }
+ d.dynTab.setMaxSize(15 + 32 + 1 /* slop */)
+ if want := uint32(6 + 32); d.dynTab.size != want {
+ t.Fatalf("after setMaxSize, size = %d; want %d", d.dynTab.size, want)
+ }
+ if got, want := d.mustAt(len(staticTable)+1), (pair("foo", "bar")); got != want {
+ t.Errorf("at(dyn 1) = %v; want %v", got, want)
+ }
+ add(pair("long", strings.Repeat("x", 500)))
+ if want := uint32(0); d.dynTab.size != want {
+ t.Fatalf("after big one, size = %d; want %d", d.dynTab.size, want)
+ }
+}
+
+func TestDecoderDecode(t *testing.T) {
+ tests := []struct {
+ name string
+ in []byte
+ want []HeaderField
+ wantDynTab []HeaderField // newest entry first
+ }{
+ // C.2.1 Literal Header Field with Indexing
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.1
+ {"C.2.1", dehex("400a 6375 7374 6f6d 2d6b 6579 0d63 7573 746f 6d2d 6865 6164 6572"),
+ []HeaderField{pair("custom-key", "custom-header")},
+ []HeaderField{pair("custom-key", "custom-header")},
+ },
+
+ // C.2.2 Literal Header Field without Indexing
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.2
+ {"C.2.2", dehex("040c 2f73 616d 706c 652f 7061 7468"),
+ []HeaderField{pair(":path", "/sample/path")},
+ []HeaderField{}},
+
+ // C.2.3 Literal Header Field never Indexed
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.3
+ {"C.2.3", dehex("1008 7061 7373 776f 7264 0673 6563 7265 74"),
+ []HeaderField{{"password", "secret", true}},
+ []HeaderField{}},
+
+ // C.2.4 Indexed Header Field
+ // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.4
+ {"C.2.4", []byte("\x82"),
+ []HeaderField{pair(":method", "GET")},
+ []HeaderField{}},
+ }
+ for _, tt := range tests {
+ d := NewDecoder(4096, nil)
+ hf, err := d.DecodeFull(tt.in)
+ if err != nil {
+ t.Errorf("%s: %v", tt.name, err)
+ continue
+ }
+ if !reflect.DeepEqual(hf, tt.want) {
+ t.Errorf("%s: Got %v; want %v", tt.name, hf, tt.want)
+ }
+ gotDynTab := d.dynTab.reverseCopy()
+ if !reflect.DeepEqual(gotDynTab, tt.wantDynTab) {
+ t.Errorf("%s: dynamic table after = %v; want %v", tt.name, gotDynTab, tt.wantDynTab)
+ }
+ }
+}
+
+func (dt *dynamicTable) reverseCopy() (hf []HeaderField) {
+ hf = make([]HeaderField, len(dt.ents))
+ for i := range hf {
+ hf[i] = dt.ents[len(dt.ents)-1-i]
+ }
+ return
+}
+
+type encAndWant struct {
+ enc []byte
+ want []HeaderField
+ wantDynTab []HeaderField
+ wantDynSize uint32
+}
+
+// C.3 Request Examples without Huffman Coding
+// http://http2.github.io/http2-spec/compression.html#rfc.section.C.3
+func TestDecodeC3_NoHuffman(t *testing.T) {
+ testDecodeSeries(t, 4096, []encAndWant{
+ {dehex("8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d"),
+ []HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "http"),
+ pair(":path", "/"),
+ pair(":authority", "www.example.com"),
+ },
+ []HeaderField{
+ pair(":authority", "www.example.com"),
+ },
+ 57,
+ },
+ {dehex("8286 84be 5808 6e6f 2d63 6163 6865"),
+ []HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "http"),
+ pair(":path", "/"),
+ pair(":authority", "www.example.com"),
+ pair("cache-control", "no-cache"),
+ },
+ []HeaderField{
+ pair("cache-control", "no-cache"),
+ pair(":authority", "www.example.com"),
+ },
+ 110,
+ },
+ {dehex("8287 85bf 400a 6375 7374 6f6d 2d6b 6579 0c63 7573 746f 6d2d 7661 6c75 65"),
+ []HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "https"),
+ pair(":path", "/index.html"),
+ pair(":authority", "www.example.com"),
+ pair("custom-key", "custom-value"),
+ },
+ []HeaderField{
+ pair("custom-key", "custom-value"),
+ pair("cache-control", "no-cache"),
+ pair(":authority", "www.example.com"),
+ },
+ 164,
+ },
+ })
+}
+
+// C.4 Request Examples with Huffman Coding
+// http://http2.github.io/http2-spec/compression.html#rfc.section.C.4
+func TestDecodeC4_Huffman(t *testing.T) {
+ testDecodeSeries(t, 4096, []encAndWant{
+ {dehex("8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"),
+ []HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "http"),
+ pair(":path", "/"),
+ pair(":authority", "www.example.com"),
+ },
+ []HeaderField{
+ pair(":authority", "www.example.com"),
+ },
+ 57,
+ },
+ {dehex("8286 84be 5886 a8eb 1064 9cbf"),
+ []HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "http"),
+ pair(":path", "/"),
+ pair(":authority", "www.example.com"),
+ pair("cache-control", "no-cache"),
+ },
+ []HeaderField{
+ pair("cache-control", "no-cache"),
+ pair(":authority", "www.example.com"),
+ },
+ 110,
+ },
+ {dehex("8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf"),
+ []HeaderField{
+ pair(":method", "GET"),
+ pair(":scheme", "https"),
+ pair(":path", "/index.html"),
+ pair(":authority", "www.example.com"),
+ pair("custom-key", "custom-value"),
+ },
+ []HeaderField{
+ pair("custom-key", "custom-value"),
+ pair("cache-control", "no-cache"),
+ pair(":authority", "www.example.com"),
+ },
+ 164,
+ },
+ })
+}
+
+// http://http2.github.io/http2-spec/compression.html#rfc.section.C.5
+// "This section shows several consecutive header lists, corresponding
+// to HTTP responses, on the same connection. The HTTP/2 setting
+// parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
+// octets, causing some evictions to occur."
+func TestDecodeC5_ResponsesNoHuff(t *testing.T) {
+ testDecodeSeries(t, 256, []encAndWant{
+ {dehex(`
+4803 3330 3258 0770 7269 7661 7465 611d
+4d6f 6e2c 2032 3120 4f63 7420 3230 3133
+2032 303a 3133 3a32 3120 474d 546e 1768
+7474 7073 3a2f 2f77 7777 2e65 7861 6d70
+6c65 2e63 6f6d
+`),
+ []HeaderField{
+ pair(":status", "302"),
+ pair("cache-control", "private"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("location", "https://www.example.com"),
+ },
+ []HeaderField{
+ pair("location", "https://www.example.com"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("cache-control", "private"),
+ pair(":status", "302"),
+ },
+ 222,
+ },
+ {dehex("4803 3330 37c1 c0bf"),
+ []HeaderField{
+ pair(":status", "307"),
+ pair("cache-control", "private"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("location", "https://www.example.com"),
+ },
+ []HeaderField{
+ pair(":status", "307"),
+ pair("location", "https://www.example.com"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("cache-control", "private"),
+ },
+ 222,
+ },
+ {dehex(`
+88c1 611d 4d6f 6e2c 2032 3120 4f63 7420
+3230 3133 2032 303a 3133 3a32 3220 474d
+54c0 5a04 677a 6970 7738 666f 6f3d 4153
+444a 4b48 514b 425a 584f 5157 454f 5049
+5541 5851 5745 4f49 553b 206d 6178 2d61
+6765 3d33 3630 303b 2076 6572 7369 6f6e
+3d31
+`),
+ []HeaderField{
+ pair(":status", "200"),
+ pair("cache-control", "private"),
+ pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
+ pair("location", "https://www.example.com"),
+ pair("content-encoding", "gzip"),
+ pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
+ },
+ []HeaderField{
+ pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
+ pair("content-encoding", "gzip"),
+ pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
+ },
+ 215,
+ },
+ })
+}
+
+// http://http2.github.io/http2-spec/compression.html#rfc.section.C.6
+// "This section shows the same examples as the previous section, but
+// using Huffman encoding for the literal values. The HTTP/2 setting
+// parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
+// octets, causing some evictions to occur. The eviction mechanism
+// uses the length of the decoded literal values, so the same
+// evictions occurs as in the previous section."
+func TestDecodeC6_ResponsesHuffman(t *testing.T) {
+ testDecodeSeries(t, 256, []encAndWant{
+ {dehex(`
+4882 6402 5885 aec3 771a 4b61 96d0 7abe
+9410 54d4 44a8 2005 9504 0b81 66e0 82a6
+2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8
+e9ae 82ae 43d3
+`),
+ []HeaderField{
+ pair(":status", "302"),
+ pair("cache-control", "private"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("location", "https://www.example.com"),
+ },
+ []HeaderField{
+ pair("location", "https://www.example.com"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("cache-control", "private"),
+ pair(":status", "302"),
+ },
+ 222,
+ },
+ {dehex("4883 640e ffc1 c0bf"),
+ []HeaderField{
+ pair(":status", "307"),
+ pair("cache-control", "private"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("location", "https://www.example.com"),
+ },
+ []HeaderField{
+ pair(":status", "307"),
+ pair("location", "https://www.example.com"),
+ pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
+ pair("cache-control", "private"),
+ },
+ 222,
+ },
+ {dehex(`
+88c1 6196 d07a be94 1054 d444 a820 0595
+040b 8166 e084 a62d 1bff c05a 839b d9ab
+77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b
+3960 d5af 2708 7f36 72c1 ab27 0fb5 291f
+9587 3160 65c0 03ed 4ee5 b106 3d50 07
+`),
+ []HeaderField{
+ pair(":status", "200"),
+ pair("cache-control", "private"),
+ pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
+ pair("location", "https://www.example.com"),
+ pair("content-encoding", "gzip"),
+ pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
+ },
+ []HeaderField{
+ pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
+ pair("content-encoding", "gzip"),
+ pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
+ },
+ 215,
+ },
+ })
+}
+
+func testDecodeSeries(t *testing.T, size uint32, steps []encAndWant) {
+ d := NewDecoder(size, nil)
+ for i, step := range steps {
+ hf, err := d.DecodeFull(step.enc)
+ if err != nil {
+ t.Fatalf("Error at step index %d: %v", i, err)
+ }
+ if !reflect.DeepEqual(hf, step.want) {
+ t.Fatalf("At step index %d: Got headers %v; want %v", i, hf, step.want)
+ }
+ gotDynTab := d.dynTab.reverseCopy()
+ if !reflect.DeepEqual(gotDynTab, step.wantDynTab) {
+ t.Errorf("After step index %d, dynamic table = %v; want %v", i, gotDynTab, step.wantDynTab)
+ }
+ if d.dynTab.size != step.wantDynSize {
+ t.Errorf("After step index %d, dynamic table size = %v; want %v", i, d.dynTab.size, step.wantDynSize)
+ }
+ }
+}
+
+func TestHuffmanDecode(t *testing.T) {
+ tests := []struct {
+ inHex, want string
+ }{
+ {"f1e3 c2e5 f23a 6ba0 ab90 f4ff", "www.example.com"},
+ {"a8eb 1064 9cbf", "no-cache"},
+ {"25a8 49e9 5ba9 7d7f", "custom-key"},
+ {"25a8 49e9 5bb8 e8b4 bf", "custom-value"},
+ {"6402", "302"},
+ {"aec3 771a 4b", "private"},
+ {"d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3", "https://www.example.com"},
+ {"9bd9 ab", "gzip"},
+ {"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+ }
+ for i, tt := range tests {
+ var buf bytes.Buffer
+ in, err := hex.DecodeString(strings.Replace(tt.inHex, " ", "", -1))
+ if err != nil {
+ t.Errorf("%d. hex input error: %v", i, err)
+ continue
+ }
+ if _, err := HuffmanDecode(&buf, in); err != nil {
+ t.Errorf("%d. decode error: %v", i, err)
+ continue
+ }
+ if got := buf.String(); tt.want != got {
+ t.Errorf("%d. decode = %q; want %q", i, got, tt.want)
+ }
+ }
+}
+
+func TestAppendHuffmanString(t *testing.T) {
+ tests := []struct {
+ in, want string
+ }{
+ {"www.example.com", "f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
+ {"no-cache", "a8eb 1064 9cbf"},
+ {"custom-key", "25a8 49e9 5ba9 7d7f"},
+ {"custom-value", "25a8 49e9 5bb8 e8b4 bf"},
+ {"302", "6402"},
+ {"private", "aec3 771a 4b"},
+ {"Mon, 21 Oct 2013 20:13:21 GMT", "d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff"},
+ {"https://www.example.com", "9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3"},
+ {"gzip", "9bd9 ab"},
+ {"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ "94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07"},
+ }
+ for i, tt := range tests {
+ buf := []byte{}
+ want := strings.Replace(tt.want, " ", "", -1)
+ buf = AppendHuffmanString(buf, tt.in)
+ if got := hex.EncodeToString(buf); want != got {
+ t.Errorf("%d. encode = %q; want %q", i, got, want)
+ }
+ }
+}
+
+func TestReadVarInt(t *testing.T) {
+ type res struct {
+ i uint64
+ consumed int
+ err error
+ }
+ tests := []struct {
+ n byte
+ p []byte
+ want res
+ }{
+ // Fits in a byte:
+ {1, []byte{0}, res{0, 1, nil}},
+ {2, []byte{2}, res{2, 1, nil}},
+ {3, []byte{6}, res{6, 1, nil}},
+ {4, []byte{14}, res{14, 1, nil}},
+ {5, []byte{30}, res{30, 1, nil}},
+ {6, []byte{62}, res{62, 1, nil}},
+ {7, []byte{126}, res{126, 1, nil}},
+ {8, []byte{254}, res{254, 1, nil}},
+
+ // Doesn't fit in a byte:
+ {1, []byte{1}, res{0, 0, errNeedMore}},
+ {2, []byte{3}, res{0, 0, errNeedMore}},
+ {3, []byte{7}, res{0, 0, errNeedMore}},
+ {4, []byte{15}, res{0, 0, errNeedMore}},
+ {5, []byte{31}, res{0, 0, errNeedMore}},
+ {6, []byte{63}, res{0, 0, errNeedMore}},
+ {7, []byte{127}, res{0, 0, errNeedMore}},
+ {8, []byte{255}, res{0, 0, errNeedMore}},
+
+ // Ignoring top bits:
+ {5, []byte{255, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 111
+ {5, []byte{159, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 100
+ {5, []byte{191, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 101
+
+ // Extra byte:
+ {5, []byte{191, 154, 10, 2}, res{1337, 3, nil}}, // extra byte
+
+ // Short a byte:
+ {5, []byte{191, 154}, res{0, 0, errNeedMore}},
+
+ // integer overflow:
+ {1, []byte{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, res{0, 0, errVarintOverflow}},
+ }
+ for _, tt := range tests {
+ i, remain, err := readVarInt(tt.n, tt.p)
+ consumed := len(tt.p) - len(remain)
+ got := res{i, consumed, err}
+ if got != tt.want {
+ t.Errorf("readVarInt(%d, %v ~ %x) = %+v; want %+v", tt.n, tt.p, tt.p, got, tt.want)
+ }
+ }
+}
+
+func dehex(s string) []byte {
+ s = strings.Replace(s, " ", "", -1)
+ s = strings.Replace(s, "\n", "", -1)
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/huffman.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/huffman.go
new file mode 100644
index 000000000..9fe76f68e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/huffman.go
@@ -0,0 +1,159 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package hpack
+
+import (
+ "bytes"
+ "io"
+ "sync"
+)
+
+var bufPool = sync.Pool{
+ New: func() interface{} { return new(bytes.Buffer) },
+}
+
+// HuffmanDecode decodes the string in v and writes the expanded
+// result to w, returning the number of bytes written to w and the
+// Write call's return value. At most one Write call is made.
+func HuffmanDecode(w io.Writer, v []byte) (int, error) {
+ buf := bufPool.Get().(*bytes.Buffer)
+ buf.Reset()
+ defer bufPool.Put(buf)
+
+ n := rootHuffmanNode
+ cur, nbits := uint(0), uint8(0)
+ for _, b := range v {
+ cur = cur<<8 | uint(b)
+ nbits += 8
+ for nbits >= 8 {
+ n = n.children[byte(cur>>(nbits-8))]
+ if n.children == nil {
+ buf.WriteByte(n.sym)
+ nbits -= n.codeLen
+ n = rootHuffmanNode
+ } else {
+ nbits -= 8
+ }
+ }
+ }
+ for nbits > 0 {
+ n = n.children[byte(cur<<(8-nbits))]
+ if n.children != nil || n.codeLen > nbits {
+ break
+ }
+ buf.WriteByte(n.sym)
+ nbits -= n.codeLen
+ n = rootHuffmanNode
+ }
+ return w.Write(buf.Bytes())
+}
+
+type node struct {
+ // children is non-nil for internal nodes
+ children []*node
+
+ // The following are only valid if children is nil:
+ codeLen uint8 // number of bits that led to the output of sym
+ sym byte // output symbol
+}
+
+func newInternalNode() *node {
+ return &node{children: make([]*node, 256)}
+}
+
+var rootHuffmanNode = newInternalNode()
+
+func init() {
+ for i, code := range huffmanCodes {
+ if i > 255 {
+ panic("too many huffman codes")
+ }
+ addDecoderNode(byte(i), code, huffmanCodeLen[i])
+ }
+}
+
+func addDecoderNode(sym byte, code uint32, codeLen uint8) {
+ cur := rootHuffmanNode
+ for codeLen > 8 {
+ codeLen -= 8
+ i := uint8(code >> codeLen)
+ if cur.children[i] == nil {
+ cur.children[i] = newInternalNode()
+ }
+ cur = cur.children[i]
+ }
+ shift := 8 - codeLen
+ start, end := int(uint8(code<> (nbits - rembits))
+ dst[len(dst)-1] |= t
+ }
+
+ return dst
+}
+
+// HuffmanEncodeLength returns the number of bytes required to encode
+// s in Huffman codes. The result is round up to byte boundary.
+func HuffmanEncodeLength(s string) uint64 {
+ n := uint64(0)
+ for i := 0; i < len(s); i++ {
+ n += uint64(huffmanCodeLen[s[i]])
+ }
+ return (n + 7) / 8
+}
+
+// appendByteToHuffmanCode appends Huffman code for c to dst and
+// returns the extended buffer and the remaining bits in the last
+// element. The appending is not byte aligned and the remaining bits
+// in the last element of dst is given in rembits.
+func appendByteToHuffmanCode(dst []byte, rembits uint8, c byte) ([]byte, uint8) {
+ code := huffmanCodes[c]
+ nbits := huffmanCodeLen[c]
+
+ for {
+ if rembits > nbits {
+ t := uint8(code << (rembits - nbits))
+ dst[len(dst)-1] |= t
+ rembits -= nbits
+ break
+ }
+
+ t := uint8(code >> (nbits - rembits))
+ dst[len(dst)-1] |= t
+
+ nbits -= rembits
+ rembits = 8
+
+ if nbits == 0 {
+ break
+ }
+
+ dst = append(dst, 0)
+ }
+
+ return dst, rembits
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/tables.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/tables.go
new file mode 100644
index 000000000..f898e2512
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack/tables.go
@@ -0,0 +1,353 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package hpack
+
+func pair(name, value string) HeaderField {
+ return HeaderField{Name: name, Value: value}
+}
+
+// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-07#appendix-B
+var staticTable = []HeaderField{
+ pair(":authority", ""), // index 1 (1-based)
+ pair(":method", "GET"),
+ pair(":method", "POST"),
+ pair(":path", "/"),
+ pair(":path", "/index.html"),
+ pair(":scheme", "http"),
+ pair(":scheme", "https"),
+ pair(":status", "200"),
+ pair(":status", "204"),
+ pair(":status", "206"),
+ pair(":status", "304"),
+ pair(":status", "400"),
+ pair(":status", "404"),
+ pair(":status", "500"),
+ pair("accept-charset", ""),
+ pair("accept-encoding", "gzip, deflate"),
+ pair("accept-language", ""),
+ pair("accept-ranges", ""),
+ pair("accept", ""),
+ pair("access-control-allow-origin", ""),
+ pair("age", ""),
+ pair("allow", ""),
+ pair("authorization", ""),
+ pair("cache-control", ""),
+ pair("content-disposition", ""),
+ pair("content-encoding", ""),
+ pair("content-language", ""),
+ pair("content-length", ""),
+ pair("content-location", ""),
+ pair("content-range", ""),
+ pair("content-type", ""),
+ pair("cookie", ""),
+ pair("date", ""),
+ pair("etag", ""),
+ pair("expect", ""),
+ pair("expires", ""),
+ pair("from", ""),
+ pair("host", ""),
+ pair("if-match", ""),
+ pair("if-modified-since", ""),
+ pair("if-none-match", ""),
+ pair("if-range", ""),
+ pair("if-unmodified-since", ""),
+ pair("last-modified", ""),
+ pair("link", ""),
+ pair("location", ""),
+ pair("max-forwards", ""),
+ pair("proxy-authenticate", ""),
+ pair("proxy-authorization", ""),
+ pair("range", ""),
+ pair("referer", ""),
+ pair("refresh", ""),
+ pair("retry-after", ""),
+ pair("server", ""),
+ pair("set-cookie", ""),
+ pair("strict-transport-security", ""),
+ pair("transfer-encoding", ""),
+ pair("user-agent", ""),
+ pair("vary", ""),
+ pair("via", ""),
+ pair("www-authenticate", ""),
+}
+
+var huffmanCodes = []uint32{
+ 0x1ff8,
+ 0x7fffd8,
+ 0xfffffe2,
+ 0xfffffe3,
+ 0xfffffe4,
+ 0xfffffe5,
+ 0xfffffe6,
+ 0xfffffe7,
+ 0xfffffe8,
+ 0xffffea,
+ 0x3ffffffc,
+ 0xfffffe9,
+ 0xfffffea,
+ 0x3ffffffd,
+ 0xfffffeb,
+ 0xfffffec,
+ 0xfffffed,
+ 0xfffffee,
+ 0xfffffef,
+ 0xffffff0,
+ 0xffffff1,
+ 0xffffff2,
+ 0x3ffffffe,
+ 0xffffff3,
+ 0xffffff4,
+ 0xffffff5,
+ 0xffffff6,
+ 0xffffff7,
+ 0xffffff8,
+ 0xffffff9,
+ 0xffffffa,
+ 0xffffffb,
+ 0x14,
+ 0x3f8,
+ 0x3f9,
+ 0xffa,
+ 0x1ff9,
+ 0x15,
+ 0xf8,
+ 0x7fa,
+ 0x3fa,
+ 0x3fb,
+ 0xf9,
+ 0x7fb,
+ 0xfa,
+ 0x16,
+ 0x17,
+ 0x18,
+ 0x0,
+ 0x1,
+ 0x2,
+ 0x19,
+ 0x1a,
+ 0x1b,
+ 0x1c,
+ 0x1d,
+ 0x1e,
+ 0x1f,
+ 0x5c,
+ 0xfb,
+ 0x7ffc,
+ 0x20,
+ 0xffb,
+ 0x3fc,
+ 0x1ffa,
+ 0x21,
+ 0x5d,
+ 0x5e,
+ 0x5f,
+ 0x60,
+ 0x61,
+ 0x62,
+ 0x63,
+ 0x64,
+ 0x65,
+ 0x66,
+ 0x67,
+ 0x68,
+ 0x69,
+ 0x6a,
+ 0x6b,
+ 0x6c,
+ 0x6d,
+ 0x6e,
+ 0x6f,
+ 0x70,
+ 0x71,
+ 0x72,
+ 0xfc,
+ 0x73,
+ 0xfd,
+ 0x1ffb,
+ 0x7fff0,
+ 0x1ffc,
+ 0x3ffc,
+ 0x22,
+ 0x7ffd,
+ 0x3,
+ 0x23,
+ 0x4,
+ 0x24,
+ 0x5,
+ 0x25,
+ 0x26,
+ 0x27,
+ 0x6,
+ 0x74,
+ 0x75,
+ 0x28,
+ 0x29,
+ 0x2a,
+ 0x7,
+ 0x2b,
+ 0x76,
+ 0x2c,
+ 0x8,
+ 0x9,
+ 0x2d,
+ 0x77,
+ 0x78,
+ 0x79,
+ 0x7a,
+ 0x7b,
+ 0x7ffe,
+ 0x7fc,
+ 0x3ffd,
+ 0x1ffd,
+ 0xffffffc,
+ 0xfffe6,
+ 0x3fffd2,
+ 0xfffe7,
+ 0xfffe8,
+ 0x3fffd3,
+ 0x3fffd4,
+ 0x3fffd5,
+ 0x7fffd9,
+ 0x3fffd6,
+ 0x7fffda,
+ 0x7fffdb,
+ 0x7fffdc,
+ 0x7fffdd,
+ 0x7fffde,
+ 0xffffeb,
+ 0x7fffdf,
+ 0xffffec,
+ 0xffffed,
+ 0x3fffd7,
+ 0x7fffe0,
+ 0xffffee,
+ 0x7fffe1,
+ 0x7fffe2,
+ 0x7fffe3,
+ 0x7fffe4,
+ 0x1fffdc,
+ 0x3fffd8,
+ 0x7fffe5,
+ 0x3fffd9,
+ 0x7fffe6,
+ 0x7fffe7,
+ 0xffffef,
+ 0x3fffda,
+ 0x1fffdd,
+ 0xfffe9,
+ 0x3fffdb,
+ 0x3fffdc,
+ 0x7fffe8,
+ 0x7fffe9,
+ 0x1fffde,
+ 0x7fffea,
+ 0x3fffdd,
+ 0x3fffde,
+ 0xfffff0,
+ 0x1fffdf,
+ 0x3fffdf,
+ 0x7fffeb,
+ 0x7fffec,
+ 0x1fffe0,
+ 0x1fffe1,
+ 0x3fffe0,
+ 0x1fffe2,
+ 0x7fffed,
+ 0x3fffe1,
+ 0x7fffee,
+ 0x7fffef,
+ 0xfffea,
+ 0x3fffe2,
+ 0x3fffe3,
+ 0x3fffe4,
+ 0x7ffff0,
+ 0x3fffe5,
+ 0x3fffe6,
+ 0x7ffff1,
+ 0x3ffffe0,
+ 0x3ffffe1,
+ 0xfffeb,
+ 0x7fff1,
+ 0x3fffe7,
+ 0x7ffff2,
+ 0x3fffe8,
+ 0x1ffffec,
+ 0x3ffffe2,
+ 0x3ffffe3,
+ 0x3ffffe4,
+ 0x7ffffde,
+ 0x7ffffdf,
+ 0x3ffffe5,
+ 0xfffff1,
+ 0x1ffffed,
+ 0x7fff2,
+ 0x1fffe3,
+ 0x3ffffe6,
+ 0x7ffffe0,
+ 0x7ffffe1,
+ 0x3ffffe7,
+ 0x7ffffe2,
+ 0xfffff2,
+ 0x1fffe4,
+ 0x1fffe5,
+ 0x3ffffe8,
+ 0x3ffffe9,
+ 0xffffffd,
+ 0x7ffffe3,
+ 0x7ffffe4,
+ 0x7ffffe5,
+ 0xfffec,
+ 0xfffff3,
+ 0xfffed,
+ 0x1fffe6,
+ 0x3fffe9,
+ 0x1fffe7,
+ 0x1fffe8,
+ 0x7ffff3,
+ 0x3fffea,
+ 0x3fffeb,
+ 0x1ffffee,
+ 0x1ffffef,
+ 0xfffff4,
+ 0xfffff5,
+ 0x3ffffea,
+ 0x7ffff4,
+ 0x3ffffeb,
+ 0x7ffffe6,
+ 0x3ffffec,
+ 0x3ffffed,
+ 0x7ffffe7,
+ 0x7ffffe8,
+ 0x7ffffe9,
+ 0x7ffffea,
+ 0x7ffffeb,
+ 0xffffffe,
+ 0x7ffffec,
+ 0x7ffffed,
+ 0x7ffffee,
+ 0x7ffffef,
+ 0x7fffff0,
+ 0x3ffffee,
+}
+
+var huffmanCodeLen = []uint8{
+ 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
+ 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+ 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
+ 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
+ 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
+ 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
+ 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
+ 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
+ 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
+ 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
+ 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
+ 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
+ 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
+ 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
+ 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26,
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/http2.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/http2.go
new file mode 100644
index 000000000..136692a55
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/http2.go
@@ -0,0 +1,249 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+// Package http2 implements the HTTP/2 protocol.
+//
+// This is a work in progress. This package is low-level and intended
+// to be used directly by very few people. Most users will use it
+// indirectly through integration with the net/http package. See
+// ConfigureServer. That ConfigureServer call will likely be automatic
+// or available via an empty import in the future.
+//
+// This package currently targets draft-14. See http://http2.github.io/
+package http2
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net/http"
+ "strconv"
+ "sync"
+)
+
+var VerboseLogs = false
+
+const (
+ // ClientPreface is the string that must be sent by new
+ // connections from clients.
+ ClientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+
+ // SETTINGS_MAX_FRAME_SIZE default
+ // http://http2.github.io/http2-spec/#rfc.section.6.5.2
+ initialMaxFrameSize = 16384
+
+ // NextProtoTLS is the NPN/ALPN protocol negotiated during
+ // HTTP/2's TLS setup.
+ NextProtoTLS = "h2"
+
+ // http://http2.github.io/http2-spec/#SettingValues
+ initialHeaderTableSize = 4096
+
+ initialWindowSize = 65535 // 6.9.2 Initial Flow Control Window Size
+
+ defaultMaxReadFrameSize = 1 << 20
+)
+
+var (
+ clientPreface = []byte(ClientPreface)
+)
+
+type streamState int
+
+const (
+ stateIdle streamState = iota
+ stateOpen
+ stateHalfClosedLocal
+ stateHalfClosedRemote
+ stateResvLocal
+ stateResvRemote
+ stateClosed
+)
+
+var stateName = [...]string{
+ stateIdle: "Idle",
+ stateOpen: "Open",
+ stateHalfClosedLocal: "HalfClosedLocal",
+ stateHalfClosedRemote: "HalfClosedRemote",
+ stateResvLocal: "ResvLocal",
+ stateResvRemote: "ResvRemote",
+ stateClosed: "Closed",
+}
+
+func (st streamState) String() string {
+ return stateName[st]
+}
+
+// Setting is a setting parameter: which setting it is, and its value.
+type Setting struct {
+ // ID is which setting is being set.
+ // See http://http2.github.io/http2-spec/#SettingValues
+ ID SettingID
+
+ // Val is the value.
+ Val uint32
+}
+
+func (s Setting) String() string {
+ return fmt.Sprintf("[%v = %d]", s.ID, s.Val)
+}
+
+// Valid reports whether the setting is valid.
+func (s Setting) Valid() error {
+ // Limits and error codes from 6.5.2 Defined SETTINGS Parameters
+ switch s.ID {
+ case SettingEnablePush:
+ if s.Val != 1 && s.Val != 0 {
+ return ConnectionError(ErrCodeProtocol)
+ }
+ case SettingInitialWindowSize:
+ if s.Val > 1<<31-1 {
+ return ConnectionError(ErrCodeFlowControl)
+ }
+ case SettingMaxFrameSize:
+ if s.Val < 16384 || s.Val > 1<<24-1 {
+ return ConnectionError(ErrCodeProtocol)
+ }
+ }
+ return nil
+}
+
+// A SettingID is an HTTP/2 setting as defined in
+// http://http2.github.io/http2-spec/#iana-settings
+type SettingID uint16
+
+const (
+ SettingHeaderTableSize SettingID = 0x1
+ SettingEnablePush SettingID = 0x2
+ SettingMaxConcurrentStreams SettingID = 0x3
+ SettingInitialWindowSize SettingID = 0x4
+ SettingMaxFrameSize SettingID = 0x5
+ SettingMaxHeaderListSize SettingID = 0x6
+)
+
+var settingName = map[SettingID]string{
+ SettingHeaderTableSize: "HEADER_TABLE_SIZE",
+ SettingEnablePush: "ENABLE_PUSH",
+ SettingMaxConcurrentStreams: "MAX_CONCURRENT_STREAMS",
+ SettingInitialWindowSize: "INITIAL_WINDOW_SIZE",
+ SettingMaxFrameSize: "MAX_FRAME_SIZE",
+ SettingMaxHeaderListSize: "MAX_HEADER_LIST_SIZE",
+}
+
+func (s SettingID) String() string {
+ if v, ok := settingName[s]; ok {
+ return v
+ }
+ return fmt.Sprintf("UNKNOWN_SETTING_%d", uint16(s))
+}
+
+func validHeader(v string) bool {
+ if len(v) == 0 {
+ return false
+ }
+ for _, r := range v {
+ // "Just as in HTTP/1.x, header field names are
+ // strings of ASCII characters that are compared in a
+ // case-insensitive fashion. However, header field
+ // names MUST be converted to lowercase prior to their
+ // encoding in HTTP/2. "
+ if r >= 127 || ('A' <= r && r <= 'Z') {
+ return false
+ }
+ }
+ return true
+}
+
+var httpCodeStringCommon = map[int]string{} // n -> strconv.Itoa(n)
+
+func init() {
+ for i := 100; i <= 999; i++ {
+ if v := http.StatusText(i); v != "" {
+ httpCodeStringCommon[i] = strconv.Itoa(i)
+ }
+ }
+}
+
+func httpCodeString(code int) string {
+ if s, ok := httpCodeStringCommon[code]; ok {
+ return s
+ }
+ return strconv.Itoa(code)
+}
+
+// from pkg io
+type stringWriter interface {
+ WriteString(s string) (n int, err error)
+}
+
+// A gate lets two goroutines coordinate their activities.
+type gate chan struct{}
+
+func (g gate) Done() { g <- struct{}{} }
+func (g gate) Wait() { <-g }
+
+// A closeWaiter is like a sync.WaitGroup but only goes 1 to 0 (open to closed).
+type closeWaiter chan struct{}
+
+// Init makes a closeWaiter usable.
+// It exists because so a closeWaiter value can be placed inside a
+// larger struct and have the Mutex and Cond's memory in the same
+// allocation.
+func (cw *closeWaiter) Init() {
+ *cw = make(chan struct{})
+}
+
+// Close marks the closeWaiter as closed and unblocks any waiters.
+func (cw closeWaiter) Close() {
+ close(cw)
+}
+
+// Wait waits for the closeWaiter to become closed.
+func (cw closeWaiter) Wait() {
+ <-cw
+}
+
+// bufferedWriter is a buffered writer that writes to w.
+// Its buffered writer is lazily allocated as needed, to minimize
+// idle memory usage with many connections.
+type bufferedWriter struct {
+ w io.Writer // immutable
+ bw *bufio.Writer // non-nil when data is buffered
+}
+
+func newBufferedWriter(w io.Writer) *bufferedWriter {
+ return &bufferedWriter{w: w}
+}
+
+var bufWriterPool = sync.Pool{
+ New: func() interface{} {
+ // TODO: pick something better? this is a bit under
+ // (3 x typical 1500 byte MTU) at least.
+ return bufio.NewWriterSize(nil, 4<<10)
+ },
+}
+
+func (w *bufferedWriter) Write(p []byte) (n int, err error) {
+ if w.bw == nil {
+ bw := bufWriterPool.Get().(*bufio.Writer)
+ bw.Reset(w.w)
+ w.bw = bw
+ }
+ return w.bw.Write(p)
+}
+
+func (w *bufferedWriter) Flush() error {
+ bw := w.bw
+ if bw == nil {
+ return nil
+ }
+ err := bw.Flush()
+ bw.Reset(nil)
+ bufWriterPool.Put(bw)
+ w.bw = nil
+ return err
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/http2_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/http2_test.go
new file mode 100644
index 000000000..c0cd2f852
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/http2_test.go
@@ -0,0 +1,152 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "net/http"
+ "os/exec"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+)
+
+var knownFailing = flag.Bool("known_failing", false, "Run known-failing tests.")
+
+func condSkipFailingTest(t *testing.T) {
+ if !*knownFailing {
+ t.Skip("Skipping known-failing test without --known_failing")
+ }
+}
+
+func init() {
+ DebugGoroutines = true
+ flag.BoolVar(&VerboseLogs, "verboseh2", false, "Verbose HTTP/2 debug logging")
+}
+
+func TestSettingString(t *testing.T) {
+ tests := []struct {
+ s Setting
+ want string
+ }{
+ {Setting{SettingMaxFrameSize, 123}, "[MAX_FRAME_SIZE = 123]"},
+ {Setting{1<<16 - 1, 123}, "[UNKNOWN_SETTING_65535 = 123]"},
+ }
+ for i, tt := range tests {
+ got := fmt.Sprint(tt.s)
+ if got != tt.want {
+ t.Errorf("%d. for %#v, string = %q; want %q", i, tt.s, got, tt.want)
+ }
+ }
+}
+
+type twriter struct {
+ t testing.TB
+ st *serverTester // optional
+}
+
+func (w twriter) Write(p []byte) (n int, err error) {
+ if w.st != nil {
+ ps := string(p)
+ for _, phrase := range w.st.logFilter {
+ if strings.Contains(ps, phrase) {
+ return len(p), nil // no logging
+ }
+ }
+ }
+ w.t.Logf("%s", p)
+ return len(p), nil
+}
+
+// like encodeHeader, but don't add implicit psuedo headers.
+func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte {
+ var buf bytes.Buffer
+ enc := hpack.NewEncoder(&buf)
+ for len(headers) > 0 {
+ k, v := headers[0], headers[1]
+ headers = headers[2:]
+ if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil {
+ t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
+ }
+ }
+ return buf.Bytes()
+}
+
+// Verify that curl has http2.
+func requireCurl(t *testing.T) {
+ out, err := dockerLogs(curl(t, "--version"))
+ if err != nil {
+ t.Skipf("failed to determine curl features; skipping test")
+ }
+ if !strings.Contains(string(out), "HTTP2") {
+ t.Skip("curl doesn't support HTTP2; skipping test")
+ }
+}
+
+func curl(t *testing.T, args ...string) (container string) {
+ out, err := exec.Command("docker", append([]string{"run", "-d", "--net=host", "gohttp2/curl"}, args...)...).CombinedOutput()
+ if err != nil {
+ t.Skipf("Failed to run curl in docker: %v, %s", err, out)
+ }
+ return strings.TrimSpace(string(out))
+}
+
+type puppetCommand struct {
+ fn func(w http.ResponseWriter, r *http.Request)
+ done chan<- bool
+}
+
+type handlerPuppet struct {
+ ch chan puppetCommand
+}
+
+func newHandlerPuppet() *handlerPuppet {
+ return &handlerPuppet{
+ ch: make(chan puppetCommand),
+ }
+}
+
+func (p *handlerPuppet) act(w http.ResponseWriter, r *http.Request) {
+ for cmd := range p.ch {
+ cmd.fn(w, r)
+ cmd.done <- true
+ }
+}
+
+func (p *handlerPuppet) done() { close(p.ch) }
+func (p *handlerPuppet) do(fn func(http.ResponseWriter, *http.Request)) {
+ done := make(chan bool)
+ p.ch <- puppetCommand{fn, done}
+ <-done
+}
+func dockerLogs(container string) ([]byte, error) {
+ out, err := exec.Command("docker", "wait", container).CombinedOutput()
+ if err != nil {
+ return out, err
+ }
+ exitStatus, err := strconv.Atoi(strings.TrimSpace(string(out)))
+ if err != nil {
+ return out, errors.New("unexpected exit status from docker wait")
+ }
+ out, err = exec.Command("docker", "logs", container).CombinedOutput()
+ exec.Command("docker", "rm", container).Run()
+ if err == nil && exitStatus != 0 {
+ err = fmt.Errorf("exit status %d: %s", exitStatus, out)
+ }
+ return out, err
+}
+
+func kill(container string) {
+ exec.Command("docker", "kill", container).Run()
+ exec.Command("docker", "rm", container).Run()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/pipe.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/pipe.go
new file mode 100644
index 000000000..ce9aad533
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/pipe.go
@@ -0,0 +1,43 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "sync"
+)
+
+type pipe struct {
+ b buffer
+ c sync.Cond
+ m sync.Mutex
+}
+
+// Read waits until data is available and copies bytes
+// from the buffer into p.
+func (r *pipe) Read(p []byte) (n int, err error) {
+ r.c.L.Lock()
+ defer r.c.L.Unlock()
+ for r.b.Len() == 0 && !r.b.closed {
+ r.c.Wait()
+ }
+ return r.b.Read(p)
+}
+
+// Write copies bytes from p into the buffer and wakes a reader.
+// It is an error to write more data than the buffer can hold.
+func (w *pipe) Write(p []byte) (n int, err error) {
+ w.c.L.Lock()
+ defer w.c.L.Unlock()
+ defer w.c.Signal()
+ return w.b.Write(p)
+}
+
+func (c *pipe) Close(err error) {
+ c.c.L.Lock()
+ defer c.c.L.Unlock()
+ defer c.c.Signal()
+ c.b.Close(err)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/pipe_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/pipe_test.go
new file mode 100644
index 000000000..10c4c327c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/pipe_test.go
@@ -0,0 +1,24 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "errors"
+ "testing"
+)
+
+func TestPipeClose(t *testing.T) {
+ var p pipe
+ p.c.L = &p.m
+ a := errors.New("a")
+ b := errors.New("b")
+ p.Close(a)
+ p.Close(b)
+ _, err := p.Read(make([]byte, 1))
+ if err != a {
+ t.Errorf("err = %v want %v", err, a)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/priority_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/priority_test.go
new file mode 100644
index 000000000..d9648fd32
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/priority_test.go
@@ -0,0 +1,121 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "testing"
+)
+
+func TestPriority(t *testing.T) {
+ // A -> B
+ // move A's parent to B
+ streams := make(map[uint32]*stream)
+ a := &stream{
+ parent: nil,
+ weight: 16,
+ }
+ streams[1] = a
+ b := &stream{
+ parent: a,
+ weight: 16,
+ }
+ streams[2] = b
+ adjustStreamPriority(streams, 1, PriorityParam{
+ Weight: 20,
+ StreamDep: 2,
+ })
+ if a.parent != b {
+ t.Errorf("Expected A's parent to be B")
+ }
+ if a.weight != 20 {
+ t.Errorf("Expected A's weight to be 20; got %d", a.weight)
+ }
+ if b.parent != nil {
+ t.Errorf("Expected B to have no parent")
+ }
+ if b.weight != 16 {
+ t.Errorf("Expected B's weight to be 16; got %d", b.weight)
+ }
+}
+
+func TestPriorityExclusiveZero(t *testing.T) {
+ // A B and C are all children of the 0 stream.
+ // Exclusive reprioritization to any of the streams
+ // should bring the rest of the streams under the
+ // reprioritized stream
+ streams := make(map[uint32]*stream)
+ a := &stream{
+ parent: nil,
+ weight: 16,
+ }
+ streams[1] = a
+ b := &stream{
+ parent: nil,
+ weight: 16,
+ }
+ streams[2] = b
+ c := &stream{
+ parent: nil,
+ weight: 16,
+ }
+ streams[3] = c
+ adjustStreamPriority(streams, 3, PriorityParam{
+ Weight: 20,
+ StreamDep: 0,
+ Exclusive: true,
+ })
+ if a.parent != c {
+ t.Errorf("Expected A's parent to be C")
+ }
+ if a.weight != 16 {
+ t.Errorf("Expected A's weight to be 16; got %d", a.weight)
+ }
+ if b.parent != c {
+ t.Errorf("Expected B's parent to be C")
+ }
+ if b.weight != 16 {
+ t.Errorf("Expected B's weight to be 16; got %d", b.weight)
+ }
+ if c.parent != nil {
+ t.Errorf("Expected C to have no parent")
+ }
+ if c.weight != 20 {
+ t.Errorf("Expected C's weight to be 20; got %d", b.weight)
+ }
+}
+
+func TestPriorityOwnParent(t *testing.T) {
+ streams := make(map[uint32]*stream)
+ a := &stream{
+ parent: nil,
+ weight: 16,
+ }
+ streams[1] = a
+ b := &stream{
+ parent: a,
+ weight: 16,
+ }
+ streams[2] = b
+ adjustStreamPriority(streams, 1, PriorityParam{
+ Weight: 20,
+ StreamDep: 1,
+ })
+ if a.parent != nil {
+ t.Errorf("Expected A's parent to be nil")
+ }
+ if a.weight != 20 {
+ t.Errorf("Expected A's weight to be 20; got %d", a.weight)
+ }
+ if b.parent != a {
+ t.Errorf("Expected B's parent to be A")
+ }
+ if b.weight != 16 {
+ t.Errorf("Expected B's weight to be 16; got %d", b.weight)
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/server.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/server.go
new file mode 100644
index 000000000..104331bce
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/server.go
@@ -0,0 +1,1777 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+// TODO: replace all <-sc.doneServing with reads from the stream's cw
+// instead, and make sure that on close we close all open
+// streams. then remove doneServing?
+
+// TODO: finish GOAWAY support. Consider each incoming frame type and
+// whether it should be ignored during a shutdown race.
+
+// TODO: disconnect idle clients. GFE seems to do 4 minutes. make
+// configurable? or maximum number of idle clients and remove the
+// oldest?
+
+// TODO: turn off the serve goroutine when idle, so
+// an idle conn only has the readFrames goroutine active. (which could
+// also be optimized probably to pin less memory in crypto/tls). This
+// would involve tracking when the serve goroutine is active (atomic
+// int32 read/CAS probably?) and starting it up when frames arrive,
+// and shutting it down when all handlers exit. the occasional PING
+// packets could use time.AfterFunc to call sc.wakeStartServeLoop()
+// (which is a no-op if already running) and then queue the PING write
+// as normal. The serve loop would then exit in most cases (if no
+// Handlers running) and not be woken up again until the PING packet
+// returns.
+
+// TODO (maybe): add a mechanism for Handlers to going into
+// half-closed-local mode (rw.(io.Closer) test?) but not exit their
+// handler, and continue to be able to read from the
+// Request.Body. This would be a somewhat semantic change from HTTP/1
+// (or at least what we expose in net/http), so I'd probably want to
+// add it there too. For now, this package says that returning from
+// the Handler ServeHTTP function means you're both done reading and
+// done writing, without a way to stop just one or the other.
+
+package http2
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+)
+
+const (
+ prefaceTimeout = 10 * time.Second
+ firstSettingsTimeout = 2 * time.Second // should be in-flight with preface anyway
+ handlerChunkWriteSize = 4 << 10
+ defaultMaxStreams = 250 // TODO: make this 100 as the GFE seems to?
+)
+
+var (
+ errClientDisconnected = errors.New("client disconnected")
+ errClosedBody = errors.New("body closed by handler")
+ errStreamBroken = errors.New("http2: stream broken")
+)
+
+var responseWriterStatePool = sync.Pool{
+ New: func() interface{} {
+ rws := &responseWriterState{}
+ rws.bw = bufio.NewWriterSize(chunkWriter{rws}, handlerChunkWriteSize)
+ return rws
+ },
+}
+
+// Test hooks.
+var (
+ testHookOnConn func()
+ testHookGetServerConn func(*serverConn)
+ testHookOnPanicMu *sync.Mutex // nil except in tests
+ testHookOnPanic func(sc *serverConn, panicVal interface{}) (rePanic bool)
+)
+
+// Server is an HTTP/2 server.
+type Server struct {
+ // MaxHandlers limits the number of http.Handler ServeHTTP goroutines
+ // which may run at a time over all connections.
+ // Negative or zero no limit.
+ // TODO: implement
+ MaxHandlers int
+
+ // MaxConcurrentStreams optionally specifies the number of
+ // concurrent streams that each client may have open at a
+ // time. This is unrelated to the number of http.Handler goroutines
+ // which may be active globally, which is MaxHandlers.
+ // If zero, MaxConcurrentStreams defaults to at least 100, per
+ // the HTTP/2 spec's recommendations.
+ MaxConcurrentStreams uint32
+
+ // MaxReadFrameSize optionally specifies the largest frame
+ // this server is willing to read. A valid value is between
+ // 16k and 16M, inclusive. If zero or otherwise invalid, a
+ // default value is used.
+ MaxReadFrameSize uint32
+
+ // PermitProhibitedCipherSuites, if true, permits the use of
+ // cipher suites prohibited by the HTTP/2 spec.
+ PermitProhibitedCipherSuites bool
+}
+
+func (s *Server) maxReadFrameSize() uint32 {
+ if v := s.MaxReadFrameSize; v >= minMaxFrameSize && v <= maxFrameSize {
+ return v
+ }
+ return defaultMaxReadFrameSize
+}
+
+func (s *Server) maxConcurrentStreams() uint32 {
+ if v := s.MaxConcurrentStreams; v > 0 {
+ return v
+ }
+ return defaultMaxStreams
+}
+
+// ConfigureServer adds HTTP/2 support to a net/http Server.
+//
+// The configuration conf may be nil.
+//
+// ConfigureServer must be called before s begins serving.
+func ConfigureServer(s *http.Server, conf *Server) {
+ if conf == nil {
+ conf = new(Server)
+ }
+ if s.TLSConfig == nil {
+ s.TLSConfig = new(tls.Config)
+ }
+
+ // Note: not setting MinVersion to tls.VersionTLS12,
+ // as we don't want to interfere with HTTP/1.1 traffic
+ // on the user's server. We enforce TLS 1.2 later once
+ // we accept a connection. Ideally this should be done
+ // during next-proto selection, but using TLS <1.2 with
+ // HTTP/2 is still the client's bug.
+
+ // Be sure we advertise tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ // at least.
+ // TODO: enable PreferServerCipherSuites?
+ if s.TLSConfig.CipherSuites != nil {
+ const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ haveRequired := false
+ for _, v := range s.TLSConfig.CipherSuites {
+ if v == requiredCipher {
+ haveRequired = true
+ break
+ }
+ }
+ if !haveRequired {
+ s.TLSConfig.CipherSuites = append(s.TLSConfig.CipherSuites, requiredCipher)
+ }
+ }
+
+ haveNPN := false
+ for _, p := range s.TLSConfig.NextProtos {
+ if p == NextProtoTLS {
+ haveNPN = true
+ break
+ }
+ }
+ if !haveNPN {
+ s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
+ }
+ // h2-14 is temporary (as of 2015-03-05) while we wait for all browsers
+ // to switch to "h2".
+ s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2-14")
+
+ if s.TLSNextProto == nil {
+ s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
+ }
+ protoHandler := func(hs *http.Server, c *tls.Conn, h http.Handler) {
+ if testHookOnConn != nil {
+ testHookOnConn()
+ }
+ conf.handleConn(hs, c, h)
+ }
+ s.TLSNextProto[NextProtoTLS] = protoHandler
+ s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
+}
+
+func (srv *Server) handleConn(hs *http.Server, c net.Conn, h http.Handler) {
+ sc := &serverConn{
+ srv: srv,
+ hs: hs,
+ conn: c,
+ remoteAddrStr: c.RemoteAddr().String(),
+ bw: newBufferedWriter(c),
+ handler: h,
+ streams: make(map[uint32]*stream),
+ readFrameCh: make(chan frameAndGate),
+ readFrameErrCh: make(chan error, 1), // must be buffered for 1
+ wantWriteFrameCh: make(chan frameWriteMsg, 8),
+ wroteFrameCh: make(chan struct{}, 1), // buffered; one send in reading goroutine
+ bodyReadCh: make(chan bodyReadMsg), // buffering doesn't matter either way
+ doneServing: make(chan struct{}),
+ advMaxStreams: srv.maxConcurrentStreams(),
+ writeSched: writeScheduler{
+ maxFrameSize: initialMaxFrameSize,
+ },
+ initialWindowSize: initialWindowSize,
+ headerTableSize: initialHeaderTableSize,
+ serveG: newGoroutineLock(),
+ pushEnabled: true,
+ }
+ sc.flow.add(initialWindowSize)
+ sc.inflow.add(initialWindowSize)
+ sc.hpackEncoder = hpack.NewEncoder(&sc.headerWriteBuf)
+ sc.hpackDecoder = hpack.NewDecoder(initialHeaderTableSize, sc.onNewHeaderField)
+
+ fr := NewFramer(sc.bw, c)
+ fr.SetMaxReadFrameSize(srv.maxReadFrameSize())
+ sc.framer = fr
+
+ if tc, ok := c.(*tls.Conn); ok {
+ sc.tlsState = new(tls.ConnectionState)
+ *sc.tlsState = tc.ConnectionState()
+ // 9.2 Use of TLS Features
+ // An implementation of HTTP/2 over TLS MUST use TLS
+ // 1.2 or higher with the restrictions on feature set
+ // and cipher suite described in this section. Due to
+ // implementation limitations, it might not be
+ // possible to fail TLS negotiation. An endpoint MUST
+ // immediately terminate an HTTP/2 connection that
+ // does not meet the TLS requirements described in
+ // this section with a connection error (Section
+ // 5.4.1) of type INADEQUATE_SECURITY.
+ if sc.tlsState.Version < tls.VersionTLS12 {
+ sc.rejectConn(ErrCodeInadequateSecurity, "TLS version too low")
+ return
+ }
+
+ if sc.tlsState.ServerName == "" {
+ // Client must use SNI, but we don't enforce that anymore,
+ // since it was causing problems when connecting to bare IP
+ // addresses during development.
+ //
+ // TODO: optionally enforce? Or enforce at the time we receive
+ // a new request, and verify the the ServerName matches the :authority?
+ // But that precludes proxy situations, perhaps.
+ //
+ // So for now, do nothing here again.
+ }
+
+ if !srv.PermitProhibitedCipherSuites && isBadCipher(sc.tlsState.CipherSuite) {
+ // "Endpoints MAY choose to generate a connection error
+ // (Section 5.4.1) of type INADEQUATE_SECURITY if one of
+ // the prohibited cipher suites are negotiated."
+ //
+ // We choose that. In my opinion, the spec is weak
+ // here. It also says both parties must support at least
+ // TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 so there's no
+ // excuses here. If we really must, we could allow an
+ // "AllowInsecureWeakCiphers" option on the server later.
+ // Let's see how it plays out first.
+ sc.rejectConn(ErrCodeInadequateSecurity, fmt.Sprintf("Prohibited TLS 1.2 Cipher Suite: %x", sc.tlsState.CipherSuite))
+ return
+ }
+ }
+
+ if hook := testHookGetServerConn; hook != nil {
+ hook(sc)
+ }
+ sc.serve()
+}
+
+// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
+func isBadCipher(cipher uint16) bool {
+ switch cipher {
+ case tls.TLS_RSA_WITH_RC4_128_SHA,
+ tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ // Reject cipher suites from Appendix A.
+ // "This list includes those cipher suites that do not
+ // offer an ephemeral key exchange and those that are
+ // based on the TLS null, stream or block cipher type"
+ return true
+ default:
+ return false
+ }
+}
+
+func (sc *serverConn) rejectConn(err ErrCode, debug string) {
+ log.Printf("REJECTING conn: %v, %s", err, debug)
+ // ignoring errors. hanging up anyway.
+ sc.framer.WriteGoAway(0, err, []byte(debug))
+ sc.bw.Flush()
+ sc.conn.Close()
+}
+
+// frameAndGates coordinates the readFrames and serve
+// goroutines. Because the Framer interface only permits the most
+// recently-read Frame from being accessed, the readFrames goroutine
+// blocks until it has a frame, passes it to serve, and then waits for
+// serve to be done with it before reading the next one.
+type frameAndGate struct {
+ f Frame
+ g gate
+}
+
+type serverConn struct {
+ // Immutable:
+ srv *Server
+ hs *http.Server
+ conn net.Conn
+ bw *bufferedWriter // writing to conn
+ handler http.Handler
+ framer *Framer
+ hpackDecoder *hpack.Decoder
+ doneServing chan struct{} // closed when serverConn.serve ends
+ readFrameCh chan frameAndGate // written by serverConn.readFrames
+ readFrameErrCh chan error
+ wantWriteFrameCh chan frameWriteMsg // from handlers -> serve
+ wroteFrameCh chan struct{} // from writeFrameAsync -> serve, tickles more frame writes
+ bodyReadCh chan bodyReadMsg // from handlers -> serve
+ testHookCh chan func() // code to run on the serve loop
+ flow flow // conn-wide (not stream-specific) outbound flow control
+ inflow flow // conn-wide inbound flow control
+ tlsState *tls.ConnectionState // shared by all handlers, like net/http
+ remoteAddrStr string
+
+ // Everything following is owned by the serve loop; use serveG.check():
+ serveG goroutineLock // used to verify funcs are on serve()
+ pushEnabled bool
+ sawFirstSettings bool // got the initial SETTINGS frame after the preface
+ needToSendSettingsAck bool
+ unackedSettings int // how many SETTINGS have we sent without ACKs?
+ clientMaxStreams uint32 // SETTINGS_MAX_CONCURRENT_STREAMS from client (our PUSH_PROMISE limit)
+ advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
+ curOpenStreams uint32 // client's number of open streams
+ maxStreamID uint32 // max ever seen
+ streams map[uint32]*stream
+ initialWindowSize int32
+ headerTableSize uint32
+ maxHeaderListSize uint32 // zero means unknown (default)
+ canonHeader map[string]string // http2-lower-case -> Go-Canonical-Case
+ req requestParam // non-zero while reading request headers
+ writingFrame bool // started write goroutine but haven't heard back on wroteFrameCh
+ needsFrameFlush bool // last frame write wasn't a flush
+ writeSched writeScheduler
+ inGoAway bool // we've started to or sent GOAWAY
+ needToSendGoAway bool // we need to schedule a GOAWAY frame write
+ goAwayCode ErrCode
+ shutdownTimerCh <-chan time.Time // nil until used
+ shutdownTimer *time.Timer // nil until used
+
+ // Owned by the writeFrameAsync goroutine:
+ headerWriteBuf bytes.Buffer
+ hpackEncoder *hpack.Encoder
+}
+
+// requestParam is the state of the next request, initialized over
+// potentially several frames HEADERS + zero or more CONTINUATION
+// frames.
+type requestParam struct {
+ // stream is non-nil if we're reading (HEADER or CONTINUATION)
+ // frames for a request (but not DATA).
+ stream *stream
+ header http.Header
+ method, path string
+ scheme, authority string
+ sawRegularHeader bool // saw a non-pseudo header already
+ invalidHeader bool // an invalid header was seen
+}
+
+// stream represents a stream. This is the minimal metadata needed by
+// the serve goroutine. Most of the actual stream state is owned by
+// the http.Handler's goroutine in the responseWriter. Because the
+// responseWriter's responseWriterState is recycled at the end of a
+// handler, this struct intentionally has no pointer to the
+// *responseWriter{,State} itself, as the Handler ending nils out the
+// responseWriter's state field.
+type stream struct {
+ // immutable:
+ id uint32
+ body *pipe // non-nil if expecting DATA frames
+ cw closeWaiter // closed wait stream transitions to closed state
+
+ // owned by serverConn's serve loop:
+ bodyBytes int64 // body bytes seen so far
+ declBodyBytes int64 // or -1 if undeclared
+ flow flow // limits writing from Handler to client
+ inflow flow // what the client is allowed to POST/etc to us
+ parent *stream // or nil
+ weight uint8
+ state streamState
+ sentReset bool // only true once detached from streams map
+ gotReset bool // only true once detacted from streams map
+}
+
+func (sc *serverConn) Framer() *Framer { return sc.framer }
+func (sc *serverConn) CloseConn() error { return sc.conn.Close() }
+func (sc *serverConn) Flush() error { return sc.bw.Flush() }
+func (sc *serverConn) HeaderEncoder() (*hpack.Encoder, *bytes.Buffer) {
+ return sc.hpackEncoder, &sc.headerWriteBuf
+}
+
+func (sc *serverConn) state(streamID uint32) (streamState, *stream) {
+ sc.serveG.check()
+ // http://http2.github.io/http2-spec/#rfc.section.5.1
+ if st, ok := sc.streams[streamID]; ok {
+ return st.state, st
+ }
+ // "The first use of a new stream identifier implicitly closes all
+ // streams in the "idle" state that might have been initiated by
+ // that peer with a lower-valued stream identifier. For example, if
+ // a client sends a HEADERS frame on stream 7 without ever sending a
+ // frame on stream 5, then stream 5 transitions to the "closed"
+ // state when the first frame for stream 7 is sent or received."
+ if streamID <= sc.maxStreamID {
+ return stateClosed, nil
+ }
+ return stateIdle, nil
+}
+
+func (sc *serverConn) vlogf(format string, args ...interface{}) {
+ if VerboseLogs {
+ sc.logf(format, args...)
+ }
+}
+
+func (sc *serverConn) logf(format string, args ...interface{}) {
+ if lg := sc.hs.ErrorLog; lg != nil {
+ lg.Printf(format, args...)
+ } else {
+ log.Printf(format, args...)
+ }
+}
+
+func (sc *serverConn) condlogf(err error, format string, args ...interface{}) {
+ if err == nil {
+ return
+ }
+ str := err.Error()
+ if err == io.EOF || strings.Contains(str, "use of closed network connection") {
+ // Boring, expected errors.
+ sc.vlogf(format, args...)
+ } else {
+ sc.logf(format, args...)
+ }
+}
+
+func (sc *serverConn) onNewHeaderField(f hpack.HeaderField) {
+ sc.serveG.check()
+ sc.vlogf("got header field %+v", f)
+ switch {
+ case !validHeader(f.Name):
+ sc.req.invalidHeader = true
+ case strings.HasPrefix(f.Name, ":"):
+ if sc.req.sawRegularHeader {
+ sc.logf("pseudo-header after regular header")
+ sc.req.invalidHeader = true
+ return
+ }
+ var dst *string
+ switch f.Name {
+ case ":method":
+ dst = &sc.req.method
+ case ":path":
+ dst = &sc.req.path
+ case ":scheme":
+ dst = &sc.req.scheme
+ case ":authority":
+ dst = &sc.req.authority
+ default:
+ // 8.1.2.1 Pseudo-Header Fields
+ // "Endpoints MUST treat a request or response
+ // that contains undefined or invalid
+ // pseudo-header fields as malformed (Section
+ // 8.1.2.6)."
+ sc.logf("invalid pseudo-header %q", f.Name)
+ sc.req.invalidHeader = true
+ return
+ }
+ if *dst != "" {
+ sc.logf("duplicate pseudo-header %q sent", f.Name)
+ sc.req.invalidHeader = true
+ return
+ }
+ *dst = f.Value
+ case f.Name == "cookie":
+ sc.req.sawRegularHeader = true
+ if s, ok := sc.req.header["Cookie"]; ok && len(s) == 1 {
+ s[0] = s[0] + "; " + f.Value
+ } else {
+ sc.req.header.Add("Cookie", f.Value)
+ }
+ default:
+ sc.req.sawRegularHeader = true
+ sc.req.header.Add(sc.canonicalHeader(f.Name), f.Value)
+ }
+}
+
+func (sc *serverConn) canonicalHeader(v string) string {
+ sc.serveG.check()
+ cv, ok := commonCanonHeader[v]
+ if ok {
+ return cv
+ }
+ cv, ok = sc.canonHeader[v]
+ if ok {
+ return cv
+ }
+ if sc.canonHeader == nil {
+ sc.canonHeader = make(map[string]string)
+ }
+ cv = http.CanonicalHeaderKey(v)
+ sc.canonHeader[v] = cv
+ return cv
+}
+
+// readFrames is the loop that reads incoming frames.
+// It's run on its own goroutine.
+func (sc *serverConn) readFrames() {
+ g := make(gate, 1)
+ for {
+ f, err := sc.framer.ReadFrame()
+ if err != nil {
+ sc.readFrameErrCh <- err
+ close(sc.readFrameCh)
+ return
+ }
+ sc.readFrameCh <- frameAndGate{f, g}
+ // We can't read another frame until this one is
+ // processed, as the ReadFrame interface doesn't copy
+ // memory. The Frame accessor methods access the last
+ // frame's (shared) buffer. So we wait for the
+ // serve goroutine to tell us it's done:
+ g.Wait()
+ }
+}
+
+// writeFrameAsync runs in its own goroutine and writes a single frame
+// and then reports when it's done.
+// At most one goroutine can be running writeFrameAsync at a time per
+// serverConn.
+func (sc *serverConn) writeFrameAsync(wm frameWriteMsg) {
+ err := wm.write.writeFrame(sc)
+ if ch := wm.done; ch != nil {
+ select {
+ case ch <- err:
+ default:
+ panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wm.write))
+ }
+ }
+ sc.wroteFrameCh <- struct{}{} // tickle frame selection scheduler
+}
+
+func (sc *serverConn) closeAllStreamsOnConnClose() {
+ sc.serveG.check()
+ for _, st := range sc.streams {
+ sc.closeStream(st, errClientDisconnected)
+ }
+}
+
+func (sc *serverConn) stopShutdownTimer() {
+ sc.serveG.check()
+ if t := sc.shutdownTimer; t != nil {
+ t.Stop()
+ }
+}
+
+func (sc *serverConn) notePanic() {
+ if testHookOnPanicMu != nil {
+ testHookOnPanicMu.Lock()
+ defer testHookOnPanicMu.Unlock()
+ }
+ if testHookOnPanic != nil {
+ if e := recover(); e != nil {
+ if testHookOnPanic(sc, e) {
+ panic(e)
+ }
+ }
+ }
+}
+
+func (sc *serverConn) serve() {
+ sc.serveG.check()
+ defer sc.notePanic()
+ defer sc.conn.Close()
+ defer sc.closeAllStreamsOnConnClose()
+ defer sc.stopShutdownTimer()
+ defer close(sc.doneServing) // unblocks handlers trying to send
+
+ sc.vlogf("HTTP/2 connection from %v on %p", sc.conn.RemoteAddr(), sc.hs)
+
+ sc.writeFrame(frameWriteMsg{
+ write: writeSettings{
+ {SettingMaxFrameSize, sc.srv.maxReadFrameSize()},
+ {SettingMaxConcurrentStreams, sc.advMaxStreams},
+
+ // TODO: more actual settings, notably
+ // SettingInitialWindowSize, but then we also
+ // want to bump up the conn window size the
+ // same amount here right after the settings
+ },
+ })
+ sc.unackedSettings++
+
+ if err := sc.readPreface(); err != nil {
+ sc.condlogf(err, "error reading preface from client %v: %v", sc.conn.RemoteAddr(), err)
+ return
+ }
+
+ go sc.readFrames() // closed by defer sc.conn.Close above
+
+ settingsTimer := time.NewTimer(firstSettingsTimeout)
+ for {
+ select {
+ case wm := <-sc.wantWriteFrameCh:
+ sc.writeFrame(wm)
+ case <-sc.wroteFrameCh:
+ sc.writingFrame = false
+ sc.scheduleFrameWrite()
+ case fg, ok := <-sc.readFrameCh:
+ if !ok {
+ sc.readFrameCh = nil
+ }
+ if !sc.processFrameFromReader(fg, ok) {
+ return
+ }
+ if settingsTimer.C != nil {
+ settingsTimer.Stop()
+ settingsTimer.C = nil
+ }
+ case m := <-sc.bodyReadCh:
+ sc.noteBodyRead(m.st, m.n)
+ case <-settingsTimer.C:
+ sc.logf("timeout waiting for SETTINGS frames from %v", sc.conn.RemoteAddr())
+ return
+ case <-sc.shutdownTimerCh:
+ sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
+ return
+ case fn := <-sc.testHookCh:
+ fn()
+ }
+ }
+}
+
+// readPreface reads the ClientPreface greeting from the peer
+// or returns an error on timeout or an invalid greeting.
+func (sc *serverConn) readPreface() error {
+ errc := make(chan error, 1)
+ go func() {
+ // Read the client preface
+ buf := make([]byte, len(ClientPreface))
+ if _, err := io.ReadFull(sc.conn, buf); err != nil {
+ errc <- err
+ } else if !bytes.Equal(buf, clientPreface) {
+ errc <- fmt.Errorf("bogus greeting %q", buf)
+ } else {
+ errc <- nil
+ }
+ }()
+ timer := time.NewTimer(prefaceTimeout) // TODO: configurable on *Server?
+ defer timer.Stop()
+ select {
+ case <-timer.C:
+ return errors.New("timeout waiting for client preface")
+ case err := <-errc:
+ if err == nil {
+ sc.vlogf("client %v said hello", sc.conn.RemoteAddr())
+ }
+ return err
+ }
+}
+
+// writeDataFromHandler writes the data described in req to stream.id.
+//
+// The provided ch is used to avoid allocating new channels for each
+// write operation. It's expected that the caller reuses writeData and ch
+// over time.
+//
+// The flow control currently happens in the Handler where it waits
+// for 1 or more bytes to be available to then write here. So at this
+// point we know that we have flow control. But this might have to
+// change when priority is implemented, so the serve goroutine knows
+// the total amount of bytes waiting to be sent and can can have more
+// scheduling decisions available.
+func (sc *serverConn) writeDataFromHandler(stream *stream, writeData *writeData, ch chan error) error {
+ sc.writeFrameFromHandler(frameWriteMsg{
+ write: writeData,
+ stream: stream,
+ done: ch,
+ })
+ select {
+ case err := <-ch:
+ return err
+ case <-sc.doneServing:
+ return errClientDisconnected
+ case <-stream.cw:
+ return errStreamBroken
+ }
+}
+
+// writeFrameFromHandler sends wm to sc.wantWriteFrameCh, but aborts
+// if the connection has gone away.
+//
+// This must not be run from the serve goroutine itself, else it might
+// deadlock writing to sc.wantWriteFrameCh (which is only mildly
+// buffered and is read by serve itself). If you're on the serve
+// goroutine, call writeFrame instead.
+func (sc *serverConn) writeFrameFromHandler(wm frameWriteMsg) {
+ sc.serveG.checkNotOn() // NOT
+ select {
+ case sc.wantWriteFrameCh <- wm:
+ case <-sc.doneServing:
+ // Client has closed their connection to the server.
+ }
+}
+
+// writeFrame schedules a frame to write and sends it if there's nothing
+// already being written.
+//
+// There is no pushback here (the serve goroutine never blocks). It's
+// the http.Handlers that block, waiting for their previous frames to
+// make it onto the wire
+//
+// If you're not on the serve goroutine, use writeFrameFromHandler instead.
+func (sc *serverConn) writeFrame(wm frameWriteMsg) {
+ sc.serveG.check()
+ sc.writeSched.add(wm)
+ sc.scheduleFrameWrite()
+}
+
+// startFrameWrite starts a goroutine to write wm (in a separate
+// goroutine since that might block on the network), and updates the
+// serve goroutine's state about the world, updated from info in wm.
+func (sc *serverConn) startFrameWrite(wm frameWriteMsg) {
+ sc.serveG.check()
+ if sc.writingFrame {
+ panic("internal error: can only be writing one frame at a time")
+ }
+
+ st := wm.stream
+ if st != nil {
+ switch st.state {
+ case stateHalfClosedLocal:
+ panic("internal error: attempt to send frame on half-closed-local stream")
+ case stateClosed:
+ if st.sentReset || st.gotReset {
+ // Skip this frame. But fake the frame write to reschedule:
+ sc.wroteFrameCh <- struct{}{}
+ return
+ }
+ panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wm))
+ }
+ }
+
+ sc.writingFrame = true
+ sc.needsFrameFlush = true
+ if endsStream(wm.write) {
+ if st == nil {
+ panic("internal error: expecting non-nil stream")
+ }
+ switch st.state {
+ case stateOpen:
+ // Here we would go to stateHalfClosedLocal in
+ // theory, but since our handler is done and
+ // the net/http package provides no mechanism
+ // for finishing writing to a ResponseWriter
+ // while still reading data (see possible TODO
+ // at top of this file), we go into closed
+ // state here anyway, after telling the peer
+ // we're hanging up on them.
+ st.state = stateHalfClosedLocal // won't last long, but necessary for closeStream via resetStream
+ errCancel := StreamError{st.id, ErrCodeCancel}
+ sc.resetStream(errCancel)
+ case stateHalfClosedRemote:
+ sc.closeStream(st, nil)
+ }
+ }
+ go sc.writeFrameAsync(wm)
+}
+
+// scheduleFrameWrite tickles the frame writing scheduler.
+//
+// If a frame is already being written, nothing happens. This will be called again
+// when the frame is done being written.
+//
+// If a frame isn't being written we need to send one, the best frame
+// to send is selected, preferring first things that aren't
+// stream-specific (e.g. ACKing settings), and then finding the
+// highest priority stream.
+//
+// If a frame isn't being written and there's nothing else to send, we
+// flush the write buffer.
+func (sc *serverConn) scheduleFrameWrite() {
+ sc.serveG.check()
+ if sc.writingFrame {
+ return
+ }
+ if sc.needToSendGoAway {
+ sc.needToSendGoAway = false
+ sc.startFrameWrite(frameWriteMsg{
+ write: &writeGoAway{
+ maxStreamID: sc.maxStreamID,
+ code: sc.goAwayCode,
+ },
+ })
+ return
+ }
+ if sc.needToSendSettingsAck {
+ sc.needToSendSettingsAck = false
+ sc.startFrameWrite(frameWriteMsg{write: writeSettingsAck{}})
+ return
+ }
+ if !sc.inGoAway {
+ if wm, ok := sc.writeSched.take(); ok {
+ sc.startFrameWrite(wm)
+ return
+ }
+ }
+ if sc.needsFrameFlush {
+ sc.startFrameWrite(frameWriteMsg{write: flushFrameWriter{}})
+ sc.needsFrameFlush = false // after startFrameWrite, since it sets this true
+ return
+ }
+}
+
+func (sc *serverConn) goAway(code ErrCode) {
+ sc.serveG.check()
+ if sc.inGoAway {
+ return
+ }
+ if code != ErrCodeNo {
+ sc.shutDownIn(250 * time.Millisecond)
+ } else {
+ // TODO: configurable
+ sc.shutDownIn(1 * time.Second)
+ }
+ sc.inGoAway = true
+ sc.needToSendGoAway = true
+ sc.goAwayCode = code
+ sc.scheduleFrameWrite()
+}
+
+func (sc *serverConn) shutDownIn(d time.Duration) {
+ sc.serveG.check()
+ sc.shutdownTimer = time.NewTimer(d)
+ sc.shutdownTimerCh = sc.shutdownTimer.C
+}
+
+func (sc *serverConn) resetStream(se StreamError) {
+ sc.serveG.check()
+ sc.writeFrame(frameWriteMsg{write: se})
+ if st, ok := sc.streams[se.StreamID]; ok {
+ st.sentReset = true
+ sc.closeStream(st, se)
+ }
+}
+
+// curHeaderStreamID returns the stream ID of the header block we're
+// currently in the middle of reading. If this returns non-zero, the
+// next frame must be a CONTINUATION with this stream id.
+func (sc *serverConn) curHeaderStreamID() uint32 {
+ sc.serveG.check()
+ st := sc.req.stream
+ if st == nil {
+ return 0
+ }
+ return st.id
+}
+
+// processFrameFromReader processes the serve loop's read from readFrameCh from the
+// frame-reading goroutine.
+// processFrameFromReader returns whether the connection should be kept open.
+func (sc *serverConn) processFrameFromReader(fg frameAndGate, fgValid bool) bool {
+ sc.serveG.check()
+ var clientGone bool
+ var err error
+ if !fgValid {
+ err = <-sc.readFrameErrCh
+ if err == ErrFrameTooLarge {
+ sc.goAway(ErrCodeFrameSize)
+ return true // goAway will close the loop
+ }
+ clientGone = err == io.EOF || strings.Contains(err.Error(), "use of closed network connection")
+ if clientGone {
+ // TODO: could we also get into this state if
+ // the peer does a half close
+ // (e.g. CloseWrite) because they're done
+ // sending frames but they're still wanting
+ // our open replies? Investigate.
+ // TODO: add CloseWrite to crypto/tls.Conn first
+ // so we have a way to test this? I suppose
+ // just for testing we could have a non-TLS mode.
+ return false
+ }
+ }
+
+ if fgValid {
+ f := fg.f
+ sc.vlogf("got %v: %#v", f.Header(), f)
+ err = sc.processFrame(f)
+ fg.g.Done() // unblock the readFrames goroutine
+ if err == nil {
+ return true
+ }
+ }
+
+ switch ev := err.(type) {
+ case StreamError:
+ sc.resetStream(ev)
+ return true
+ case goAwayFlowError:
+ sc.goAway(ErrCodeFlowControl)
+ return true
+ case ConnectionError:
+ sc.logf("%v: %v", sc.conn.RemoteAddr(), ev)
+ sc.goAway(ErrCode(ev))
+ return true // goAway will handle shutdown
+ default:
+ if !fgValid {
+ sc.logf("disconnecting; error reading frame from client %s: %v", sc.conn.RemoteAddr(), err)
+ } else {
+ sc.logf("disconnection due to other error: %v", err)
+ }
+ }
+ return false
+}
+
+func (sc *serverConn) processFrame(f Frame) error {
+ sc.serveG.check()
+
+ // First frame received must be SETTINGS.
+ if !sc.sawFirstSettings {
+ if _, ok := f.(*SettingsFrame); !ok {
+ return ConnectionError(ErrCodeProtocol)
+ }
+ sc.sawFirstSettings = true
+ }
+
+ if s := sc.curHeaderStreamID(); s != 0 {
+ if cf, ok := f.(*ContinuationFrame); !ok {
+ return ConnectionError(ErrCodeProtocol)
+ } else if cf.Header().StreamID != s {
+ return ConnectionError(ErrCodeProtocol)
+ }
+ }
+
+ switch f := f.(type) {
+ case *SettingsFrame:
+ return sc.processSettings(f)
+ case *HeadersFrame:
+ return sc.processHeaders(f)
+ case *ContinuationFrame:
+ return sc.processContinuation(f)
+ case *WindowUpdateFrame:
+ return sc.processWindowUpdate(f)
+ case *PingFrame:
+ return sc.processPing(f)
+ case *DataFrame:
+ return sc.processData(f)
+ case *RSTStreamFrame:
+ return sc.processResetStream(f)
+ case *PriorityFrame:
+ return sc.processPriority(f)
+ case *PushPromiseFrame:
+ // A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
+ // frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
+ return ConnectionError(ErrCodeProtocol)
+ default:
+ log.Printf("Ignoring frame: %v", f.Header())
+ return nil
+ }
+}
+
+func (sc *serverConn) processPing(f *PingFrame) error {
+ sc.serveG.check()
+ if f.Flags.Has(FlagSettingsAck) {
+ // 6.7 PING: " An endpoint MUST NOT respond to PING frames
+ // containing this flag."
+ return nil
+ }
+ if f.StreamID != 0 {
+ // "PING frames are not associated with any individual
+ // stream. If a PING frame is received with a stream
+ // identifier field value other than 0x0, the recipient MUST
+ // respond with a connection error (Section 5.4.1) of type
+ // PROTOCOL_ERROR."
+ return ConnectionError(ErrCodeProtocol)
+ }
+ sc.writeFrame(frameWriteMsg{write: writePingAck{f}})
+ return nil
+}
+
+func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
+ sc.serveG.check()
+ switch {
+ case f.StreamID != 0: // stream-level flow control
+ st := sc.streams[f.StreamID]
+ if st == nil {
+ // "WINDOW_UPDATE can be sent by a peer that has sent a
+ // frame bearing the END_STREAM flag. This means that a
+ // receiver could receive a WINDOW_UPDATE frame on a "half
+ // closed (remote)" or "closed" stream. A receiver MUST
+ // NOT treat this as an error, see Section 5.1."
+ return nil
+ }
+ if !st.flow.add(int32(f.Increment)) {
+ return StreamError{f.StreamID, ErrCodeFlowControl}
+ }
+ default: // connection-level flow control
+ if !sc.flow.add(int32(f.Increment)) {
+ return goAwayFlowError{}
+ }
+ }
+ sc.scheduleFrameWrite()
+ return nil
+}
+
+func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
+ sc.serveG.check()
+
+ state, st := sc.state(f.StreamID)
+ if state == stateIdle {
+ // 6.4 "RST_STREAM frames MUST NOT be sent for a
+ // stream in the "idle" state. If a RST_STREAM frame
+ // identifying an idle stream is received, the
+ // recipient MUST treat this as a connection error
+ // (Section 5.4.1) of type PROTOCOL_ERROR.
+ return ConnectionError(ErrCodeProtocol)
+ }
+ if st != nil {
+ st.gotReset = true
+ sc.closeStream(st, StreamError{f.StreamID, f.ErrCode})
+ }
+ return nil
+}
+
+func (sc *serverConn) closeStream(st *stream, err error) {
+ sc.serveG.check()
+ if st.state == stateIdle || st.state == stateClosed {
+ panic(fmt.Sprintf("invariant; can't close stream in state %v", st.state))
+ }
+ st.state = stateClosed
+ sc.curOpenStreams--
+ delete(sc.streams, st.id)
+ if p := st.body; p != nil {
+ p.Close(err)
+ }
+ st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
+ sc.writeSched.forgetStream(st.id)
+}
+
+func (sc *serverConn) processSettings(f *SettingsFrame) error {
+ sc.serveG.check()
+ if f.IsAck() {
+ sc.unackedSettings--
+ if sc.unackedSettings < 0 {
+ // Why is the peer ACKing settings we never sent?
+ // The spec doesn't mention this case, but
+ // hang up on them anyway.
+ return ConnectionError(ErrCodeProtocol)
+ }
+ return nil
+ }
+ if err := f.ForeachSetting(sc.processSetting); err != nil {
+ return err
+ }
+ sc.needToSendSettingsAck = true
+ sc.scheduleFrameWrite()
+ return nil
+}
+
+func (sc *serverConn) processSetting(s Setting) error {
+ sc.serveG.check()
+ if err := s.Valid(); err != nil {
+ return err
+ }
+ sc.vlogf("processing setting %v", s)
+ switch s.ID {
+ case SettingHeaderTableSize:
+ sc.headerTableSize = s.Val
+ sc.hpackEncoder.SetMaxDynamicTableSize(s.Val)
+ case SettingEnablePush:
+ sc.pushEnabled = s.Val != 0
+ case SettingMaxConcurrentStreams:
+ sc.clientMaxStreams = s.Val
+ case SettingInitialWindowSize:
+ return sc.processSettingInitialWindowSize(s.Val)
+ case SettingMaxFrameSize:
+ sc.writeSched.maxFrameSize = s.Val
+ case SettingMaxHeaderListSize:
+ sc.maxHeaderListSize = s.Val
+ default:
+ // Unknown setting: "An endpoint that receives a SETTINGS
+ // frame with any unknown or unsupported identifier MUST
+ // ignore that setting."
+ }
+ return nil
+}
+
+func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
+ sc.serveG.check()
+ // Note: val already validated to be within range by
+ // processSetting's Valid call.
+
+ // "A SETTINGS frame can alter the initial flow control window
+ // size for all current streams. When the value of
+ // SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST
+ // adjust the size of all stream flow control windows that it
+ // maintains by the difference between the new value and the
+ // old value."
+ old := sc.initialWindowSize
+ sc.initialWindowSize = int32(val)
+ growth := sc.initialWindowSize - old // may be negative
+ for _, st := range sc.streams {
+ if !st.flow.add(growth) {
+ // 6.9.2 Initial Flow Control Window Size
+ // "An endpoint MUST treat a change to
+ // SETTINGS_INITIAL_WINDOW_SIZE that causes any flow
+ // control window to exceed the maximum size as a
+ // connection error (Section 5.4.1) of type
+ // FLOW_CONTROL_ERROR."
+ return ConnectionError(ErrCodeFlowControl)
+ }
+ }
+ return nil
+}
+
+func (sc *serverConn) processData(f *DataFrame) error {
+ sc.serveG.check()
+ // "If a DATA frame is received whose stream is not in "open"
+ // or "half closed (local)" state, the recipient MUST respond
+ // with a stream error (Section 5.4.2) of type STREAM_CLOSED."
+ id := f.Header().StreamID
+ st, ok := sc.streams[id]
+ if !ok || st.state != stateOpen {
+ // This includes sending a RST_STREAM if the stream is
+ // in stateHalfClosedLocal (which currently means that
+ // the http.Handler returned, so it's done reading &
+ // done writing). Try to stop the client from sending
+ // more DATA.
+ return StreamError{id, ErrCodeStreamClosed}
+ }
+ if st.body == nil {
+ panic("internal error: should have a body in this state")
+ }
+ data := f.Data()
+
+ // Sender sending more than they'd declared?
+ if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {
+ st.body.Close(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
+ return StreamError{id, ErrCodeStreamClosed}
+ }
+ if len(data) > 0 {
+ // Check whether the client has flow control quota.
+ if int(st.inflow.available()) < len(data) {
+ return StreamError{id, ErrCodeFlowControl}
+ }
+ st.inflow.take(int32(len(data)))
+ wrote, err := st.body.Write(data)
+ if err != nil {
+ return StreamError{id, ErrCodeStreamClosed}
+ }
+ if wrote != len(data) {
+ panic("internal error: bad Writer")
+ }
+ st.bodyBytes += int64(len(data))
+ }
+ if f.StreamEnded() {
+ if st.declBodyBytes != -1 && st.declBodyBytes != st.bodyBytes {
+ st.body.Close(fmt.Errorf("request declared a Content-Length of %d but only wrote %d bytes",
+ st.declBodyBytes, st.bodyBytes))
+ } else {
+ st.body.Close(io.EOF)
+ }
+ st.state = stateHalfClosedRemote
+ }
+ return nil
+}
+
+func (sc *serverConn) processHeaders(f *HeadersFrame) error {
+ sc.serveG.check()
+ id := f.Header().StreamID
+ if sc.inGoAway {
+ // Ignore.
+ return nil
+ }
+ // http://http2.github.io/http2-spec/#rfc.section.5.1.1
+ if id%2 != 1 || id <= sc.maxStreamID || sc.req.stream != nil {
+ // Streams initiated by a client MUST use odd-numbered
+ // stream identifiers. [...] The identifier of a newly
+ // established stream MUST be numerically greater than all
+ // streams that the initiating endpoint has opened or
+ // reserved. [...] An endpoint that receives an unexpected
+ // stream identifier MUST respond with a connection error
+ // (Section 5.4.1) of type PROTOCOL_ERROR.
+ return ConnectionError(ErrCodeProtocol)
+ }
+ if id > sc.maxStreamID {
+ sc.maxStreamID = id
+ }
+ st := &stream{
+ id: id,
+ state: stateOpen,
+ }
+ if f.StreamEnded() {
+ st.state = stateHalfClosedRemote
+ }
+ st.cw.Init()
+
+ st.flow.conn = &sc.flow // link to conn-level counter
+ st.flow.add(sc.initialWindowSize)
+ st.inflow.conn = &sc.inflow // link to conn-level counter
+ st.inflow.add(initialWindowSize) // TODO: update this when we send a higher initial window size in the initial settings
+
+ sc.streams[id] = st
+ if f.HasPriority() {
+ adjustStreamPriority(sc.streams, st.id, f.Priority)
+ }
+ sc.curOpenStreams++
+ sc.req = requestParam{
+ stream: st,
+ header: make(http.Header),
+ }
+ return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
+}
+
+func (sc *serverConn) processContinuation(f *ContinuationFrame) error {
+ sc.serveG.check()
+ st := sc.streams[f.Header().StreamID]
+ if st == nil || sc.curHeaderStreamID() != st.id {
+ return ConnectionError(ErrCodeProtocol)
+ }
+ return sc.processHeaderBlockFragment(st, f.HeaderBlockFragment(), f.HeadersEnded())
+}
+
+func (sc *serverConn) processHeaderBlockFragment(st *stream, frag []byte, end bool) error {
+ sc.serveG.check()
+ if _, err := sc.hpackDecoder.Write(frag); err != nil {
+ // TODO: convert to stream error I assume?
+ return err
+ }
+ if !end {
+ return nil
+ }
+ if err := sc.hpackDecoder.Close(); err != nil {
+ // TODO: convert to stream error I assume?
+ return err
+ }
+ defer sc.resetPendingRequest()
+ if sc.curOpenStreams > sc.advMaxStreams {
+ // "Endpoints MUST NOT exceed the limit set by their
+ // peer. An endpoint that receives a HEADERS frame
+ // that causes their advertised concurrent stream
+ // limit to be exceeded MUST treat this as a stream
+ // error (Section 5.4.2) of type PROTOCOL_ERROR or
+ // REFUSED_STREAM."
+ if sc.unackedSettings == 0 {
+ // They should know better.
+ return StreamError{st.id, ErrCodeProtocol}
+ }
+ // Assume it's a network race, where they just haven't
+ // received our last SETTINGS update. But actually
+ // this can't happen yet, because we don't yet provide
+ // a way for users to adjust server parameters at
+ // runtime.
+ return StreamError{st.id, ErrCodeRefusedStream}
+ }
+
+ rw, req, err := sc.newWriterAndRequest()
+ if err != nil {
+ return err
+ }
+ st.body = req.Body.(*requestBody).pipe // may be nil
+ st.declBodyBytes = req.ContentLength
+ go sc.runHandler(rw, req)
+ return nil
+}
+
+func (sc *serverConn) processPriority(f *PriorityFrame) error {
+ adjustStreamPriority(sc.streams, f.StreamID, f.PriorityParam)
+ return nil
+}
+
+func adjustStreamPriority(streams map[uint32]*stream, streamID uint32, priority PriorityParam) {
+ st, ok := streams[streamID]
+ if !ok {
+ // TODO: not quite correct (this streamID might
+ // already exist in the dep tree, but be closed), but
+ // close enough for now.
+ return
+ }
+ st.weight = priority.Weight
+ parent := streams[priority.StreamDep] // might be nil
+ if parent == st {
+ // if client tries to set this stream to be the parent of itself
+ // ignore and keep going
+ return
+ }
+
+ // section 5.3.3: If a stream is made dependent on one of its
+ // own dependencies, the formerly dependent stream is first
+ // moved to be dependent on the reprioritized stream's previous
+ // parent. The moved dependency retains its weight.
+ for piter := parent; piter != nil; piter = piter.parent {
+ if piter == st {
+ parent.parent = st.parent
+ break
+ }
+ }
+ st.parent = parent
+ if priority.Exclusive && (st.parent != nil || priority.StreamDep == 0) {
+ for _, openStream := range streams {
+ if openStream != st && openStream.parent == st.parent {
+ openStream.parent = st
+ }
+ }
+ }
+}
+
+// resetPendingRequest zeros out all state related to a HEADERS frame
+// and its zero or more CONTINUATION frames sent to start a new
+// request.
+func (sc *serverConn) resetPendingRequest() {
+ sc.serveG.check()
+ sc.req = requestParam{}
+}
+
+func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, error) {
+ sc.serveG.check()
+ rp := &sc.req
+ if rp.invalidHeader || rp.method == "" || rp.path == "" ||
+ (rp.scheme != "https" && rp.scheme != "http") {
+ // See 8.1.2.6 Malformed Requests and Responses:
+ //
+ // Malformed requests or responses that are detected
+ // MUST be treated as a stream error (Section 5.4.2)
+ // of type PROTOCOL_ERROR."
+ //
+ // 8.1.2.3 Request Pseudo-Header Fields
+ // "All HTTP/2 requests MUST include exactly one valid
+ // value for the :method, :scheme, and :path
+ // pseudo-header fields"
+ return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+ }
+ var tlsState *tls.ConnectionState // nil if not scheme https
+ if rp.scheme == "https" {
+ tlsState = sc.tlsState
+ }
+ authority := rp.authority
+ if authority == "" {
+ authority = rp.header.Get("Host")
+ }
+ needsContinue := rp.header.Get("Expect") == "100-continue"
+ if needsContinue {
+ rp.header.Del("Expect")
+ }
+ bodyOpen := rp.stream.state == stateOpen
+ body := &requestBody{
+ conn: sc,
+ stream: rp.stream,
+ needsContinue: needsContinue,
+ }
+ // TODO: handle asterisk '*' requests + test
+ url, err := url.ParseRequestURI(rp.path)
+ if err != nil {
+ // TODO: find the right error code?
+ return nil, nil, StreamError{rp.stream.id, ErrCodeProtocol}
+ }
+ req := &http.Request{
+ Method: rp.method,
+ URL: url,
+ RemoteAddr: sc.remoteAddrStr,
+ Header: rp.header,
+ RequestURI: rp.path,
+ Proto: "HTTP/2.0",
+ ProtoMajor: 2,
+ ProtoMinor: 0,
+ TLS: tlsState,
+ Host: authority,
+ Body: body,
+ }
+ if bodyOpen {
+ body.pipe = &pipe{
+ b: buffer{buf: make([]byte, initialWindowSize)}, // TODO: share/remove XXX
+ }
+ body.pipe.c.L = &body.pipe.m
+
+ if vv, ok := rp.header["Content-Length"]; ok {
+ req.ContentLength, _ = strconv.ParseInt(vv[0], 10, 64)
+ } else {
+ req.ContentLength = -1
+ }
+ }
+
+ rws := responseWriterStatePool.Get().(*responseWriterState)
+ bwSave := rws.bw
+ *rws = responseWriterState{} // zero all the fields
+ rws.conn = sc
+ rws.bw = bwSave
+ rws.bw.Reset(chunkWriter{rws})
+ rws.stream = rp.stream
+ rws.req = req
+ rws.body = body
+ rws.frameWriteCh = make(chan error, 1)
+
+ rw := &responseWriter{rws: rws}
+ return rw, req, nil
+}
+
+// Run on its own goroutine.
+func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request) {
+ defer rw.handlerDone()
+ // TODO: catch panics like net/http.Server
+ sc.handler.ServeHTTP(rw, req)
+}
+
+// called from handler goroutines.
+// h may be nil.
+func (sc *serverConn) writeHeaders(st *stream, headerData *writeResHeaders, tempCh chan error) {
+ sc.serveG.checkNotOn() // NOT on
+ var errc chan error
+ if headerData.h != nil {
+ // If there's a header map (which we don't own), so we have to block on
+ // waiting for this frame to be written, so an http.Flush mid-handler
+ // writes out the correct value of keys, before a handler later potentially
+ // mutates it.
+ errc = tempCh
+ }
+ sc.writeFrameFromHandler(frameWriteMsg{
+ write: headerData,
+ stream: st,
+ done: errc,
+ })
+ if errc != nil {
+ select {
+ case <-errc:
+ // Ignore. Just for synchronization.
+ // Any error will be handled in the writing goroutine.
+ case <-sc.doneServing:
+ // Client has closed the connection.
+ }
+ }
+}
+
+// called from handler goroutines.
+func (sc *serverConn) write100ContinueHeaders(st *stream) {
+ sc.writeFrameFromHandler(frameWriteMsg{
+ write: write100ContinueHeadersFrame{st.id},
+ stream: st,
+ })
+}
+
+// A bodyReadMsg tells the server loop that the http.Handler read n
+// bytes of the DATA from the client on the given stream.
+type bodyReadMsg struct {
+ st *stream
+ n int
+}
+
+// called from handler goroutines.
+// Notes that the handler for the given stream ID read n bytes of its body
+// and schedules flow control tokens to be sent.
+func (sc *serverConn) noteBodyReadFromHandler(st *stream, n int) {
+ sc.serveG.checkNotOn() // NOT on
+ sc.bodyReadCh <- bodyReadMsg{st, n}
+}
+
+func (sc *serverConn) noteBodyRead(st *stream, n int) {
+ sc.serveG.check()
+ sc.sendWindowUpdate(nil, n) // conn-level
+ if st.state != stateHalfClosedRemote && st.state != stateClosed {
+ // Don't send this WINDOW_UPDATE if the stream is closed
+ // remotely.
+ sc.sendWindowUpdate(st, n)
+ }
+}
+
+// st may be nil for conn-level
+func (sc *serverConn) sendWindowUpdate(st *stream, n int) {
+ sc.serveG.check()
+ // "The legal range for the increment to the flow control
+ // window is 1 to 2^31-1 (2,147,483,647) octets."
+ // A Go Read call on 64-bit machines could in theory read
+ // a larger Read than this. Very unlikely, but we handle it here
+ // rather than elsewhere for now.
+ const maxUint31 = 1<<31 - 1
+ for n >= maxUint31 {
+ sc.sendWindowUpdate32(st, maxUint31)
+ n -= maxUint31
+ }
+ sc.sendWindowUpdate32(st, int32(n))
+}
+
+// st may be nil for conn-level
+func (sc *serverConn) sendWindowUpdate32(st *stream, n int32) {
+ sc.serveG.check()
+ if n == 0 {
+ return
+ }
+ if n < 0 {
+ panic("negative update")
+ }
+ var streamID uint32
+ if st != nil {
+ streamID = st.id
+ }
+ sc.writeFrame(frameWriteMsg{
+ write: writeWindowUpdate{streamID: streamID, n: uint32(n)},
+ stream: st,
+ })
+ var ok bool
+ if st == nil {
+ ok = sc.inflow.add(n)
+ } else {
+ ok = st.inflow.add(n)
+ }
+ if !ok {
+ panic("internal error; sent too many window updates without decrements?")
+ }
+}
+
+type requestBody struct {
+ stream *stream
+ conn *serverConn
+ closed bool
+ pipe *pipe // non-nil if we have a HTTP entity message body
+ needsContinue bool // need to send a 100-continue
+}
+
+func (b *requestBody) Close() error {
+ if b.pipe != nil {
+ b.pipe.Close(errClosedBody)
+ }
+ b.closed = true
+ return nil
+}
+
+func (b *requestBody) Read(p []byte) (n int, err error) {
+ if b.needsContinue {
+ b.needsContinue = false
+ b.conn.write100ContinueHeaders(b.stream)
+ }
+ if b.pipe == nil {
+ return 0, io.EOF
+ }
+ n, err = b.pipe.Read(p)
+ if n > 0 {
+ b.conn.noteBodyReadFromHandler(b.stream, n)
+ }
+ return
+}
+
+// responseWriter is the http.ResponseWriter implementation. It's
+// intentionally small (1 pointer wide) to minimize garbage. The
+// responseWriterState pointer inside is zeroed at the end of a
+// request (in handlerDone) and calls on the responseWriter thereafter
+// simply crash (caller's mistake), but the much larger responseWriterState
+// and buffers are reused between multiple requests.
+type responseWriter struct {
+ rws *responseWriterState
+}
+
+// Optional http.ResponseWriter interfaces implemented.
+var (
+ _ http.CloseNotifier = (*responseWriter)(nil)
+ _ http.Flusher = (*responseWriter)(nil)
+ _ stringWriter = (*responseWriter)(nil)
+)
+
+type responseWriterState struct {
+ // immutable within a request:
+ stream *stream
+ req *http.Request
+ body *requestBody // to close at end of request, if DATA frames didn't
+ conn *serverConn
+
+ // TODO: adjust buffer writing sizes based on server config, frame size updates from peer, etc
+ bw *bufio.Writer // writing to a chunkWriter{this *responseWriterState}
+
+ // mutated by http.Handler goroutine:
+ handlerHeader http.Header // nil until called
+ snapHeader http.Header // snapshot of handlerHeader at WriteHeader time
+ status int // status code passed to WriteHeader
+ wroteHeader bool // WriteHeader called (explicitly or implicitly). Not necessarily sent to user yet.
+ sentHeader bool // have we sent the header frame?
+ handlerDone bool // handler has finished
+ curWrite writeData
+ frameWriteCh chan error // re-used whenever we need to block on a frame being written
+
+ closeNotifierMu sync.Mutex // guards closeNotifierCh
+ closeNotifierCh chan bool // nil until first used
+}
+
+type chunkWriter struct{ rws *responseWriterState }
+
+func (cw chunkWriter) Write(p []byte) (n int, err error) { return cw.rws.writeChunk(p) }
+
+// writeChunk writes chunks from the bufio.Writer. But because
+// bufio.Writer may bypass its chunking, sometimes p may be
+// arbitrarily large.
+//
+// writeChunk is also responsible (on the first chunk) for sending the
+// HEADER response.
+func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
+ if !rws.wroteHeader {
+ rws.writeHeader(200)
+ }
+ if !rws.sentHeader {
+ rws.sentHeader = true
+ var ctype, clen string // implicit ones, if we can calculate it
+ if rws.handlerDone && rws.snapHeader.Get("Content-Length") == "" {
+ clen = strconv.Itoa(len(p))
+ }
+ if rws.snapHeader.Get("Content-Type") == "" {
+ ctype = http.DetectContentType(p)
+ }
+ endStream := rws.handlerDone && len(p) == 0
+ rws.conn.writeHeaders(rws.stream, &writeResHeaders{
+ streamID: rws.stream.id,
+ httpResCode: rws.status,
+ h: rws.snapHeader,
+ endStream: endStream,
+ contentType: ctype,
+ contentLength: clen,
+ }, rws.frameWriteCh)
+ if endStream {
+ return 0, nil
+ }
+ }
+ if len(p) == 0 && !rws.handlerDone {
+ return 0, nil
+ }
+ curWrite := &rws.curWrite
+ curWrite.streamID = rws.stream.id
+ curWrite.p = p
+ curWrite.endStream = rws.handlerDone
+ if err := rws.conn.writeDataFromHandler(rws.stream, curWrite, rws.frameWriteCh); err != nil {
+ return 0, err
+ }
+ return len(p), nil
+}
+
+func (w *responseWriter) Flush() {
+ rws := w.rws
+ if rws == nil {
+ panic("Header called after Handler finished")
+ }
+ if rws.bw.Buffered() > 0 {
+ if err := rws.bw.Flush(); err != nil {
+ // Ignore the error. The frame writer already knows.
+ return
+ }
+ } else {
+ // The bufio.Writer won't call chunkWriter.Write
+ // (writeChunk with zero bytes, so we have to do it
+ // ourselves to force the HTTP response header and/or
+ // final DATA frame (with END_STREAM) to be sent.
+ rws.writeChunk(nil)
+ }
+}
+
+func (w *responseWriter) CloseNotify() <-chan bool {
+ rws := w.rws
+ if rws == nil {
+ panic("CloseNotify called after Handler finished")
+ }
+ rws.closeNotifierMu.Lock()
+ ch := rws.closeNotifierCh
+ if ch == nil {
+ ch = make(chan bool, 1)
+ rws.closeNotifierCh = ch
+ go func() {
+ rws.stream.cw.Wait() // wait for close
+ ch <- true
+ }()
+ }
+ rws.closeNotifierMu.Unlock()
+ return ch
+}
+
+func (w *responseWriter) Header() http.Header {
+ rws := w.rws
+ if rws == nil {
+ panic("Header called after Handler finished")
+ }
+ if rws.handlerHeader == nil {
+ rws.handlerHeader = make(http.Header)
+ }
+ return rws.handlerHeader
+}
+
+func (w *responseWriter) WriteHeader(code int) {
+ rws := w.rws
+ if rws == nil {
+ panic("WriteHeader called after Handler finished")
+ }
+ rws.writeHeader(code)
+}
+
+func (rws *responseWriterState) writeHeader(code int) {
+ if !rws.wroteHeader {
+ rws.wroteHeader = true
+ rws.status = code
+ if len(rws.handlerHeader) > 0 {
+ rws.snapHeader = cloneHeader(rws.handlerHeader)
+ }
+ }
+}
+
+func cloneHeader(h http.Header) http.Header {
+ h2 := make(http.Header, len(h))
+ for k, vv := range h {
+ vv2 := make([]string, len(vv))
+ copy(vv2, vv)
+ h2[k] = vv2
+ }
+ return h2
+}
+
+// The Life Of A Write is like this:
+//
+// * Handler calls w.Write or w.WriteString ->
+// * -> rws.bw (*bufio.Writer) ->
+// * (Handler migth call Flush)
+// * -> chunkWriter{rws}
+// * -> responseWriterState.writeChunk(p []byte)
+// * -> responseWriterState.writeChunk (most of the magic; see comment there)
+func (w *responseWriter) Write(p []byte) (n int, err error) {
+ return w.write(len(p), p, "")
+}
+
+func (w *responseWriter) WriteString(s string) (n int, err error) {
+ return w.write(len(s), nil, s)
+}
+
+// either dataB or dataS is non-zero.
+func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int, err error) {
+ rws := w.rws
+ if rws == nil {
+ panic("Write called after Handler finished")
+ }
+ if !rws.wroteHeader {
+ w.WriteHeader(200)
+ }
+ if dataB != nil {
+ return rws.bw.Write(dataB)
+ } else {
+ return rws.bw.WriteString(dataS)
+ }
+}
+
+func (w *responseWriter) handlerDone() {
+ rws := w.rws
+ if rws == nil {
+ panic("handlerDone called twice")
+ }
+ rws.handlerDone = true
+ w.Flush()
+ w.rws = nil
+ responseWriterStatePool.Put(rws)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/server_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/server_test.go
new file mode 100644
index 000000000..5ff6b5b76
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/server_test.go
@@ -0,0 +1,2252 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "reflect"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+)
+
+var stderrVerbose = flag.Bool("stderr_verbose", false, "Mirror verbosity to stderr, unbuffered")
+
+type serverTester struct {
+ cc net.Conn // client conn
+ t testing.TB
+ ts *httptest.Server
+ fr *Framer
+ logBuf *bytes.Buffer
+ logFilter []string // substrings to filter out
+ scMu sync.Mutex // guards sc
+ sc *serverConn
+
+ // writing headers:
+ headerBuf bytes.Buffer
+ hpackEnc *hpack.Encoder
+
+ // reading frames:
+ frc chan Frame
+ frErrc chan error
+ readTimer *time.Timer
+}
+
+func init() {
+ testHookOnPanicMu = new(sync.Mutex)
+}
+
+func resetHooks() {
+ testHookOnPanicMu.Lock()
+ testHookOnPanic = nil
+ testHookOnPanicMu.Unlock()
+}
+
+type serverTesterOpt string
+
+var optOnlyServer = serverTesterOpt("only_server")
+
+func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}) *serverTester {
+ resetHooks()
+
+ logBuf := new(bytes.Buffer)
+ ts := httptest.NewUnstartedServer(handler)
+
+ tlsConfig := &tls.Config{
+ InsecureSkipVerify: true,
+ // The h2-14 is temporary, until curl is updated. (as used by unit tests
+ // in Docker)
+ NextProtos: []string{NextProtoTLS, "h2-14"},
+ }
+
+ onlyServer := false
+ for _, opt := range opts {
+ switch v := opt.(type) {
+ case func(*tls.Config):
+ v(tlsConfig)
+ case func(*httptest.Server):
+ v(ts)
+ case serverTesterOpt:
+ onlyServer = (v == optOnlyServer)
+ default:
+ t.Fatalf("unknown newServerTester option type %T", v)
+ }
+ }
+
+ ConfigureServer(ts.Config, &Server{})
+
+ st := &serverTester{
+ t: t,
+ ts: ts,
+ logBuf: logBuf,
+ frc: make(chan Frame, 1),
+ frErrc: make(chan error, 1),
+ }
+ st.hpackEnc = hpack.NewEncoder(&st.headerBuf)
+
+ var stderrv io.Writer = ioutil.Discard
+ if *stderrVerbose {
+ stderrv = os.Stderr
+ }
+
+ ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
+ ts.Config.ErrorLog = log.New(io.MultiWriter(stderrv, twriter{t: t, st: st}, logBuf), "", log.LstdFlags)
+ ts.StartTLS()
+
+ if VerboseLogs {
+ t.Logf("Running test server at: %s", ts.URL)
+ }
+ testHookGetServerConn = func(v *serverConn) {
+ st.scMu.Lock()
+ defer st.scMu.Unlock()
+ st.sc = v
+ st.sc.testHookCh = make(chan func())
+ }
+ log.SetOutput(io.MultiWriter(stderrv, twriter{t: t, st: st}))
+ if !onlyServer {
+ cc, err := tls.Dial("tcp", ts.Listener.Addr().String(), tlsConfig)
+ if err != nil {
+ t.Fatal(err)
+ }
+ st.cc = cc
+ st.fr = NewFramer(cc, cc)
+ }
+
+ return st
+}
+
+func (st *serverTester) closeConn() {
+ st.scMu.Lock()
+ defer st.scMu.Unlock()
+ st.sc.conn.Close()
+}
+
+func (st *serverTester) addLogFilter(phrase string) {
+ st.logFilter = append(st.logFilter, phrase)
+}
+
+func (st *serverTester) stream(id uint32) *stream {
+ ch := make(chan *stream, 1)
+ st.sc.testHookCh <- func() {
+ ch <- st.sc.streams[id]
+ }
+ return <-ch
+}
+
+func (st *serverTester) streamState(id uint32) streamState {
+ ch := make(chan streamState, 1)
+ st.sc.testHookCh <- func() {
+ state, _ := st.sc.state(id)
+ ch <- state
+ }
+ return <-ch
+}
+
+func (st *serverTester) Close() {
+ st.ts.Close()
+ if st.cc != nil {
+ st.cc.Close()
+ }
+ log.SetOutput(os.Stderr)
+}
+
+// greet initiates the client's HTTP/2 connection into a state where
+// frames may be sent.
+func (st *serverTester) greet() {
+ st.writePreface()
+ st.writeInitialSettings()
+ st.wantSettings()
+ st.writeSettingsAck()
+ st.wantSettingsAck()
+}
+
+func (st *serverTester) writePreface() {
+ n, err := st.cc.Write(clientPreface)
+ if err != nil {
+ st.t.Fatalf("Error writing client preface: %v", err)
+ }
+ if n != len(clientPreface) {
+ st.t.Fatalf("Writing client preface, wrote %d bytes; want %d", n, len(clientPreface))
+ }
+}
+
+func (st *serverTester) writeInitialSettings() {
+ if err := st.fr.WriteSettings(); err != nil {
+ st.t.Fatalf("Error writing initial SETTINGS frame from client to server: %v", err)
+ }
+}
+
+func (st *serverTester) writeSettingsAck() {
+ if err := st.fr.WriteSettingsAck(); err != nil {
+ st.t.Fatalf("Error writing ACK of server's SETTINGS: %v", err)
+ }
+}
+
+func (st *serverTester) writeHeaders(p HeadersFrameParam) {
+ if err := st.fr.WriteHeaders(p); err != nil {
+ st.t.Fatalf("Error writing HEADERS: %v", err)
+ }
+}
+
+func (st *serverTester) encodeHeaderField(k, v string) {
+ err := st.hpackEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ if err != nil {
+ st.t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err)
+ }
+}
+
+// encodeHeader encodes headers and returns their HPACK bytes. headers
+// must contain an even number of key/value pairs. There may be
+// multiple pairs for keys (e.g. "cookie"). The :method, :path, and
+// :scheme headers default to GET, / and https.
+func (st *serverTester) encodeHeader(headers ...string) []byte {
+ if len(headers)%2 == 1 {
+ panic("odd number of kv args")
+ }
+
+ st.headerBuf.Reset()
+
+ if len(headers) == 0 {
+ // Fast path, mostly for benchmarks, so test code doesn't pollute
+ // profiles when we're looking to improve server allocations.
+ st.encodeHeaderField(":method", "GET")
+ st.encodeHeaderField(":path", "/")
+ st.encodeHeaderField(":scheme", "https")
+ return st.headerBuf.Bytes()
+ }
+
+ if len(headers) == 2 && headers[0] == ":method" {
+ // Another fast path for benchmarks.
+ st.encodeHeaderField(":method", headers[1])
+ st.encodeHeaderField(":path", "/")
+ st.encodeHeaderField(":scheme", "https")
+ return st.headerBuf.Bytes()
+ }
+
+ pseudoCount := map[string]int{}
+ keys := []string{":method", ":path", ":scheme"}
+ vals := map[string][]string{
+ ":method": {"GET"},
+ ":path": {"/"},
+ ":scheme": {"https"},
+ }
+ for len(headers) > 0 {
+ k, v := headers[0], headers[1]
+ headers = headers[2:]
+ if _, ok := vals[k]; !ok {
+ keys = append(keys, k)
+ }
+ if strings.HasPrefix(k, ":") {
+ pseudoCount[k]++
+ if pseudoCount[k] == 1 {
+ vals[k] = []string{v}
+ } else {
+ // Allows testing of invalid headers w/ dup pseudo fields.
+ vals[k] = append(vals[k], v)
+ }
+ } else {
+ vals[k] = append(vals[k], v)
+ }
+ }
+ st.headerBuf.Reset()
+ for _, k := range keys {
+ for _, v := range vals[k] {
+ st.encodeHeaderField(k, v)
+ }
+ }
+ return st.headerBuf.Bytes()
+}
+
+// bodylessReq1 writes a HEADERS frames with StreamID 1 and EndStream and EndHeaders set.
+func (st *serverTester) bodylessReq1(headers ...string) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(headers...),
+ EndStream: true,
+ EndHeaders: true,
+ })
+}
+
+func (st *serverTester) writeData(streamID uint32, endStream bool, data []byte) {
+ if err := st.fr.WriteData(streamID, endStream, data); err != nil {
+ st.t.Fatalf("Error writing DATA: %v", err)
+ }
+}
+
+func (st *serverTester) readFrame() (Frame, error) {
+ go func() {
+ fr, err := st.fr.ReadFrame()
+ if err != nil {
+ st.frErrc <- err
+ } else {
+ st.frc <- fr
+ }
+ }()
+ t := st.readTimer
+ if t == nil {
+ t = time.NewTimer(2 * time.Second)
+ st.readTimer = t
+ }
+ t.Reset(2 * time.Second)
+ defer t.Stop()
+ select {
+ case f := <-st.frc:
+ return f, nil
+ case err := <-st.frErrc:
+ return nil, err
+ case <-t.C:
+ return nil, errors.New("timeout waiting for frame")
+ }
+}
+
+func (st *serverTester) wantHeaders() *HeadersFrame {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a HEADERS frame: %v", err)
+ }
+ hf, ok := f.(*HeadersFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *HeadersFrame", f)
+ }
+ return hf
+}
+
+func (st *serverTester) wantContinuation() *ContinuationFrame {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a CONTINUATION frame: %v", err)
+ }
+ cf, ok := f.(*ContinuationFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *ContinuationFrame", f)
+ }
+ return cf
+}
+
+func (st *serverTester) wantData() *DataFrame {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a DATA frame: %v", err)
+ }
+ df, ok := f.(*DataFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *DataFrame", f)
+ }
+ return df
+}
+
+func (st *serverTester) wantSettings() *SettingsFrame {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a SETTINGS frame: %v", err)
+ }
+ sf, ok := f.(*SettingsFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *SettingsFrame", f)
+ }
+ return sf
+}
+
+func (st *serverTester) wantPing() *PingFrame {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a PING frame: %v", err)
+ }
+ pf, ok := f.(*PingFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *PingFrame", f)
+ }
+ return pf
+}
+
+func (st *serverTester) wantGoAway() *GoAwayFrame {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a GOAWAY frame: %v", err)
+ }
+ gf, ok := f.(*GoAwayFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *GoAwayFrame", f)
+ }
+ return gf
+}
+
+func (st *serverTester) wantRSTStream(streamID uint32, errCode ErrCode) {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting an RSTStream frame: %v", err)
+ }
+ rs, ok := f.(*RSTStreamFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *RSTStreamFrame", f)
+ }
+ if rs.FrameHeader.StreamID != streamID {
+ st.t.Fatalf("RSTStream StreamID = %d; want %d", rs.FrameHeader.StreamID, streamID)
+ }
+ if rs.ErrCode != errCode {
+ st.t.Fatalf("RSTStream ErrCode = %d (%s); want %d (%s)", rs.ErrCode, rs.ErrCode, errCode, errCode)
+ }
+}
+
+func (st *serverTester) wantWindowUpdate(streamID, incr uint32) {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatalf("Error while expecting a WINDOW_UPDATE frame: %v", err)
+ }
+ wu, ok := f.(*WindowUpdateFrame)
+ if !ok {
+ st.t.Fatalf("got a %T; want *WindowUpdateFrame", f)
+ }
+ if wu.FrameHeader.StreamID != streamID {
+ st.t.Fatalf("WindowUpdate StreamID = %d; want %d", wu.FrameHeader.StreamID, streamID)
+ }
+ if wu.Increment != incr {
+ st.t.Fatalf("WindowUpdate increment = %d; want %d", wu.Increment, incr)
+ }
+}
+
+func (st *serverTester) wantSettingsAck() {
+ f, err := st.readFrame()
+ if err != nil {
+ st.t.Fatal(err)
+ }
+ sf, ok := f.(*SettingsFrame)
+ if !ok {
+ st.t.Fatalf("Wanting a settings ACK, received a %T", f)
+ }
+ if !sf.Header().Flags.Has(FlagSettingsAck) {
+ st.t.Fatal("Settings Frame didn't have ACK set")
+ }
+
+}
+
+func TestServer(t *testing.T) {
+ gotReq := make(chan bool, 1)
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Foo", "Bar")
+ gotReq <- true
+ })
+ defer st.Close()
+
+ covers("3.5", `
+ The server connection preface consists of a potentially empty
+ SETTINGS frame ([SETTINGS]) that MUST be the first frame the
+ server sends in the HTTP/2 connection.
+ `)
+
+ st.writePreface()
+ st.writeInitialSettings()
+ st.wantSettings()
+ st.writeSettingsAck()
+ st.wantSettingsAck()
+
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(),
+ EndStream: true, // no DATA frames
+ EndHeaders: true,
+ })
+
+ select {
+ case <-gotReq:
+ case <-time.After(2 * time.Second):
+ t.Error("timeout waiting for request")
+ }
+}
+
+func TestServer_Request_Get(t *testing.T) {
+ testServerRequest(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader("foo-bar", "some-value"),
+ EndStream: true, // no DATA frames
+ EndHeaders: true,
+ })
+ }, func(r *http.Request) {
+ if r.Method != "GET" {
+ t.Errorf("Method = %q; want GET", r.Method)
+ }
+ if r.URL.Path != "/" {
+ t.Errorf("URL.Path = %q; want /", r.URL.Path)
+ }
+ if r.ContentLength != 0 {
+ t.Errorf("ContentLength = %v; want 0", r.ContentLength)
+ }
+ if r.Close {
+ t.Error("Close = true; want false")
+ }
+ if !strings.Contains(r.RemoteAddr, ":") {
+ t.Errorf("RemoteAddr = %q; want something with a colon", r.RemoteAddr)
+ }
+ if r.Proto != "HTTP/2.0" || r.ProtoMajor != 2 || r.ProtoMinor != 0 {
+ t.Errorf("Proto = %q Major=%v,Minor=%v; want HTTP/2.0", r.Proto, r.ProtoMajor, r.ProtoMinor)
+ }
+ wantHeader := http.Header{
+ "Foo-Bar": []string{"some-value"},
+ }
+ if !reflect.DeepEqual(r.Header, wantHeader) {
+ t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
+ }
+ if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
+ t.Errorf("Read = %d, %v; want 0, EOF", n, err)
+ }
+ })
+}
+
+func TestServer_Request_Get_PathSlashes(t *testing.T) {
+ testServerRequest(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":path", "/%2f/"),
+ EndStream: true, // no DATA frames
+ EndHeaders: true,
+ })
+ }, func(r *http.Request) {
+ if r.RequestURI != "/%2f/" {
+ t.Errorf("RequestURI = %q; want /%%2f/", r.RequestURI)
+ }
+ if r.URL.Path != "///" {
+ t.Errorf("URL.Path = %q; want ///", r.URL.Path)
+ }
+ })
+}
+
+// TODO: add a test with EndStream=true on the HEADERS but setting a
+// Content-Length anyway. Should we just omit it and force it to
+// zero?
+
+func TestServer_Request_Post_NoContentLength_EndStream(t *testing.T) {
+ testServerRequest(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ }, func(r *http.Request) {
+ if r.Method != "POST" {
+ t.Errorf("Method = %q; want POST", r.Method)
+ }
+ if r.ContentLength != 0 {
+ t.Errorf("ContentLength = %v; want 0", r.ContentLength)
+ }
+ if n, err := r.Body.Read([]byte(" ")); err != io.EOF || n != 0 {
+ t.Errorf("Read = %d, %v; want 0, EOF", n, err)
+ }
+ })
+}
+
+func TestServer_Request_Post_Body_ImmediateEOF(t *testing.T) {
+ testBodyContents(t, -1, "", func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false, // to say DATA frames are coming
+ EndHeaders: true,
+ })
+ st.writeData(1, true, nil) // just kidding. empty body.
+ })
+}
+
+func TestServer_Request_Post_Body_OneData(t *testing.T) {
+ const content = "Some content"
+ testBodyContents(t, -1, content, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false, // to say DATA frames are coming
+ EndHeaders: true,
+ })
+ st.writeData(1, true, []byte(content))
+ })
+}
+
+func TestServer_Request_Post_Body_TwoData(t *testing.T) {
+ const content = "Some content"
+ testBodyContents(t, -1, content, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false, // to say DATA frames are coming
+ EndHeaders: true,
+ })
+ st.writeData(1, false, []byte(content[:5]))
+ st.writeData(1, true, []byte(content[5:]))
+ })
+}
+
+func TestServer_Request_Post_Body_ContentLength_Correct(t *testing.T) {
+ const content = "Some content"
+ testBodyContents(t, int64(len(content)), content, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(
+ ":method", "POST",
+ "content-length", strconv.Itoa(len(content)),
+ ),
+ EndStream: false, // to say DATA frames are coming
+ EndHeaders: true,
+ })
+ st.writeData(1, true, []byte(content))
+ })
+}
+
+func TestServer_Request_Post_Body_ContentLength_TooLarge(t *testing.T) {
+ testBodyContentsFail(t, 3, "request declared a Content-Length of 3 but only wrote 2 bytes",
+ func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(
+ ":method", "POST",
+ "content-length", "3",
+ ),
+ EndStream: false, // to say DATA frames are coming
+ EndHeaders: true,
+ })
+ st.writeData(1, true, []byte("12"))
+ })
+}
+
+func TestServer_Request_Post_Body_ContentLength_TooSmall(t *testing.T) {
+ testBodyContentsFail(t, 4, "sender tried to send more than declared Content-Length of 4 bytes",
+ func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(
+ ":method", "POST",
+ "content-length", "4",
+ ),
+ EndStream: false, // to say DATA frames are coming
+ EndHeaders: true,
+ })
+ st.writeData(1, true, []byte("12345"))
+ })
+}
+
+func testBodyContents(t *testing.T, wantContentLength int64, wantBody string, write func(st *serverTester)) {
+ testServerRequest(t, write, func(r *http.Request) {
+ if r.Method != "POST" {
+ t.Errorf("Method = %q; want POST", r.Method)
+ }
+ if r.ContentLength != wantContentLength {
+ t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
+ }
+ all, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(all) != wantBody {
+ t.Errorf("Read = %q; want %q", all, wantBody)
+ }
+ if err := r.Body.Close(); err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+ })
+}
+
+func testBodyContentsFail(t *testing.T, wantContentLength int64, wantReadError string, write func(st *serverTester)) {
+ testServerRequest(t, write, func(r *http.Request) {
+ if r.Method != "POST" {
+ t.Errorf("Method = %q; want POST", r.Method)
+ }
+ if r.ContentLength != wantContentLength {
+ t.Errorf("ContentLength = %v; want %d", r.ContentLength, wantContentLength)
+ }
+ all, err := ioutil.ReadAll(r.Body)
+ if err == nil {
+ t.Fatalf("expected an error (%q) reading from the body. Successfully read %q instead.",
+ wantReadError, all)
+ }
+ if !strings.Contains(err.Error(), wantReadError) {
+ t.Fatalf("Body.Read = %v; want substring %q", err, wantReadError)
+ }
+ if err := r.Body.Close(); err != nil {
+ t.Fatalf("Close: %v", err)
+ }
+ })
+}
+
+// Using a Host header, instead of :authority
+func TestServer_Request_Get_Host(t *testing.T) {
+ const host = "example.com"
+ testServerRequest(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader("host", host),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ }, func(r *http.Request) {
+ if r.Host != host {
+ t.Errorf("Host = %q; want %q", r.Host, host)
+ }
+ })
+}
+
+// Using an :authority pseudo-header, instead of Host
+func TestServer_Request_Get_Authority(t *testing.T) {
+ const host = "example.com"
+ testServerRequest(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":authority", host),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ }, func(r *http.Request) {
+ if r.Host != host {
+ t.Errorf("Host = %q; want %q", r.Host, host)
+ }
+ })
+}
+
+func TestServer_Request_WithContinuation(t *testing.T) {
+ wantHeader := http.Header{
+ "Foo-One": []string{"value-one"},
+ "Foo-Two": []string{"value-two"},
+ "Foo-Three": []string{"value-three"},
+ }
+ testServerRequest(t, func(st *serverTester) {
+ fullHeaders := st.encodeHeader(
+ "foo-one", "value-one",
+ "foo-two", "value-two",
+ "foo-three", "value-three",
+ )
+ remain := fullHeaders
+ chunks := 0
+ for len(remain) > 0 {
+ const maxChunkSize = 5
+ chunk := remain
+ if len(chunk) > maxChunkSize {
+ chunk = chunk[:maxChunkSize]
+ }
+ remain = remain[len(chunk):]
+
+ if chunks == 0 {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: chunk,
+ EndStream: true, // no DATA frames
+ EndHeaders: false, // we'll have continuation frames
+ })
+ } else {
+ err := st.fr.WriteContinuation(1, len(remain) == 0, chunk)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ chunks++
+ }
+ if chunks < 2 {
+ t.Fatal("too few chunks")
+ }
+ }, func(r *http.Request) {
+ if !reflect.DeepEqual(r.Header, wantHeader) {
+ t.Errorf("Header = %#v; want %#v", r.Header, wantHeader)
+ }
+ })
+}
+
+// Concatenated cookie headers. ("8.1.2.5 Compressing the Cookie Header Field")
+func TestServer_Request_CookieConcat(t *testing.T) {
+ const host = "example.com"
+ testServerRequest(t, func(st *serverTester) {
+ st.bodylessReq1(
+ ":authority", host,
+ "cookie", "a=b",
+ "cookie", "c=d",
+ "cookie", "e=f",
+ )
+ }, func(r *http.Request) {
+ const want = "a=b; c=d; e=f"
+ if got := r.Header.Get("Cookie"); got != want {
+ t.Errorf("Cookie = %q; want %q", got, want)
+ }
+ })
+}
+
+func TestServer_Request_Reject_CapitalHeader(t *testing.T) {
+ testRejectRequest(t, func(st *serverTester) { st.bodylessReq1("UPPER", "v") })
+}
+
+func TestServer_Request_Reject_Pseudo_Missing_method(t *testing.T) {
+ testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":method", "") })
+}
+
+func TestServer_Request_Reject_Pseudo_ExactlyOne(t *testing.T) {
+ // 8.1.2.3 Request Pseudo-Header Fields
+ // "All HTTP/2 requests MUST include exactly one valid value" ...
+ testRejectRequest(t, func(st *serverTester) {
+ st.addLogFilter("duplicate pseudo-header")
+ st.bodylessReq1(":method", "GET", ":method", "POST")
+ })
+}
+
+func TestServer_Request_Reject_Pseudo_AfterRegular(t *testing.T) {
+ // 8.1.2.3 Request Pseudo-Header Fields
+ // "All pseudo-header fields MUST appear in the header block
+ // before regular header fields. Any request or response that
+ // contains a pseudo-header field that appears in a header
+ // block after a regular header field MUST be treated as
+ // malformed (Section 8.1.2.6)."
+ testRejectRequest(t, func(st *serverTester) {
+ st.addLogFilter("pseudo-header after regular header")
+ var buf bytes.Buffer
+ enc := hpack.NewEncoder(&buf)
+ enc.WriteField(hpack.HeaderField{Name: ":method", Value: "GET"})
+ enc.WriteField(hpack.HeaderField{Name: "regular", Value: "foobar"})
+ enc.WriteField(hpack.HeaderField{Name: ":path", Value: "/"})
+ enc.WriteField(hpack.HeaderField{Name: ":scheme", Value: "https"})
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: buf.Bytes(),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ })
+}
+
+func TestServer_Request_Reject_Pseudo_Missing_path(t *testing.T) {
+ testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":path", "") })
+}
+
+func TestServer_Request_Reject_Pseudo_Missing_scheme(t *testing.T) {
+ testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "") })
+}
+
+func TestServer_Request_Reject_Pseudo_scheme_invalid(t *testing.T) {
+ testRejectRequest(t, func(st *serverTester) { st.bodylessReq1(":scheme", "bogus") })
+}
+
+func TestServer_Request_Reject_Pseudo_Unknown(t *testing.T) {
+ testRejectRequest(t, func(st *serverTester) {
+ st.addLogFilter(`invalid pseudo-header ":unknown_thing"`)
+ st.bodylessReq1(":unknown_thing", "")
+ })
+}
+
+func testRejectRequest(t *testing.T, send func(*serverTester)) {
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ t.Fatal("server request made it to handler; should've been rejected")
+ })
+ defer st.Close()
+
+ st.greet()
+ send(st)
+ st.wantRSTStream(1, ErrCodeProtocol)
+}
+
+func TestServer_Ping(t *testing.T) {
+ st := newServerTester(t, nil)
+ defer st.Close()
+ st.greet()
+
+ // Server should ignore this one, since it has ACK set.
+ ackPingData := [8]byte{1, 2, 4, 8, 16, 32, 64, 128}
+ if err := st.fr.WritePing(true, ackPingData); err != nil {
+ t.Fatal(err)
+ }
+
+ // But the server should reply to this one, since ACK is false.
+ pingData := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
+ if err := st.fr.WritePing(false, pingData); err != nil {
+ t.Fatal(err)
+ }
+
+ pf := st.wantPing()
+ if !pf.Flags.Has(FlagPingAck) {
+ t.Error("response ping doesn't have ACK set")
+ }
+ if pf.Data != pingData {
+ t.Errorf("response ping has data %q; want %q", pf.Data, pingData)
+ }
+}
+
+func TestServer_RejectsLargeFrames(t *testing.T) {
+ st := newServerTester(t, nil)
+ defer st.Close()
+ st.greet()
+
+ // Write too large of a frame (too large by one byte)
+ // We ignore the return value because it's expected that the server
+ // will only read the first 9 bytes (the headre) and then disconnect.
+ st.fr.WriteRawFrame(0xff, 0, 0, make([]byte, defaultMaxReadFrameSize+1))
+
+ gf := st.wantGoAway()
+ if gf.ErrCode != ErrCodeFrameSize {
+ t.Errorf("GOAWAY err = %v; want %v", gf.ErrCode, ErrCodeFrameSize)
+ }
+}
+
+func TestServer_Handler_Sends_WindowUpdate(t *testing.T) {
+ puppet := newHandlerPuppet()
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ puppet.act(w, r)
+ })
+ defer st.Close()
+ defer puppet.done()
+
+ st.greet()
+
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false, // data coming
+ EndHeaders: true,
+ })
+ st.writeData(1, false, []byte("abcdef"))
+ puppet.do(readBodyHandler(t, "abc"))
+ st.wantWindowUpdate(0, 3)
+ st.wantWindowUpdate(1, 3)
+
+ puppet.do(readBodyHandler(t, "def"))
+ st.wantWindowUpdate(0, 3)
+ st.wantWindowUpdate(1, 3)
+
+ st.writeData(1, true, []byte("ghijkl")) // END_STREAM here
+ puppet.do(readBodyHandler(t, "ghi"))
+ puppet.do(readBodyHandler(t, "jkl"))
+ st.wantWindowUpdate(0, 3)
+ st.wantWindowUpdate(0, 3) // no more stream-level, since END_STREAM
+}
+
+func TestServer_Send_GoAway_After_Bogus_WindowUpdate(t *testing.T) {
+ st := newServerTester(t, nil)
+ defer st.Close()
+ st.greet()
+ if err := st.fr.WriteWindowUpdate(0, 1<<31-1); err != nil {
+ t.Fatal(err)
+ }
+ gf := st.wantGoAway()
+ if gf.ErrCode != ErrCodeFlowControl {
+ t.Errorf("GOAWAY err = %v; want %v", gf.ErrCode, ErrCodeFlowControl)
+ }
+ if gf.LastStreamID != 0 {
+ t.Errorf("GOAWAY last stream ID = %v; want %v", gf.LastStreamID, 0)
+ }
+}
+
+func TestServer_Send_RstStream_After_Bogus_WindowUpdate(t *testing.T) {
+ inHandler := make(chan bool)
+ blockHandler := make(chan bool)
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ inHandler <- true
+ <-blockHandler
+ })
+ defer st.Close()
+ defer close(blockHandler)
+ st.greet()
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false, // keep it open
+ EndHeaders: true,
+ })
+ <-inHandler
+ // Send a bogus window update:
+ if err := st.fr.WriteWindowUpdate(1, 1<<31-1); err != nil {
+ t.Fatal(err)
+ }
+ st.wantRSTStream(1, ErrCodeFlowControl)
+}
+
+// testServerPostUnblock sends a hanging POST with unsent data to handler,
+// then runs fn once in the handler, and verifies that the error returned from
+// handler is acceptable. It fails if takes over 5 seconds for handler to exit.
+func testServerPostUnblock(t *testing.T,
+ handler func(http.ResponseWriter, *http.Request) error,
+ fn func(*serverTester),
+ checkErr func(error),
+ otherHeaders ...string) {
+ inHandler := make(chan bool)
+ errc := make(chan error, 1)
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ inHandler <- true
+ errc <- handler(w, r)
+ })
+ st.greet()
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(append([]string{":method", "POST"}, otherHeaders...)...),
+ EndStream: false, // keep it open
+ EndHeaders: true,
+ })
+ <-inHandler
+ fn(st)
+ select {
+ case err := <-errc:
+ if checkErr != nil {
+ checkErr(err)
+ }
+ case <-time.After(5 * time.Second):
+ t.Fatal("timeout waiting for Handler to return")
+ }
+ st.Close()
+}
+
+func TestServer_RSTStream_Unblocks_Read(t *testing.T) {
+ testServerPostUnblock(t,
+ func(w http.ResponseWriter, r *http.Request) (err error) {
+ _, err = r.Body.Read(make([]byte, 1))
+ return
+ },
+ func(st *serverTester) {
+ if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
+ t.Fatal(err)
+ }
+ },
+ func(err error) {
+ if err == nil {
+ t.Error("unexpected nil error from Request.Body.Read")
+ }
+ },
+ )
+}
+
+func TestServer_DeadConn_Unblocks_Read(t *testing.T) {
+ testServerPostUnblock(t,
+ func(w http.ResponseWriter, r *http.Request) (err error) {
+ _, err = r.Body.Read(make([]byte, 1))
+ return
+ },
+ func(st *serverTester) { st.cc.Close() },
+ func(err error) {
+ if err == nil {
+ t.Error("unexpected nil error from Request.Body.Read")
+ }
+ },
+ )
+}
+
+var blockUntilClosed = func(w http.ResponseWriter, r *http.Request) error {
+ <-w.(http.CloseNotifier).CloseNotify()
+ return nil
+}
+
+func TestServer_CloseNotify_After_RSTStream(t *testing.T) {
+ testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) {
+ if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
+ t.Fatal(err)
+ }
+ }, nil)
+}
+
+func TestServer_CloseNotify_After_ConnClose(t *testing.T) {
+ testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) { st.cc.Close() }, nil)
+}
+
+// that CloseNotify unblocks after a stream error due to the client's
+// problem that's unrelated to them explicitly canceling it (which is
+// TestServer_CloseNotify_After_RSTStream above)
+func TestServer_CloseNotify_After_StreamError(t *testing.T) {
+ testServerPostUnblock(t, blockUntilClosed, func(st *serverTester) {
+ // data longer than declared Content-Length => stream error
+ st.writeData(1, true, []byte("1234"))
+ }, nil, "content-length", "3")
+}
+
+func TestServer_StateTransitions(t *testing.T) {
+ var st *serverTester
+ inHandler := make(chan bool)
+ writeData := make(chan bool)
+ leaveHandler := make(chan bool)
+ st = newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ inHandler <- true
+ if st.stream(1) == nil {
+ t.Errorf("nil stream 1 in handler")
+ }
+ if got, want := st.streamState(1), stateOpen; got != want {
+ t.Errorf("in handler, state is %v; want %v", got, want)
+ }
+ writeData <- true
+ if n, err := r.Body.Read(make([]byte, 1)); n != 0 || err != io.EOF {
+ t.Errorf("body read = %d, %v; want 0, EOF", n, err)
+ }
+ if got, want := st.streamState(1), stateHalfClosedRemote; got != want {
+ t.Errorf("in handler, state is %v; want %v", got, want)
+ }
+
+ <-leaveHandler
+ })
+ st.greet()
+ if st.stream(1) != nil {
+ t.Fatal("stream 1 should be empty")
+ }
+ if got := st.streamState(1); got != stateIdle {
+ t.Fatalf("stream 1 should be idle; got %v", got)
+ }
+
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false, // keep it open
+ EndHeaders: true,
+ })
+ <-inHandler
+ <-writeData
+ st.writeData(1, true, nil)
+
+ leaveHandler <- true
+ hf := st.wantHeaders()
+ if !hf.StreamEnded() {
+ t.Fatal("expected END_STREAM flag")
+ }
+
+ if got, want := st.streamState(1), stateClosed; got != want {
+ t.Errorf("at end, state is %v; want %v", got, want)
+ }
+ if st.stream(1) != nil {
+ t.Fatal("at end, stream 1 should be gone")
+ }
+}
+
+// test HEADERS w/o EndHeaders + another HEADERS (should get rejected)
+func TestServer_Rejects_HeadersNoEnd_Then_Headers(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: false,
+ })
+ st.writeHeaders(HeadersFrameParam{ // Not a continuation.
+ StreamID: 3, // different stream.
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ })
+}
+
+// test HEADERS w/o EndHeaders + PING (should get rejected)
+func TestServer_Rejects_HeadersNoEnd_Then_Ping(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: false,
+ })
+ if err := st.fr.WritePing(false, [8]byte{}); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+// test HEADERS w/ EndHeaders + a continuation HEADERS (should get rejected)
+func TestServer_Rejects_HeadersEnd_Then_Continuation(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ st.wantHeaders()
+ if err := st.fr.WriteContinuation(1, true, encodeHeaderNoImplicit(t, "foo", "bar")); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+// test HEADERS w/o EndHeaders + a continuation HEADERS on wrong stream ID
+func TestServer_Rejects_HeadersNoEnd_Then_ContinuationWrongStream(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: false,
+ })
+ if err := st.fr.WriteContinuation(3, true, encodeHeaderNoImplicit(t, "foo", "bar")); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+// No HEADERS on stream 0.
+func TestServer_Rejects_Headers0(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ st.fr.AllowIllegalWrites = true
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 0,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ })
+}
+
+// No CONTINUATION on stream 0.
+func TestServer_Rejects_Continuation0(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ st.fr.AllowIllegalWrites = true
+ if err := st.fr.WriteContinuation(0, true, st.encodeHeader()); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+func TestServer_Rejects_PushPromise(t *testing.T) {
+ testServerRejects(t, func(st *serverTester) {
+ pp := PushPromiseParam{
+ StreamID: 1,
+ PromiseID: 3,
+ }
+ if err := st.fr.WritePushPromise(pp); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+// testServerRejects tests that the server hangs up with a GOAWAY
+// frame and a server close after the client does something
+// deserving a CONNECTION_ERROR.
+func testServerRejects(t *testing.T, writeReq func(*serverTester)) {
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {})
+ st.addLogFilter("connection error: PROTOCOL_ERROR")
+ defer st.Close()
+ st.greet()
+ writeReq(st)
+
+ st.wantGoAway()
+ errc := make(chan error, 1)
+ go func() {
+ fr, err := st.fr.ReadFrame()
+ if err == nil {
+ err = fmt.Errorf("got frame of type %T", fr)
+ }
+ errc <- err
+ }()
+ select {
+ case err := <-errc:
+ if err != io.EOF {
+ t.Errorf("ReadFrame = %v; want io.EOF", err)
+ }
+ case <-time.After(2 * time.Second):
+ t.Error("timeout waiting for disconnect")
+ }
+}
+
+// testServerRequest sets up an idle HTTP/2 connection and lets you
+// write a single request with writeReq, and then verify that the
+// *http.Request is built correctly in checkReq.
+func testServerRequest(t *testing.T, writeReq func(*serverTester), checkReq func(*http.Request)) {
+ gotReq := make(chan bool, 1)
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ if r.Body == nil {
+ t.Fatal("nil Body")
+ }
+ checkReq(r)
+ gotReq <- true
+ })
+ defer st.Close()
+
+ st.greet()
+ writeReq(st)
+
+ select {
+ case <-gotReq:
+ case <-time.After(2 * time.Second):
+ t.Error("timeout waiting for request")
+ }
+}
+
+func getSlash(st *serverTester) { st.bodylessReq1() }
+
+func TestServer_Response_NoData(t *testing.T) {
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ // Nothing.
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if !hf.StreamEnded() {
+ t.Fatal("want END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ })
+}
+
+func TestServer_Response_NoData_Header_FooBar(t *testing.T) {
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.Header().Set("Foo-Bar", "some-value")
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if !hf.StreamEnded() {
+ t.Fatal("want END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"foo-bar", "some-value"},
+ {"content-type", "text/plain; charset=utf-8"},
+ {"content-length", "0"},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ })
+}
+
+func TestServer_Response_Data_Sniff_DoesntOverride(t *testing.T) {
+ const msg = "this is HTML."
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.Header().Set("Content-Type", "foo/bar")
+ io.WriteString(w, msg)
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("don't want END_STREAM, expecting data")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"content-type", "foo/bar"},
+ {"content-length", strconv.Itoa(len(msg))},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ df := st.wantData()
+ if !df.StreamEnded() {
+ t.Error("expected DATA to have END_STREAM flag")
+ }
+ if got := string(df.Data()); got != msg {
+ t.Errorf("got DATA %q; want %q", got, msg)
+ }
+ })
+}
+
+func TestServer_Response_TransferEncoding_chunked(t *testing.T) {
+ const msg = "hi"
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.Header().Set("Transfer-Encoding", "chunked") // should be stripped
+ io.WriteString(w, msg)
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"content-type", "text/plain; charset=utf-8"},
+ {"content-length", strconv.Itoa(len(msg))},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ })
+}
+
+// Header accessed only after the initial write.
+func TestServer_Response_Data_IgnoreHeaderAfterWrite_After(t *testing.T) {
+ const msg = "this is HTML."
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ io.WriteString(w, msg)
+ w.Header().Set("foo", "should be ignored")
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"content-type", "text/html; charset=utf-8"},
+ {"content-length", strconv.Itoa(len(msg))},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ })
+}
+
+// Header accessed before the initial write and later mutated.
+func TestServer_Response_Data_IgnoreHeaderAfterWrite_Overwrite(t *testing.T) {
+ const msg = "this is HTML."
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.Header().Set("foo", "proper value")
+ io.WriteString(w, msg)
+ w.Header().Set("foo", "should be ignored")
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"foo", "proper value"},
+ {"content-type", "text/html; charset=utf-8"},
+ {"content-length", strconv.Itoa(len(msg))},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ })
+}
+
+func TestServer_Response_Data_SniffLenType(t *testing.T) {
+ const msg = "this is HTML."
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ io.WriteString(w, msg)
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("don't want END_STREAM, expecting data")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"content-type", "text/html; charset=utf-8"},
+ {"content-length", strconv.Itoa(len(msg))},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ df := st.wantData()
+ if !df.StreamEnded() {
+ t.Error("expected DATA to have END_STREAM flag")
+ }
+ if got := string(df.Data()); got != msg {
+ t.Errorf("got DATA %q; want %q", got, msg)
+ }
+ })
+}
+
+func TestServer_Response_Header_Flush_MidWrite(t *testing.T) {
+ const msg = "this is HTML"
+ const msg2 = ", and this is the next chunk"
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ io.WriteString(w, msg)
+ w.(http.Flusher).Flush()
+ io.WriteString(w, msg2)
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"content-type", "text/html; charset=utf-8"}, // sniffed
+ // and no content-length
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ {
+ df := st.wantData()
+ if df.StreamEnded() {
+ t.Error("unexpected END_STREAM flag")
+ }
+ if got := string(df.Data()); got != msg {
+ t.Errorf("got DATA %q; want %q", got, msg)
+ }
+ }
+ {
+ df := st.wantData()
+ if !df.StreamEnded() {
+ t.Error("wanted END_STREAM flag on last data chunk")
+ }
+ if got := string(df.Data()); got != msg2 {
+ t.Errorf("got DATA %q; want %q", got, msg2)
+ }
+ }
+ })
+}
+
+func TestServer_Response_LargeWrite(t *testing.T) {
+ const size = 1 << 20
+ const maxFrameSize = 16 << 10
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ n, err := w.Write(bytes.Repeat([]byte("a"), size))
+ if err != nil {
+ return fmt.Errorf("Write error: %v", err)
+ }
+ if n != size {
+ return fmt.Errorf("wrong size %d from Write", n)
+ }
+ return nil
+ }, func(st *serverTester) {
+ if err := st.fr.WriteSettings(
+ Setting{SettingInitialWindowSize, 0},
+ Setting{SettingMaxFrameSize, maxFrameSize},
+ ); err != nil {
+ t.Fatal(err)
+ }
+ st.wantSettingsAck()
+
+ getSlash(st) // make the single request
+
+ // Give the handler quota to write:
+ if err := st.fr.WriteWindowUpdate(1, size); err != nil {
+ t.Fatal(err)
+ }
+ // Give the handler quota to write to connection-level
+ // window as well
+ if err := st.fr.WriteWindowUpdate(0, size); err != nil {
+ t.Fatal(err)
+ }
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "200"},
+ {"content-type", "text/plain; charset=utf-8"}, // sniffed
+ // and no content-length
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+ var bytes, frames int
+ for {
+ df := st.wantData()
+ bytes += len(df.Data())
+ frames++
+ for _, b := range df.Data() {
+ if b != 'a' {
+ t.Fatal("non-'a' byte seen in DATA")
+ }
+ }
+ if df.StreamEnded() {
+ break
+ }
+ }
+ if bytes != size {
+ t.Errorf("Got %d bytes; want %d", bytes, size)
+ }
+ if want := int(size / maxFrameSize); frames < want || frames > want*2 {
+ t.Errorf("Got %d frames; want %d", frames, size)
+ }
+ })
+}
+
+// Test that the handler can't write more than the client allows
+func TestServer_Response_LargeWrite_FlowControlled(t *testing.T) {
+ const size = 1 << 20
+ const maxFrameSize = 16 << 10
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.(http.Flusher).Flush()
+ n, err := w.Write(bytes.Repeat([]byte("a"), size))
+ if err != nil {
+ return fmt.Errorf("Write error: %v", err)
+ }
+ if n != size {
+ return fmt.Errorf("wrong size %d from Write", n)
+ }
+ return nil
+ }, func(st *serverTester) {
+ // Set the window size to something explicit for this test.
+ // It's also how much initial data we expect.
+ const initWindowSize = 123
+ if err := st.fr.WriteSettings(
+ Setting{SettingInitialWindowSize, initWindowSize},
+ Setting{SettingMaxFrameSize, maxFrameSize},
+ ); err != nil {
+ t.Fatal(err)
+ }
+ st.wantSettingsAck()
+
+ getSlash(st) // make the single request
+ defer func() { st.fr.WriteRSTStream(1, ErrCodeCancel) }()
+
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+
+ df := st.wantData()
+ if got := len(df.Data()); got != initWindowSize {
+ t.Fatalf("Initial window size = %d but got DATA with %d bytes", initWindowSize, got)
+ }
+
+ for _, quota := range []int{1, 13, 127} {
+ if err := st.fr.WriteWindowUpdate(1, uint32(quota)); err != nil {
+ t.Fatal(err)
+ }
+ df := st.wantData()
+ if int(quota) != len(df.Data()) {
+ t.Fatalf("read %d bytes after giving %d quota", len(df.Data()), quota)
+ }
+ }
+
+ if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+// Test that the handler blocked in a Write is unblocked if the server sends a RST_STREAM.
+func TestServer_Response_RST_Unblocks_LargeWrite(t *testing.T) {
+ const size = 1 << 20
+ const maxFrameSize = 16 << 10
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.(http.Flusher).Flush()
+ errc := make(chan error, 1)
+ go func() {
+ _, err := w.Write(bytes.Repeat([]byte("a"), size))
+ errc <- err
+ }()
+ select {
+ case err := <-errc:
+ if err == nil {
+ return errors.New("unexpected nil error from Write in handler")
+ }
+ return nil
+ case <-time.After(2 * time.Second):
+ return errors.New("timeout waiting for Write in handler")
+ }
+ }, func(st *serverTester) {
+ if err := st.fr.WriteSettings(
+ Setting{SettingInitialWindowSize, 0},
+ Setting{SettingMaxFrameSize, maxFrameSize},
+ ); err != nil {
+ t.Fatal(err)
+ }
+ st.wantSettingsAck()
+
+ getSlash(st) // make the single request
+
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+
+ if err := st.fr.WriteRSTStream(1, ErrCodeCancel); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+
+func TestServer_Response_Empty_Data_Not_FlowControlled(t *testing.T) {
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ w.(http.Flusher).Flush()
+ // Nothing; send empty DATA
+ return nil
+ }, func(st *serverTester) {
+ // Handler gets no data quota:
+ if err := st.fr.WriteSettings(Setting{SettingInitialWindowSize, 0}); err != nil {
+ t.Fatal(err)
+ }
+ st.wantSettingsAck()
+
+ getSlash(st) // make the single request
+
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+
+ df := st.wantData()
+ if got := len(df.Data()); got != 0 {
+ t.Fatalf("unexpected %d DATA bytes; want 0", got)
+ }
+ if !df.StreamEnded() {
+ t.Fatal("DATA didn't have END_STREAM")
+ }
+ })
+}
+
+func TestServer_Response_Automatic100Continue(t *testing.T) {
+ const msg = "foo"
+ const reply = "bar"
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ if v := r.Header.Get("Expect"); v != "" {
+ t.Errorf("Expect header = %q; want empty", v)
+ }
+ buf := make([]byte, len(msg))
+ // This read should trigger the 100-continue being sent.
+ if n, err := io.ReadFull(r.Body, buf); err != nil || n != len(msg) || string(buf) != msg {
+ return fmt.Errorf("ReadFull = %q, %v; want %q, nil", buf[:n], err, msg)
+ }
+ _, err := io.WriteString(w, reply)
+ return err
+ }, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1, // clients send odd numbers
+ BlockFragment: st.encodeHeader(":method", "POST", "expect", "100-continue"),
+ EndStream: false,
+ EndHeaders: true,
+ })
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth := decodeHeader(t, hf.HeaderBlockFragment())
+ wanth := [][2]string{
+ {":status", "100"},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Fatalf("Got headers %v; want %v", goth, wanth)
+ }
+
+ // Okay, they sent status 100, so we can send our
+ // gigantic and/or sensitive "foo" payload now.
+ st.writeData(1, true, []byte(msg))
+
+ st.wantWindowUpdate(0, uint32(len(msg)))
+
+ hf = st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("expected data to follow")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ goth = decodeHeader(t, hf.HeaderBlockFragment())
+ wanth = [][2]string{
+ {":status", "200"},
+ {"content-type", "text/plain; charset=utf-8"},
+ {"content-length", strconv.Itoa(len(reply))},
+ }
+ if !reflect.DeepEqual(goth, wanth) {
+ t.Errorf("Got headers %v; want %v", goth, wanth)
+ }
+
+ df := st.wantData()
+ if string(df.Data()) != reply {
+ t.Errorf("Client read %q; want %q", df.Data(), reply)
+ }
+ if !df.StreamEnded() {
+ t.Errorf("expect data stream end")
+ }
+ })
+}
+
+func TestServer_HandlerWriteErrorOnDisconnect(t *testing.T) {
+ errc := make(chan error, 1)
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ p := []byte("some data.\n")
+ for {
+ _, err := w.Write(p)
+ if err != nil {
+ errc <- err
+ return nil
+ }
+ }
+ }, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: false,
+ EndHeaders: true,
+ })
+ hf := st.wantHeaders()
+ if hf.StreamEnded() {
+ t.Fatal("unexpected END_STREAM flag")
+ }
+ if !hf.HeadersEnded() {
+ t.Fatal("want END_HEADERS flag")
+ }
+ // Close the connection and wait for the handler to (hopefully) notice.
+ st.cc.Close()
+ select {
+ case <-errc:
+ case <-time.After(5 * time.Second):
+ t.Error("timeout")
+ }
+ })
+}
+
+func TestServer_Rejects_Too_Many_Streams(t *testing.T) {
+ const testPath = "/some/path"
+
+ inHandler := make(chan uint32)
+ leaveHandler := make(chan bool)
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ id := w.(*responseWriter).rws.stream.id
+ inHandler <- id
+ if id == 1+(defaultMaxStreams+1)*2 && r.URL.Path != testPath {
+ t.Errorf("decoded final path as %q; want %q", r.URL.Path, testPath)
+ }
+ <-leaveHandler
+ })
+ defer st.Close()
+ st.greet()
+ nextStreamID := uint32(1)
+ streamID := func() uint32 {
+ defer func() { nextStreamID += 2 }()
+ return nextStreamID
+ }
+ sendReq := func(id uint32, headers ...string) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: id,
+ BlockFragment: st.encodeHeader(headers...),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ }
+ for i := 0; i < defaultMaxStreams; i++ {
+ sendReq(streamID())
+ <-inHandler
+ }
+ defer func() {
+ for i := 0; i < defaultMaxStreams; i++ {
+ leaveHandler <- true
+ }
+ }()
+
+ // And this one should cross the limit:
+ // (It's also sent as a CONTINUATION, to verify we still track the decoder context,
+ // even if we're rejecting it)
+ rejectID := streamID()
+ headerBlock := st.encodeHeader(":path", testPath)
+ frag1, frag2 := headerBlock[:3], headerBlock[3:]
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: rejectID,
+ BlockFragment: frag1,
+ EndStream: true,
+ EndHeaders: false, // CONTINUATION coming
+ })
+ if err := st.fr.WriteContinuation(rejectID, true, frag2); err != nil {
+ t.Fatal(err)
+ }
+ st.wantRSTStream(rejectID, ErrCodeProtocol)
+
+ // But let a handler finish:
+ leaveHandler <- true
+ st.wantHeaders()
+
+ // And now another stream should be able to start:
+ goodID := streamID()
+ sendReq(goodID, ":path", testPath)
+ select {
+ case got := <-inHandler:
+ if got != goodID {
+ t.Errorf("Got stream %d; want %d", got, goodID)
+ }
+ case <-time.After(3 * time.Second):
+ t.Error("timeout waiting for handler")
+ }
+}
+
+// So many response headers that the server needs to use CONTINUATION frames:
+func TestServer_Response_ManyHeaders_With_Continuation(t *testing.T) {
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ h := w.Header()
+ for i := 0; i < 5000; i++ {
+ h.Set(fmt.Sprintf("x-header-%d", i), fmt.Sprintf("x-value-%d", i))
+ }
+ return nil
+ }, func(st *serverTester) {
+ getSlash(st)
+ hf := st.wantHeaders()
+ if hf.HeadersEnded() {
+ t.Fatal("got unwanted END_HEADERS flag")
+ }
+ n := 0
+ for {
+ n++
+ cf := st.wantContinuation()
+ if cf.HeadersEnded() {
+ break
+ }
+ }
+ if n < 5 {
+ t.Errorf("Only got %d CONTINUATION frames; expected 5+ (currently 6)", n)
+ }
+ })
+}
+
+// This previously crashed (reported by Mathieu Lonjaret as observed
+// while using Camlistore) because we got a DATA frame from the client
+// after the handler exited and our logic at the time was wrong,
+// keeping a stream in the map in stateClosed, which tickled an
+// invariant check later when we tried to remove that stream (via
+// defer sc.closeAllStreamsOnConnClose) when the serverConn serve loop
+// ended.
+func TestServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) {
+ testServerResponse(t, func(w http.ResponseWriter, r *http.Request) error {
+ // nothing
+ return nil
+ }, func(st *serverTester) {
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: 1,
+ BlockFragment: st.encodeHeader(),
+ EndStream: false, // DATA is coming
+ EndHeaders: true,
+ })
+ hf := st.wantHeaders()
+ if !hf.HeadersEnded() || !hf.StreamEnded() {
+ t.Fatalf("want END_HEADERS+END_STREAM, got %v", hf)
+ }
+
+ // Sent when the a Handler closes while a client has
+ // indicated it's still sending DATA:
+ st.wantRSTStream(1, ErrCodeCancel)
+
+ // Now the handler has ended, so it's ended its
+ // stream, but the client hasn't closed its side
+ // (stateClosedLocal). So send more data and verify
+ // it doesn't crash with an internal invariant panic, like
+ // it did before.
+ st.writeData(1, true, []byte("foo"))
+
+ // Sent after a peer sends data anyway (admittedly the
+ // previous RST_STREAM might've still been in-flight),
+ // but they'll get the more friendly 'cancel' code
+ // first.
+ st.wantRSTStream(1, ErrCodeStreamClosed)
+
+ // Set up a bunch of machinery to record the panic we saw
+ // previously.
+ var (
+ panMu sync.Mutex
+ panicVal interface{}
+ )
+
+ testHookOnPanicMu.Lock()
+ testHookOnPanic = func(sc *serverConn, pv interface{}) bool {
+ panMu.Lock()
+ panicVal = pv
+ panMu.Unlock()
+ return true
+ }
+ testHookOnPanicMu.Unlock()
+
+ // Now force the serve loop to end, via closing the connection.
+ st.cc.Close()
+ select {
+ case <-st.sc.doneServing:
+ // Loop has exited.
+ panMu.Lock()
+ got := panicVal
+ panMu.Unlock()
+ if got != nil {
+ t.Errorf("Got panic: %v", got)
+ }
+ case <-time.After(5 * time.Second):
+ t.Error("timeout")
+ }
+ })
+}
+
+func TestServer_Rejects_TLS10(t *testing.T) { testRejectTLS(t, tls.VersionTLS10) }
+func TestServer_Rejects_TLS11(t *testing.T) { testRejectTLS(t, tls.VersionTLS11) }
+
+func testRejectTLS(t *testing.T, max uint16) {
+ st := newServerTester(t, nil, func(c *tls.Config) {
+ c.MaxVersion = max
+ })
+ defer st.Close()
+ gf := st.wantGoAway()
+ if got, want := gf.ErrCode, ErrCodeInadequateSecurity; got != want {
+ t.Errorf("Got error code %v; want %v", got, want)
+ }
+}
+
+func TestServer_Rejects_TLSBadCipher(t *testing.T) {
+ st := newServerTester(t, nil, func(c *tls.Config) {
+ // Only list bad ones:
+ c.CipherSuites = []uint16{
+ tls.TLS_RSA_WITH_RC4_128_SHA,
+ tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_RSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
+ tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ }
+ })
+ defer st.Close()
+ gf := st.wantGoAway()
+ if got, want := gf.ErrCode, ErrCodeInadequateSecurity; got != want {
+ t.Errorf("Got error code %v; want %v", got, want)
+ }
+}
+
+func TestServer_Advertises_Common_Cipher(t *testing.T) {
+ const requiredSuite = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+ st := newServerTester(t, nil, func(c *tls.Config) {
+ // Have the client only support the one required by the spec.
+ c.CipherSuites = []uint16{requiredSuite}
+ }, func(ts *httptest.Server) {
+ var srv *http.Server = ts.Config
+ // Have the server configured with one specific cipher suite
+ // which is banned. This tests that ConfigureServer ends up
+ // adding the good one to this list.
+ srv.TLSConfig = &tls.Config{
+ CipherSuites: []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}, // just a banned one
+ }
+ })
+ defer st.Close()
+ st.greet()
+}
+
+// TODO: move this onto *serverTester, and re-use the same hpack
+// decoding context throughout. We're just getting lucky here with
+// creating a new decoder each time.
+func decodeHeader(t *testing.T, headerBlock []byte) (pairs [][2]string) {
+ d := hpack.NewDecoder(initialHeaderTableSize, func(f hpack.HeaderField) {
+ pairs = append(pairs, [2]string{f.Name, f.Value})
+ })
+ if _, err := d.Write(headerBlock); err != nil {
+ t.Fatalf("hpack decoding error: %v", err)
+ }
+ if err := d.Close(); err != nil {
+ t.Fatalf("hpack decoding error: %v", err)
+ }
+ return
+}
+
+// testServerResponse sets up an idle HTTP/2 connection and lets you
+// write a single request with writeReq, and then reply to it in some way with the provided handler,
+// and then verify the output with the serverTester again (assuming the handler returns nil)
+func testServerResponse(t testing.TB,
+ handler func(http.ResponseWriter, *http.Request) error,
+ client func(*serverTester),
+) {
+ errc := make(chan error, 1)
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ if r.Body == nil {
+ t.Fatal("nil Body")
+ }
+ errc <- handler(w, r)
+ })
+ defer st.Close()
+
+ donec := make(chan bool)
+ go func() {
+ defer close(donec)
+ st.greet()
+ client(st)
+ }()
+
+ select {
+ case <-donec:
+ return
+ case <-time.After(5 * time.Second):
+ t.Fatal("timeout")
+ }
+
+ select {
+ case err := <-errc:
+ if err != nil {
+ t.Fatalf("Error in handler: %v", err)
+ }
+ case <-time.After(2 * time.Second):
+ t.Error("timeout waiting for handler to finish")
+ }
+}
+
+// readBodyHandler returns an http Handler func that reads len(want)
+// bytes from r.Body and fails t if the contents read were not
+// the value of want.
+func readBodyHandler(t *testing.T, want string) func(w http.ResponseWriter, r *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ buf := make([]byte, len(want))
+ _, err := io.ReadFull(r.Body, buf)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if string(buf) != want {
+ t.Errorf("read %q; want %q", buf, want)
+ }
+ }
+}
+
+// TestServerWithCurl currently fails, hence the LenientCipherSuites test. See:
+// https://github.com/tatsuhiro-t/nghttp2/issues/140 &
+// http://sourceforge.net/p/curl/bugs/1472/
+func TestServerWithCurl(t *testing.T) { testServerWithCurl(t, false) }
+func TestServerWithCurl_LenientCipherSuites(t *testing.T) { testServerWithCurl(t, true) }
+
+func testServerWithCurl(t *testing.T, permitProhibitedCipherSuites bool) {
+ if runtime.GOOS != "linux" {
+ t.Skip("skipping Docker test when not on Linux; requires --net which won't work with boot2docker anyway")
+ }
+ requireCurl(t)
+ const msg = "Hello from curl!\n"
+ ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Foo", "Bar")
+ w.Header().Set("Client-Proto", r.Proto)
+ io.WriteString(w, msg)
+ }))
+ ConfigureServer(ts.Config, &Server{
+ PermitProhibitedCipherSuites: permitProhibitedCipherSuites,
+ })
+ ts.TLS = ts.Config.TLSConfig // the httptest.Server has its own copy of this TLS config
+ ts.StartTLS()
+ defer ts.Close()
+
+ var gotConn int32
+ testHookOnConn = func() { atomic.StoreInt32(&gotConn, 1) }
+
+ t.Logf("Running test server for curl to hit at: %s", ts.URL)
+ container := curl(t, "--silent", "--http2", "--insecure", "-v", ts.URL)
+ defer kill(container)
+ resc := make(chan interface{}, 1)
+ go func() {
+ res, err := dockerLogs(container)
+ if err != nil {
+ resc <- err
+ } else {
+ resc <- res
+ }
+ }()
+ select {
+ case res := <-resc:
+ if err, ok := res.(error); ok {
+ t.Fatal(err)
+ }
+ if !strings.Contains(string(res.([]byte)), "foo: Bar") {
+ t.Errorf("didn't see foo: Bar header")
+ t.Logf("Got: %s", res)
+ }
+ if !strings.Contains(string(res.([]byte)), "client-proto: HTTP/2") {
+ t.Errorf("didn't see client-proto: HTTP/2 header")
+ t.Logf("Got: %s", res)
+ }
+ if !strings.Contains(string(res.([]byte)), msg) {
+ t.Errorf("didn't see %q content", msg)
+ t.Logf("Got: %s", res)
+ }
+ case <-time.After(3 * time.Second):
+ t.Errorf("timeout waiting for curl")
+ }
+
+ if atomic.LoadInt32(&gotConn) == 0 {
+ t.Error("never saw an http2 connection")
+ }
+}
+
+func BenchmarkServerGets(b *testing.B) {
+ b.ReportAllocs()
+
+ const msg = "Hello, world"
+ st := newServerTester(b, func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, msg)
+ })
+ defer st.Close()
+ st.greet()
+
+ // Give the server quota to reply. (plus it has the the 64KB)
+ if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
+ b.Fatal(err)
+ }
+
+ for i := 0; i < b.N; i++ {
+ id := 1 + uint32(i)*2
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: id,
+ BlockFragment: st.encodeHeader(),
+ EndStream: true,
+ EndHeaders: true,
+ })
+ st.wantHeaders()
+ df := st.wantData()
+ if !df.StreamEnded() {
+ b.Fatalf("DATA didn't have END_STREAM; got %v", df)
+ }
+ }
+}
+
+func BenchmarkServerPosts(b *testing.B) {
+ b.ReportAllocs()
+
+ const msg = "Hello, world"
+ st := newServerTester(b, func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, msg)
+ })
+ defer st.Close()
+ st.greet()
+
+ // Give the server quota to reply. (plus it has the the 64KB)
+ if err := st.fr.WriteWindowUpdate(0, uint32(b.N*len(msg))); err != nil {
+ b.Fatal(err)
+ }
+
+ for i := 0; i < b.N; i++ {
+ id := 1 + uint32(i)*2
+ st.writeHeaders(HeadersFrameParam{
+ StreamID: id,
+ BlockFragment: st.encodeHeader(":method", "POST"),
+ EndStream: false,
+ EndHeaders: true,
+ })
+ st.writeData(id, true, nil)
+ st.wantHeaders()
+ df := st.wantData()
+ if !df.StreamEnded() {
+ b.Fatalf("DATA didn't have END_STREAM; got %v", df)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/testdata/draft-ietf-httpbis-http2.xml b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/testdata/draft-ietf-httpbis-http2.xml
new file mode 100644
index 000000000..31a84bed4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/testdata/draft-ietf-httpbis-http2.xml
@@ -0,0 +1,5021 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol version 2
+
+
+ Twist
+
+ mbelshe@chromium.org
+
+
+
+
+ Google, Inc
+
+ fenix@google.com
+
+
+
+
+ Mozilla
+
+
+ 331 E Evelyn Street
+ Mountain View
+ CA
+ 94041
+ US
+
+ martin.thomson@gmail.com
+
+
+
+
+ Applications
+ HTTPbis
+ HTTP
+ SPDY
+ Web
+
+
+
+ This specification describes an optimized expression of the semantics of the Hypertext
+ Transfer Protocol (HTTP). HTTP/2 enables a more efficient use of network resources and a
+ reduced perception of latency by introducing header field compression and allowing multiple
+ concurrent messages on the same connection. It also introduces unsolicited push of
+ representations from servers to clients.
+
+
+ This specification is an alternative to, but does not obsolete, the HTTP/1.1 message syntax.
+ HTTP's existing semantics remain unchanged.
+
+
+
+
+
+ Discussion of this draft takes place on the HTTPBIS working group mailing list
+ (ietf-http-wg@w3.org), which is archived at .
+
+
+ Working Group information can be found at ; that specific to HTTP/2 are at .
+
+
+ The changes in this draft are summarized in .
+
+
+
+
+
+
+
+
+
+ The Hypertext Transfer Protocol (HTTP) is a wildly successful protocol. However, the
+ HTTP/1.1 message format ( ) has
+ several characteristics that have a negative overall effect on application performance
+ today.
+
+
+ In particular, HTTP/1.0 allowed only one request to be outstanding at a time on a given
+ TCP connection. HTTP/1.1 added request pipelining, but this only partially addressed
+ request concurrency and still suffers from head-of-line blocking. Therefore, HTTP/1.1
+ clients that need to make many requests typically use multiple connections to a server in
+ order to achieve concurrency and thereby reduce latency.
+
+
+ Furthermore, HTTP header fields are often repetitive and verbose, causing unnecessary
+ network traffic, as well as causing the initial TCP congestion
+ window to quickly fill. This can result in excessive latency when multiple requests are
+ made on a new TCP connection.
+
+
+ HTTP/2 addresses these issues by defining an optimized mapping of HTTP's semantics to an
+ underlying connection. Specifically, it allows interleaving of request and response
+ messages on the same connection and uses an efficient coding for HTTP header fields. It
+ also allows prioritization of requests, letting more important requests complete more
+ quickly, further improving performance.
+
+
+ The resulting protocol is more friendly to the network, because fewer TCP connections can
+ be used in comparison to HTTP/1.x. This means less competition with other flows, and
+ longer-lived connections, which in turn leads to better utilization of available network
+ capacity.
+
+
+ Finally, HTTP/2 also enables more efficient processing of messages through use of binary
+ message framing.
+
+
+
+
+
+ HTTP/2 provides an optimized transport for HTTP semantics. HTTP/2 supports all of the core
+ features of HTTP/1.1, but aims to be more efficient in several ways.
+
+
+ The basic protocol unit in HTTP/2 is a frame . Each frame
+ type serves a different purpose. For example, HEADERS and
+ DATA frames form the basis of HTTP requests and
+ responses ; other frame types like SETTINGS ,
+ WINDOW_UPDATE , and PUSH_PROMISE are used in support of other
+ HTTP/2 features.
+
+
+ Multiplexing of requests is achieved by having each HTTP request-response exchange
+ associated with its own stream . Streams are largely
+ independent of each other, so a blocked or stalled request or response does not prevent
+ progress on other streams.
+
+
+ Flow control and prioritization ensure that it is possible to efficiently use multiplexed
+ streams. Flow control helps to ensure that only data that
+ can be used by a receiver is transmitted. Prioritization ensures that limited resources can be directed
+ to the most important streams first.
+
+
+ HTTP/2 adds a new interaction mode, whereby a server can push
+ responses to a client . Server push allows a server to speculatively send a client
+ data that the server anticipates the client will need, trading off some network usage
+ against a potential latency gain. The server does this by synthesizing a request, which it
+ sends as a PUSH_PROMISE frame. The server is then able to send a response to
+ the synthetic request on a separate stream.
+
+
+ Frames that contain HTTP header fields are compressed .
+ HTTP requests can be highly redundant, so compression can reduce the size of requests and
+ responses significantly.
+
+
+
+
+ The HTTP/2 specification is split into four parts:
+
+
+ Starting HTTP/2 covers how an HTTP/2 connection is
+ initiated.
+
+
+ The framing and streams layers describe the way HTTP/2 frames are
+ structured and formed into multiplexed streams.
+
+
+ Frame and error
+ definitions include details of the frame and error types used in HTTP/2.
+
+
+ HTTP mappings and additional
+ requirements describe how HTTP semantics are expressed using frames and
+ streams.
+
+
+
+
+ While some of the frame and stream layer concepts are isolated from HTTP, this
+ specification does not define a completely generic framing layer. The framing and streams
+ layers are tailored to the needs of the HTTP protocol and server push.
+
+
+
+
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD
+ NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as
+ described in RFC 2119 .
+
+
+ All numeric values are in network byte order. Values are unsigned unless otherwise
+ indicated. Literal values are provided in decimal or hexadecimal as appropriate.
+ Hexadecimal literals are prefixed with 0x to distinguish them
+ from decimal literals.
+
+
+ The following terms are used:
+
+
+ The endpoint initiating the HTTP/2 connection.
+
+
+ A transport-layer connection between two endpoints.
+
+
+ An error that affects the entire HTTP/2 connection.
+
+
+ Either the client or server of the connection.
+
+
+ The smallest unit of communication within an HTTP/2 connection, consisting of a header
+ and a variable-length sequence of octets structured according to the frame type.
+
+
+ An endpoint. When discussing a particular endpoint, "peer" refers to the endpoint
+ that is remote to the primary subject of discussion.
+
+
+ An endpoint that is receiving frames.
+
+
+ An endpoint that is transmitting frames.
+
+
+ The endpoint which did not initiate the HTTP/2 connection.
+
+
+ A bi-directional flow of frames across a virtual channel within the HTTP/2 connection.
+
+
+ An error on the individual HTTP/2 stream.
+
+
+
+
+ Finally, the terms "gateway", "intermediary", "proxy", and "tunnel" are defined
+ in .
+
+
+
+
+
+
+ An HTTP/2 connection is an application layer protocol running on top of a TCP connection
+ ( ). The client is the TCP connection initiator.
+
+
+ HTTP/2 uses the same "http" and "https" URI schemes used by HTTP/1.1. HTTP/2 shares the same
+ default port numbers: 80 for "http" URIs and 443 for "https" URIs. As a result,
+ implementations processing requests for target resource URIs like http://example.org/foo or https://example.com/bar are required to first discover whether the
+ upstream server (the immediate peer to which the client wishes to establish a connection)
+ supports HTTP/2.
+
+
+
+ The means by which support for HTTP/2 is determined is different for "http" and "https"
+ URIs. Discovery for "http" URIs is described in . Discovery
+ for "https" URIs is described in .
+
+
+
+
+ The protocol defined in this document has two identifiers.
+
+
+
+ The string "h2" identifies the protocol where HTTP/2 uses TLS . This identifier is used in the TLS application layer protocol negotiation extension (ALPN)
+ field and any place that HTTP/2 over TLS is identified.
+
+
+ The "h2" string is serialized into an ALPN protocol identifier as the two octet
+ sequence: 0x68, 0x32.
+
+
+
+
+ The string "h2c" identifies the protocol where HTTP/2 is run over cleartext TCP.
+ This identifier is used in the HTTP/1.1 Upgrade header field and any place that
+ HTTP/2 over TCP is identified.
+
+
+
+
+
+ Negotiating "h2" or "h2c" implies the use of the transport, security, framing and message
+ semantics described in this document.
+
+
+ RFC Editor's Note: please remove the remainder of this section prior to the
+ publication of a final version of this document.
+
+
+ Only implementations of the final, published RFC can identify themselves as "h2" or "h2c".
+ Until such an RFC exists, implementations MUST NOT identify themselves using these
+ strings.
+
+
+ Examples and text throughout the rest of this document use "h2" as a matter of
+ editorial convenience only. Implementations of draft versions MUST NOT identify using
+ this string.
+
+
+ Implementations of draft versions of the protocol MUST add the string "-" and the
+ corresponding draft number to the identifier. For example, draft-ietf-httpbis-http2-11
+ over TLS is identified using the string "h2-11".
+
+
+ Non-compatible experiments that are based on these draft versions MUST append the string
+ "-" and an experiment name to the identifier. For example, an experimental implementation
+ of packet mood-based encoding based on draft-ietf-httpbis-http2-09 might identify itself
+ as "h2-09-emo". Note that any label MUST conform to the "token" syntax defined in
+ . Experimenters are
+ encouraged to coordinate their experiments on the ietf-http-wg@w3.org mailing list.
+
+
+
+
+
+ A client that makes a request for an "http" URI without prior knowledge about support for
+ HTTP/2 uses the HTTP Upgrade mechanism ( ). The client makes an HTTP/1.1 request that includes an Upgrade
+ header field identifying HTTP/2 with the "h2c" token. The HTTP/1.1 request MUST include
+ exactly one HTTP2-Settings header field.
+
+
+ For example:
+
+
+]]>
+
+
+ Requests that contain an entity body MUST be sent in their entirety before the client can
+ send HTTP/2 frames. This means that a large request entity can block the use of the
+ connection until it is completely sent.
+
+
+ If concurrency of an initial request with subsequent requests is important, an OPTIONS
+ request can be used to perform the upgrade to HTTP/2, at the cost of an additional
+ round-trip.
+
+
+ A server that does not support HTTP/2 can respond to the request as though the Upgrade
+ header field were absent:
+
+
+
+HTTP/1.1 200 OK
+Content-Length: 243
+Content-Type: text/html
+
+...
+
+
+
+ A server MUST ignore a "h2" token in an Upgrade header field. Presence of a token with
+ "h2" implies HTTP/2 over TLS, which is instead negotiated as described in .
+
+
+ A server that supports HTTP/2 can accept the upgrade with a 101 (Switching Protocols)
+ response. After the empty line that terminates the 101 response, the server can begin
+ sending HTTP/2 frames. These frames MUST include a response to the request that initiated
+ the Upgrade.
+
+
+
+
+ For example:
+
+
+HTTP/1.1 101 Switching Protocols
+Connection: Upgrade
+Upgrade: h2c
+
+[ HTTP/2 connection ...
+
+
+
+ The first HTTP/2 frame sent by the server is a SETTINGS frame ( ) as the server connection preface ( ). Upon receiving the 101 response, the client sends a connection preface , which includes a
+ SETTINGS frame.
+
+
+ The HTTP/1.1 request that is sent prior to upgrade is assigned stream identifier 1 and is
+ assigned default priority values . Stream 1 is
+ implicitly half closed from the client toward the server, since the request is completed
+ as an HTTP/1.1 request. After commencing the HTTP/2 connection, stream 1 is used for the
+ response.
+
+
+
+
+ A request that upgrades from HTTP/1.1 to HTTP/2 MUST include exactly one HTTP2-Settings header field. The HTTP2-Settings header field is a connection-specific header field
+ that includes parameters that govern the HTTP/2 connection, provided in anticipation of
+ the server accepting the request to upgrade.
+
+
+
+
+
+ A server MUST NOT upgrade the connection to HTTP/2 if this header field is not present,
+ or if more than one is present. A server MUST NOT send this header field.
+
+
+
+ The content of the HTTP2-Settings header field is the
+ payload of a SETTINGS frame ( ), encoded as a
+ base64url string (that is, the URL- and filename-safe Base64 encoding described in , with any trailing '=' characters omitted). The
+ ABNF production for token68 is
+ defined in .
+
+
+ Since the upgrade is only intended to apply to the immediate connection, a client
+ sending HTTP2-Settings MUST also send HTTP2-Settings as a connection option in the Connection header field to prevent it from being forwarded
+ downstream.
+
+
+ A server decodes and interprets these values as it would any other
+ SETTINGS frame. Acknowledgement of the
+ SETTINGS parameters is not necessary, since a 101 response serves as implicit
+ acknowledgment. Providing these values in the Upgrade request gives a client an
+ opportunity to provide parameters prior to receiving any frames from the server.
+
+
+
+
+
+
+ A client that makes a request to an "https" URI uses TLS
+ with the application layer protocol negotiation extension .
+
+
+ HTTP/2 over TLS uses the "h2" application token. The "h2c" token MUST NOT be sent by a
+ client or selected by a server.
+
+
+ Once TLS negotiation is complete, both the client and the server send a connection preface .
+
+
+
+
+
+ A client can learn that a particular server supports HTTP/2 by other means. For example,
+ describes a mechanism for advertising this capability.
+
+
+ A client MAY immediately send HTTP/2 frames to a server that is known to support HTTP/2,
+ after the connection preface ; a server can
+ identify such a connection by the presence of the connection preface. This only affects
+ the establishment of HTTP/2 connections over cleartext TCP; implementations that support
+ HTTP/2 over TLS MUST use protocol negotiation in TLS .
+
+
+ Without additional information, prior support for HTTP/2 is not a strong signal that a
+ given server will support HTTP/2 for future connections. For example, it is possible for
+ server configurations to change, for configurations to differ between instances in
+ clustered servers, or for network conditions to change.
+
+
+
+
+
+ Upon establishment of a TCP connection and determination that HTTP/2 will be used by both
+ peers, each endpoint MUST send a connection preface as a final confirmation and to
+ establish the initial SETTINGS parameters for the HTTP/2 connection. The client and
+ server each send a different connection preface.
+
+
+ The client connection preface starts with a sequence of 24 octets, which in hex notation
+ are:
+
+
+
+
+
+ (the string PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n ). This sequence
+ is followed by a SETTINGS frame ( ). The
+ SETTINGS frame MAY be empty. The client sends the client connection
+ preface immediately upon receipt of a 101 Switching Protocols response (indicating a
+ successful upgrade), or as the first application data octets of a TLS connection. If
+ starting an HTTP/2 connection with prior knowledge of server support for the protocol, the
+ client connection preface is sent upon connection establishment.
+
+
+
+
+ The client connection preface is selected so that a large proportion of HTTP/1.1 or
+ HTTP/1.0 servers and intermediaries do not attempt to process further frames. Note
+ that this does not address the concerns raised in .
+
+
+
+
+ The server connection preface consists of a potentially empty SETTINGS
+ frame ( ) that MUST be the first frame the server sends in the
+ HTTP/2 connection.
+
+
+ The SETTINGS frames received from a peer as part of the connection preface
+ MUST be acknowledged (see ) after sending the connection
+ preface.
+
+
+ To avoid unnecessary latency, clients are permitted to send additional frames to the
+ server immediately after sending the client connection preface, without waiting to receive
+ the server connection preface. It is important to note, however, that the server
+ connection preface SETTINGS frame might include parameters that necessarily
+ alter how a client is expected to communicate with the server. Upon receiving the
+ SETTINGS frame, the client is expected to honor any parameters established.
+ In some configurations, it is possible for the server to transmit SETTINGS
+ before the client sends additional frames, providing an opportunity to avoid this issue.
+
+
+ Clients and servers MUST treat an invalid connection preface as a connection error of type
+ PROTOCOL_ERROR . A GOAWAY frame ( )
+ MAY be omitted in this case, since an invalid preface indicates that the peer is not using
+ HTTP/2.
+
+
+
+
+
+
+ Once the HTTP/2 connection is established, endpoints can begin exchanging frames.
+
+
+
+
+ All frames begin with a fixed 9-octet header followed by a variable-length payload.
+
+
+
+
+
+ The fields of the frame header are defined as:
+
+
+
+ The length of the frame payload expressed as an unsigned 24-bit integer. Values
+ greater than 214 (16,384) MUST NOT be sent unless the receiver has
+ set a larger value for SETTINGS_MAX_FRAME_SIZE .
+
+
+ The 9 octets of the frame header are not included in this value.
+
+
+
+
+ The 8-bit type of the frame. The frame type determines the format and semantics of
+ the frame. Implementations MUST ignore and discard any frame that has a type that
+ is unknown.
+
+
+
+
+ An 8-bit field reserved for frame-type specific boolean flags.
+
+
+ Flags are assigned semantics specific to the indicated frame type. Flags that have
+ no defined semantics for a particular frame type MUST be ignored, and MUST be left
+ unset (0) when sending.
+
+
+
+
+ A reserved 1-bit field. The semantics of this bit are undefined and the bit MUST
+ remain unset (0) when sending and MUST be ignored when receiving.
+
+
+
+
+ A 31-bit stream identifier (see ). The value 0 is
+ reserved for frames that are associated with the connection as a whole as opposed to
+ an individual stream.
+
+
+
+
+
+ The structure and content of the frame payload is dependent entirely on the frame type.
+
+
+
+
+
+ The size of a frame payload is limited by the maximum size that a receiver advertises in
+ the SETTINGS_MAX_FRAME_SIZE setting. This setting can have any value
+ between 214 (16,384) and 224 -1 (16,777,215) octets,
+ inclusive.
+
+
+ All implementations MUST be capable of receiving and minimally processing frames up to
+ 214 octets in length, plus the 9 octet frame
+ header . The size of the frame header is not included when describing frame sizes.
+
+
+ Certain frame types, such as PING , impose additional limits
+ on the amount of payload data allowed.
+
+
+
+
+ If a frame size exceeds any defined limit, or is too small to contain mandatory frame
+ data, the endpoint MUST send a FRAME_SIZE_ERROR error. A frame size error
+ in a frame that could alter the state of the entire connection MUST be treated as a connection error ; this includes any frame carrying
+ a header block (that is, HEADERS ,
+ PUSH_PROMISE , and CONTINUATION ), SETTINGS ,
+ and any WINDOW_UPDATE frame with a stream identifier of 0.
+
+
+ Endpoints are not obligated to use all available space in a frame. Responsiveness can be
+ improved by using frames that are smaller than the permitted maximum size. Sending large
+ frames can result in delays in sending time-sensitive frames (such
+ RST_STREAM , WINDOW_UPDATE , or PRIORITY )
+ which if blocked by the transmission of a large frame, could affect performance.
+
+
+
+
+
+ Just as in HTTP/1, a header field in HTTP/2 is a name with one or more associated values.
+ They are used within HTTP request and response messages as well as server push operations
+ (see ).
+
+
+ Header lists are collections of zero or more header fields. When transmitted over a
+ connection, a header list is serialized into a header block using HTTP Header Compression . The serialized header block is then
+ divided into one or more octet sequences, called header block fragments, and transmitted
+ within the payload of HEADERS , PUSH_PROMISE or CONTINUATION frames.
+
+
+ The Cookie header field is treated specially by the HTTP
+ mapping (see ).
+
+
+ A receiving endpoint reassembles the header block by concatenating its fragments, then
+ decompresses the block to reconstruct the header list.
+
+
+ A complete header block consists of either:
+
+
+ a single HEADERS or PUSH_PROMISE frame,
+ with the END_HEADERS flag set, or
+
+
+ a HEADERS or PUSH_PROMISE frame with the END_HEADERS
+ flag cleared and one or more CONTINUATION frames,
+ where the last CONTINUATION frame has the END_HEADERS flag set.
+
+
+
+
+ Header compression is stateful. One compression context and one decompression context is
+ used for the entire connection. Each header block is processed as a discrete unit.
+ Header blocks MUST be transmitted as a contiguous sequence of frames, with no interleaved
+ frames of any other type or from any other stream. The last frame in a sequence of
+ HEADERS or CONTINUATION frames MUST have the END_HEADERS
+ flag set. The last frame in a sequence of PUSH_PROMISE or
+ CONTINUATION frames MUST have the END_HEADERS flag set. This allows a
+ header block to be logically equivalent to a single frame.
+
+
+ Header block fragments can only be sent as the payload of HEADERS ,
+ PUSH_PROMISE or CONTINUATION frames, because these frames
+ carry data that can modify the compression context maintained by a receiver. An endpoint
+ receiving HEADERS , PUSH_PROMISE or
+ CONTINUATION frames MUST reassemble header blocks and perform decompression
+ even if the frames are to be discarded. A receiver MUST terminate the connection with a
+ connection error of type
+ COMPRESSION_ERROR if it does not decompress a header block.
+
+
+
+
+
+
+ A "stream" is an independent, bi-directional sequence of frames exchanged between the client
+ and server within an HTTP/2 connection. Streams have several important characteristics:
+
+
+ A single HTTP/2 connection can contain multiple concurrently open streams, with either
+ endpoint interleaving frames from multiple streams.
+
+
+ Streams can be established and used unilaterally or shared by either the client or
+ server.
+
+
+ Streams can be closed by either endpoint.
+
+
+ The order in which frames are sent on a stream is significant. Recipients process frames
+ in the order they are received. In particular, the order of HEADERS ,
+ and DATA frames is semantically significant.
+
+
+ Streams are identified by an integer. Stream identifiers are assigned to streams by the
+ endpoint initiating the stream.
+
+
+
+
+
+
+ The lifecycle of a stream is shown in .
+
+
+
+
+ | |<-----------' |
+ | R | closed | R |
+ `-------------------->| |<--------------------'
+ +--------+
+
+ H: HEADERS frame (with implied CONTINUATIONs)
+ PP: PUSH_PROMISE frame (with implied CONTINUATIONs)
+ ES: END_STREAM flag
+ R: RST_STREAM frame
+]]>
+
+
+
+
+ Note that this diagram shows stream state transitions and the frames and flags that affect
+ those transitions only. In this regard, CONTINUATION frames do not result
+ in state transitions; they are effectively part of the HEADERS or
+ PUSH_PROMISE that they follow. For this purpose, the END_STREAM flag is
+ processed as a separate event to the frame that bears it; a HEADERS frame
+ with the END_STREAM flag set can cause two state transitions.
+
+
+ Both endpoints have a subjective view of the state of a stream that could be different
+ when frames are in transit. Endpoints do not coordinate the creation of streams; they are
+ created unilaterally by either endpoint. The negative consequences of a mismatch in
+ states are limited to the "closed" state after sending RST_STREAM , where
+ frames might be received for some time after closing.
+
+
+ Streams have the following states:
+
+
+
+
+
+ All streams start in the "idle" state. In this state, no frames have been
+ exchanged.
+
+
+ The following transitions are valid from this state:
+
+
+ Sending or receiving a HEADERS frame causes the stream to become
+ "open". The stream identifier is selected as described in . The same HEADERS frame can also
+ cause a stream to immediately become "half closed".
+
+
+ Sending a PUSH_PROMISE frame marks the associated stream for
+ later use. The stream state for the reserved stream transitions to "reserved
+ (local)".
+
+
+ Receiving a PUSH_PROMISE frame marks the associated stream as
+ reserved by the remote peer. The state of the stream becomes "reserved
+ (remote)".
+
+
+
+
+ Receiving any frames other than HEADERS or
+ PUSH_PROMISE on a stream in this state MUST be treated as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+
+
+ A stream in the "reserved (local)" state is one that has been promised by sending a
+ PUSH_PROMISE frame. A PUSH_PROMISE frame reserves an
+ idle stream by associating the stream with an open stream that was initiated by the
+ remote peer (see ).
+
+
+ In this state, only the following transitions are possible:
+
+
+ The endpoint can send a HEADERS frame. This causes the stream to
+ open in a "half closed (remote)" state.
+
+
+ Either endpoint can send a RST_STREAM frame to cause the stream
+ to become "closed". This releases the stream reservation.
+
+
+
+
+ An endpoint MUST NOT send any type of frame other than HEADERS or
+ RST_STREAM in this state.
+
+
+ A PRIORITY frame MAY be received in this state. Receiving any type
+ of frame other than RST_STREAM or PRIORITY on a stream
+ in this state MUST be treated as a connection
+ error of type PROTOCOL_ERROR .
+
+
+
+
+
+
+ A stream in the "reserved (remote)" state has been reserved by a remote peer.
+
+
+ In this state, only the following transitions are possible:
+
+
+ Receiving a HEADERS frame causes the stream to transition to
+ "half closed (local)".
+
+
+ Either endpoint can send a RST_STREAM frame to cause the stream
+ to become "closed". This releases the stream reservation.
+
+
+
+
+ An endpoint MAY send a PRIORITY frame in this state to reprioritize
+ the reserved stream. An endpoint MUST NOT send any type of frame other than
+ RST_STREAM , WINDOW_UPDATE , or PRIORITY
+ in this state.
+
+
+ Receiving any type of frame other than HEADERS or
+ RST_STREAM on a stream in this state MUST be treated as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+
+
+ A stream in the "open" state may be used by both peers to send frames of any type.
+ In this state, sending peers observe advertised stream
+ level flow control limits .
+
+
+ From this state either endpoint can send a frame with an END_STREAM flag set, which
+ causes the stream to transition into one of the "half closed" states: an endpoint
+ sending an END_STREAM flag causes the stream state to become "half closed (local)";
+ an endpoint receiving an END_STREAM flag causes the stream state to become "half
+ closed (remote)".
+
+
+ Either endpoint can send a RST_STREAM frame from this state, causing
+ it to transition immediately to "closed".
+
+
+
+
+
+
+ A stream that is in the "half closed (local)" state cannot be used for sending
+ frames. Only WINDOW_UPDATE , PRIORITY and
+ RST_STREAM frames can be sent in this state.
+
+
+ A stream transitions from this state to "closed" when a frame that contains an
+ END_STREAM flag is received, or when either peer sends a RST_STREAM
+ frame.
+
+
+ A receiver can ignore WINDOW_UPDATE frames in this state, which might
+ arrive for a short period after a frame bearing the END_STREAM flag is sent.
+
+
+ PRIORITY frames received in this state are used to reprioritize
+ streams that depend on the current stream.
+
+
+
+
+
+
+ A stream that is "half closed (remote)" is no longer being used by the peer to send
+ frames. In this state, an endpoint is no longer obligated to maintain a receiver
+ flow control window if it performs flow control.
+
+
+ If an endpoint receives additional frames for a stream that is in this state, other
+ than WINDOW_UPDATE , PRIORITY or
+ RST_STREAM , it MUST respond with a stream error of type
+ STREAM_CLOSED .
+
+
+ A stream that is "half closed (remote)" can be used by the endpoint to send frames
+ of any type. In this state, the endpoint continues to observe advertised stream level flow control limits .
+
+
+ A stream can transition from this state to "closed" by sending a frame that contains
+ an END_STREAM flag, or when either peer sends a RST_STREAM frame.
+
+
+
+
+
+
+ The "closed" state is the terminal state.
+
+
+ An endpoint MUST NOT send frames other than PRIORITY on a closed
+ stream. An endpoint that receives any frame other than PRIORITY
+ after receiving a RST_STREAM MUST treat that as a stream error of type
+ STREAM_CLOSED . Similarly, an endpoint that receives any frames after
+ receiving a frame with the END_STREAM flag set MUST treat that as a connection error of type
+ STREAM_CLOSED , unless the frame is permitted as described below.
+
+
+ WINDOW_UPDATE or RST_STREAM frames can be received in
+ this state for a short period after a DATA or HEADERS
+ frame containing an END_STREAM flag is sent. Until the remote peer receives and
+ processes RST_STREAM or the frame bearing the END_STREAM flag, it
+ might send frames of these types. Endpoints MUST ignore
+ WINDOW_UPDATE or RST_STREAM frames received in this
+ state, though endpoints MAY choose to treat frames that arrive a significant time
+ after sending END_STREAM as a connection
+ error of type PROTOCOL_ERROR .
+
+
+ PRIORITY frames can be sent on closed streams to prioritize streams
+ that are dependent on the closed stream. Endpoints SHOULD process
+ PRIORITY frame, though they can be ignored if the stream has been
+ removed from the dependency tree (see ).
+
+
+ If this state is reached as a result of sending a RST_STREAM frame,
+ the peer that receives the RST_STREAM might have already sent - or
+ enqueued for sending - frames on the stream that cannot be withdrawn. An endpoint
+ MUST ignore frames that it receives on closed streams after it has sent a
+ RST_STREAM frame. An endpoint MAY choose to limit the period over
+ which it ignores frames and treat frames that arrive after this time as being in
+ error.
+
+
+ Flow controlled frames (i.e., DATA ) received after sending
+ RST_STREAM are counted toward the connection flow control window.
+ Even though these frames might be ignored, because they are sent before the sender
+ receives the RST_STREAM , the sender will consider the frames to count
+ against the flow control window.
+
+
+ An endpoint might receive a PUSH_PROMISE frame after it sends
+ RST_STREAM . PUSH_PROMISE causes a stream to become
+ "reserved" even if the associated stream has been reset. Therefore, a
+ RST_STREAM is needed to close an unwanted promised stream.
+
+
+
+
+
+ In the absence of more specific guidance elsewhere in this document, implementations
+ SHOULD treat the receipt of a frame that is not expressly permitted in the description of
+ a state as a connection error of type
+ PROTOCOL_ERROR . Frame of unknown types are ignored.
+
+
+ An example of the state transitions for an HTTP request/response exchange can be found in
+ . An example of the state transitions for server push can be
+ found in and .
+
+
+
+
+ Streams are identified with an unsigned 31-bit integer. Streams initiated by a client
+ MUST use odd-numbered stream identifiers; those initiated by the server MUST use
+ even-numbered stream identifiers. A stream identifier of zero (0x0) is used for
+ connection control messages; the stream identifier zero cannot be used to establish a
+ new stream.
+
+
+ HTTP/1.1 requests that are upgraded to HTTP/2 (see ) are
+ responded to with a stream identifier of one (0x1). After the upgrade
+ completes, stream 0x1 is "half closed (local)" to the client. Therefore, stream 0x1
+ cannot be selected as a new stream identifier by a client that upgrades from HTTP/1.1.
+
+
+ The identifier of a newly established stream MUST be numerically greater than all
+ streams that the initiating endpoint has opened or reserved. This governs streams that
+ are opened using a HEADERS frame and streams that are reserved using
+ PUSH_PROMISE . An endpoint that receives an unexpected stream identifier
+ MUST respond with a connection error of
+ type PROTOCOL_ERROR .
+
+
+ The first use of a new stream identifier implicitly closes all streams in the "idle"
+ state that might have been initiated by that peer with a lower-valued stream identifier.
+ For example, if a client sends a HEADERS frame on stream 7 without ever
+ sending a frame on stream 5, then stream 5 transitions to the "closed" state when the
+ first frame for stream 7 is sent or received.
+
+
+ Stream identifiers cannot be reused. Long-lived connections can result in an endpoint
+ exhausting the available range of stream identifiers. A client that is unable to
+ establish a new stream identifier can establish a new connection for new streams. A
+ server that is unable to establish a new stream identifier can send a
+ GOAWAY frame so that the client is forced to open a new connection for
+ new streams.
+
+
+
+
+
+ A peer can limit the number of concurrently active streams using the
+ SETTINGS_MAX_CONCURRENT_STREAMS parameter (see ) within a SETTINGS frame. The maximum concurrent
+ streams setting is specific to each endpoint and applies only to the peer that receives
+ the setting. That is, clients specify the maximum number of concurrent streams the
+ server can initiate, and servers specify the maximum number of concurrent streams the
+ client can initiate.
+
+
+ Streams that are in the "open" state, or either of the "half closed" states count toward
+ the maximum number of streams that an endpoint is permitted to open. Streams in any of
+ these three states count toward the limit advertised in the
+ SETTINGS_MAX_CONCURRENT_STREAMS setting. Streams in either of the
+ "reserved" states do not count toward the stream limit.
+
+
+ Endpoints MUST NOT exceed the limit set by their peer. An endpoint that receives a
+ HEADERS frame that causes their advertised concurrent stream limit to be
+ exceeded MUST treat this as a stream error . An
+ endpoint that wishes to reduce the value of
+ SETTINGS_MAX_CONCURRENT_STREAMS to a value that is below the current
+ number of open streams can either close streams that exceed the new value or allow
+ streams to complete.
+
+
+
+
+
+
+ Using streams for multiplexing introduces contention over use of the TCP connection,
+ resulting in blocked streams. A flow control scheme ensures that streams on the same
+ connection do not destructively interfere with each other. Flow control is used for both
+ individual streams and for the connection as a whole.
+
+
+ HTTP/2 provides for flow control through use of the WINDOW_UPDATE frame .
+
+
+
+
+ HTTP/2 stream flow control aims to allow a variety of flow control algorithms to be
+ used without requiring protocol changes. Flow control in HTTP/2 has the following
+ characteristics:
+
+
+ Flow control is specific to a connection; i.e., it is "hop-by-hop", not
+ "end-to-end".
+
+
+ Flow control is based on window update frames. Receivers advertise how many octets
+ they are prepared to receive on a stream and for the entire connection. This is a
+ credit-based scheme.
+
+
+ Flow control is directional with overall control provided by the receiver. A
+ receiver MAY choose to set any window size that it desires for each stream and for
+ the entire connection. A sender MUST respect flow control limits imposed by a
+ receiver. Clients, servers and intermediaries all independently advertise their
+ flow control window as a receiver and abide by the flow control limits set by
+ their peer when sending.
+
+
+ The initial value for the flow control window is 65,535 octets for both new streams
+ and the overall connection.
+
+
+ The frame type determines whether flow control applies to a frame. Of the frames
+ specified in this document, only DATA frames are subject to flow
+ control; all other frame types do not consume space in the advertised flow control
+ window. This ensures that important control frames are not blocked by flow control.
+
+
+ Flow control cannot be disabled.
+
+
+ HTTP/2 defines only the format and semantics of the WINDOW_UPDATE
+ frame ( ). This document does not stipulate how a
+ receiver decides when to send this frame or the value that it sends, nor does it
+ specify how a sender chooses to send packets. Implementations are able to select
+ any algorithm that suits their needs.
+
+
+
+
+ Implementations are also responsible for managing how requests and responses are sent
+ based on priority; choosing how to avoid head of line blocking for requests; and
+ managing the creation of new streams. Algorithm choices for these could interact with
+ any flow control algorithm.
+
+
+
+
+
+ Flow control is defined to protect endpoints that are operating under resource
+ constraints. For example, a proxy needs to share memory between many connections, and
+ also might have a slow upstream connection and a fast downstream one. Flow control
+ addresses cases where the receiver is unable process data on one stream, yet wants to
+ continue to process other streams in the same connection.
+
+
+ Deployments that do not require this capability can advertise a flow control window of
+ the maximum size, incrementing the available space when new data is received. This
+ effectively disables flow control for that receiver. Conversely, a sender is always
+ subject to the flow control window advertised by the receiver.
+
+
+ Deployments with constrained resources (for example, memory) can employ flow control to
+ limit the amount of memory a peer can consume. Note, however, that this can lead to
+ suboptimal use of available network resources if flow control is enabled without
+ knowledge of the bandwidth-delay product (see ).
+
+
+ Even with full awareness of the current bandwidth-delay product, implementation of flow
+ control can be difficult. When using flow control, the receiver MUST read from the TCP
+ receive buffer in a timely fashion. Failure to do so could lead to a deadlock when
+ critical frames, such as WINDOW_UPDATE , are not read and acted upon.
+
+
+
+
+
+
+ A client can assign a priority for a new stream by including prioritization information in
+ the HEADERS frame that opens the stream. For an existing
+ stream, the PRIORITY frame can be used to change the
+ priority.
+
+
+ The purpose of prioritization is to allow an endpoint to express how it would prefer its
+ peer allocate resources when managing concurrent streams. Most importantly, priority can
+ be used to select streams for transmitting frames when there is limited capacity for
+ sending.
+
+
+ Streams can be prioritized by marking them as dependent on the completion of other streams
+ ( ). Each dependency is assigned a relative weight, a number
+ that is used to determine the relative proportion of available resources that are assigned
+ to streams dependent on the same stream.
+
+
+
+ Explicitly setting the priority for a stream is input to a prioritization process. It
+ does not guarantee any particular processing or transmission order for the stream relative
+ to any other stream. An endpoint cannot force a peer to process concurrent streams in a
+ particular order using priority. Expressing priority is therefore only ever a suggestion.
+
+
+ Providing prioritization information is optional, so default values are used if no
+ explicit indicator is provided ( ).
+
+
+
+
+ Each stream can be given an explicit dependency on another stream. Including a
+ dependency expresses a preference to allocate resources to the identified stream rather
+ than to the dependent stream.
+
+
+ A stream that is not dependent on any other stream is given a stream dependency of 0x0.
+ In other words, the non-existent stream 0 forms the root of the tree.
+
+
+ A stream that depends on another stream is a dependent stream. The stream upon which a
+ stream is dependent is a parent stream. A dependency on a stream that is not currently
+ in the tree - such as a stream in the "idle" state - results in that stream being given
+ a default priority .
+
+
+ When assigning a dependency on another stream, the stream is added as a new dependency
+ of the parent stream. Dependent streams that share the same parent are not ordered with
+ respect to each other. For example, if streams B and C are dependent on stream A, and
+ if stream D is created with a dependency on stream A, this results in a dependency order
+ of A followed by B, C, and D in any order.
+
+
+ /|\
+ B C B D C
+]]>
+
+
+ An exclusive flag allows for the insertion of a new level of dependencies. The
+ exclusive flag causes the stream to become the sole dependency of its parent stream,
+ causing other dependencies to become dependent on the exclusive stream. In the
+ previous example, if stream D is created with an exclusive dependency on stream A, this
+ results in D becoming the dependency parent of B and C.
+
+
+ D
+ B C / \
+ B C
+]]>
+
+
+ Inside the dependency tree, a dependent stream SHOULD only be allocated resources if all
+ of the streams that it depends on (the chain of parent streams up to 0x0) are either
+ closed, or it is not possible to make progress on them.
+
+
+ A stream cannot depend on itself. An endpoint MUST treat this as a stream error of type PROTOCOL_ERROR .
+
+
+
+
+
+ All dependent streams are allocated an integer weight between 1 and 256 (inclusive).
+
+
+ Streams with the same parent SHOULD be allocated resources proportionally based on their
+ weight. Thus, if stream B depends on stream A with weight 4, and C depends on stream A
+ with weight 12, and if no progress can be made on A, stream B ideally receives one third
+ of the resources allocated to stream C.
+
+
+
+
+
+ Stream priorities are changed using the PRIORITY frame. Setting a
+ dependency causes a stream to become dependent on the identified parent stream.
+
+
+ Dependent streams move with their parent stream if the parent is reprioritized. Setting
+ a dependency with the exclusive flag for a reprioritized stream moves all the
+ dependencies of the new parent stream to become dependent on the reprioritized stream.
+
+
+ If a stream is made dependent on one of its own dependencies, the formerly dependent
+ stream is first moved to be dependent on the reprioritized stream's previous parent.
+ The moved dependency retains its weight.
+
+
+
+ For example, consider an original dependency tree where B and C depend on A, D and E
+ depend on C, and F depends on D. If A is made dependent on D, then D takes the place
+ of A. All other dependency relationships stay the same, except for F, which becomes
+ dependent on A if the reprioritization is exclusive.
+
+ F B C ==> F A OR A
+ / \ | / \ /|\
+ D E E B C B C F
+ | | |
+ F E E
+ (intermediate) (non-exclusive) (exclusive)
+]]>
+
+
+
+
+
+ When a stream is removed from the dependency tree, its dependencies can be moved to
+ become dependent on the parent of the closed stream. The weights of new dependencies
+ are recalculated by distributing the weight of the dependency of the closed stream
+ proportionally based on the weights of its dependencies.
+
+
+ Streams that are removed from the dependency tree cause some prioritization information
+ to be lost. Resources are shared between streams with the same parent stream, which
+ means that if a stream in that set closes or becomes blocked, any spare capacity
+ allocated to a stream is distributed to the immediate neighbors of the stream. However,
+ if the common dependency is removed from the tree, those streams share resources with
+ streams at the next highest level.
+
+
+ For example, assume streams A and B share a parent, and streams C and D both depend on
+ stream A. Prior to the removal of stream A, if streams A and D are unable to proceed,
+ then stream C receives all the resources dedicated to stream A. If stream A is removed
+ from the tree, the weight of stream A is divided between streams C and D. If stream D
+ is still unable to proceed, this results in stream C receiving a reduced proportion of
+ resources. For equal starting weights, C receives one third, rather than one half, of
+ available resources.
+
+
+ It is possible for a stream to become closed while prioritization information that
+ creates a dependency on that stream is in transit. If a stream identified in a
+ dependency has no associated priority information, then the dependent stream is instead
+ assigned a default priority . This potentially creates
+ suboptimal prioritization, since the stream could be given a priority that is different
+ to what is intended.
+
+
+ To avoid these problems, an endpoint SHOULD retain stream prioritization state for a
+ period after streams become closed. The longer state is retained, the lower the chance
+ that streams are assigned incorrect or default priority values.
+
+
+ This could create a large state burden for an endpoint, so this state MAY be limited.
+ An endpoint MAY apply a fixed upper limit on the number of closed streams for which
+ prioritization state is tracked to limit state exposure. The amount of additional state
+ an endpoint maintains could be dependent on load; under high load, prioritization state
+ can be discarded to limit resource commitments. In extreme cases, an endpoint could
+ even discard prioritization state for active or reserved streams. If a fixed limit is
+ applied, endpoints SHOULD maintain state for at least as many streams as allowed by
+ their setting for SETTINGS_MAX_CONCURRENT_STREAMS .
+
+
+ An endpoint receiving a PRIORITY frame that changes the priority of a
+ closed stream SHOULD alter the dependencies of the streams that depend on it, if it has
+ retained enough state to do so.
+
+
+
+
+
+ Providing priority information is optional. Streams are assigned a non-exclusive
+ dependency on stream 0x0 by default. Pushed streams
+ initially depend on their associated stream. In both cases, streams are assigned a
+ default weight of 16.
+
+
+
+
+
+
+ HTTP/2 framing permits two classes of error:
+
+
+ An error condition that renders the entire connection unusable is a connection error.
+
+
+ An error in an individual stream is a stream error.
+
+
+
+
+ A list of error codes is included in .
+
+
+
+
+ A connection error is any error which prevents further processing of the framing layer,
+ or which corrupts any connection state.
+
+
+ An endpoint that encounters a connection error SHOULD first send a GOAWAY
+ frame ( ) with the stream identifier of the last stream that it
+ successfully received from its peer. The GOAWAY frame includes an error
+ code that indicates why the connection is terminating. After sending the
+ GOAWAY frame, the endpoint MUST close the TCP connection.
+
+
+ It is possible that the GOAWAY will not be reliably received by the
+ receiving endpoint (see ). In the event of a connection error,
+ GOAWAY only provides a best effort attempt to communicate with the peer
+ about why the connection is being terminated.
+
+
+ An endpoint can end a connection at any time. In particular, an endpoint MAY choose to
+ treat a stream error as a connection error. Endpoints SHOULD send a
+ GOAWAY frame when ending a connection, providing that circumstances
+ permit it.
+
+
+
+
+
+ A stream error is an error related to a specific stream that does not affect processing
+ of other streams.
+
+
+ An endpoint that detects a stream error sends a RST_STREAM frame ( ) that contains the stream identifier of the stream where the error
+ occurred. The RST_STREAM frame includes an error code that indicates the
+ type of error.
+
+
+ A RST_STREAM is the last frame that an endpoint can send on a stream.
+ The peer that sends the RST_STREAM frame MUST be prepared to receive any
+ frames that were sent or enqueued for sending by the remote peer. These frames can be
+ ignored, except where they modify connection state (such as the state maintained for
+ header compression , or flow control).
+
+
+ Normally, an endpoint SHOULD NOT send more than one RST_STREAM frame for
+ any stream. However, an endpoint MAY send additional RST_STREAM frames if
+ it receives frames on a closed stream after more than a round-trip time. This behavior
+ is permitted to deal with misbehaving implementations.
+
+
+ An endpoint MUST NOT send a RST_STREAM in response to an
+ RST_STREAM frame, to avoid looping.
+
+
+
+
+
+ If the TCP connection is closed or reset while streams remain in open or half closed
+ states, then the endpoint MUST assume that those streams were abnormally interrupted and
+ could be incomplete.
+
+
+
+
+
+
+ HTTP/2 permits extension of the protocol. Protocol extensions can be used to provide
+ additional services or alter any aspect of the protocol, within the limitations described
+ in this section. Extensions are effective only within the scope of a single HTTP/2
+ connection.
+
+
+ Extensions are permitted to use new frame types , new
+ settings , or new error
+ codes . Registries are established for managing these extension points: frame types , settings and
+ error codes .
+
+
+ Implementations MUST ignore unknown or unsupported values in all extensible protocol
+ elements. Implementations MUST discard frames that have unknown or unsupported types.
+ This means that any of these extension points can be safely used by extensions without
+ prior arrangement or negotiation. However, extension frames that appear in the middle of
+ a header block are not permitted; these MUST be treated
+ as a connection error of type
+ PROTOCOL_ERROR .
+
+
+ However, extensions that could change the semantics of existing protocol components MUST
+ be negotiated before being used. For example, an extension that changes the layout of the
+ HEADERS frame cannot be used until the peer has given a positive signal
+ that this is acceptable. In this case, it could also be necessary to coordinate when the
+ revised layout comes into effect. Note that treating any frame other than
+ DATA frames as flow controlled is such a change in semantics, and can only
+ be done through negotiation.
+
+
+ This document doesn't mandate a specific method for negotiating the use of an extension,
+ but notes that a setting could be used for that
+ purpose. If both peers set a value that indicates willingness to use the extension, then
+ the extension can be used. If a setting is used for extension negotiation, the initial
+ value MUST be defined so that the extension is initially disabled.
+
+
+
+
+
+
+ This specification defines a number of frame types, each identified by a unique 8-bit type
+ code. Each frame type serves a distinct purpose either in the establishment and management
+ of the connection as a whole, or of individual streams.
+
+
+ The transmission of specific frame types can alter the state of a connection. If endpoints
+ fail to maintain a synchronized view of the connection state, successful communication
+ within the connection will no longer be possible. Therefore, it is important that endpoints
+ have a shared comprehension of how the state is affected by the use any given frame.
+
+
+
+
+ DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated
+ with a stream. One or more DATA frames are used, for instance, to carry HTTP request or
+ response payloads.
+
+
+ DATA frames MAY also contain arbitrary padding. Padding can be added to DATA frames to
+ obscure the size of messages.
+
+
+
+
+
+ The DATA frame contains the following fields:
+
+
+ An 8-bit field containing the length of the frame padding in units of octets. This
+ field is optional and is only present if the PADDED flag is set.
+
+
+ Application data. The amount of data is the remainder of the frame payload after
+ subtracting the length of the other fields that are present.
+
+
+ Padding octets that contain no application semantic value. Padding octets MUST be set
+ to zero when sending and ignored when receiving.
+
+
+
+
+
+ The DATA frame defines the following flags:
+
+
+ Bit 1 being set indicates that this frame is the last that the endpoint will send for
+ the identified stream. Setting this flag causes the stream to enter one of the "half closed" states or the "closed" state .
+
+
+ Bit 4 being set indicates that the Pad Length field and any padding that it describes
+ is present.
+
+
+
+
+ DATA frames MUST be associated with a stream. If a DATA frame is received whose stream
+ identifier field is 0x0, the recipient MUST respond with a connection error of type
+ PROTOCOL_ERROR .
+
+
+ DATA frames are subject to flow control and can only be sent when a stream is in the
+ "open" or "half closed (remote)" states. The entire DATA frame payload is included in flow
+ control, including Pad Length and Padding fields if present. If a DATA frame is received
+ whose stream is not in "open" or "half closed (local)" state, the recipient MUST respond
+ with a stream error of type
+ STREAM_CLOSED .
+
+
+ The total number of padding octets is determined by the value of the Pad Length field. If
+ the length of the padding is greater than the length of the frame payload, the recipient
+ MUST treat this as a connection error of
+ type PROTOCOL_ERROR .
+
+
+ A frame can be increased in size by one octet by including a Pad Length field with a
+ value of zero.
+
+
+
+
+ Padding is a security feature; see .
+
+
+
+
+
+ The HEADERS frame (type=0x1) is used to open a stream ,
+ and additionally carries a header block fragment. HEADERS frames can be sent on a stream
+ in the "open" or "half closed (remote)" states.
+
+
+
+
+
+ The HEADERS frame payload has the following fields:
+
+
+ An 8-bit field containing the length of the frame padding in units of octets. This
+ field is only present if the PADDED flag is set.
+
+
+ A single bit flag indicates that the stream dependency is exclusive, see . This field is only present if the PRIORITY flag is set.
+
+
+ A 31-bit stream identifier for the stream that this stream depends on, see . This field is only present if the PRIORITY flag is set.
+
+
+ An 8-bit weight for the stream, see . Add one to the
+ value to obtain a weight between 1 and 256. This field is only present if the
+ PRIORITY flag is set.
+
+
+ A header block fragment .
+
+
+ Padding octets that contain no application semantic value. Padding octets MUST be set
+ to zero when sending and ignored when receiving.
+
+
+
+
+
+ The HEADERS frame defines the following flags:
+
+
+
+ Bit 1 being set indicates that the header block is
+ the last that the endpoint will send for the identified stream. Setting this flag
+ causes the stream to enter one of "half closed"
+ states .
+
+
+ A HEADERS frame carries the END_STREAM flag that signals the end of a stream.
+ However, a HEADERS frame with the END_STREAM flag set can be followed by
+ CONTINUATION frames on the same stream. Logically, the
+ CONTINUATION frames are part of the HEADERS frame.
+
+
+
+
+ Bit 3 being set indicates that this frame contains an entire header block and is not followed by any
+ CONTINUATION frames.
+
+
+ A HEADERS frame without the END_HEADERS flag set MUST be followed by a
+ CONTINUATION frame for the same stream. A receiver MUST treat the
+ receipt of any other type of frame or a frame on a different stream as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+ Bit 4 being set indicates that the Pad Length field and any padding that it
+ describes is present.
+
+
+
+
+ Bit 6 being set indicates that the Exclusive Flag (E), Stream Dependency, and Weight
+ fields are present; see .
+
+
+
+
+
+
+ The payload of a HEADERS frame contains a header block
+ fragment . A header block that does not fit within a HEADERS frame is continued in
+ a CONTINUATION frame .
+
+
+
+ HEADERS frames MUST be associated with a stream. If a HEADERS frame is received whose
+ stream identifier field is 0x0, the recipient MUST respond with a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+ The HEADERS frame changes the connection state as described in .
+
+
+
+ The HEADERS frame includes optional padding. Padding fields and flags are identical to
+ those defined for DATA frames .
+
+
+ Prioritization information in a HEADERS frame is logically equivalent to a separate
+ PRIORITY frame, but inclusion in HEADERS avoids the potential for churn in
+ stream prioritization when new streams are created. Priorization fields in HEADERS frames
+ subsequent to the first on a stream reprioritize the
+ stream .
+
+
+
+
+
+ The PRIORITY frame (type=0x2) specifies the sender-advised
+ priority of a stream . It can be sent at any time for an existing stream, including
+ closed streams. This enables reprioritization of existing streams.
+
+
+
+
+
+ The payload of a PRIORITY frame contains the following fields:
+
+
+ A single bit flag indicates that the stream dependency is exclusive, see .
+
+
+ A 31-bit stream identifier for the stream that this stream depends on, see .
+
+
+ An 8-bit weight for the identified stream dependency, see . Add one to the value to obtain a weight between 1 and 256.
+
+
+
+
+
+ The PRIORITY frame does not define any flags.
+
+
+
+ The PRIORITY frame is associated with an existing stream. If a PRIORITY frame is received
+ with a stream identifier of 0x0, the recipient MUST respond with a connection error of type
+ PROTOCOL_ERROR .
+
+
+ The PRIORITY frame can be sent on a stream in any of the "reserved (remote)", "open",
+ "half closed (local)", "half closed (remote)", or "closed" states, though it cannot be
+ sent between consecutive frames that comprise a single header
+ block . Note that this frame could arrive after processing or frame sending has
+ completed, which would cause it to have no effect on the current stream. For a stream
+ that is in the "half closed (remote)" or "closed" - state, this frame can only affect
+ processing of the current stream and not frame transmission.
+
+
+ The PRIORITY frame is the only frame that can be sent for a stream in the "closed" state.
+ This allows for the reprioritization of a group of dependent streams by altering the
+ priority of a parent stream, which might be closed. However, a PRIORITY frame sent on a
+ closed stream risks being ignored due to the peer having discarded priority state
+ information for that stream.
+
+
+
+
+
+ The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream. When sent by
+ the initiator of a stream, it indicates that they wish to cancel the stream or that an
+ error condition has occurred. When sent by the receiver of a stream, it indicates that
+ either the receiver is rejecting the stream, requesting that the stream be cancelled, or
+ that an error condition has occurred.
+
+
+
+
+
+
+ The RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error code . The error code indicates why the stream is being
+ terminated.
+
+
+
+ The RST_STREAM frame does not define any flags.
+
+
+
+ The RST_STREAM frame fully terminates the referenced stream and causes it to enter the
+ closed state. After receiving a RST_STREAM on a stream, the receiver MUST NOT send
+ additional frames for that stream, with the exception of PRIORITY . However,
+ after sending the RST_STREAM, the sending endpoint MUST be prepared to receive and process
+ additional frames sent on the stream that might have been sent by the peer prior to the
+ arrival of the RST_STREAM.
+
+
+
+ RST_STREAM frames MUST be associated with a stream. If a RST_STREAM frame is received
+ with a stream identifier of 0x0, the recipient MUST treat this as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+ RST_STREAM frames MUST NOT be sent for a stream in the "idle" state. If a RST_STREAM
+ frame identifying an idle stream is received, the recipient MUST treat this as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+
+
+ The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints
+ communicate, such as preferences and constraints on peer behavior. The SETTINGS frame is
+ also used to acknowledge the receipt of those parameters. Individually, a SETTINGS
+ parameter can also be referred to as a "setting".
+
+
+ SETTINGS parameters are not negotiated; they describe characteristics of the sending peer,
+ which are used by the receiving peer. Different values for the same parameter can be
+ advertised by each peer. For example, a client might set a high initial flow control
+ window, whereas a server might set a lower value to conserve resources.
+
+
+
+ A SETTINGS frame MUST be sent by both endpoints at the start of a connection, and MAY be
+ sent at any other time by either endpoint over the lifetime of the connection.
+ Implementations MUST support all of the parameters defined by this specification.
+
+
+
+ Each parameter in a SETTINGS frame replaces any existing value for that parameter.
+ Parameters are processed in the order in which they appear, and a receiver of a SETTINGS
+ frame does not need to maintain any state other than the current value of its
+ parameters. Therefore, the value of a SETTINGS parameter is the last value that is seen by
+ a receiver.
+
+
+ SETTINGS parameters are acknowledged by the receiving peer. To enable this, the SETTINGS
+ frame defines the following flag:
+
+
+ Bit 1 being set indicates that this frame acknowledges receipt and application of the
+ peer's SETTINGS frame. When this bit is set, the payload of the SETTINGS frame MUST
+ be empty. Receipt of a SETTINGS frame with the ACK flag set and a length field value
+ other than 0 MUST be treated as a connection
+ error of type FRAME_SIZE_ERROR . For more info, see Settings Synchronization .
+
+
+
+
+ SETTINGS frames always apply to a connection, never a single stream. The stream
+ identifier for a SETTINGS frame MUST be zero (0x0). If an endpoint receives a SETTINGS
+ frame whose stream identifier field is anything other than 0x0, the endpoint MUST respond
+ with a connection error of type
+ PROTOCOL_ERROR .
+
+
+ The SETTINGS frame affects connection state. A badly formed or incomplete SETTINGS frame
+ MUST be treated as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+ The payload of a SETTINGS frame consists of zero or more parameters, each consisting of
+ an unsigned 16-bit setting identifier and an unsigned 32-bit value.
+
+
+
+
+
+
+
+
+
+ The following parameters are defined:
+
+
+
+ Allows the sender to inform the remote endpoint of the maximum size of the header
+ compression table used to decode header blocks, in octets. The encoder can select
+ any size equal to or less than this value by using signaling specific to the
+ header compression format inside a header block. The initial value is 4,096
+ octets.
+
+
+
+
+ This setting can be use to disable server
+ push . An endpoint MUST NOT send a PUSH_PROMISE frame if it
+ receives this parameter set to a value of 0. An endpoint that has both set this
+ parameter to 0 and had it acknowledged MUST treat the receipt of a
+ PUSH_PROMISE frame as a connection error of type
+ PROTOCOL_ERROR .
+
+
+ The initial value is 1, which indicates that server push is permitted. Any value
+ other than 0 or 1 MUST be treated as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+ Indicates the maximum number of concurrent streams that the sender will allow.
+ This limit is directional: it applies to the number of streams that the sender
+ permits the receiver to create. Initially there is no limit to this value. It is
+ recommended that this value be no smaller than 100, so as to not unnecessarily
+ limit parallelism.
+
+
+ A value of 0 for SETTINGS_MAX_CONCURRENT_STREAMS SHOULD NOT be treated as special
+ by endpoints. A zero value does prevent the creation of new streams, however this
+ can also happen for any limit that is exhausted with active streams. Servers
+ SHOULD only set a zero value for short durations; if a server does not wish to
+ accept requests, closing the connection could be preferable.
+
+
+
+
+ Indicates the sender's initial window size (in octets) for stream level flow
+ control. The initial value is 216 -1 (65,535) octets.
+
+
+ This setting affects the window size of all streams, including existing streams,
+ see .
+
+
+ Values above the maximum flow control window size of 231 -1 MUST
+ be treated as a connection error of
+ type FLOW_CONTROL_ERROR .
+
+
+
+
+ Indicates the size of the largest frame payload that the sender is willing to
+ receive, in octets.
+
+
+ The initial value is 214 (16,384) octets. The value advertised by
+ an endpoint MUST be between this initial value and the maximum allowed frame size
+ (224 -1 or 16,777,215 octets), inclusive. Values outside this range
+ MUST be treated as a connection error
+ of type PROTOCOL_ERROR .
+
+
+
+
+ This advisory setting informs a peer of the maximum size of header list that the
+ sender is prepared to accept, in octets. The value is based on the uncompressed
+ size of header fields, including the length of the name and value in octets plus
+ an overhead of 32 octets for each header field.
+
+
+ For any given request, a lower limit than what is advertised MAY be enforced. The
+ initial value of this setting is unlimited.
+
+
+
+
+
+ An endpoint that receives a SETTINGS frame with any unknown or unsupported identifier
+ MUST ignore that setting.
+
+
+
+
+
+ Most values in SETTINGS benefit from or require an understanding of when the peer has
+ received and applied the changed parameter values. In order to provide
+ such synchronization timepoints, the recipient of a SETTINGS frame in which the ACK flag
+ is not set MUST apply the updated parameters as soon as possible upon receipt.
+
+
+ The values in the SETTINGS frame MUST be processed in the order they appear, with no
+ other frame processing between values. Unsupported parameters MUST be ignored. Once
+ all values have been processed, the recipient MUST immediately emit a SETTINGS frame
+ with the ACK flag set. Upon receiving a SETTINGS frame with the ACK flag set, the sender
+ of the altered parameters can rely on the setting having been applied.
+
+
+ If the sender of a SETTINGS frame does not receive an acknowledgement within a
+ reasonable amount of time, it MAY issue a connection error of type
+ SETTINGS_TIMEOUT .
+
+
+
+
+
+
+ The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of
+ streams the sender intends to initiate. The PUSH_PROMISE frame includes the unsigned
+ 31-bit identifier of the stream the endpoint plans to create along with a set of headers
+ that provide additional context for the stream. contains a
+ thorough description of the use of PUSH_PROMISE frames.
+
+
+
+
+
+
+ The PUSH_PROMISE frame payload has the following fields:
+
+
+ An 8-bit field containing the length of the frame padding in units of octets. This
+ field is only present if the PADDED flag is set.
+
+
+ A single reserved bit.
+
+
+ An unsigned 31-bit integer that identifies the stream that is reserved by the
+ PUSH_PROMISE. The promised stream identifier MUST be a valid choice for the next
+ stream sent by the sender (see new stream
+ identifier ).
+
+
+ A header block fragment containing request header
+ fields.
+
+
+ Padding octets.
+
+
+
+
+
+ The PUSH_PROMISE frame defines the following flags:
+
+
+
+ Bit 3 being set indicates that this frame contains an entire header block and is not followed by any
+ CONTINUATION frames.
+
+
+ A PUSH_PROMISE frame without the END_HEADERS flag set MUST be followed by a
+ CONTINUATION frame for the same stream. A receiver MUST treat the receipt of any
+ other type of frame or a frame on a different stream as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+ Bit 4 being set indicates that the Pad Length field and any padding that it
+ describes is present.
+
+
+
+
+
+
+ PUSH_PROMISE frames MUST be associated with an existing, peer-initiated stream. The stream
+ identifier of a PUSH_PROMISE frame indicates the stream it is associated with. If the
+ stream identifier field specifies the value 0x0, a recipient MUST respond with a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+ Promised streams are not required to be used in the order they are promised. The
+ PUSH_PROMISE only reserves stream identifiers for later use.
+
+
+
+ PUSH_PROMISE MUST NOT be sent if the SETTINGS_ENABLE_PUSH setting of the
+ peer endpoint is set to 0. An endpoint that has set this setting and has received
+ acknowledgement MUST treat the receipt of a PUSH_PROMISE frame as a connection error of type
+ PROTOCOL_ERROR .
+
+
+ Recipients of PUSH_PROMISE frames can choose to reject promised streams by returning a
+ RST_STREAM referencing the promised stream identifier back to the sender of
+ the PUSH_PROMISE.
+
+
+
+ A PUSH_PROMISE frame modifies the connection state in two ways. The inclusion of a header block potentially modifies the state maintained for
+ header compression. PUSH_PROMISE also reserves a stream for later use, causing the
+ promised stream to enter the "reserved" state. A sender MUST NOT send a PUSH_PROMISE on a
+ stream unless that stream is either "open" or "half closed (remote)"; the sender MUST
+ ensure that the promised stream is a valid choice for a new stream identifier (that is, the promised stream MUST
+ be in the "idle" state).
+
+
+ Since PUSH_PROMISE reserves a stream, ignoring a PUSH_PROMISE frame causes the stream
+ state to become indeterminate. A receiver MUST treat the receipt of a PUSH_PROMISE on a
+ stream that is neither "open" nor "half closed (local)" as a connection error of type
+ PROTOCOL_ERROR . However, an endpoint that has sent
+ RST_STREAM on the associated stream MUST handle PUSH_PROMISE frames that
+ might have been created before the RST_STREAM frame is received and
+ processed.
+
+
+ A receiver MUST treat the receipt of a PUSH_PROMISE that promises an illegal stream identifier (that is, an identifier for a
+ stream that is not currently in the "idle" state) as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+ The PUSH_PROMISE frame includes optional padding. Padding fields and flags are identical
+ to those defined for DATA frames .
+
+
+
+
+
+ The PING frame (type=0x6) is a mechanism for measuring a minimal round trip time from the
+ sender, as well as determining whether an idle connection is still functional. PING
+ frames can be sent from any endpoint.
+
+
+
+
+
+
+ In addition to the frame header, PING frames MUST contain 8 octets of data in the payload.
+ A sender can include any value it chooses and use those bytes in any fashion.
+
+
+ Receivers of a PING frame that does not include an ACK flag MUST send a PING frame with
+ the ACK flag set in response, with an identical payload. PING responses SHOULD be given
+ higher priority than any other frame.
+
+
+
+ The PING frame defines the following flags:
+
+
+ Bit 1 being set indicates that this PING frame is a PING response. An endpoint MUST
+ set this flag in PING responses. An endpoint MUST NOT respond to PING frames
+ containing this flag.
+
+
+
+
+ PING frames are not associated with any individual stream. If a PING frame is received
+ with a stream identifier field value other than 0x0, the recipient MUST respond with a
+ connection error of type
+ PROTOCOL_ERROR .
+
+
+ Receipt of a PING frame with a length field value other than 8 MUST be treated as a connection error of type
+ FRAME_SIZE_ERROR .
+
+
+
+
+
+
+ The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this
+ connection. GOAWAY can be sent by either the client or the server. Once sent, the sender
+ will ignore frames sent on any new streams with identifiers higher than the included last
+ stream identifier. Receivers of a GOAWAY frame MUST NOT open additional streams on the
+ connection, although a new connection can be established for new streams.
+
+
+ The purpose of this frame is to allow an endpoint to gracefully stop accepting new
+ streams, while still finishing processing of previously established streams. This enables
+ administrative actions, like server maintainance.
+
+
+ There is an inherent race condition between an endpoint starting new streams and the
+ remote sending a GOAWAY frame. To deal with this case, the GOAWAY contains the stream
+ identifier of the last peer-initiated stream which was or might be processed on the
+ sending endpoint in this connection. For instance, if the server sends a GOAWAY frame,
+ the identified stream is the highest numbered stream initiated by the client.
+
+
+ If the receiver of the GOAWAY has sent data on streams with a higher stream identifier
+ than what is indicated in the GOAWAY frame, those streams are not or will not be
+ processed. The receiver of the GOAWAY frame can treat the streams as though they had
+ never been created at all, thereby allowing those streams to be retried later on a new
+ connection.
+
+
+ Endpoints SHOULD always send a GOAWAY frame before closing a connection so that the remote
+ can know whether a stream has been partially processed or not. For example, if an HTTP
+ client sends a POST at the same time that a server closes a connection, the client cannot
+ know if the server started to process that POST request if the server does not send a
+ GOAWAY frame to indicate what streams it might have acted on.
+
+
+ An endpoint might choose to close a connection without sending GOAWAY for misbehaving
+ peers.
+
+
+
+
+
+
+ The GOAWAY frame does not define any flags.
+
+
+ The GOAWAY frame applies to the connection, not a specific stream. An endpoint MUST treat
+ a GOAWAY frame with a stream identifier other than 0x0 as a connection error of type
+ PROTOCOL_ERROR .
+
+
+ The last stream identifier in the GOAWAY frame contains the highest numbered stream
+ identifier for which the sender of the GOAWAY frame might have taken some action on, or
+ might yet take action on. All streams up to and including the identified stream might
+ have been processed in some way. The last stream identifier can be set to 0 if no streams
+ were processed.
+
+
+ In this context, "processed" means that some data from the stream was passed to some
+ higher layer of software that might have taken some action as a result.
+
+
+ If a connection terminates without a GOAWAY frame, the last stream identifier is
+ effectively the highest possible stream identifier.
+
+
+ On streams with lower or equal numbered identifiers that were not closed completely prior
+ to the connection being closed, re-attempting requests, transactions, or any protocol
+ activity is not possible, with the exception of idempotent actions like HTTP GET, PUT, or
+ DELETE. Any protocol activity that uses higher numbered streams can be safely retried
+ using a new connection.
+
+
+ Activity on streams numbered lower or equal to the last stream identifier might still
+ complete successfully. The sender of a GOAWAY frame might gracefully shut down a
+ connection by sending a GOAWAY frame, maintaining the connection in an open state until
+ all in-progress streams complete.
+
+
+ An endpoint MAY send multiple GOAWAY frames if circumstances change. For instance, an
+ endpoint that sends GOAWAY with NO_ERROR during graceful shutdown could
+ subsequently encounter an condition that requires immediate termination of the connection.
+ The last stream identifier from the last GOAWAY frame received indicates which streams
+ could have been acted upon. Endpoints MUST NOT increase the value they send in the last
+ stream identifier, since the peers might already have retried unprocessed requests on
+ another connection.
+
+
+ A client that is unable to retry requests loses all requests that are in flight when the
+ server closes the connection. This is especially true for intermediaries that might
+ not be serving clients using HTTP/2. A server that is attempting to gracefully shut down
+ a connection SHOULD send an initial GOAWAY frame with the last stream identifier set to
+ 231 -1 and a NO_ERROR code. This signals to the client that
+ a shutdown is imminent and that no further requests can be initiated. After waiting at
+ least one round trip time, the server can send another GOAWAY frame with an updated last
+ stream identifier. This ensures that a connection can be cleanly shut down without losing
+ requests.
+
+
+
+ After sending a GOAWAY frame, the sender can discard frames for streams with identifiers
+ higher than the identified last stream. However, any frames that alter connection state
+ cannot be completely ignored. For instance, HEADERS ,
+ PUSH_PROMISE and CONTINUATION frames MUST be minimally
+ processed to ensure the state maintained for header compression is consistent (see ); similarly DATA frames MUST be counted toward the connection flow
+ control window. Failure to process these frames can cause flow control or header
+ compression state to become unsynchronized.
+
+
+
+ The GOAWAY frame also contains a 32-bit error code that
+ contains the reason for closing the connection.
+
+
+ Endpoints MAY append opaque data to the payload of any GOAWAY frame. Additional debug
+ data is intended for diagnostic purposes only and carries no semantic value. Debug
+ information could contain security- or privacy-sensitive data. Logged or otherwise
+ persistently stored debug data MUST have adequate safeguards to prevent unauthorized
+ access.
+
+
+
+
+
+ The WINDOW_UPDATE frame (type=0x8) is used to implement flow control; see for an overview.
+
+
+ Flow control operates at two levels: on each individual stream and on the entire
+ connection.
+
+
+ Both types of flow control are hop-by-hop; that is, only between the two endpoints.
+ Intermediaries do not forward WINDOW_UPDATE frames between dependent connections.
+ However, throttling of data transfer by any receiver can indirectly cause the propagation
+ of flow control information toward the original sender.
+
+
+ Flow control only applies to frames that are identified as being subject to flow control.
+ Of the frame types defined in this document, this includes only DATA frames.
+ Frames that are exempt from flow control MUST be accepted and processed, unless the
+ receiver is unable to assign resources to handling the frame. A receiver MAY respond with
+ a stream error or connection error of type
+ FLOW_CONTROL_ERROR if it is unable to accept a frame.
+
+
+
+
+
+ The payload of a WINDOW_UPDATE frame is one reserved bit, plus an unsigned 31-bit integer
+ indicating the number of octets that the sender can transmit in addition to the existing
+ flow control window. The legal range for the increment to the flow control window is 1 to
+ 231 -1 (0x7fffffff) octets.
+
+
+ The WINDOW_UPDATE frame does not define any flags.
+
+
+ The WINDOW_UPDATE frame can be specific to a stream or to the entire connection. In the
+ former case, the frame's stream identifier indicates the affected stream; in the latter,
+ the value "0" indicates that the entire connection is the subject of the frame.
+
+
+ A receiver MUST treat the receipt of a WINDOW_UPDATE frame with an flow control window
+ increment of 0 as a stream error of type
+ PROTOCOL_ERROR ; errors on the connection flow control window MUST be
+ treated as a connection error .
+
+
+ WINDOW_UPDATE can be sent by a peer that has sent a frame bearing the END_STREAM flag.
+ This means that a receiver could receive a WINDOW_UPDATE frame on a "half closed (remote)"
+ or "closed" stream. A receiver MUST NOT treat this as an error, see .
+
+
+ A receiver that receives a flow controlled frame MUST always account for its contribution
+ against the connection flow control window, unless the receiver treats this as a connection error . This is necessary even if the
+ frame is in error. Since the sender counts the frame toward the flow control window, if
+ the receiver does not, the flow control window at sender and receiver can become
+ different.
+
+
+
+
+ Flow control in HTTP/2 is implemented using a window kept by each sender on every
+ stream. The flow control window is a simple integer value that indicates how many octets
+ of data the sender is permitted to transmit; as such, its size is a measure of the
+ buffering capacity of the receiver.
+
+
+ Two flow control windows are applicable: the stream flow control window and the
+ connection flow control window. The sender MUST NOT send a flow controlled frame with a
+ length that exceeds the space available in either of the flow control windows advertised
+ by the receiver. Frames with zero length with the END_STREAM flag set (that is, an
+ empty DATA frame) MAY be sent if there is no available space in either
+ flow control window.
+
+
+ For flow control calculations, the 9 octet frame header is not counted.
+
+
+ After sending a flow controlled frame, the sender reduces the space available in both
+ windows by the length of the transmitted frame.
+
+
+ The receiver of a frame sends a WINDOW_UPDATE frame as it consumes data and frees up
+ space in flow control windows. Separate WINDOW_UPDATE frames are sent for the stream
+ and connection level flow control windows.
+
+
+ A sender that receives a WINDOW_UPDATE frame updates the corresponding window by the
+ amount specified in the frame.
+
+
+ A sender MUST NOT allow a flow control window to exceed 231 -1 octets.
+ If a sender receives a WINDOW_UPDATE that causes a flow control window to exceed this
+ maximum it MUST terminate either the stream or the connection, as appropriate. For
+ streams, the sender sends a RST_STREAM with the error code of
+ FLOW_CONTROL_ERROR code; for the connection, a GOAWAY
+ frame with a FLOW_CONTROL_ERROR code.
+
+
+ Flow controlled frames from the sender and WINDOW_UPDATE frames from the receiver are
+ completely asynchronous with respect to each other. This property allows a receiver to
+ aggressively update the window size kept by the sender to prevent streams from stalling.
+
+
+
+
+
+ When an HTTP/2 connection is first established, new streams are created with an initial
+ flow control window size of 65,535 octets. The connection flow control window is 65,535
+ octets. Both endpoints can adjust the initial window size for new streams by including
+ a value for SETTINGS_INITIAL_WINDOW_SIZE in the SETTINGS
+ frame that forms part of the connection preface. The connection flow control window can
+ only be changed using WINDOW_UPDATE frames.
+
+
+ Prior to receiving a SETTINGS frame that sets a value for
+ SETTINGS_INITIAL_WINDOW_SIZE , an endpoint can only use the default
+ initial window size when sending flow controlled frames. Similarly, the connection flow
+ control window is set to the default initial window size until a WINDOW_UPDATE frame is
+ received.
+
+
+ A SETTINGS frame can alter the initial flow control window size for all
+ current streams. When the value of SETTINGS_INITIAL_WINDOW_SIZE changes,
+ a receiver MUST adjust the size of all stream flow control windows that it maintains by
+ the difference between the new value and the old value.
+
+
+ A change to SETTINGS_INITIAL_WINDOW_SIZE can cause the available space in
+ a flow control window to become negative. A sender MUST track the negative flow control
+ window, and MUST NOT send new flow controlled frames until it receives WINDOW_UPDATE
+ frames that cause the flow control window to become positive.
+
+
+ For example, if the client sends 60KB immediately on connection establishment, and the
+ server sets the initial window size to be 16KB, the client will recalculate the
+ available flow control window to be -44KB on receipt of the SETTINGS
+ frame. The client retains a negative flow control window until WINDOW_UPDATE frames
+ restore the window to being positive, after which the client can resume sending.
+
+
+ A SETTINGS frame cannot alter the connection flow control window.
+
+
+ An endpoint MUST treat a change to SETTINGS_INITIAL_WINDOW_SIZE that
+ causes any flow control window to exceed the maximum size as a connection error of type
+ FLOW_CONTROL_ERROR .
+
+
+
+
+
+ A receiver that wishes to use a smaller flow control window than the current size can
+ send a new SETTINGS frame. However, the receiver MUST be prepared to
+ receive data that exceeds this window size, since the sender might send data that
+ exceeds the lower limit prior to processing the SETTINGS frame.
+
+
+ After sending a SETTINGS frame that reduces the initial flow control window size, a
+ receiver has two options for handling streams that exceed flow control limits:
+
+
+ The receiver can immediately send RST_STREAM with
+ FLOW_CONTROL_ERROR error code for the affected streams.
+
+
+ The receiver can accept the streams and tolerate the resulting head of line
+ blocking, sending WINDOW_UPDATE frames as it consumes data.
+
+
+
+
+
+
+
+
+ The CONTINUATION frame (type=0x9) is used to continue a sequence of header block fragments . Any number of CONTINUATION frames can
+ be sent on an existing stream, as long as the preceding frame is on the same stream and is
+ a HEADERS , PUSH_PROMISE or CONTINUATION frame without the
+ END_HEADERS flag set.
+
+
+
+
+
+
+ The CONTINUATION frame payload contains a header block
+ fragment .
+
+
+
+ The CONTINUATION frame defines the following flag:
+
+
+
+ Bit 3 being set indicates that this frame ends a header
+ block .
+
+
+ If the END_HEADERS bit is not set, this frame MUST be followed by another
+ CONTINUATION frame. A receiver MUST treat the receipt of any other type of frame or
+ a frame on a different stream as a connection
+ error of type PROTOCOL_ERROR .
+
+
+
+
+
+
+ The CONTINUATION frame changes the connection state as defined in .
+
+
+
+ CONTINUATION frames MUST be associated with a stream. If a CONTINUATION frame is received
+ whose stream identifier field is 0x0, the recipient MUST respond with a connection error of type PROTOCOL_ERROR.
+
+
+
+ A CONTINUATION frame MUST be preceded by a HEADERS ,
+ PUSH_PROMISE or CONTINUATION frame without the END_HEADERS flag set. A
+ recipient that observes violation of this rule MUST respond with a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+
+
+ Error codes are 32-bit fields that are used in RST_STREAM and
+ GOAWAY frames to convey the reasons for the stream or connection error.
+
+
+
+ Error codes share a common code space. Some error codes apply only to either streams or the
+ entire connection and have no defined semantics in the other context.
+
+
+
+ The following error codes are defined:
+
+
+ The associated condition is not as a result of an error. For example, a
+ GOAWAY might include this code to indicate graceful shutdown of a
+ connection.
+
+
+ The endpoint detected an unspecific protocol error. This error is for use when a more
+ specific error code is not available.
+
+
+ The endpoint encountered an unexpected internal error.
+
+
+ The endpoint detected that its peer violated the flow control protocol.
+
+
+ The endpoint sent a SETTINGS frame, but did not receive a response in a
+ timely manner. See Settings Synchronization .
+
+
+ The endpoint received a frame after a stream was half closed.
+
+
+ The endpoint received a frame with an invalid size.
+
+
+ The endpoint refuses the stream prior to performing any application processing, see
+ for details.
+
+
+ Used by the endpoint to indicate that the stream is no longer needed.
+
+
+ The endpoint is unable to maintain the header compression context for the connection.
+
+
+ The connection established in response to a CONNECT
+ request was reset or abnormally closed.
+
+
+ The endpoint detected that its peer is exhibiting a behavior that might be generating
+ excessive load.
+
+
+ The underlying transport has properties that do not meet minimum security
+ requirements (see ).
+
+
+
+
+ Unknown or unsupported error codes MUST NOT trigger any special behavior. These MAY be
+ treated by an implementation as being equivalent to INTERNAL_ERROR .
+
+
+
+
+
+ HTTP/2 is intended to be as compatible as possible with current uses of HTTP. This means
+ that, from the application perspective, the features of the protocol are largely
+ unchanged. To achieve this, all request and response semantics are preserved, although the
+ syntax of conveying those semantics has changed.
+
+
+ Thus, the specification and requirements of HTTP/1.1 Semantics and Content , Conditional Requests , Range Requests , Caching and Authentication are applicable to HTTP/2. Selected portions of HTTP/1.1 Message Syntax
+ and Routing , such as the HTTP and HTTPS URI schemes, are also
+ applicable in HTTP/2, but the expression of those semantics for this protocol are defined
+ in the sections below.
+
+
+
+
+ A client sends an HTTP request on a new stream, using a previously unused stream identifier . A server sends an HTTP response on
+ the same stream as the request.
+
+
+ An HTTP message (request or response) consists of:
+
+
+ for a response only, zero or more HEADERS frames (each followed by zero
+ or more CONTINUATION frames) containing the message headers of
+ informational (1xx) HTTP responses (see and ),
+ and
+
+
+ one HEADERS frame (followed by zero or more CONTINUATION
+ frames) containing the message headers (see ), and
+
+
+ zero or more DATA frames containing the message payload (see ), and
+
+
+ optionally, one HEADERS frame, followed by zero or more
+ CONTINUATION frames containing the trailer-part, if present (see ).
+
+
+ The last frame in the sequence bears an END_STREAM flag, noting that a
+ HEADERS frame bearing the END_STREAM flag can be followed by
+ CONTINUATION frames that carry any remaining portions of the header block.
+
+
+ Other frames (from any stream) MUST NOT occur between either HEADERS frame
+ and any CONTINUATION frames that might follow.
+
+
+
+ Trailing header fields are carried in a header block that also terminates the stream.
+ That is, a sequence starting with a HEADERS frame, followed by zero or more
+ CONTINUATION frames, where the HEADERS frame bears an
+ END_STREAM flag. Header blocks after the first that do not terminate the stream are not
+ part of an HTTP request or response.
+
+
+ A HEADERS frame (and associated CONTINUATION frames) can
+ only appear at the start or end of a stream. An endpoint that receives a
+ HEADERS frame without the END_STREAM flag set after receiving a final
+ (non-informational) status code MUST treat the corresponding request or response as malformed .
+
+
+
+ An HTTP request/response exchange fully consumes a single stream. A request starts with
+ the HEADERS frame that puts the stream into an "open" state. The request
+ ends with a frame bearing END_STREAM, which causes the stream to become "half closed
+ (local)" for the client and "half closed (remote)" for the server. A response starts with
+ a HEADERS frame and ends with a frame bearing END_STREAM, which places the
+ stream in the "closed" state.
+
+
+
+
+
+ HTTP/2 removes support for the 101 (Switching Protocols) informational status code
+ ( ).
+
+
+ The semantics of 101 (Switching Protocols) aren't applicable to a multiplexed protocol.
+ Alternative protocols are able to use the same mechanisms that HTTP/2 uses to negotiate
+ their use (see ).
+
+
+
+
+
+ HTTP header fields carry information as a series of key-value pairs. For a listing of
+ registered HTTP headers, see the Message Header Field Registry maintained at .
+
+
+
+
+ While HTTP/1.x used the message start-line (see ) to convey the target URI and method of the request, and the
+ status code for the response, HTTP/2 uses special pseudo-header fields beginning with
+ ':' character (ASCII 0x3a) for this purpose.
+
+
+ Pseudo-header fields are not HTTP header fields. Endpoints MUST NOT generate
+ pseudo-header fields other than those defined in this document.
+
+
+ Pseudo-header fields are only valid in the context in which they are defined.
+ Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header
+ fields defined for responses MUST NOT appear in requests. Pseudo-header fields MUST
+ NOT appear in trailers. Endpoints MUST treat a request or response that contains
+ undefined or invalid pseudo-header fields as malformed .
+
+
+ Just as in HTTP/1.x, header field names are strings of ASCII characters that are
+ compared in a case-insensitive fashion. However, header field names MUST be converted
+ to lowercase prior to their encoding in HTTP/2. A request or response containing
+ uppercase header field names MUST be treated as malformed .
+
+
+ All pseudo-header fields MUST appear in the header block before regular header fields.
+ Any request or response that contains a pseudo-header field that appears in a header
+ block after a regular header field MUST be treated as malformed .
+
+
+
+
+
+ HTTP/2 does not use the Connection header field to
+ indicate connection-specific header fields; in this protocol, connection-specific
+ metadata is conveyed by other means. An endpoint MUST NOT generate a HTTP/2 message
+ containing connection-specific header fields; any message containing
+ connection-specific header fields MUST be treated as malformed .
+
+
+ This means that an intermediary transforming an HTTP/1.x message to HTTP/2 will need
+ to remove any header fields nominated by the Connection header field, along with the
+ Connection header field itself. Such intermediaries SHOULD also remove other
+ connection-specific header fields, such as Keep-Alive, Proxy-Connection,
+ Transfer-Encoding and Upgrade, even if they are not nominated by Connection.
+
+
+ One exception to this is the TE header field, which MAY be present in an HTTP/2
+ request, but when it is MUST NOT contain any value other than "trailers".
+
+
+
+
+ HTTP/2 purposefully does not support upgrade to another protocol. The handshake
+ methods described in are believed sufficient to
+ negotiate the use of alternative protocols.
+
+
+
+
+
+
+
+ The following pseudo-header fields are defined for HTTP/2 requests:
+
+
+
+ The :method pseudo-header field includes the HTTP
+ method ( ).
+
+
+
+
+ The :scheme pseudo-header field includes the scheme
+ portion of the target URI ( ).
+
+
+ :scheme is not restricted to http and https schemed URIs. A
+ proxy or gateway can translate requests for non-HTTP schemes, enabling the use
+ of HTTP to interact with non-HTTP services.
+
+
+
+
+ The :authority pseudo-header field includes the
+ authority portion of the target URI ( ). The authority MUST NOT include the deprecated userinfo subcomponent for http
+ or https schemed URIs.
+
+
+ To ensure that the HTTP/1.1 request line can be reproduced accurately, this
+ pseudo-header field MUST be omitted when translating from an HTTP/1.1 request
+ that has a request target in origin or asterisk form (see ). Clients that generate
+ HTTP/2 requests directly SHOULD use the :authority pseudo-header
+ field instead of the Host header field. An
+ intermediary that converts an HTTP/2 request to HTTP/1.1 MUST create a Host header field if one is not present in a request by
+ copying the value of the :authority pseudo-header
+ field.
+
+
+
+
+ The :path pseudo-header field includes the path and
+ query parts of the target URI (the path-absolute
+ production from and optionally a '?' character
+ followed by the query production, see and ). A request in asterisk form includes the value '*' for the
+ :path pseudo-header field.
+
+
+ This pseudo-header field MUST NOT be empty for http
+ or https URIs; http or
+ https URIs that do not contain a path component
+ MUST include a value of '/'. The exception to this rule is an OPTIONS request
+ for an http or https
+ URI that does not include a path component; these MUST include a :path pseudo-header field with a value of '*' (see ).
+
+
+
+
+
+ All HTTP/2 requests MUST include exactly one valid value for the :method , :scheme , and :path pseudo-header fields, unless it is a CONNECT request . An HTTP request that omits mandatory
+ pseudo-header fields is malformed .
+
+
+ HTTP/2 does not define a way to carry the version identifier that is included in the
+ HTTP/1.1 request line.
+
+
+
+
+
+ For HTTP/2 responses, a single :status pseudo-header
+ field is defined that carries the HTTP status code field (see ). This pseudo-header field MUST be included in all
+ responses, otherwise the response is malformed .
+
+
+ HTTP/2 does not define a way to carry the version or reason phrase that is included in
+ an HTTP/1.1 status line.
+
+
+
+
+
+ The Cookie header field can carry a significant amount of
+ redundant data.
+
+
+ The Cookie header field uses a semi-colon (";") to delimit cookie-pairs (or "crumbs").
+ This header field doesn't follow the list construction rules in HTTP (see ), which prevents cookie-pairs from
+ being separated into different name-value pairs. This can significantly reduce
+ compression efficiency as individual cookie-pairs are updated.
+
+
+ To allow for better compression efficiency, the Cookie header field MAY be split into
+ separate header fields, each with one or more cookie-pairs. If there are multiple
+ Cookie header fields after decompression, these MUST be concatenated into a single
+ octet string using the two octet delimiter of 0x3B, 0x20 (the ASCII string "; ")
+ before being passed into a non-HTTP/2 context, such as an HTTP/1.1 connection, or a
+ generic HTTP server application.
+
+
+
+ Therefore, the following two lists of Cookie header fields are semantically
+ equivalent.
+
+
+
+
+
+
+
+ A malformed request or response is one that is an otherwise valid sequence of HTTP/2
+ frames, but is otherwise invalid due to the presence of extraneous frames, prohibited
+ header fields, the absence of mandatory header fields, or the inclusion of uppercase
+ header field names.
+
+
+ A request or response that includes an entity body can include a content-length header field. A request or response is also
+ malformed if the value of a content-length header field
+ does not equal the sum of the DATA frame payload lengths that form the
+ body. A response that is defined to have no payload, as described in , can have a non-zero
+ content-length header field, even though no content is
+ included in DATA frames.
+
+
+ Intermediaries that process HTTP requests or responses (i.e., any intermediary not
+ acting as a tunnel) MUST NOT forward a malformed request or response. Malformed
+ requests or responses that are detected MUST be treated as a stream error of type PROTOCOL_ERROR .
+
+
+ For malformed requests, a server MAY send an HTTP response prior to closing or
+ resetting the stream. Clients MUST NOT accept a malformed response. Note that these
+ requirements are intended to protect against several types of common attacks against
+ HTTP; they are deliberately strict, because being permissive can expose
+ implementations to these vulnerabilities.
+
+
+
+
+
+
+ This section shows HTTP/1.1 requests and responses, with illustrations of equivalent
+ HTTP/2 requests and responses.
+
+
+ An HTTP GET request includes request header fields and no body and is therefore
+ transmitted as a single HEADERS frame, followed by zero or more
+ CONTINUATION frames containing the serialized block of request header
+ fields. The HEADERS frame in the following has both the END_HEADERS and
+ END_STREAM flags set; no CONTINUATION frames are sent:
+
+
+
+ + END_STREAM
+ Accept: image/jpeg + END_HEADERS
+ :method = GET
+ :scheme = https
+ :path = /resource
+ host = example.org
+ accept = image/jpeg
+]]>
+
+
+
+ Similarly, a response that includes only response header fields is transmitted as a
+ HEADERS frame (again, followed by zero or more
+ CONTINUATION frames) containing the serialized block of response header
+ fields.
+
+
+
+ + END_STREAM
+ Expires: Thu, 23 Jan ... + END_HEADERS
+ :status = 304
+ etag = "xyzzy"
+ expires = Thu, 23 Jan ...
+]]>
+
+
+
+ An HTTP POST request that includes request header fields and payload data is transmitted
+ as one HEADERS frame, followed by zero or more
+ CONTINUATION frames containing the request header fields, followed by one
+ or more DATA frames, with the last CONTINUATION (or
+ HEADERS ) frame having the END_HEADERS flag set and the final
+ DATA frame having the END_STREAM flag set:
+
+
+
+ - END_STREAM
+ Content-Type: image/jpeg - END_HEADERS
+ Content-Length: 123 :method = POST
+ :path = /resource
+ {binary data} :scheme = https
+
+ CONTINUATION
+ + END_HEADERS
+ content-type = image/jpeg
+ host = example.org
+ content-length = 123
+
+ DATA
+ + END_STREAM
+ {binary data}
+]]>
+
+ Note that data contributing to any given header field could be spread between header
+ block fragments. The allocation of header fields to frames in this example is
+ illustrative only.
+
+
+
+
+ A response that includes header fields and payload data is transmitted as a
+ HEADERS frame, followed by zero or more CONTINUATION
+ frames, followed by one or more DATA frames, with the last
+ DATA frame in the sequence having the END_STREAM flag set:
+
+
+
+ - END_STREAM
+ Content-Length: 123 + END_HEADERS
+ :status = 200
+ {binary data} content-type = image/jpeg
+ content-length = 123
+
+ DATA
+ + END_STREAM
+ {binary data}
+]]>
+
+
+
+ Trailing header fields are sent as a header block after both the request or response
+ header block and all the DATA frames have been sent. The
+ HEADERS frame starting the trailers header block has the END_STREAM flag
+ set.
+
+
+
+ - END_STREAM
+ Transfer-Encoding: chunked + END_HEADERS
+ Trailer: Foo :status = 200
+ content-length = 123
+ 123 content-type = image/jpeg
+ {binary data} trailer = Foo
+ 0
+ Foo: bar DATA
+ - END_STREAM
+ {binary data}
+
+ HEADERS
+ + END_STREAM
+ + END_HEADERS
+ foo = bar
+]]>
+
+
+
+
+
+ An informational response using a 1xx status code other than 101 is transmitted as a
+ HEADERS frame, followed by zero or more CONTINUATION
+ frames:
+
+ - END_STREAM
+ + END_HEADERS
+ :status = 103
+ extension-field = bar
+]]>
+
+
+
+
+
+ In HTTP/1.1, an HTTP client is unable to retry a non-idempotent request when an error
+ occurs, because there is no means to determine the nature of the error. It is possible
+ that some server processing occurred prior to the error, which could result in
+ undesirable effects if the request were reattempted.
+
+
+ HTTP/2 provides two mechanisms for providing a guarantee to a client that a request has
+ not been processed:
+
+
+ The GOAWAY frame indicates the highest stream number that might have
+ been processed. Requests on streams with higher numbers are therefore guaranteed to
+ be safe to retry.
+
+
+ The REFUSED_STREAM error code can be included in a
+ RST_STREAM frame to indicate that the stream is being closed prior to
+ any processing having occurred. Any request that was sent on the reset stream can
+ be safely retried.
+
+
+
+
+ Requests that have not been processed have not failed; clients MAY automatically retry
+ them, even those with non-idempotent methods.
+
+
+ A server MUST NOT indicate that a stream has not been processed unless it can guarantee
+ that fact. If frames that are on a stream are passed to the application layer for any
+ stream, then REFUSED_STREAM MUST NOT be used for that stream, and a
+ GOAWAY frame MUST include a stream identifier that is greater than or
+ equal to the given stream identifier.
+
+
+ In addition to these mechanisms, the PING frame provides a way for a
+ client to easily test a connection. Connections that remain idle can become broken as
+ some middleboxes (for instance, network address translators, or load balancers) silently
+ discard connection bindings. The PING frame allows a client to safely
+ test whether a connection is still active without sending a request.
+
+
+
+
+
+
+ HTTP/2 allows a server to pre-emptively send (or "push") responses (along with
+ corresponding "promised" requests) to a client in association with a previous
+ client-initiated request. This can be useful when the server knows the client will need
+ to have those responses available in order to fully process the response to the original
+ request.
+
+
+
+ Pushing additional message exchanges in this fashion is optional, and is negotiated
+ between individual endpoints. The SETTINGS_ENABLE_PUSH setting can be set
+ to 0 to indicate that server push is disabled.
+
+
+ Promised requests MUST be cacheable (see ), MUST be safe (see ) and MUST NOT include a request body. Clients that receive a
+ promised request that is not cacheable, unsafe or that includes a request body MUST
+ reset the stream with a stream error of type
+ PROTOCOL_ERROR .
+
+
+ Pushed responses that are cacheable (see ) can be stored by the client, if it implements a HTTP
+ cache. Pushed responses are considered successfully validated on the origin server (e.g.,
+ if the "no-cache" cache response directive is present) while the stream identified by the
+ promised stream ID is still open.
+
+
+ Pushed responses that are not cacheable MUST NOT be stored by any HTTP cache. They MAY
+ be made available to the application separately.
+
+
+ An intermediary can receive pushes from the server and choose not to forward them on to
+ the client. In other words, how to make use of the pushed information is up to that
+ intermediary. Equally, the intermediary might choose to make additional pushes to the
+ client, without any action taken by the server.
+
+
+ A client cannot push. Thus, servers MUST treat the receipt of a
+ PUSH_PROMISE frame as a connection
+ error of type PROTOCOL_ERROR . Clients MUST reject any attempt to
+ change the SETTINGS_ENABLE_PUSH setting to a value other than 0 by treating
+ the message as a connection error of type
+ PROTOCOL_ERROR .
+
+
+
+
+ Server push is semantically equivalent to a server responding to a request; however, in
+ this case that request is also sent by the server, as a PUSH_PROMISE
+ frame.
+
+
+ The PUSH_PROMISE frame includes a header block that contains a complete
+ set of request header fields that the server attributes to the request. It is not
+ possible to push a response to a request that includes a request body.
+
+
+
+ Pushed responses are always associated with an explicit request from the client. The
+ PUSH_PROMISE frames sent by the server are sent on that explicit
+ request's stream. The PUSH_PROMISE frame also includes a promised stream
+ identifier, chosen from the stream identifiers available to the server (see ).
+
+
+
+ The header fields in PUSH_PROMISE and any subsequent
+ CONTINUATION frames MUST be a valid and complete set of request header fields . The server MUST include a method in
+ the :method header field that is safe and cacheable. If a
+ client receives a PUSH_PROMISE that does not include a complete and valid
+ set of header fields, or the :method header field identifies
+ a method that is not safe, it MUST respond with a stream error of type PROTOCOL_ERROR .
+
+
+
+ The server SHOULD send PUSH_PROMISE ( )
+ frames prior to sending any frames that reference the promised responses. This avoids a
+ race where clients issue requests prior to receiving any PUSH_PROMISE
+ frames.
+
+
+ For example, if the server receives a request for a document containing embedded links
+ to multiple image files, and the server chooses to push those additional images to the
+ client, sending push promises before the DATA frames that contain the
+ image links ensures that the client is able to see the promises before discovering
+ embedded links. Similarly, if the server pushes responses referenced by the header block
+ (for instance, in Link header fields), sending the push promises before sending the
+ header block ensures that clients do not request them.
+
+
+
+ PUSH_PROMISE frames MUST NOT be sent by the client.
+
+
+ PUSH_PROMISE frames can be sent by the server in response to any
+ client-initiated stream, but the stream MUST be in either the "open" or "half closed
+ (remote)" state with respect to the server. PUSH_PROMISE frames are
+ interspersed with the frames that comprise a response, though they cannot be
+ interspersed with HEADERS and CONTINUATION frames that
+ comprise a single header block.
+
+
+ Sending a PUSH_PROMISE frame creates a new stream and puts the stream
+ into the “reserved (local)” state for the server and the “reserved (remote)” state for
+ the client.
+
+
+
+
+
+ After sending the PUSH_PROMISE frame, the server can begin delivering the
+ pushed response as a response on a server-initiated
+ stream that uses the promised stream identifier. The server uses this stream to
+ transmit an HTTP response, using the same sequence of frames as defined in . This stream becomes "half closed"
+ to the client after the initial HEADERS frame is sent.
+
+
+
+ Once a client receives a PUSH_PROMISE frame and chooses to accept the
+ pushed response, the client SHOULD NOT issue any requests for the promised response
+ until after the promised stream has closed.
+
+
+
+ If the client determines, for any reason, that it does not wish to receive the pushed
+ response from the server, or if the server takes too long to begin sending the promised
+ response, the client can send an RST_STREAM frame, using either the
+ CANCEL or REFUSED_STREAM codes, and referencing the pushed
+ stream's identifier.
+
+
+ A client can use the SETTINGS_MAX_CONCURRENT_STREAMS setting to limit the
+ number of responses that can be concurrently pushed by a server. Advertising a
+ SETTINGS_MAX_CONCURRENT_STREAMS value of zero disables server push by
+ preventing the server from creating the necessary streams. This does not prohibit a
+ server from sending PUSH_PROMISE frames; clients need to reset any
+ promised streams that are not wanted.
+
+
+
+ Clients receiving a pushed response MUST validate that either the server is
+ authoritative (see ), or the proxy that provided the pushed
+ response is configured for the corresponding request. For example, a server that offers
+ a certificate for only the example.com DNS-ID or Common Name
+ is not permitted to push a response for https://www.example.org/doc .
+
+
+ The response for a PUSH_PROMISE stream begins with a
+ HEADERS frame, which immediately puts the stream into the “half closed
+ (remote)” state for the server and “half closed (local)” state for the client, and ends
+ with a frame bearing END_STREAM, which places the stream in the "closed" state.
+
+
+ The client never sends a frame with the END_STREAM flag for a server push.
+
+
+
+
+
+
+
+
+
+ In HTTP/1.x, the pseudo-method CONNECT ( ) is used to convert an HTTP connection into a tunnel to a remote host.
+ CONNECT is primarily used with HTTP proxies to establish a TLS session with an origin
+ server for the purposes of interacting with https resources.
+
+
+ In HTTP/2, the CONNECT method is used to establish a tunnel over a single HTTP/2 stream to
+ a remote host, for similar purposes. The HTTP header field mapping works as defined in
+ Request Header Fields , with a few
+ differences. Specifically:
+
+
+ The :method header field is set to CONNECT .
+
+
+ The :scheme and :path header
+ fields MUST be omitted.
+
+
+ The :authority header field contains the host and port to
+ connect to (equivalent to the authority-form of the request-target of CONNECT
+ requests, see ).
+
+
+
+
+ A proxy that supports CONNECT establishes a TCP connection to
+ the server identified in the :authority header field. Once
+ this connection is successfully established, the proxy sends a HEADERS
+ frame containing a 2xx series status code to the client, as defined in .
+
+
+ After the initial HEADERS frame sent by each peer, all subsequent
+ DATA frames correspond to data sent on the TCP connection. The payload of
+ any DATA frames sent by the client is transmitted by the proxy to the TCP
+ server; data received from the TCP server is assembled into DATA frames by
+ the proxy. Frame types other than DATA or stream management frames
+ (RST_STREAM , WINDOW_UPDATE , and PRIORITY )
+ MUST NOT be sent on a connected stream, and MUST be treated as a stream error if received.
+
+
+ The TCP connection can be closed by either peer. The END_STREAM flag on a
+ DATA frame is treated as being equivalent to the TCP FIN bit. A client is
+ expected to send a DATA frame with the END_STREAM flag set after receiving
+ a frame bearing the END_STREAM flag. A proxy that receives a DATA frame
+ with the END_STREAM flag set sends the attached data with the FIN bit set on the last TCP
+ segment. A proxy that receives a TCP segment with the FIN bit set sends a
+ DATA frame with the END_STREAM flag set. Note that the final TCP segment
+ or DATA frame could be empty.
+
+
+ A TCP connection error is signaled with RST_STREAM . A proxy treats any
+ error in the TCP connection, which includes receiving a TCP segment with the RST bit set,
+ as a stream error of type
+ CONNECT_ERROR . Correspondingly, a proxy MUST send a TCP segment with the
+ RST bit set if it detects an error with the stream or the HTTP/2 connection.
+
+
+
+
+
+
+ This section outlines attributes of the HTTP protocol that improve interoperability, reduce
+ exposure to known security vulnerabilities, or reduce the potential for implementation
+ variation.
+
+
+
+
+ HTTP/2 connections are persistent. For best performance, it is expected clients will not
+ close connections until it is determined that no further communication with a server is
+ necessary (for example, when a user navigates away from a particular web page), or until
+ the server closes the connection.
+
+
+ Clients SHOULD NOT open more than one HTTP/2 connection to a given host and port pair,
+ where host is derived from a URI, a selected alternative
+ service , or a configured proxy.
+
+
+ A client can create additional connections as replacements, either to replace connections
+ that are near to exhausting the available stream
+ identifier space , to refresh the keying material for a TLS connection, or to
+ replace connections that have encountered errors .
+
+
+ A client MAY open multiple connections to the same IP address and TCP port using different
+ Server Name Indication values or to provide different TLS
+ client certificates, but SHOULD avoid creating multiple connections with the same
+ configuration.
+
+
+ Servers are encouraged to maintain open connections for as long as possible, but are
+ permitted to terminate idle connections if necessary. When either endpoint chooses to
+ close the transport-layer TCP connection, the terminating endpoint SHOULD first send a
+ GOAWAY ( ) frame so that both endpoints can reliably
+ determine whether previously sent frames have been processed and gracefully complete or
+ terminate any necessary remaining tasks.
+
+
+
+
+ Connections that are made to an origin servers, either directly or through a tunnel
+ created using the CONNECT method MAY be reused for
+ requests with multiple different URI authority components. A connection can be reused
+ as long as the origin server is authoritative . For
+ http resources, this depends on the host having resolved to
+ the same IP address.
+
+
+ For https resources, connection reuse additionally depends
+ on having a certificate that is valid for the host in the URI. An origin server might
+ offer a certificate with multiple subjectAltName attributes,
+ or names with wildcards, one of which is valid for the authority in the URI. For
+ example, a certificate with a subjectAltName of *.example.com might permit the use of the same connection for
+ requests to URIs starting with https://a.example.com/ and
+ https://b.example.com/ .
+
+
+ In some deployments, reusing a connection for multiple origins can result in requests
+ being directed to the wrong origin server. For example, TLS termination might be
+ performed by a middlebox that uses the TLS Server Name Indication
+ (SNI) extension to select an origin server. This means that it is possible
+ for clients to send confidential information to servers that might not be the intended
+ target for the request, even though the server is otherwise authoritative.
+
+
+ A server that does not wish clients to reuse connections can indicate that it is not
+ authoritative for a request by sending a 421 (Misdirected Request) status code in response
+ to the request (see ).
+
+
+ A client that is configured to use a proxy over HTTP/2 directs requests to that proxy
+ through a single connection. That is, all requests sent via a proxy reuse the
+ connection to the proxy.
+
+
+
+
+
+ The 421 (Misdirected Request) status code indicates that the request was directed at a
+ server that is not able to produce a response. This can be sent by a server that is not
+ configured to produce responses for the combination of scheme and authority that are
+ included in the request URI.
+
+
+ Clients receiving a 421 (Misdirected Request) response from a server MAY retry the
+ request - whether the request method is idempotent or not - over a different connection.
+ This is possible if a connection is reused ( ) or if an alternative
+ service is selected ( ).
+
+
+ This status code MUST NOT be generated by proxies.
+
+
+ A 421 response is cacheable by default; i.e., unless otherwise indicated by the method
+ definition or explicit cache controls (see ).
+
+
+
+
+
+
+ Implementations of HTTP/2 MUST support TLS 1.2 for HTTP/2 over
+ TLS. The general TLS usage guidance in SHOULD be followed, with
+ some additional restrictions that are specific to HTTP/2.
+
+
+
+ An implementation of HTTP/2 over TLS MUST use TLS 1.2 or higher with the restrictions on
+ feature set and cipher suite described in this section. Due to implementation
+ limitations, it might not be possible to fail TLS negotiation. An endpoint MUST
+ immediately terminate an HTTP/2 connection that does not meet these minimum requirements
+ with a connection error of type
+ INADEQUATE_SECURITY .
+
+
+
+
+ The TLS implementation MUST support the Server Name Indication
+ (SNI) extension to TLS. HTTP/2 clients MUST indicate the target domain name when
+ negotiating TLS.
+
+
+ The TLS implementation MUST disable compression. TLS compression can lead to the
+ exposure of information that would not otherwise be revealed .
+ Generic compression is unnecessary since HTTP/2 provides compression features that are
+ more aware of context and therefore likely to be more appropriate for use for
+ performance, security or other reasons.
+
+
+ The TLS implementation MUST disable renegotiation. An endpoint MUST treat a TLS
+ renegotiation as a connection error of type
+ PROTOCOL_ERROR . Note that disabling renegotiation can result in
+ long-lived connections becoming unusable due to limits on the number of messages the
+ underlying cipher suite can encipher.
+
+
+ A client MAY use renegotiation to provide confidentiality protection for client
+ credentials offered in the handshake, but any renegotiation MUST occur prior to sending
+ the connection preface. A server SHOULD request a client certificate if it sees a
+ renegotiation request immediately after establishing a connection.
+
+
+ This effectively prevents the use of renegotiation in response to a request for a
+ specific protected resource. A future specification might provide a way to support this
+ use case.
+
+
+
+
+
+ The set of TLS cipher suites that are permitted in HTTP/2 is restricted. HTTP/2 MUST
+ only be used with cipher suites that have ephemeral key exchange, such as the ephemeral Diffie-Hellman (DHE) or the elliptic curve variant (ECDHE) . Ephemeral key exchange MUST
+ have a minimum size of 2048 bits for DHE or security level of 128 bits for ECDHE.
+ Clients MUST accept DHE sizes of up to 4096 bits. HTTP MUST NOT be used with cipher
+ suites that use stream or block ciphers. Authenticated Encryption with Additional Data
+ (AEAD) modes, such as the Galois Counter Model (GCM) mode for
+ AES are acceptable.
+
+
+ The effect of these restrictions is that TLS 1.2 implementations could have
+ non-intersecting sets of available cipher suites, since these prevent the use of the
+ cipher suite that TLS 1.2 makes mandatory. To avoid this problem, implementations of
+ HTTP/2 that use TLS 1.2 MUST support TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 with P256 .
+
+
+ Clients MAY advertise support of cipher suites that are prohibited by the above
+ restrictions in order to allow for connection to servers that do not support HTTP/2.
+ This enables a fallback to protocols without these constraints without the additional
+ latency imposed by using a separate connection for fallback.
+
+
+
+
+
+
+
+
+ HTTP/2 relies on the HTTP/1.1 definition of authority for determining whether a server is
+ authoritative in providing a given response, see . This relies on local name resolution for the "http"
+ URI scheme, and the authenticated server identity for the "https" scheme (see ).
+
+
+
+
+
+ In a cross-protocol attack, an attacker causes a client to initiate a transaction in one
+ protocol toward a server that understands a different protocol. An attacker might be able
+ to cause the transaction to appear as valid transaction in the second protocol. In
+ combination with the capabilities of the web context, this can be used to interact with
+ poorly protected servers in private networks.
+
+
+ Completing a TLS handshake with an ALPN identifier for HTTP/2 can be considered sufficient
+ protection against cross protocol attacks. ALPN provides a positive indication that a
+ server is willing to proceed with HTTP/2, which prevents attacks on other TLS-based
+ protocols.
+
+
+ The encryption in TLS makes it difficult for attackers to control the data which could be
+ used in a cross-protocol attack on a cleartext protocol.
+
+
+ The cleartext version of HTTP/2 has minimal protection against cross-protocol attacks.
+ The connection preface contains a string that is
+ designed to confuse HTTP/1.1 servers, but no special protection is offered for other
+ protocols. A server that is willing to ignore parts of an HTTP/1.1 request containing an
+ Upgrade header field in addition to the client connection preface could be exposed to a
+ cross-protocol attack.
+
+
+
+
+
+ HTTP/2 header field names and values are encoded as sequences of octets with a length
+ prefix. This enables HTTP/2 to carry any string of octets as the name or value of a
+ header field. An intermediary that translates HTTP/2 requests or responses into HTTP/1.1
+ directly could permit the creation of corrupted HTTP/1.1 messages. An attacker might
+ exploit this behavior to cause the intermediary to create HTTP/1.1 messages with illegal
+ header fields, extra header fields, or even new messages that are entirely falsified.
+
+
+ Header field names or values that contain characters not permitted by HTTP/1.1, including
+ carriage return (ASCII 0xd) or line feed (ASCII 0xa) MUST NOT be translated verbatim by an
+ intermediary, as stipulated in .
+
+
+ Translation from HTTP/1.x to HTTP/2 does not produce the same opportunity to an attacker.
+ Intermediaries that perform translation to HTTP/2 MUST remove any instances of the obs-fold production from header field values.
+
+
+
+
+
+ Pushed responses do not have an explicit request from the client; the request
+ is provided by the server in the PUSH_PROMISE frame.
+
+
+ Caching responses that are pushed is possible based on the guidance provided by the origin
+ server in the Cache-Control header field. However, this can cause issues if a single
+ server hosts more than one tenant. For example, a server might offer multiple users each
+ a small portion of its URI space.
+
+
+ Where multiple tenants share space on the same server, that server MUST ensure that
+ tenants are not able to push representations of resources that they do not have authority
+ over. Failure to enforce this would allow a tenant to provide a representation that would
+ be served out of cache, overriding the actual representation that the authoritative tenant
+ provides.
+
+
+ Pushed responses for which an origin server is not authoritative (see
+ ) are never cached or used.
+
+
+
+
+
+ An HTTP/2 connection can demand a greater commitment of resources to operate than a
+ HTTP/1.1 connection. The use of header compression and flow control depend on a
+ commitment of resources for storing a greater amount of state. Settings for these
+ features ensure that memory commitments for these features are strictly bounded.
+
+
+ The number of PUSH_PROMISE frames is not constrained in the same fashion.
+ A client that accepts server push SHOULD limit the number of streams it allows to be in
+ the "reserved (remote)" state. Excessive number of server push streams can be treated as
+ a stream error of type
+ ENHANCE_YOUR_CALM .
+
+
+ Processing capacity cannot be guarded as effectively as state capacity.
+
+
+ The SETTINGS frame can be abused to cause a peer to expend additional
+ processing time. This might be done by pointlessly changing SETTINGS parameters, setting
+ multiple undefined parameters, or changing the same setting multiple times in the same
+ frame. WINDOW_UPDATE or PRIORITY frames can be abused to
+ cause an unnecessary waste of resources.
+
+
+ Large numbers of small or empty frames can be abused to cause a peer to expend time
+ processing frame headers. Note however that some uses are entirely legitimate, such as
+ the sending of an empty DATA frame to end a stream.
+
+
+ Header compression also offers some opportunities to waste processing resources; see for more details on potential abuses.
+
+
+ Limits in SETTINGS parameters cannot be reduced instantaneously, which
+ leaves an endpoint exposed to behavior from a peer that could exceed the new limits. In
+ particular, immediately after establishing a connection, limits set by a server are not
+ known to clients and could be exceeded without being an obvious protocol violation.
+
+
+ All these features - i.e., SETTINGS changes, small frames, header
+ compression - have legitimate uses. These features become a burden only when they are
+ used unnecessarily or to excess.
+
+
+ An endpoint that doesn't monitor this behavior exposes itself to a risk of denial of
+ service attack. Implementations SHOULD track the use of these features and set limits on
+ their use. An endpoint MAY treat activity that is suspicious as a connection error of type
+ ENHANCE_YOUR_CALM .
+
+
+
+
+ A large header block can cause an implementation to
+ commit a large amount of state. Header fields that are critical for routing can appear
+ toward the end of a header block, which prevents streaming of header fields to their
+ ultimate destination. For this an other reasons, such as ensuring cache correctness,
+ means that an endpoint might need to buffer the entire header block. Since there is no
+ hard limit to the size of a header block, some endpoints could be forced commit a large
+ amount of available memory for header fields.
+
+
+ An endpoint can use the SETTINGS_MAX_HEADER_LIST_SIZE to advise peers of
+ limits that might apply on the size of header blocks. This setting is only advisory, so
+ endpoints MAY choose to send header blocks that exceed this limit and risk having the
+ request or response being treated as malformed. This setting specific to a connection,
+ so any request or response could encounter a hop with a lower, unknown limit. An
+ intermediary can attempt to avoid this problem by passing on values presented by
+ different peers, but they are not obligated to do so.
+
+
+ A server that receives a larger header block than it is willing to handle can send an
+ HTTP 431 (Request Header Fields Too Large) status code . A
+ client can discard responses that it cannot process. The header block MUST be processed
+ to ensure a consistent connection state, unless the connection is closed.
+
+
+
+
+
+
+ HTTP/2 enables greater use of compression for both header fields ( ) and entity bodies. Compression can allow an attacker to recover
+ secret data when it is compressed in the same context as data under attacker control.
+
+
+ There are demonstrable attacks on compression that exploit the characteristics of the web
+ (e.g., ). The attacker induces multiple requests containing
+ varying plaintext, observing the length of the resulting ciphertext in each, which
+ reveals a shorter length when a guess about the secret is correct.
+
+
+ Implementations communicating on a secure channel MUST NOT compress content that includes
+ both confidential and attacker-controlled data unless separate compression dictionaries
+ are used for each source of data. Compression MUST NOT be used if the source of data
+ cannot be reliably determined. Generic stream compression, such as that provided by TLS
+ MUST NOT be used with HTTP/2 ( ).
+
+
+ Further considerations regarding the compression of header fields are described in .
+
+
+
+
+
+ Padding within HTTP/2 is not intended as a replacement for general purpose padding, such
+ as might be provided by TLS . Redundant padding could even be
+ counterproductive. Correct application can depend on having specific knowledge of the
+ data that is being padded.
+
+
+ To mitigate attacks that rely on compression, disabling or limiting compression might be
+ preferable to padding as a countermeasure.
+
+
+ Padding can be used to obscure the exact size of frame content, and is provided to
+ mitigate specific attacks within HTTP. For example, attacks where compressed content
+ includes both attacker-controlled plaintext and secret data (see for example, ).
+
+
+ Use of padding can result in less protection than might seem immediately obvious. At
+ best, padding only makes it more difficult for an attacker to infer length information by
+ increasing the number of frames an attacker has to observe. Incorrectly implemented
+ padding schemes can be easily defeated. In particular, randomized padding with a
+ predictable distribution provides very little protection; similarly, padding payloads to a
+ fixed size exposes information as payload sizes cross the fixed size boundary, which could
+ be possible if an attacker can control plaintext.
+
+
+ Intermediaries SHOULD retain padding for DATA frames, but MAY drop padding
+ for HEADERS and PUSH_PROMISE frames. A valid reason for an
+ intermediary to change the amount of padding of frames is to improve the protections that
+ padding provides.
+
+
+
+
+
+ Several characteristics of HTTP/2 provide an observer an opportunity to correlate actions
+ of a single client or server over time. This includes the value of settings, the manner
+ in which flow control windows are managed, the way priorities are allocated to streams,
+ timing of reactions to stimulus, and handling of any optional features.
+
+
+ As far as this creates observable differences in behavior, they could be used as a basis
+ for fingerprinting a specific client, as defined in .
+
+
+
+
+
+
+ A string for identifying HTTP/2 is entered into the "Application Layer Protocol Negotiation
+ (ALPN) Protocol IDs" registry established in .
+
+
+ This document establishes a registry for frame types, settings, and error codes. These new
+ registries are entered into a new "Hypertext Transfer Protocol (HTTP) 2 Parameters" section.
+
+
+ This document registers the HTTP2-Settings header field for
+ use in HTTP; and the 421 (Misdirected Request) status code.
+
+
+ This document registers the PRI method for use in HTTP, to avoid
+ collisions with the connection preface .
+
+
+
+
+ This document creates two registrations for the identification of HTTP/2 in the
+ "Application Layer Protocol Negotiation (ALPN) Protocol IDs" registry established in .
+
+
+ The "h2" string identifies HTTP/2 when used over TLS:
+
+ HTTP/2 over TLS
+ 0x68 0x32 ("h2")
+ This document
+
+
+
+ The "h2c" string identifies HTTP/2 when used over cleartext TCP:
+
+ HTTP/2 over TCP
+ 0x68 0x32 0x63 ("h2c")
+ This document
+
+
+
+
+
+
+ This document establishes a registry for HTTP/2 frame type codes. The "HTTP/2 Frame
+ Type" registry manages an 8-bit space. The "HTTP/2 Frame Type" registry operates under
+ either of the "IETF Review" or "IESG Approval" policies for
+ values between 0x00 and 0xef, with values between 0xf0 and 0xff being reserved for
+ experimental use.
+
+
+ New entries in this registry require the following information:
+
+
+ A name or label for the frame type.
+
+
+ The 8-bit code assigned to the frame type.
+
+
+ A reference to a specification that includes a description of the frame layout,
+ it's semantics and flags that the frame type uses, including any parts of the frame
+ that are conditionally present based on the value of flags.
+
+
+
+
+ The entries in the following table are registered by this document.
+
+
+ Frame Type
+ Code
+ Section
+ DATA 0x0
+ HEADERS 0x1
+ PRIORITY 0x2
+ RST_STREAM 0x3
+ SETTINGS 0x4
+ PUSH_PROMISE 0x5
+ PING 0x6
+ GOAWAY 0x7
+ WINDOW_UPDATE 0x8
+ CONTINUATION 0x9
+
+
+
+
+
+ This document establishes a registry for HTTP/2 settings. The "HTTP/2 Settings" registry
+ manages a 16-bit space. The "HTTP/2 Settings" registry operates under the "Expert Review" policy for values in the range from 0x0000 to
+ 0xefff, with values between and 0xf000 and 0xffff being reserved for experimental use.
+
+
+ New registrations are advised to provide the following information:
+
+
+ A symbolic name for the setting. Specifying a setting name is optional.
+
+
+ The 16-bit code assigned to the setting.
+
+
+ An initial value for the setting.
+
+
+ An optional reference to a specification that describes the use of the setting.
+
+
+
+
+ An initial set of setting registrations can be found in .
+
+
+ Name
+ Code
+ Initial Value
+ Specification
+ HEADER_TABLE_SIZE
+ 0x1 4096
+ ENABLE_PUSH
+ 0x2 1
+ MAX_CONCURRENT_STREAMS
+ 0x3 (infinite)
+ INITIAL_WINDOW_SIZE
+ 0x4 65535
+ MAX_FRAME_SIZE
+ 0x5 16384
+ MAX_HEADER_LIST_SIZE
+ 0x6 (infinite)
+
+
+
+
+
+
+ This document establishes a registry for HTTP/2 error codes. The "HTTP/2 Error Code"
+ registry manages a 32-bit space. The "HTTP/2 Error Code" registry operates under the
+ "Expert Review" policy .
+
+
+ Registrations for error codes are required to include a description of the error code. An
+ expert reviewer is advised to examine new registrations for possible duplication with
+ existing error codes. Use of existing registrations is to be encouraged, but not
+ mandated.
+
+
+ New registrations are advised to provide the following information:
+
+
+ A name for the error code. Specifying an error code name is optional.
+
+
+ The 32-bit error code value.
+
+
+ A brief description of the error code semantics, longer if no detailed specification
+ is provided.
+
+
+ An optional reference for a specification that defines the error code.
+
+
+
+
+ The entries in the following table are registered by this document.
+
+
+ Name
+ Code
+ Description
+ Specification
+ NO_ERROR 0x0
+ Graceful shutdown
+
+ PROTOCOL_ERROR 0x1
+ Protocol error detected
+
+ INTERNAL_ERROR 0x2
+ Implementation fault
+
+ FLOW_CONTROL_ERROR 0x3
+ Flow control limits exceeded
+
+ SETTINGS_TIMEOUT 0x4
+ Settings not acknowledged
+
+ STREAM_CLOSED 0x5
+ Frame received for closed stream
+
+ FRAME_SIZE_ERROR 0x6
+ Frame size incorrect
+
+ REFUSED_STREAM 0x7
+ Stream not processed
+
+ CANCEL 0x8
+ Stream cancelled
+
+ COMPRESSION_ERROR 0x9
+ Compression state not updated
+
+ CONNECT_ERROR 0xa
+ TCP connection error for CONNECT method
+
+ ENHANCE_YOUR_CALM 0xb
+ Processing capacity exceeded
+
+ INADEQUATE_SECURITY 0xc
+ Negotiated TLS parameters not acceptable
+
+
+
+
+
+
+
+ This section registers the HTTP2-Settings header field in the
+ Permanent Message Header Field Registry .
+
+
+ HTTP2-Settings
+
+
+ http
+
+
+ standard
+
+
+ IETF
+
+
+ of this document
+
+
+ This header field is only used by an HTTP/2 client for Upgrade-based negotiation.
+
+
+
+
+
+
+
+ This section registers the PRI method in the HTTP Method
+ Registry ( ).
+
+
+ PRI
+
+
+ No
+
+
+ No
+
+
+ of this document
+
+
+ This method is never used by an actual client. This method will appear to be used
+ when an HTTP/1.1 server or intermediary attempts to parse an HTTP/2 connection
+ preface.
+
+
+
+
+
+
+
+ This document registers the 421 (Misdirected Request) HTTP Status code in the Hypertext
+ Transfer Protocol (HTTP) Status Code Registry ( ).
+
+
+
+
+ 421
+
+
+ Misdirected Request
+
+
+ of this document
+
+
+
+
+
+
+
+
+
+ This document includes substantial input from the following individuals:
+
+
+ Adam Langley, Wan-Teh Chang, Jim Morrison, Mark Nottingham, Alyssa Wilk, Costin
+ Manolache, William Chan, Vitaliy Lvin, Joe Chan, Adam Barth, Ryan Hamilton, Gavin
+ Peters, Kent Alstad, Kevin Lindsay, Paul Amer, Fan Yang, Jonathan Leighton (SPDY
+ contributors).
+
+
+ Gabriel Montenegro and Willy Tarreau (Upgrade mechanism).
+
+
+ William Chan, Salvatore Loreto, Osama Mazahir, Gabriel Montenegro, Jitu Padhye, Roberto
+ Peon, Rob Trace (Flow control).
+
+
+ Mike Bishop (Extensibility).
+
+
+ Mark Nottingham, Julian Reschke, James Snell, Jeff Pinner, Mike Bishop, Herve Ruellan
+ (Substantial editorial contributions).
+
+
+ Kari Hurtta, Tatsuhiro Tsujikawa, Greg Wilkins, Poul-Henning Kamp.
+
+
+ Alexey Melnikov was an editor of this document during 2013.
+
+
+ A substantial proportion of Martin's contribution was supported by Microsoft during his
+ employment there.
+
+
+
+
+
+
+
+
+
+
+ HPACK - Header Compression for HTTP/2
+
+
+
+
+
+
+
+
+
+
+
+ Transmission Control Protocol
+
+
+ University of Southern California (USC)/Information Sciences
+ Institute
+
+
+
+
+
+
+
+
+
+
+ Key words for use in RFCs to Indicate Requirement Levels
+
+
+ Harvard University
+ sob@harvard.edu
+
+
+
+
+
+
+
+
+
+
+ HTTP Over TLS
+
+
+
+
+
+
+
+
+
+ Uniform Resource Identifier (URI): Generic
+ Syntax
+
+
+
+
+
+
+
+
+
+
+
+ The Base16, Base32, and Base64 Data Encodings
+
+
+
+
+
+
+
+
+ Guidelines for Writing an IANA Considerations Section in RFCs
+
+
+
+
+
+
+
+
+
+
+ Augmented BNF for Syntax Specifications: ABNF
+
+
+
+
+
+
+
+
+
+
+ The Transport Layer Security (TLS) Protocol Version 1.2
+
+
+
+
+
+
+
+
+
+
+ Transport Layer Security (TLS) Extensions: Extension Definitions
+
+
+
+
+
+
+
+
+
+ Transport Layer Security (TLS) Application-Layer Protocol Negotiation Extension
+
+
+
+
+
+
+
+
+
+
+
+
+ TLS Elliptic Curve Cipher Suites with SHA-256/384 and AES Galois
+ Counter Mode (GCM)
+
+
+
+
+
+
+
+
+
+
+ Digital Signature Standard (DSS)
+
+ NIST
+
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
+
+ Adobe Systems Incorporated
+ fielding@gbiv.com
+
+
+ greenbytes GmbH
+ julian.reschke@greenbytes.de
+
+
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content
+
+ Adobe Systems Incorporated
+ fielding@gbiv.com
+
+
+ greenbytes GmbH
+ julian.reschke@greenbytes.de
+
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests
+
+ Adobe Systems Incorporated
+ fielding@gbiv.com
+
+
+ greenbytes GmbH
+ julian.reschke@greenbytes.de
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol (HTTP/1.1): Range Requests
+
+ Adobe Systems Incorporated
+ fielding@gbiv.com
+
+
+ World Wide Web Consortium
+ ylafon@w3.org
+
+
+ greenbytes GmbH
+ julian.reschke@greenbytes.de
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol (HTTP/1.1): Caching
+
+ Adobe Systems Incorporated
+ fielding@gbiv.com
+
+
+ Akamai
+ mnot@mnot.net
+
+
+ greenbytes GmbH
+ julian.reschke@greenbytes.de
+
+
+
+
+
+
+
+
+ Hypertext Transfer Protocol (HTTP/1.1): Authentication
+
+ Adobe Systems Incorporated
+ fielding@gbiv.com
+
+
+ greenbytes GmbH
+ julian.reschke@greenbytes.de
+
+
+
+
+
+
+
+
+
+ HTTP State Management Mechanism
+
+
+
+
+
+
+
+
+
+
+
+ TCP Extensions for High Performance
+
+
+
+
+
+
+
+
+
+
+
+ Transport Layer Security Protocol Compression Methods
+
+
+
+
+
+
+
+
+ Additional HTTP Status Codes
+
+
+
+
+
+
+
+
+
+
+ Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AES Galois Counter Mode (GCM) Cipher Suites for TLS
+
+
+
+
+
+
+
+
+
+
+
+ HTML5
+
+
+
+
+
+
+
+
+
+
+ Latest version available at
+ .
+
+
+
+
+
+
+ Talking to Yourself for Fun and Profit
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BREACH: Reviving the CRIME Attack
+
+
+
+
+
+
+
+
+
+
+ Registration Procedures for Message Header Fields
+
+ Nine by Nine
+ GK-IETF@ninebynine.org
+
+
+ BEA Systems
+ mnot@pobox.com
+
+
+ HP Labs
+ JeffMogul@acm.org
+
+
+
+
+
+
+
+
+
+ Recommendations for Secure Use of TLS and DTLS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ HTTP Alternative Services
+
+
+ Akamai
+
+
+ Mozilla
+
+
+ greenbytes
+
+
+
+
+
+
+
+
+
+
+ This section is to be removed by RFC Editor before publication.
+
+
+
+
+ Renamed Not Authoritative status code to Misdirected Request.
+
+
+
+
+
+ Pseudo-header fields are now required to appear strictly before regular ones.
+
+
+ Restored 1xx series status codes, except 101.
+
+
+ Changed frame length field 24-bits. Expanded frame header to 9 octets. Added a setting
+ to limit the damage.
+
+
+ Added a setting to advise peers of header set size limits.
+
+
+ Removed segments.
+
+
+ Made non-semantic-bearing HEADERS frames illegal in the HTTP mapping.
+
+
+
+
+
+ Restored extensibility options.
+
+
+ Restricting TLS cipher suites to AEAD only.
+
+
+ Removing Content-Encoding requirements.
+
+
+ Permitting the use of PRIORITY after stream close.
+
+
+ Removed ALTSVC frame.
+
+
+ Removed BLOCKED frame.
+
+
+ Reducing the maximum padding size to 256 octets; removing padding from
+ CONTINUATION frames.
+
+
+ Removed per-frame GZIP compression.
+
+
+
+
+
+ Added BLOCKED frame (at risk).
+
+
+ Simplified priority scheme.
+
+
+ Added DATA per-frame GZIP compression.
+
+
+
+
+
+ Changed "connection header" to "connection preface" to avoid confusion.
+
+
+ Added dependency-based stream prioritization.
+
+
+ Added "h2c" identifier to distinguish between cleartext and secured HTTP/2.
+
+
+ Adding missing padding to PUSH_PROMISE .
+
+
+ Integrate ALTSVC frame and supporting text.
+
+
+ Dropping requirement on "deflate" Content-Encoding.
+
+
+ Improving security considerations around use of compression.
+
+
+
+
+
+ Adding padding for data frames.
+
+
+ Renumbering frame types, error codes, and settings.
+
+
+ Adding INADEQUATE_SECURITY error code.
+
+
+ Updating TLS usage requirements to 1.2; forbidding TLS compression.
+
+
+ Removing extensibility for frames and settings.
+
+
+ Changing setting identifier size.
+
+
+ Removing the ability to disable flow control.
+
+
+ Changing the protocol identification token to "h2".
+
+
+ Changing the use of :authority to make it optional and to allow userinfo in non-HTTP
+ cases.
+
+
+ Allowing split on 0x0 for Cookie.
+
+
+ Reserved PRI method in HTTP/1.1 to avoid possible future collisions.
+
+
+
+
+
+ Added cookie crumbling for more efficient header compression.
+
+
+ Added header field ordering with the value-concatenation mechanism.
+
+
+
+
+
+ Marked draft for implementation.
+
+
+
+
+
+ Adding definition for CONNECT method.
+
+
+ Constraining the use of push to safe, cacheable methods with no request body.
+
+
+ Changing from :host to :authority to remove any potential confusion.
+
+
+ Adding setting for header compression table size.
+
+
+ Adding settings acknowledgement.
+
+
+ Removing unnecessary and potentially problematic flags from CONTINUATION.
+
+
+ Added denial of service considerations.
+
+
+
+
+ Marking the draft ready for implementation.
+
+
+ Renumbering END_PUSH_PROMISE flag.
+
+
+ Editorial clarifications and changes.
+
+
+
+
+
+ Added CONTINUATION frame for HEADERS and PUSH_PROMISE.
+
+
+ PUSH_PROMISE is no longer implicitly prohibited if SETTINGS_MAX_CONCURRENT_STREAMS is
+ zero.
+
+
+ Push expanded to allow all safe methods without a request body.
+
+
+ Clarified the use of HTTP header fields in requests and responses. Prohibited HTTP/1.1
+ hop-by-hop header fields.
+
+
+ Requiring that intermediaries not forward requests with missing or illegal routing
+ :-headers.
+
+
+ Clarified requirements around handling different frames after stream close, stream reset
+ and GOAWAY .
+
+
+ Added more specific prohibitions for sending of different frame types in various stream
+ states.
+
+
+ Making the last received setting value the effective value.
+
+
+ Clarified requirements on TLS version, extension and ciphers.
+
+
+
+
+
+ Committed major restructuring atrocities.
+
+
+ Added reference to first header compression draft.
+
+
+ Added more formal description of frame lifecycle.
+
+
+ Moved END_STREAM (renamed from FINAL) back to HEADERS /DATA .
+
+
+ Removed HEADERS+PRIORITY, added optional priority to HEADERS frame.
+
+
+ Added PRIORITY frame.
+
+
+
+
+
+ Added continuations to frames carrying header blocks.
+
+
+ Replaced use of "session" with "connection" to avoid confusion with other HTTP stateful
+ concepts, like cookies.
+
+
+ Removed "message".
+
+
+ Switched to TLS ALPN from NPN.
+
+
+ Editorial changes.
+
+
+
+
+
+ Added IANA considerations section for frame types, error codes and settings.
+
+
+ Removed data frame compression.
+
+
+ Added PUSH_PROMISE .
+
+
+ Added globally applicable flags to framing.
+
+
+ Removed zlib-based header compression mechanism.
+
+
+ Updated references.
+
+
+ Clarified stream identifier reuse.
+
+
+ Removed CREDENTIALS frame and associated mechanisms.
+
+
+ Added advice against naive implementation of flow control.
+
+
+ Added session header section.
+
+
+ Restructured frame header. Removed distinction between data and control frames.
+
+
+ Altered flow control properties to include session-level limits.
+
+
+ Added note on cacheability of pushed resources and multiple tenant servers.
+
+
+ Changed protocol label form based on discussions.
+
+
+
+
+
+ Changed title throughout.
+
+
+ Removed section on Incompatibilities with SPDY draft#2.
+
+
+ Changed INTERNAL_ERROR on GOAWAY to have a value of 2 .
+
+
+ Replaced abstract and introduction.
+
+
+ Added section on starting HTTP/2.0, including upgrade mechanism.
+
+
+ Removed unused references.
+
+
+ Added flow control principles based on .
+
+
+
+
+
+ Adopted as base for draft-ietf-httpbis-http2.
+
+
+ Updated authors/editors list.
+
+
+ Added status note.
+
+
+
+
+
+
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/transport.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/transport.go
new file mode 100644
index 000000000..fcad20367
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/transport.go
@@ -0,0 +1,553 @@
+// Copyright 2015 The Go Authors.
+// See https://go.googlesource.com/go/+/master/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://go.googlesource.com/go/+/master/LICENSE
+
+package http2
+
+import (
+ "bufio"
+ "bytes"
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+)
+
+type Transport struct {
+ Fallback http.RoundTripper
+
+ // TODO: remove this and make more general with a TLS dial hook, like http
+ InsecureTLSDial bool
+
+ connMu sync.Mutex
+ conns map[string][]*clientConn // key is host:port
+}
+
+type clientConn struct {
+ t *Transport
+ tconn *tls.Conn
+ tlsState *tls.ConnectionState
+ connKey []string // key(s) this connection is cached in, in t.conns
+
+ readerDone chan struct{} // closed on error
+ readerErr error // set before readerDone is closed
+ hdec *hpack.Decoder
+ nextRes *http.Response
+
+ mu sync.Mutex
+ closed bool
+ goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
+ streams map[uint32]*clientStream
+ nextStreamID uint32
+ bw *bufio.Writer
+ werr error // first write error that has occurred
+ br *bufio.Reader
+ fr *Framer
+ // Settings from peer:
+ maxFrameSize uint32
+ maxConcurrentStreams uint32
+ initialWindowSize uint32
+ hbuf bytes.Buffer // HPACK encoder writes into this
+ henc *hpack.Encoder
+}
+
+type clientStream struct {
+ ID uint32
+ resc chan resAndError
+ pw *io.PipeWriter
+ pr *io.PipeReader
+}
+
+type stickyErrWriter struct {
+ w io.Writer
+ err *error
+}
+
+func (sew stickyErrWriter) Write(p []byte) (n int, err error) {
+ if *sew.err != nil {
+ return 0, *sew.err
+ }
+ n, err = sew.w.Write(p)
+ *sew.err = err
+ return
+}
+
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+ if req.URL.Scheme != "https" {
+ if t.Fallback == nil {
+ return nil, errors.New("http2: unsupported scheme and no Fallback")
+ }
+ return t.Fallback.RoundTrip(req)
+ }
+
+ host, port, err := net.SplitHostPort(req.URL.Host)
+ if err != nil {
+ host = req.URL.Host
+ port = "443"
+ }
+
+ for {
+ cc, err := t.getClientConn(host, port)
+ if err != nil {
+ return nil, err
+ }
+ res, err := cc.roundTrip(req)
+ if shouldRetryRequest(err) { // TODO: or clientconn is overloaded (too many outstanding requests)?
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+ }
+}
+
+// CloseIdleConnections closes any connections which were previously
+// connected from previous requests but are now sitting idle.
+// It does not interrupt any connections currently in use.
+func (t *Transport) CloseIdleConnections() {
+ t.connMu.Lock()
+ defer t.connMu.Unlock()
+ for _, vv := range t.conns {
+ for _, cc := range vv {
+ cc.closeIfIdle()
+ }
+ }
+}
+
+var errClientConnClosed = errors.New("http2: client conn is closed")
+
+func shouldRetryRequest(err error) bool {
+ // TODO: or GOAWAY graceful shutdown stuff
+ return err == errClientConnClosed
+}
+
+func (t *Transport) removeClientConn(cc *clientConn) {
+ t.connMu.Lock()
+ defer t.connMu.Unlock()
+ for _, key := range cc.connKey {
+ vv, ok := t.conns[key]
+ if !ok {
+ continue
+ }
+ newList := filterOutClientConn(vv, cc)
+ if len(newList) > 0 {
+ t.conns[key] = newList
+ } else {
+ delete(t.conns, key)
+ }
+ }
+}
+
+func filterOutClientConn(in []*clientConn, exclude *clientConn) []*clientConn {
+ out := in[:0]
+ for _, v := range in {
+ if v != exclude {
+ out = append(out, v)
+ }
+ }
+ return out
+}
+
+func (t *Transport) getClientConn(host, port string) (*clientConn, error) {
+ t.connMu.Lock()
+ defer t.connMu.Unlock()
+
+ key := net.JoinHostPort(host, port)
+
+ for _, cc := range t.conns[key] {
+ if cc.canTakeNewRequest() {
+ return cc, nil
+ }
+ }
+ if t.conns == nil {
+ t.conns = make(map[string][]*clientConn)
+ }
+ cc, err := t.newClientConn(host, port, key)
+ if err != nil {
+ return nil, err
+ }
+ t.conns[key] = append(t.conns[key], cc)
+ return cc, nil
+}
+
+func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
+ cfg := &tls.Config{
+ ServerName: host,
+ NextProtos: []string{NextProtoTLS},
+ InsecureSkipVerify: t.InsecureTLSDial,
+ }
+ tconn, err := tls.Dial("tcp", host+":"+port, cfg)
+ if err != nil {
+ return nil, err
+ }
+ if err := tconn.Handshake(); err != nil {
+ return nil, err
+ }
+ if !t.InsecureTLSDial {
+ if err := tconn.VerifyHostname(cfg.ServerName); err != nil {
+ return nil, err
+ }
+ }
+ state := tconn.ConnectionState()
+ if p := state.NegotiatedProtocol; p != NextProtoTLS {
+ // TODO(bradfitz): fall back to Fallback
+ return nil, fmt.Errorf("bad protocol: %v", p)
+ }
+ if !state.NegotiatedProtocolIsMutual {
+ return nil, errors.New("could not negotiate protocol mutually")
+ }
+ if _, err := tconn.Write(clientPreface); err != nil {
+ return nil, err
+ }
+
+ cc := &clientConn{
+ t: t,
+ tconn: tconn,
+ connKey: []string{key}, // TODO: cert's validated hostnames too
+ tlsState: &state,
+ readerDone: make(chan struct{}),
+ nextStreamID: 1,
+ maxFrameSize: 16 << 10, // spec default
+ initialWindowSize: 65535, // spec default
+ maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
+ streams: make(map[uint32]*clientStream),
+ }
+ cc.bw = bufio.NewWriter(stickyErrWriter{tconn, &cc.werr})
+ cc.br = bufio.NewReader(tconn)
+ cc.fr = NewFramer(cc.bw, cc.br)
+ cc.henc = hpack.NewEncoder(&cc.hbuf)
+
+ cc.fr.WriteSettings()
+ // TODO: re-send more conn-level flow control tokens when server uses all these.
+ cc.fr.WriteWindowUpdate(0, 1<<30) // um, 0x7fffffff doesn't work to Google? it hangs?
+ cc.bw.Flush()
+ if cc.werr != nil {
+ return nil, cc.werr
+ }
+
+ // Read the obligatory SETTINGS frame
+ f, err := cc.fr.ReadFrame()
+ if err != nil {
+ return nil, err
+ }
+ sf, ok := f.(*SettingsFrame)
+ if !ok {
+ return nil, fmt.Errorf("expected settings frame, got: %T", f)
+ }
+ cc.fr.WriteSettingsAck()
+ cc.bw.Flush()
+
+ sf.ForeachSetting(func(s Setting) error {
+ switch s.ID {
+ case SettingMaxFrameSize:
+ cc.maxFrameSize = s.Val
+ case SettingMaxConcurrentStreams:
+ cc.maxConcurrentStreams = s.Val
+ case SettingInitialWindowSize:
+ cc.initialWindowSize = s.Val
+ default:
+ // TODO(bradfitz): handle more
+ log.Printf("Unhandled Setting: %v", s)
+ }
+ return nil
+ })
+ // TODO: figure out henc size
+ cc.hdec = hpack.NewDecoder(initialHeaderTableSize, cc.onNewHeaderField)
+
+ go cc.readLoop()
+ return cc, nil
+}
+
+func (cc *clientConn) setGoAway(f *GoAwayFrame) {
+ cc.mu.Lock()
+ defer cc.mu.Unlock()
+ cc.goAway = f
+}
+
+func (cc *clientConn) canTakeNewRequest() bool {
+ cc.mu.Lock()
+ defer cc.mu.Unlock()
+ return cc.goAway == nil &&
+ int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) &&
+ cc.nextStreamID < 2147483647
+}
+
+func (cc *clientConn) closeIfIdle() {
+ cc.mu.Lock()
+ if len(cc.streams) > 0 {
+ cc.mu.Unlock()
+ return
+ }
+ cc.closed = true
+ // TODO: do clients send GOAWAY too? maybe? Just Close:
+ cc.mu.Unlock()
+
+ cc.tconn.Close()
+}
+
+func (cc *clientConn) roundTrip(req *http.Request) (*http.Response, error) {
+ cc.mu.Lock()
+
+ if cc.closed {
+ cc.mu.Unlock()
+ return nil, errClientConnClosed
+ }
+
+ cs := cc.newStream()
+ hasBody := false // TODO
+
+ // we send: HEADERS[+CONTINUATION] + (DATA?)
+ hdrs := cc.encodeHeaders(req)
+ first := true
+ for len(hdrs) > 0 {
+ chunk := hdrs
+ if len(chunk) > int(cc.maxFrameSize) {
+ chunk = chunk[:cc.maxFrameSize]
+ }
+ hdrs = hdrs[len(chunk):]
+ endHeaders := len(hdrs) == 0
+ if first {
+ cc.fr.WriteHeaders(HeadersFrameParam{
+ StreamID: cs.ID,
+ BlockFragment: chunk,
+ EndStream: !hasBody,
+ EndHeaders: endHeaders,
+ })
+ first = false
+ } else {
+ cc.fr.WriteContinuation(cs.ID, endHeaders, chunk)
+ }
+ }
+ cc.bw.Flush()
+ werr := cc.werr
+ cc.mu.Unlock()
+
+ if hasBody {
+ // TODO: write data. and it should probably be interleaved:
+ // go ... io.Copy(dataFrameWriter{cc, cs, ...}, req.Body) ... etc
+ }
+
+ if werr != nil {
+ return nil, werr
+ }
+
+ re := <-cs.resc
+ if re.err != nil {
+ return nil, re.err
+ }
+ res := re.res
+ res.Request = req
+ res.TLS = cc.tlsState
+ return res, nil
+}
+
+// requires cc.mu be held.
+func (cc *clientConn) encodeHeaders(req *http.Request) []byte {
+ cc.hbuf.Reset()
+
+ // TODO(bradfitz): figure out :authority-vs-Host stuff between http2 and Go
+ host := req.Host
+ if host == "" {
+ host = req.URL.Host
+ }
+
+ path := req.URL.Path
+ if path == "" {
+ path = "/"
+ }
+
+ cc.writeHeader(":authority", host) // probably not right for all sites
+ cc.writeHeader(":method", req.Method)
+ cc.writeHeader(":path", path)
+ cc.writeHeader(":scheme", "https")
+
+ for k, vv := range req.Header {
+ lowKey := strings.ToLower(k)
+ if lowKey == "host" {
+ continue
+ }
+ for _, v := range vv {
+ cc.writeHeader(lowKey, v)
+ }
+ }
+ return cc.hbuf.Bytes()
+}
+
+func (cc *clientConn) writeHeader(name, value string) {
+ log.Printf("sending %q = %q", name, value)
+ cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value})
+}
+
+type resAndError struct {
+ res *http.Response
+ err error
+}
+
+// requires cc.mu be held.
+func (cc *clientConn) newStream() *clientStream {
+ cs := &clientStream{
+ ID: cc.nextStreamID,
+ resc: make(chan resAndError, 1),
+ }
+ cc.nextStreamID += 2
+ cc.streams[cs.ID] = cs
+ return cs
+}
+
+func (cc *clientConn) streamByID(id uint32, andRemove bool) *clientStream {
+ cc.mu.Lock()
+ defer cc.mu.Unlock()
+ cs := cc.streams[id]
+ if andRemove {
+ delete(cc.streams, id)
+ }
+ return cs
+}
+
+// runs in its own goroutine.
+func (cc *clientConn) readLoop() {
+ defer cc.t.removeClientConn(cc)
+ defer close(cc.readerDone)
+
+ activeRes := map[uint32]*clientStream{} // keyed by streamID
+ // Close any response bodies if the server closes prematurely.
+ // TODO: also do this if we've written the headers but not
+ // gotten a response yet.
+ defer func() {
+ err := cc.readerErr
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ for _, cs := range activeRes {
+ cs.pw.CloseWithError(err)
+ }
+ }()
+
+ // continueStreamID is the stream ID we're waiting for
+ // continuation frames for.
+ var continueStreamID uint32
+
+ for {
+ f, err := cc.fr.ReadFrame()
+ if err != nil {
+ cc.readerErr = err
+ return
+ }
+ log.Printf("Transport received %v: %#v", f.Header(), f)
+
+ streamID := f.Header().StreamID
+
+ _, isContinue := f.(*ContinuationFrame)
+ if isContinue {
+ if streamID != continueStreamID {
+ log.Printf("Protocol violation: got CONTINUATION with id %d; want %d", streamID, continueStreamID)
+ cc.readerErr = ConnectionError(ErrCodeProtocol)
+ return
+ }
+ } else if continueStreamID != 0 {
+ // Continue frames need to be adjacent in the stream
+ // and we were in the middle of headers.
+ log.Printf("Protocol violation: got %T for stream %d, want CONTINUATION for %d", f, streamID, continueStreamID)
+ cc.readerErr = ConnectionError(ErrCodeProtocol)
+ return
+ }
+
+ if streamID%2 == 0 {
+ // Ignore streams pushed from the server for now.
+ // These always have an even stream id.
+ continue
+ }
+ streamEnded := false
+ if ff, ok := f.(streamEnder); ok {
+ streamEnded = ff.StreamEnded()
+ }
+
+ cs := cc.streamByID(streamID, streamEnded)
+ if cs == nil {
+ log.Printf("Received frame for untracked stream ID %d", streamID)
+ continue
+ }
+
+ switch f := f.(type) {
+ case *HeadersFrame:
+ cc.nextRes = &http.Response{
+ Proto: "HTTP/2.0",
+ ProtoMajor: 2,
+ Header: make(http.Header),
+ }
+ cs.pr, cs.pw = io.Pipe()
+ cc.hdec.Write(f.HeaderBlockFragment())
+ case *ContinuationFrame:
+ cc.hdec.Write(f.HeaderBlockFragment())
+ case *DataFrame:
+ log.Printf("DATA: %q", f.Data())
+ cs.pw.Write(f.Data())
+ case *GoAwayFrame:
+ cc.t.removeClientConn(cc)
+ if f.ErrCode != 0 {
+ // TODO: deal with GOAWAY more. particularly the error code
+ log.Printf("transport got GOAWAY with error code = %v", f.ErrCode)
+ }
+ cc.setGoAway(f)
+ default:
+ log.Printf("Transport: unhandled response frame type %T", f)
+ }
+ headersEnded := false
+ if he, ok := f.(headersEnder); ok {
+ headersEnded = he.HeadersEnded()
+ if headersEnded {
+ continueStreamID = 0
+ } else {
+ continueStreamID = streamID
+ }
+ }
+
+ if streamEnded {
+ cs.pw.Close()
+ delete(activeRes, streamID)
+ }
+ if headersEnded {
+ if cs == nil {
+ panic("couldn't find stream") // TODO be graceful
+ }
+ // TODO: set the Body to one which notes the
+ // Close and also sends the server a
+ // RST_STREAM
+ cc.nextRes.Body = cs.pr
+ res := cc.nextRes
+ activeRes[streamID] = cs
+ cs.resc <- resAndError{res: res}
+ }
+ }
+}
+
+func (cc *clientConn) onNewHeaderField(f hpack.HeaderField) {
+ // TODO: verifiy pseudo headers come before non-pseudo headers
+ // TODO: verifiy the status is set
+ log.Printf("Header field: %+v", f)
+ if f.Name == ":status" {
+ code, err := strconv.Atoi(f.Value)
+ if err != nil {
+ panic("TODO: be graceful")
+ }
+ cc.nextRes.Status = f.Value + " " + http.StatusText(code)
+ cc.nextRes.StatusCode = code
+ return
+ }
+ if strings.HasPrefix(f.Name, ":") {
+ // "Endpoints MUST NOT generate pseudo-header fields other than those defined in this document."
+ // TODO: treat as invalid?
+ return
+ }
+ cc.nextRes.Header.Add(http.CanonicalHeaderKey(f.Name), f.Value)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/transport_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/transport_test.go
new file mode 100644
index 000000000..0622923ce
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/transport_test.go
@@ -0,0 +1,168 @@
+// Copyright 2015 The Go Authors.
+// See https://go.googlesource.com/go/+/master/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://go.googlesource.com/go/+/master/LICENSE
+
+package http2
+
+import (
+ "flag"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+var (
+ extNet = flag.Bool("extnet", false, "do external network tests")
+ transportHost = flag.String("transporthost", "http2.golang.org", "hostname to use for TestTransport")
+ insecure = flag.Bool("insecure", false, "insecure TLS dials")
+)
+
+func TestTransportExternal(t *testing.T) {
+ if !*extNet {
+ t.Skip("skipping external network test")
+ }
+ req, _ := http.NewRequest("GET", "https://"+*transportHost+"/", nil)
+ rt := &Transport{
+ InsecureTLSDial: *insecure,
+ }
+ res, err := rt.RoundTrip(req)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ res.Write(os.Stdout)
+}
+
+func TestTransport(t *testing.T) {
+ const body = "sup"
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, body)
+ })
+ defer st.Close()
+
+ tr := &Transport{InsecureTLSDial: true}
+ defer tr.CloseIdleConnections()
+
+ req, err := http.NewRequest("GET", st.ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+
+ t.Logf("Got res: %+v", res)
+ if g, w := res.StatusCode, 200; g != w {
+ t.Errorf("StatusCode = %v; want %v", g, w)
+ }
+ if g, w := res.Status, "200 OK"; g != w {
+ t.Errorf("Status = %q; want %q", g, w)
+ }
+ wantHeader := http.Header{
+ "Content-Length": []string{"3"},
+ "Content-Type": []string{"text/plain; charset=utf-8"},
+ }
+ if !reflect.DeepEqual(res.Header, wantHeader) {
+ t.Errorf("res Header = %v; want %v", res.Header, wantHeader)
+ }
+ if res.Request != req {
+ t.Errorf("Response.Request = %p; want %p", res.Request, req)
+ }
+ if res.TLS == nil {
+ t.Errorf("Response.TLS = nil; want non-nil", res.TLS)
+ }
+ slurp, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Error("Body read: %v", err)
+ } else if string(slurp) != body {
+ t.Errorf("Body = %q; want %q", slurp, body)
+ }
+
+}
+
+func TestTransportReusesConns(t *testing.T) {
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ io.WriteString(w, r.RemoteAddr)
+ }, optOnlyServer)
+ defer st.Close()
+ tr := &Transport{InsecureTLSDial: true}
+ defer tr.CloseIdleConnections()
+ get := func() string {
+ req, err := http.NewRequest("GET", st.ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ slurp, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ t.Fatalf("Body read: %v", err)
+ }
+ addr := strings.TrimSpace(string(slurp))
+ if addr == "" {
+ t.Fatalf("didn't get an addr in response")
+ }
+ return addr
+ }
+ first := get()
+ second := get()
+ if first != second {
+ t.Errorf("first and second responses were on different connections: %q vs %q", first, second)
+ }
+}
+
+func TestTransportAbortClosesPipes(t *testing.T) {
+ shutdown := make(chan struct{})
+ st := newServerTester(t,
+ func(w http.ResponseWriter, r *http.Request) {
+ w.(http.Flusher).Flush()
+ <-shutdown
+ },
+ optOnlyServer,
+ )
+ defer st.Close()
+ defer close(shutdown) // we must shutdown before st.Close() to avoid hanging
+
+ done := make(chan struct{})
+ requestMade := make(chan struct{})
+ go func() {
+ defer close(done)
+ tr := &Transport{
+ InsecureTLSDial: true,
+ }
+ req, err := http.NewRequest("GET", st.ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ close(requestMade)
+ _, err = ioutil.ReadAll(res.Body)
+ if err == nil {
+ t.Error("expected error from res.Body.Read")
+ }
+ }()
+
+ <-requestMade
+ // Now force the serve loop to end, via closing the connection.
+ st.closeConn()
+ // deadlock? that's a bug.
+ select {
+ case <-done:
+ case <-time.After(3 * time.Second):
+ t.Fatal("timeout")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/write.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/write.go
new file mode 100644
index 000000000..0a074a68f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/write.go
@@ -0,0 +1,204 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+)
+
+// writeFramer is implemented by any type that is used to write frames.
+type writeFramer interface {
+ writeFrame(writeContext) error
+}
+
+// writeContext is the interface needed by the various frame writer
+// types below. All the writeFrame methods below are scheduled via the
+// frame writing scheduler (see writeScheduler in writesched.go).
+//
+// This interface is implemented by *serverConn.
+// TODO: use it from the client code too, once it exists.
+type writeContext interface {
+ Framer() *Framer
+ Flush() error
+ CloseConn() error
+ // HeaderEncoder returns an HPACK encoder that writes to the
+ // returned buffer.
+ HeaderEncoder() (*hpack.Encoder, *bytes.Buffer)
+}
+
+// endsStream reports whether the given frame writer w will locally
+// close the stream.
+func endsStream(w writeFramer) bool {
+ switch v := w.(type) {
+ case *writeData:
+ return v.endStream
+ case *writeResHeaders:
+ return v.endStream
+ }
+ return false
+}
+
+type flushFrameWriter struct{}
+
+func (flushFrameWriter) writeFrame(ctx writeContext) error {
+ return ctx.Flush()
+}
+
+type writeSettings []Setting
+
+func (s writeSettings) writeFrame(ctx writeContext) error {
+ return ctx.Framer().WriteSettings([]Setting(s)...)
+}
+
+type writeGoAway struct {
+ maxStreamID uint32
+ code ErrCode
+}
+
+func (p *writeGoAway) writeFrame(ctx writeContext) error {
+ err := ctx.Framer().WriteGoAway(p.maxStreamID, p.code, nil)
+ if p.code != 0 {
+ ctx.Flush() // ignore error: we're hanging up on them anyway
+ time.Sleep(50 * time.Millisecond)
+ ctx.CloseConn()
+ }
+ return err
+}
+
+type writeData struct {
+ streamID uint32
+ p []byte
+ endStream bool
+}
+
+func (w *writeData) String() string {
+ return fmt.Sprintf("writeData(stream=%d, p=%d, endStream=%v)", w.streamID, len(w.p), w.endStream)
+}
+
+func (w *writeData) writeFrame(ctx writeContext) error {
+ return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
+}
+
+func (se StreamError) writeFrame(ctx writeContext) error {
+ return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
+}
+
+type writePingAck struct{ pf *PingFrame }
+
+func (w writePingAck) writeFrame(ctx writeContext) error {
+ return ctx.Framer().WritePing(true, w.pf.Data)
+}
+
+type writeSettingsAck struct{}
+
+func (writeSettingsAck) writeFrame(ctx writeContext) error {
+ return ctx.Framer().WriteSettingsAck()
+}
+
+// writeResHeaders is a request to write a HEADERS and 0+ CONTINUATION frames
+// for HTTP response headers from a server handler.
+type writeResHeaders struct {
+ streamID uint32
+ httpResCode int
+ h http.Header // may be nil
+ endStream bool
+
+ contentType string
+ contentLength string
+}
+
+func (w *writeResHeaders) writeFrame(ctx writeContext) error {
+ enc, buf := ctx.HeaderEncoder()
+ buf.Reset()
+ enc.WriteField(hpack.HeaderField{Name: ":status", Value: httpCodeString(w.httpResCode)})
+ for k, vv := range w.h {
+ k = lowerHeader(k)
+ for _, v := range vv {
+ // TODO: more of "8.1.2.2 Connection-Specific Header Fields"
+ if k == "transfer-encoding" && v != "trailers" {
+ continue
+ }
+ enc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ }
+ }
+ if w.contentType != "" {
+ enc.WriteField(hpack.HeaderField{Name: "content-type", Value: w.contentType})
+ }
+ if w.contentLength != "" {
+ enc.WriteField(hpack.HeaderField{Name: "content-length", Value: w.contentLength})
+ }
+
+ headerBlock := buf.Bytes()
+ if len(headerBlock) == 0 {
+ panic("unexpected empty hpack")
+ }
+
+ // For now we're lazy and just pick the minimum MAX_FRAME_SIZE
+ // that all peers must support (16KB). Later we could care
+ // more and send larger frames if the peer advertised it, but
+ // there's little point. Most headers are small anyway (so we
+ // generally won't have CONTINUATION frames), and extra frames
+ // only waste 9 bytes anyway.
+ const maxFrameSize = 16384
+
+ first := true
+ for len(headerBlock) > 0 {
+ frag := headerBlock
+ if len(frag) > maxFrameSize {
+ frag = frag[:maxFrameSize]
+ }
+ headerBlock = headerBlock[len(frag):]
+ endHeaders := len(headerBlock) == 0
+ var err error
+ if first {
+ first = false
+ err = ctx.Framer().WriteHeaders(HeadersFrameParam{
+ StreamID: w.streamID,
+ BlockFragment: frag,
+ EndStream: w.endStream,
+ EndHeaders: endHeaders,
+ })
+ } else {
+ err = ctx.Framer().WriteContinuation(w.streamID, endHeaders, frag)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+type write100ContinueHeadersFrame struct {
+ streamID uint32
+}
+
+func (w write100ContinueHeadersFrame) writeFrame(ctx writeContext) error {
+ enc, buf := ctx.HeaderEncoder()
+ buf.Reset()
+ enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
+ return ctx.Framer().WriteHeaders(HeadersFrameParam{
+ StreamID: w.streamID,
+ BlockFragment: buf.Bytes(),
+ EndStream: false,
+ EndHeaders: true,
+ })
+}
+
+type writeWindowUpdate struct {
+ streamID uint32 // or 0 for conn-level
+ n uint32
+}
+
+func (wu writeWindowUpdate) writeFrame(ctx writeContext) error {
+ return ctx.Framer().WriteWindowUpdate(wu.streamID, wu.n)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/writesched.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/writesched.go
new file mode 100644
index 000000000..0e1b7486f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/writesched.go
@@ -0,0 +1,286 @@
+// Copyright 2014 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.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import "fmt"
+
+// frameWriteMsg is a request to write a frame.
+type frameWriteMsg struct {
+ // write is the interface value that does the writing, once the
+ // writeScheduler (below) has decided to select this frame
+ // to write. The write functions are all defined in write.go.
+ write writeFramer
+
+ stream *stream // used for prioritization. nil for non-stream frames.
+
+ // done, if non-nil, must be a buffered channel with space for
+ // 1 message and is sent the return value from write (or an
+ // earlier error) when the frame has been written.
+ done chan error
+}
+
+// for debugging only:
+func (wm frameWriteMsg) String() string {
+ var streamID uint32
+ if wm.stream != nil {
+ streamID = wm.stream.id
+ }
+ var des string
+ if s, ok := wm.write.(fmt.Stringer); ok {
+ des = s.String()
+ } else {
+ des = fmt.Sprintf("%T", wm.write)
+ }
+ return fmt.Sprintf("[frameWriteMsg stream=%d, ch=%v, type: %v]", streamID, wm.done != nil, des)
+}
+
+// writeScheduler tracks pending frames to write, priorities, and decides
+// the next one to use. It is not thread-safe.
+type writeScheduler struct {
+ // zero are frames not associated with a specific stream.
+ // They're sent before any stream-specific freams.
+ zero writeQueue
+
+ // maxFrameSize is the maximum size of a DATA frame
+ // we'll write. Must be non-zero and between 16K-16M.
+ maxFrameSize uint32
+
+ // sq contains the stream-specific queues, keyed by stream ID.
+ // when a stream is idle, it's deleted from the map.
+ sq map[uint32]*writeQueue
+
+ // canSend is a slice of memory that's reused between frame
+ // scheduling decisions to hold the list of writeQueues (from sq)
+ // which have enough flow control data to send. After canSend is
+ // built, the best is selected.
+ canSend []*writeQueue
+
+ // pool of empty queues for reuse.
+ queuePool []*writeQueue
+}
+
+func (ws *writeScheduler) putEmptyQueue(q *writeQueue) {
+ if len(q.s) != 0 {
+ panic("queue must be empty")
+ }
+ ws.queuePool = append(ws.queuePool, q)
+}
+
+func (ws *writeScheduler) getEmptyQueue() *writeQueue {
+ ln := len(ws.queuePool)
+ if ln == 0 {
+ return new(writeQueue)
+ }
+ q := ws.queuePool[ln-1]
+ ws.queuePool = ws.queuePool[:ln-1]
+ return q
+}
+
+func (ws *writeScheduler) empty() bool { return ws.zero.empty() && len(ws.sq) == 0 }
+
+func (ws *writeScheduler) add(wm frameWriteMsg) {
+ st := wm.stream
+ if st == nil {
+ ws.zero.push(wm)
+ } else {
+ ws.streamQueue(st.id).push(wm)
+ }
+}
+
+func (ws *writeScheduler) streamQueue(streamID uint32) *writeQueue {
+ if q, ok := ws.sq[streamID]; ok {
+ return q
+ }
+ if ws.sq == nil {
+ ws.sq = make(map[uint32]*writeQueue)
+ }
+ q := ws.getEmptyQueue()
+ ws.sq[streamID] = q
+ return q
+}
+
+// take returns the most important frame to write and removes it from the scheduler.
+// It is illegal to call this if the scheduler is empty or if there are no connection-level
+// flow control bytes available.
+func (ws *writeScheduler) take() (wm frameWriteMsg, ok bool) {
+ if ws.maxFrameSize == 0 {
+ panic("internal error: ws.maxFrameSize not initialized or invalid")
+ }
+
+ // If there any frames not associated with streams, prefer those first.
+ // These are usually SETTINGS, etc.
+ if !ws.zero.empty() {
+ return ws.zero.shift(), true
+ }
+ if len(ws.sq) == 0 {
+ return
+ }
+
+ // Next, prioritize frames on streams that aren't DATA frames (no cost).
+ for id, q := range ws.sq {
+ if q.firstIsNoCost() {
+ return ws.takeFrom(id, q)
+ }
+ }
+
+ // Now, all that remains are DATA frames with non-zero bytes to
+ // send. So pick the best one.
+ if len(ws.canSend) != 0 {
+ panic("should be empty")
+ }
+ for _, q := range ws.sq {
+ if n := ws.streamWritableBytes(q); n > 0 {
+ ws.canSend = append(ws.canSend, q)
+ }
+ }
+ if len(ws.canSend) == 0 {
+ return
+ }
+ defer ws.zeroCanSend()
+
+ // TODO: find the best queue
+ q := ws.canSend[0]
+
+ return ws.takeFrom(q.streamID(), q)
+}
+
+// zeroCanSend is defered from take.
+func (ws *writeScheduler) zeroCanSend() {
+ for i := range ws.canSend {
+ ws.canSend[i] = nil
+ }
+ ws.canSend = ws.canSend[:0]
+}
+
+// streamWritableBytes returns the number of DATA bytes we could write
+// from the given queue's stream, if this stream/queue were
+// selected. It is an error to call this if q's head isn't a
+// *writeData.
+func (ws *writeScheduler) streamWritableBytes(q *writeQueue) int32 {
+ wm := q.head()
+ ret := wm.stream.flow.available() // max we can write
+ if ret == 0 {
+ return 0
+ }
+ if int32(ws.maxFrameSize) < ret {
+ ret = int32(ws.maxFrameSize)
+ }
+ if ret == 0 {
+ panic("internal error: ws.maxFrameSize not initialized or invalid")
+ }
+ wd := wm.write.(*writeData)
+ if len(wd.p) < int(ret) {
+ ret = int32(len(wd.p))
+ }
+ return ret
+}
+
+func (ws *writeScheduler) takeFrom(id uint32, q *writeQueue) (wm frameWriteMsg, ok bool) {
+ wm = q.head()
+ // If the first item in this queue costs flow control tokens
+ // and we don't have enough, write as much as we can.
+ if wd, ok := wm.write.(*writeData); ok && len(wd.p) > 0 {
+ allowed := wm.stream.flow.available() // max we can write
+ if allowed == 0 {
+ // No quota available. Caller can try the next stream.
+ return frameWriteMsg{}, false
+ }
+ if int32(ws.maxFrameSize) < allowed {
+ allowed = int32(ws.maxFrameSize)
+ }
+ // TODO: further restrict the allowed size, because even if
+ // the peer says it's okay to write 16MB data frames, we might
+ // want to write smaller ones to properly weight competing
+ // streams' priorities.
+
+ if len(wd.p) > int(allowed) {
+ wm.stream.flow.take(allowed)
+ chunk := wd.p[:allowed]
+ wd.p = wd.p[allowed:]
+ // Make up a new write message of a valid size, rather
+ // than shifting one off the queue.
+ return frameWriteMsg{
+ stream: wm.stream,
+ write: &writeData{
+ streamID: wd.streamID,
+ p: chunk,
+ // even if the original had endStream set, there
+ // arebytes remaining because len(wd.p) > allowed,
+ // so we know endStream is false:
+ endStream: false,
+ },
+ // our caller is blocking on the final DATA frame, not
+ // these intermediates, so no need to wait:
+ done: nil,
+ }, true
+ }
+ wm.stream.flow.take(int32(len(wd.p)))
+ }
+
+ q.shift()
+ if q.empty() {
+ ws.putEmptyQueue(q)
+ delete(ws.sq, id)
+ }
+ return wm, true
+}
+
+func (ws *writeScheduler) forgetStream(id uint32) {
+ q, ok := ws.sq[id]
+ if !ok {
+ return
+ }
+ delete(ws.sq, id)
+
+ // But keep it for others later.
+ for i := range q.s {
+ q.s[i] = frameWriteMsg{}
+ }
+ q.s = q.s[:0]
+ ws.putEmptyQueue(q)
+}
+
+type writeQueue struct {
+ s []frameWriteMsg
+}
+
+// streamID returns the stream ID for a non-empty stream-specific queue.
+func (q *writeQueue) streamID() uint32 { return q.s[0].stream.id }
+
+func (q *writeQueue) empty() bool { return len(q.s) == 0 }
+
+func (q *writeQueue) push(wm frameWriteMsg) {
+ q.s = append(q.s, wm)
+}
+
+// head returns the next item that would be removed by shift.
+func (q *writeQueue) head() frameWriteMsg {
+ if len(q.s) == 0 {
+ panic("invalid use of queue")
+ }
+ return q.s[0]
+}
+
+func (q *writeQueue) shift() frameWriteMsg {
+ if len(q.s) == 0 {
+ panic("invalid use of queue")
+ }
+ wm := q.s[0]
+ // TODO: less copy-happy queue.
+ copy(q.s, q.s[1:])
+ q.s[len(q.s)-1] = frameWriteMsg{}
+ q.s = q.s[:len(q.s)-1]
+ return wm
+}
+
+func (q *writeQueue) firstIsNoCost() bool {
+ if df, ok := q.s[0].write.(*writeData); ok {
+ return len(df.p) == 0
+ }
+ return true
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/z_spec_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/z_spec_test.go
new file mode 100644
index 000000000..07b53e3ae
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/z_spec_test.go
@@ -0,0 +1,357 @@
+// Copyright 2014 The Go Authors.
+// See https://code.google.com/p/go/source/browse/CONTRIBUTORS
+// Licensed under the same terms as Go itself:
+// https://code.google.com/p/go/source/browse/LICENSE
+
+package http2
+
+import (
+ "bytes"
+ "encoding/xml"
+ "flag"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+)
+
+var coverSpec = flag.Bool("coverspec", false, "Run spec coverage tests")
+
+// The global map of sentence coverage for the http2 spec.
+var defaultSpecCoverage specCoverage
+
+var loadSpecOnce sync.Once
+
+func loadSpec() {
+ if f, err := os.Open("testdata/draft-ietf-httpbis-http2.xml"); err != nil {
+ panic(err)
+ } else {
+ defaultSpecCoverage = readSpecCov(f)
+ f.Close()
+ }
+}
+
+// covers marks all sentences for section sec in defaultSpecCoverage. Sentences not
+// "covered" will be included in report outputed by TestSpecCoverage.
+func covers(sec, sentences string) {
+ loadSpecOnce.Do(loadSpec)
+ defaultSpecCoverage.cover(sec, sentences)
+}
+
+type specPart struct {
+ section string
+ sentence string
+}
+
+func (ss specPart) Less(oo specPart) bool {
+ atoi := func(s string) int {
+ n, err := strconv.Atoi(s)
+ if err != nil {
+ panic(err)
+ }
+ return n
+ }
+ a := strings.Split(ss.section, ".")
+ b := strings.Split(oo.section, ".")
+ for len(a) > 0 {
+ if len(b) == 0 {
+ return false
+ }
+ x, y := atoi(a[0]), atoi(b[0])
+ if x == y {
+ a, b = a[1:], b[1:]
+ continue
+ }
+ return x < y
+ }
+ if len(b) > 0 {
+ return true
+ }
+ return false
+}
+
+type bySpecSection []specPart
+
+func (a bySpecSection) Len() int { return len(a) }
+func (a bySpecSection) Less(i, j int) bool { return a[i].Less(a[j]) }
+func (a bySpecSection) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+type specCoverage struct {
+ coverage map[specPart]bool
+ d *xml.Decoder
+}
+
+func joinSection(sec []int) string {
+ s := fmt.Sprintf("%d", sec[0])
+ for _, n := range sec[1:] {
+ s = fmt.Sprintf("%s.%d", s, n)
+ }
+ return s
+}
+
+func (sc specCoverage) readSection(sec []int) {
+ var (
+ buf = new(bytes.Buffer)
+ sub = 0
+ )
+ for {
+ tk, err := sc.d.Token()
+ if err != nil {
+ if err == io.EOF {
+ return
+ }
+ panic(err)
+ }
+ switch v := tk.(type) {
+ case xml.StartElement:
+ if skipElement(v) {
+ if err := sc.d.Skip(); err != nil {
+ panic(err)
+ }
+ if v.Name.Local == "section" {
+ sub++
+ }
+ break
+ }
+ switch v.Name.Local {
+ case "section":
+ sub++
+ sc.readSection(append(sec, sub))
+ case "xref":
+ buf.Write(sc.readXRef(v))
+ }
+ case xml.CharData:
+ if len(sec) == 0 {
+ break
+ }
+ buf.Write(v)
+ case xml.EndElement:
+ if v.Name.Local == "section" {
+ sc.addSentences(joinSection(sec), buf.String())
+ return
+ }
+ }
+ }
+}
+
+func (sc specCoverage) readXRef(se xml.StartElement) []byte {
+ var b []byte
+ for {
+ tk, err := sc.d.Token()
+ if err != nil {
+ panic(err)
+ }
+ switch v := tk.(type) {
+ case xml.CharData:
+ if b != nil {
+ panic("unexpected CharData")
+ }
+ b = []byte(string(v))
+ case xml.EndElement:
+ if v.Name.Local != "xref" {
+ panic("expected ")
+ }
+ if b != nil {
+ return b
+ }
+ sig := attrSig(se)
+ switch sig {
+ case "target":
+ return []byte(fmt.Sprintf("[%s]", attrValue(se, "target")))
+ case "fmt-of,rel,target", "fmt-,,rel,target":
+ return []byte(fmt.Sprintf("[%s, %s]", attrValue(se, "target"), attrValue(se, "rel")))
+ case "fmt-of,sec,target", "fmt-,,sec,target":
+ return []byte(fmt.Sprintf("[section %s of %s]", attrValue(se, "sec"), attrValue(se, "target")))
+ case "fmt-of,rel,sec,target":
+ return []byte(fmt.Sprintf("[section %s of %s, %s]", attrValue(se, "sec"), attrValue(se, "target"), attrValue(se, "rel")))
+ default:
+ panic(fmt.Sprintf("unknown attribute signature %q in %#v", sig, fmt.Sprintf("%#v", se)))
+ }
+ default:
+ panic(fmt.Sprintf("unexpected tag %q", v))
+ }
+ }
+}
+
+var skipAnchor = map[string]bool{
+ "intro": true,
+ "Overview": true,
+}
+
+var skipTitle = map[string]bool{
+ "Acknowledgements": true,
+ "Change Log": true,
+ "Document Organization": true,
+ "Conventions and Terminology": true,
+}
+
+func skipElement(s xml.StartElement) bool {
+ switch s.Name.Local {
+ case "artwork":
+ return true
+ case "section":
+ for _, attr := range s.Attr {
+ switch attr.Name.Local {
+ case "anchor":
+ if skipAnchor[attr.Value] || strings.HasPrefix(attr.Value, "changes.since.") {
+ return true
+ }
+ case "title":
+ if skipTitle[attr.Value] {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func readSpecCov(r io.Reader) specCoverage {
+ sc := specCoverage{
+ coverage: map[specPart]bool{},
+ d: xml.NewDecoder(r)}
+ sc.readSection(nil)
+ return sc
+}
+
+func (sc specCoverage) addSentences(sec string, sentence string) {
+ for _, s := range parseSentences(sentence) {
+ sc.coverage[specPart{sec, s}] = false
+ }
+}
+
+func (sc specCoverage) cover(sec string, sentence string) {
+ for _, s := range parseSentences(sentence) {
+ p := specPart{sec, s}
+ if _, ok := sc.coverage[p]; !ok {
+ panic(fmt.Sprintf("Not found in spec: %q, %q", sec, s))
+ }
+ sc.coverage[specPart{sec, s}] = true
+ }
+
+}
+
+var whitespaceRx = regexp.MustCompile(`\s+`)
+
+func parseSentences(sens string) []string {
+ sens = strings.TrimSpace(sens)
+ if sens == "" {
+ return nil
+ }
+ ss := strings.Split(whitespaceRx.ReplaceAllString(sens, " "), ". ")
+ for i, s := range ss {
+ s = strings.TrimSpace(s)
+ if !strings.HasSuffix(s, ".") {
+ s += "."
+ }
+ ss[i] = s
+ }
+ return ss
+}
+
+func TestSpecParseSentences(t *testing.T) {
+ tests := []struct {
+ ss string
+ want []string
+ }{
+ {"Sentence 1. Sentence 2.",
+ []string{
+ "Sentence 1.",
+ "Sentence 2.",
+ }},
+ {"Sentence 1. \nSentence 2.\tSentence 3.",
+ []string{
+ "Sentence 1.",
+ "Sentence 2.",
+ "Sentence 3.",
+ }},
+ }
+
+ for i, tt := range tests {
+ got := parseSentences(tt.ss)
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("%d: got = %q, want %q", i, got, tt.want)
+ }
+ }
+}
+
+func TestSpecCoverage(t *testing.T) {
+ if !*coverSpec {
+ t.Skip()
+ }
+
+ loadSpecOnce.Do(loadSpec)
+
+ var (
+ list []specPart
+ cv = defaultSpecCoverage.coverage
+ total = len(cv)
+ complete = 0
+ )
+
+ for sp, touched := range defaultSpecCoverage.coverage {
+ if touched {
+ complete++
+ } else {
+ list = append(list, sp)
+ }
+ }
+ sort.Stable(bySpecSection(list))
+
+ if testing.Short() && len(list) > 5 {
+ list = list[:5]
+ }
+
+ for _, p := range list {
+ t.Errorf("\tSECTION %s: %s", p.section, p.sentence)
+ }
+
+ t.Logf("%d/%d (%d%%) sentances covered", complete, total, (complete/total)*100)
+}
+
+func attrSig(se xml.StartElement) string {
+ var names []string
+ for _, attr := range se.Attr {
+ if attr.Name.Local == "fmt" {
+ names = append(names, "fmt-"+attr.Value)
+ } else {
+ names = append(names, attr.Name.Local)
+ }
+ }
+ sort.Strings(names)
+ return strings.Join(names, ",")
+}
+
+func attrValue(se xml.StartElement, attr string) string {
+ for _, a := range se.Attr {
+ if a.Name.Local == attr {
+ return a.Value
+ }
+ }
+ panic("unknown attribute " + attr)
+}
+
+func TestSpecPartLess(t *testing.T) {
+ tests := []struct {
+ sec1, sec2 string
+ want bool
+ }{
+ {"6.2.1", "6.2", false},
+ {"6.2", "6.2.1", true},
+ {"6.10", "6.10.1", true},
+ {"6.10", "6.1.1", false}, // 10, not 1
+ {"6.1", "6.1", false}, // equal, so not less
+ }
+ for _, tt := range tests {
+ got := (specPart{tt.sec1, "foo"}).Less(specPart{tt.sec2, "foo"})
+ if got != tt.want {
+ t.Errorf("Less(%q, %q) = %v; want %v", tt.sec1, tt.sec2, got, tt.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go
new file mode 100644
index 000000000..f1f8ab797
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver.go
@@ -0,0 +1,209 @@
+package semver
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+type Version struct {
+ Major int64
+ Minor int64
+ Patch int64
+ PreRelease PreRelease
+ Metadata string
+}
+
+type PreRelease string
+
+func splitOff(input *string, delim string) (val string) {
+ parts := strings.SplitN(*input, delim, 2)
+
+ if len(parts) == 2 {
+ *input = parts[0]
+ val = parts[1]
+ }
+
+ return val
+}
+
+func NewVersion(version string) (*Version, error) {
+ v := Version{}
+
+ dotParts := strings.SplitN(version, ".", 3)
+
+ if len(dotParts) != 3 {
+ return nil, errors.New(fmt.Sprintf("%s is not in dotted-tri format", version))
+ }
+
+ v.Metadata = splitOff(&dotParts[2], "+")
+ v.PreRelease = PreRelease(splitOff(&dotParts[2], "-"))
+
+ parsed := make([]int64, 3, 3)
+
+ for i, v := range dotParts[:3] {
+ val, err := strconv.ParseInt(v, 10, 64)
+ parsed[i] = val
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ v.Major = parsed[0]
+ v.Minor = parsed[1]
+ v.Patch = parsed[2]
+
+ return &v, nil
+}
+
+func Must(v *Version, err error) *Version {
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+func (v *Version) String() string {
+ var buffer bytes.Buffer
+
+ base := fmt.Sprintf("%d.%d.%d", v.Major, v.Minor, v.Patch)
+ buffer.WriteString(base)
+
+ if v.PreRelease != "" {
+ buffer.WriteString(fmt.Sprintf("-%s", v.PreRelease))
+ }
+
+ if v.Metadata != "" {
+ buffer.WriteString(fmt.Sprintf("+%s", v.Metadata))
+ }
+
+ return buffer.String()
+}
+
+func (v *Version) LessThan(versionB Version) bool {
+ versionA := *v
+ cmp := recursiveCompare(versionA.Slice(), versionB.Slice())
+
+ if cmp == 0 {
+ cmp = preReleaseCompare(versionA, versionB)
+ }
+
+ if cmp == -1 {
+ return true
+ }
+
+ return false
+}
+
+/* Slice converts the comparable parts of the semver into a slice of strings */
+func (v *Version) Slice() []int64 {
+ return []int64{v.Major, v.Minor, v.Patch}
+}
+
+func (p *PreRelease) Slice() []string {
+ preRelease := string(*p)
+ return strings.Split(preRelease, ".")
+}
+
+func preReleaseCompare(versionA Version, versionB Version) int {
+ a := versionA.PreRelease
+ b := versionB.PreRelease
+
+ /* Handle the case where if two versions are otherwise equal it is the
+ * one without a PreRelease that is greater */
+ if len(a) == 0 && (len(b) > 0) {
+ return 1
+ } else if len(b) == 0 && (len(a) > 0) {
+ return -1
+ }
+
+ // If there is a prelease, check and compare each part.
+ return recursivePreReleaseCompare(a.Slice(), b.Slice())
+}
+
+func recursiveCompare(versionA []int64, versionB []int64) int {
+ if len(versionA) == 0 {
+ return 0
+ }
+
+ a := versionA[0]
+ b := versionB[0]
+
+ if a > b {
+ return 1
+ } else if a < b {
+ return -1
+ }
+
+ return recursiveCompare(versionA[1:], versionB[1:])
+}
+
+func recursivePreReleaseCompare(versionA []string, versionB []string) int {
+ // Handle slice length disparity.
+ if len(versionA) == 0 {
+ // Nothing to compare too, so we return 0
+ return 0
+ } else if len(versionB) == 0 {
+ // We're longer than versionB so return 1.
+ return 1
+ }
+
+ a := versionA[0]
+ b := versionB[0]
+
+ aInt := false; bInt := false
+
+ aI, err := strconv.Atoi(versionA[0])
+ if err == nil {
+ aInt = true
+ }
+
+ bI, err := strconv.Atoi(versionB[0])
+ if err == nil {
+ bInt = true
+ }
+
+ // Handle Integer Comparison
+ if aInt && bInt {
+ if aI > bI {
+ return 1
+ } else if aI < bI {
+ return -1
+ }
+ }
+
+ // Handle String Comparison
+ if a > b {
+ return 1
+ } else if a < b {
+ return -1
+ }
+
+ return recursivePreReleaseCompare(versionA[1:], versionB[1:])
+}
+
+// BumpMajor increments the Major field by 1 and resets all other fields to their default values
+func (v *Version) BumpMajor() {
+ v.Major += 1
+ v.Minor = 0
+ v.Patch = 0
+ v.PreRelease = PreRelease("")
+ v.Metadata = ""
+}
+
+// BumpMinor increments the Minor field by 1 and resets all other fields to their default values
+func (v *Version) BumpMinor() {
+ v.Minor += 1
+ v.Patch = 0
+ v.PreRelease = PreRelease("")
+ v.Metadata = ""
+}
+
+// BumpPatch increments the Patch field by 1 and resets all other fields to their default values
+func (v *Version) BumpPatch() {
+ v.Patch += 1
+ v.PreRelease = PreRelease("")
+ v.Metadata = ""
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go
new file mode 100644
index 000000000..9bfc3b8a9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/semver_test.go
@@ -0,0 +1,223 @@
+package semver
+
+import (
+ "errors"
+ "math/rand"
+ "reflect"
+ "testing"
+ "time"
+)
+
+type fixture struct {
+ greaterVersion string
+ lesserVersion string
+}
+
+var fixtures = []fixture{
+ fixture{"0.0.0", "0.0.0-foo"},
+ fixture{"0.0.1", "0.0.0"},
+ fixture{"1.0.0", "0.9.9"},
+ fixture{"0.10.0", "0.9.0"},
+ fixture{"0.99.0", "0.10.0"},
+ fixture{"2.0.0", "1.2.3"},
+ fixture{"0.0.0", "0.0.0-foo"},
+ fixture{"0.0.1", "0.0.0"},
+ fixture{"1.0.0", "0.9.9"},
+ fixture{"0.10.0", "0.9.0"},
+ fixture{"0.99.0", "0.10.0"},
+ fixture{"2.0.0", "1.2.3"},
+ fixture{"0.0.0", "0.0.0-foo"},
+ fixture{"0.0.1", "0.0.0"},
+ fixture{"1.0.0", "0.9.9"},
+ fixture{"0.10.0", "0.9.0"},
+ fixture{"0.99.0", "0.10.0"},
+ fixture{"2.0.0", "1.2.3"},
+ fixture{"1.2.3", "1.2.3-asdf"},
+ fixture{"1.2.3", "1.2.3-4"},
+ fixture{"1.2.3", "1.2.3-4-foo"},
+ fixture{"1.2.3-5-foo", "1.2.3-5"},
+ fixture{"1.2.3-5", "1.2.3-4"},
+ fixture{"1.2.3-5-foo", "1.2.3-5-Foo"},
+ fixture{"3.0.0", "2.7.2+asdf"},
+ fixture{"3.0.0+foobar", "2.7.2"},
+ fixture{"1.2.3-a.10", "1.2.3-a.5"},
+ fixture{"1.2.3-a.b", "1.2.3-a.5"},
+ fixture{"1.2.3-a.b", "1.2.3-a"},
+ fixture{"1.2.3-a.b.c.10.d.5", "1.2.3-a.b.c.5.d.100"},
+ fixture{"1.0.0", "1.0.0-rc.1"},
+ fixture{"1.0.0-rc.2", "1.0.0-rc.1"},
+ fixture{"1.0.0-rc.1", "1.0.0-beta.11"},
+ fixture{"1.0.0-beta.11", "1.0.0-beta.2"},
+ fixture{"1.0.0-beta.2", "1.0.0-beta"},
+ fixture{"1.0.0-beta", "1.0.0-alpha.beta"},
+ fixture{"1.0.0-alpha.beta", "1.0.0-alpha.1"},
+ fixture{"1.0.0-alpha.1", "1.0.0-alpha"},
+}
+
+func TestCompare(t *testing.T) {
+ for _, v := range fixtures {
+ gt, err := NewVersion(v.greaterVersion)
+ if err != nil {
+ t.Error(err)
+ }
+
+ lt, err := NewVersion(v.lesserVersion)
+ if err != nil {
+ t.Error(err)
+ }
+
+ if gt.LessThan(*lt) == true {
+ t.Errorf("%s should not be less than %s", gt, lt)
+ }
+ }
+}
+
+func testString(t *testing.T, orig string, version *Version) {
+ if orig != version.String() {
+ t.Errorf("%s != %s", orig, version)
+ }
+}
+
+func TestString(t *testing.T) {
+ for _, v := range fixtures {
+ gt, err := NewVersion(v.greaterVersion)
+ if err != nil {
+ t.Error(err)
+ }
+ testString(t, v.greaterVersion, gt)
+
+ lt, err := NewVersion(v.lesserVersion)
+ if err != nil {
+ t.Error(err)
+ }
+ testString(t, v.lesserVersion, lt)
+ }
+}
+
+func shuffleStringSlice(src []string) []string {
+ dest := make([]string, len(src))
+ rand.Seed(time.Now().Unix())
+ perm := rand.Perm(len(src))
+ for i, v := range perm {
+ dest[v] = src[i]
+ }
+ return dest
+}
+
+func TestSort(t *testing.T) {
+ sortedVersions := []string{"1.0.0", "1.0.2", "1.2.0", "3.1.1"}
+ unsortedVersions := shuffleStringSlice(sortedVersions)
+
+ semvers := []*Version{}
+ for _, v := range unsortedVersions {
+ sv, err := NewVersion(v)
+ if err != nil {
+ t.Fatal(err)
+ }
+ semvers = append(semvers, sv)
+ }
+
+ Sort(semvers)
+
+ for idx, sv := range semvers {
+ if sv.String() != sortedVersions[idx] {
+ t.Fatalf("incorrect sort at index %v", idx)
+ }
+ }
+}
+
+func TestBumpMajor(t *testing.T) {
+ version, _ := NewVersion("1.0.0")
+ version.BumpMajor()
+ if version.Major != 2 {
+ t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
+ }
+
+ version, _ = NewVersion("1.5.2")
+ version.BumpMajor()
+ if version.Minor != 0 && version.Patch != 0 {
+ t.Fatalf("bumping major on 1.5.2 resulted in %v", version)
+ }
+
+ version, _ = NewVersion("1.0.0+build.1-alpha.1")
+ version.BumpMajor()
+ if version.PreRelease != "" && version.PreRelease != "" {
+ t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
+ }
+}
+
+func TestBumpMinor(t *testing.T) {
+ version, _ := NewVersion("1.0.0")
+ version.BumpMinor()
+
+ if version.Major != 1 {
+ t.Fatalf("bumping minor on 1.0.0 resulted in %v", version)
+ }
+
+ if version.Minor != 1 {
+ t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
+ }
+
+ version, _ = NewVersion("1.0.0+build.1-alpha.1")
+ version.BumpMinor()
+ if version.PreRelease != "" && version.PreRelease != "" {
+ t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
+ }
+}
+
+func TestBumpPatch(t *testing.T) {
+ version, _ := NewVersion("1.0.0")
+ version.BumpPatch()
+
+ if version.Major != 1 {
+ t.Fatalf("bumping minor on 1.0.0 resulted in %v", version)
+ }
+
+ if version.Minor != 0 {
+ t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
+ }
+
+ if version.Patch != 1 {
+ t.Fatalf("bumping major on 1.0.0 resulted in %v", version)
+ }
+
+ version, _ = NewVersion("1.0.0+build.1-alpha.1")
+ version.BumpPatch()
+ if version.PreRelease != "" && version.PreRelease != "" {
+ t.Fatalf("bumping major on 1.0.0+build.1-alpha.1 resulted in %v", version)
+ }
+}
+
+func TestMust(t *testing.T) {
+ tests := []struct {
+ versionStr string
+
+ version *Version
+ recov interface{}
+ }{
+ {
+ versionStr: "1.0.0",
+ version: &Version{Major: 1},
+ },
+ {
+ versionStr: "version number",
+ recov: errors.New("version number is not in dotted-tri format"),
+ },
+ }
+
+ for _, tt := range tests {
+ func() {
+ defer func() {
+ recov := recover()
+ if !reflect.DeepEqual(tt.recov, recov) {
+ t.Fatalf("incorrect panic for %q: want %v, got %v", tt.versionStr, tt.recov, recov)
+ }
+ }()
+
+ version := Must(NewVersion(tt.versionStr))
+ if !reflect.DeepEqual(tt.version, version) {
+ t.Fatalf("incorrect version for %q: want %+v, got %+v", tt.versionStr, tt.version, version)
+ }
+ }()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/sort.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/sort.go
new file mode 100644
index 000000000..86203007a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver/sort.go
@@ -0,0 +1,24 @@
+package semver
+
+import (
+ "sort"
+)
+
+type Versions []*Version
+
+func (s Versions) Len() int {
+ return len(s)
+}
+
+func (s Versions) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s Versions) Less(i, j int) bool {
+ return s[i].LessThan(*s[j])
+}
+
+// Sort sorts the given slice of Version
+func Sort(versions []*Version) {
+ sort.Sort(Versions(versions))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-systemd/journal/send.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-systemd/journal/send.go
new file mode 100644
index 000000000..1620ae94a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-systemd/journal/send.go
@@ -0,0 +1,166 @@
+// Copyright 2015 CoreOS, 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 journal provides write bindings to the systemd journal
+package journal
+
+import (
+ "bytes"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "syscall"
+)
+
+// Priority of a journal message
+type Priority int
+
+const (
+ PriEmerg Priority = iota
+ PriAlert
+ PriCrit
+ PriErr
+ PriWarning
+ PriNotice
+ PriInfo
+ PriDebug
+)
+
+var conn net.Conn
+
+func init() {
+ var err error
+ conn, err = net.Dial("unixgram", "/run/systemd/journal/socket")
+ if err != nil {
+ conn = nil
+ }
+}
+
+// Enabled returns true iff the systemd journal is available for logging
+func Enabled() bool {
+ return conn != nil
+}
+
+// Send a message to the systemd journal. vars is a map of journald fields to
+// values. Fields must be composed of uppercase letters, numbers, and
+// underscores, but must not start with an underscore. Within these
+// restrictions, any arbitrary field name may be used. Some names have special
+// significance: see the journalctl documentation
+// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
+// for more details. vars may be nil.
+func Send(message string, priority Priority, vars map[string]string) error {
+ if conn == nil {
+ return journalError("could not connect to journald socket")
+ }
+
+ data := new(bytes.Buffer)
+ appendVariable(data, "PRIORITY", strconv.Itoa(int(priority)))
+ appendVariable(data, "MESSAGE", message)
+ for k, v := range vars {
+ appendVariable(data, k, v)
+ }
+
+ _, err := io.Copy(conn, data)
+ if err != nil && isSocketSpaceError(err) {
+ file, err := tempFd()
+ if err != nil {
+ return journalError(err.Error())
+ }
+ _, err = io.Copy(file, data)
+ if err != nil {
+ return journalError(err.Error())
+ }
+
+ rights := syscall.UnixRights(int(file.Fd()))
+
+ /* this connection should always be a UnixConn, but better safe than sorry */
+ unixConn, ok := conn.(*net.UnixConn)
+ if !ok {
+ return journalError("can't send file through non-Unix connection")
+ }
+ unixConn.WriteMsgUnix([]byte{}, rights, nil)
+ } else if err != nil {
+ return journalError(err.Error())
+ }
+ return nil
+}
+
+func appendVariable(w io.Writer, name, value string) {
+ if !validVarName(name) {
+ journalError("variable name contains invalid character, ignoring")
+ }
+ if strings.ContainsRune(value, '\n') {
+ /* When the value contains a newline, we write:
+ * - the variable name, followed by a newline
+ * - the size (in 64bit little endian format)
+ * - the data, followed by a newline
+ */
+ fmt.Fprintln(w, name)
+ binary.Write(w, binary.LittleEndian, uint64(len(value)))
+ fmt.Fprintln(w, value)
+ } else {
+ /* just write the variable and value all on one line */
+ fmt.Fprintf(w, "%s=%s\n", name, value)
+ }
+}
+
+func validVarName(name string) bool {
+ /* The variable name must be in uppercase and consist only of characters,
+ * numbers and underscores, and may not begin with an underscore. (from the docs)
+ */
+
+ valid := name[0] != '_'
+ for _, c := range name {
+ valid = valid && ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_'
+ }
+ return valid
+}
+
+func isSocketSpaceError(err error) bool {
+ opErr, ok := err.(*net.OpError)
+ if !ok {
+ return false
+ }
+
+ sysErr, ok := opErr.Err.(syscall.Errno)
+ if !ok {
+ return false
+ }
+
+ return sysErr == syscall.EMSGSIZE || sysErr == syscall.ENOBUFS
+}
+
+func tempFd() (*os.File, error) {
+ file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX")
+ if err != nil {
+ return nil, err
+ }
+ syscall.Unlink(file.Name())
+ if err != nil {
+ return nil, err
+ }
+ return file, nil
+}
+
+func journalError(s string) error {
+ s = "journal error: " + s
+ fmt.Fprintln(os.Stderr, s)
+ return errors.New(s)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/README.md
new file mode 100644
index 000000000..1053dd001
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/README.md
@@ -0,0 +1,38 @@
+# CoreOS Log
+
+There are far too many logging packages out there, with varying degrees of licenses, far too many features (colorization, all sorts of log frameworks) or are just a pain to use (lack of `Fatalln()`?)
+
+## Design Principles
+
+* `package main` is the place where logging gets turned on and routed
+
+A library should not touch log options, only generate log entries. Libraries are silent until main lets them speak.
+
+* All log options are runtime-configurable.
+
+Still the job of `main` to expose these configurations. `main` may delegate this to, say, a configuration webhook, but does so explicitly.
+
+* There is one log object per package. It is registered under its repository and package name.
+
+`main` activates logging for its repository and any dependency repositories it would also like to have output in its logstream. `main` also dictates at which level each subpackage logs.
+
+* There is *one* output stream, and it is an `io.Writer` composed with a formatter.
+
+Splitting streams is probably not the job of your program, but rather, your log aggregation framework. If you must split output streams, again, `main` configures this and you can write a very simple two-output struct that satisfies io.Writer.
+
+Fancy colorful formatting and JSON output are beyond the scope of a basic logging framework -- they're application/log-collector dependant. These are, at best, provided as options, but more likely, provided by your application.
+
+* Log objects are an interface
+
+An object knows best how to print itself. Log objects can collect more interesting metadata if they wish, however, because text isn't going away anytime soon, they must all be marshalable to text. The simplest log object is a string, which returns itself. If you wish to do more fancy tricks for printing your log objects, see also JSON output -- introspect and write a formatter which can handle your advanced log interface. Making strings is the only thing guaranteed.
+
+* Log levels have specific meanings:
+
+ * Critical: Unrecoverable. Must fail.
+ * Error: Data has been lost, a request has failed for a bad reason, or a required resource has been lost
+ * Warning: (Hopefully) Temporary conditions that may cause errors, but may work fine. A replica disappearing (that may reconnect) is a warning.
+ * Notice: Normal, but important (uncommon) log information.
+ * Info: Normal, working log information, everything is fine, but helpful notices for auditing or common operations.
+ * Debug: Everything is still fine, but even common operations may be logged, and less helpful but more quantity of notices.
+ * Trace: Anything goes, from logging every function call as part of a common operation, to tracing execution of a query.
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/example/hello_dolly.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/example/hello_dolly.go
new file mode 100644
index 000000000..450288d5b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/example/hello_dolly.go
@@ -0,0 +1,57 @@
+// Copyright 2015 CoreOS, 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 main
+
+import (
+ "flag"
+ oldlog "log"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+)
+
+var logLevel = capnslog.INFO
+var log = capnslog.NewPackageLogger("github.com/coreos/pkg/capnslog/cmd", "main")
+var dlog = capnslog.NewPackageLogger("github.com/coreos/pkg/capnslog/cmd", "dolly")
+
+func init() {
+ flag.Var(&logLevel, "log-level", "Global log level.")
+}
+
+func main() {
+ rl := capnslog.MustRepoLogger("github.com/coreos/pkg/capnslog/cmd")
+
+ // We can parse the log level configs from the command line
+ flag.Parse()
+ if flag.NArg() > 1 {
+ cfg, err := rl.ParseLogLevelConfig(flag.Arg(1))
+ if err != nil {
+ log.Fatal(err)
+ }
+ rl.SetLogLevel(cfg)
+ log.Infof("Setting output to %s", flag.Arg(1))
+ }
+
+ // Send some messages at different levels to the different packages
+ dlog.Infof("Hello Dolly")
+ dlog.Warningf("Well hello, Dolly")
+ log.Errorf("It's so nice to have you back where you belong")
+ dlog.Debugf("You're looking swell, Dolly")
+ dlog.Tracef("I can tell, Dolly")
+
+ // We also have control over the built-in "log" package.
+ capnslog.SetGlobalLogLevel(logLevel)
+ oldlog.Println("You're still glowin', you're still crowin', you're still lookin' strong")
+ log.Fatalf("Dolly'll never go away again")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/formatters.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/formatters.go
new file mode 100644
index 000000000..99ec6f824
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/formatters.go
@@ -0,0 +1,106 @@
+// Copyright 2015 CoreOS, 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 capnslog
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "runtime"
+ "strings"
+ "time"
+)
+
+type Formatter interface {
+ Format(pkg string, level LogLevel, depth int, entries ...interface{})
+ Flush()
+}
+
+func NewStringFormatter(w io.Writer) *StringFormatter {
+ return &StringFormatter{
+ w: bufio.NewWriter(w),
+ }
+}
+
+type StringFormatter struct {
+ w *bufio.Writer
+}
+
+func (s *StringFormatter) Format(pkg string, l LogLevel, i int, entries ...interface{}) {
+ now := time.Now().UTC()
+ s.w.WriteString(now.Format(time.RFC3339))
+ s.w.WriteByte(' ')
+ writeEntries(s.w, pkg, l, i, entries...)
+ s.Flush()
+}
+
+func writeEntries(w *bufio.Writer, pkg string, _ LogLevel, _ int, entries ...interface{}) {
+ if pkg != "" {
+ w.WriteString(pkg + ": ")
+ }
+ str := fmt.Sprint(entries...)
+ endsInNL := strings.HasSuffix(str, "\n")
+ w.WriteString(str)
+ if !endsInNL {
+ w.WriteString("\n")
+ }
+}
+
+func (s *StringFormatter) Flush() {
+ s.w.Flush()
+}
+
+func NewPrettyFormatter(w io.Writer, debug bool) Formatter {
+ return &PrettyFormatter{
+ w: bufio.NewWriter(w),
+ debug: debug,
+ }
+}
+
+type PrettyFormatter struct {
+ w *bufio.Writer
+ debug bool
+}
+
+func (c *PrettyFormatter) Format(pkg string, l LogLevel, depth int, entries ...interface{}) {
+ now := time.Now()
+ ts := now.Format("2006-01-02 15:04:05")
+ c.w.WriteString(ts)
+ ms := now.Nanosecond() / 1000
+ c.w.WriteString(fmt.Sprintf(".%06d", ms))
+ if c.debug {
+ _, file, line, ok := runtime.Caller(depth) // It's always the same number of frames to the user's call.
+ if !ok {
+ file = "???"
+ line = 1
+ } else {
+ slash := strings.LastIndex(file, "/")
+ if slash >= 0 {
+ file = file[slash+1:]
+ }
+ }
+ if line < 0 {
+ line = 0 // not a real line number
+ }
+ c.w.WriteString(fmt.Sprintf(" [%s:%d]", file, line))
+ }
+ c.w.WriteString(fmt.Sprint(" ", l.Char(), " | "))
+ writeEntries(c.w, pkg, l, depth, entries...)
+ c.Flush()
+}
+
+func (c *PrettyFormatter) Flush() {
+ c.w.Flush()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/glog_formatter.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/glog_formatter.go
new file mode 100644
index 000000000..426603ef3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/glog_formatter.go
@@ -0,0 +1,96 @@
+// Copyright 2015 CoreOS, 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 capnslog
+
+import (
+ "bufio"
+ "bytes"
+ "io"
+ "os"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var pid = os.Getpid()
+
+type GlogFormatter struct {
+ StringFormatter
+}
+
+func NewGlogFormatter(w io.Writer) *GlogFormatter {
+ g := &GlogFormatter{}
+ g.w = bufio.NewWriter(w)
+ return g
+}
+
+func (g GlogFormatter) Format(pkg string, level LogLevel, depth int, entries ...interface{}) {
+ g.w.Write(GlogHeader(level, depth+1))
+ g.StringFormatter.Format(pkg, level, depth+1, entries...)
+}
+
+func GlogHeader(level LogLevel, depth int) []byte {
+ // Lmmdd hh:mm:ss.uuuuuu threadid file:line]
+ now := time.Now().UTC()
+ _, file, line, ok := runtime.Caller(depth) // It's always the same number of frames to the user's call.
+ if !ok {
+ file = "???"
+ line = 1
+ } else {
+ slash := strings.LastIndex(file, "/")
+ if slash >= 0 {
+ file = file[slash+1:]
+ }
+ }
+ if line < 0 {
+ line = 0 // not a real line number
+ }
+ buf := &bytes.Buffer{}
+ buf.Grow(30)
+ _, month, day := now.Date()
+ hour, minute, second := now.Clock()
+ buf.WriteString(level.Char())
+ twoDigits(buf, int(month))
+ twoDigits(buf, day)
+ buf.WriteByte(' ')
+ twoDigits(buf, hour)
+ buf.WriteByte(':')
+ twoDigits(buf, minute)
+ buf.WriteByte(':')
+ twoDigits(buf, second)
+ buf.WriteByte('.')
+ buf.WriteString(strconv.Itoa(now.Nanosecond() / 1000))
+ buf.WriteByte('Z')
+ buf.WriteByte(' ')
+ buf.WriteString(strconv.Itoa(pid))
+ buf.WriteByte(' ')
+ buf.WriteString(file)
+ buf.WriteByte(':')
+ buf.WriteString(strconv.Itoa(line))
+ buf.WriteByte(']')
+ buf.WriteByte(' ')
+ return buf.Bytes()
+}
+
+const digits = "0123456789"
+
+func twoDigits(b *bytes.Buffer, d int) {
+ c2 := digits[d%10]
+ d /= 10
+ c1 := digits[d%10]
+ b.WriteByte(c1)
+ b.WriteByte(c2)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/init.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/init.go
new file mode 100644
index 000000000..44b8cd361
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/init.go
@@ -0,0 +1,49 @@
+// Copyright 2015 CoreOS, 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.
+//
+// +build !windows
+
+package capnslog
+
+import (
+ "io"
+ "os"
+ "syscall"
+)
+
+// Here's where the opinionation comes in. We need some sensible defaults,
+// especially after taking over the log package. Your project (whatever it may
+// be) may see things differently. That's okay; there should be no defaults in
+// the main package that cannot be controlled or overridden programatically,
+// otherwise it's a bug. Doing so is creating your own init_log.go file much
+// like this one.
+
+func init() {
+ initHijack()
+
+ // Go `log` pacakge uses os.Stderr.
+ SetFormatter(NewDefaultFormatter(os.Stderr))
+ SetGlobalLogLevel(INFO)
+}
+
+func NewDefaultFormatter(out io.Writer) Formatter {
+ if syscall.Getppid() == 1 {
+ // We're running under init, which may be systemd.
+ f, err := NewJournaldFormatter()
+ if err == nil {
+ return f
+ }
+ }
+ return NewPrettyFormatter(out, false)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/init_windows.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/init_windows.go
new file mode 100644
index 000000000..a98bd6679
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/init_windows.go
@@ -0,0 +1,25 @@
+// Copyright 2015 CoreOS, 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 capnslog
+
+import "os"
+
+func init() {
+ initHijack()
+
+ // Go `log` pacakge uses os.Stderr.
+ SetFormatter(NewPrettyFormatter(os.Stderr, false))
+ SetGlobalLogLevel(INFO)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/journald_formatter.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/journald_formatter.go
new file mode 100644
index 000000000..401121279
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/journald_formatter.go
@@ -0,0 +1,66 @@
+// Copyright 2015 CoreOS, 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.
+//
+// +build !windows
+
+package capnslog
+
+import (
+ "errors"
+ "fmt"
+ "os"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-systemd/journal"
+)
+
+func NewJournaldFormatter() (Formatter, error) {
+ if !journal.Enabled() {
+ return nil, errors.New("No systemd detected")
+ }
+ return &journaldFormatter{}, nil
+}
+
+type journaldFormatter struct{}
+
+func (j *journaldFormatter) Format(pkg string, l LogLevel, _ int, entries ...interface{}) {
+ var pri journal.Priority
+ switch l {
+ case CRITICAL:
+ pri = journal.PriCrit
+ case ERROR:
+ pri = journal.PriErr
+ case WARNING:
+ pri = journal.PriWarning
+ case NOTICE:
+ pri = journal.PriNotice
+ case INFO:
+ pri = journal.PriInfo
+ case DEBUG:
+ pri = journal.PriDebug
+ case TRACE:
+ pri = journal.PriDebug
+ default:
+ panic("Unhandled loglevel")
+ }
+ msg := fmt.Sprint(entries...)
+ tags := map[string]string{
+ "PACKAGE": pkg,
+ }
+ err := journal.Send(msg, pri, tags)
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ }
+}
+
+func (j *journaldFormatter) Flush() {}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/log_hijack.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/log_hijack.go
new file mode 100644
index 000000000..970086b9f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/log_hijack.go
@@ -0,0 +1,39 @@
+// Copyright 2015 CoreOS, 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 capnslog
+
+import (
+ "log"
+)
+
+func initHijack() {
+ pkg := NewPackageLogger("log", "")
+ w := packageWriter{pkg}
+ log.SetFlags(0)
+ log.SetPrefix("")
+ log.SetOutput(w)
+}
+
+type packageWriter struct {
+ pl *PackageLogger
+}
+
+func (p packageWriter) Write(b []byte) (int, error) {
+ if p.pl.level < INFO {
+ return 0, nil
+ }
+ p.pl.internalLog(calldepth+2, INFO, string(b))
+ return len(b), nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/logmap.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/logmap.go
new file mode 100644
index 000000000..849544883
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/logmap.go
@@ -0,0 +1,240 @@
+// Copyright 2015 CoreOS, 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 capnslog
+
+import (
+ "errors"
+ "strings"
+ "sync"
+)
+
+// LogLevel is the set of all log levels.
+type LogLevel int8
+
+const (
+ // CRITICAL is the lowest log level; only errors which will end the program will be propagated.
+ CRITICAL LogLevel = iota - 1
+ // ERROR is for errors that are not fatal but lead to troubling behavior.
+ ERROR
+ // WARNING is for errors which are not fatal and not errors, but are unusual. Often sourced from misconfigurations.
+ WARNING
+ // NOTICE is for normal but significant conditions.
+ NOTICE
+ // INFO is a log level for common, everyday log updates.
+ INFO
+ // DEBUG is the default hidden level for more verbose updates about internal processes.
+ DEBUG
+ // TRACE is for (potentially) call by call tracing of programs.
+ TRACE
+)
+
+// Char returns a single-character representation of the log level.
+func (l LogLevel) Char() string {
+ switch l {
+ case CRITICAL:
+ return "C"
+ case ERROR:
+ return "E"
+ case WARNING:
+ return "W"
+ case NOTICE:
+ return "N"
+ case INFO:
+ return "I"
+ case DEBUG:
+ return "D"
+ case TRACE:
+ return "T"
+ default:
+ panic("Unhandled loglevel")
+ }
+}
+
+// String returns a multi-character representation of the log level.
+func (l LogLevel) String() string {
+ switch l {
+ case CRITICAL:
+ return "CRITICAL"
+ case ERROR:
+ return "ERROR"
+ case WARNING:
+ return "WARNING"
+ case NOTICE:
+ return "NOTICE"
+ case INFO:
+ return "INFO"
+ case DEBUG:
+ return "DEBUG"
+ case TRACE:
+ return "TRACE"
+ default:
+ panic("Unhandled loglevel")
+ }
+}
+
+// Update using the given string value. Fulfills the flag.Value interface.
+func (l *LogLevel) Set(s string) error {
+ value, err := ParseLevel(s)
+ if err != nil {
+ return err
+ }
+
+ *l = value
+ return nil
+}
+
+// ParseLevel translates some potential loglevel strings into their corresponding levels.
+func ParseLevel(s string) (LogLevel, error) {
+ switch s {
+ case "CRITICAL", "C":
+ return CRITICAL, nil
+ case "ERROR", "0", "E":
+ return ERROR, nil
+ case "WARNING", "1", "W":
+ return WARNING, nil
+ case "NOTICE", "2", "N":
+ return NOTICE, nil
+ case "INFO", "3", "I":
+ return INFO, nil
+ case "DEBUG", "4", "D":
+ return DEBUG, nil
+ case "TRACE", "5", "T":
+ return TRACE, nil
+ }
+ return CRITICAL, errors.New("couldn't parse log level " + s)
+}
+
+type RepoLogger map[string]*PackageLogger
+
+type loggerStruct struct {
+ sync.Mutex
+ repoMap map[string]RepoLogger
+ formatter Formatter
+}
+
+// logger is the global logger
+var logger = new(loggerStruct)
+
+// SetGlobalLogLevel sets the log level for all packages in all repositories
+// registered with capnslog.
+func SetGlobalLogLevel(l LogLevel) {
+ logger.Lock()
+ defer logger.Unlock()
+ for _, r := range logger.repoMap {
+ r.setRepoLogLevelInternal(l)
+ }
+}
+
+// GetRepoLogger may return the handle to the repository's set of packages' loggers.
+func GetRepoLogger(repo string) (RepoLogger, error) {
+ logger.Lock()
+ defer logger.Unlock()
+ r, ok := logger.repoMap[repo]
+ if !ok {
+ return nil, errors.New("no packages registered for repo " + repo)
+ }
+ return r, nil
+}
+
+// MustRepoLogger returns the handle to the repository's packages' loggers.
+func MustRepoLogger(repo string) RepoLogger {
+ r, err := GetRepoLogger(repo)
+ if err != nil {
+ panic(err)
+ }
+ return r
+}
+
+// SetRepoLogLevel sets the log level for all packages in the repository.
+func (r RepoLogger) SetRepoLogLevel(l LogLevel) {
+ logger.Lock()
+ defer logger.Unlock()
+ r.setRepoLogLevelInternal(l)
+}
+
+func (r RepoLogger) setRepoLogLevelInternal(l LogLevel) {
+ for _, v := range r {
+ v.level = l
+ }
+}
+
+// ParseLogLevelConfig parses a comma-separated string of "package=loglevel", in
+// order, and returns a map of the results, for use in SetLogLevel.
+func (r RepoLogger) ParseLogLevelConfig(conf string) (map[string]LogLevel, error) {
+ setlist := strings.Split(conf, ",")
+ out := make(map[string]LogLevel)
+ for _, setstring := range setlist {
+ setting := strings.Split(setstring, "=")
+ if len(setting) != 2 {
+ return nil, errors.New("oddly structured `pkg=level` option: " + setstring)
+ }
+ l, err := ParseLevel(setting[1])
+ if err != nil {
+ return nil, err
+ }
+ out[setting[0]] = l
+ }
+ return out, nil
+}
+
+// SetLogLevel takes a map of package names within a repository to their desired
+// loglevel, and sets the levels appropriately. Unknown packages are ignored.
+// "*" is a special package name that corresponds to all packages, and will be
+// processed first.
+func (r RepoLogger) SetLogLevel(m map[string]LogLevel) {
+ logger.Lock()
+ defer logger.Unlock()
+ if l, ok := m["*"]; ok {
+ r.setRepoLogLevelInternal(l)
+ }
+ for k, v := range m {
+ l, ok := r[k]
+ if !ok {
+ continue
+ }
+ l.level = v
+ }
+}
+
+// SetFormatter sets the formatting function for all logs.
+func SetFormatter(f Formatter) {
+ logger.Lock()
+ defer logger.Unlock()
+ logger.formatter = f
+}
+
+// NewPackageLogger creates a package logger object.
+// This should be defined as a global var in your package, referencing your repo.
+func NewPackageLogger(repo string, pkg string) (p *PackageLogger) {
+ logger.Lock()
+ defer logger.Unlock()
+ if logger.repoMap == nil {
+ logger.repoMap = make(map[string]RepoLogger)
+ }
+ r, rok := logger.repoMap[repo]
+ if !rok {
+ logger.repoMap[repo] = make(RepoLogger)
+ r = logger.repoMap[repo]
+ }
+ p, pok := r[pkg]
+ if !pok {
+ r[pkg] = &PackageLogger{
+ pkg: pkg,
+ level: INFO,
+ }
+ p = r[pkg]
+ }
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/pkg_logger.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/pkg_logger.go
new file mode 100644
index 000000000..32d2f16a9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/pkg_logger.go
@@ -0,0 +1,158 @@
+// Copyright 2015 CoreOS, 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 capnslog
+
+import (
+ "fmt"
+ "os"
+)
+
+type PackageLogger struct {
+ pkg string
+ level LogLevel
+}
+
+const calldepth = 2
+
+func (p *PackageLogger) internalLog(depth int, inLevel LogLevel, entries ...interface{}) {
+ if inLevel != CRITICAL && p.level < inLevel {
+ return
+ }
+ logger.Lock()
+ defer logger.Unlock()
+ if logger.formatter != nil {
+ logger.formatter.Format(p.pkg, inLevel, depth+1, entries...)
+ }
+}
+
+func (p *PackageLogger) LevelAt(l LogLevel) bool {
+ return p.level >= l
+}
+
+// Log a formatted string at any level between ERROR and TRACE
+func (p *PackageLogger) Logf(l LogLevel, format string, args ...interface{}) {
+ p.internalLog(calldepth, l, fmt.Sprintf(format, args...))
+}
+
+// Log a message at any level between ERROR and TRACE
+func (p *PackageLogger) Log(l LogLevel, args ...interface{}) {
+ p.internalLog(calldepth, l, fmt.Sprint(args...))
+}
+
+// log stdlib compatibility
+
+func (p *PackageLogger) Println(args ...interface{}) {
+ p.internalLog(calldepth, INFO, fmt.Sprintln(args...))
+}
+
+func (p *PackageLogger) Printf(format string, args ...interface{}) {
+ p.internalLog(calldepth, INFO, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Print(args ...interface{}) {
+ p.internalLog(calldepth, INFO, fmt.Sprint(args...))
+}
+
+// Panic and fatal
+
+func (p *PackageLogger) Panicf(format string, args ...interface{}) {
+ s := fmt.Sprintf(format, args...)
+ p.internalLog(calldepth, CRITICAL, s)
+ panic(s)
+}
+
+func (p *PackageLogger) Panic(args ...interface{}) {
+ s := fmt.Sprint(args...)
+ p.internalLog(calldepth, CRITICAL, s)
+ panic(s)
+}
+
+func (p *PackageLogger) Fatalf(format string, args ...interface{}) {
+ s := fmt.Sprintf(format, args...)
+ p.internalLog(calldepth, CRITICAL, s)
+ os.Exit(1)
+}
+
+func (p *PackageLogger) Fatal(args ...interface{}) {
+ s := fmt.Sprint(args...)
+ p.internalLog(calldepth, CRITICAL, s)
+ os.Exit(1)
+}
+
+// Error Functions
+
+func (p *PackageLogger) Errorf(format string, args ...interface{}) {
+ p.internalLog(calldepth, ERROR, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Error(entries ...interface{}) {
+ p.internalLog(calldepth, ERROR, entries...)
+}
+
+// Warning Functions
+
+func (p *PackageLogger) Warningf(format string, args ...interface{}) {
+ p.internalLog(calldepth, WARNING, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Warning(entries ...interface{}) {
+ p.internalLog(calldepth, WARNING, entries...)
+}
+
+// Notice Functions
+
+func (p *PackageLogger) Noticef(format string, args ...interface{}) {
+ p.internalLog(calldepth, NOTICE, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Notice(entries ...interface{}) {
+ p.internalLog(calldepth, NOTICE, entries...)
+}
+
+// Info Functions
+
+func (p *PackageLogger) Infof(format string, args ...interface{}) {
+ p.internalLog(calldepth, INFO, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Info(entries ...interface{}) {
+ p.internalLog(calldepth, INFO, entries...)
+}
+
+// Debug Functions
+
+func (p *PackageLogger) Debugf(format string, args ...interface{}) {
+ p.internalLog(calldepth, DEBUG, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Debug(entries ...interface{}) {
+ p.internalLog(calldepth, DEBUG, entries...)
+}
+
+// Trace Functions
+
+func (p *PackageLogger) Tracef(format string, args ...interface{}) {
+ p.internalLog(calldepth, TRACE, fmt.Sprintf(format, args...))
+}
+
+func (p *PackageLogger) Trace(entries ...interface{}) {
+ p.internalLog(calldepth, TRACE, entries...)
+}
+
+func (p *PackageLogger) Flush() {
+ logger.Lock()
+ defer logger.Unlock()
+ logger.formatter.Flush()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/syslog_formatter.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/syslog_formatter.go
new file mode 100644
index 000000000..4be5a1f2d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog/syslog_formatter.go
@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, 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.
+//
+// +build !windows
+
+package capnslog
+
+import (
+ "fmt"
+ "log/syslog"
+)
+
+func NewSyslogFormatter(w *syslog.Writer) Formatter {
+ return &syslogFormatter{w}
+}
+
+func NewDefaultSyslogFormatter(tag string) (Formatter, error) {
+ w, err := syslog.New(syslog.LOG_DEBUG, tag)
+ if err != nil {
+ return nil, err
+ }
+ return NewSyslogFormatter(w), nil
+}
+
+type syslogFormatter struct {
+ w *syslog.Writer
+}
+
+func (s *syslogFormatter) Format(pkg string, l LogLevel, _ int, entries ...interface{}) {
+ for _, entry := range entries {
+ str := fmt.Sprint(entry)
+ switch l {
+ case CRITICAL:
+ s.w.Crit(str)
+ case ERROR:
+ s.w.Err(str)
+ case WARNING:
+ s.w.Warning(str)
+ case NOTICE:
+ s.w.Notice(str)
+ case INFO:
+ s.w.Info(str)
+ case DEBUG:
+ s.w.Debug(str)
+ case TRACE:
+ s.w.Debug(str)
+ default:
+ panic("Unhandled loglevel")
+ }
+ }
+}
+
+func (s *syslogFormatter) Flush() {
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/Makefile
new file mode 100644
index 000000000..ba28d87a3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/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-min-version --version="3.0.0" --proto_path=.:../../../../ proto3_proto/proto3.proto
+ make
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/all_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/all_test.go
new file mode 100644
index 000000000..c805ae4fd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/all_test.go
@@ -0,0 +1,2071 @@
+// 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_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "math/rand"
+ "reflect"
+ "runtime/debug"
+ "strings"
+ "testing"
+ "time"
+
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+var globalO *Buffer
+
+func old() *Buffer {
+ if globalO == nil {
+ globalO = NewBuffer(nil)
+ }
+ globalO.Reset()
+ return globalO
+}
+
+func equalbytes(b1, b2 []byte, t *testing.T) {
+ if len(b1) != len(b2) {
+ t.Errorf("wrong lengths: 2*%d != %d", len(b1), len(b2))
+ return
+ }
+ for i := 0; i < len(b1); i++ {
+ if b1[i] != b2[i] {
+ t.Errorf("bad byte[%d]:%x %x: %s %s", i, b1[i], b2[i], b1, b2)
+ }
+ }
+}
+
+func initGoTestField() *GoTestField {
+ f := new(GoTestField)
+ f.Label = String("label")
+ f.Type = String("type")
+ return f
+}
+
+// These are all structurally equivalent but the tag numbers differ.
+// (It's remarkable that required, optional, and repeated all have
+// 8 letters.)
+func initGoTest_RequiredGroup() *GoTest_RequiredGroup {
+ return &GoTest_RequiredGroup{
+ RequiredField: String("required"),
+ }
+}
+
+func initGoTest_OptionalGroup() *GoTest_OptionalGroup {
+ return &GoTest_OptionalGroup{
+ RequiredField: String("optional"),
+ }
+}
+
+func initGoTest_RepeatedGroup() *GoTest_RepeatedGroup {
+ return &GoTest_RepeatedGroup{
+ RequiredField: String("repeated"),
+ }
+}
+
+func initGoTest(setdefaults bool) *GoTest {
+ pb := new(GoTest)
+ if setdefaults {
+ pb.F_BoolDefaulted = Bool(Default_GoTest_F_BoolDefaulted)
+ pb.F_Int32Defaulted = Int32(Default_GoTest_F_Int32Defaulted)
+ pb.F_Int64Defaulted = Int64(Default_GoTest_F_Int64Defaulted)
+ pb.F_Fixed32Defaulted = Uint32(Default_GoTest_F_Fixed32Defaulted)
+ pb.F_Fixed64Defaulted = Uint64(Default_GoTest_F_Fixed64Defaulted)
+ pb.F_Uint32Defaulted = Uint32(Default_GoTest_F_Uint32Defaulted)
+ pb.F_Uint64Defaulted = Uint64(Default_GoTest_F_Uint64Defaulted)
+ pb.F_FloatDefaulted = Float32(Default_GoTest_F_FloatDefaulted)
+ pb.F_DoubleDefaulted = Float64(Default_GoTest_F_DoubleDefaulted)
+ pb.F_StringDefaulted = String(Default_GoTest_F_StringDefaulted)
+ pb.F_BytesDefaulted = Default_GoTest_F_BytesDefaulted
+ pb.F_Sint32Defaulted = Int32(Default_GoTest_F_Sint32Defaulted)
+ pb.F_Sint64Defaulted = Int64(Default_GoTest_F_Sint64Defaulted)
+ }
+
+ pb.Kind = GoTest_TIME.Enum()
+ pb.RequiredField = initGoTestField()
+ pb.F_BoolRequired = Bool(true)
+ pb.F_Int32Required = Int32(3)
+ pb.F_Int64Required = Int64(6)
+ pb.F_Fixed32Required = Uint32(32)
+ pb.F_Fixed64Required = Uint64(64)
+ pb.F_Uint32Required = Uint32(3232)
+ pb.F_Uint64Required = Uint64(6464)
+ pb.F_FloatRequired = Float32(3232)
+ pb.F_DoubleRequired = Float64(6464)
+ pb.F_StringRequired = String("string")
+ pb.F_BytesRequired = []byte("bytes")
+ pb.F_Sint32Required = Int32(-32)
+ pb.F_Sint64Required = Int64(-64)
+ pb.Requiredgroup = initGoTest_RequiredGroup()
+
+ return pb
+}
+
+func fail(msg string, b *bytes.Buffer, s string, t *testing.T) {
+ data := b.Bytes()
+ ld := len(data)
+ ls := len(s) / 2
+
+ fmt.Printf("fail %s ld=%d ls=%d\n", msg, ld, ls)
+
+ // find the interesting spot - n
+ n := ls
+ if ld < ls {
+ n = ld
+ }
+ j := 0
+ for i := 0; i < n; i++ {
+ bs := hex(s[j])*16 + hex(s[j+1])
+ j += 2
+ if data[i] == bs {
+ continue
+ }
+ n = i
+ break
+ }
+ l := n - 10
+ if l < 0 {
+ l = 0
+ }
+ h := n + 10
+
+ // find the interesting spot - n
+ fmt.Printf("is[%d]:", l)
+ for i := l; i < h; i++ {
+ if i >= ld {
+ fmt.Printf(" --")
+ continue
+ }
+ fmt.Printf(" %.2x", data[i])
+ }
+ fmt.Printf("\n")
+
+ fmt.Printf("sb[%d]:", l)
+ for i := l; i < h; i++ {
+ if i >= ls {
+ fmt.Printf(" --")
+ continue
+ }
+ bs := hex(s[j])*16 + hex(s[j+1])
+ j += 2
+ fmt.Printf(" %.2x", bs)
+ }
+ fmt.Printf("\n")
+
+ t.Fail()
+
+ // t.Errorf("%s: \ngood: %s\nbad: %x", msg, s, b.Bytes())
+ // Print the output in a partially-decoded format; can
+ // be helpful when updating the test. It produces the output
+ // that is pasted, with minor edits, into the argument to verify().
+ // data := b.Bytes()
+ // nesting := 0
+ // for b.Len() > 0 {
+ // start := len(data) - b.Len()
+ // var u uint64
+ // u, err := DecodeVarint(b)
+ // if err != nil {
+ // fmt.Printf("decode error on varint:", err)
+ // return
+ // }
+ // wire := u & 0x7
+ // tag := u >> 3
+ // switch wire {
+ // case WireVarint:
+ // v, err := DecodeVarint(b)
+ // if err != nil {
+ // fmt.Printf("decode error on varint:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n",
+ // data[start:len(data)-b.Len()], tag, wire, v)
+ // case WireFixed32:
+ // v, err := DecodeFixed32(b)
+ // if err != nil {
+ // fmt.Printf("decode error on fixed32:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n",
+ // data[start:len(data)-b.Len()], tag, wire, v)
+ // case WireFixed64:
+ // v, err := DecodeFixed64(b)
+ // if err != nil {
+ // fmt.Printf("decode error on fixed64:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n",
+ // data[start:len(data)-b.Len()], tag, wire, v)
+ // case WireBytes:
+ // nb, err := DecodeVarint(b)
+ // if err != nil {
+ // fmt.Printf("decode error on bytes:", err)
+ // return
+ // }
+ // after_tag := len(data) - b.Len()
+ // str := make([]byte, nb)
+ // _, err = b.Read(str)
+ // if err != nil {
+ // fmt.Printf("decode error on bytes:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" \"%x\" // field %d, encoding %d (FIELD)\n",
+ // data[start:after_tag], str, tag, wire)
+ // case WireStartGroup:
+ // nesting++
+ // fmt.Printf("\t\t\"%x\"\t\t// start group field %d level %d\n",
+ // data[start:len(data)-b.Len()], tag, nesting)
+ // case WireEndGroup:
+ // fmt.Printf("\t\t\"%x\"\t\t// end group field %d level %d\n",
+ // data[start:len(data)-b.Len()], tag, nesting)
+ // nesting--
+ // default:
+ // fmt.Printf("unrecognized wire type %d\n", wire)
+ // return
+ // }
+ // }
+}
+
+func hex(c uint8) uint8 {
+ if '0' <= c && c <= '9' {
+ return c - '0'
+ }
+ if 'a' <= c && c <= 'f' {
+ return 10 + c - 'a'
+ }
+ if 'A' <= c && c <= 'F' {
+ return 10 + c - 'A'
+ }
+ return 0
+}
+
+func equal(b []byte, s string, t *testing.T) bool {
+ if 2*len(b) != len(s) {
+ // fail(fmt.Sprintf("wrong lengths: 2*%d != %d", len(b), len(s)), b, s, t)
+ fmt.Printf("wrong lengths: 2*%d != %d\n", len(b), len(s))
+ return false
+ }
+ for i, j := 0, 0; i < len(b); i, j = i+1, j+2 {
+ x := hex(s[j])*16 + hex(s[j+1])
+ if b[i] != x {
+ // fail(fmt.Sprintf("bad byte[%d]:%x %x", i, b[i], x), b, s, t)
+ fmt.Printf("bad byte[%d]:%x %x", i, b[i], x)
+ return false
+ }
+ }
+ return true
+}
+
+func overify(t *testing.T, pb *GoTest, expected string) {
+ o := old()
+ err := o.Marshal(pb)
+ if err != nil {
+ fmt.Printf("overify marshal-1 err = %v", err)
+ o.DebugPrint("", o.Bytes())
+ t.Fatalf("expected = %s", expected)
+ }
+ if !equal(o.Bytes(), expected, t) {
+ o.DebugPrint("overify neq 1", o.Bytes())
+ t.Fatalf("expected = %s", expected)
+ }
+
+ // Now test Unmarshal by recreating the original buffer.
+ pbd := new(GoTest)
+ err = o.Unmarshal(pbd)
+ if err != nil {
+ t.Fatalf("overify unmarshal err = %v", err)
+ o.DebugPrint("", o.Bytes())
+ t.Fatalf("string = %s", expected)
+ }
+ o.Reset()
+ err = o.Marshal(pbd)
+ if err != nil {
+ t.Errorf("overify marshal-2 err = %v", err)
+ o.DebugPrint("", o.Bytes())
+ t.Fatalf("string = %s", expected)
+ }
+ if !equal(o.Bytes(), expected, t) {
+ o.DebugPrint("overify neq 2", o.Bytes())
+ t.Fatalf("string = %s", expected)
+ }
+}
+
+// Simple tests for numeric encode/decode primitives (varint, etc.)
+func TestNumericPrimitives(t *testing.T) {
+ for i := uint64(0); i < 1e6; i += 111 {
+ o := old()
+ if o.EncodeVarint(i) != nil {
+ t.Error("EncodeVarint")
+ break
+ }
+ x, e := o.DecodeVarint()
+ if e != nil {
+ t.Fatal("DecodeVarint")
+ }
+ if x != i {
+ t.Fatal("varint decode fail:", i, x)
+ }
+
+ o = old()
+ if o.EncodeFixed32(i) != nil {
+ t.Fatal("encFixed32")
+ }
+ x, e = o.DecodeFixed32()
+ if e != nil {
+ t.Fatal("decFixed32")
+ }
+ if x != i {
+ t.Fatal("fixed32 decode fail:", i, x)
+ }
+
+ o = old()
+ if o.EncodeFixed64(i*1234567) != nil {
+ t.Error("encFixed64")
+ break
+ }
+ x, e = o.DecodeFixed64()
+ if e != nil {
+ t.Error("decFixed64")
+ break
+ }
+ if x != i*1234567 {
+ t.Error("fixed64 decode fail:", i*1234567, x)
+ break
+ }
+
+ o = old()
+ i32 := int32(i - 12345)
+ if o.EncodeZigzag32(uint64(i32)) != nil {
+ t.Fatal("EncodeZigzag32")
+ }
+ x, e = o.DecodeZigzag32()
+ if e != nil {
+ t.Fatal("DecodeZigzag32")
+ }
+ if x != uint64(uint32(i32)) {
+ t.Fatal("zigzag32 decode fail:", i32, x)
+ }
+
+ o = old()
+ i64 := int64(i - 12345)
+ if o.EncodeZigzag64(uint64(i64)) != nil {
+ t.Fatal("EncodeZigzag64")
+ }
+ x, e = o.DecodeZigzag64()
+ if e != nil {
+ t.Fatal("DecodeZigzag64")
+ }
+ if x != uint64(i64) {
+ t.Fatal("zigzag64 decode fail:", i64, x)
+ }
+ }
+}
+
+// fakeMarshaler is a simple struct implementing Marshaler and Message interfaces.
+type fakeMarshaler struct {
+ b []byte
+ err error
+}
+
+func (f fakeMarshaler) Marshal() ([]byte, error) {
+ return f.b, f.err
+}
+
+func (f fakeMarshaler) String() string {
+ return fmt.Sprintf("Bytes: %v Error: %v", f.b, f.err)
+}
+
+func (f fakeMarshaler) ProtoMessage() {}
+
+func (f fakeMarshaler) Reset() {}
+
+// Simple tests for proto messages that implement the Marshaler interface.
+func TestMarshalerEncoding(t *testing.T) {
+ tests := []struct {
+ name string
+ m Message
+ want []byte
+ wantErr error
+ }{
+ {
+ name: "Marshaler that fails",
+ m: fakeMarshaler{
+ err: errors.New("some marshal err"),
+ b: []byte{5, 6, 7},
+ },
+ // Since there's an error, nothing should be written to buffer.
+ want: nil,
+ wantErr: errors.New("some marshal err"),
+ },
+ {
+ name: "Marshaler that succeeds",
+ m: fakeMarshaler{
+ b: []byte{0, 1, 2, 3, 4, 127, 255},
+ },
+ want: []byte{0, 1, 2, 3, 4, 127, 255},
+ wantErr: nil,
+ },
+ }
+ for _, test := range tests {
+ b := NewBuffer(nil)
+ err := b.Marshal(test.m)
+ if !reflect.DeepEqual(test.wantErr, err) {
+ t.Errorf("%s: got err %v wanted %v", test.name, err, test.wantErr)
+ }
+ if !reflect.DeepEqual(test.want, b.Bytes()) {
+ t.Errorf("%s: got bytes %v wanted %v", test.name, b.Bytes(), test.want)
+ }
+ }
+}
+
+// Simple tests for bytes
+func TestBytesPrimitives(t *testing.T) {
+ o := old()
+ bytes := []byte{'n', 'o', 'w', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 't', 'i', 'm', 'e'}
+ if o.EncodeRawBytes(bytes) != nil {
+ t.Error("EncodeRawBytes")
+ }
+ decb, e := o.DecodeRawBytes(false)
+ if e != nil {
+ t.Error("DecodeRawBytes")
+ }
+ equalbytes(bytes, decb, t)
+}
+
+// Simple tests for strings
+func TestStringPrimitives(t *testing.T) {
+ o := old()
+ s := "now is the time"
+ if o.EncodeStringBytes(s) != nil {
+ t.Error("enc_string")
+ }
+ decs, e := o.DecodeStringBytes()
+ if e != nil {
+ t.Error("dec_string")
+ }
+ if s != decs {
+ t.Error("string encode/decode fail:", s, decs)
+ }
+}
+
+// Do we catch the "required bit not set" case?
+func TestRequiredBit(t *testing.T) {
+ o := old()
+ pb := new(GoTest)
+ err := o.Marshal(pb)
+ if err == nil {
+ t.Error("did not catch missing required fields")
+ } else if strings.Index(err.Error(), "Kind") < 0 {
+ t.Error("wrong error type:", err)
+ }
+}
+
+// Check that all fields are nil.
+// Clearly silly, and a residue from a more interesting test with an earlier,
+// different initialization property, but it once caught a compiler bug so
+// it lives.
+func checkInitialized(pb *GoTest, t *testing.T) {
+ if pb.F_BoolDefaulted != nil {
+ t.Error("New or Reset did not set boolean:", *pb.F_BoolDefaulted)
+ }
+ if pb.F_Int32Defaulted != nil {
+ t.Error("New or Reset did not set int32:", *pb.F_Int32Defaulted)
+ }
+ if pb.F_Int64Defaulted != nil {
+ t.Error("New or Reset did not set int64:", *pb.F_Int64Defaulted)
+ }
+ if pb.F_Fixed32Defaulted != nil {
+ t.Error("New or Reset did not set fixed32:", *pb.F_Fixed32Defaulted)
+ }
+ if pb.F_Fixed64Defaulted != nil {
+ t.Error("New or Reset did not set fixed64:", *pb.F_Fixed64Defaulted)
+ }
+ if pb.F_Uint32Defaulted != nil {
+ t.Error("New or Reset did not set uint32:", *pb.F_Uint32Defaulted)
+ }
+ if pb.F_Uint64Defaulted != nil {
+ t.Error("New or Reset did not set uint64:", *pb.F_Uint64Defaulted)
+ }
+ if pb.F_FloatDefaulted != nil {
+ t.Error("New or Reset did not set float:", *pb.F_FloatDefaulted)
+ }
+ if pb.F_DoubleDefaulted != nil {
+ t.Error("New or Reset did not set double:", *pb.F_DoubleDefaulted)
+ }
+ if pb.F_StringDefaulted != nil {
+ t.Error("New or Reset did not set string:", *pb.F_StringDefaulted)
+ }
+ if pb.F_BytesDefaulted != nil {
+ t.Error("New or Reset did not set bytes:", string(pb.F_BytesDefaulted))
+ }
+ if pb.F_Sint32Defaulted != nil {
+ t.Error("New or Reset did not set int32:", *pb.F_Sint32Defaulted)
+ }
+ if pb.F_Sint64Defaulted != nil {
+ t.Error("New or Reset did not set int64:", *pb.F_Sint64Defaulted)
+ }
+}
+
+// Does Reset() reset?
+func TestReset(t *testing.T) {
+ pb := initGoTest(true)
+ // muck with some values
+ pb.F_BoolDefaulted = Bool(false)
+ pb.F_Int32Defaulted = Int32(237)
+ pb.F_Int64Defaulted = Int64(12346)
+ pb.F_Fixed32Defaulted = Uint32(32000)
+ pb.F_Fixed64Defaulted = Uint64(666)
+ pb.F_Uint32Defaulted = Uint32(323232)
+ pb.F_Uint64Defaulted = nil
+ pb.F_FloatDefaulted = nil
+ pb.F_DoubleDefaulted = Float64(0)
+ pb.F_StringDefaulted = String("gotcha")
+ pb.F_BytesDefaulted = []byte("asdfasdf")
+ pb.F_Sint32Defaulted = Int32(123)
+ pb.F_Sint64Defaulted = Int64(789)
+ pb.Reset()
+ checkInitialized(pb, t)
+}
+
+// All required fields set, no defaults provided.
+func TestEncodeDecode1(t *testing.T) {
+ pb := initGoTest(false)
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 0x20
+ "714000000000000000"+ // field 14, encoding 1, value 0x40
+ "78a019"+ // field 15, encoding 0, value 0xca0 = 3232
+ "8001c032"+ // field 16, encoding 0, value 0x1940 = 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2, string "string"
+ "b304"+ // field 70, encoding 3, start group
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // field 70, encoding 4, end group
+ "aa0605"+"6279746573"+ // field 101, encoding 2, string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f") // field 103, encoding 0, 0x7f zigzag64
+}
+
+// All required fields set, defaults provided.
+func TestEncodeDecode2(t *testing.T) {
+ pb := initGoTest(true)
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All default fields set to their default value by hand
+func TestEncodeDecode3(t *testing.T) {
+ pb := initGoTest(false)
+ pb.F_BoolDefaulted = Bool(true)
+ pb.F_Int32Defaulted = Int32(32)
+ pb.F_Int64Defaulted = Int64(64)
+ pb.F_Fixed32Defaulted = Uint32(320)
+ pb.F_Fixed64Defaulted = Uint64(640)
+ pb.F_Uint32Defaulted = Uint32(3200)
+ pb.F_Uint64Defaulted = Uint64(6400)
+ pb.F_FloatDefaulted = Float32(314159)
+ pb.F_DoubleDefaulted = Float64(271828)
+ pb.F_StringDefaulted = String("hello, \"world!\"\n")
+ pb.F_BytesDefaulted = []byte("Bignose")
+ pb.F_Sint32Defaulted = Int32(-32)
+ pb.F_Sint64Defaulted = Int64(-64)
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All required fields set, defaults provided, all non-defaulted optional fields have values.
+func TestEncodeDecode4(t *testing.T) {
+ pb := initGoTest(true)
+ pb.Table = String("hello")
+ pb.Param = Int32(7)
+ pb.OptionalField = initGoTestField()
+ pb.F_BoolOptional = Bool(true)
+ pb.F_Int32Optional = Int32(32)
+ pb.F_Int64Optional = Int64(64)
+ pb.F_Fixed32Optional = Uint32(3232)
+ pb.F_Fixed64Optional = Uint64(6464)
+ pb.F_Uint32Optional = Uint32(323232)
+ pb.F_Uint64Optional = Uint64(646464)
+ pb.F_FloatOptional = Float32(32.)
+ pb.F_DoubleOptional = Float64(64.)
+ pb.F_StringOptional = String("hello")
+ pb.F_BytesOptional = []byte("Bignose")
+ pb.F_Sint32Optional = Int32(-32)
+ pb.F_Sint64Optional = Int64(-64)
+ pb.Optionalgroup = initGoTest_OptionalGroup()
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "1205"+"68656c6c6f"+ // field 2, encoding 2, string "hello"
+ "1807"+ // field 3, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "320d"+"0a056c6162656c120474797065"+ // field 6, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "f00101"+ // field 30, encoding 0, value 1
+ "f80120"+ // field 31, encoding 0, value 32
+ "800240"+ // field 32, encoding 0, value 64
+ "8d02a00c0000"+ // field 33, encoding 5, value 3232
+ "91024019000000000000"+ // field 34, encoding 1, value 6464
+ "9802a0dd13"+ // field 35, encoding 0, value 323232
+ "a002c0ba27"+ // field 36, encoding 0, value 646464
+ "ad0200000042"+ // field 37, encoding 5, value 32.0
+ "b1020000000000005040"+ // field 38, encoding 1, value 64.0
+ "ba0205"+"68656c6c6f"+ // field 39, encoding 2, string "hello"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "d305"+ // start group field 90 level 1
+ "da0508"+"6f7074696f6e616c"+ // field 91, encoding 2, string "optional"
+ "d405"+ // end group field 90 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "ea1207"+"4269676e6f7365"+ // field 301, encoding 2, string "Bignose"
+ "f0123f"+ // field 302, encoding 0, value 63
+ "f8127f"+ // field 303, encoding 0, value 127
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All required fields set, defaults provided, all repeated fields given two values.
+func TestEncodeDecode5(t *testing.T) {
+ pb := initGoTest(true)
+ pb.RepeatedField = []*GoTestField{initGoTestField(), initGoTestField()}
+ pb.F_BoolRepeated = []bool{false, true}
+ pb.F_Int32Repeated = []int32{32, 33}
+ pb.F_Int64Repeated = []int64{64, 65}
+ pb.F_Fixed32Repeated = []uint32{3232, 3333}
+ pb.F_Fixed64Repeated = []uint64{6464, 6565}
+ pb.F_Uint32Repeated = []uint32{323232, 333333}
+ pb.F_Uint64Repeated = []uint64{646464, 656565}
+ pb.F_FloatRepeated = []float32{32., 33.}
+ pb.F_DoubleRepeated = []float64{64., 65.}
+ pb.F_StringRepeated = []string{"hello", "sailor"}
+ pb.F_BytesRepeated = [][]byte{[]byte("big"), []byte("nose")}
+ pb.F_Sint32Repeated = []int32{32, -32}
+ pb.F_Sint64Repeated = []int64{64, -64}
+ pb.Repeatedgroup = []*GoTest_RepeatedGroup{initGoTest_RepeatedGroup(), initGoTest_RepeatedGroup()}
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField)
+ "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "a00100"+ // field 20, encoding 0, value 0
+ "a00101"+ // field 20, encoding 0, value 1
+ "a80120"+ // field 21, encoding 0, value 32
+ "a80121"+ // field 21, encoding 0, value 33
+ "b00140"+ // field 22, encoding 0, value 64
+ "b00141"+ // field 22, encoding 0, value 65
+ "bd01a00c0000"+ // field 23, encoding 5, value 3232
+ "bd01050d0000"+ // field 23, encoding 5, value 3333
+ "c1014019000000000000"+ // field 24, encoding 1, value 6464
+ "c101a519000000000000"+ // field 24, encoding 1, value 6565
+ "c801a0dd13"+ // field 25, encoding 0, value 323232
+ "c80195ac14"+ // field 25, encoding 0, value 333333
+ "d001c0ba27"+ // field 26, encoding 0, value 646464
+ "d001b58928"+ // field 26, encoding 0, value 656565
+ "dd0100000042"+ // field 27, encoding 5, value 32.0
+ "dd0100000442"+ // field 27, encoding 5, value 33.0
+ "e1010000000000005040"+ // field 28, encoding 1, value 64.0
+ "e1010000000000405040"+ // field 28, encoding 1, value 65.0
+ "ea0105"+"68656c6c6f"+ // field 29, encoding 2, string "hello"
+ "ea0106"+"7361696c6f72"+ // field 29, encoding 2, string "sailor"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "8305"+ // start group field 80 level 1
+ "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated"
+ "8405"+ // end group field 80 level 1
+ "8305"+ // start group field 80 level 1
+ "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated"
+ "8405"+ // end group field 80 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "ca0c03"+"626967"+ // field 201, encoding 2, string "big"
+ "ca0c04"+"6e6f7365"+ // field 201, encoding 2, string "nose"
+ "d00c40"+ // field 202, encoding 0, value 32
+ "d00c3f"+ // field 202, encoding 0, value -32
+ "d80c8001"+ // field 203, encoding 0, value 64
+ "d80c7f"+ // field 203, encoding 0, value -64
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All required fields set, all packed repeated fields given two values.
+func TestEncodeDecode6(t *testing.T) {
+ pb := initGoTest(false)
+ pb.F_BoolRepeatedPacked = []bool{false, true}
+ pb.F_Int32RepeatedPacked = []int32{32, 33}
+ pb.F_Int64RepeatedPacked = []int64{64, 65}
+ pb.F_Fixed32RepeatedPacked = []uint32{3232, 3333}
+ pb.F_Fixed64RepeatedPacked = []uint64{6464, 6565}
+ pb.F_Uint32RepeatedPacked = []uint32{323232, 333333}
+ pb.F_Uint64RepeatedPacked = []uint64{646464, 656565}
+ pb.F_FloatRepeatedPacked = []float32{32., 33.}
+ pb.F_DoubleRepeatedPacked = []float64{64., 65.}
+ pb.F_Sint32RepeatedPacked = []int32{32, -32}
+ pb.F_Sint64RepeatedPacked = []int64{64, -64}
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "9203020001"+ // field 50, encoding 2, 2 bytes, value 0, value 1
+ "9a03022021"+ // field 51, encoding 2, 2 bytes, value 32, value 33
+ "a203024041"+ // field 52, encoding 2, 2 bytes, value 64, value 65
+ "aa0308"+ // field 53, encoding 2, 8 bytes
+ "a00c0000050d0000"+ // value 3232, value 3333
+ "b20310"+ // field 54, encoding 2, 16 bytes
+ "4019000000000000a519000000000000"+ // value 6464, value 6565
+ "ba0306"+ // field 55, encoding 2, 6 bytes
+ "a0dd1395ac14"+ // value 323232, value 333333
+ "c20306"+ // field 56, encoding 2, 6 bytes
+ "c0ba27b58928"+ // value 646464, value 656565
+ "ca0308"+ // field 57, encoding 2, 8 bytes
+ "0000004200000442"+ // value 32.0, value 33.0
+ "d20310"+ // field 58, encoding 2, 16 bytes
+ "00000000000050400000000000405040"+ // value 64.0, value 65.0
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "b21f02"+ // field 502, encoding 2, 2 bytes
+ "403f"+ // value 32, value -32
+ "ba1f03"+ // field 503, encoding 2, 3 bytes
+ "80017f") // value 64, value -64
+}
+
+// Test that we can encode empty bytes fields.
+func TestEncodeDecodeBytes1(t *testing.T) {
+ pb := initGoTest(false)
+
+ // Create our bytes
+ pb.F_BytesRequired = []byte{}
+ pb.F_BytesRepeated = [][]byte{{}}
+ pb.F_BytesOptional = []byte{}
+
+ d, err := Marshal(pb)
+ if err != nil {
+ t.Error(err)
+ }
+
+ pbd := new(GoTest)
+ if err := Unmarshal(d, pbd); err != nil {
+ t.Error(err)
+ }
+
+ if pbd.F_BytesRequired == nil || len(pbd.F_BytesRequired) != 0 {
+ t.Error("required empty bytes field is incorrect")
+ }
+ if pbd.F_BytesRepeated == nil || len(pbd.F_BytesRepeated) == 1 && pbd.F_BytesRepeated[0] == nil {
+ t.Error("repeated empty bytes field is incorrect")
+ }
+ if pbd.F_BytesOptional == nil || len(pbd.F_BytesOptional) != 0 {
+ t.Error("optional empty bytes field is incorrect")
+ }
+}
+
+// Test that we encode nil-valued fields of a repeated bytes field correctly.
+// Since entries in a repeated field cannot be nil, nil must mean empty value.
+func TestEncodeDecodeBytes2(t *testing.T) {
+ pb := initGoTest(false)
+
+ // Create our bytes
+ pb.F_BytesRepeated = [][]byte{nil}
+
+ d, err := Marshal(pb)
+ if err != nil {
+ t.Error(err)
+ }
+
+ pbd := new(GoTest)
+ if err := Unmarshal(d, pbd); err != nil {
+ t.Error(err)
+ }
+
+ if len(pbd.F_BytesRepeated) != 1 || pbd.F_BytesRepeated[0] == nil {
+ t.Error("Unexpected value for repeated bytes field")
+ }
+}
+
+// All required fields set, defaults provided, all repeated fields given two values.
+func TestSkippingUnrecognizedFields(t *testing.T) {
+ o := old()
+ pb := initGoTestField()
+
+ // Marshal it normally.
+ o.Marshal(pb)
+
+ // Now new a GoSkipTest record.
+ skip := &GoSkipTest{
+ SkipInt32: Int32(32),
+ SkipFixed32: Uint32(3232),
+ SkipFixed64: Uint64(6464),
+ SkipString: String("skipper"),
+ Skipgroup: &GoSkipTest_SkipGroup{
+ GroupInt32: Int32(75),
+ GroupString: String("wxyz"),
+ },
+ }
+
+ // Marshal it into same buffer.
+ o.Marshal(skip)
+
+ pbd := new(GoTestField)
+ o.Unmarshal(pbd)
+
+ // The __unrecognized field should be a marshaling of GoSkipTest
+ skipd := new(GoSkipTest)
+
+ o.SetBuf(pbd.XXX_unrecognized)
+ o.Unmarshal(skipd)
+
+ if *skipd.SkipInt32 != *skip.SkipInt32 {
+ t.Error("skip int32", skipd.SkipInt32)
+ }
+ if *skipd.SkipFixed32 != *skip.SkipFixed32 {
+ t.Error("skip fixed32", skipd.SkipFixed32)
+ }
+ if *skipd.SkipFixed64 != *skip.SkipFixed64 {
+ t.Error("skip fixed64", skipd.SkipFixed64)
+ }
+ if *skipd.SkipString != *skip.SkipString {
+ t.Error("skip string", *skipd.SkipString)
+ }
+ if *skipd.Skipgroup.GroupInt32 != *skip.Skipgroup.GroupInt32 {
+ t.Error("skip group int32", skipd.Skipgroup.GroupInt32)
+ }
+ if *skipd.Skipgroup.GroupString != *skip.Skipgroup.GroupString {
+ t.Error("skip group string", *skipd.Skipgroup.GroupString)
+ }
+}
+
+// Check that unrecognized fields of a submessage are preserved.
+func TestSubmessageUnrecognizedFields(t *testing.T) {
+ nm := &NewMessage{
+ Nested: &NewMessage_Nested{
+ Name: String("Nigel"),
+ FoodGroup: String("carbs"),
+ },
+ }
+ b, err := Marshal(nm)
+ if err != nil {
+ t.Fatalf("Marshal of NewMessage: %v", err)
+ }
+
+ // Unmarshal into an OldMessage.
+ om := new(OldMessage)
+ if err := Unmarshal(b, om); err != nil {
+ t.Fatalf("Unmarshal to OldMessage: %v", err)
+ }
+ exp := &OldMessage{
+ Nested: &OldMessage_Nested{
+ Name: String("Nigel"),
+ // normal protocol buffer users should not do this
+ XXX_unrecognized: []byte("\x12\x05carbs"),
+ },
+ }
+ if !Equal(om, exp) {
+ t.Errorf("om = %v, want %v", om, exp)
+ }
+
+ // Clone the OldMessage.
+ om = Clone(om).(*OldMessage)
+ if !Equal(om, exp) {
+ t.Errorf("Clone(om) = %v, want %v", om, exp)
+ }
+
+ // Marshal the OldMessage, then unmarshal it into an empty NewMessage.
+ if b, err = Marshal(om); err != nil {
+ t.Fatalf("Marshal of OldMessage: %v", err)
+ }
+ t.Logf("Marshal(%v) -> %q", om, b)
+ nm2 := new(NewMessage)
+ if err := Unmarshal(b, nm2); err != nil {
+ t.Fatalf("Unmarshal to NewMessage: %v", err)
+ }
+ if !Equal(nm, nm2) {
+ t.Errorf("NewMessage round-trip: %v => %v", nm, nm2)
+ }
+}
+
+// Check that an int32 field can be upgraded to an int64 field.
+func TestNegativeInt32(t *testing.T) {
+ om := &OldMessage{
+ Num: Int32(-1),
+ }
+ b, err := Marshal(om)
+ if err != nil {
+ t.Fatalf("Marshal of OldMessage: %v", err)
+ }
+
+ // Check the size. It should be 11 bytes;
+ // 1 for the field/wire type, and 10 for the negative number.
+ if len(b) != 11 {
+ t.Errorf("%v marshaled as %q, wanted 11 bytes", om, b)
+ }
+
+ // Unmarshal into a NewMessage.
+ nm := new(NewMessage)
+ if err := Unmarshal(b, nm); err != nil {
+ t.Fatalf("Unmarshal to NewMessage: %v", err)
+ }
+ want := &NewMessage{
+ Num: Int64(-1),
+ }
+ if !Equal(nm, want) {
+ t.Errorf("nm = %v, want %v", nm, want)
+ }
+}
+
+// Check that we can grow an array (repeated field) to have many elements.
+// This test doesn't depend only on our encoding; for variety, it makes sure
+// we create, encode, and decode the correct contents explicitly. It's therefore
+// a bit messier.
+// This test also uses (and hence tests) the Marshal/Unmarshal functions
+// instead of the methods.
+func TestBigRepeated(t *testing.T) {
+ pb := initGoTest(true)
+
+ // Create the arrays
+ const N = 50 // Internally the library starts much smaller.
+ pb.Repeatedgroup = make([]*GoTest_RepeatedGroup, N)
+ pb.F_Sint64Repeated = make([]int64, N)
+ pb.F_Sint32Repeated = make([]int32, N)
+ pb.F_BytesRepeated = make([][]byte, N)
+ pb.F_StringRepeated = make([]string, N)
+ pb.F_DoubleRepeated = make([]float64, N)
+ pb.F_FloatRepeated = make([]float32, N)
+ pb.F_Uint64Repeated = make([]uint64, N)
+ pb.F_Uint32Repeated = make([]uint32, N)
+ pb.F_Fixed64Repeated = make([]uint64, N)
+ pb.F_Fixed32Repeated = make([]uint32, N)
+ pb.F_Int64Repeated = make([]int64, N)
+ pb.F_Int32Repeated = make([]int32, N)
+ pb.F_BoolRepeated = make([]bool, N)
+ pb.RepeatedField = make([]*GoTestField, N)
+
+ // Fill in the arrays with checkable values.
+ igtf := initGoTestField()
+ igtrg := initGoTest_RepeatedGroup()
+ for i := 0; i < N; i++ {
+ pb.Repeatedgroup[i] = igtrg
+ pb.F_Sint64Repeated[i] = int64(i)
+ pb.F_Sint32Repeated[i] = int32(i)
+ s := fmt.Sprint(i)
+ pb.F_BytesRepeated[i] = []byte(s)
+ pb.F_StringRepeated[i] = s
+ pb.F_DoubleRepeated[i] = float64(i)
+ pb.F_FloatRepeated[i] = float32(i)
+ pb.F_Uint64Repeated[i] = uint64(i)
+ pb.F_Uint32Repeated[i] = uint32(i)
+ pb.F_Fixed64Repeated[i] = uint64(i)
+ pb.F_Fixed32Repeated[i] = uint32(i)
+ pb.F_Int64Repeated[i] = int64(i)
+ pb.F_Int32Repeated[i] = int32(i)
+ pb.F_BoolRepeated[i] = i%2 == 0
+ pb.RepeatedField[i] = igtf
+ }
+
+ // Marshal.
+ buf, _ := Marshal(pb)
+
+ // Now test Unmarshal by recreating the original buffer.
+ pbd := new(GoTest)
+ Unmarshal(buf, pbd)
+
+ // Check the checkable values
+ for i := uint64(0); i < N; i++ {
+ if pbd.Repeatedgroup[i] == nil { // TODO: more checking?
+ t.Error("pbd.Repeatedgroup bad")
+ }
+ var x uint64
+ x = uint64(pbd.F_Sint64Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Sint64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Sint32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Sint32Repeated bad", x, i)
+ }
+ s := fmt.Sprint(i)
+ equalbytes(pbd.F_BytesRepeated[i], []byte(s), t)
+ if pbd.F_StringRepeated[i] != s {
+ t.Error("pbd.F_Sint32Repeated bad", pbd.F_StringRepeated[i], i)
+ }
+ x = uint64(pbd.F_DoubleRepeated[i])
+ if x != i {
+ t.Error("pbd.F_DoubleRepeated bad", x, i)
+ }
+ x = uint64(pbd.F_FloatRepeated[i])
+ if x != i {
+ t.Error("pbd.F_FloatRepeated bad", x, i)
+ }
+ x = pbd.F_Uint64Repeated[i]
+ if x != i {
+ t.Error("pbd.F_Uint64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Uint32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Uint32Repeated bad", x, i)
+ }
+ x = pbd.F_Fixed64Repeated[i]
+ if x != i {
+ t.Error("pbd.F_Fixed64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Fixed32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Fixed32Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Int64Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Int64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Int32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Int32Repeated bad", x, i)
+ }
+ if pbd.F_BoolRepeated[i] != (i%2 == 0) {
+ t.Error("pbd.F_BoolRepeated bad", x, i)
+ }
+ if pbd.RepeatedField[i] == nil { // TODO: more checking?
+ t.Error("pbd.RepeatedField bad")
+ }
+ }
+}
+
+// Verify we give a useful message when decoding to the wrong structure type.
+func TestTypeMismatch(t *testing.T) {
+ pb1 := initGoTest(true)
+
+ // Marshal
+ o := old()
+ o.Marshal(pb1)
+
+ // Now Unmarshal it to the wrong type.
+ pb2 := initGoTestField()
+ err := o.Unmarshal(pb2)
+ if err == nil {
+ t.Error("expected error, got no error")
+ } else if !strings.Contains(err.Error(), "bad wiretype") {
+ t.Error("expected bad wiretype error, got", err)
+ }
+}
+
+func encodeDecode(t *testing.T, in, out Message, msg string) {
+ buf, err := Marshal(in)
+ if err != nil {
+ t.Fatalf("failed marshaling %v: %v", msg, err)
+ }
+ if err := Unmarshal(buf, out); err != nil {
+ t.Fatalf("failed unmarshaling %v: %v", msg, err)
+ }
+}
+
+func TestPackedNonPackedDecoderSwitching(t *testing.T) {
+ np, p := new(NonPackedTest), new(PackedTest)
+
+ // non-packed -> packed
+ np.A = []int32{0, 1, 1, 2, 3, 5}
+ encodeDecode(t, np, p, "non-packed -> packed")
+ if !reflect.DeepEqual(np.A, p.B) {
+ t.Errorf("failed non-packed -> packed; np.A=%+v, p.B=%+v", np.A, p.B)
+ }
+
+ // packed -> non-packed
+ np.Reset()
+ p.B = []int32{3, 1, 4, 1, 5, 9}
+ encodeDecode(t, p, np, "packed -> non-packed")
+ if !reflect.DeepEqual(p.B, np.A) {
+ t.Errorf("failed packed -> non-packed; p.B=%+v, np.A=%+v", p.B, np.A)
+ }
+}
+
+func TestProto1RepeatedGroup(t *testing.T) {
+ pb := &MessageList{
+ Message: []*MessageList_Message{
+ {
+ Name: String("blah"),
+ Count: Int32(7),
+ },
+ // NOTE: pb.Message[1] is a nil
+ nil,
+ },
+ }
+
+ o := old()
+ err := o.Marshal(pb)
+ if err == nil || !strings.Contains(err.Error(), "repeated field Message has nil") {
+ t.Fatalf("unexpected or no error when marshaling: %v", err)
+ }
+}
+
+// Test that enums work. Checks for a bug introduced by making enums
+// named types instead of int32: newInt32FromUint64 would crash with
+// a type mismatch in reflect.PointTo.
+func TestEnum(t *testing.T) {
+ pb := new(GoEnum)
+ pb.Foo = FOO_FOO1.Enum()
+ o := old()
+ if err := o.Marshal(pb); err != nil {
+ t.Fatal("error encoding enum:", err)
+ }
+ pb1 := new(GoEnum)
+ if err := o.Unmarshal(pb1); err != nil {
+ t.Fatal("error decoding enum:", err)
+ }
+ if *pb1.Foo != FOO_FOO1 {
+ t.Error("expected 7 but got ", *pb1.Foo)
+ }
+}
+
+// Enum types have String methods. Check that enum fields can be printed.
+// We don't care what the value actually is, just as long as it doesn't crash.
+func TestPrintingNilEnumFields(t *testing.T) {
+ pb := new(GoEnum)
+ fmt.Sprintf("%+v", pb)
+}
+
+// Verify that absent required fields cause Marshal/Unmarshal to return errors.
+func TestRequiredFieldEnforcement(t *testing.T) {
+ pb := new(GoTestField)
+ _, err := Marshal(pb)
+ if err == nil {
+ t.Error("marshal: expected error, got nil")
+ } else if strings.Index(err.Error(), "Label") < 0 {
+ t.Errorf("marshal: bad error type: %v", err)
+ }
+
+ // A slightly sneaky, yet valid, proto. It encodes the same required field twice,
+ // so simply counting the required fields is insufficient.
+ // field 1, encoding 2, value "hi"
+ buf := []byte("\x0A\x02hi\x0A\x02hi")
+ err = Unmarshal(buf, pb)
+ if err == nil {
+ t.Error("unmarshal: expected error, got nil")
+ } else if strings.Index(err.Error(), "{Unknown}") < 0 {
+ t.Errorf("unmarshal: bad error type: %v", err)
+ }
+}
+
+func TestTypedNilMarshal(t *testing.T) {
+ // A typed nil should return ErrNil and not crash.
+ _, err := Marshal((*GoEnum)(nil))
+ if err != ErrNil {
+ t.Errorf("Marshal: got err %v, want ErrNil", err)
+ }
+}
+
+// A type that implements the Marshaler interface, but is not nillable.
+type nonNillableInt uint64
+
+func (nni nonNillableInt) Marshal() ([]byte, error) {
+ return EncodeVarint(uint64(nni)), nil
+}
+
+type NNIMessage struct {
+ nni nonNillableInt
+}
+
+func (*NNIMessage) Reset() {}
+func (*NNIMessage) String() string { return "" }
+func (*NNIMessage) ProtoMessage() {}
+
+// A type that implements the Marshaler interface and is nillable.
+type nillableMessage struct {
+ x uint64
+}
+
+func (nm *nillableMessage) Marshal() ([]byte, error) {
+ return EncodeVarint(nm.x), nil
+}
+
+type NMMessage struct {
+ nm *nillableMessage
+}
+
+func (*NMMessage) Reset() {}
+func (*NMMessage) String() string { return "" }
+func (*NMMessage) ProtoMessage() {}
+
+// Verify a type that uses the Marshaler interface, but has a nil pointer.
+func TestNilMarshaler(t *testing.T) {
+ // Try a struct with a Marshaler field that is nil.
+ // It should be directly marshable.
+ nmm := new(NMMessage)
+ if _, err := Marshal(nmm); err != nil {
+ t.Error("unexpected error marshaling nmm: ", err)
+ }
+
+ // Try a struct with a Marshaler field that is not nillable.
+ nnim := new(NNIMessage)
+ nnim.nni = 7
+ var _ Marshaler = nnim.nni // verify it is truly a Marshaler
+ if _, err := Marshal(nnim); err != nil {
+ t.Error("unexpected error marshaling nnim: ", err)
+ }
+}
+
+func TestAllSetDefaults(t *testing.T) {
+ // Exercise SetDefaults with all scalar field types.
+ m := &Defaults{
+ // NaN != NaN, so override that here.
+ F_Nan: Float32(1.7),
+ }
+ expected := &Defaults{
+ F_Bool: Bool(true),
+ F_Int32: Int32(32),
+ F_Int64: Int64(64),
+ F_Fixed32: Uint32(320),
+ F_Fixed64: Uint64(640),
+ F_Uint32: Uint32(3200),
+ F_Uint64: Uint64(6400),
+ F_Float: Float32(314159),
+ F_Double: Float64(271828),
+ F_String: String(`hello, "world!"` + "\n"),
+ F_Bytes: []byte("Bignose"),
+ F_Sint32: Int32(-32),
+ F_Sint64: Int64(-64),
+ F_Enum: Defaults_GREEN.Enum(),
+ F_Pinf: Float32(float32(math.Inf(1))),
+ F_Ninf: Float32(float32(math.Inf(-1))),
+ F_Nan: Float32(1.7),
+ StrZero: String(""),
+ }
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("SetDefaults failed\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestSetDefaultsWithSetField(t *testing.T) {
+ // Check that a set value is not overridden.
+ m := &Defaults{
+ F_Int32: Int32(12),
+ }
+ SetDefaults(m)
+ if v := m.GetF_Int32(); v != 12 {
+ t.Errorf("m.FInt32 = %v, want 12", v)
+ }
+}
+
+func TestSetDefaultsWithSubMessage(t *testing.T) {
+ m := &OtherMessage{
+ Key: Int64(123),
+ Inner: &InnerMessage{
+ Host: String("gopher"),
+ },
+ }
+ expected := &OtherMessage{
+ Key: Int64(123),
+ Inner: &InnerMessage{
+ Host: String("gopher"),
+ Port: Int32(4000),
+ },
+ }
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestSetDefaultsWithRepeatedSubMessage(t *testing.T) {
+ m := &MyMessage{
+ RepInner: []*InnerMessage{{}},
+ }
+ expected := &MyMessage{
+ RepInner: []*InnerMessage{{
+ Port: Int32(4000),
+ }},
+ }
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestSetDefaultWithRepeatedNonMessage(t *testing.T) {
+ m := &MyMessage{
+ Pet: []string{"turtle", "wombat"},
+ }
+ expected := Clone(m)
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestMaximumTagNumber(t *testing.T) {
+ m := &MaxTag{
+ LastField: String("natural goat essence"),
+ }
+ buf, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("proto.Marshal failed: %v", err)
+ }
+ m2 := new(MaxTag)
+ if err := Unmarshal(buf, m2); err != nil {
+ t.Fatalf("proto.Unmarshal failed: %v", err)
+ }
+ if got, want := m2.GetLastField(), *m.LastField; got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
+
+func TestJSON(t *testing.T) {
+ m := &MyMessage{
+ Count: Int32(4),
+ Pet: []string{"bunny", "kitty"},
+ Inner: &InnerMessage{
+ Host: String("cauchy"),
+ },
+ Bikeshed: MyMessage_GREEN.Enum(),
+ }
+ const expected = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"},"bikeshed":1}`
+
+ b, err := json.Marshal(m)
+ if err != nil {
+ t.Fatalf("json.Marshal failed: %v", err)
+ }
+ s := string(b)
+ if s != expected {
+ t.Errorf("got %s\nwant %s", s, expected)
+ }
+
+ received := new(MyMessage)
+ if err := json.Unmarshal(b, received); err != nil {
+ t.Fatalf("json.Unmarshal failed: %v", err)
+ }
+ if !Equal(received, m) {
+ t.Fatalf("got %s, want %s", received, m)
+ }
+
+ // Test unmarshalling of JSON with symbolic enum name.
+ const old = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"},"bikeshed":"GREEN"}`
+ received.Reset()
+ if err := json.Unmarshal([]byte(old), received); err != nil {
+ t.Fatalf("json.Unmarshal failed: %v", err)
+ }
+ if !Equal(received, m) {
+ t.Fatalf("got %s, want %s", received, m)
+ }
+}
+
+func TestBadWireType(t *testing.T) {
+ b := []byte{7<<3 | 6} // field 7, wire type 6
+ pb := new(OtherMessage)
+ if err := Unmarshal(b, pb); err == nil {
+ t.Errorf("Unmarshal did not fail")
+ } else if !strings.Contains(err.Error(), "unknown wire type") {
+ t.Errorf("wrong error: %v", err)
+ }
+}
+
+func TestBytesWithInvalidLength(t *testing.T) {
+ // If a byte sequence has an invalid (negative) length, Unmarshal should not panic.
+ b := []byte{2<<3 | WireBytes, 0xff, 0xff, 0xff, 0xff, 0xff, 0}
+ Unmarshal(b, new(MyMessage))
+}
+
+func TestLengthOverflow(t *testing.T) {
+ // Overflowing a length should not panic.
+ b := []byte{2<<3 | WireBytes, 1, 1, 3<<3 | WireBytes, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01}
+ Unmarshal(b, new(MyMessage))
+}
+
+func TestVarintOverflow(t *testing.T) {
+ // Overflowing a 64-bit length should not be allowed.
+ b := []byte{1<<3 | WireVarint, 0x01, 3<<3 | WireBytes, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01}
+ if err := Unmarshal(b, new(MyMessage)); err == nil {
+ t.Fatalf("Overflowed uint64 length without error")
+ }
+}
+
+func TestUnmarshalFuzz(t *testing.T) {
+ const N = 1000
+ seed := time.Now().UnixNano()
+ t.Logf("RNG seed is %d", seed)
+ rng := rand.New(rand.NewSource(seed))
+ buf := make([]byte, 20)
+ for i := 0; i < N; i++ {
+ for j := range buf {
+ buf[j] = byte(rng.Intn(256))
+ }
+ fuzzUnmarshal(t, buf)
+ }
+}
+
+func TestMergeMessages(t *testing.T) {
+ pb := &MessageList{Message: []*MessageList_Message{{Name: String("x"), Count: Int32(1)}}}
+ data, err := Marshal(pb)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+
+ pb1 := new(MessageList)
+ if err := Unmarshal(data, pb1); err != nil {
+ t.Fatalf("first Unmarshal: %v", err)
+ }
+ if err := Unmarshal(data, pb1); err != nil {
+ t.Fatalf("second Unmarshal: %v", err)
+ }
+ if len(pb1.Message) != 1 {
+ t.Errorf("two Unmarshals produced %d Messages, want 1", len(pb1.Message))
+ }
+
+ pb2 := new(MessageList)
+ if err := UnmarshalMerge(data, pb2); err != nil {
+ t.Fatalf("first UnmarshalMerge: %v", err)
+ }
+ if err := UnmarshalMerge(data, pb2); err != nil {
+ t.Fatalf("second UnmarshalMerge: %v", err)
+ }
+ if len(pb2.Message) != 2 {
+ t.Errorf("two UnmarshalMerges produced %d Messages, want 2", len(pb2.Message))
+ }
+}
+
+func TestExtensionMarshalOrder(t *testing.T) {
+ m := &MyMessage{Count: Int(123)}
+ if err := SetExtension(m, E_Ext_More, &Ext{Data: String("alpha")}); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+ if err := SetExtension(m, E_Ext_Text, String("aleph")); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+ if err := SetExtension(m, E_Ext_Number, Int32(1)); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+
+ // Serialize m several times, and check we get the same bytes each time.
+ var orig []byte
+ for i := 0; i < 100; i++ {
+ b, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if i == 0 {
+ orig = b
+ continue
+ }
+ if !bytes.Equal(b, orig) {
+ t.Errorf("Bytes differ on attempt #%d", i)
+ }
+ }
+}
+
+// Many extensions, because small maps might not iterate differently on each iteration.
+var exts = []*ExtensionDesc{
+ E_X201,
+ E_X202,
+ E_X203,
+ E_X204,
+ E_X205,
+ E_X206,
+ E_X207,
+ E_X208,
+ E_X209,
+ E_X210,
+ E_X211,
+ E_X212,
+ E_X213,
+ E_X214,
+ E_X215,
+ E_X216,
+ E_X217,
+ E_X218,
+ E_X219,
+ E_X220,
+ E_X221,
+ E_X222,
+ E_X223,
+ E_X224,
+ E_X225,
+ E_X226,
+ E_X227,
+ E_X228,
+ E_X229,
+ E_X230,
+ E_X231,
+ E_X232,
+ E_X233,
+ E_X234,
+ E_X235,
+ E_X236,
+ E_X237,
+ E_X238,
+ E_X239,
+ E_X240,
+ E_X241,
+ E_X242,
+ E_X243,
+ E_X244,
+ E_X245,
+ E_X246,
+ E_X247,
+ E_X248,
+ E_X249,
+ E_X250,
+}
+
+func TestMessageSetMarshalOrder(t *testing.T) {
+ m := &MyMessageSet{}
+ for _, x := range exts {
+ if err := SetExtension(m, x, &Empty{}); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+ }
+
+ buf, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+
+ // Serialize m several times, and check we get the same bytes each time.
+ for i := 0; i < 10; i++ {
+ b1, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if !bytes.Equal(b1, buf) {
+ t.Errorf("Bytes differ on re-Marshal #%d", i)
+ }
+
+ m2 := &MyMessageSet{}
+ if err := Unmarshal(buf, m2); err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+ b2, err := Marshal(m2)
+ if err != nil {
+ t.Errorf("re-Marshal: %v", err)
+ }
+ if !bytes.Equal(b2, buf) {
+ t.Errorf("Bytes differ on round-trip #%d", i)
+ }
+ }
+}
+
+func TestUnmarshalMergesMessages(t *testing.T) {
+ // If a nested message occurs twice in the input,
+ // the fields should be merged when decoding.
+ a := &OtherMessage{
+ Key: Int64(123),
+ Inner: &InnerMessage{
+ Host: String("polhode"),
+ Port: Int32(1234),
+ },
+ }
+ aData, err := Marshal(a)
+ if err != nil {
+ t.Fatalf("Marshal(a): %v", err)
+ }
+ b := &OtherMessage{
+ Weight: Float32(1.2),
+ Inner: &InnerMessage{
+ Host: String("herpolhode"),
+ Connected: Bool(true),
+ },
+ }
+ bData, err := Marshal(b)
+ if err != nil {
+ t.Fatalf("Marshal(b): %v", err)
+ }
+ want := &OtherMessage{
+ Key: Int64(123),
+ Weight: Float32(1.2),
+ Inner: &InnerMessage{
+ Host: String("herpolhode"),
+ Port: Int32(1234),
+ Connected: Bool(true),
+ },
+ }
+ got := new(OtherMessage)
+ if err := Unmarshal(append(aData, bData...), got); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ if !Equal(got, want) {
+ t.Errorf("\n got %v\nwant %v", got, want)
+ }
+}
+
+func TestEncodingSizes(t *testing.T) {
+ tests := []struct {
+ m Message
+ n int
+ }{
+ {&Defaults{F_Int32: Int32(math.MaxInt32)}, 6},
+ {&Defaults{F_Int32: Int32(math.MinInt32)}, 11},
+ {&Defaults{F_Uint32: Uint32(uint32(math.MaxInt32) + 1)}, 6},
+ {&Defaults{F_Uint32: Uint32(math.MaxUint32)}, 6},
+ }
+ for _, test := range tests {
+ b, err := Marshal(test.m)
+ if err != nil {
+ t.Errorf("Marshal(%v): %v", test.m, err)
+ continue
+ }
+ if len(b) != test.n {
+ t.Errorf("Marshal(%v) yielded %d bytes, want %d bytes", test.m, len(b), test.n)
+ }
+ }
+}
+
+func TestRequiredNotSetError(t *testing.T) {
+ pb := initGoTest(false)
+ pb.RequiredField.Label = nil
+ pb.F_Int32Required = nil
+ pb.F_Int64Required = nil
+
+ expected := "0807" + // field 1, encoding 0, value 7
+ "2206" + "120474797065" + // field 4, encoding 2 (GoTestField)
+ "5001" + // field 10, encoding 0, value 1
+ "6d20000000" + // field 13, encoding 5, value 0x20
+ "714000000000000000" + // field 14, encoding 1, value 0x40
+ "78a019" + // field 15, encoding 0, value 0xca0 = 3232
+ "8001c032" + // field 16, encoding 0, value 0x1940 = 6464
+ "8d0100004a45" + // field 17, encoding 5, value 3232.0
+ "9101000000000040b940" + // field 18, encoding 1, value 6464.0
+ "9a0106" + "737472696e67" + // field 19, encoding 2, string "string"
+ "b304" + // field 70, encoding 3, start group
+ "ba0408" + "7265717569726564" + // field 71, encoding 2, string "required"
+ "b404" + // field 70, encoding 4, end group
+ "aa0605" + "6279746573" + // field 101, encoding 2, string "bytes"
+ "b0063f" + // field 102, encoding 0, 0x3f zigzag32
+ "b8067f" // field 103, encoding 0, 0x7f zigzag64
+
+ o := old()
+ bytes, err := Marshal(pb)
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ fmt.Printf("marshal-1 err = %v, want *RequiredNotSetError", err)
+ o.DebugPrint("", bytes)
+ t.Fatalf("expected = %s", expected)
+ }
+ if strings.Index(err.Error(), "RequiredField.Label") < 0 {
+ t.Errorf("marshal-1 wrong err msg: %v", err)
+ }
+ if !equal(bytes, expected, t) {
+ o.DebugPrint("neq 1", bytes)
+ t.Fatalf("expected = %s", expected)
+ }
+
+ // Now test Unmarshal by recreating the original buffer.
+ pbd := new(GoTest)
+ err = Unmarshal(bytes, pbd)
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ t.Fatalf("unmarshal err = %v, want *RequiredNotSetError", err)
+ o.DebugPrint("", bytes)
+ t.Fatalf("string = %s", expected)
+ }
+ if strings.Index(err.Error(), "RequiredField.{Unknown}") < 0 {
+ t.Errorf("unmarshal wrong err msg: %v", err)
+ }
+ bytes, err = Marshal(pbd)
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ t.Errorf("marshal-2 err = %v, want *RequiredNotSetError", err)
+ o.DebugPrint("", bytes)
+ t.Fatalf("string = %s", expected)
+ }
+ if strings.Index(err.Error(), "RequiredField.Label") < 0 {
+ t.Errorf("marshal-2 wrong err msg: %v", err)
+ }
+ if !equal(bytes, expected, t) {
+ o.DebugPrint("neq 2", bytes)
+ t.Fatalf("string = %s", expected)
+ }
+}
+
+func fuzzUnmarshal(t *testing.T, data []byte) {
+ defer func() {
+ if e := recover(); e != nil {
+ t.Errorf("These bytes caused a panic: %+v", data)
+ t.Logf("Stack:\n%s", debug.Stack())
+ t.FailNow()
+ }
+ }()
+
+ pb := new(MyMessage)
+ Unmarshal(data, pb)
+}
+
+func TestMapFieldMarshal(t *testing.T) {
+ m := &MessageWithMap{
+ NameMapping: map[int32]string{
+ 1: "Rob",
+ 4: "Ian",
+ 8: "Dave",
+ },
+ }
+ b, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+
+ // b should be the concatenation of these three byte sequences in some order.
+ parts := []string{
+ "\n\a\b\x01\x12\x03Rob",
+ "\n\a\b\x04\x12\x03Ian",
+ "\n\b\b\x08\x12\x04Dave",
+ }
+ ok := false
+ for i := range parts {
+ for j := range parts {
+ if j == i {
+ continue
+ }
+ for k := range parts {
+ if k == i || k == j {
+ continue
+ }
+ try := parts[i] + parts[j] + parts[k]
+ if bytes.Equal(b, []byte(try)) {
+ ok = true
+ break
+ }
+ }
+ }
+ }
+ if !ok {
+ t.Fatalf("Incorrect Marshal output.\n got %q\nwant %q (or a permutation of that)", b, parts[0]+parts[1]+parts[2])
+ }
+ t.Logf("FYI b: %q", b)
+
+ (new(Buffer)).DebugPrint("Dump of b", b)
+}
+
+func TestMapFieldRoundTrips(t *testing.T) {
+ m := &MessageWithMap{
+ NameMapping: map[int32]string{
+ 1: "Rob",
+ 4: "Ian",
+ 8: "Dave",
+ },
+ MsgMapping: map[int64]*FloatingPoint{
+ 0x7001: {F: Float64(2.0)},
+ },
+ ByteMapping: map[bool][]byte{
+ false: []byte("that's not right!"),
+ true: []byte("aye, 'tis true!"),
+ },
+ }
+ b, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ t.Logf("FYI b: %q", b)
+ m2 := new(MessageWithMap)
+ if err := Unmarshal(b, m2); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ for _, pair := range [][2]interface{}{
+ {m.NameMapping, m2.NameMapping},
+ {m.MsgMapping, m2.MsgMapping},
+ {m.ByteMapping, m2.ByteMapping},
+ } {
+ if !reflect.DeepEqual(pair[0], pair[1]) {
+ t.Errorf("Map did not survive a round trip.\ninitial: %v\n final: %v", pair[0], pair[1])
+ }
+ }
+}
+
+// Benchmarks
+
+func testMsg() *GoTest {
+ pb := initGoTest(true)
+ const N = 1000 // Internally the library starts much smaller.
+ pb.F_Int32Repeated = make([]int32, N)
+ pb.F_DoubleRepeated = make([]float64, N)
+ for i := 0; i < N; i++ {
+ pb.F_Int32Repeated[i] = int32(i)
+ pb.F_DoubleRepeated[i] = float64(i)
+ }
+ return pb
+}
+
+func bytesMsg() *GoTest {
+ pb := initGoTest(true)
+ buf := make([]byte, 4000)
+ for i := range buf {
+ buf[i] = byte(i)
+ }
+ pb.F_BytesDefaulted = buf
+ return pb
+}
+
+func benchmarkMarshal(b *testing.B, pb Message, marshal func(Message) ([]byte, error)) {
+ d, _ := marshal(pb)
+ b.SetBytes(int64(len(d)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ marshal(pb)
+ }
+}
+
+func benchmarkBufferMarshal(b *testing.B, pb Message) {
+ p := NewBuffer(nil)
+ benchmarkMarshal(b, pb, func(pb0 Message) ([]byte, error) {
+ p.Reset()
+ err := p.Marshal(pb0)
+ return p.Bytes(), err
+ })
+}
+
+func benchmarkSize(b *testing.B, pb Message) {
+ benchmarkMarshal(b, pb, func(pb0 Message) ([]byte, error) {
+ Size(pb)
+ return nil, nil
+ })
+}
+
+func newOf(pb Message) Message {
+ in := reflect.ValueOf(pb)
+ if in.IsNil() {
+ return pb
+ }
+ return reflect.New(in.Type().Elem()).Interface().(Message)
+}
+
+func benchmarkUnmarshal(b *testing.B, pb Message, unmarshal func([]byte, Message) error) {
+ d, _ := Marshal(pb)
+ b.SetBytes(int64(len(d)))
+ pbd := newOf(pb)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ unmarshal(d, pbd)
+ }
+}
+
+func benchmarkBufferUnmarshal(b *testing.B, pb Message) {
+ p := NewBuffer(nil)
+ benchmarkUnmarshal(b, pb, func(d []byte, pb0 Message) error {
+ p.SetBuf(d)
+ return p.Unmarshal(pb0)
+ })
+}
+
+// Benchmark{Marshal,BufferMarshal,Size,Unmarshal,BufferUnmarshal}{,Bytes}
+
+func BenchmarkMarshal(b *testing.B) {
+ benchmarkMarshal(b, testMsg(), Marshal)
+}
+
+func BenchmarkBufferMarshal(b *testing.B) {
+ benchmarkBufferMarshal(b, testMsg())
+}
+
+func BenchmarkSize(b *testing.B) {
+ benchmarkSize(b, testMsg())
+}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ benchmarkUnmarshal(b, testMsg(), Unmarshal)
+}
+
+func BenchmarkBufferUnmarshal(b *testing.B) {
+ benchmarkBufferUnmarshal(b, testMsg())
+}
+
+func BenchmarkMarshalBytes(b *testing.B) {
+ benchmarkMarshal(b, bytesMsg(), Marshal)
+}
+
+func BenchmarkBufferMarshalBytes(b *testing.B) {
+ benchmarkBufferMarshal(b, bytesMsg())
+}
+
+func BenchmarkSizeBytes(b *testing.B) {
+ benchmarkSize(b, bytesMsg())
+}
+
+func BenchmarkUnmarshalBytes(b *testing.B) {
+ benchmarkUnmarshal(b, bytesMsg(), Unmarshal)
+}
+
+func BenchmarkBufferUnmarshalBytes(b *testing.B) {
+ benchmarkBufferUnmarshal(b, bytesMsg())
+}
+
+func BenchmarkUnmarshalUnrecognizedFields(b *testing.B) {
+ b.StopTimer()
+ pb := initGoTestField()
+ skip := &GoSkipTest{
+ SkipInt32: Int32(32),
+ SkipFixed32: Uint32(3232),
+ SkipFixed64: Uint64(6464),
+ SkipString: String("skipper"),
+ Skipgroup: &GoSkipTest_SkipGroup{
+ GroupInt32: Int32(75),
+ GroupString: String("wxyz"),
+ },
+ }
+
+ pbd := new(GoTestField)
+ p := NewBuffer(nil)
+ p.Marshal(pb)
+ p.Marshal(skip)
+ p2 := NewBuffer(nil)
+
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ p2.SetBuf(p.Bytes())
+ p2.Unmarshal(pbd)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/clone.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/clone.go
new file mode 100644
index 000000000..ba3e94bb9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/clone.go
@@ -0,0 +1,202 @@
+// 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: MessageSet and 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) {
+ 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))
+ }
+
+ if emIn, ok := in.Addr().Interface().(extensionsMap); ok {
+ emOut := out.Addr().Interface().(extensionsMap)
+ mergeExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
+ } else if emIn, ok := in.Addr().Interface().(extensionsBytes); ok {
+ emOut := out.Addr().Interface().(extensionsBytes)
+ bIn := emIn.GetExtensions()
+ bOut := emOut.GetExtensions()
+ *bOut = append(*bOut, *bIn...)
+ }
+
+ 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...))
+ }
+}
+
+func mergeAny(out, in reflect.Value) {
+ 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:
+ out.Set(in)
+ 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))
+ 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())
+ case reflect.Slice:
+ if in.IsNil() {
+ return
+ }
+ if in.Type().Elem().Kind() == reflect.Uint8 {
+ // []byte is a scalar bytes field, not a repeated field.
+ // 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))
+ 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))
+ 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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/clone_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/clone_test.go
new file mode 100644
index 000000000..7115418a0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/clone_test.go
@@ -0,0 +1,226 @@
+// 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.
+
+package proto_test
+
+import (
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+var cloneTestMessage = &pb.MyMessage{
+ Count: proto.Int32(42),
+ Name: proto.String("Dave"),
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Inner: &pb.InnerMessage{
+ Host: proto.String("niles"),
+ Port: proto.Int32(9099),
+ Connected: proto.Bool(true),
+ },
+ Others: []*pb.OtherMessage{
+ {
+ Value: []byte("some bytes"),
+ },
+ },
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(6),
+ },
+ RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
+}
+
+func init() {
+ ext := &pb.Ext{
+ Data: proto.String("extension"),
+ }
+ if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil {
+ panic("SetExtension: " + err.Error())
+ }
+}
+
+func TestClone(t *testing.T) {
+ m := proto.Clone(cloneTestMessage).(*pb.MyMessage)
+ if !proto.Equal(m, cloneTestMessage) {
+ t.Errorf("Clone(%v) = %v", cloneTestMessage, m)
+ }
+
+ // Verify it was a deep copy.
+ *m.Inner.Port++
+ if proto.Equal(m, cloneTestMessage) {
+ t.Error("Mutating clone changed the original")
+ }
+ // Byte fields and repeated fields should be copied.
+ if &m.Pet[0] == &cloneTestMessage.Pet[0] {
+ t.Error("Pet: repeated field not copied")
+ }
+ if &m.Others[0] == &cloneTestMessage.Others[0] {
+ t.Error("Others: repeated field not copied")
+ }
+ if &m.Others[0].Value[0] == &cloneTestMessage.Others[0].Value[0] {
+ t.Error("Others[0].Value: bytes field not copied")
+ }
+ if &m.RepBytes[0] == &cloneTestMessage.RepBytes[0] {
+ t.Error("RepBytes: repeated field not copied")
+ }
+ if &m.RepBytes[0][0] == &cloneTestMessage.RepBytes[0][0] {
+ t.Error("RepBytes[0]: bytes field not copied")
+ }
+}
+
+func TestCloneNil(t *testing.T) {
+ var m *pb.MyMessage
+ if c := proto.Clone(m); !proto.Equal(m, c) {
+ t.Errorf("Clone(%v) = %v", m, c)
+ }
+}
+
+var mergeTests = []struct {
+ src, dst, want proto.Message
+}{
+ {
+ src: &pb.MyMessage{
+ Count: proto.Int32(42),
+ },
+ dst: &pb.MyMessage{
+ Name: proto.String("Dave"),
+ },
+ want: &pb.MyMessage{
+ Count: proto.Int32(42),
+ Name: proto.String("Dave"),
+ },
+ },
+ {
+ src: &pb.MyMessage{
+ Inner: &pb.InnerMessage{
+ Host: proto.String("hey"),
+ Connected: proto.Bool(true),
+ },
+ Pet: []string{"horsey"},
+ Others: []*pb.OtherMessage{
+ {
+ Value: []byte("some bytes"),
+ },
+ },
+ },
+ dst: &pb.MyMessage{
+ Inner: &pb.InnerMessage{
+ Host: proto.String("niles"),
+ Port: proto.Int32(9099),
+ },
+ Pet: []string{"bunny", "kitty"},
+ Others: []*pb.OtherMessage{
+ {
+ Key: proto.Int64(31415926535),
+ },
+ {
+ // Explicitly test a src=nil field
+ Inner: nil,
+ },
+ },
+ },
+ want: &pb.MyMessage{
+ Inner: &pb.InnerMessage{
+ Host: proto.String("hey"),
+ Connected: proto.Bool(true),
+ Port: proto.Int32(9099),
+ },
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Others: []*pb.OtherMessage{
+ {
+ Key: proto.Int64(31415926535),
+ },
+ {},
+ {
+ Value: []byte("some bytes"),
+ },
+ },
+ },
+ },
+ {
+ src: &pb.MyMessage{
+ RepBytes: [][]byte{[]byte("wow")},
+ },
+ dst: &pb.MyMessage{
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(6),
+ },
+ RepBytes: [][]byte{[]byte("sham")},
+ },
+ want: &pb.MyMessage{
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(6),
+ },
+ RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
+ },
+ },
+ // Check that a scalar bytes field replaces rather than appends.
+ {
+ src: &pb.OtherMessage{Value: []byte("foo")},
+ dst: &pb.OtherMessage{Value: []byte("bar")},
+ want: &pb.OtherMessage{Value: []byte("foo")},
+ },
+ {
+ src: &pb.MessageWithMap{
+ NameMapping: map[int32]string{6: "Nigel"},
+ MsgMapping: map[int64]*pb.FloatingPoint{
+ 0x4001: {F: proto.Float64(2.0)},
+ },
+ ByteMapping: map[bool][]byte{true: []byte("wowsa")},
+ },
+ dst: &pb.MessageWithMap{
+ NameMapping: map[int32]string{
+ 6: "Bruce", // should be overwritten
+ 7: "Andrew",
+ },
+ },
+ want: &pb.MessageWithMap{
+ NameMapping: map[int32]string{
+ 6: "Nigel",
+ 7: "Andrew",
+ },
+ MsgMapping: map[int64]*pb.FloatingPoint{
+ 0x4001: {F: proto.Float64(2.0)},
+ },
+ ByteMapping: map[bool][]byte{true: []byte("wowsa")},
+ },
+ },
+}
+
+func TestMerge(t *testing.T) {
+ for _, m := range mergeTests {
+ got := proto.Clone(m.dst)
+ proto.Merge(got, m.src)
+ if !proto.Equal(got, m.want) {
+ t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/decode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/decode.go
new file mode 100644
index 000000000..8bba59414
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/decode.go
@@ -0,0 +1,826 @@
+// 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")
+
+// 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) {
+ // x, n already 0
+ 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
+}
+
+// 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) {
+ // x, err already 0
+
+ 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
+}
+
+// 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)
+}
+
+// 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.
+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 {
+ 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 := structPointer_Interface(base, st).(extendableProto); isExtensionField(e, int32(tag)) {
+ if err = o.skip(st, tag, wire); err == nil {
+ if ee, ok := e.(extensionsMap); ok {
+ ext := ee.ExtensionMap()[int32(tag)] // may be missing
+ ext.enc = append(ext.enc, o.buf[oi:o.index]...)
+ ee.ExtensionMap()[int32(tag)] = ext
+ } else if ee, ok := e.(extensionsBytes); ok {
+ ext := ee.GetExtensions()
+ *ext = append(*ext, o.buf[oi:o.index]...)
+ }
+ }
+ 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
+
+ y := *v
+ for i := 0; i < nb; i++ {
+ 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_Map(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])
+ }
+ }
+
+ v.SetMapIndex(keyptr.Elem(), valptr.Elem())
+ 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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/decode_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/decode_gogo.go
new file mode 100644
index 000000000..6a77aad76
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/decode_gogo.go
@@ -0,0 +1,175 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+import (
+ "reflect"
+)
+
+// Decode a reference to a struct pointer.
+func (o *Buffer) dec_ref_struct_message(p *Properties, base structPointer) (err error) {
+ raw, e := o.DecodeRawBytes(false)
+ if e != nil {
+ return e
+ }
+
+ // If the object can unmarshal itself, let it.
+ if p.isUnmarshaler {
+ panic("not supported, since this is a pointer receiver")
+ }
+
+ obuf := o.buf
+ oi := o.index
+ o.buf = raw
+ o.index = 0
+
+ bas := structPointer_FieldPointer(base, p.field)
+
+ err = o.unmarshalType(p.stype, p.sprop, false, bas)
+ o.buf = obuf
+ o.index = oi
+
+ return err
+}
+
+// Decode a slice of references to struct pointers ([]struct).
+func (o *Buffer) dec_slice_ref_struct(p *Properties, is_group bool, base structPointer) error {
+ newBas := appendStructPointer(base, p.field, p.sstype)
+
+ if is_group {
+ panic("not supported, maybe in future, if requested.")
+ }
+
+ raw, err := o.DecodeRawBytes(false)
+ if err != nil {
+ return err
+ }
+
+ // If the object can unmarshal itself, let it.
+ if p.isUnmarshaler {
+ panic("not supported, since this is not a pointer receiver.")
+ }
+
+ obuf := o.buf
+ oi := o.index
+ o.buf = raw
+ o.index = 0
+
+ err = o.unmarshalType(p.stype, p.sprop, is_group, newBas)
+
+ o.buf = obuf
+ o.index = oi
+
+ return err
+}
+
+// Decode a slice of references to struct pointers.
+func (o *Buffer) dec_slice_ref_struct_message(p *Properties, base structPointer) error {
+ return o.dec_slice_ref_struct(p, false, base)
+}
+
+func setPtrCustomType(base structPointer, f field, v interface{}) {
+ if v == nil {
+ return
+ }
+ structPointer_SetStructPointer(base, f, structPointer(reflect.ValueOf(v).Pointer()))
+}
+
+func setCustomType(base structPointer, f field, value interface{}) {
+ if value == nil {
+ return
+ }
+ v := reflect.ValueOf(value).Elem()
+ t := reflect.TypeOf(value).Elem()
+ kind := t.Kind()
+ switch kind {
+ case reflect.Slice:
+ slice := reflect.MakeSlice(t, v.Len(), v.Cap())
+ reflect.Copy(slice, v)
+ oldHeader := structPointer_GetSliceHeader(base, f)
+ oldHeader.Data = slice.Pointer()
+ oldHeader.Len = v.Len()
+ oldHeader.Cap = v.Cap()
+ default:
+ l := 1
+ size := reflect.TypeOf(value).Elem().Size()
+ if kind == reflect.Array {
+ l = reflect.TypeOf(value).Elem().Len()
+ size = reflect.TypeOf(value).Size()
+ }
+ total := int(size) * l
+ structPointer_Copy(toStructPointer(reflect.ValueOf(value)), structPointer_Add(base, f), total)
+ }
+}
+
+func (o *Buffer) dec_custom_bytes(p *Properties, base structPointer) error {
+ b, err := o.DecodeRawBytes(true)
+ if err != nil {
+ return err
+ }
+ i := reflect.New(p.ctype.Elem()).Interface()
+ custom := (i).(Unmarshaler)
+ if err := custom.Unmarshal(b); err != nil {
+ return err
+ }
+ setPtrCustomType(base, p.field, custom)
+ return nil
+}
+
+func (o *Buffer) dec_custom_ref_bytes(p *Properties, base structPointer) error {
+ b, err := o.DecodeRawBytes(true)
+ if err != nil {
+ return err
+ }
+ i := reflect.New(p.ctype).Interface()
+ custom := (i).(Unmarshaler)
+ if err := custom.Unmarshal(b); err != nil {
+ return err
+ }
+ if custom != nil {
+ setCustomType(base, p.field, custom)
+ }
+ return nil
+}
+
+// Decode a slice of bytes ([]byte) into a slice of custom types.
+func (o *Buffer) dec_custom_slice_bytes(p *Properties, base structPointer) error {
+ b, err := o.DecodeRawBytes(true)
+ if err != nil {
+ return err
+ }
+ i := reflect.New(p.ctype.Elem()).Interface()
+ custom := (i).(Unmarshaler)
+ if err := custom.Unmarshal(b); err != nil {
+ return err
+ }
+ newBas := appendStructPointer(base, p.field, p.ctype)
+
+ setCustomType(newBas, 0, custom)
+
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/encode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/encode.go
new file mode 100644
index 000000000..d1abc3328
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/encode.go
@@ -0,0 +1,1288 @@
+// 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")
+
+ // ErrNil is the error returned if Marshal is called with nil.
+ ErrNil = errors.New("proto: Marshal called with nil")
+)
+
+// 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
+
+// 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
+}
+
+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(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+
+func sizeZigzag64(x uint64) int {
+ return sizeVarint(uint64((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)
+ var state errorState
+ if err != nil && !state.shouldContinue(err, nil) {
+ return nil, err
+ }
+ if p.buf == nil && err == nil {
+ // Return a non-nil slice on success.
+ return []byte{}, nil
+ }
+ return p.buf, 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()
+ if err != nil {
+ return err
+ }
+ p.buf = append(p.buf, data...)
+ return nil
+ }
+
+ 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++
+ }
+
+ 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++
+ }
+
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 == "" {
+ 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 nil
+ }
+
+ 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 {
+ 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 {
+ 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 += len(p.tagcode)
+ 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 {
+ v := *structPointer_ExtMap(base, p.field)
+ if err := encodeExtensionMap(v); err != nil {
+ return err
+ }
+ // 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 sizeExtensionMap(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_Map(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 {
+ return err
+ }
+ return nil
+ }
+
+ keys := v.MapKeys()
+ sort.Sort(mapKeys(keys))
+ for _, key := range keys {
+ 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_Map(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
+ }
+ }
+ }
+ }
+
+ // Add unrecognized fields at the end.
+ if prop.unrecField.IsValid() {
+ v := *structPointer_Bytes(base, prop.unrecField)
+ 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)
+ }
+
+ 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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/encode_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/encode_gogo.go
new file mode 100644
index 000000000..f77cfb1ee
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/encode_gogo.go
@@ -0,0 +1,354 @@
+// Extensions for Protocol Buffers to create more go like structures.
+//
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2010 The Go Authors. All rights reserved.
+// http://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
+
+import (
+ "reflect"
+)
+
+func NewRequiredNotSetError(field string) *RequiredNotSetError {
+ return &RequiredNotSetError{field}
+}
+
+type Sizer interface {
+ Size() int
+}
+
+func (o *Buffer) enc_ext_slice_byte(p *Properties, base structPointer) error {
+ s := *structPointer_Bytes(base, p.field)
+ if s == nil {
+ return ErrNil
+ }
+ o.buf = append(o.buf, s...)
+ return nil
+}
+
+func size_ext_slice_byte(p *Properties, base structPointer) (n int) {
+ s := *structPointer_Bytes(base, p.field)
+ if s == nil {
+ return 0
+ }
+ n += len(s)
+ return
+}
+
+// Encode a reference to bool pointer.
+func (o *Buffer) enc_ref_bool(p *Properties, base structPointer) error {
+ v := *structPointer_BoolVal(base, p.field)
+ x := 0
+ if v {
+ x = 1
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func size_ref_bool(p *Properties, base structPointer) int {
+ return len(p.tagcode) + 1 // each bool takes exactly one byte
+}
+
+// Encode a reference to int32 pointer.
+func (o *Buffer) enc_ref_int32(p *Properties, base structPointer) error {
+ v := structPointer_Word32Val(base, p.field)
+ x := int32(word32Val_Get(v))
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func size_ref_int32(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word32Val(base, p.field)
+ x := int32(word32Val_Get(v))
+ n += len(p.tagcode)
+ n += p.valSize(uint64(x))
+ return
+}
+
+func (o *Buffer) enc_ref_uint32(p *Properties, base structPointer) error {
+ v := structPointer_Word32Val(base, p.field)
+ x := word32Val_Get(v)
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, uint64(x))
+ return nil
+}
+
+func size_ref_uint32(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word32Val(base, p.field)
+ x := word32Val_Get(v)
+ n += len(p.tagcode)
+ n += p.valSize(uint64(x))
+ return
+}
+
+// Encode a reference to an int64 pointer.
+func (o *Buffer) enc_ref_int64(p *Properties, base structPointer) error {
+ v := structPointer_Word64Val(base, p.field)
+ x := word64Val_Get(v)
+ o.buf = append(o.buf, p.tagcode...)
+ p.valEnc(o, x)
+ return nil
+}
+
+func size_ref_int64(p *Properties, base structPointer) (n int) {
+ v := structPointer_Word64Val(base, p.field)
+ x := word64Val_Get(v)
+ n += len(p.tagcode)
+ n += p.valSize(x)
+ return
+}
+
+// Encode a reference to a string pointer.
+func (o *Buffer) enc_ref_string(p *Properties, base structPointer) error {
+ v := *structPointer_StringVal(base, p.field)
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeStringBytes(v)
+ return nil
+}
+
+func size_ref_string(p *Properties, base structPointer) (n int) {
+ v := *structPointer_StringVal(base, p.field)
+ n += len(p.tagcode)
+ n += sizeStringBytes(v)
+ return
+}
+
+// Encode a reference to a message struct.
+func (o *Buffer) enc_ref_struct_message(p *Properties, base structPointer) error {
+ var state errorState
+ structp := structPointer_GetRefStructPointer(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 nil
+ }
+
+ o.buf = append(o.buf, p.tagcode...)
+ return o.enc_len_struct(p.sprop, structp, &state)
+}
+
+//TODO this is only copied, please fix this
+func size_ref_struct_message(p *Properties, base structPointer) int {
+ structp := structPointer_GetRefStructPointer(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 slice of references to message struct pointers ([]struct).
+func (o *Buffer) enc_slice_ref_struct_message(p *Properties, base structPointer) error {
+ var state errorState
+ ss := structPointer_GetStructPointer(base, p.field)
+ ss1 := structPointer_GetRefStructPointer(ss, field(0))
+ size := p.stype.Size()
+ l := structPointer_Len(base, p.field)
+ for i := 0; i < l; i++ {
+ structp := structPointer_Add(ss1, field(uintptr(i)*size))
+ 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
+}
+
+//TODO this is only copied, please fix this
+func size_slice_ref_struct_message(p *Properties, base structPointer) (n int) {
+ ss := structPointer_GetStructPointer(base, p.field)
+ ss1 := structPointer_GetRefStructPointer(ss, field(0))
+ size := p.stype.Size()
+ l := structPointer_Len(base, p.field)
+ n += l * len(p.tagcode)
+ for i := 0; i < l; i++ {
+ structp := structPointer_Add(ss1, field(uintptr(i)*size))
+ 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 += len(p.tagcode)
+ n += sizeRawBytes(data)
+ continue
+ }
+
+ n0 := size_struct(p.sprop, structp)
+ n1 := sizeVarint(uint64(n0)) // size of encoded length
+ n += n0 + n1
+ }
+ return
+}
+
+func (o *Buffer) enc_custom_bytes(p *Properties, base structPointer) error {
+ i := structPointer_InterfaceRef(base, p.field, p.ctype)
+ if i == nil {
+ return ErrNil
+ }
+ custom := i.(Marshaler)
+ data, err := custom.Marshal()
+ if err != nil {
+ return err
+ }
+ if data == nil {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(data)
+ return nil
+}
+
+func size_custom_bytes(p *Properties, base structPointer) (n int) {
+ n += len(p.tagcode)
+ i := structPointer_InterfaceRef(base, p.field, p.ctype)
+ if i == nil {
+ return 0
+ }
+ custom := i.(Marshaler)
+ data, _ := custom.Marshal()
+ n += sizeRawBytes(data)
+ return
+}
+
+func (o *Buffer) enc_custom_ref_bytes(p *Properties, base structPointer) error {
+ custom := structPointer_InterfaceAt(base, p.field, p.ctype).(Marshaler)
+ data, err := custom.Marshal()
+ if err != nil {
+ return err
+ }
+ if data == nil {
+ return ErrNil
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(data)
+ return nil
+}
+
+func size_custom_ref_bytes(p *Properties, base structPointer) (n int) {
+ n += len(p.tagcode)
+ i := structPointer_InterfaceAt(base, p.field, p.ctype)
+ if i == nil {
+ return 0
+ }
+ custom := i.(Marshaler)
+ data, _ := custom.Marshal()
+ n += sizeRawBytes(data)
+ return
+}
+
+func (o *Buffer) enc_custom_slice_bytes(p *Properties, base structPointer) error {
+ inter := structPointer_InterfaceRef(base, p.field, p.ctype)
+ if inter == nil {
+ return ErrNil
+ }
+ slice := reflect.ValueOf(inter)
+ l := slice.Len()
+ for i := 0; i < l; i++ {
+ v := slice.Index(i)
+ custom := v.Interface().(Marshaler)
+ data, err := custom.Marshal()
+ if err != nil {
+ return err
+ }
+ o.buf = append(o.buf, p.tagcode...)
+ o.EncodeRawBytes(data)
+ }
+ return nil
+}
+
+func size_custom_slice_bytes(p *Properties, base structPointer) (n int) {
+ inter := structPointer_InterfaceRef(base, p.field, p.ctype)
+ if inter == nil {
+ return 0
+ }
+ slice := reflect.ValueOf(inter)
+ l := slice.Len()
+ n += l * len(p.tagcode)
+ for i := 0; i < l; i++ {
+ v := slice.Index(i)
+ custom := v.Interface().(Marshaler)
+ data, _ := custom.Marshal()
+ n += sizeRawBytes(data)
+ }
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/equal.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/equal.go
new file mode 100644
index 000000000..d8673a3e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/equal.go
@@ -0,0 +1,256 @@
+// 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.
+// TODO: MessageSet.
+
+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.
+ - Two repeated fields are equal iff their lengths are the same,
+ and their corresponding elements are equal (a "bytes" field,
+ although represented by []byte, is not a repeated field)
+ - 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.
+ - 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 {
+ 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) {
+ return false
+ }
+ }
+
+ if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
+ em2 := v2.FieldByName("XXX_extensions")
+ if !equalExtensions(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.
+func equalAny(v1, v2 reflect.Value) 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.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) {
+ return false
+ }
+ }
+ return true
+ case reflect.Ptr:
+ return equalAny(v1.Elem(), v2.Elem())
+ case reflect.Slice:
+ if v1.Type().Elem().Kind() == reflect.Uint8 {
+ // short circuit: []byte
+ 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)) {
+ 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.
+// em1 and em2 are extension maps.
+func equalExtensions(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)) {
+ 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)) {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/equal_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/equal_test.go
new file mode 100644
index 000000000..56b09e55d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/equal_test.go
@@ -0,0 +1,191 @@
+// 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.
+
+package proto_test
+
+import (
+ "testing"
+
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+// Four identical base messages.
+// The init function adds extensions to some of them.
+var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)}
+var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)}
+var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)}
+var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)}
+
+// Two messages with non-message extensions.
+var messageWithInt32Extension1 = &pb.MyMessage{Count: Int32(8)}
+var messageWithInt32Extension2 = &pb.MyMessage{Count: Int32(8)}
+
+func init() {
+ ext1 := &pb.Ext{Data: String("Kirk")}
+ ext2 := &pb.Ext{Data: String("Picard")}
+
+ // messageWithExtension1a has ext1, but never marshals it.
+ if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil {
+ panic("SetExtension on 1a failed: " + err.Error())
+ }
+
+ // messageWithExtension1b is the unmarshaled form of messageWithExtension1a.
+ if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil {
+ panic("SetExtension on 1b failed: " + err.Error())
+ }
+ buf, err := Marshal(messageWithExtension1b)
+ if err != nil {
+ panic("Marshal of 1b failed: " + err.Error())
+ }
+ messageWithExtension1b.Reset()
+ if err := Unmarshal(buf, messageWithExtension1b); err != nil {
+ panic("Unmarshal of 1b failed: " + err.Error())
+ }
+
+ // messageWithExtension2 has ext2.
+ if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil {
+ panic("SetExtension on 2 failed: " + err.Error())
+ }
+
+ if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(23)); err != nil {
+ panic("SetExtension on Int32-1 failed: " + err.Error())
+ }
+ if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(24)); err != nil {
+ panic("SetExtension on Int32-2 failed: " + err.Error())
+ }
+}
+
+var EqualTests = []struct {
+ desc string
+ a, b Message
+ exp bool
+}{
+ {"different types", &pb.GoEnum{}, &pb.GoTestField{}, false},
+ {"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true},
+ {"nil vs nil", nil, nil, true},
+ {"typed nil vs typed nil", (*pb.GoEnum)(nil), (*pb.GoEnum)(nil), true},
+ {"typed nil vs empty", (*pb.GoEnum)(nil), &pb.GoEnum{}, false},
+ {"different typed nil", (*pb.GoEnum)(nil), (*pb.GoTestField)(nil), false},
+
+ {"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false},
+ {"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false},
+ {"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false},
+ {"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true},
+
+ {"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false},
+ {"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false},
+ {"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false},
+ {"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true},
+ {"repeated, nil equal nil", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: nil}, true},
+ {"repeated, nil equal empty", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: []int32{}}, true},
+ {"repeated, empty equal nil", &pb.GoTest{F_Int32Repeated: []int32{}}, &pb.GoTest{F_Int32Repeated: nil}, true},
+
+ {
+ "nested, different",
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}},
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}},
+ false,
+ },
+ {
+ "nested, equal",
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
+ true,
+ },
+
+ {"bytes", &pb.OtherMessage{Value: []byte("foo")}, &pb.OtherMessage{Value: []byte("foo")}, true},
+ {"bytes, empty", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: []byte{}}, true},
+ {"bytes, empty vs nil", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: nil}, false},
+ {
+ "repeated bytes",
+ &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
+ &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
+ true,
+ },
+
+ {"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false},
+ {"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true},
+ {"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false},
+
+ {"int32 extension vs. itself", messageWithInt32Extension1, messageWithInt32Extension1, true},
+ {"int32 extension vs. a different int32", messageWithInt32Extension1, messageWithInt32Extension2, false},
+
+ {
+ "message with group",
+ &pb.MyMessage{
+ Count: Int32(1),
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: Int32(5),
+ },
+ },
+ &pb.MyMessage{
+ Count: Int32(1),
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: Int32(5),
+ },
+ },
+ true,
+ },
+
+ {
+ "map same",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ true,
+ },
+ {
+ "map different entry",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}},
+ false,
+ },
+ {
+ "map different key only",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}},
+ false,
+ },
+ {
+ "map different value only",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}},
+ false,
+ },
+}
+
+func TestEqual(t *testing.T) {
+ for _, tc := range EqualTests {
+ if res := Equal(tc.a, tc.b); res != tc.exp {
+ t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions.go
new file mode 100644
index 000000000..50e04e59f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions.go
@@ -0,0 +1,481 @@
+// 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 that may be extended.
+type extendableProto interface {
+ Message
+ ExtensionRangeArray() []ExtensionRange
+}
+
+type extensionsMap interface {
+ extendableProto
+ ExtensionMap() map[int32]Extension
+}
+
+type extensionsBytes interface {
+ extendableProto
+ GetExtensions() *[]byte
+}
+
+var extendableProtoType = reflect.TypeOf((*extendableProto)(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
+}
+
+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 extendableProto, id int32, b []byte) {
+ if ebase, ok := base.(extensionsMap); ok {
+ ebase.ExtensionMap()[id] = Extension{enc: b}
+ } else if ebase, ok := base.(extensionsBytes); ok {
+ clearExtension(base, id)
+ ext := ebase.GetExtensions()
+ *ext = append(*ext, b...)
+ } else {
+ panic("unreachable")
+ }
+}
+
+// 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 {
+ // Check the extended type.
+ if a, b := reflect.TypeOf(pb), 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
+}
+
+// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
+func encodeExtensionMap(m map[int32]Extension) error {
+ for k, e := range m {
+ err := encodeExtension(&e)
+ if err != nil {
+ return err
+ }
+ m[k] = e
+ }
+ return nil
+}
+
+func encodeExtension(e *Extension) error {
+ if e.value == nil || e.desc == nil {
+ // Extension is only in its encoded form.
+ return nil
+ }
+ // 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
+ return nil
+}
+
+func sizeExtensionMap(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 extendableProto, extension *ExtensionDesc) bool {
+ // TODO: Check types, field numbers, etc.?
+ if epb, doki := pb.(extensionsMap); doki {
+ _, ok := epb.ExtensionMap()[extension.Field]
+ return ok
+ } else if epb, doki := pb.(extensionsBytes); doki {
+ ext := epb.GetExtensions()
+ buf := *ext
+ o := 0
+ for o < len(buf) {
+ tag, n := DecodeVarint(buf[o:])
+ fieldNum := int32(tag >> 3)
+ if int32(fieldNum) == extension.Field {
+ return true
+ }
+ wireType := int(tag & 0x7)
+ o += n
+ l, err := size(buf[o:], wireType)
+ if err != nil {
+ return false
+ }
+ o += l
+ }
+ return false
+ }
+ panic("unreachable")
+}
+
+func deleteExtension(pb extensionsBytes, theFieldNum int32, offset int) int {
+ ext := pb.GetExtensions()
+ for offset < len(*ext) {
+ tag, n1 := DecodeVarint((*ext)[offset:])
+ fieldNum := int32(tag >> 3)
+ wireType := int(tag & 0x7)
+ n2, err := size((*ext)[offset+n1:], wireType)
+ if err != nil {
+ panic(err)
+ }
+ newOffset := offset + n1 + n2
+ if fieldNum == theFieldNum {
+ *ext = append((*ext)[:offset], (*ext)[newOffset:]...)
+ return offset
+ }
+ offset = newOffset
+ }
+ return -1
+}
+
+func clearExtension(pb extendableProto, fieldNum int32) {
+ if epb, doki := pb.(extensionsMap); doki {
+ delete(epb.ExtensionMap(), fieldNum)
+ } else if epb, doki := pb.(extensionsBytes); doki {
+ offset := 0
+ for offset != -1 {
+ offset = deleteExtension(epb, fieldNum, offset)
+ }
+ } else {
+ panic("unreachable")
+ }
+}
+
+// ClearExtension removes the given extension from pb.
+func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
+ // TODO: Check types, field numbers, etc.?
+ clearExtension(pb, extension.Field)
+}
+
+// GetExtension parses and returns the given extension of pb.
+// If the extension is not present it returns ErrMissingExtension.
+func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
+ if err := checkExtensionTypes(pb, extension); err != nil {
+ return nil, err
+ }
+
+ if epb, doki := pb.(extensionsMap); doki {
+ emap := epb.ExtensionMap()
+ e, ok := emap[extension.Field]
+ if !ok {
+ return nil, ErrMissingExtension
+ }
+ 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
+ } else if epb, doki := pb.(extensionsBytes); doki {
+ ext := epb.GetExtensions()
+ o := 0
+ for o < len(*ext) {
+ tag, n := DecodeVarint((*ext)[o:])
+ fieldNum := int32(tag >> 3)
+ wireType := int(tag & 0x7)
+ l, err := size((*ext)[o+n:], wireType)
+ if err != nil {
+ return nil, err
+ }
+ if int32(fieldNum) == extension.Field {
+ v, err := decodeExtension((*ext)[o:o+n+l], extension)
+ if err != nil {
+ return nil, err
+ }
+ return v, nil
+ }
+ o += n + l
+ }
+ }
+ panic("unreachable")
+}
+
+// decodeExtension decodes an extension encoded in b.
+func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
+ o := NewBuffer(b)
+
+ t := reflect.TypeOf(extension.ExtensionType)
+ rep := extension.repeated()
+
+ 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 !rep || 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 := pb.(extendableProto)
+ if !ok {
+ err = errors.New("proto: not an extendable proto")
+ return
+ }
+ 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
+}
+
+// SetExtension sets the specified extension of pb to the specified value.
+func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
+ if err := checkExtensionTypes(pb, 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)
+ }
+ return setExtension(pb, extension, value)
+}
+
+func setExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
+ if epb, doki := pb.(extensionsMap); doki {
+ epb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
+ } else if epb, doki := pb.(extensionsBytes); doki {
+ ClearExtension(pb, extension)
+ ext := epb.GetExtensions()
+ et := reflect.TypeOf(extension.ExtensionType)
+ props := extensionProperties(extension)
+ p := NewBuffer(nil)
+ x := reflect.New(et)
+ x.Elem().Set(reflect.ValueOf(value))
+ if err := props.enc(p, props, toStructPointer(x)); err != nil {
+ return err
+ }
+ *ext = append(*ext, p.buf...)
+ }
+ return nil
+}
+
+// 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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions_gogo.go
new file mode 100644
index 000000000..bd55fb68b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions_gogo.go
@@ -0,0 +1,221 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+)
+
+func GetBoolExtension(pb extendableProto, extension *ExtensionDesc, ifnotset bool) bool {
+ if reflect.ValueOf(pb).IsNil() {
+ return ifnotset
+ }
+ value, err := GetExtension(pb, extension)
+ if err != nil {
+ return ifnotset
+ }
+ if value == nil {
+ return ifnotset
+ }
+ if value.(*bool) == nil {
+ return ifnotset
+ }
+ return *(value.(*bool))
+}
+
+func (this *Extension) Equal(that *Extension) bool {
+ return bytes.Equal(this.enc, that.enc)
+}
+
+func SizeOfExtensionMap(m map[int32]Extension) (n int) {
+ return sizeExtensionMap(m)
+}
+
+type sortableMapElem struct {
+ field int32
+ ext Extension
+}
+
+func newSortableExtensionsFromMap(m map[int32]Extension) sortableExtensions {
+ s := make(sortableExtensions, 0, len(m))
+ for k, v := range m {
+ s = append(s, &sortableMapElem{field: k, ext: v})
+ }
+ return s
+}
+
+type sortableExtensions []*sortableMapElem
+
+func (this sortableExtensions) Len() int { return len(this) }
+
+func (this sortableExtensions) Swap(i, j int) { this[i], this[j] = this[j], this[i] }
+
+func (this sortableExtensions) Less(i, j int) bool { return this[i].field < this[j].field }
+
+func (this sortableExtensions) String() string {
+ sort.Sort(this)
+ ss := make([]string, len(this))
+ for i := range this {
+ ss[i] = fmt.Sprintf("%d: %v", this[i].field, this[i].ext)
+ }
+ return "map[" + strings.Join(ss, ",") + "]"
+}
+
+func StringFromExtensionsMap(m map[int32]Extension) string {
+ return newSortableExtensionsFromMap(m).String()
+}
+
+func StringFromExtensionsBytes(ext []byte) string {
+ m, err := BytesToExtensionsMap(ext)
+ if err != nil {
+ panic(err)
+ }
+ return StringFromExtensionsMap(m)
+}
+
+func EncodeExtensionMap(m map[int32]Extension, data []byte) (n int, err error) {
+ if err := encodeExtensionMap(m); err != nil {
+ return 0, err
+ }
+ keys := make([]int, 0, len(m))
+ for k := range m {
+ keys = append(keys, int(k))
+ }
+ sort.Ints(keys)
+ for _, k := range keys {
+ n += copy(data[n:], m[int32(k)].enc)
+ }
+ return n, nil
+}
+
+func GetRawExtension(m map[int32]Extension, id int32) ([]byte, error) {
+ if m[id].value == nil || m[id].desc == nil {
+ return m[id].enc, nil
+ }
+ if err := encodeExtensionMap(m); err != nil {
+ return nil, err
+ }
+ return m[id].enc, nil
+}
+
+func size(buf []byte, wire int) (int, error) {
+ switch wire {
+ case WireVarint:
+ _, n := DecodeVarint(buf)
+ return n, nil
+ case WireFixed64:
+ return 8, nil
+ case WireBytes:
+ v, n := DecodeVarint(buf)
+ return int(v) + n, nil
+ case WireFixed32:
+ return 4, nil
+ case WireStartGroup:
+ offset := 0
+ for {
+ u, n := DecodeVarint(buf[offset:])
+ fwire := int(u & 0x7)
+ offset += n
+ if fwire == WireEndGroup {
+ return offset, nil
+ }
+ s, err := size(buf[offset:], wire)
+ if err != nil {
+ return 0, err
+ }
+ offset += s
+ }
+ }
+ return 0, fmt.Errorf("proto: can't get size for unknown wire type %d", wire)
+}
+
+func BytesToExtensionsMap(buf []byte) (map[int32]Extension, error) {
+ m := make(map[int32]Extension)
+ i := 0
+ for i < len(buf) {
+ tag, n := DecodeVarint(buf[i:])
+ if n <= 0 {
+ return nil, fmt.Errorf("unable to decode varint")
+ }
+ fieldNum := int32(tag >> 3)
+ wireType := int(tag & 0x7)
+ l, err := size(buf[i+n:], wireType)
+ if err != nil {
+ return nil, err
+ }
+ end := i + int(l) + n
+ m[int32(fieldNum)] = Extension{enc: buf[i:end]}
+ i = end
+ }
+ return m, nil
+}
+
+func NewExtension(e []byte) Extension {
+ ee := Extension{enc: make([]byte, len(e))}
+ copy(ee.enc, e)
+ return ee
+}
+
+func (this Extension) GoString() string {
+ if this.enc == nil {
+ if err := encodeExtension(&this); err != nil {
+ panic(err)
+ }
+ }
+ return fmt.Sprintf("proto.NewExtension(%#v)", this.enc)
+}
+
+func SetUnsafeExtension(pb extendableProto, fieldNum int32, value interface{}) error {
+ typ := reflect.TypeOf(pb).Elem()
+ ext, ok := extensionMaps[typ]
+ if !ok {
+ return fmt.Errorf("proto: bad extended type; %s is not extendable", typ.String())
+ }
+ desc, ok := ext[fieldNum]
+ if !ok {
+ return errors.New("proto: bad extension number; not in declared ranges")
+ }
+ return setExtension(pb, desc, value)
+}
+
+func GetUnsafeExtension(pb extendableProto, fieldNum int32) (interface{}, error) {
+ typ := reflect.TypeOf(pb).Elem()
+ ext, ok := extensionMaps[typ]
+ if !ok {
+ return nil, fmt.Errorf("proto: bad extended type; %s is not extendable", typ.String())
+ }
+ desc, ok := ext[fieldNum]
+ if !ok {
+ return nil, fmt.Errorf("unregistered field number %d", fieldNum)
+ }
+ return GetExtension(pb, desc)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions_test.go
new file mode 100644
index 000000000..275516093
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/extensions_test.go
@@ -0,0 +1,153 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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_test
+
+import (
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+func TestGetExtensionsWithMissingExtensions(t *testing.T) {
+ msg := &pb.MyMessage{}
+ ext1 := &pb.Ext{}
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
+ t.Fatalf("Could not set ext1: %s", ext1)
+ }
+ exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{
+ pb.E_Ext_More,
+ pb.E_Ext_Text,
+ })
+ if err != nil {
+ t.Fatalf("GetExtensions() failed: %s", err)
+ }
+ if exts[0] != ext1 {
+ t.Errorf("ext1 not in returned extensions: %T %v", exts[0], exts[0])
+ }
+ if exts[1] != nil {
+ t.Errorf("ext2 in returned extensions: %T %v", exts[1], exts[1])
+ }
+}
+
+func TestGetExtensionStability(t *testing.T) {
+ check := func(m *pb.MyMessage) bool {
+ ext1, err := proto.GetExtension(m, pb.E_Ext_More)
+ if err != nil {
+ t.Fatalf("GetExtension() failed: %s", err)
+ }
+ ext2, err := proto.GetExtension(m, pb.E_Ext_More)
+ if err != nil {
+ t.Fatalf("GetExtension() failed: %s", err)
+ }
+ return ext1 == ext2
+ }
+ msg := &pb.MyMessage{Count: proto.Int32(4)}
+ ext0 := &pb.Ext{}
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil {
+ t.Fatalf("Could not set ext1: %s", ext0)
+ }
+ if !check(msg) {
+ t.Errorf("GetExtension() not stable before marshaling")
+ }
+ bb, err := proto.Marshal(msg)
+ if err != nil {
+ t.Fatalf("Marshal() failed: %s", err)
+ }
+ msg1 := &pb.MyMessage{}
+ err = proto.Unmarshal(bb, msg1)
+ if err != nil {
+ t.Fatalf("Unmarshal() failed: %s", err)
+ }
+ if !check(msg1) {
+ t.Errorf("GetExtension() not stable after unmarshaling")
+ }
+}
+
+func TestExtensionsRoundTrip(t *testing.T) {
+ msg := &pb.MyMessage{}
+ ext1 := &pb.Ext{
+ Data: proto.String("hi"),
+ }
+ ext2 := &pb.Ext{
+ Data: proto.String("there"),
+ }
+ exists := proto.HasExtension(msg, pb.E_Ext_More)
+ if exists {
+ t.Error("Extension More present unexpectedly")
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
+ t.Error(err)
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil {
+ t.Error(err)
+ }
+ e, err := proto.GetExtension(msg, pb.E_Ext_More)
+ if err != nil {
+ t.Error(err)
+ }
+ x, ok := e.(*pb.Ext)
+ if !ok {
+ t.Errorf("e has type %T, expected testdata.Ext", e)
+ } else if *x.Data != "there" {
+ t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x)
+ }
+ proto.ClearExtension(msg, pb.E_Ext_More)
+ if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension {
+ t.Errorf("got %v, expected ErrMissingExtension", e)
+ }
+ if _, err := proto.GetExtension(msg, pb.E_X215); err == nil {
+ t.Error("expected bad extension error, got nil")
+ }
+ if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil {
+ t.Error("expected extension err")
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil {
+ t.Error("expected some sort of type mismatch error, got nil")
+ }
+}
+
+func TestNilExtension(t *testing.T) {
+ msg := &pb.MyMessage{
+ Count: proto.Int32(1),
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_Text, proto.String("hello")); err != nil {
+ t.Fatal(err)
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, (*pb.Ext)(nil)); err == nil {
+ t.Error("expected SetExtension to fail due to a nil extension")
+ } else if want := "proto: SetExtension called with nil value of type *testdata.Ext"; err.Error() != want {
+ t.Errorf("expected error %v, got %v", want, err)
+ }
+ // Note: if the behavior of Marshal is ever changed to ignore nil extensions, update
+ // this test to verify that E_Ext_Text is properly propagated through marshal->unmarshal.
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/lib.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/lib.go
new file mode 100644
index 000000000..9e254e5ac
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/lib.go
@@ -0,0 +1,790 @@
+// 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.
+ - Marshal and Unmarshal are functions to encode and decode the wire format.
+
+ 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;
+ }
+ }
+
+ The resulting file, test.pb.go, is:
+
+ package example
+
+ import proto "github.com/gogo/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"`
+ XXX_unrecognized []byte `json:"-"`
+ }
+ func (m *Test) Reset() { *m = Test{} }
+ func (m *Test) String() string { return proto.CompactTextString(m) }
+ func (*Test) ProtoMessage() {}
+ 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 init() {
+ proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
+ }
+
+ To create and play with a Test object:
+
+ package main
+
+ import (
+ "log"
+
+ "github.com/gogo/protobuf/proto"
+ pb "./example.pb"
+ )
+
+ func main() {
+ test := &pb.Test{
+ Label: proto.String("hello"),
+ Type: proto.Int32(17),
+ Optionalgroup: &pb.Test_OptionalGroup{
+ RequiredField: proto.String("good bye"),
+ },
+ }
+ 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())
+ }
+ // etc.
+ }
+*/
+package proto
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "reflect"
+ "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 // write 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 (o *Buffer) DebugPrint(s string, b []byte) {
+ var u uint64
+
+ obuf := o.buf
+ index := o.index
+ o.buf = b
+ o.index = 0
+ depth := 0
+
+ fmt.Printf("\n--- %s ---\n", s)
+
+out:
+ for {
+ for i := 0; i < depth; i++ {
+ fmt.Print(" ")
+ }
+
+ index := o.index
+ if index == len(o.buf) {
+ break
+ }
+
+ op, err := o.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 = o.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 = o.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 = o.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)
+ break
+
+ case WireVarint:
+ u, err = o.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:
+ if err != nil {
+ fmt.Printf("%3d: t=%3d start err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d start\n", index, tag)
+ depth++
+
+ case WireEndGroup:
+ depth--
+ if err != nil {
+ fmt.Printf("%3d: t=%3d end err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d end\n", index, tag)
+ }
+ }
+
+ if depth != 0 {
+ fmt.Printf("%3d: start-end not balanced %d\n", o.index, depth)
+ }
+ fmt.Printf("\n")
+
+ o.buf = obuf
+ o.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
+
+ var canHaveDefault, nestedMessage 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 {
+ dm.nested = append(dm.nested, fi)
+ }
+ continue
+ }
+
+ sf := scalarField{
+ index: fi,
+ kind: ft.Elem().Kind(),
+ }
+
+ // scalar fields without defaults
+ if !prop.HasDefault {
+ dm.scalars = append(dm.scalars, sf)
+ continue
+ }
+
+ // a scalar field: either *T or []byte
+ switch ft.Elem().Kind() {
+ case reflect.Bool:
+ x, err := strconv.ParseBool(prop.Default)
+ if err != nil {
+ log.Printf("proto: bad default bool %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = x
+ case reflect.Float32:
+ x, err := strconv.ParseFloat(prop.Default, 32)
+ if err != nil {
+ log.Printf("proto: bad default float32 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = float32(x)
+ case reflect.Float64:
+ x, err := strconv.ParseFloat(prop.Default, 64)
+ if err != nil {
+ log.Printf("proto: bad default float64 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = x
+ case reflect.Int32:
+ x, err := strconv.ParseInt(prop.Default, 10, 32)
+ if err != nil {
+ log.Printf("proto: bad default int32 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = int32(x)
+ case reflect.Int64:
+ x, err := strconv.ParseInt(prop.Default, 10, 64)
+ if err != nil {
+ log.Printf("proto: bad default int64 %q: %v", prop.Default, err)
+ continue
+ }
+ 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 {
+ log.Printf("proto: bad default uint32 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = uint32(x)
+ case reflect.Uint64:
+ x, err := strconv.ParseUint(prop.Default, 10, 64)
+ if err != nil {
+ log.Printf("proto: bad default uint64 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = x
+ default:
+ log.Printf("proto: unhandled def kind %v", ft.Elem().Kind())
+ continue
+ }
+
+ dm.scalars = append(dm.scalars, sf)
+ }
+
+ return dm
+}
+
+// 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.
+
+type mapKeys []reflect.Value
+
+func (s mapKeys) Len() int { return len(s) }
+func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s mapKeys) Less(i, j int) bool {
+ return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/lib_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/lib_gogo.go
new file mode 100644
index 000000000..a6c2c06b2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/lib_gogo.go
@@ -0,0 +1,40 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+import (
+ "encoding/json"
+ "strconv"
+)
+
+func MarshalJSONEnum(m map[int32]string, value int32) ([]byte, error) {
+ s, ok := m[value]
+ if !ok {
+ s = strconv.Itoa(int(value))
+ }
+ return json.Marshal(s)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/message_set.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/message_set.go
new file mode 100644
index 000000000..9d912bce1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/message_set.go
@@ -0,0 +1,287 @@
+// 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.
+//
+// When a proto1 proto has a field that looks like:
+// optional message info = 3;
+// the protocol compiler produces a field in the generated struct that looks like:
+// Info *_proto_.MessageSet `protobuf:"bytes,3,opt,name=info"`
+// The package is automatically inserted so there is no need for that proto file to
+// import 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(m map[int32]Extension) ([]byte, error) {
+ if err := encodeExtensionMap(m); err != nil {
+ return nil, err
+ }
+
+ // 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, m map[int32]Extension) error {
+ 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(m map[int32]Extension) ([]byte, error) {
+ 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, m map[int32]Extension) 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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/message_set_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/message_set_test.go
new file mode 100644
index 000000000..7c29bccf4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/message_set_test.go
@@ -0,0 +1,66 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestUnmarshalMessageSetWithDuplicate(t *testing.T) {
+ // Check that a repeated message set entry will be concatenated.
+ in := &MessageSet{
+ Item: []*_MessageSet_Item{
+ {TypeId: Int32(12345), Message: []byte("hoo")},
+ {TypeId: Int32(12345), Message: []byte("hah")},
+ },
+ }
+ b, err := Marshal(in)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ t.Logf("Marshaled bytes: %q", b)
+
+ m := make(map[int32]Extension)
+ if err := UnmarshalMessageSet(b, m); err != nil {
+ t.Fatalf("UnmarshalMessageSet: %v", err)
+ }
+ ext, ok := m[12345]
+ if !ok {
+ t.Fatalf("Didn't retrieve extension 12345; map is %v", m)
+ }
+ // Skip wire type/field number and length varints.
+ got := skipVarint(skipVarint(ext.enc))
+ if want := []byte("hoohah"); !bytes.Equal(got, want) {
+ t.Errorf("Combined extension is %q, want %q", got, want)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_reflect.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_reflect.go
new file mode 100644
index 000000000..c68b12525
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_reflect.go
@@ -0,0 +1,479 @@
+// 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
+
+// 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)
+}
+
+// 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)
+}
+
+// Map returns the reflect.Value for the address of a map field in the struct.
+func structPointer_Map(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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_unsafe.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_unsafe.go
new file mode 100644
index 000000000..48bc0fa48
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_unsafe.go
@@ -0,0 +1,266 @@
+// 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
+
+// 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_ExtMap(p structPointer, f field) *map[int32]Extension {
+ return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// Map returns the reflect.Value for the address of a map field in the struct.
+func structPointer_Map(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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go
new file mode 100644
index 000000000..6bc85fa98
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/pointer_unsafe_gogo.go
@@ -0,0 +1,108 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+// This file contains the implementation of the proto field accesses using package unsafe.
+
+package proto
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+func structPointer_InterfaceAt(p structPointer, f field, t reflect.Type) interface{} {
+ point := unsafe.Pointer(uintptr(p) + uintptr(f))
+ r := reflect.NewAt(t, point)
+ return r.Interface()
+}
+
+func structPointer_InterfaceRef(p structPointer, f field, t reflect.Type) interface{} {
+ point := unsafe.Pointer(uintptr(p) + uintptr(f))
+ r := reflect.NewAt(t, point)
+ if r.Elem().IsNil() {
+ return nil
+ }
+ return r.Elem().Interface()
+}
+
+func copyUintPtr(oldptr, newptr uintptr, size int) {
+ oldbytes := make([]byte, 0)
+ oldslice := (*reflect.SliceHeader)(unsafe.Pointer(&oldbytes))
+ oldslice.Data = oldptr
+ oldslice.Len = size
+ oldslice.Cap = size
+ newbytes := make([]byte, 0)
+ newslice := (*reflect.SliceHeader)(unsafe.Pointer(&newbytes))
+ newslice.Data = newptr
+ newslice.Len = size
+ newslice.Cap = size
+ copy(newbytes, oldbytes)
+}
+
+func structPointer_Copy(oldptr structPointer, newptr structPointer, size int) {
+ copyUintPtr(uintptr(oldptr), uintptr(newptr), size)
+}
+
+func appendStructPointer(base structPointer, f field, typ reflect.Type) structPointer {
+ size := typ.Elem().Size()
+ oldHeader := structPointer_GetSliceHeader(base, f)
+ newLen := oldHeader.Len + 1
+ slice := reflect.MakeSlice(typ, newLen, newLen)
+ bas := toStructPointer(slice)
+ for i := 0; i < oldHeader.Len; i++ {
+ newElemptr := uintptr(bas) + uintptr(i)*size
+ oldElemptr := oldHeader.Data + uintptr(i)*size
+ copyUintPtr(oldElemptr, newElemptr, int(size))
+ }
+
+ oldHeader.Data = uintptr(bas)
+ oldHeader.Len = newLen
+ oldHeader.Cap = newLen
+
+ return structPointer(unsafe.Pointer(uintptr(unsafe.Pointer(bas)) + uintptr(uintptr(newLen-1)*size)))
+}
+
+func structPointer_FieldPointer(p structPointer, f field) structPointer {
+ return structPointer(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+func structPointer_GetRefStructPointer(p structPointer, f field) structPointer {
+ return structPointer((*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))))
+}
+
+func structPointer_GetSliceHeader(p structPointer, f field) *reflect.SliceHeader {
+ return (*reflect.SliceHeader)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+func structPointer_Add(p structPointer, size field) structPointer {
+ return structPointer(unsafe.Pointer(uintptr(p) + uintptr(size)))
+}
+
+func structPointer_Len(p structPointer, f field) int {
+ return len(*(*[]interface{})(unsafe.Pointer(structPointer_GetRefStructPointer(p, f))))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/properties.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/properties.go
new file mode 100644
index 000000000..13245c00d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/properties.go
@@ -0,0 +1,815 @@
+// Extensions for Protocol Buffers to create more go like structures.
+//
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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"
+ "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)
+
+// 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
+}
+
+// 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)
+ 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
+
+ Default string // default value
+ HasDefault bool // whether an explicit default was provided
+ CustomType string
+ 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
+ sstype reflect.Type // set for slices of structs types only
+ ctype reflect.Type // set for custom 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"
+ }
+ if p.OrigName != p.Name {
+ s += ",name=" + p.OrigName
+ }
+ if p.proto3 {
+ s += ",proto3"
+ }
+ 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, "enum="):
+ p.Enum = f[5:]
+ case f == "proto3":
+ p.proto3 = 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
+ }
+ case strings.HasPrefix(f, "embedded="):
+ p.OrigName = strings.Split(f, "=")[1]
+ case strings.HasPrefix(f, "customtype="):
+ p.CustomType = strings.Split(f, "=")[1]
+ }
+ }
+}
+
+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
+ if len(p.CustomType) > 0 {
+ p.setCustomEncAndDec(typ)
+ p.setTag(lockGetProp)
+ return
+ }
+ switch t1 := typ; t1.Kind() {
+ default:
+ fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
+
+ // proto3 scalar types
+
+ case reflect.Bool:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_bool
+ p.dec = (*Buffer).dec_proto3_bool
+ p.size = size_proto3_bool
+ } else {
+ p.enc = (*Buffer).enc_ref_bool
+ p.dec = (*Buffer).dec_proto3_bool
+ p.size = size_ref_bool
+ }
+ case reflect.Int32:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_int32
+ p.dec = (*Buffer).dec_proto3_int32
+ p.size = size_proto3_int32
+ } else {
+ p.enc = (*Buffer).enc_ref_int32
+ p.dec = (*Buffer).dec_proto3_int32
+ p.size = size_ref_int32
+ }
+ case reflect.Uint32:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_uint32
+ p.dec = (*Buffer).dec_proto3_int32 // can reuse
+ p.size = size_proto3_uint32
+ } else {
+ p.enc = (*Buffer).enc_ref_uint32
+ p.dec = (*Buffer).dec_proto3_int32 // can reuse
+ p.size = size_ref_uint32
+ }
+ case reflect.Int64, reflect.Uint64:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_int64
+ p.dec = (*Buffer).dec_proto3_int64
+ p.size = size_proto3_int64
+ } else {
+ p.enc = (*Buffer).enc_ref_int64
+ p.dec = (*Buffer).dec_proto3_int64
+ p.size = size_ref_int64
+ }
+ case reflect.Float32:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
+ p.dec = (*Buffer).dec_proto3_int32
+ p.size = size_proto3_uint32
+ } else {
+ p.enc = (*Buffer).enc_ref_uint32 // can just treat them as bits
+ p.dec = (*Buffer).dec_proto3_int32
+ p.size = size_ref_uint32
+ }
+ case reflect.Float64:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
+ p.dec = (*Buffer).dec_proto3_int64
+ p.size = size_proto3_int64
+ } else {
+ p.enc = (*Buffer).enc_ref_int64 // can just treat them as bits
+ p.dec = (*Buffer).dec_proto3_int64
+ p.size = size_ref_int64
+ }
+ case reflect.String:
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_string
+ p.dec = (*Buffer).dec_proto3_string
+ p.size = size_proto3_string
+ } else {
+ p.enc = (*Buffer).enc_ref_string
+ p.dec = (*Buffer).dec_proto3_string
+ p.size = size_ref_string
+ }
+ case reflect.Struct:
+ p.stype = typ
+ p.isMarshaler = isMarshaler(typ)
+ p.isUnmarshaler = isUnmarshaler(typ)
+ if p.Wire == "bytes" {
+ p.enc = (*Buffer).enc_ref_struct_message
+ p.dec = (*Buffer).dec_ref_struct_message
+ p.size = size_ref_struct_message
+ } else {
+ fmt.Fprintf(os.Stderr, "proto: no coders for struct %T\n", typ)
+ }
+
+ 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.enc = (*Buffer).enc_slice_byte
+ p.dec = (*Buffer).dec_slice_byte
+ p.size = size_slice_byte
+ // This is a []byte, which is either a bytes field,
+ // or the value of a map field. In the latter case,
+ // we always encode an empty []byte, so we should not
+ // use the proto3 enc/size funcs.
+ // f == nil iff this is the key/value of a map field.
+ if p.proto3 && f != nil {
+ p.enc = (*Buffer).enc_proto3_slice_byte
+ p.size = size_proto3_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.Struct:
+ p.setSliceOfNonPointerStructs(t1)
+ }
+
+ 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)
+ }
+ p.setTag(lockGetProp)
+}
+
+func (p *Properties) setTag(lockGetProp bool) {
+ // 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 {
+ return t.Implements(marshalerType)
+}
+
+// isUnmarshaler reports whether type t implements Unmarshaler.
+func isUnmarshaler(t reflect.Type) bool {
+ 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)
+ 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_extensions" { // special case
+ if len(f.Tag.Get("protobuf")) > 0 {
+ p.enc = (*Buffer).enc_ext_slice_byte
+ p.dec = nil // not needed
+ p.size = size_ext_slice_byte
+ } else {
+ p.enc = (*Buffer).enc_map
+ p.dec = nil // not needed
+ p.size = size_map
+ }
+ }
+ if f.Name == "XXX_unrecognized" { // special case
+ prop.unrecField = toField(&f)
+ }
+ 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_") {
+ fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
+ }
+ }
+
+ // Re-order prop.order.
+ sort.Sort(prop)
+
+ // 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)
+var enumStringMaps = make(map[string]map[int32]string)
+
+// 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
+ if _, ok := enumStringMaps[typeName]; ok {
+ panic("proto: duplicate enum registered: " + typeName)
+ }
+ enumStringMaps[typeName] = unusedNameMap
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/properties_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/properties_gogo.go
new file mode 100644
index 000000000..8daf9f776
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/properties_gogo.go
@@ -0,0 +1,64 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+)
+
+func (p *Properties) setCustomEncAndDec(typ reflect.Type) {
+ p.ctype = typ
+ if p.Repeated {
+ p.enc = (*Buffer).enc_custom_slice_bytes
+ p.dec = (*Buffer).dec_custom_slice_bytes
+ p.size = size_custom_slice_bytes
+ } else if typ.Kind() == reflect.Ptr {
+ p.enc = (*Buffer).enc_custom_bytes
+ p.dec = (*Buffer).dec_custom_bytes
+ p.size = size_custom_bytes
+ } else {
+ p.enc = (*Buffer).enc_custom_ref_bytes
+ p.dec = (*Buffer).dec_custom_ref_bytes
+ p.size = size_custom_ref_bytes
+ }
+}
+
+func (p *Properties) setSliceOfNonPointerStructs(typ reflect.Type) {
+ t2 := typ.Elem()
+ p.sstype = typ
+ p.stype = t2
+ p.isMarshaler = isMarshaler(t2)
+ p.isUnmarshaler = isUnmarshaler(t2)
+ p.enc = (*Buffer).enc_slice_ref_struct_message
+ p.dec = (*Buffer).dec_slice_ref_struct_message
+ p.size = size_slice_ref_struct_message
+ if p.Wire != "bytes" {
+ fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T \n", typ, t2)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto/proto3.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto/proto3.pb.go
new file mode 100644
index 000000000..5ec11613b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto/proto3.pb.go
@@ -0,0 +1,122 @@
+// Code generated by protoc-gen-gogo.
+// source: proto3_proto/proto3.proto
+// DO NOT EDIT!
+
+/*
+Package proto3_proto is a generated protocol buffer package.
+
+It is generated from these files:
+ proto3_proto/proto3.proto
+
+It has these top-level messages:
+ Message
+ Nested
+ MessageWithMap
+*/
+package proto3_proto
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+import testdata "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+
+type Message_Humour int32
+
+const (
+ Message_UNKNOWN Message_Humour = 0
+ Message_PUNS Message_Humour = 1
+ Message_SLAPSTICK Message_Humour = 2
+ Message_BILL_BAILEY Message_Humour = 3
+)
+
+var Message_Humour_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "PUNS",
+ 2: "SLAPSTICK",
+ 3: "BILL_BAILEY",
+}
+var Message_Humour_value = map[string]int32{
+ "UNKNOWN": 0,
+ "PUNS": 1,
+ "SLAPSTICK": 2,
+ "BILL_BAILEY": 3,
+}
+
+func (x Message_Humour) String() string {
+ return proto.EnumName(Message_Humour_name, int32(x))
+}
+
+type Message struct {
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ Hilarity Message_Humour `protobuf:"varint,2,opt,name=hilarity,proto3,enum=proto3_proto.Message_Humour" json:"hilarity,omitempty"`
+ HeightInCm uint32 `protobuf:"varint,3,opt,name=height_in_cm,proto3" json:"height_in_cm,omitempty"`
+ Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"`
+ ResultCount int64 `protobuf:"varint,7,opt,name=result_count,proto3" json:"result_count,omitempty"`
+ TrueScotsman bool `protobuf:"varint,8,opt,name=true_scotsman,proto3" json:"true_scotsman,omitempty"`
+ Score float32 `protobuf:"fixed32,9,opt,name=score,proto3" json:"score,omitempty"`
+ Key []uint64 `protobuf:"varint,5,rep,name=key" json:"key,omitempty"`
+ Nested *Nested `protobuf:"bytes,6,opt,name=nested" json:"nested,omitempty"`
+ Terrain map[string]*Nested `protobuf:"bytes,10,rep,name=terrain" json:"terrain,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
+ Proto2Field *testdata.SubDefaults `protobuf:"bytes,11,opt,name=proto2_field" json:"proto2_field,omitempty"`
+ Proto2Value map[string]*testdata.SubDefaults `protobuf:"bytes,13,rep,name=proto2_value" json:"proto2_value,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
+}
+
+func (m *Message) Reset() { *m = Message{} }
+func (m *Message) String() string { return proto.CompactTextString(m) }
+func (*Message) ProtoMessage() {}
+
+func (m *Message) GetNested() *Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+func (m *Message) GetTerrain() map[string]*Nested {
+ if m != nil {
+ return m.Terrain
+ }
+ return nil
+}
+
+func (m *Message) GetProto2Field() *testdata.SubDefaults {
+ if m != nil {
+ return m.Proto2Field
+ }
+ return nil
+}
+
+func (m *Message) GetProto2Value() map[string]*testdata.SubDefaults {
+ if m != nil {
+ return m.Proto2Value
+ }
+ return nil
+}
+
+type Nested struct {
+ Bunny string `protobuf:"bytes,1,opt,name=bunny,proto3" json:"bunny,omitempty"`
+}
+
+func (m *Nested) Reset() { *m = Nested{} }
+func (m *Nested) String() string { return proto.CompactTextString(m) }
+func (*Nested) ProtoMessage() {}
+
+type MessageWithMap struct {
+ ByteMapping map[bool][]byte `protobuf:"bytes,1,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
+func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
+func (*MessageWithMap) ProtoMessage() {}
+
+func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
+ if m != nil {
+ return m.ByteMapping
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("proto3_proto.Message_Humour", Message_Humour_name, Message_Humour_value)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto/proto3.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto/proto3.proto
new file mode 100644
index 000000000..ca670015a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto/proto3.proto
@@ -0,0 +1,68 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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.
+
+syntax = "proto3";
+
+package proto3_proto;
+
+import "github.com/gogo/protobuf/proto/testdata/test.proto";
+
+message Message {
+ enum Humour {
+ UNKNOWN = 0;
+ PUNS = 1;
+ SLAPSTICK = 2;
+ BILL_BAILEY = 3;
+ }
+
+ string name = 1;
+ Humour hilarity = 2;
+ uint32 height_in_cm = 3;
+ bytes data = 4;
+ int64 result_count = 7;
+ bool true_scotsman = 8;
+ float score = 9;
+
+ repeated uint64 key = 5;
+ Nested nested = 6;
+
+ map terrain = 10;
+ testdata.SubDefaults proto2_field = 11;
+ map proto2_value = 13;
+}
+
+message Nested {
+ string bunny = 1;
+}
+
+message MessageWithMap {
+ map byte_mapping = 1;
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_test.go
new file mode 100644
index 000000000..27e838727
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_test.go
@@ -0,0 +1,125 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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_test
+
+import (
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto"
+ tpb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+func TestProto3ZeroValues(t *testing.T) {
+ tests := []struct {
+ desc string
+ m proto.Message
+ }{
+ {"zero message", &pb.Message{}},
+ {"empty bytes field", &pb.Message{Data: []byte{}}},
+ }
+ for _, test := range tests {
+ b, err := proto.Marshal(test.m)
+ if err != nil {
+ t.Errorf("%s: proto.Marshal: %v", test.desc, err)
+ continue
+ }
+ if len(b) > 0 {
+ t.Errorf("%s: Encoding is non-empty: %q", test.desc, b)
+ }
+ }
+}
+
+func TestRoundTripProto3(t *testing.T) {
+ m := &pb.Message{
+ Name: "David", // (2 | 1<<3): 0x0a 0x05 "David"
+ Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01
+ HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01
+ Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto"
+ ResultCount: 47, // (0 | 7<<3): 0x38 0x2f
+ TrueScotsman: true, // (0 | 8<<3): 0x40 0x01
+ Score: 8.1, // (5 | 9<<3): 0x4d <8.1>
+
+ Key: []uint64{1, 0xdeadbeef},
+ Nested: &pb.Nested{
+ Bunny: "Monty",
+ },
+ }
+ t.Logf(" m: %v", m)
+
+ b, err := proto.Marshal(m)
+ if err != nil {
+ t.Fatalf("proto.Marshal: %v", err)
+ }
+ t.Logf(" b: %q", b)
+
+ m2 := new(pb.Message)
+ if err := proto.Unmarshal(b, m2); err != nil {
+ t.Fatalf("proto.Unmarshal: %v", err)
+ }
+ t.Logf("m2: %v", m2)
+
+ if !proto.Equal(m, m2) {
+ t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2)
+ }
+}
+
+func TestProto3SetDefaults(t *testing.T) {
+ in := &pb.Message{
+ Terrain: map[string]*pb.Nested{
+ "meadow": new(pb.Nested),
+ },
+ Proto2Field: new(tpb.SubDefaults),
+ Proto2Value: map[string]*tpb.SubDefaults{
+ "badlands": new(tpb.SubDefaults),
+ },
+ }
+
+ got := proto.Clone(in).(*pb.Message)
+ proto.SetDefaults(got)
+
+ // There are no defaults in proto3. Everything should be the zero value, but
+ // we need to remember to set defaults for nested proto2 messages.
+ want := &pb.Message{
+ Terrain: map[string]*pb.Nested{
+ "meadow": new(pb.Nested),
+ },
+ Proto2Field: &tpb.SubDefaults{N: proto.Int64(7)},
+ Proto2Value: map[string]*tpb.SubDefaults{
+ "badlands": {N: proto.Int64(7)},
+ },
+ }
+
+ if !proto.Equal(got, want) {
+ t.Errorf("with in = %v\nproto.SetDefaults(in) =>\ngot %v\nwant %v", in, got, want)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/size2_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/size2_test.go
new file mode 100644
index 000000000..a2729c39a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/size2_test.go
@@ -0,0 +1,63 @@
+// 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.
+
+package proto
+
+import (
+ "testing"
+)
+
+// This is a separate file and package from size_test.go because that one uses
+// generated messages and thus may not be in package proto without having a circular
+// dependency, whereas this file tests unexported details of size.go.
+
+func TestVarintSize(t *testing.T) {
+ // Check the edge cases carefully.
+ testCases := []struct {
+ n uint64
+ size int
+ }{
+ {0, 1},
+ {1, 1},
+ {127, 1},
+ {128, 2},
+ {16383, 2},
+ {16384, 3},
+ {1<<63 - 1, 9},
+ {1 << 63, 10},
+ }
+ for _, tc := range testCases {
+ size := sizeVarint(tc.n)
+ if size != tc.size {
+ t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/size_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/size_test.go
new file mode 100644
index 000000000..4fbec925b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/size_test.go
@@ -0,0 +1,142 @@
+// 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.
+
+package proto_test
+
+import (
+ "log"
+ "strings"
+ "testing"
+
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ proto3pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)}
+
+// messageWithExtension2 is in equal_test.go.
+var messageWithExtension3 = &pb.MyMessage{Count: Int32(8)}
+
+func init() {
+ if err := SetExtension(messageWithExtension1, pb.E_Ext_More, &pb.Ext{Data: String("Abbott")}); err != nil {
+ log.Panicf("SetExtension: %v", err)
+ }
+ if err := SetExtension(messageWithExtension3, pb.E_Ext_More, &pb.Ext{Data: String("Costello")}); err != nil {
+ log.Panicf("SetExtension: %v", err)
+ }
+
+ // Force messageWithExtension3 to have the extension encoded.
+ Marshal(messageWithExtension3)
+
+}
+
+var SizeTests = []struct {
+ desc string
+ pb Message
+}{
+ {"empty", &pb.OtherMessage{}},
+ // Basic types.
+ {"bool", &pb.Defaults{F_Bool: Bool(true)}},
+ {"int32", &pb.Defaults{F_Int32: Int32(12)}},
+ {"negative int32", &pb.Defaults{F_Int32: Int32(-1)}},
+ {"small int64", &pb.Defaults{F_Int64: Int64(1)}},
+ {"big int64", &pb.Defaults{F_Int64: Int64(1 << 20)}},
+ {"negative int64", &pb.Defaults{F_Int64: Int64(-1)}},
+ {"fixed32", &pb.Defaults{F_Fixed32: Uint32(71)}},
+ {"fixed64", &pb.Defaults{F_Fixed64: Uint64(72)}},
+ {"uint32", &pb.Defaults{F_Uint32: Uint32(123)}},
+ {"uint64", &pb.Defaults{F_Uint64: Uint64(124)}},
+ {"float", &pb.Defaults{F_Float: Float32(12.6)}},
+ {"double", &pb.Defaults{F_Double: Float64(13.9)}},
+ {"string", &pb.Defaults{F_String: String("niles")}},
+ {"bytes", &pb.Defaults{F_Bytes: []byte("wowsa")}},
+ {"bytes, empty", &pb.Defaults{F_Bytes: []byte{}}},
+ {"sint32", &pb.Defaults{F_Sint32: Int32(65)}},
+ {"sint64", &pb.Defaults{F_Sint64: Int64(67)}},
+ {"enum", &pb.Defaults{F_Enum: pb.Defaults_BLUE.Enum()}},
+ // Repeated.
+ {"empty repeated bool", &pb.MoreRepeated{Bools: []bool{}}},
+ {"repeated bool", &pb.MoreRepeated{Bools: []bool{false, true, true, false}}},
+ {"packed repeated bool", &pb.MoreRepeated{BoolsPacked: []bool{false, true, true, false, true, true, true}}},
+ {"repeated int32", &pb.MoreRepeated{Ints: []int32{1, 12203, 1729, -1}}},
+ {"repeated int32 packed", &pb.MoreRepeated{IntsPacked: []int32{1, 12203, 1729}}},
+ {"repeated int64 packed", &pb.MoreRepeated{Int64SPacked: []int64{
+ // Need enough large numbers to verify that the header is counting the number of bytes
+ // for the field, not the number of elements.
+ 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
+ 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
+ }}},
+ {"repeated string", &pb.MoreRepeated{Strings: []string{"r", "ken", "gri"}}},
+ {"repeated fixed", &pb.MoreRepeated{Fixeds: []uint32{1, 2, 3, 4}}},
+ // Nested.
+ {"nested", &pb.OldMessage{Nested: &pb.OldMessage_Nested{Name: String("whatever")}}},
+ {"group", &pb.GroupOld{G: &pb.GroupOld_G{X: Int32(12345)}}},
+ // Other things.
+ {"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}},
+ {"extension (unencoded)", messageWithExtension1},
+ {"extension (encoded)", messageWithExtension3},
+ // proto3 message
+ {"proto3 empty", &proto3pb.Message{}},
+ {"proto3 bool", &proto3pb.Message{TrueScotsman: true}},
+ {"proto3 int64", &proto3pb.Message{ResultCount: 1}},
+ {"proto3 uint32", &proto3pb.Message{HeightInCm: 123}},
+ {"proto3 float", &proto3pb.Message{Score: 12.6}},
+ {"proto3 string", &proto3pb.Message{Name: "Snezana"}},
+ {"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}},
+ {"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}},
+ {"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
+ {"proto3 map field with empty bytes", &proto3pb.MessageWithMap{ByteMapping: map[bool][]byte{false: {}}}},
+
+ {"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}},
+ {"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: {F: Float64(2.0)}}}},
+ {"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}},
+ {"map field with empty bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: {}}}},
+
+ {"map field with big entry", &pb.MessageWithMap{NameMapping: map[int32]string{8: strings.Repeat("x", 125)}}},
+ {"map field with big key and val", &pb.MessageWithMap{StrToStr: map[string]string{strings.Repeat("x", 70): strings.Repeat("y", 70)}}},
+ {"map field with big numeric key", &pb.MessageWithMap{NameMapping: map[int32]string{0xf00d: "om nom nom"}}},
+}
+
+func TestSize(t *testing.T) {
+ for _, tc := range SizeTests {
+ size := Size(tc.pb)
+ b, err := Marshal(tc.pb)
+ if err != nil {
+ t.Errorf("%v: Marshal failed: %v", tc.desc, err)
+ continue
+ }
+ if size != len(b) {
+ t.Errorf("%v: Size(%v) = %d, want %d", tc.desc, tc.pb, size, len(b))
+ t.Logf("%v: bytes: %#v", tc.desc, b)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/skip_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/skip_gogo.go
new file mode 100644
index 000000000..4fe7e0815
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/skip_gogo.go
@@ -0,0 +1,117 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+import (
+ "fmt"
+ "io"
+)
+
+func Skip(data []byte) (n int, err error) {
+ l := len(data)
+ index := 0
+ for index < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if index >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[index]
+ index++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if index >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ index++
+ if data[index-1] < 0x80 {
+ break
+ }
+ }
+ return index, nil
+ case 1:
+ index += 8
+ return index, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if index >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[index]
+ index++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ index += length
+ return index, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = index
+ for shift := uint(0); ; shift += 7 {
+ if index >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[index]
+ index++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := Skip(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ index = start + next
+ }
+ return index, nil
+ case 4:
+ return index, nil
+ case 5:
+ index += 4
+ return index, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/Makefile
new file mode 100644
index 000000000..1e676c37f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/Makefile
@@ -0,0 +1,37 @@
+# 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.
+
+all: regenerate
+
+regenerate:
+ go install github.com/gogo/protobuf/protoc-gen-gogo/version/protoc-min-version
+ protoc-min-version --version="3.0.0" --gogo_out=. test.proto
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/golden_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/golden_test.go
new file mode 100644
index 000000000..8e8451537
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/golden_test.go
@@ -0,0 +1,86 @@
+// 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.
+
+// Verify that the compiler output for test.proto is unchanged.
+
+package testdata
+
+import (
+ "crypto/sha1"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+)
+
+// sum returns in string form (for easy comparison) the SHA-1 hash of the named file.
+func sum(t *testing.T, name string) string {
+ data, err := ioutil.ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("sum(%q): length is %d", name, len(data))
+ hash := sha1.New()
+ _, err = hash.Write(data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return fmt.Sprintf("% x", hash.Sum(nil))
+}
+
+func run(t *testing.T, name string, args ...string) {
+ cmd := exec.Command(name, args...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestGolden(t *testing.T) {
+ // Compute the original checksum.
+ goldenSum := sum(t, "test.pb.go")
+ // Run the proto compiler.
+ run(t, "protoc", "--gogo_out="+os.TempDir(), "test.proto")
+ newFile := filepath.Join(os.TempDir(), "test.pb.go")
+ defer os.Remove(newFile)
+ // Compute the new checksum.
+ newSum := sum(t, newFile)
+ // Verify
+ if newSum != goldenSum {
+ run(t, "diff", "-u", "test.pb.go", newFile)
+ t.Fatal("Code generated by protoc-gen-go has changed; update test.pb.go")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.pb.go
new file mode 100644
index 000000000..6d6e97cda
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.pb.go
@@ -0,0 +1,2397 @@
+// Code generated by protoc-gen-gogo.
+// source: test.proto
+// DO NOT EDIT!
+
+/*
+Package testdata is a generated protocol buffer package.
+
+It is generated from these files:
+ test.proto
+
+It has these top-level messages:
+ GoEnum
+ GoTestField
+ GoTest
+ GoSkipTest
+ NonPackedTest
+ PackedTest
+ MaxTag
+ OldMessage
+ NewMessage
+ InnerMessage
+ OtherMessage
+ MyMessage
+ Ext
+ MyMessageSet
+ Empty
+ MessageList
+ Strings
+ Defaults
+ SubDefaults
+ RepeatedEnum
+ MoreRepeated
+ GroupOld
+ GroupNew
+ FloatingPoint
+ MessageWithMap
+*/
+package testdata
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type FOO int32
+
+const (
+ FOO_FOO1 FOO = 1
+)
+
+var FOO_name = map[int32]string{
+ 1: "FOO1",
+}
+var FOO_value = map[string]int32{
+ "FOO1": 1,
+}
+
+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, "FOO")
+ if err != nil {
+ return err
+ }
+ *x = FOO(value)
+ return nil
+}
+
+// An enum, for completeness.
+type GoTest_KIND int32
+
+const (
+ GoTest_VOID GoTest_KIND = 0
+ // Basic types
+ GoTest_BOOL GoTest_KIND = 1
+ GoTest_BYTES GoTest_KIND = 2
+ GoTest_FINGERPRINT GoTest_KIND = 3
+ GoTest_FLOAT GoTest_KIND = 4
+ GoTest_INT GoTest_KIND = 5
+ GoTest_STRING GoTest_KIND = 6
+ GoTest_TIME GoTest_KIND = 7
+ // Groupings
+ GoTest_TUPLE GoTest_KIND = 8
+ GoTest_ARRAY GoTest_KIND = 9
+ GoTest_MAP GoTest_KIND = 10
+ // Table types
+ GoTest_TABLE GoTest_KIND = 11
+ // Functions
+ GoTest_FUNCTION GoTest_KIND = 12
+)
+
+var GoTest_KIND_name = map[int32]string{
+ 0: "VOID",
+ 1: "BOOL",
+ 2: "BYTES",
+ 3: "FINGERPRINT",
+ 4: "FLOAT",
+ 5: "INT",
+ 6: "STRING",
+ 7: "TIME",
+ 8: "TUPLE",
+ 9: "ARRAY",
+ 10: "MAP",
+ 11: "TABLE",
+ 12: "FUNCTION",
+}
+var GoTest_KIND_value = map[string]int32{
+ "VOID": 0,
+ "BOOL": 1,
+ "BYTES": 2,
+ "FINGERPRINT": 3,
+ "FLOAT": 4,
+ "INT": 5,
+ "STRING": 6,
+ "TIME": 7,
+ "TUPLE": 8,
+ "ARRAY": 9,
+ "MAP": 10,
+ "TABLE": 11,
+ "FUNCTION": 12,
+}
+
+func (x GoTest_KIND) Enum() *GoTest_KIND {
+ p := new(GoTest_KIND)
+ *p = x
+ return p
+}
+func (x GoTest_KIND) String() string {
+ return proto.EnumName(GoTest_KIND_name, int32(x))
+}
+func (x *GoTest_KIND) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(GoTest_KIND_value, data, "GoTest_KIND")
+ if err != nil {
+ return err
+ }
+ *x = GoTest_KIND(value)
+ return nil
+}
+
+type MyMessage_Color int32
+
+const (
+ MyMessage_RED MyMessage_Color = 0
+ MyMessage_GREEN MyMessage_Color = 1
+ MyMessage_BLUE MyMessage_Color = 2
+)
+
+var MyMessage_Color_name = map[int32]string{
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
+}
+var MyMessage_Color_value = map[string]int32{
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
+}
+
+func (x MyMessage_Color) Enum() *MyMessage_Color {
+ p := new(MyMessage_Color)
+ *p = x
+ return p
+}
+func (x MyMessage_Color) String() string {
+ return proto.EnumName(MyMessage_Color_name, int32(x))
+}
+func (x *MyMessage_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MyMessage_Color_value, data, "MyMessage_Color")
+ if err != nil {
+ return err
+ }
+ *x = MyMessage_Color(value)
+ return nil
+}
+
+type Defaults_Color int32
+
+const (
+ Defaults_RED Defaults_Color = 0
+ Defaults_GREEN Defaults_Color = 1
+ Defaults_BLUE Defaults_Color = 2
+)
+
+var Defaults_Color_name = map[int32]string{
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
+}
+var Defaults_Color_value = map[string]int32{
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
+}
+
+func (x Defaults_Color) Enum() *Defaults_Color {
+ p := new(Defaults_Color)
+ *p = x
+ return p
+}
+func (x Defaults_Color) String() string {
+ return proto.EnumName(Defaults_Color_name, int32(x))
+}
+func (x *Defaults_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(Defaults_Color_value, data, "Defaults_Color")
+ if err != nil {
+ return err
+ }
+ *x = Defaults_Color(value)
+ return nil
+}
+
+type RepeatedEnum_Color int32
+
+const (
+ RepeatedEnum_RED RepeatedEnum_Color = 1
+)
+
+var RepeatedEnum_Color_name = map[int32]string{
+ 1: "RED",
+}
+var RepeatedEnum_Color_value = map[string]int32{
+ "RED": 1,
+}
+
+func (x RepeatedEnum_Color) Enum() *RepeatedEnum_Color {
+ p := new(RepeatedEnum_Color)
+ *p = x
+ return p
+}
+func (x RepeatedEnum_Color) String() string {
+ return proto.EnumName(RepeatedEnum_Color_name, int32(x))
+}
+func (x *RepeatedEnum_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(RepeatedEnum_Color_value, data, "RepeatedEnum_Color")
+ if err != nil {
+ return err
+ }
+ *x = RepeatedEnum_Color(value)
+ return nil
+}
+
+type GoEnum struct {
+ Foo *FOO `protobuf:"varint,1,req,name=foo,enum=testdata.FOO" json:"foo,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoEnum) Reset() { *m = GoEnum{} }
+func (m *GoEnum) String() string { return proto.CompactTextString(m) }
+func (*GoEnum) ProtoMessage() {}
+
+func (m *GoEnum) GetFoo() FOO {
+ if m != nil && m.Foo != nil {
+ return *m.Foo
+ }
+ return FOO_FOO1
+}
+
+type GoTestField struct {
+ Label *string `protobuf:"bytes,1,req" json:"Label,omitempty"`
+ Type *string `protobuf:"bytes,2,req" json:"Type,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTestField) Reset() { *m = GoTestField{} }
+func (m *GoTestField) String() string { return proto.CompactTextString(m) }
+func (*GoTestField) ProtoMessage() {}
+
+func (m *GoTestField) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *GoTestField) GetType() string {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return ""
+}
+
+type GoTest struct {
+ // Some typical parameters
+ Kind *GoTest_KIND `protobuf:"varint,1,req,enum=testdata.GoTest_KIND" json:"Kind,omitempty"`
+ Table *string `protobuf:"bytes,2,opt" json:"Table,omitempty"`
+ Param *int32 `protobuf:"varint,3,opt" json:"Param,omitempty"`
+ // Required, repeated and optional foreign fields.
+ RequiredField *GoTestField `protobuf:"bytes,4,req" json:"RequiredField,omitempty"`
+ RepeatedField []*GoTestField `protobuf:"bytes,5,rep" json:"RepeatedField,omitempty"`
+ OptionalField *GoTestField `protobuf:"bytes,6,opt" json:"OptionalField,omitempty"`
+ // Required fields of all basic types
+ F_BoolRequired *bool `protobuf:"varint,10,req,name=F_Bool_required" json:"F_Bool_required,omitempty"`
+ F_Int32Required *int32 `protobuf:"varint,11,req,name=F_Int32_required" json:"F_Int32_required,omitempty"`
+ F_Int64Required *int64 `protobuf:"varint,12,req,name=F_Int64_required" json:"F_Int64_required,omitempty"`
+ F_Fixed32Required *uint32 `protobuf:"fixed32,13,req,name=F_Fixed32_required" json:"F_Fixed32_required,omitempty"`
+ F_Fixed64Required *uint64 `protobuf:"fixed64,14,req,name=F_Fixed64_required" json:"F_Fixed64_required,omitempty"`
+ F_Uint32Required *uint32 `protobuf:"varint,15,req,name=F_Uint32_required" json:"F_Uint32_required,omitempty"`
+ F_Uint64Required *uint64 `protobuf:"varint,16,req,name=F_Uint64_required" json:"F_Uint64_required,omitempty"`
+ F_FloatRequired *float32 `protobuf:"fixed32,17,req,name=F_Float_required" json:"F_Float_required,omitempty"`
+ F_DoubleRequired *float64 `protobuf:"fixed64,18,req,name=F_Double_required" json:"F_Double_required,omitempty"`
+ F_StringRequired *string `protobuf:"bytes,19,req,name=F_String_required" json:"F_String_required,omitempty"`
+ F_BytesRequired []byte `protobuf:"bytes,101,req,name=F_Bytes_required" json:"F_Bytes_required,omitempty"`
+ F_Sint32Required *int32 `protobuf:"zigzag32,102,req,name=F_Sint32_required" json:"F_Sint32_required,omitempty"`
+ F_Sint64Required *int64 `protobuf:"zigzag64,103,req,name=F_Sint64_required" json:"F_Sint64_required,omitempty"`
+ // Repeated fields of all basic types
+ F_BoolRepeated []bool `protobuf:"varint,20,rep,name=F_Bool_repeated" json:"F_Bool_repeated,omitempty"`
+ F_Int32Repeated []int32 `protobuf:"varint,21,rep,name=F_Int32_repeated" json:"F_Int32_repeated,omitempty"`
+ F_Int64Repeated []int64 `protobuf:"varint,22,rep,name=F_Int64_repeated" json:"F_Int64_repeated,omitempty"`
+ F_Fixed32Repeated []uint32 `protobuf:"fixed32,23,rep,name=F_Fixed32_repeated" json:"F_Fixed32_repeated,omitempty"`
+ F_Fixed64Repeated []uint64 `protobuf:"fixed64,24,rep,name=F_Fixed64_repeated" json:"F_Fixed64_repeated,omitempty"`
+ F_Uint32Repeated []uint32 `protobuf:"varint,25,rep,name=F_Uint32_repeated" json:"F_Uint32_repeated,omitempty"`
+ F_Uint64Repeated []uint64 `protobuf:"varint,26,rep,name=F_Uint64_repeated" json:"F_Uint64_repeated,omitempty"`
+ F_FloatRepeated []float32 `protobuf:"fixed32,27,rep,name=F_Float_repeated" json:"F_Float_repeated,omitempty"`
+ F_DoubleRepeated []float64 `protobuf:"fixed64,28,rep,name=F_Double_repeated" json:"F_Double_repeated,omitempty"`
+ F_StringRepeated []string `protobuf:"bytes,29,rep,name=F_String_repeated" json:"F_String_repeated,omitempty"`
+ F_BytesRepeated [][]byte `protobuf:"bytes,201,rep,name=F_Bytes_repeated" json:"F_Bytes_repeated,omitempty"`
+ F_Sint32Repeated []int32 `protobuf:"zigzag32,202,rep,name=F_Sint32_repeated" json:"F_Sint32_repeated,omitempty"`
+ F_Sint64Repeated []int64 `protobuf:"zigzag64,203,rep,name=F_Sint64_repeated" json:"F_Sint64_repeated,omitempty"`
+ // Optional fields of all basic types
+ F_BoolOptional *bool `protobuf:"varint,30,opt,name=F_Bool_optional" json:"F_Bool_optional,omitempty"`
+ F_Int32Optional *int32 `protobuf:"varint,31,opt,name=F_Int32_optional" json:"F_Int32_optional,omitempty"`
+ F_Int64Optional *int64 `protobuf:"varint,32,opt,name=F_Int64_optional" json:"F_Int64_optional,omitempty"`
+ F_Fixed32Optional *uint32 `protobuf:"fixed32,33,opt,name=F_Fixed32_optional" json:"F_Fixed32_optional,omitempty"`
+ F_Fixed64Optional *uint64 `protobuf:"fixed64,34,opt,name=F_Fixed64_optional" json:"F_Fixed64_optional,omitempty"`
+ F_Uint32Optional *uint32 `protobuf:"varint,35,opt,name=F_Uint32_optional" json:"F_Uint32_optional,omitempty"`
+ F_Uint64Optional *uint64 `protobuf:"varint,36,opt,name=F_Uint64_optional" json:"F_Uint64_optional,omitempty"`
+ F_FloatOptional *float32 `protobuf:"fixed32,37,opt,name=F_Float_optional" json:"F_Float_optional,omitempty"`
+ F_DoubleOptional *float64 `protobuf:"fixed64,38,opt,name=F_Double_optional" json:"F_Double_optional,omitempty"`
+ F_StringOptional *string `protobuf:"bytes,39,opt,name=F_String_optional" json:"F_String_optional,omitempty"`
+ F_BytesOptional []byte `protobuf:"bytes,301,opt,name=F_Bytes_optional" json:"F_Bytes_optional,omitempty"`
+ F_Sint32Optional *int32 `protobuf:"zigzag32,302,opt,name=F_Sint32_optional" json:"F_Sint32_optional,omitempty"`
+ F_Sint64Optional *int64 `protobuf:"zigzag64,303,opt,name=F_Sint64_optional" json:"F_Sint64_optional,omitempty"`
+ // Default-valued fields of all basic types
+ F_BoolDefaulted *bool `protobuf:"varint,40,opt,name=F_Bool_defaulted,def=1" json:"F_Bool_defaulted,omitempty"`
+ F_Int32Defaulted *int32 `protobuf:"varint,41,opt,name=F_Int32_defaulted,def=32" json:"F_Int32_defaulted,omitempty"`
+ F_Int64Defaulted *int64 `protobuf:"varint,42,opt,name=F_Int64_defaulted,def=64" json:"F_Int64_defaulted,omitempty"`
+ F_Fixed32Defaulted *uint32 `protobuf:"fixed32,43,opt,name=F_Fixed32_defaulted,def=320" json:"F_Fixed32_defaulted,omitempty"`
+ F_Fixed64Defaulted *uint64 `protobuf:"fixed64,44,opt,name=F_Fixed64_defaulted,def=640" json:"F_Fixed64_defaulted,omitempty"`
+ F_Uint32Defaulted *uint32 `protobuf:"varint,45,opt,name=F_Uint32_defaulted,def=3200" json:"F_Uint32_defaulted,omitempty"`
+ F_Uint64Defaulted *uint64 `protobuf:"varint,46,opt,name=F_Uint64_defaulted,def=6400" json:"F_Uint64_defaulted,omitempty"`
+ F_FloatDefaulted *float32 `protobuf:"fixed32,47,opt,name=F_Float_defaulted,def=314159" json:"F_Float_defaulted,omitempty"`
+ F_DoubleDefaulted *float64 `protobuf:"fixed64,48,opt,name=F_Double_defaulted,def=271828" json:"F_Double_defaulted,omitempty"`
+ F_StringDefaulted *string `protobuf:"bytes,49,opt,name=F_String_defaulted,def=hello, \"world!\"\n" json:"F_String_defaulted,omitempty"`
+ F_BytesDefaulted []byte `protobuf:"bytes,401,opt,name=F_Bytes_defaulted,def=Bignose" json:"F_Bytes_defaulted,omitempty"`
+ F_Sint32Defaulted *int32 `protobuf:"zigzag32,402,opt,name=F_Sint32_defaulted,def=-32" json:"F_Sint32_defaulted,omitempty"`
+ F_Sint64Defaulted *int64 `protobuf:"zigzag64,403,opt,name=F_Sint64_defaulted,def=-64" json:"F_Sint64_defaulted,omitempty"`
+ // Packed repeated fields (no string or bytes).
+ F_BoolRepeatedPacked []bool `protobuf:"varint,50,rep,packed,name=F_Bool_repeated_packed" json:"F_Bool_repeated_packed,omitempty"`
+ F_Int32RepeatedPacked []int32 `protobuf:"varint,51,rep,packed,name=F_Int32_repeated_packed" json:"F_Int32_repeated_packed,omitempty"`
+ F_Int64RepeatedPacked []int64 `protobuf:"varint,52,rep,packed,name=F_Int64_repeated_packed" json:"F_Int64_repeated_packed,omitempty"`
+ F_Fixed32RepeatedPacked []uint32 `protobuf:"fixed32,53,rep,packed,name=F_Fixed32_repeated_packed" json:"F_Fixed32_repeated_packed,omitempty"`
+ F_Fixed64RepeatedPacked []uint64 `protobuf:"fixed64,54,rep,packed,name=F_Fixed64_repeated_packed" json:"F_Fixed64_repeated_packed,omitempty"`
+ F_Uint32RepeatedPacked []uint32 `protobuf:"varint,55,rep,packed,name=F_Uint32_repeated_packed" json:"F_Uint32_repeated_packed,omitempty"`
+ F_Uint64RepeatedPacked []uint64 `protobuf:"varint,56,rep,packed,name=F_Uint64_repeated_packed" json:"F_Uint64_repeated_packed,omitempty"`
+ F_FloatRepeatedPacked []float32 `protobuf:"fixed32,57,rep,packed,name=F_Float_repeated_packed" json:"F_Float_repeated_packed,omitempty"`
+ F_DoubleRepeatedPacked []float64 `protobuf:"fixed64,58,rep,packed,name=F_Double_repeated_packed" json:"F_Double_repeated_packed,omitempty"`
+ F_Sint32RepeatedPacked []int32 `protobuf:"zigzag32,502,rep,packed,name=F_Sint32_repeated_packed" json:"F_Sint32_repeated_packed,omitempty"`
+ F_Sint64RepeatedPacked []int64 `protobuf:"zigzag64,503,rep,packed,name=F_Sint64_repeated_packed" json:"F_Sint64_repeated_packed,omitempty"`
+ Requiredgroup *GoTest_RequiredGroup `protobuf:"group,70,req,name=RequiredGroup" json:"requiredgroup,omitempty"`
+ Repeatedgroup []*GoTest_RepeatedGroup `protobuf:"group,80,rep,name=RepeatedGroup" json:"repeatedgroup,omitempty"`
+ Optionalgroup *GoTest_OptionalGroup `protobuf:"group,90,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest) Reset() { *m = GoTest{} }
+func (m *GoTest) String() string { return proto.CompactTextString(m) }
+func (*GoTest) ProtoMessage() {}
+
+const Default_GoTest_F_BoolDefaulted bool = true
+const Default_GoTest_F_Int32Defaulted int32 = 32
+const Default_GoTest_F_Int64Defaulted int64 = 64
+const Default_GoTest_F_Fixed32Defaulted uint32 = 320
+const Default_GoTest_F_Fixed64Defaulted uint64 = 640
+const Default_GoTest_F_Uint32Defaulted uint32 = 3200
+const Default_GoTest_F_Uint64Defaulted uint64 = 6400
+const Default_GoTest_F_FloatDefaulted float32 = 314159
+const Default_GoTest_F_DoubleDefaulted float64 = 271828
+const Default_GoTest_F_StringDefaulted string = "hello, \"world!\"\n"
+
+var Default_GoTest_F_BytesDefaulted []byte = []byte("Bignose")
+
+const Default_GoTest_F_Sint32Defaulted int32 = -32
+const Default_GoTest_F_Sint64Defaulted int64 = -64
+
+func (m *GoTest) GetKind() GoTest_KIND {
+ if m != nil && m.Kind != nil {
+ return *m.Kind
+ }
+ return GoTest_VOID
+}
+
+func (m *GoTest) GetTable() string {
+ if m != nil && m.Table != nil {
+ return *m.Table
+ }
+ return ""
+}
+
+func (m *GoTest) GetParam() int32 {
+ if m != nil && m.Param != nil {
+ return *m.Param
+ }
+ return 0
+}
+
+func (m *GoTest) GetRequiredField() *GoTestField {
+ if m != nil {
+ return m.RequiredField
+ }
+ return nil
+}
+
+func (m *GoTest) GetRepeatedField() []*GoTestField {
+ if m != nil {
+ return m.RepeatedField
+ }
+ return nil
+}
+
+func (m *GoTest) GetOptionalField() *GoTestField {
+ if m != nil {
+ return m.OptionalField
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BoolRequired() bool {
+ if m != nil && m.F_BoolRequired != nil {
+ return *m.F_BoolRequired
+ }
+ return false
+}
+
+func (m *GoTest) GetF_Int32Required() int32 {
+ if m != nil && m.F_Int32Required != nil {
+ return *m.F_Int32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Int64Required() int64 {
+ if m != nil && m.F_Int64Required != nil {
+ return *m.F_Int64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed32Required() uint32 {
+ if m != nil && m.F_Fixed32Required != nil {
+ return *m.F_Fixed32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed64Required() uint64 {
+ if m != nil && m.F_Fixed64Required != nil {
+ return *m.F_Fixed64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint32Required() uint32 {
+ if m != nil && m.F_Uint32Required != nil {
+ return *m.F_Uint32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint64Required() uint64 {
+ if m != nil && m.F_Uint64Required != nil {
+ return *m.F_Uint64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_FloatRequired() float32 {
+ if m != nil && m.F_FloatRequired != nil {
+ return *m.F_FloatRequired
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_DoubleRequired() float64 {
+ if m != nil && m.F_DoubleRequired != nil {
+ return *m.F_DoubleRequired
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_StringRequired() string {
+ if m != nil && m.F_StringRequired != nil {
+ return *m.F_StringRequired
+ }
+ return ""
+}
+
+func (m *GoTest) GetF_BytesRequired() []byte {
+ if m != nil {
+ return m.F_BytesRequired
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Required() int32 {
+ if m != nil && m.F_Sint32Required != nil {
+ return *m.F_Sint32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Sint64Required() int64 {
+ if m != nil && m.F_Sint64Required != nil {
+ return *m.F_Sint64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_BoolRepeated() []bool {
+ if m != nil {
+ return m.F_BoolRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int32Repeated() []int32 {
+ if m != nil {
+ return m.F_Int32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int64Repeated() []int64 {
+ if m != nil {
+ return m.F_Int64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed32Repeated() []uint32 {
+ if m != nil {
+ return m.F_Fixed32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed64Repeated() []uint64 {
+ if m != nil {
+ return m.F_Fixed64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint32Repeated() []uint32 {
+ if m != nil {
+ return m.F_Uint32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint64Repeated() []uint64 {
+ if m != nil {
+ return m.F_Uint64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_FloatRepeated() []float32 {
+ if m != nil {
+ return m.F_FloatRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_DoubleRepeated() []float64 {
+ if m != nil {
+ return m.F_DoubleRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_StringRepeated() []string {
+ if m != nil {
+ return m.F_StringRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BytesRepeated() [][]byte {
+ if m != nil {
+ return m.F_BytesRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Repeated() []int32 {
+ if m != nil {
+ return m.F_Sint32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint64Repeated() []int64 {
+ if m != nil {
+ return m.F_Sint64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BoolOptional() bool {
+ if m != nil && m.F_BoolOptional != nil {
+ return *m.F_BoolOptional
+ }
+ return false
+}
+
+func (m *GoTest) GetF_Int32Optional() int32 {
+ if m != nil && m.F_Int32Optional != nil {
+ return *m.F_Int32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Int64Optional() int64 {
+ if m != nil && m.F_Int64Optional != nil {
+ return *m.F_Int64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed32Optional() uint32 {
+ if m != nil && m.F_Fixed32Optional != nil {
+ return *m.F_Fixed32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed64Optional() uint64 {
+ if m != nil && m.F_Fixed64Optional != nil {
+ return *m.F_Fixed64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint32Optional() uint32 {
+ if m != nil && m.F_Uint32Optional != nil {
+ return *m.F_Uint32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint64Optional() uint64 {
+ if m != nil && m.F_Uint64Optional != nil {
+ return *m.F_Uint64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_FloatOptional() float32 {
+ if m != nil && m.F_FloatOptional != nil {
+ return *m.F_FloatOptional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_DoubleOptional() float64 {
+ if m != nil && m.F_DoubleOptional != nil {
+ return *m.F_DoubleOptional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_StringOptional() string {
+ if m != nil && m.F_StringOptional != nil {
+ return *m.F_StringOptional
+ }
+ return ""
+}
+
+func (m *GoTest) GetF_BytesOptional() []byte {
+ if m != nil {
+ return m.F_BytesOptional
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Optional() int32 {
+ if m != nil && m.F_Sint32Optional != nil {
+ return *m.F_Sint32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Sint64Optional() int64 {
+ if m != nil && m.F_Sint64Optional != nil {
+ return *m.F_Sint64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_BoolDefaulted() bool {
+ if m != nil && m.F_BoolDefaulted != nil {
+ return *m.F_BoolDefaulted
+ }
+ return Default_GoTest_F_BoolDefaulted
+}
+
+func (m *GoTest) GetF_Int32Defaulted() int32 {
+ if m != nil && m.F_Int32Defaulted != nil {
+ return *m.F_Int32Defaulted
+ }
+ return Default_GoTest_F_Int32Defaulted
+}
+
+func (m *GoTest) GetF_Int64Defaulted() int64 {
+ if m != nil && m.F_Int64Defaulted != nil {
+ return *m.F_Int64Defaulted
+ }
+ return Default_GoTest_F_Int64Defaulted
+}
+
+func (m *GoTest) GetF_Fixed32Defaulted() uint32 {
+ if m != nil && m.F_Fixed32Defaulted != nil {
+ return *m.F_Fixed32Defaulted
+ }
+ return Default_GoTest_F_Fixed32Defaulted
+}
+
+func (m *GoTest) GetF_Fixed64Defaulted() uint64 {
+ if m != nil && m.F_Fixed64Defaulted != nil {
+ return *m.F_Fixed64Defaulted
+ }
+ return Default_GoTest_F_Fixed64Defaulted
+}
+
+func (m *GoTest) GetF_Uint32Defaulted() uint32 {
+ if m != nil && m.F_Uint32Defaulted != nil {
+ return *m.F_Uint32Defaulted
+ }
+ return Default_GoTest_F_Uint32Defaulted
+}
+
+func (m *GoTest) GetF_Uint64Defaulted() uint64 {
+ if m != nil && m.F_Uint64Defaulted != nil {
+ return *m.F_Uint64Defaulted
+ }
+ return Default_GoTest_F_Uint64Defaulted
+}
+
+func (m *GoTest) GetF_FloatDefaulted() float32 {
+ if m != nil && m.F_FloatDefaulted != nil {
+ return *m.F_FloatDefaulted
+ }
+ return Default_GoTest_F_FloatDefaulted
+}
+
+func (m *GoTest) GetF_DoubleDefaulted() float64 {
+ if m != nil && m.F_DoubleDefaulted != nil {
+ return *m.F_DoubleDefaulted
+ }
+ return Default_GoTest_F_DoubleDefaulted
+}
+
+func (m *GoTest) GetF_StringDefaulted() string {
+ if m != nil && m.F_StringDefaulted != nil {
+ return *m.F_StringDefaulted
+ }
+ return Default_GoTest_F_StringDefaulted
+}
+
+func (m *GoTest) GetF_BytesDefaulted() []byte {
+ if m != nil && m.F_BytesDefaulted != nil {
+ return m.F_BytesDefaulted
+ }
+ return append([]byte(nil), Default_GoTest_F_BytesDefaulted...)
+}
+
+func (m *GoTest) GetF_Sint32Defaulted() int32 {
+ if m != nil && m.F_Sint32Defaulted != nil {
+ return *m.F_Sint32Defaulted
+ }
+ return Default_GoTest_F_Sint32Defaulted
+}
+
+func (m *GoTest) GetF_Sint64Defaulted() int64 {
+ if m != nil && m.F_Sint64Defaulted != nil {
+ return *m.F_Sint64Defaulted
+ }
+ return Default_GoTest_F_Sint64Defaulted
+}
+
+func (m *GoTest) GetF_BoolRepeatedPacked() []bool {
+ if m != nil {
+ return m.F_BoolRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int32RepeatedPacked() []int32 {
+ if m != nil {
+ return m.F_Int32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int64RepeatedPacked() []int64 {
+ if m != nil {
+ return m.F_Int64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed32RepeatedPacked() []uint32 {
+ if m != nil {
+ return m.F_Fixed32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed64RepeatedPacked() []uint64 {
+ if m != nil {
+ return m.F_Fixed64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint32RepeatedPacked() []uint32 {
+ if m != nil {
+ return m.F_Uint32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint64RepeatedPacked() []uint64 {
+ if m != nil {
+ return m.F_Uint64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_FloatRepeatedPacked() []float32 {
+ if m != nil {
+ return m.F_FloatRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_DoubleRepeatedPacked() []float64 {
+ if m != nil {
+ return m.F_DoubleRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32RepeatedPacked() []int32 {
+ if m != nil {
+ return m.F_Sint32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint64RepeatedPacked() []int64 {
+ if m != nil {
+ return m.F_Sint64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetRequiredgroup() *GoTest_RequiredGroup {
+ if m != nil {
+ return m.Requiredgroup
+ }
+ return nil
+}
+
+func (m *GoTest) GetRepeatedgroup() []*GoTest_RepeatedGroup {
+ if m != nil {
+ return m.Repeatedgroup
+ }
+ return nil
+}
+
+func (m *GoTest) GetOptionalgroup() *GoTest_OptionalGroup {
+ if m != nil {
+ return m.Optionalgroup
+ }
+ return nil
+}
+
+// Required, repeated, and optional groups.
+type GoTest_RequiredGroup struct {
+ RequiredField *string `protobuf:"bytes,71,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_RequiredGroup) Reset() { *m = GoTest_RequiredGroup{} }
+func (m *GoTest_RequiredGroup) String() string { return proto.CompactTextString(m) }
+func (*GoTest_RequiredGroup) ProtoMessage() {}
+
+func (m *GoTest_RequiredGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoTest_RepeatedGroup struct {
+ RequiredField *string `protobuf:"bytes,81,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_RepeatedGroup) Reset() { *m = GoTest_RepeatedGroup{} }
+func (m *GoTest_RepeatedGroup) String() string { return proto.CompactTextString(m) }
+func (*GoTest_RepeatedGroup) ProtoMessage() {}
+
+func (m *GoTest_RepeatedGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoTest_OptionalGroup struct {
+ RequiredField *string `protobuf:"bytes,91,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_OptionalGroup) Reset() { *m = GoTest_OptionalGroup{} }
+func (m *GoTest_OptionalGroup) String() string { return proto.CompactTextString(m) }
+func (*GoTest_OptionalGroup) ProtoMessage() {}
+
+func (m *GoTest_OptionalGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+// For testing skipping of unrecognized fields.
+// Numbers are all big, larger than tag numbers in GoTestField,
+// the message used in the corresponding test.
+type GoSkipTest struct {
+ SkipInt32 *int32 `protobuf:"varint,11,req,name=skip_int32" json:"skip_int32,omitempty"`
+ SkipFixed32 *uint32 `protobuf:"fixed32,12,req,name=skip_fixed32" json:"skip_fixed32,omitempty"`
+ SkipFixed64 *uint64 `protobuf:"fixed64,13,req,name=skip_fixed64" json:"skip_fixed64,omitempty"`
+ SkipString *string `protobuf:"bytes,14,req,name=skip_string" json:"skip_string,omitempty"`
+ Skipgroup *GoSkipTest_SkipGroup `protobuf:"group,15,req,name=SkipGroup" json:"skipgroup,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoSkipTest) Reset() { *m = GoSkipTest{} }
+func (m *GoSkipTest) String() string { return proto.CompactTextString(m) }
+func (*GoSkipTest) ProtoMessage() {}
+
+func (m *GoSkipTest) GetSkipInt32() int32 {
+ if m != nil && m.SkipInt32 != nil {
+ return *m.SkipInt32
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipFixed32() uint32 {
+ if m != nil && m.SkipFixed32 != nil {
+ return *m.SkipFixed32
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipFixed64() uint64 {
+ if m != nil && m.SkipFixed64 != nil {
+ return *m.SkipFixed64
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipString() string {
+ if m != nil && m.SkipString != nil {
+ return *m.SkipString
+ }
+ return ""
+}
+
+func (m *GoSkipTest) GetSkipgroup() *GoSkipTest_SkipGroup {
+ if m != nil {
+ return m.Skipgroup
+ }
+ return nil
+}
+
+type GoSkipTest_SkipGroup struct {
+ GroupInt32 *int32 `protobuf:"varint,16,req,name=group_int32" json:"group_int32,omitempty"`
+ GroupString *string `protobuf:"bytes,17,req,name=group_string" json:"group_string,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoSkipTest_SkipGroup) Reset() { *m = GoSkipTest_SkipGroup{} }
+func (m *GoSkipTest_SkipGroup) String() string { return proto.CompactTextString(m) }
+func (*GoSkipTest_SkipGroup) ProtoMessage() {}
+
+func (m *GoSkipTest_SkipGroup) GetGroupInt32() int32 {
+ if m != nil && m.GroupInt32 != nil {
+ return *m.GroupInt32
+ }
+ return 0
+}
+
+func (m *GoSkipTest_SkipGroup) GetGroupString() string {
+ if m != nil && m.GroupString != nil {
+ return *m.GroupString
+ }
+ return ""
+}
+
+// For testing packed/non-packed decoder switching.
+// A serialized instance of one should be deserializable as the other.
+type NonPackedTest struct {
+ A []int32 `protobuf:"varint,1,rep,name=a" json:"a,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NonPackedTest) Reset() { *m = NonPackedTest{} }
+func (m *NonPackedTest) String() string { return proto.CompactTextString(m) }
+func (*NonPackedTest) ProtoMessage() {}
+
+func (m *NonPackedTest) GetA() []int32 {
+ if m != nil {
+ return m.A
+ }
+ return nil
+}
+
+type PackedTest struct {
+ B []int32 `protobuf:"varint,1,rep,packed,name=b" json:"b,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PackedTest) Reset() { *m = PackedTest{} }
+func (m *PackedTest) String() string { return proto.CompactTextString(m) }
+func (*PackedTest) ProtoMessage() {}
+
+func (m *PackedTest) GetB() []int32 {
+ if m != nil {
+ return m.B
+ }
+ return nil
+}
+
+type MaxTag struct {
+ // Maximum possible tag number.
+ LastField *string `protobuf:"bytes,536870911,opt,name=last_field" json:"last_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MaxTag) Reset() { *m = MaxTag{} }
+func (m *MaxTag) String() string { return proto.CompactTextString(m) }
+func (*MaxTag) ProtoMessage() {}
+
+func (m *MaxTag) GetLastField() string {
+ if m != nil && m.LastField != nil {
+ return *m.LastField
+ }
+ return ""
+}
+
+type OldMessage struct {
+ Nested *OldMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"`
+ Num *int32 `protobuf:"varint,2,opt,name=num" json:"num,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OldMessage) Reset() { *m = OldMessage{} }
+func (m *OldMessage) String() string { return proto.CompactTextString(m) }
+func (*OldMessage) ProtoMessage() {}
+
+func (m *OldMessage) GetNested() *OldMessage_Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+func (m *OldMessage) GetNum() int32 {
+ if m != nil && m.Num != nil {
+ return *m.Num
+ }
+ return 0
+}
+
+type OldMessage_Nested struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OldMessage_Nested) Reset() { *m = OldMessage_Nested{} }
+func (m *OldMessage_Nested) String() string { return proto.CompactTextString(m) }
+func (*OldMessage_Nested) ProtoMessage() {}
+
+func (m *OldMessage_Nested) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+// NewMessage is wire compatible with OldMessage;
+// imagine it as a future version.
+type NewMessage struct {
+ Nested *NewMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"`
+ // This is an int32 in OldMessage.
+ Num *int64 `protobuf:"varint,2,opt,name=num" json:"num,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NewMessage) Reset() { *m = NewMessage{} }
+func (m *NewMessage) String() string { return proto.CompactTextString(m) }
+func (*NewMessage) ProtoMessage() {}
+
+func (m *NewMessage) GetNested() *NewMessage_Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+func (m *NewMessage) GetNum() int64 {
+ if m != nil && m.Num != nil {
+ return *m.Num
+ }
+ return 0
+}
+
+type NewMessage_Nested struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ FoodGroup *string `protobuf:"bytes,2,opt,name=food_group" json:"food_group,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NewMessage_Nested) Reset() { *m = NewMessage_Nested{} }
+func (m *NewMessage_Nested) String() string { return proto.CompactTextString(m) }
+func (*NewMessage_Nested) ProtoMessage() {}
+
+func (m *NewMessage_Nested) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *NewMessage_Nested) GetFoodGroup() string {
+ if m != nil && m.FoodGroup != nil {
+ return *m.FoodGroup
+ }
+ return ""
+}
+
+type InnerMessage struct {
+ Host *string `protobuf:"bytes,1,req,name=host" json:"host,omitempty"`
+ Port *int32 `protobuf:"varint,2,opt,name=port,def=4000" json:"port,omitempty"`
+ Connected *bool `protobuf:"varint,3,opt,name=connected" json:"connected,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *InnerMessage) Reset() { *m = InnerMessage{} }
+func (m *InnerMessage) String() string { return proto.CompactTextString(m) }
+func (*InnerMessage) ProtoMessage() {}
+
+const Default_InnerMessage_Port int32 = 4000
+
+func (m *InnerMessage) GetHost() string {
+ if m != nil && m.Host != nil {
+ return *m.Host
+ }
+ return ""
+}
+
+func (m *InnerMessage) GetPort() int32 {
+ if m != nil && m.Port != nil {
+ return *m.Port
+ }
+ return Default_InnerMessage_Port
+}
+
+func (m *InnerMessage) GetConnected() bool {
+ if m != nil && m.Connected != nil {
+ return *m.Connected
+ }
+ return false
+}
+
+type OtherMessage struct {
+ Key *int64 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"`
+ Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+ Weight *float32 `protobuf:"fixed32,3,opt,name=weight" json:"weight,omitempty"`
+ Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner" json:"inner,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OtherMessage) Reset() { *m = OtherMessage{} }
+func (m *OtherMessage) String() string { return proto.CompactTextString(m) }
+func (*OtherMessage) ProtoMessage() {}
+
+func (m *OtherMessage) GetKey() int64 {
+ if m != nil && m.Key != nil {
+ return *m.Key
+ }
+ return 0
+}
+
+func (m *OtherMessage) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *OtherMessage) GetWeight() float32 {
+ if m != nil && m.Weight != nil {
+ return *m.Weight
+ }
+ return 0
+}
+
+func (m *OtherMessage) GetInner() *InnerMessage {
+ if m != nil {
+ return m.Inner
+ }
+ return nil
+}
+
+type MyMessage struct {
+ Count *int32 `protobuf:"varint,1,req,name=count" json:"count,omitempty"`
+ Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
+ Quote *string `protobuf:"bytes,3,opt,name=quote" json:"quote,omitempty"`
+ Pet []string `protobuf:"bytes,4,rep,name=pet" json:"pet,omitempty"`
+ Inner *InnerMessage `protobuf:"bytes,5,opt,name=inner" json:"inner,omitempty"`
+ Others []*OtherMessage `protobuf:"bytes,6,rep,name=others" json:"others,omitempty"`
+ RepInner []*InnerMessage `protobuf:"bytes,12,rep,name=rep_inner" json:"rep_inner,omitempty"`
+ Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=testdata.MyMessage_Color" json:"bikeshed,omitempty"`
+ Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup,omitempty"`
+ // This field becomes [][]byte in the generated code.
+ RepBytes [][]byte `protobuf:"bytes,10,rep,name=rep_bytes" json:"rep_bytes,omitempty"`
+ Bigfloat *float64 `protobuf:"fixed64,11,opt,name=bigfloat" json:"bigfloat,omitempty"`
+ XXX_extensions map[int32]proto.Extension `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessage) Reset() { *m = MyMessage{} }
+func (m *MyMessage) String() string { return proto.CompactTextString(m) }
+func (*MyMessage) ProtoMessage() {}
+
+var extRange_MyMessage = []proto.ExtensionRange{
+ {100, 536870911},
+}
+
+func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange {
+ return extRange_MyMessage
+}
+func (m *MyMessage) ExtensionMap() map[int32]proto.Extension {
+ if m.XXX_extensions == nil {
+ m.XXX_extensions = make(map[int32]proto.Extension)
+ }
+ return m.XXX_extensions
+}
+
+func (m *MyMessage) GetCount() int32 {
+ if m != nil && m.Count != nil {
+ return *m.Count
+ }
+ return 0
+}
+
+func (m *MyMessage) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MyMessage) GetQuote() string {
+ if m != nil && m.Quote != nil {
+ return *m.Quote
+ }
+ return ""
+}
+
+func (m *MyMessage) GetPet() []string {
+ if m != nil {
+ return m.Pet
+ }
+ return nil
+}
+
+func (m *MyMessage) GetInner() *InnerMessage {
+ if m != nil {
+ return m.Inner
+ }
+ return nil
+}
+
+func (m *MyMessage) GetOthers() []*OtherMessage {
+ if m != nil {
+ return m.Others
+ }
+ return nil
+}
+
+func (m *MyMessage) GetRepInner() []*InnerMessage {
+ if m != nil {
+ return m.RepInner
+ }
+ return nil
+}
+
+func (m *MyMessage) GetBikeshed() MyMessage_Color {
+ if m != nil && m.Bikeshed != nil {
+ return *m.Bikeshed
+ }
+ return MyMessage_RED
+}
+
+func (m *MyMessage) GetSomegroup() *MyMessage_SomeGroup {
+ if m != nil {
+ return m.Somegroup
+ }
+ return nil
+}
+
+func (m *MyMessage) GetRepBytes() [][]byte {
+ if m != nil {
+ return m.RepBytes
+ }
+ return nil
+}
+
+func (m *MyMessage) GetBigfloat() float64 {
+ if m != nil && m.Bigfloat != nil {
+ return *m.Bigfloat
+ }
+ return 0
+}
+
+type MyMessage_SomeGroup struct {
+ GroupField *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessage_SomeGroup) Reset() { *m = MyMessage_SomeGroup{} }
+func (m *MyMessage_SomeGroup) String() string { return proto.CompactTextString(m) }
+func (*MyMessage_SomeGroup) ProtoMessage() {}
+
+func (m *MyMessage_SomeGroup) GetGroupField() int32 {
+ if m != nil && m.GroupField != nil {
+ return *m.GroupField
+ }
+ return 0
+}
+
+type Ext struct {
+ Data *string `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Ext) Reset() { *m = Ext{} }
+func (m *Ext) String() string { return proto.CompactTextString(m) }
+func (*Ext) ProtoMessage() {}
+
+func (m *Ext) GetData() string {
+ if m != nil && m.Data != nil {
+ return *m.Data
+ }
+ return ""
+}
+
+var E_Ext_More = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*Ext)(nil),
+ Field: 103,
+ Name: "testdata.Ext.more",
+ Tag: "bytes,103,opt,name=more",
+}
+
+var E_Ext_Text = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 104,
+ Name: "testdata.Ext.text",
+ Tag: "bytes,104,opt,name=text",
+}
+
+var E_Ext_Number = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*int32)(nil),
+ Field: 105,
+ Name: "testdata.Ext.number",
+ Tag: "varint,105,opt,name=number",
+}
+
+type MyMessageSet struct {
+ XXX_extensions map[int32]proto.Extension `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessageSet) Reset() { *m = MyMessageSet{} }
+func (m *MyMessageSet) String() string { return proto.CompactTextString(m) }
+func (*MyMessageSet) ProtoMessage() {}
+
+func (m *MyMessageSet) Marshal() ([]byte, error) {
+ return proto.MarshalMessageSet(m.ExtensionMap())
+}
+func (m *MyMessageSet) Unmarshal(buf []byte) error {
+ return proto.UnmarshalMessageSet(buf, m.ExtensionMap())
+}
+func (m *MyMessageSet) MarshalJSON() ([]byte, error) {
+ return proto.MarshalMessageSetJSON(m.XXX_extensions)
+}
+func (m *MyMessageSet) UnmarshalJSON(buf []byte) error {
+ return proto.UnmarshalMessageSetJSON(buf, m.XXX_extensions)
+}
+
+// ensure MyMessageSet satisfies proto.Marshaler and proto.Unmarshaler
+var _ proto.Marshaler = (*MyMessageSet)(nil)
+var _ proto.Unmarshaler = (*MyMessageSet)(nil)
+
+var extRange_MyMessageSet = []proto.ExtensionRange{
+ {100, 2147483646},
+}
+
+func (*MyMessageSet) ExtensionRangeArray() []proto.ExtensionRange {
+ return extRange_MyMessageSet
+}
+func (m *MyMessageSet) ExtensionMap() map[int32]proto.Extension {
+ if m.XXX_extensions == nil {
+ m.XXX_extensions = make(map[int32]proto.Extension)
+ }
+ return m.XXX_extensions
+}
+
+type Empty struct {
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+
+type MessageList struct {
+ Message []*MessageList_Message `protobuf:"group,1,rep" json:"message,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageList) Reset() { *m = MessageList{} }
+func (m *MessageList) String() string { return proto.CompactTextString(m) }
+func (*MessageList) ProtoMessage() {}
+
+func (m *MessageList) GetMessage() []*MessageList_Message {
+ if m != nil {
+ return m.Message
+ }
+ return nil
+}
+
+type MessageList_Message struct {
+ Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
+ Count *int32 `protobuf:"varint,3,req,name=count" json:"count,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageList_Message) Reset() { *m = MessageList_Message{} }
+func (m *MessageList_Message) String() string { return proto.CompactTextString(m) }
+func (*MessageList_Message) ProtoMessage() {}
+
+func (m *MessageList_Message) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MessageList_Message) GetCount() int32 {
+ if m != nil && m.Count != nil {
+ return *m.Count
+ }
+ return 0
+}
+
+type Strings struct {
+ StringField *string `protobuf:"bytes,1,opt,name=string_field" json:"string_field,omitempty"`
+ BytesField []byte `protobuf:"bytes,2,opt,name=bytes_field" json:"bytes_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Strings) Reset() { *m = Strings{} }
+func (m *Strings) String() string { return proto.CompactTextString(m) }
+func (*Strings) ProtoMessage() {}
+
+func (m *Strings) GetStringField() string {
+ if m != nil && m.StringField != nil {
+ return *m.StringField
+ }
+ return ""
+}
+
+func (m *Strings) GetBytesField() []byte {
+ if m != nil {
+ return m.BytesField
+ }
+ return nil
+}
+
+type Defaults struct {
+ // Default-valued fields of all basic types.
+ // Same as GoTest, but copied here to make testing easier.
+ F_Bool *bool `protobuf:"varint,1,opt,def=1" json:"F_Bool,omitempty"`
+ F_Int32 *int32 `protobuf:"varint,2,opt,def=32" json:"F_Int32,omitempty"`
+ F_Int64 *int64 `protobuf:"varint,3,opt,def=64" json:"F_Int64,omitempty"`
+ F_Fixed32 *uint32 `protobuf:"fixed32,4,opt,def=320" json:"F_Fixed32,omitempty"`
+ F_Fixed64 *uint64 `protobuf:"fixed64,5,opt,def=640" json:"F_Fixed64,omitempty"`
+ F_Uint32 *uint32 `protobuf:"varint,6,opt,def=3200" json:"F_Uint32,omitempty"`
+ F_Uint64 *uint64 `protobuf:"varint,7,opt,def=6400" json:"F_Uint64,omitempty"`
+ F_Float *float32 `protobuf:"fixed32,8,opt,def=314159" json:"F_Float,omitempty"`
+ F_Double *float64 `protobuf:"fixed64,9,opt,def=271828" json:"F_Double,omitempty"`
+ F_String *string `protobuf:"bytes,10,opt,def=hello, \"world!\"\n" json:"F_String,omitempty"`
+ F_Bytes []byte `protobuf:"bytes,11,opt,def=Bignose" json:"F_Bytes,omitempty"`
+ F_Sint32 *int32 `protobuf:"zigzag32,12,opt,def=-32" json:"F_Sint32,omitempty"`
+ F_Sint64 *int64 `protobuf:"zigzag64,13,opt,def=-64" json:"F_Sint64,omitempty"`
+ F_Enum *Defaults_Color `protobuf:"varint,14,opt,enum=testdata.Defaults_Color,def=1" json:"F_Enum,omitempty"`
+ // More fields with crazy defaults.
+ F_Pinf *float32 `protobuf:"fixed32,15,opt,def=inf" json:"F_Pinf,omitempty"`
+ F_Ninf *float32 `protobuf:"fixed32,16,opt,def=-inf" json:"F_Ninf,omitempty"`
+ F_Nan *float32 `protobuf:"fixed32,17,opt,def=nan" json:"F_Nan,omitempty"`
+ // Sub-message.
+ Sub *SubDefaults `protobuf:"bytes,18,opt,name=sub" json:"sub,omitempty"`
+ // Redundant but explicit defaults.
+ StrZero *string `protobuf:"bytes,19,opt,name=str_zero,def=" json:"str_zero,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Defaults) Reset() { *m = Defaults{} }
+func (m *Defaults) String() string { return proto.CompactTextString(m) }
+func (*Defaults) ProtoMessage() {}
+
+const Default_Defaults_F_Bool bool = true
+const Default_Defaults_F_Int32 int32 = 32
+const Default_Defaults_F_Int64 int64 = 64
+const Default_Defaults_F_Fixed32 uint32 = 320
+const Default_Defaults_F_Fixed64 uint64 = 640
+const Default_Defaults_F_Uint32 uint32 = 3200
+const Default_Defaults_F_Uint64 uint64 = 6400
+const Default_Defaults_F_Float float32 = 314159
+const Default_Defaults_F_Double float64 = 271828
+const Default_Defaults_F_String string = "hello, \"world!\"\n"
+
+var Default_Defaults_F_Bytes []byte = []byte("Bignose")
+
+const Default_Defaults_F_Sint32 int32 = -32
+const Default_Defaults_F_Sint64 int64 = -64
+const Default_Defaults_F_Enum Defaults_Color = Defaults_GREEN
+
+var Default_Defaults_F_Pinf float32 = float32(math.Inf(1))
+var Default_Defaults_F_Ninf float32 = float32(math.Inf(-1))
+var Default_Defaults_F_Nan float32 = float32(math.NaN())
+
+func (m *Defaults) GetF_Bool() bool {
+ if m != nil && m.F_Bool != nil {
+ return *m.F_Bool
+ }
+ return Default_Defaults_F_Bool
+}
+
+func (m *Defaults) GetF_Int32() int32 {
+ if m != nil && m.F_Int32 != nil {
+ return *m.F_Int32
+ }
+ return Default_Defaults_F_Int32
+}
+
+func (m *Defaults) GetF_Int64() int64 {
+ if m != nil && m.F_Int64 != nil {
+ return *m.F_Int64
+ }
+ return Default_Defaults_F_Int64
+}
+
+func (m *Defaults) GetF_Fixed32() uint32 {
+ if m != nil && m.F_Fixed32 != nil {
+ return *m.F_Fixed32
+ }
+ return Default_Defaults_F_Fixed32
+}
+
+func (m *Defaults) GetF_Fixed64() uint64 {
+ if m != nil && m.F_Fixed64 != nil {
+ return *m.F_Fixed64
+ }
+ return Default_Defaults_F_Fixed64
+}
+
+func (m *Defaults) GetF_Uint32() uint32 {
+ if m != nil && m.F_Uint32 != nil {
+ return *m.F_Uint32
+ }
+ return Default_Defaults_F_Uint32
+}
+
+func (m *Defaults) GetF_Uint64() uint64 {
+ if m != nil && m.F_Uint64 != nil {
+ return *m.F_Uint64
+ }
+ return Default_Defaults_F_Uint64
+}
+
+func (m *Defaults) GetF_Float() float32 {
+ if m != nil && m.F_Float != nil {
+ return *m.F_Float
+ }
+ return Default_Defaults_F_Float
+}
+
+func (m *Defaults) GetF_Double() float64 {
+ if m != nil && m.F_Double != nil {
+ return *m.F_Double
+ }
+ return Default_Defaults_F_Double
+}
+
+func (m *Defaults) GetF_String() string {
+ if m != nil && m.F_String != nil {
+ return *m.F_String
+ }
+ return Default_Defaults_F_String
+}
+
+func (m *Defaults) GetF_Bytes() []byte {
+ if m != nil && m.F_Bytes != nil {
+ return m.F_Bytes
+ }
+ return append([]byte(nil), Default_Defaults_F_Bytes...)
+}
+
+func (m *Defaults) GetF_Sint32() int32 {
+ if m != nil && m.F_Sint32 != nil {
+ return *m.F_Sint32
+ }
+ return Default_Defaults_F_Sint32
+}
+
+func (m *Defaults) GetF_Sint64() int64 {
+ if m != nil && m.F_Sint64 != nil {
+ return *m.F_Sint64
+ }
+ return Default_Defaults_F_Sint64
+}
+
+func (m *Defaults) GetF_Enum() Defaults_Color {
+ if m != nil && m.F_Enum != nil {
+ return *m.F_Enum
+ }
+ return Default_Defaults_F_Enum
+}
+
+func (m *Defaults) GetF_Pinf() float32 {
+ if m != nil && m.F_Pinf != nil {
+ return *m.F_Pinf
+ }
+ return Default_Defaults_F_Pinf
+}
+
+func (m *Defaults) GetF_Ninf() float32 {
+ if m != nil && m.F_Ninf != nil {
+ return *m.F_Ninf
+ }
+ return Default_Defaults_F_Ninf
+}
+
+func (m *Defaults) GetF_Nan() float32 {
+ if m != nil && m.F_Nan != nil {
+ return *m.F_Nan
+ }
+ return Default_Defaults_F_Nan
+}
+
+func (m *Defaults) GetSub() *SubDefaults {
+ if m != nil {
+ return m.Sub
+ }
+ return nil
+}
+
+func (m *Defaults) GetStrZero() string {
+ if m != nil && m.StrZero != nil {
+ return *m.StrZero
+ }
+ return ""
+}
+
+type SubDefaults struct {
+ N *int64 `protobuf:"varint,1,opt,name=n,def=7" json:"n,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SubDefaults) Reset() { *m = SubDefaults{} }
+func (m *SubDefaults) String() string { return proto.CompactTextString(m) }
+func (*SubDefaults) ProtoMessage() {}
+
+const Default_SubDefaults_N int64 = 7
+
+func (m *SubDefaults) GetN() int64 {
+ if m != nil && m.N != nil {
+ return *m.N
+ }
+ return Default_SubDefaults_N
+}
+
+type RepeatedEnum struct {
+ Color []RepeatedEnum_Color `protobuf:"varint,1,rep,name=color,enum=testdata.RepeatedEnum_Color" json:"color,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RepeatedEnum) Reset() { *m = RepeatedEnum{} }
+func (m *RepeatedEnum) String() string { return proto.CompactTextString(m) }
+func (*RepeatedEnum) ProtoMessage() {}
+
+func (m *RepeatedEnum) GetColor() []RepeatedEnum_Color {
+ if m != nil {
+ return m.Color
+ }
+ return nil
+}
+
+type MoreRepeated struct {
+ Bools []bool `protobuf:"varint,1,rep,name=bools" json:"bools,omitempty"`
+ BoolsPacked []bool `protobuf:"varint,2,rep,packed,name=bools_packed" json:"bools_packed,omitempty"`
+ Ints []int32 `protobuf:"varint,3,rep,name=ints" json:"ints,omitempty"`
+ IntsPacked []int32 `protobuf:"varint,4,rep,packed,name=ints_packed" json:"ints_packed,omitempty"`
+ Int64SPacked []int64 `protobuf:"varint,7,rep,packed,name=int64s_packed" json:"int64s_packed,omitempty"`
+ Strings []string `protobuf:"bytes,5,rep,name=strings" json:"strings,omitempty"`
+ Fixeds []uint32 `protobuf:"fixed32,6,rep,name=fixeds" json:"fixeds,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MoreRepeated) Reset() { *m = MoreRepeated{} }
+func (m *MoreRepeated) String() string { return proto.CompactTextString(m) }
+func (*MoreRepeated) ProtoMessage() {}
+
+func (m *MoreRepeated) GetBools() []bool {
+ if m != nil {
+ return m.Bools
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetBoolsPacked() []bool {
+ if m != nil {
+ return m.BoolsPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetInts() []int32 {
+ if m != nil {
+ return m.Ints
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetIntsPacked() []int32 {
+ if m != nil {
+ return m.IntsPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetInt64SPacked() []int64 {
+ if m != nil {
+ return m.Int64SPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetStrings() []string {
+ if m != nil {
+ return m.Strings
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetFixeds() []uint32 {
+ if m != nil {
+ return m.Fixeds
+ }
+ return nil
+}
+
+type GroupOld struct {
+ G *GroupOld_G `protobuf:"group,101,opt" json:"g,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupOld) Reset() { *m = GroupOld{} }
+func (m *GroupOld) String() string { return proto.CompactTextString(m) }
+func (*GroupOld) ProtoMessage() {}
+
+func (m *GroupOld) GetG() *GroupOld_G {
+ if m != nil {
+ return m.G
+ }
+ return nil
+}
+
+type GroupOld_G struct {
+ X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupOld_G) Reset() { *m = GroupOld_G{} }
+func (m *GroupOld_G) String() string { return proto.CompactTextString(m) }
+func (*GroupOld_G) ProtoMessage() {}
+
+func (m *GroupOld_G) GetX() int32 {
+ if m != nil && m.X != nil {
+ return *m.X
+ }
+ return 0
+}
+
+type GroupNew struct {
+ G *GroupNew_G `protobuf:"group,101,opt" json:"g,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupNew) Reset() { *m = GroupNew{} }
+func (m *GroupNew) String() string { return proto.CompactTextString(m) }
+func (*GroupNew) ProtoMessage() {}
+
+func (m *GroupNew) GetG() *GroupNew_G {
+ if m != nil {
+ return m.G
+ }
+ return nil
+}
+
+type GroupNew_G struct {
+ X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"`
+ Y *int32 `protobuf:"varint,3,opt,name=y" json:"y,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupNew_G) Reset() { *m = GroupNew_G{} }
+func (m *GroupNew_G) String() string { return proto.CompactTextString(m) }
+func (*GroupNew_G) ProtoMessage() {}
+
+func (m *GroupNew_G) GetX() int32 {
+ if m != nil && m.X != nil {
+ return *m.X
+ }
+ return 0
+}
+
+func (m *GroupNew_G) GetY() int32 {
+ if m != nil && m.Y != nil {
+ return *m.Y
+ }
+ return 0
+}
+
+type FloatingPoint struct {
+ F *float64 `protobuf:"fixed64,1,req,name=f" json:"f,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *FloatingPoint) Reset() { *m = FloatingPoint{} }
+func (m *FloatingPoint) String() string { return proto.CompactTextString(m) }
+func (*FloatingPoint) ProtoMessage() {}
+
+func (m *FloatingPoint) GetF() float64 {
+ if m != nil && m.F != nil {
+ return *m.F
+ }
+ return 0
+}
+
+type MessageWithMap struct {
+ NameMapping map[int32]string `protobuf:"bytes,1,rep,name=name_mapping" json:"name_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ MsgMapping map[int64]*FloatingPoint `protobuf:"bytes,2,rep,name=msg_mapping" json:"msg_mapping,omitempty" protobuf_key:"zigzag64,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ ByteMapping map[bool][]byte `protobuf:"bytes,3,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ StrToStr map[string]string `protobuf:"bytes,4,rep,name=str_to_str" json:"str_to_str,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
+func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
+func (*MessageWithMap) ProtoMessage() {}
+
+func (m *MessageWithMap) GetNameMapping() map[int32]string {
+ if m != nil {
+ return m.NameMapping
+ }
+ return nil
+}
+
+func (m *MessageWithMap) GetMsgMapping() map[int64]*FloatingPoint {
+ if m != nil {
+ return m.MsgMapping
+ }
+ return nil
+}
+
+func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
+ if m != nil {
+ return m.ByteMapping
+ }
+ return nil
+}
+
+func (m *MessageWithMap) GetStrToStr() map[string]string {
+ if m != nil {
+ return m.StrToStr
+ }
+ return nil
+}
+
+var E_Greeting = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: ([]string)(nil),
+ Field: 106,
+ Name: "testdata.greeting",
+ Tag: "bytes,106,rep,name=greeting",
+}
+
+var E_X201 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 201,
+ Name: "testdata.x201",
+ Tag: "bytes,201,opt,name=x201",
+}
+
+var E_X202 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 202,
+ Name: "testdata.x202",
+ Tag: "bytes,202,opt,name=x202",
+}
+
+var E_X203 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 203,
+ Name: "testdata.x203",
+ Tag: "bytes,203,opt,name=x203",
+}
+
+var E_X204 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 204,
+ Name: "testdata.x204",
+ Tag: "bytes,204,opt,name=x204",
+}
+
+var E_X205 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 205,
+ Name: "testdata.x205",
+ Tag: "bytes,205,opt,name=x205",
+}
+
+var E_X206 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 206,
+ Name: "testdata.x206",
+ Tag: "bytes,206,opt,name=x206",
+}
+
+var E_X207 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 207,
+ Name: "testdata.x207",
+ Tag: "bytes,207,opt,name=x207",
+}
+
+var E_X208 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 208,
+ Name: "testdata.x208",
+ Tag: "bytes,208,opt,name=x208",
+}
+
+var E_X209 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 209,
+ Name: "testdata.x209",
+ Tag: "bytes,209,opt,name=x209",
+}
+
+var E_X210 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 210,
+ Name: "testdata.x210",
+ Tag: "bytes,210,opt,name=x210",
+}
+
+var E_X211 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 211,
+ Name: "testdata.x211",
+ Tag: "bytes,211,opt,name=x211",
+}
+
+var E_X212 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 212,
+ Name: "testdata.x212",
+ Tag: "bytes,212,opt,name=x212",
+}
+
+var E_X213 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 213,
+ Name: "testdata.x213",
+ Tag: "bytes,213,opt,name=x213",
+}
+
+var E_X214 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 214,
+ Name: "testdata.x214",
+ Tag: "bytes,214,opt,name=x214",
+}
+
+var E_X215 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 215,
+ Name: "testdata.x215",
+ Tag: "bytes,215,opt,name=x215",
+}
+
+var E_X216 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 216,
+ Name: "testdata.x216",
+ Tag: "bytes,216,opt,name=x216",
+}
+
+var E_X217 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 217,
+ Name: "testdata.x217",
+ Tag: "bytes,217,opt,name=x217",
+}
+
+var E_X218 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 218,
+ Name: "testdata.x218",
+ Tag: "bytes,218,opt,name=x218",
+}
+
+var E_X219 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 219,
+ Name: "testdata.x219",
+ Tag: "bytes,219,opt,name=x219",
+}
+
+var E_X220 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 220,
+ Name: "testdata.x220",
+ Tag: "bytes,220,opt,name=x220",
+}
+
+var E_X221 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 221,
+ Name: "testdata.x221",
+ Tag: "bytes,221,opt,name=x221",
+}
+
+var E_X222 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 222,
+ Name: "testdata.x222",
+ Tag: "bytes,222,opt,name=x222",
+}
+
+var E_X223 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 223,
+ Name: "testdata.x223",
+ Tag: "bytes,223,opt,name=x223",
+}
+
+var E_X224 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 224,
+ Name: "testdata.x224",
+ Tag: "bytes,224,opt,name=x224",
+}
+
+var E_X225 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 225,
+ Name: "testdata.x225",
+ Tag: "bytes,225,opt,name=x225",
+}
+
+var E_X226 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 226,
+ Name: "testdata.x226",
+ Tag: "bytes,226,opt,name=x226",
+}
+
+var E_X227 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 227,
+ Name: "testdata.x227",
+ Tag: "bytes,227,opt,name=x227",
+}
+
+var E_X228 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 228,
+ Name: "testdata.x228",
+ Tag: "bytes,228,opt,name=x228",
+}
+
+var E_X229 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 229,
+ Name: "testdata.x229",
+ Tag: "bytes,229,opt,name=x229",
+}
+
+var E_X230 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 230,
+ Name: "testdata.x230",
+ Tag: "bytes,230,opt,name=x230",
+}
+
+var E_X231 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 231,
+ Name: "testdata.x231",
+ Tag: "bytes,231,opt,name=x231",
+}
+
+var E_X232 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 232,
+ Name: "testdata.x232",
+ Tag: "bytes,232,opt,name=x232",
+}
+
+var E_X233 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 233,
+ Name: "testdata.x233",
+ Tag: "bytes,233,opt,name=x233",
+}
+
+var E_X234 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 234,
+ Name: "testdata.x234",
+ Tag: "bytes,234,opt,name=x234",
+}
+
+var E_X235 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 235,
+ Name: "testdata.x235",
+ Tag: "bytes,235,opt,name=x235",
+}
+
+var E_X236 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 236,
+ Name: "testdata.x236",
+ Tag: "bytes,236,opt,name=x236",
+}
+
+var E_X237 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 237,
+ Name: "testdata.x237",
+ Tag: "bytes,237,opt,name=x237",
+}
+
+var E_X238 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 238,
+ Name: "testdata.x238",
+ Tag: "bytes,238,opt,name=x238",
+}
+
+var E_X239 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 239,
+ Name: "testdata.x239",
+ Tag: "bytes,239,opt,name=x239",
+}
+
+var E_X240 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 240,
+ Name: "testdata.x240",
+ Tag: "bytes,240,opt,name=x240",
+}
+
+var E_X241 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 241,
+ Name: "testdata.x241",
+ Tag: "bytes,241,opt,name=x241",
+}
+
+var E_X242 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 242,
+ Name: "testdata.x242",
+ Tag: "bytes,242,opt,name=x242",
+}
+
+var E_X243 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 243,
+ Name: "testdata.x243",
+ Tag: "bytes,243,opt,name=x243",
+}
+
+var E_X244 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 244,
+ Name: "testdata.x244",
+ Tag: "bytes,244,opt,name=x244",
+}
+
+var E_X245 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 245,
+ Name: "testdata.x245",
+ Tag: "bytes,245,opt,name=x245",
+}
+
+var E_X246 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 246,
+ Name: "testdata.x246",
+ Tag: "bytes,246,opt,name=x246",
+}
+
+var E_X247 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 247,
+ Name: "testdata.x247",
+ Tag: "bytes,247,opt,name=x247",
+}
+
+var E_X248 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 248,
+ Name: "testdata.x248",
+ Tag: "bytes,248,opt,name=x248",
+}
+
+var E_X249 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 249,
+ Name: "testdata.x249",
+ Tag: "bytes,249,opt,name=x249",
+}
+
+var E_X250 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 250,
+ Name: "testdata.x250",
+ Tag: "bytes,250,opt,name=x250",
+}
+
+func init() {
+ proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value)
+ proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value)
+ proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value)
+ proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value)
+ proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value)
+ proto.RegisterExtension(E_Ext_More)
+ proto.RegisterExtension(E_Ext_Text)
+ proto.RegisterExtension(E_Ext_Number)
+ proto.RegisterExtension(E_Greeting)
+ proto.RegisterExtension(E_X201)
+ proto.RegisterExtension(E_X202)
+ proto.RegisterExtension(E_X203)
+ proto.RegisterExtension(E_X204)
+ proto.RegisterExtension(E_X205)
+ proto.RegisterExtension(E_X206)
+ proto.RegisterExtension(E_X207)
+ proto.RegisterExtension(E_X208)
+ proto.RegisterExtension(E_X209)
+ proto.RegisterExtension(E_X210)
+ proto.RegisterExtension(E_X211)
+ proto.RegisterExtension(E_X212)
+ proto.RegisterExtension(E_X213)
+ proto.RegisterExtension(E_X214)
+ proto.RegisterExtension(E_X215)
+ proto.RegisterExtension(E_X216)
+ proto.RegisterExtension(E_X217)
+ proto.RegisterExtension(E_X218)
+ proto.RegisterExtension(E_X219)
+ proto.RegisterExtension(E_X220)
+ proto.RegisterExtension(E_X221)
+ proto.RegisterExtension(E_X222)
+ proto.RegisterExtension(E_X223)
+ proto.RegisterExtension(E_X224)
+ proto.RegisterExtension(E_X225)
+ proto.RegisterExtension(E_X226)
+ proto.RegisterExtension(E_X227)
+ proto.RegisterExtension(E_X228)
+ proto.RegisterExtension(E_X229)
+ proto.RegisterExtension(E_X230)
+ proto.RegisterExtension(E_X231)
+ proto.RegisterExtension(E_X232)
+ proto.RegisterExtension(E_X233)
+ proto.RegisterExtension(E_X234)
+ proto.RegisterExtension(E_X235)
+ proto.RegisterExtension(E_X236)
+ proto.RegisterExtension(E_X237)
+ proto.RegisterExtension(E_X238)
+ proto.RegisterExtension(E_X239)
+ proto.RegisterExtension(E_X240)
+ proto.RegisterExtension(E_X241)
+ proto.RegisterExtension(E_X242)
+ proto.RegisterExtension(E_X243)
+ proto.RegisterExtension(E_X244)
+ proto.RegisterExtension(E_X245)
+ proto.RegisterExtension(E_X246)
+ proto.RegisterExtension(E_X247)
+ proto.RegisterExtension(E_X248)
+ proto.RegisterExtension(E_X249)
+ proto.RegisterExtension(E_X250)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.pb.go.golden b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.pb.go.golden
new file mode 100644
index 000000000..0387853d5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.pb.go.golden
@@ -0,0 +1,1737 @@
+// Code generated by protoc-gen-gogo.
+// source: test.proto
+// DO NOT EDIT!
+
+package testdata
+
+import proto "github.com/gogo/protobuf/proto"
+import json "encoding/json"
+import math "math"
+
+import ()
+
+// Reference proto, json, and math imports to suppress error if they are not otherwise used.
+var _ = proto.Marshal
+var _ = &json.SyntaxError{}
+var _ = math.Inf
+
+type FOO int32
+
+const (
+ FOO_FOO1 FOO = 1
+)
+
+var FOO_name = map[int32]string{
+ 1: "FOO1",
+}
+var FOO_value = map[string]int32{
+ "FOO1": 1,
+}
+
+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) MarshalJSON() ([]byte, error) {
+ return json.Marshal(x.String())
+}
+func (x *FOO) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(FOO_value, data, "FOO")
+ if err != nil {
+ return err
+ }
+ *x = FOO(value)
+ return nil
+}
+
+type GoTest_KIND int32
+
+const (
+ GoTest_VOID GoTest_KIND = 0
+ GoTest_BOOL GoTest_KIND = 1
+ GoTest_BYTES GoTest_KIND = 2
+ GoTest_FINGERPRINT GoTest_KIND = 3
+ GoTest_FLOAT GoTest_KIND = 4
+ GoTest_INT GoTest_KIND = 5
+ GoTest_STRING GoTest_KIND = 6
+ GoTest_TIME GoTest_KIND = 7
+ GoTest_TUPLE GoTest_KIND = 8
+ GoTest_ARRAY GoTest_KIND = 9
+ GoTest_MAP GoTest_KIND = 10
+ GoTest_TABLE GoTest_KIND = 11
+ GoTest_FUNCTION GoTest_KIND = 12
+)
+
+var GoTest_KIND_name = map[int32]string{
+ 0: "VOID",
+ 1: "BOOL",
+ 2: "BYTES",
+ 3: "FINGERPRINT",
+ 4: "FLOAT",
+ 5: "INT",
+ 6: "STRING",
+ 7: "TIME",
+ 8: "TUPLE",
+ 9: "ARRAY",
+ 10: "MAP",
+ 11: "TABLE",
+ 12: "FUNCTION",
+}
+var GoTest_KIND_value = map[string]int32{
+ "VOID": 0,
+ "BOOL": 1,
+ "BYTES": 2,
+ "FINGERPRINT": 3,
+ "FLOAT": 4,
+ "INT": 5,
+ "STRING": 6,
+ "TIME": 7,
+ "TUPLE": 8,
+ "ARRAY": 9,
+ "MAP": 10,
+ "TABLE": 11,
+ "FUNCTION": 12,
+}
+
+func (x GoTest_KIND) Enum() *GoTest_KIND {
+ p := new(GoTest_KIND)
+ *p = x
+ return p
+}
+func (x GoTest_KIND) String() string {
+ return proto.EnumName(GoTest_KIND_name, int32(x))
+}
+func (x GoTest_KIND) MarshalJSON() ([]byte, error) {
+ return json.Marshal(x.String())
+}
+func (x *GoTest_KIND) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(GoTest_KIND_value, data, "GoTest_KIND")
+ if err != nil {
+ return err
+ }
+ *x = GoTest_KIND(value)
+ return nil
+}
+
+type MyMessage_Color int32
+
+const (
+ MyMessage_RED MyMessage_Color = 0
+ MyMessage_GREEN MyMessage_Color = 1
+ MyMessage_BLUE MyMessage_Color = 2
+)
+
+var MyMessage_Color_name = map[int32]string{
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
+}
+var MyMessage_Color_value = map[string]int32{
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
+}
+
+func (x MyMessage_Color) Enum() *MyMessage_Color {
+ p := new(MyMessage_Color)
+ *p = x
+ return p
+}
+func (x MyMessage_Color) String() string {
+ return proto.EnumName(MyMessage_Color_name, int32(x))
+}
+func (x MyMessage_Color) MarshalJSON() ([]byte, error) {
+ return json.Marshal(x.String())
+}
+func (x *MyMessage_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MyMessage_Color_value, data, "MyMessage_Color")
+ if err != nil {
+ return err
+ }
+ *x = MyMessage_Color(value)
+ return nil
+}
+
+type Defaults_Color int32
+
+const (
+ Defaults_RED Defaults_Color = 0
+ Defaults_GREEN Defaults_Color = 1
+ Defaults_BLUE Defaults_Color = 2
+)
+
+var Defaults_Color_name = map[int32]string{
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
+}
+var Defaults_Color_value = map[string]int32{
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
+}
+
+func (x Defaults_Color) Enum() *Defaults_Color {
+ p := new(Defaults_Color)
+ *p = x
+ return p
+}
+func (x Defaults_Color) String() string {
+ return proto.EnumName(Defaults_Color_name, int32(x))
+}
+func (x Defaults_Color) MarshalJSON() ([]byte, error) {
+ return json.Marshal(x.String())
+}
+func (x *Defaults_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(Defaults_Color_value, data, "Defaults_Color")
+ if err != nil {
+ return err
+ }
+ *x = Defaults_Color(value)
+ return nil
+}
+
+type RepeatedEnum_Color int32
+
+const (
+ RepeatedEnum_RED RepeatedEnum_Color = 1
+)
+
+var RepeatedEnum_Color_name = map[int32]string{
+ 1: "RED",
+}
+var RepeatedEnum_Color_value = map[string]int32{
+ "RED": 1,
+}
+
+func (x RepeatedEnum_Color) Enum() *RepeatedEnum_Color {
+ p := new(RepeatedEnum_Color)
+ *p = x
+ return p
+}
+func (x RepeatedEnum_Color) String() string {
+ return proto.EnumName(RepeatedEnum_Color_name, int32(x))
+}
+func (x RepeatedEnum_Color) MarshalJSON() ([]byte, error) {
+ return json.Marshal(x.String())
+}
+func (x *RepeatedEnum_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(RepeatedEnum_Color_value, data, "RepeatedEnum_Color")
+ if err != nil {
+ return err
+ }
+ *x = RepeatedEnum_Color(value)
+ return nil
+}
+
+type GoEnum struct {
+ Foo *FOO `protobuf:"varint,1,req,name=foo,enum=testdata.FOO" json:"foo,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoEnum) Reset() { *m = GoEnum{} }
+func (m *GoEnum) String() string { return proto.CompactTextString(m) }
+func (*GoEnum) ProtoMessage() {}
+
+func (m *GoEnum) GetFoo() FOO {
+ if m != nil && m.Foo != nil {
+ return *m.Foo
+ }
+ return 0
+}
+
+type GoTestField struct {
+ Label *string `protobuf:"bytes,1,req" json:"Label,omitempty"`
+ Type *string `protobuf:"bytes,2,req" json:"Type,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTestField) Reset() { *m = GoTestField{} }
+func (m *GoTestField) String() string { return proto.CompactTextString(m) }
+func (*GoTestField) ProtoMessage() {}
+
+func (m *GoTestField) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *GoTestField) GetType() string {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return ""
+}
+
+type GoTest struct {
+ Kind *GoTest_KIND `protobuf:"varint,1,req,enum=testdata.GoTest_KIND" json:"Kind,omitempty"`
+ Table *string `protobuf:"bytes,2,opt" json:"Table,omitempty"`
+ Param *int32 `protobuf:"varint,3,opt" json:"Param,omitempty"`
+ RequiredField *GoTestField `protobuf:"bytes,4,req" json:"RequiredField,omitempty"`
+ RepeatedField []*GoTestField `protobuf:"bytes,5,rep" json:"RepeatedField,omitempty"`
+ OptionalField *GoTestField `protobuf:"bytes,6,opt" json:"OptionalField,omitempty"`
+ F_BoolRequired *bool `protobuf:"varint,10,req,name=F_Bool_required" json:"F_Bool_required,omitempty"`
+ F_Int32Required *int32 `protobuf:"varint,11,req,name=F_Int32_required" json:"F_Int32_required,omitempty"`
+ F_Int64Required *int64 `protobuf:"varint,12,req,name=F_Int64_required" json:"F_Int64_required,omitempty"`
+ F_Fixed32Required *uint32 `protobuf:"fixed32,13,req,name=F_Fixed32_required" json:"F_Fixed32_required,omitempty"`
+ F_Fixed64Required *uint64 `protobuf:"fixed64,14,req,name=F_Fixed64_required" json:"F_Fixed64_required,omitempty"`
+ F_Uint32Required *uint32 `protobuf:"varint,15,req,name=F_Uint32_required" json:"F_Uint32_required,omitempty"`
+ F_Uint64Required *uint64 `protobuf:"varint,16,req,name=F_Uint64_required" json:"F_Uint64_required,omitempty"`
+ F_FloatRequired *float32 `protobuf:"fixed32,17,req,name=F_Float_required" json:"F_Float_required,omitempty"`
+ F_DoubleRequired *float64 `protobuf:"fixed64,18,req,name=F_Double_required" json:"F_Double_required,omitempty"`
+ F_StringRequired *string `protobuf:"bytes,19,req,name=F_String_required" json:"F_String_required,omitempty"`
+ F_BytesRequired []byte `protobuf:"bytes,101,req,name=F_Bytes_required" json:"F_Bytes_required,omitempty"`
+ F_Sint32Required *int32 `protobuf:"zigzag32,102,req,name=F_Sint32_required" json:"F_Sint32_required,omitempty"`
+ F_Sint64Required *int64 `protobuf:"zigzag64,103,req,name=F_Sint64_required" json:"F_Sint64_required,omitempty"`
+ F_BoolRepeated []bool `protobuf:"varint,20,rep,name=F_Bool_repeated" json:"F_Bool_repeated,omitempty"`
+ F_Int32Repeated []int32 `protobuf:"varint,21,rep,name=F_Int32_repeated" json:"F_Int32_repeated,omitempty"`
+ F_Int64Repeated []int64 `protobuf:"varint,22,rep,name=F_Int64_repeated" json:"F_Int64_repeated,omitempty"`
+ F_Fixed32Repeated []uint32 `protobuf:"fixed32,23,rep,name=F_Fixed32_repeated" json:"F_Fixed32_repeated,omitempty"`
+ F_Fixed64Repeated []uint64 `protobuf:"fixed64,24,rep,name=F_Fixed64_repeated" json:"F_Fixed64_repeated,omitempty"`
+ F_Uint32Repeated []uint32 `protobuf:"varint,25,rep,name=F_Uint32_repeated" json:"F_Uint32_repeated,omitempty"`
+ F_Uint64Repeated []uint64 `protobuf:"varint,26,rep,name=F_Uint64_repeated" json:"F_Uint64_repeated,omitempty"`
+ F_FloatRepeated []float32 `protobuf:"fixed32,27,rep,name=F_Float_repeated" json:"F_Float_repeated,omitempty"`
+ F_DoubleRepeated []float64 `protobuf:"fixed64,28,rep,name=F_Double_repeated" json:"F_Double_repeated,omitempty"`
+ F_StringRepeated []string `protobuf:"bytes,29,rep,name=F_String_repeated" json:"F_String_repeated,omitempty"`
+ F_BytesRepeated [][]byte `protobuf:"bytes,201,rep,name=F_Bytes_repeated" json:"F_Bytes_repeated,omitempty"`
+ F_Sint32Repeated []int32 `protobuf:"zigzag32,202,rep,name=F_Sint32_repeated" json:"F_Sint32_repeated,omitempty"`
+ F_Sint64Repeated []int64 `protobuf:"zigzag64,203,rep,name=F_Sint64_repeated" json:"F_Sint64_repeated,omitempty"`
+ F_BoolOptional *bool `protobuf:"varint,30,opt,name=F_Bool_optional" json:"F_Bool_optional,omitempty"`
+ F_Int32Optional *int32 `protobuf:"varint,31,opt,name=F_Int32_optional" json:"F_Int32_optional,omitempty"`
+ F_Int64Optional *int64 `protobuf:"varint,32,opt,name=F_Int64_optional" json:"F_Int64_optional,omitempty"`
+ F_Fixed32Optional *uint32 `protobuf:"fixed32,33,opt,name=F_Fixed32_optional" json:"F_Fixed32_optional,omitempty"`
+ F_Fixed64Optional *uint64 `protobuf:"fixed64,34,opt,name=F_Fixed64_optional" json:"F_Fixed64_optional,omitempty"`
+ F_Uint32Optional *uint32 `protobuf:"varint,35,opt,name=F_Uint32_optional" json:"F_Uint32_optional,omitempty"`
+ F_Uint64Optional *uint64 `protobuf:"varint,36,opt,name=F_Uint64_optional" json:"F_Uint64_optional,omitempty"`
+ F_FloatOptional *float32 `protobuf:"fixed32,37,opt,name=F_Float_optional" json:"F_Float_optional,omitempty"`
+ F_DoubleOptional *float64 `protobuf:"fixed64,38,opt,name=F_Double_optional" json:"F_Double_optional,omitempty"`
+ F_StringOptional *string `protobuf:"bytes,39,opt,name=F_String_optional" json:"F_String_optional,omitempty"`
+ F_BytesOptional []byte `protobuf:"bytes,301,opt,name=F_Bytes_optional" json:"F_Bytes_optional,omitempty"`
+ F_Sint32Optional *int32 `protobuf:"zigzag32,302,opt,name=F_Sint32_optional" json:"F_Sint32_optional,omitempty"`
+ F_Sint64Optional *int64 `protobuf:"zigzag64,303,opt,name=F_Sint64_optional" json:"F_Sint64_optional,omitempty"`
+ F_BoolDefaulted *bool `protobuf:"varint,40,opt,name=F_Bool_defaulted,def=1" json:"F_Bool_defaulted,omitempty"`
+ F_Int32Defaulted *int32 `protobuf:"varint,41,opt,name=F_Int32_defaulted,def=32" json:"F_Int32_defaulted,omitempty"`
+ F_Int64Defaulted *int64 `protobuf:"varint,42,opt,name=F_Int64_defaulted,def=64" json:"F_Int64_defaulted,omitempty"`
+ F_Fixed32Defaulted *uint32 `protobuf:"fixed32,43,opt,name=F_Fixed32_defaulted,def=320" json:"F_Fixed32_defaulted,omitempty"`
+ F_Fixed64Defaulted *uint64 `protobuf:"fixed64,44,opt,name=F_Fixed64_defaulted,def=640" json:"F_Fixed64_defaulted,omitempty"`
+ F_Uint32Defaulted *uint32 `protobuf:"varint,45,opt,name=F_Uint32_defaulted,def=3200" json:"F_Uint32_defaulted,omitempty"`
+ F_Uint64Defaulted *uint64 `protobuf:"varint,46,opt,name=F_Uint64_defaulted,def=6400" json:"F_Uint64_defaulted,omitempty"`
+ F_FloatDefaulted *float32 `protobuf:"fixed32,47,opt,name=F_Float_defaulted,def=314159" json:"F_Float_defaulted,omitempty"`
+ F_DoubleDefaulted *float64 `protobuf:"fixed64,48,opt,name=F_Double_defaulted,def=271828" json:"F_Double_defaulted,omitempty"`
+ F_StringDefaulted *string `protobuf:"bytes,49,opt,name=F_String_defaulted,def=hello, \"world!\"\n" json:"F_String_defaulted,omitempty"`
+ F_BytesDefaulted []byte `protobuf:"bytes,401,opt,name=F_Bytes_defaulted,def=Bignose" json:"F_Bytes_defaulted,omitempty"`
+ F_Sint32Defaulted *int32 `protobuf:"zigzag32,402,opt,name=F_Sint32_defaulted,def=-32" json:"F_Sint32_defaulted,omitempty"`
+ F_Sint64Defaulted *int64 `protobuf:"zigzag64,403,opt,name=F_Sint64_defaulted,def=-64" json:"F_Sint64_defaulted,omitempty"`
+ F_BoolRepeatedPacked []bool `protobuf:"varint,50,rep,packed,name=F_Bool_repeated_packed" json:"F_Bool_repeated_packed,omitempty"`
+ F_Int32RepeatedPacked []int32 `protobuf:"varint,51,rep,packed,name=F_Int32_repeated_packed" json:"F_Int32_repeated_packed,omitempty"`
+ F_Int64RepeatedPacked []int64 `protobuf:"varint,52,rep,packed,name=F_Int64_repeated_packed" json:"F_Int64_repeated_packed,omitempty"`
+ F_Fixed32RepeatedPacked []uint32 `protobuf:"fixed32,53,rep,packed,name=F_Fixed32_repeated_packed" json:"F_Fixed32_repeated_packed,omitempty"`
+ F_Fixed64RepeatedPacked []uint64 `protobuf:"fixed64,54,rep,packed,name=F_Fixed64_repeated_packed" json:"F_Fixed64_repeated_packed,omitempty"`
+ F_Uint32RepeatedPacked []uint32 `protobuf:"varint,55,rep,packed,name=F_Uint32_repeated_packed" json:"F_Uint32_repeated_packed,omitempty"`
+ F_Uint64RepeatedPacked []uint64 `protobuf:"varint,56,rep,packed,name=F_Uint64_repeated_packed" json:"F_Uint64_repeated_packed,omitempty"`
+ F_FloatRepeatedPacked []float32 `protobuf:"fixed32,57,rep,packed,name=F_Float_repeated_packed" json:"F_Float_repeated_packed,omitempty"`
+ F_DoubleRepeatedPacked []float64 `protobuf:"fixed64,58,rep,packed,name=F_Double_repeated_packed" json:"F_Double_repeated_packed,omitempty"`
+ F_Sint32RepeatedPacked []int32 `protobuf:"zigzag32,502,rep,packed,name=F_Sint32_repeated_packed" json:"F_Sint32_repeated_packed,omitempty"`
+ F_Sint64RepeatedPacked []int64 `protobuf:"zigzag64,503,rep,packed,name=F_Sint64_repeated_packed" json:"F_Sint64_repeated_packed,omitempty"`
+ Requiredgroup *GoTest_RequiredGroup `protobuf:"group,70,req,name=RequiredGroup" json:"requiredgroup,omitempty"`
+ Repeatedgroup []*GoTest_RepeatedGroup `protobuf:"group,80,rep,name=RepeatedGroup" json:"repeatedgroup,omitempty"`
+ Optionalgroup *GoTest_OptionalGroup `protobuf:"group,90,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest) Reset() { *m = GoTest{} }
+func (m *GoTest) String() string { return proto.CompactTextString(m) }
+func (*GoTest) ProtoMessage() {}
+
+const Default_GoTest_F_BoolDefaulted bool = true
+const Default_GoTest_F_Int32Defaulted int32 = 32
+const Default_GoTest_F_Int64Defaulted int64 = 64
+const Default_GoTest_F_Fixed32Defaulted uint32 = 320
+const Default_GoTest_F_Fixed64Defaulted uint64 = 640
+const Default_GoTest_F_Uint32Defaulted uint32 = 3200
+const Default_GoTest_F_Uint64Defaulted uint64 = 6400
+const Default_GoTest_F_FloatDefaulted float32 = 314159
+const Default_GoTest_F_DoubleDefaulted float64 = 271828
+const Default_GoTest_F_StringDefaulted string = "hello, \"world!\"\n"
+
+var Default_GoTest_F_BytesDefaulted []byte = []byte("Bignose")
+
+const Default_GoTest_F_Sint32Defaulted int32 = -32
+const Default_GoTest_F_Sint64Defaulted int64 = -64
+
+func (m *GoTest) GetKind() GoTest_KIND {
+ if m != nil && m.Kind != nil {
+ return *m.Kind
+ }
+ return 0
+}
+
+func (m *GoTest) GetTable() string {
+ if m != nil && m.Table != nil {
+ return *m.Table
+ }
+ return ""
+}
+
+func (m *GoTest) GetParam() int32 {
+ if m != nil && m.Param != nil {
+ return *m.Param
+ }
+ return 0
+}
+
+func (m *GoTest) GetRequiredField() *GoTestField {
+ if m != nil {
+ return m.RequiredField
+ }
+ return nil
+}
+
+func (m *GoTest) GetRepeatedField() []*GoTestField {
+ if m != nil {
+ return m.RepeatedField
+ }
+ return nil
+}
+
+func (m *GoTest) GetOptionalField() *GoTestField {
+ if m != nil {
+ return m.OptionalField
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BoolRequired() bool {
+ if m != nil && m.F_BoolRequired != nil {
+ return *m.F_BoolRequired
+ }
+ return false
+}
+
+func (m *GoTest) GetF_Int32Required() int32 {
+ if m != nil && m.F_Int32Required != nil {
+ return *m.F_Int32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Int64Required() int64 {
+ if m != nil && m.F_Int64Required != nil {
+ return *m.F_Int64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed32Required() uint32 {
+ if m != nil && m.F_Fixed32Required != nil {
+ return *m.F_Fixed32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed64Required() uint64 {
+ if m != nil && m.F_Fixed64Required != nil {
+ return *m.F_Fixed64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint32Required() uint32 {
+ if m != nil && m.F_Uint32Required != nil {
+ return *m.F_Uint32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint64Required() uint64 {
+ if m != nil && m.F_Uint64Required != nil {
+ return *m.F_Uint64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_FloatRequired() float32 {
+ if m != nil && m.F_FloatRequired != nil {
+ return *m.F_FloatRequired
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_DoubleRequired() float64 {
+ if m != nil && m.F_DoubleRequired != nil {
+ return *m.F_DoubleRequired
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_StringRequired() string {
+ if m != nil && m.F_StringRequired != nil {
+ return *m.F_StringRequired
+ }
+ return ""
+}
+
+func (m *GoTest) GetF_BytesRequired() []byte {
+ if m != nil {
+ return m.F_BytesRequired
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Required() int32 {
+ if m != nil && m.F_Sint32Required != nil {
+ return *m.F_Sint32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Sint64Required() int64 {
+ if m != nil && m.F_Sint64Required != nil {
+ return *m.F_Sint64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_BoolRepeated() []bool {
+ if m != nil {
+ return m.F_BoolRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int32Repeated() []int32 {
+ if m != nil {
+ return m.F_Int32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int64Repeated() []int64 {
+ if m != nil {
+ return m.F_Int64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed32Repeated() []uint32 {
+ if m != nil {
+ return m.F_Fixed32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed64Repeated() []uint64 {
+ if m != nil {
+ return m.F_Fixed64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint32Repeated() []uint32 {
+ if m != nil {
+ return m.F_Uint32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint64Repeated() []uint64 {
+ if m != nil {
+ return m.F_Uint64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_FloatRepeated() []float32 {
+ if m != nil {
+ return m.F_FloatRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_DoubleRepeated() []float64 {
+ if m != nil {
+ return m.F_DoubleRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_StringRepeated() []string {
+ if m != nil {
+ return m.F_StringRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BytesRepeated() [][]byte {
+ if m != nil {
+ return m.F_BytesRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Repeated() []int32 {
+ if m != nil {
+ return m.F_Sint32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint64Repeated() []int64 {
+ if m != nil {
+ return m.F_Sint64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BoolOptional() bool {
+ if m != nil && m.F_BoolOptional != nil {
+ return *m.F_BoolOptional
+ }
+ return false
+}
+
+func (m *GoTest) GetF_Int32Optional() int32 {
+ if m != nil && m.F_Int32Optional != nil {
+ return *m.F_Int32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Int64Optional() int64 {
+ if m != nil && m.F_Int64Optional != nil {
+ return *m.F_Int64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed32Optional() uint32 {
+ if m != nil && m.F_Fixed32Optional != nil {
+ return *m.F_Fixed32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed64Optional() uint64 {
+ if m != nil && m.F_Fixed64Optional != nil {
+ return *m.F_Fixed64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint32Optional() uint32 {
+ if m != nil && m.F_Uint32Optional != nil {
+ return *m.F_Uint32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint64Optional() uint64 {
+ if m != nil && m.F_Uint64Optional != nil {
+ return *m.F_Uint64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_FloatOptional() float32 {
+ if m != nil && m.F_FloatOptional != nil {
+ return *m.F_FloatOptional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_DoubleOptional() float64 {
+ if m != nil && m.F_DoubleOptional != nil {
+ return *m.F_DoubleOptional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_StringOptional() string {
+ if m != nil && m.F_StringOptional != nil {
+ return *m.F_StringOptional
+ }
+ return ""
+}
+
+func (m *GoTest) GetF_BytesOptional() []byte {
+ if m != nil {
+ return m.F_BytesOptional
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Optional() int32 {
+ if m != nil && m.F_Sint32Optional != nil {
+ return *m.F_Sint32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Sint64Optional() int64 {
+ if m != nil && m.F_Sint64Optional != nil {
+ return *m.F_Sint64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_BoolDefaulted() bool {
+ if m != nil && m.F_BoolDefaulted != nil {
+ return *m.F_BoolDefaulted
+ }
+ return Default_GoTest_F_BoolDefaulted
+}
+
+func (m *GoTest) GetF_Int32Defaulted() int32 {
+ if m != nil && m.F_Int32Defaulted != nil {
+ return *m.F_Int32Defaulted
+ }
+ return Default_GoTest_F_Int32Defaulted
+}
+
+func (m *GoTest) GetF_Int64Defaulted() int64 {
+ if m != nil && m.F_Int64Defaulted != nil {
+ return *m.F_Int64Defaulted
+ }
+ return Default_GoTest_F_Int64Defaulted
+}
+
+func (m *GoTest) GetF_Fixed32Defaulted() uint32 {
+ if m != nil && m.F_Fixed32Defaulted != nil {
+ return *m.F_Fixed32Defaulted
+ }
+ return Default_GoTest_F_Fixed32Defaulted
+}
+
+func (m *GoTest) GetF_Fixed64Defaulted() uint64 {
+ if m != nil && m.F_Fixed64Defaulted != nil {
+ return *m.F_Fixed64Defaulted
+ }
+ return Default_GoTest_F_Fixed64Defaulted
+}
+
+func (m *GoTest) GetF_Uint32Defaulted() uint32 {
+ if m != nil && m.F_Uint32Defaulted != nil {
+ return *m.F_Uint32Defaulted
+ }
+ return Default_GoTest_F_Uint32Defaulted
+}
+
+func (m *GoTest) GetF_Uint64Defaulted() uint64 {
+ if m != nil && m.F_Uint64Defaulted != nil {
+ return *m.F_Uint64Defaulted
+ }
+ return Default_GoTest_F_Uint64Defaulted
+}
+
+func (m *GoTest) GetF_FloatDefaulted() float32 {
+ if m != nil && m.F_FloatDefaulted != nil {
+ return *m.F_FloatDefaulted
+ }
+ return Default_GoTest_F_FloatDefaulted
+}
+
+func (m *GoTest) GetF_DoubleDefaulted() float64 {
+ if m != nil && m.F_DoubleDefaulted != nil {
+ return *m.F_DoubleDefaulted
+ }
+ return Default_GoTest_F_DoubleDefaulted
+}
+
+func (m *GoTest) GetF_StringDefaulted() string {
+ if m != nil && m.F_StringDefaulted != nil {
+ return *m.F_StringDefaulted
+ }
+ return Default_GoTest_F_StringDefaulted
+}
+
+func (m *GoTest) GetF_BytesDefaulted() []byte {
+ if m != nil && m.F_BytesDefaulted != nil {
+ return m.F_BytesDefaulted
+ }
+ return append([]byte(nil), Default_GoTest_F_BytesDefaulted...)
+}
+
+func (m *GoTest) GetF_Sint32Defaulted() int32 {
+ if m != nil && m.F_Sint32Defaulted != nil {
+ return *m.F_Sint32Defaulted
+ }
+ return Default_GoTest_F_Sint32Defaulted
+}
+
+func (m *GoTest) GetF_Sint64Defaulted() int64 {
+ if m != nil && m.F_Sint64Defaulted != nil {
+ return *m.F_Sint64Defaulted
+ }
+ return Default_GoTest_F_Sint64Defaulted
+}
+
+func (m *GoTest) GetF_BoolRepeatedPacked() []bool {
+ if m != nil {
+ return m.F_BoolRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int32RepeatedPacked() []int32 {
+ if m != nil {
+ return m.F_Int32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int64RepeatedPacked() []int64 {
+ if m != nil {
+ return m.F_Int64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed32RepeatedPacked() []uint32 {
+ if m != nil {
+ return m.F_Fixed32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed64RepeatedPacked() []uint64 {
+ if m != nil {
+ return m.F_Fixed64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint32RepeatedPacked() []uint32 {
+ if m != nil {
+ return m.F_Uint32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint64RepeatedPacked() []uint64 {
+ if m != nil {
+ return m.F_Uint64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_FloatRepeatedPacked() []float32 {
+ if m != nil {
+ return m.F_FloatRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_DoubleRepeatedPacked() []float64 {
+ if m != nil {
+ return m.F_DoubleRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32RepeatedPacked() []int32 {
+ if m != nil {
+ return m.F_Sint32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint64RepeatedPacked() []int64 {
+ if m != nil {
+ return m.F_Sint64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetRequiredgroup() *GoTest_RequiredGroup {
+ if m != nil {
+ return m.Requiredgroup
+ }
+ return nil
+}
+
+func (m *GoTest) GetRepeatedgroup() []*GoTest_RepeatedGroup {
+ if m != nil {
+ return m.Repeatedgroup
+ }
+ return nil
+}
+
+func (m *GoTest) GetOptionalgroup() *GoTest_OptionalGroup {
+ if m != nil {
+ return m.Optionalgroup
+ }
+ return nil
+}
+
+type GoTest_RequiredGroup struct {
+ RequiredField *string `protobuf:"bytes,71,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_RequiredGroup) Reset() { *m = GoTest_RequiredGroup{} }
+
+func (m *GoTest_RequiredGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoTest_RepeatedGroup struct {
+ RequiredField *string `protobuf:"bytes,81,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_RepeatedGroup) Reset() { *m = GoTest_RepeatedGroup{} }
+
+func (m *GoTest_RepeatedGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoTest_OptionalGroup struct {
+ RequiredField *string `protobuf:"bytes,91,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_OptionalGroup) Reset() { *m = GoTest_OptionalGroup{} }
+
+func (m *GoTest_OptionalGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoSkipTest struct {
+ SkipInt32 *int32 `protobuf:"varint,11,req,name=skip_int32" json:"skip_int32,omitempty"`
+ SkipFixed32 *uint32 `protobuf:"fixed32,12,req,name=skip_fixed32" json:"skip_fixed32,omitempty"`
+ SkipFixed64 *uint64 `protobuf:"fixed64,13,req,name=skip_fixed64" json:"skip_fixed64,omitempty"`
+ SkipString *string `protobuf:"bytes,14,req,name=skip_string" json:"skip_string,omitempty"`
+ Skipgroup *GoSkipTest_SkipGroup `protobuf:"group,15,req,name=SkipGroup" json:"skipgroup,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoSkipTest) Reset() { *m = GoSkipTest{} }
+func (m *GoSkipTest) String() string { return proto.CompactTextString(m) }
+func (*GoSkipTest) ProtoMessage() {}
+
+func (m *GoSkipTest) GetSkipInt32() int32 {
+ if m != nil && m.SkipInt32 != nil {
+ return *m.SkipInt32
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipFixed32() uint32 {
+ if m != nil && m.SkipFixed32 != nil {
+ return *m.SkipFixed32
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipFixed64() uint64 {
+ if m != nil && m.SkipFixed64 != nil {
+ return *m.SkipFixed64
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipString() string {
+ if m != nil && m.SkipString != nil {
+ return *m.SkipString
+ }
+ return ""
+}
+
+func (m *GoSkipTest) GetSkipgroup() *GoSkipTest_SkipGroup {
+ if m != nil {
+ return m.Skipgroup
+ }
+ return nil
+}
+
+type GoSkipTest_SkipGroup struct {
+ GroupInt32 *int32 `protobuf:"varint,16,req,name=group_int32" json:"group_int32,omitempty"`
+ GroupString *string `protobuf:"bytes,17,req,name=group_string" json:"group_string,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoSkipTest_SkipGroup) Reset() { *m = GoSkipTest_SkipGroup{} }
+
+func (m *GoSkipTest_SkipGroup) GetGroupInt32() int32 {
+ if m != nil && m.GroupInt32 != nil {
+ return *m.GroupInt32
+ }
+ return 0
+}
+
+func (m *GoSkipTest_SkipGroup) GetGroupString() string {
+ if m != nil && m.GroupString != nil {
+ return *m.GroupString
+ }
+ return ""
+}
+
+type NonPackedTest struct {
+ A []int32 `protobuf:"varint,1,rep,name=a" json:"a,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NonPackedTest) Reset() { *m = NonPackedTest{} }
+func (m *NonPackedTest) String() string { return proto.CompactTextString(m) }
+func (*NonPackedTest) ProtoMessage() {}
+
+func (m *NonPackedTest) GetA() []int32 {
+ if m != nil {
+ return m.A
+ }
+ return nil
+}
+
+type PackedTest struct {
+ B []int32 `protobuf:"varint,1,rep,packed,name=b" json:"b,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PackedTest) Reset() { *m = PackedTest{} }
+func (m *PackedTest) String() string { return proto.CompactTextString(m) }
+func (*PackedTest) ProtoMessage() {}
+
+func (m *PackedTest) GetB() []int32 {
+ if m != nil {
+ return m.B
+ }
+ return nil
+}
+
+type MaxTag struct {
+ LastField *string `protobuf:"bytes,536870911,opt,name=last_field" json:"last_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MaxTag) Reset() { *m = MaxTag{} }
+func (m *MaxTag) String() string { return proto.CompactTextString(m) }
+func (*MaxTag) ProtoMessage() {}
+
+func (m *MaxTag) GetLastField() string {
+ if m != nil && m.LastField != nil {
+ return *m.LastField
+ }
+ return ""
+}
+
+type OldMessage struct {
+ Nested *OldMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OldMessage) Reset() { *m = OldMessage{} }
+func (m *OldMessage) String() string { return proto.CompactTextString(m) }
+func (*OldMessage) ProtoMessage() {}
+
+func (m *OldMessage) GetNested() *OldMessage_Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+type OldMessage_Nested struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OldMessage_Nested) Reset() { *m = OldMessage_Nested{} }
+func (m *OldMessage_Nested) String() string { return proto.CompactTextString(m) }
+func (*OldMessage_Nested) ProtoMessage() {}
+
+func (m *OldMessage_Nested) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+type NewMessage struct {
+ Nested *NewMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NewMessage) Reset() { *m = NewMessage{} }
+func (m *NewMessage) String() string { return proto.CompactTextString(m) }
+func (*NewMessage) ProtoMessage() {}
+
+func (m *NewMessage) GetNested() *NewMessage_Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+type NewMessage_Nested struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ FoodGroup *string `protobuf:"bytes,2,opt,name=food_group" json:"food_group,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NewMessage_Nested) Reset() { *m = NewMessage_Nested{} }
+func (m *NewMessage_Nested) String() string { return proto.CompactTextString(m) }
+func (*NewMessage_Nested) ProtoMessage() {}
+
+func (m *NewMessage_Nested) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *NewMessage_Nested) GetFoodGroup() string {
+ if m != nil && m.FoodGroup != nil {
+ return *m.FoodGroup
+ }
+ return ""
+}
+
+type InnerMessage struct {
+ Host *string `protobuf:"bytes,1,req,name=host" json:"host,omitempty"`
+ Port *int32 `protobuf:"varint,2,opt,name=port,def=4000" json:"port,omitempty"`
+ Connected *bool `protobuf:"varint,3,opt,name=connected" json:"connected,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *InnerMessage) Reset() { *m = InnerMessage{} }
+func (m *InnerMessage) String() string { return proto.CompactTextString(m) }
+func (*InnerMessage) ProtoMessage() {}
+
+const Default_InnerMessage_Port int32 = 4000
+
+func (m *InnerMessage) GetHost() string {
+ if m != nil && m.Host != nil {
+ return *m.Host
+ }
+ return ""
+}
+
+func (m *InnerMessage) GetPort() int32 {
+ if m != nil && m.Port != nil {
+ return *m.Port
+ }
+ return Default_InnerMessage_Port
+}
+
+func (m *InnerMessage) GetConnected() bool {
+ if m != nil && m.Connected != nil {
+ return *m.Connected
+ }
+ return false
+}
+
+type OtherMessage struct {
+ Key *int64 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"`
+ Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+ Weight *float32 `protobuf:"fixed32,3,opt,name=weight" json:"weight,omitempty"`
+ Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner" json:"inner,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OtherMessage) Reset() { *m = OtherMessage{} }
+func (m *OtherMessage) String() string { return proto.CompactTextString(m) }
+func (*OtherMessage) ProtoMessage() {}
+
+func (m *OtherMessage) GetKey() int64 {
+ if m != nil && m.Key != nil {
+ return *m.Key
+ }
+ return 0
+}
+
+func (m *OtherMessage) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *OtherMessage) GetWeight() float32 {
+ if m != nil && m.Weight != nil {
+ return *m.Weight
+ }
+ return 0
+}
+
+func (m *OtherMessage) GetInner() *InnerMessage {
+ if m != nil {
+ return m.Inner
+ }
+ return nil
+}
+
+type MyMessage struct {
+ Count *int32 `protobuf:"varint,1,req,name=count" json:"count,omitempty"`
+ Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
+ Quote *string `protobuf:"bytes,3,opt,name=quote" json:"quote,omitempty"`
+ Pet []string `protobuf:"bytes,4,rep,name=pet" json:"pet,omitempty"`
+ Inner *InnerMessage `protobuf:"bytes,5,opt,name=inner" json:"inner,omitempty"`
+ Others []*OtherMessage `protobuf:"bytes,6,rep,name=others" json:"others,omitempty"`
+ Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=testdata.MyMessage_Color" json:"bikeshed,omitempty"`
+ Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup,omitempty"`
+ RepBytes [][]byte `protobuf:"bytes,10,rep,name=rep_bytes" json:"rep_bytes,omitempty"`
+ Bigfloat *float64 `protobuf:"fixed64,11,opt,name=bigfloat" json:"bigfloat,omitempty"`
+ XXX_extensions map[int32]proto.Extension `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessage) Reset() { *m = MyMessage{} }
+func (m *MyMessage) String() string { return proto.CompactTextString(m) }
+func (*MyMessage) ProtoMessage() {}
+
+var extRange_MyMessage = []proto.ExtensionRange{
+ {100, 536870911},
+}
+
+func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange {
+ return extRange_MyMessage
+}
+func (m *MyMessage) ExtensionMap() map[int32]proto.Extension {
+ if m.XXX_extensions == nil {
+ m.XXX_extensions = make(map[int32]proto.Extension)
+ }
+ return m.XXX_extensions
+}
+
+func (m *MyMessage) GetCount() int32 {
+ if m != nil && m.Count != nil {
+ return *m.Count
+ }
+ return 0
+}
+
+func (m *MyMessage) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MyMessage) GetQuote() string {
+ if m != nil && m.Quote != nil {
+ return *m.Quote
+ }
+ return ""
+}
+
+func (m *MyMessage) GetPet() []string {
+ if m != nil {
+ return m.Pet
+ }
+ return nil
+}
+
+func (m *MyMessage) GetInner() *InnerMessage {
+ if m != nil {
+ return m.Inner
+ }
+ return nil
+}
+
+func (m *MyMessage) GetOthers() []*OtherMessage {
+ if m != nil {
+ return m.Others
+ }
+ return nil
+}
+
+func (m *MyMessage) GetBikeshed() MyMessage_Color {
+ if m != nil && m.Bikeshed != nil {
+ return *m.Bikeshed
+ }
+ return 0
+}
+
+func (m *MyMessage) GetSomegroup() *MyMessage_SomeGroup {
+ if m != nil {
+ return m.Somegroup
+ }
+ return nil
+}
+
+func (m *MyMessage) GetRepBytes() [][]byte {
+ if m != nil {
+ return m.RepBytes
+ }
+ return nil
+}
+
+func (m *MyMessage) GetBigfloat() float64 {
+ if m != nil && m.Bigfloat != nil {
+ return *m.Bigfloat
+ }
+ return 0
+}
+
+type MyMessage_SomeGroup struct {
+ GroupField *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessage_SomeGroup) Reset() { *m = MyMessage_SomeGroup{} }
+
+func (m *MyMessage_SomeGroup) GetGroupField() int32 {
+ if m != nil && m.GroupField != nil {
+ return *m.GroupField
+ }
+ return 0
+}
+
+type Ext struct {
+ Data *string `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Ext) Reset() { *m = Ext{} }
+func (m *Ext) String() string { return proto.CompactTextString(m) }
+func (*Ext) ProtoMessage() {}
+
+func (m *Ext) GetData() string {
+ if m != nil && m.Data != nil {
+ return *m.Data
+ }
+ return ""
+}
+
+var E_Ext_More = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*Ext)(nil),
+ Field: 103,
+ Name: "testdata.Ext.more",
+ Tag: "bytes,103,opt,name=more",
+}
+
+var E_Ext_Text = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 104,
+ Name: "testdata.Ext.text",
+ Tag: "bytes,104,opt,name=text",
+}
+
+var E_Ext_Number = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*int32)(nil),
+ Field: 105,
+ Name: "testdata.Ext.number",
+ Tag: "varint,105,opt,name=number",
+}
+
+type MessageList struct {
+ Message []*MessageList_Message `protobuf:"group,1,rep" json:"message,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageList) Reset() { *m = MessageList{} }
+func (m *MessageList) String() string { return proto.CompactTextString(m) }
+func (*MessageList) ProtoMessage() {}
+
+func (m *MessageList) GetMessage() []*MessageList_Message {
+ if m != nil {
+ return m.Message
+ }
+ return nil
+}
+
+type MessageList_Message struct {
+ Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
+ Count *int32 `protobuf:"varint,3,req,name=count" json:"count,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageList_Message) Reset() { *m = MessageList_Message{} }
+
+func (m *MessageList_Message) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MessageList_Message) GetCount() int32 {
+ if m != nil && m.Count != nil {
+ return *m.Count
+ }
+ return 0
+}
+
+type Strings struct {
+ StringField *string `protobuf:"bytes,1,opt,name=string_field" json:"string_field,omitempty"`
+ BytesField []byte `protobuf:"bytes,2,opt,name=bytes_field" json:"bytes_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Strings) Reset() { *m = Strings{} }
+func (m *Strings) String() string { return proto.CompactTextString(m) }
+func (*Strings) ProtoMessage() {}
+
+func (m *Strings) GetStringField() string {
+ if m != nil && m.StringField != nil {
+ return *m.StringField
+ }
+ return ""
+}
+
+func (m *Strings) GetBytesField() []byte {
+ if m != nil {
+ return m.BytesField
+ }
+ return nil
+}
+
+type Defaults struct {
+ F_Bool *bool `protobuf:"varint,1,opt,def=1" json:"F_Bool,omitempty"`
+ F_Int32 *int32 `protobuf:"varint,2,opt,def=32" json:"F_Int32,omitempty"`
+ F_Int64 *int64 `protobuf:"varint,3,opt,def=64" json:"F_Int64,omitempty"`
+ F_Fixed32 *uint32 `protobuf:"fixed32,4,opt,def=320" json:"F_Fixed32,omitempty"`
+ F_Fixed64 *uint64 `protobuf:"fixed64,5,opt,def=640" json:"F_Fixed64,omitempty"`
+ F_Uint32 *uint32 `protobuf:"varint,6,opt,def=3200" json:"F_Uint32,omitempty"`
+ F_Uint64 *uint64 `protobuf:"varint,7,opt,def=6400" json:"F_Uint64,omitempty"`
+ F_Float *float32 `protobuf:"fixed32,8,opt,def=314159" json:"F_Float,omitempty"`
+ F_Double *float64 `protobuf:"fixed64,9,opt,def=271828" json:"F_Double,omitempty"`
+ F_String *string `protobuf:"bytes,10,opt,def=hello, \"world!\"\n" json:"F_String,omitempty"`
+ F_Bytes []byte `protobuf:"bytes,11,opt,def=Bignose" json:"F_Bytes,omitempty"`
+ F_Sint32 *int32 `protobuf:"zigzag32,12,opt,def=-32" json:"F_Sint32,omitempty"`
+ F_Sint64 *int64 `protobuf:"zigzag64,13,opt,def=-64" json:"F_Sint64,omitempty"`
+ F_Enum *Defaults_Color `protobuf:"varint,14,opt,enum=testdata.Defaults_Color,def=1" json:"F_Enum,omitempty"`
+ F_Pinf *float32 `protobuf:"fixed32,15,opt,def=inf" json:"F_Pinf,omitempty"`
+ F_Ninf *float32 `protobuf:"fixed32,16,opt,def=-inf" json:"F_Ninf,omitempty"`
+ F_Nan *float32 `protobuf:"fixed32,17,opt,def=nan" json:"F_Nan,omitempty"`
+ Sub *SubDefaults `protobuf:"bytes,18,opt,name=sub" json:"sub,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Defaults) Reset() { *m = Defaults{} }
+func (m *Defaults) String() string { return proto.CompactTextString(m) }
+func (*Defaults) ProtoMessage() {}
+
+const Default_Defaults_F_Bool bool = true
+const Default_Defaults_F_Int32 int32 = 32
+const Default_Defaults_F_Int64 int64 = 64
+const Default_Defaults_F_Fixed32 uint32 = 320
+const Default_Defaults_F_Fixed64 uint64 = 640
+const Default_Defaults_F_Uint32 uint32 = 3200
+const Default_Defaults_F_Uint64 uint64 = 6400
+const Default_Defaults_F_Float float32 = 314159
+const Default_Defaults_F_Double float64 = 271828
+const Default_Defaults_F_String string = "hello, \"world!\"\n"
+
+var Default_Defaults_F_Bytes []byte = []byte("Bignose")
+
+const Default_Defaults_F_Sint32 int32 = -32
+const Default_Defaults_F_Sint64 int64 = -64
+const Default_Defaults_F_Enum Defaults_Color = Defaults_GREEN
+
+var Default_Defaults_F_Pinf float32 = float32(math.Inf(1))
+var Default_Defaults_F_Ninf float32 = float32(math.Inf(-1))
+var Default_Defaults_F_Nan float32 = float32(math.NaN())
+
+func (m *Defaults) GetF_Bool() bool {
+ if m != nil && m.F_Bool != nil {
+ return *m.F_Bool
+ }
+ return Default_Defaults_F_Bool
+}
+
+func (m *Defaults) GetF_Int32() int32 {
+ if m != nil && m.F_Int32 != nil {
+ return *m.F_Int32
+ }
+ return Default_Defaults_F_Int32
+}
+
+func (m *Defaults) GetF_Int64() int64 {
+ if m != nil && m.F_Int64 != nil {
+ return *m.F_Int64
+ }
+ return Default_Defaults_F_Int64
+}
+
+func (m *Defaults) GetF_Fixed32() uint32 {
+ if m != nil && m.F_Fixed32 != nil {
+ return *m.F_Fixed32
+ }
+ return Default_Defaults_F_Fixed32
+}
+
+func (m *Defaults) GetF_Fixed64() uint64 {
+ if m != nil && m.F_Fixed64 != nil {
+ return *m.F_Fixed64
+ }
+ return Default_Defaults_F_Fixed64
+}
+
+func (m *Defaults) GetF_Uint32() uint32 {
+ if m != nil && m.F_Uint32 != nil {
+ return *m.F_Uint32
+ }
+ return Default_Defaults_F_Uint32
+}
+
+func (m *Defaults) GetF_Uint64() uint64 {
+ if m != nil && m.F_Uint64 != nil {
+ return *m.F_Uint64
+ }
+ return Default_Defaults_F_Uint64
+}
+
+func (m *Defaults) GetF_Float() float32 {
+ if m != nil && m.F_Float != nil {
+ return *m.F_Float
+ }
+ return Default_Defaults_F_Float
+}
+
+func (m *Defaults) GetF_Double() float64 {
+ if m != nil && m.F_Double != nil {
+ return *m.F_Double
+ }
+ return Default_Defaults_F_Double
+}
+
+func (m *Defaults) GetF_String() string {
+ if m != nil && m.F_String != nil {
+ return *m.F_String
+ }
+ return Default_Defaults_F_String
+}
+
+func (m *Defaults) GetF_Bytes() []byte {
+ if m != nil && m.F_Bytes != nil {
+ return m.F_Bytes
+ }
+ return append([]byte(nil), Default_Defaults_F_Bytes...)
+}
+
+func (m *Defaults) GetF_Sint32() int32 {
+ if m != nil && m.F_Sint32 != nil {
+ return *m.F_Sint32
+ }
+ return Default_Defaults_F_Sint32
+}
+
+func (m *Defaults) GetF_Sint64() int64 {
+ if m != nil && m.F_Sint64 != nil {
+ return *m.F_Sint64
+ }
+ return Default_Defaults_F_Sint64
+}
+
+func (m *Defaults) GetF_Enum() Defaults_Color {
+ if m != nil && m.F_Enum != nil {
+ return *m.F_Enum
+ }
+ return Default_Defaults_F_Enum
+}
+
+func (m *Defaults) GetF_Pinf() float32 {
+ if m != nil && m.F_Pinf != nil {
+ return *m.F_Pinf
+ }
+ return Default_Defaults_F_Pinf
+}
+
+func (m *Defaults) GetF_Ninf() float32 {
+ if m != nil && m.F_Ninf != nil {
+ return *m.F_Ninf
+ }
+ return Default_Defaults_F_Ninf
+}
+
+func (m *Defaults) GetF_Nan() float32 {
+ if m != nil && m.F_Nan != nil {
+ return *m.F_Nan
+ }
+ return Default_Defaults_F_Nan
+}
+
+func (m *Defaults) GetSub() *SubDefaults {
+ if m != nil {
+ return m.Sub
+ }
+ return nil
+}
+
+type SubDefaults struct {
+ N *int64 `protobuf:"varint,1,opt,name=n,def=7" json:"n,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SubDefaults) Reset() { *m = SubDefaults{} }
+func (m *SubDefaults) String() string { return proto.CompactTextString(m) }
+func (*SubDefaults) ProtoMessage() {}
+
+const Default_SubDefaults_N int64 = 7
+
+func (m *SubDefaults) GetN() int64 {
+ if m != nil && m.N != nil {
+ return *m.N
+ }
+ return Default_SubDefaults_N
+}
+
+type RepeatedEnum struct {
+ Color []RepeatedEnum_Color `protobuf:"varint,1,rep,name=color,enum=testdata.RepeatedEnum_Color" json:"color,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RepeatedEnum) Reset() { *m = RepeatedEnum{} }
+func (m *RepeatedEnum) String() string { return proto.CompactTextString(m) }
+func (*RepeatedEnum) ProtoMessage() {}
+
+func (m *RepeatedEnum) GetColor() []RepeatedEnum_Color {
+ if m != nil {
+ return m.Color
+ }
+ return nil
+}
+
+type MoreRepeated struct {
+ Bools []bool `protobuf:"varint,1,rep,name=bools" json:"bools,omitempty"`
+ BoolsPacked []bool `protobuf:"varint,2,rep,packed,name=bools_packed" json:"bools_packed,omitempty"`
+ Ints []int32 `protobuf:"varint,3,rep,name=ints" json:"ints,omitempty"`
+ IntsPacked []int32 `protobuf:"varint,4,rep,packed,name=ints_packed" json:"ints_packed,omitempty"`
+ Strings []string `protobuf:"bytes,5,rep,name=strings" json:"strings,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MoreRepeated) Reset() { *m = MoreRepeated{} }
+func (m *MoreRepeated) String() string { return proto.CompactTextString(m) }
+func (*MoreRepeated) ProtoMessage() {}
+
+func (m *MoreRepeated) GetBools() []bool {
+ if m != nil {
+ return m.Bools
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetBoolsPacked() []bool {
+ if m != nil {
+ return m.BoolsPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetInts() []int32 {
+ if m != nil {
+ return m.Ints
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetIntsPacked() []int32 {
+ if m != nil {
+ return m.IntsPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetStrings() []string {
+ if m != nil {
+ return m.Strings
+ }
+ return nil
+}
+
+type GroupOld struct {
+ G *GroupOld_G `protobuf:"group,1,opt" json:"g,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupOld) Reset() { *m = GroupOld{} }
+func (m *GroupOld) String() string { return proto.CompactTextString(m) }
+func (*GroupOld) ProtoMessage() {}
+
+func (m *GroupOld) GetG() *GroupOld_G {
+ if m != nil {
+ return m.G
+ }
+ return nil
+}
+
+type GroupOld_G struct {
+ X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupOld_G) Reset() { *m = GroupOld_G{} }
+
+func (m *GroupOld_G) GetX() int32 {
+ if m != nil && m.X != nil {
+ return *m.X
+ }
+ return 0
+}
+
+type GroupNew struct {
+ G *GroupNew_G `protobuf:"group,1,opt" json:"g,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupNew) Reset() { *m = GroupNew{} }
+func (m *GroupNew) String() string { return proto.CompactTextString(m) }
+func (*GroupNew) ProtoMessage() {}
+
+func (m *GroupNew) GetG() *GroupNew_G {
+ if m != nil {
+ return m.G
+ }
+ return nil
+}
+
+type GroupNew_G struct {
+ X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"`
+ Y *int32 `protobuf:"varint,3,opt,name=y" json:"y,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupNew_G) Reset() { *m = GroupNew_G{} }
+
+func (m *GroupNew_G) GetX() int32 {
+ if m != nil && m.X != nil {
+ return *m.X
+ }
+ return 0
+}
+
+func (m *GroupNew_G) GetY() int32 {
+ if m != nil && m.Y != nil {
+ return *m.Y
+ }
+ return 0
+}
+
+var E_Greeting = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: ([]string)(nil),
+ Field: 106,
+ Name: "testdata.greeting",
+ Tag: "bytes,106,rep,name=greeting",
+}
+
+func init() {
+ proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value)
+ proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value)
+ proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value)
+ proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value)
+ proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value)
+ proto.RegisterExtension(E_Ext_More)
+ proto.RegisterExtension(E_Ext_Text)
+ proto.RegisterExtension(E_Ext_Number)
+ proto.RegisterExtension(E_Greeting)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.proto
new file mode 100644
index 000000000..411634d82
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata/test.proto
@@ -0,0 +1,435 @@
+// 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.
+
+// A feature-rich test file for the protocol compiler and libraries.
+
+syntax = "proto2";
+
+package testdata;
+
+enum FOO { FOO1 = 1; };
+
+message GoEnum {
+ required FOO foo = 1;
+}
+
+message GoTestField {
+ required string Label = 1;
+ required string Type = 2;
+}
+
+message GoTest {
+ // An enum, for completeness.
+ enum KIND {
+ VOID = 0;
+
+ // Basic types
+ BOOL = 1;
+ BYTES = 2;
+ FINGERPRINT = 3;
+ FLOAT = 4;
+ INT = 5;
+ STRING = 6;
+ TIME = 7;
+
+ // Groupings
+ TUPLE = 8;
+ ARRAY = 9;
+ MAP = 10;
+
+ // Table types
+ TABLE = 11;
+
+ // Functions
+ FUNCTION = 12; // last tag
+ };
+
+ // Some typical parameters
+ required KIND Kind = 1;
+ optional string Table = 2;
+ optional int32 Param = 3;
+
+ // Required, repeated and optional foreign fields.
+ required GoTestField RequiredField = 4;
+ repeated GoTestField RepeatedField = 5;
+ optional GoTestField OptionalField = 6;
+
+ // Required fields of all basic types
+ required bool F_Bool_required = 10;
+ required int32 F_Int32_required = 11;
+ required int64 F_Int64_required = 12;
+ required fixed32 F_Fixed32_required = 13;
+ required fixed64 F_Fixed64_required = 14;
+ required uint32 F_Uint32_required = 15;
+ required uint64 F_Uint64_required = 16;
+ required float F_Float_required = 17;
+ required double F_Double_required = 18;
+ required string F_String_required = 19;
+ required bytes F_Bytes_required = 101;
+ required sint32 F_Sint32_required = 102;
+ required sint64 F_Sint64_required = 103;
+
+ // Repeated fields of all basic types
+ repeated bool F_Bool_repeated = 20;
+ repeated int32 F_Int32_repeated = 21;
+ repeated int64 F_Int64_repeated = 22;
+ repeated fixed32 F_Fixed32_repeated = 23;
+ repeated fixed64 F_Fixed64_repeated = 24;
+ repeated uint32 F_Uint32_repeated = 25;
+ repeated uint64 F_Uint64_repeated = 26;
+ repeated float F_Float_repeated = 27;
+ repeated double F_Double_repeated = 28;
+ repeated string F_String_repeated = 29;
+ repeated bytes F_Bytes_repeated = 201;
+ repeated sint32 F_Sint32_repeated = 202;
+ repeated sint64 F_Sint64_repeated = 203;
+
+ // Optional fields of all basic types
+ optional bool F_Bool_optional = 30;
+ optional int32 F_Int32_optional = 31;
+ optional int64 F_Int64_optional = 32;
+ optional fixed32 F_Fixed32_optional = 33;
+ optional fixed64 F_Fixed64_optional = 34;
+ optional uint32 F_Uint32_optional = 35;
+ optional uint64 F_Uint64_optional = 36;
+ optional float F_Float_optional = 37;
+ optional double F_Double_optional = 38;
+ optional string F_String_optional = 39;
+ optional bytes F_Bytes_optional = 301;
+ optional sint32 F_Sint32_optional = 302;
+ optional sint64 F_Sint64_optional = 303;
+
+ // Default-valued fields of all basic types
+ optional bool F_Bool_defaulted = 40 [default=true];
+ optional int32 F_Int32_defaulted = 41 [default=32];
+ optional int64 F_Int64_defaulted = 42 [default=64];
+ optional fixed32 F_Fixed32_defaulted = 43 [default=320];
+ optional fixed64 F_Fixed64_defaulted = 44 [default=640];
+ optional uint32 F_Uint32_defaulted = 45 [default=3200];
+ optional uint64 F_Uint64_defaulted = 46 [default=6400];
+ optional float F_Float_defaulted = 47 [default=314159.];
+ optional double F_Double_defaulted = 48 [default=271828.];
+ optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"];
+ optional bytes F_Bytes_defaulted = 401 [default="Bignose"];
+ optional sint32 F_Sint32_defaulted = 402 [default = -32];
+ optional sint64 F_Sint64_defaulted = 403 [default = -64];
+
+ // Packed repeated fields (no string or bytes).
+ repeated bool F_Bool_repeated_packed = 50 [packed=true];
+ repeated int32 F_Int32_repeated_packed = 51 [packed=true];
+ repeated int64 F_Int64_repeated_packed = 52 [packed=true];
+ repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true];
+ repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true];
+ repeated uint32 F_Uint32_repeated_packed = 55 [packed=true];
+ repeated uint64 F_Uint64_repeated_packed = 56 [packed=true];
+ repeated float F_Float_repeated_packed = 57 [packed=true];
+ repeated double F_Double_repeated_packed = 58 [packed=true];
+ repeated sint32 F_Sint32_repeated_packed = 502 [packed=true];
+ repeated sint64 F_Sint64_repeated_packed = 503 [packed=true];
+
+ // Required, repeated, and optional groups.
+ required group RequiredGroup = 70 {
+ required string RequiredField = 71;
+ };
+
+ repeated group RepeatedGroup = 80 {
+ required string RequiredField = 81;
+ };
+
+ optional group OptionalGroup = 90 {
+ required string RequiredField = 91;
+ };
+}
+
+// For testing skipping of unrecognized fields.
+// Numbers are all big, larger than tag numbers in GoTestField,
+// the message used in the corresponding test.
+message GoSkipTest {
+ required int32 skip_int32 = 11;
+ required fixed32 skip_fixed32 = 12;
+ required fixed64 skip_fixed64 = 13;
+ required string skip_string = 14;
+ required group SkipGroup = 15 {
+ required int32 group_int32 = 16;
+ required string group_string = 17;
+ }
+}
+
+// For testing packed/non-packed decoder switching.
+// A serialized instance of one should be deserializable as the other.
+message NonPackedTest {
+ repeated int32 a = 1;
+}
+
+message PackedTest {
+ repeated int32 b = 1 [packed=true];
+}
+
+message MaxTag {
+ // Maximum possible tag number.
+ optional string last_field = 536870911;
+}
+
+message OldMessage {
+ message Nested {
+ optional string name = 1;
+ }
+ optional Nested nested = 1;
+
+ optional int32 num = 2;
+}
+
+// NewMessage is wire compatible with OldMessage;
+// imagine it as a future version.
+message NewMessage {
+ message Nested {
+ optional string name = 1;
+ optional string food_group = 2;
+ }
+ optional Nested nested = 1;
+
+ // This is an int32 in OldMessage.
+ optional int64 num = 2;
+}
+
+// Smaller tests for ASCII formatting.
+
+message InnerMessage {
+ required string host = 1;
+ optional int32 port = 2 [default=4000];
+ optional bool connected = 3;
+}
+
+message OtherMessage {
+ optional int64 key = 1;
+ optional bytes value = 2;
+ optional float weight = 3;
+ optional InnerMessage inner = 4;
+}
+
+message MyMessage {
+ required int32 count = 1;
+ optional string name = 2;
+ optional string quote = 3;
+ repeated string pet = 4;
+ optional InnerMessage inner = 5;
+ repeated OtherMessage others = 6;
+ repeated InnerMessage rep_inner = 12;
+
+ enum Color {
+ RED = 0;
+ GREEN = 1;
+ BLUE = 2;
+ };
+ optional Color bikeshed = 7;
+
+ optional group SomeGroup = 8 {
+ optional int32 group_field = 9;
+ }
+
+ // This field becomes [][]byte in the generated code.
+ repeated bytes rep_bytes = 10;
+
+ optional double bigfloat = 11;
+
+ extensions 100 to max;
+}
+
+message Ext {
+ extend MyMessage {
+ optional Ext more = 103;
+ optional string text = 104;
+ optional int32 number = 105;
+ }
+
+ optional string data = 1;
+}
+
+extend MyMessage {
+ repeated string greeting = 106;
+}
+
+message MyMessageSet {
+ option message_set_wire_format = true;
+ extensions 100 to max;
+}
+
+message Empty {
+}
+
+extend MyMessageSet {
+ optional Empty x201 = 201;
+ optional Empty x202 = 202;
+ optional Empty x203 = 203;
+ optional Empty x204 = 204;
+ optional Empty x205 = 205;
+ optional Empty x206 = 206;
+ optional Empty x207 = 207;
+ optional Empty x208 = 208;
+ optional Empty x209 = 209;
+ optional Empty x210 = 210;
+ optional Empty x211 = 211;
+ optional Empty x212 = 212;
+ optional Empty x213 = 213;
+ optional Empty x214 = 214;
+ optional Empty x215 = 215;
+ optional Empty x216 = 216;
+ optional Empty x217 = 217;
+ optional Empty x218 = 218;
+ optional Empty x219 = 219;
+ optional Empty x220 = 220;
+ optional Empty x221 = 221;
+ optional Empty x222 = 222;
+ optional Empty x223 = 223;
+ optional Empty x224 = 224;
+ optional Empty x225 = 225;
+ optional Empty x226 = 226;
+ optional Empty x227 = 227;
+ optional Empty x228 = 228;
+ optional Empty x229 = 229;
+ optional Empty x230 = 230;
+ optional Empty x231 = 231;
+ optional Empty x232 = 232;
+ optional Empty x233 = 233;
+ optional Empty x234 = 234;
+ optional Empty x235 = 235;
+ optional Empty x236 = 236;
+ optional Empty x237 = 237;
+ optional Empty x238 = 238;
+ optional Empty x239 = 239;
+ optional Empty x240 = 240;
+ optional Empty x241 = 241;
+ optional Empty x242 = 242;
+ optional Empty x243 = 243;
+ optional Empty x244 = 244;
+ optional Empty x245 = 245;
+ optional Empty x246 = 246;
+ optional Empty x247 = 247;
+ optional Empty x248 = 248;
+ optional Empty x249 = 249;
+ optional Empty x250 = 250;
+}
+
+message MessageList {
+ repeated group Message = 1 {
+ required string name = 2;
+ required int32 count = 3;
+ }
+}
+
+message Strings {
+ optional string string_field = 1;
+ optional bytes bytes_field = 2;
+}
+
+message Defaults {
+ enum Color {
+ RED = 0;
+ GREEN = 1;
+ BLUE = 2;
+ }
+
+ // Default-valued fields of all basic types.
+ // Same as GoTest, but copied here to make testing easier.
+ optional bool F_Bool = 1 [default=true];
+ optional int32 F_Int32 = 2 [default=32];
+ optional int64 F_Int64 = 3 [default=64];
+ optional fixed32 F_Fixed32 = 4 [default=320];
+ optional fixed64 F_Fixed64 = 5 [default=640];
+ optional uint32 F_Uint32 = 6 [default=3200];
+ optional uint64 F_Uint64 = 7 [default=6400];
+ optional float F_Float = 8 [default=314159.];
+ optional double F_Double = 9 [default=271828.];
+ optional string F_String = 10 [default="hello, \"world!\"\n"];
+ optional bytes F_Bytes = 11 [default="Bignose"];
+ optional sint32 F_Sint32 = 12 [default=-32];
+ optional sint64 F_Sint64 = 13 [default=-64];
+ optional Color F_Enum = 14 [default=GREEN];
+
+ // More fields with crazy defaults.
+ optional float F_Pinf = 15 [default=inf];
+ optional float F_Ninf = 16 [default=-inf];
+ optional float F_Nan = 17 [default=nan];
+
+ // Sub-message.
+ optional SubDefaults sub = 18;
+
+ // Redundant but explicit defaults.
+ optional string str_zero = 19 [default=""];
+}
+
+message SubDefaults {
+ optional int64 n = 1 [default=7];
+}
+
+message RepeatedEnum {
+ enum Color {
+ RED = 1;
+ }
+ repeated Color color = 1;
+}
+
+message MoreRepeated {
+ repeated bool bools = 1;
+ repeated bool bools_packed = 2 [packed=true];
+ repeated int32 ints = 3;
+ repeated int32 ints_packed = 4 [packed=true];
+ repeated int64 int64s_packed = 7 [packed=true];
+ repeated string strings = 5;
+ repeated fixed32 fixeds = 6;
+}
+
+// GroupOld and GroupNew have the same wire format.
+// GroupNew has a new field inside a group.
+
+message GroupOld {
+ optional group G = 101 {
+ optional int32 x = 2;
+ }
+}
+
+message GroupNew {
+ optional group G = 101 {
+ optional int32 x = 2;
+ optional int32 y = 3;
+ }
+}
+
+message FloatingPoint {
+ required double f = 1;
+}
+
+message MessageWithMap {
+ map name_mapping = 1;
+ map msg_mapping = 2;
+ map byte_mapping = 3;
+ map str_to_str = 4;
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text.go
new file mode 100644
index 000000000..5d77a9420
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text.go
@@ -0,0 +1,824 @@
+// Extensions for Protocol Buffers to create more go like structures.
+//
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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"
+ "fmt"
+ "io"
+ "log"
+ "math"
+ "os"
+ "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.Printf("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
+}
+
+var (
+ messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem()
+)
+
+// raw is the interface satisfied by RawMessage.
+type raw interface {
+ Bytes() []byte
+}
+
+func writeStruct(w *textWriter, sv reflect.Value) error {
+ if sv.Type() == messageSetType {
+ return writeMessageSet(w, sv.Addr().Interface().(*MessageSet))
+ }
+
+ 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 len(props.Enum) > 0 {
+ if err := writeEnum(w, v, props); err != nil {
+ return err
+ }
+ } else if err := 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() // TODO: should we sort these for deterministic output?
+ 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 := writeAny(w, key, props.mkeyprop); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ // value
+ if _, err := w.WriteString("value:"); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ if err := 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 props.proto3 && fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
+ // proto3 non-repeated scalar field; skip if zero value
+ switch fv.Kind() {
+ case reflect.Bool:
+ if !fv.Bool() {
+ continue
+ }
+ case reflect.Int32, reflect.Int64:
+ if fv.Int() == 0 {
+ continue
+ }
+ case reflect.Uint32, reflect.Uint64:
+ if fv.Uint() == 0 {
+ continue
+ }
+ case reflect.Float32, reflect.Float64:
+ if fv.Float() == 0 {
+ continue
+ }
+ case reflect.String:
+ if fv.String() == "" {
+ continue
+ }
+ }
+ }
+
+ 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
+ }
+
+ if len(props.Enum) > 0 {
+ if err := writeEnum(w, fv, props); err != nil {
+ return err
+ }
+ } else if err := 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 pv.Type().Implements(extendableProtoType) {
+ if err := 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 writeAny(w *textWriter, v reflect.Value, props *Properties) error {
+ v = reflect.Indirect(v)
+
+ if props != nil && len(props.CustomType) > 0 {
+ var custom Marshaler = v.Interface().(Marshaler)
+ data, err := custom.Marshal()
+ if err != nil {
+ return err
+ }
+ if err := writeString(w, string(data)); err != nil {
+ return err
+ }
+ return nil
+ }
+
+ // 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 tm, ok := v.Interface().(encoding.TextMarshaler); ok {
+ text, err := tm.MarshalText()
+ if err != nil {
+ return err
+ }
+ if _, err = w.Write(text); err != nil {
+ return err
+ }
+ } else if err := 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 writeMessageSet(w *textWriter, ms *MessageSet) error {
+ for _, item := range ms.Item {
+ id := *item.TypeId
+ if msd, ok := messageSetMap[id]; ok {
+ // Known message set type.
+ if _, err := fmt.Fprintf(w, "[%s]: <\n", msd.name); err != nil {
+ return err
+ }
+ w.indent()
+
+ pb := reflect.New(msd.t.Elem())
+ if err := Unmarshal(item.Message, pb.Interface().(Message)); err != nil {
+ if _, err := fmt.Fprintf(w, "/* bad message: %v */\n", err); err != nil {
+ return err
+ }
+ } else {
+ if err := writeStruct(w, pb.Elem()); err != nil {
+ return err
+ }
+ }
+ } else {
+ // Unknown type.
+ if _, err := fmt.Fprintf(w, "[%d]: <\n", id); err != nil {
+ return err
+ }
+ w.indent()
+ if err := writeUnknownStruct(w, item.Message); err != nil {
+ return err
+ }
+ }
+ w.unindent()
+ if _, err := w.Write(gtNewline); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+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 writeExtensions(w *textWriter, pv reflect.Value) error {
+ emap := extensionMaps[pv.Type().Elem()]
+ ep := pv.Interface().(extendableProto)
+
+ // Order the extensions by ID.
+ // This isn't strictly necessary, but it will give us
+ // canonical output, which will also make testing easier.
+ var m map[int32]Extension
+ if em, ok := ep.(extensionsMap); ok {
+ m = em.ExtensionMap()
+ } else if em, ok := ep.(extensionsBytes); ok {
+ eb := em.GetExtensions()
+ var err error
+ m, err = BytesToExtensionsMap(*eb)
+ if err != nil {
+ return err
+ }
+ }
+
+ ids := make([]int32, 0, len(m))
+ for id := range m {
+ ids = append(ids, id)
+ }
+ sort.Sort(int32Slice(ids))
+
+ 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 {
+ if _, err := fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err); err != nil {
+ return err
+ }
+ continue
+ }
+
+ // Repeated extensions will appear as a slice.
+ if !desc.repeated() {
+ if err := writeExtension(w, desc.Name, pb); err != nil {
+ return err
+ }
+ } else {
+ v := reflect.ValueOf(pb)
+ for i := 0; i < v.Len(); i++ {
+ if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func 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 := 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
+}
+
+func marshalText(w io.Writer, pb Message, compact bool) 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: compact,
+ }
+
+ if tm, ok := pb.(encoding.TextMarshaler); ok {
+ text, err := tm.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 := writeStruct(aw, v); err != nil {
+ return err
+ }
+ if bw != nil {
+ return bw.Flush()
+ }
+ return nil
+}
+
+// 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 marshalText(w, pb, false)
+}
+
+// MarshalTextString is the same as MarshalText, but returns the string directly.
+func MarshalTextString(pb Message) string {
+ var buf bytes.Buffer
+ marshalText(&buf, pb, false)
+ return buf.String()
+}
+
+// CompactText writes a given protocol buffer in compact text format (one line).
+func CompactText(w io.Writer, pb Message) error { return marshalText(w, pb, true) }
+
+// CompactTextString is the same as CompactText, but returns the string directly.
+func CompactTextString(pb Message) string {
+ var buf bytes.Buffer
+ marshalText(&buf, pb, true)
+ return buf.String()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_gogo.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_gogo.go
new file mode 100644
index 000000000..cdb23373c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_gogo.go
@@ -0,0 +1,55 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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
+// 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
+
+import (
+ "fmt"
+ "reflect"
+)
+
+func writeEnum(w *textWriter, v reflect.Value, props *Properties) error {
+ m, ok := enumStringMaps[props.Enum]
+ if !ok {
+ if err := writeAny(w, v, props); err != nil {
+ return err
+ }
+ }
+ key := int32(0)
+ if v.Kind() == reflect.Ptr {
+ key = int32(v.Elem().Int())
+ } else {
+ key = int32(v.Int())
+ }
+ s, ok := m[key]
+ if !ok {
+ if err := writeAny(w, v, props); err != nil {
+ return err
+ }
+ }
+ _, err := fmt.Fprint(w, s)
+ return err
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_parser.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_parser.go
new file mode 100644
index 000000000..f769937bd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_parser.go
@@ -0,0 +1,800 @@
+// Extensions for Protocol Buffers to create more go like structures.
+//
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/protobuf/gogoproto
+//
+// 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"
+)
+
+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 (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 %v", p.s[0:i+1])
+ 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 && p.cur.value[0] == '"' {
+ // Look for multiple quoted strings separated by whitespace,
+ // and concatenate them.
+ cat := p.cur
+ for {
+ p.skipWhitespace()
+ if p.done || 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(st reflect.Type, name string) (int, *Properties, bool) {
+ sprops := GetProperties(st)
+ 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()
+ reqCount := GetProperties(st).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]".
+ for {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value == terminator {
+ break
+ }
+ if tok.value == "[" {
+ // Looks like an extension.
+ //
+ // TODO: Check whether we need to handle
+ // namespace rooted names (e.g. ".something.Foo").
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ 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 == tok.value {
+ desc = d
+ break
+ }
+ }
+ if desc == nil {
+ return p.errorf("unrecognized extension %q", tok.value)
+ }
+ // Check the extension terminator.
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != "]" {
+ return p.errorf("unrecognized extension terminator %q", tok.value)
+ }
+
+ 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().(extendableProto)
+ 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())
+ }
+ } else {
+ // This is a normal, non-extension field.
+ name := tok.value
+ fi, props, ok := structFieldByName(st, name)
+ if !ok {
+ return p.errorf("unknown field name %q in %v", name, st)
+ }
+
+ dst := sv.Field(fi)
+
+ 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 >
+ // Technically the "key" and "value" could come in any order,
+ // but in practice they won't.
+
+ tok := p.next()
+ var terminator string
+ switch tok.value {
+ case "<":
+ terminator = ">"
+ case "{":
+ terminator = "}"
+ default:
+ return p.errorf("expected '{' or '<', found %q", tok.value)
+ }
+ if err := p.consumeToken("key"); err != nil {
+ return err
+ }
+ if err := p.consumeToken(":"); err != nil {
+ return err
+ }
+ if err := p.readAny(key, props.mkeyprop); err != nil {
+ return err
+ }
+ if err := p.consumeToken("value"); err != nil {
+ return err
+ }
+ 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.consumeToken(terminator); err != nil {
+ return err
+ }
+
+ 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, st.Field(fi).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
+ } else if props.Required {
+ reqCount--
+ }
+ }
+
+ // For backward compatibility, permit a semicolon or comma after a field.
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != ";" && tok.value != "," {
+ p.back()
+ }
+ }
+
+ if reqCount > 0 {
+ return p.missingRequiredFieldError(sv)
+ }
+ return reqFieldErr
+}
+
+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")
+ }
+ if len(props.CustomType) > 0 {
+ if props.Repeated {
+ t := reflect.TypeOf(v.Interface())
+ if t.Kind() == reflect.Slice {
+ tc := reflect.TypeOf(new(Marshaler))
+ ok := t.Elem().Implements(tc.Elem())
+ if ok {
+ fv := v
+ flen := fv.Len()
+ if flen == fv.Cap() {
+ nav := reflect.MakeSlice(v.Type(), flen, 2*flen+1)
+ reflect.Copy(nav, fv)
+ fv.Set(nav)
+ }
+ fv.SetLen(flen + 1)
+
+ // Read one.
+ p.back()
+ return p.readAny(fv.Index(flen), props)
+ }
+ }
+ }
+ if reflect.TypeOf(v.Interface()).Kind() == reflect.Ptr {
+ custom := reflect.New(props.ctype.Elem()).Interface().(Unmarshaler)
+ err := custom.Unmarshal([]byte(tok.unquoted))
+ if err != nil {
+ return p.errorf("%v %v: %v", err, v.Type(), tok.value)
+ }
+ v.Set(reflect.ValueOf(custom))
+ } else {
+ custom := reflect.New(reflect.TypeOf(v.Interface())).Interface().(Unmarshaler)
+ err := custom.Unmarshal([]byte(tok.unquoted))
+ if err != nil {
+ return p.errorf("%v %v: %v", err, v.Type(), tok.value)
+ }
+ v.Set(reflect.Indirect(reflect.ValueOf(custom)))
+ }
+ return nil
+ }
+ 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. May already exist.
+ flen := fv.Len()
+ if flen == fv.Cap() {
+ nav := reflect.MakeSlice(at, flen, 2*flen+1)
+ reflect.Copy(nav, fv)
+ fv.Set(nav)
+ }
+ fv.SetLen(flen + 1)
+
+ // Read one.
+ p.back()
+ return p.readAny(fv.Index(flen), props)
+ case reflect.Bool:
+ // Either "true", "false", 1 or 0.
+ switch tok.value {
+ case "true", "1":
+ fv.SetBool(true)
+ return nil
+ case "false", "0":
+ 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(uint64(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/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_parser_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_parser_test.go
new file mode 100644
index 000000000..ce1a9e8df
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_parser_test.go
@@ -0,0 +1,511 @@
+// 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_test
+
+import (
+ "math"
+ "reflect"
+ "testing"
+
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ proto3pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+type UnmarshalTextTest struct {
+ in string
+ err string // if "", no error expected
+ out *MyMessage
+}
+
+func buildExtStructTest(text string) UnmarshalTextTest {
+ msg := &MyMessage{
+ Count: Int32(42),
+ }
+ SetExtension(msg, E_Ext_More, &Ext{
+ Data: String("Hello, world!"),
+ })
+ return UnmarshalTextTest{in: text, out: msg}
+}
+
+func buildExtDataTest(text string) UnmarshalTextTest {
+ msg := &MyMessage{
+ Count: Int32(42),
+ }
+ SetExtension(msg, E_Ext_Text, String("Hello, world!"))
+ SetExtension(msg, E_Ext_Number, Int32(1729))
+ return UnmarshalTextTest{in: text, out: msg}
+}
+
+func buildExtRepStringTest(text string) UnmarshalTextTest {
+ msg := &MyMessage{
+ Count: Int32(42),
+ }
+ if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
+ panic(err)
+ }
+ return UnmarshalTextTest{in: text, out: msg}
+}
+
+var unMarshalTextTests = []UnmarshalTextTest{
+ // Basic
+ {
+ in: " count:42\n name:\"Dave\" ",
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("Dave"),
+ },
+ },
+
+ // Empty quoted string
+ {
+ in: `count:42 name:""`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String(""),
+ },
+ },
+
+ // Quoted string concatenation
+ {
+ in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("My name is elsewhere"),
+ },
+ },
+
+ // Quoted string with escaped apostrophe
+ {
+ in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("HOLIDAY - New Year's Day"),
+ },
+ },
+
+ // Quoted string with single quote
+ {
+ in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String(`Roger "The Ramster" Ramjet`),
+ },
+ },
+
+ // Quoted string with all the accepted special characters from the C++ test
+ {
+ in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
+ },
+ },
+
+ // Quoted string with quoted backslash
+ {
+ in: `count:42 name: "\\'xyz"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String(`\'xyz`),
+ },
+ },
+
+ // Quoted string with UTF-8 bytes.
+ {
+ in: "count:42 name: '\303\277\302\201\xAB'",
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("\303\277\302\201\xAB"),
+ },
+ },
+
+ // Bad quoted string
+ {
+ in: `inner: < host: "\0" >` + "\n",
+ err: `line 1.15: invalid quoted string "\0"`,
+ },
+
+ // Number too large for int64
+ {
+ in: "count: 1 others { key: 123456789012345678901 }",
+ err: "line 1.23: invalid int64: 123456789012345678901",
+ },
+
+ // Number too large for int32
+ {
+ in: "count: 1234567890123",
+ err: "line 1.7: invalid int32: 1234567890123",
+ },
+
+ // Number in hexadecimal
+ {
+ in: "count: 0x2beef",
+ out: &MyMessage{
+ Count: Int32(0x2beef),
+ },
+ },
+
+ // Number in octal
+ {
+ in: "count: 024601",
+ out: &MyMessage{
+ Count: Int32(024601),
+ },
+ },
+
+ // Floating point number with "f" suffix
+ {
+ in: "count: 4 others:< weight: 17.0f >",
+ out: &MyMessage{
+ Count: Int32(4),
+ Others: []*OtherMessage{
+ {
+ Weight: Float32(17),
+ },
+ },
+ },
+ },
+
+ // Floating point positive infinity
+ {
+ in: "count: 4 bigfloat: inf",
+ out: &MyMessage{
+ Count: Int32(4),
+ Bigfloat: Float64(math.Inf(1)),
+ },
+ },
+
+ // Floating point negative infinity
+ {
+ in: "count: 4 bigfloat: -inf",
+ out: &MyMessage{
+ Count: Int32(4),
+ Bigfloat: Float64(math.Inf(-1)),
+ },
+ },
+
+ // Number too large for float32
+ {
+ in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
+ err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
+ },
+
+ // Number posing as a quoted string
+ {
+ in: `inner: < host: 12 >` + "\n",
+ err: `line 1.15: invalid string: 12`,
+ },
+
+ // Quoted string posing as int32
+ {
+ in: `count: "12"`,
+ err: `line 1.7: invalid int32: "12"`,
+ },
+
+ // Quoted string posing a float32
+ {
+ in: `others:< weight: "17.4" >`,
+ err: `line 1.17: invalid float32: "17.4"`,
+ },
+
+ // Enum
+ {
+ in: `count:42 bikeshed: BLUE`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Bikeshed: MyMessage_BLUE.Enum(),
+ },
+ },
+
+ // Repeated field
+ {
+ in: `count:42 pet: "horsey" pet:"bunny"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Pet: []string{"horsey", "bunny"},
+ },
+ },
+
+ // Repeated message with/without colon and <>/{}
+ {
+ in: `count:42 others:{} others{} others:<> others:{}`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Others: []*OtherMessage{
+ {},
+ {},
+ {},
+ {},
+ },
+ },
+ },
+
+ // Missing colon for inner message
+ {
+ in: `count:42 inner < host: "cauchy.syd" >`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Inner: &InnerMessage{
+ Host: String("cauchy.syd"),
+ },
+ },
+ },
+
+ // Missing colon for string field
+ {
+ in: `name "Dave"`,
+ err: `line 1.5: expected ':', found "\"Dave\""`,
+ },
+
+ // Missing colon for int32 field
+ {
+ in: `count 42`,
+ err: `line 1.6: expected ':', found "42"`,
+ },
+
+ // Missing required field
+ {
+ in: `name: "Pawel"`,
+ err: `proto: required field "testdata.MyMessage.count" not set`,
+ out: &MyMessage{
+ Name: String("Pawel"),
+ },
+ },
+
+ // Repeated non-repeated field
+ {
+ in: `name: "Rob" name: "Russ"`,
+ err: `line 1.12: non-repeated field "name" was repeated`,
+ },
+
+ // Group
+ {
+ in: `count: 17 SomeGroup { group_field: 12 }`,
+ out: &MyMessage{
+ Count: Int32(17),
+ Somegroup: &MyMessage_SomeGroup{
+ GroupField: Int32(12),
+ },
+ },
+ },
+
+ // Semicolon between fields
+ {
+ in: `count:3;name:"Calvin"`,
+ out: &MyMessage{
+ Count: Int32(3),
+ Name: String("Calvin"),
+ },
+ },
+ // Comma between fields
+ {
+ in: `count:4,name:"Ezekiel"`,
+ out: &MyMessage{
+ Count: Int32(4),
+ Name: String("Ezekiel"),
+ },
+ },
+
+ // Extension
+ buildExtStructTest(`count: 42 [testdata.Ext.more]:`),
+ buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
+ buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
+ buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
+
+ // Big all-in-one
+ {
+ in: "count:42 # Meaning\n" +
+ `name:"Dave" ` +
+ `quote:"\"I didn't want to go.\"" ` +
+ `pet:"bunny" ` +
+ `pet:"kitty" ` +
+ `pet:"horsey" ` +
+ `inner:<` +
+ ` host:"footrest.syd" ` +
+ ` port:7001 ` +
+ ` connected:true ` +
+ `> ` +
+ `others:<` +
+ ` key:3735928559 ` +
+ ` value:"\x01A\a\f" ` +
+ `> ` +
+ `others:<` +
+ " weight:58.9 # Atomic weight of Co\n" +
+ ` inner:<` +
+ ` host:"lesha.mtv" ` +
+ ` port:8002 ` +
+ ` >` +
+ `>`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("Dave"),
+ Quote: String(`"I didn't want to go."`),
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Inner: &InnerMessage{
+ Host: String("footrest.syd"),
+ Port: Int32(7001),
+ Connected: Bool(true),
+ },
+ Others: []*OtherMessage{
+ {
+ Key: Int64(3735928559),
+ Value: []byte{0x1, 'A', '\a', '\f'},
+ },
+ {
+ Weight: Float32(58.9),
+ Inner: &InnerMessage{
+ Host: String("lesha.mtv"),
+ Port: Int32(8002),
+ },
+ },
+ },
+ },
+ },
+}
+
+func TestUnmarshalText(t *testing.T) {
+ for i, test := range unMarshalTextTests {
+ pb := new(MyMessage)
+ err := UnmarshalText(test.in, pb)
+ if test.err == "" {
+ // We don't expect failure.
+ if err != nil {
+ t.Errorf("Test %d: Unexpected error: %v", i, err)
+ } else if !reflect.DeepEqual(pb, test.out) {
+ t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
+ i, pb, test.out)
+ }
+ } else {
+ // We do expect failure.
+ if err == nil {
+ t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
+ } else if err.Error() != test.err {
+ t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
+ i, err.Error(), test.err)
+ } else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
+ t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
+ i, pb, test.out)
+ }
+ }
+ }
+}
+
+func TestUnmarshalTextCustomMessage(t *testing.T) {
+ msg := &textMessage{}
+ if err := UnmarshalText("custom", msg); err != nil {
+ t.Errorf("Unexpected error from custom unmarshal: %v", err)
+ }
+ if UnmarshalText("not custom", msg) == nil {
+ t.Errorf("Didn't get expected error from custom unmarshal")
+ }
+}
+
+// Regression test; this caused a panic.
+func TestRepeatedEnum(t *testing.T) {
+ pb := new(RepeatedEnum)
+ if err := UnmarshalText("color: RED", pb); err != nil {
+ t.Fatal(err)
+ }
+ exp := &RepeatedEnum{
+ Color: []RepeatedEnum_Color{RepeatedEnum_RED},
+ }
+ if !Equal(pb, exp) {
+ t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
+ }
+}
+
+func TestProto3TextParsing(t *testing.T) {
+ m := new(proto3pb.Message)
+ const in = `name: "Wallace" true_scotsman: true`
+ want := &proto3pb.Message{
+ Name: "Wallace",
+ TrueScotsman: true,
+ }
+ if err := UnmarshalText(in, m); err != nil {
+ t.Fatal(err)
+ }
+ if !Equal(m, want) {
+ t.Errorf("\n got %v\nwant %v", m, want)
+ }
+}
+
+func TestMapParsing(t *testing.T) {
+ m := new(MessageWithMap)
+ const in = `name_mapping: name_mapping:` +
+ `msg_mapping:>` +
+ `msg_mapping>` + // no colon after "value"
+ `byte_mapping:`
+ want := &MessageWithMap{
+ NameMapping: map[int32]string{
+ 1: "Beatles",
+ 1234: "Feist",
+ },
+ MsgMapping: map[int64]*FloatingPoint{
+ -4: {F: Float64(2.0)},
+ -2: {F: Float64(4.0)},
+ },
+ ByteMapping: map[bool][]byte{
+ true: []byte("so be it"),
+ },
+ }
+ if err := UnmarshalText(in, m); err != nil {
+ t.Fatal(err)
+ }
+ if !Equal(m, want) {
+ t.Errorf("\n got %v\nwant %v", m, want)
+ }
+}
+
+var benchInput string
+
+func init() {
+ benchInput = "count: 4\n"
+ for i := 0; i < 1000; i++ {
+ benchInput += "pet: \"fido\"\n"
+ }
+
+ // Check it is valid input.
+ pb := new(MyMessage)
+ err := UnmarshalText(benchInput, pb)
+ if err != nil {
+ panic("Bad benchmark input: " + err.Error())
+ }
+}
+
+func BenchmarkUnmarshalText(b *testing.B) {
+ pb := new(MyMessage)
+ for i := 0; i < b.N; i++ {
+ UnmarshalText(benchInput, pb)
+ }
+ b.SetBytes(int64(len(benchInput)))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_test.go
new file mode 100644
index 000000000..dd462c22b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/text_test.go
@@ -0,0 +1,435 @@
+// 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_test
+
+import (
+ "bytes"
+ "errors"
+ "io/ioutil"
+ "math"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ proto3pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/proto3_proto"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto/testdata"
+)
+
+// textMessage implements the methods that allow it to marshal and unmarshal
+// itself as text.
+type textMessage struct {
+}
+
+func (*textMessage) MarshalText() ([]byte, error) {
+ return []byte("custom"), nil
+}
+
+func (*textMessage) UnmarshalText(bytes []byte) error {
+ if string(bytes) != "custom" {
+ return errors.New("expected 'custom'")
+ }
+ return nil
+}
+
+func (*textMessage) Reset() {}
+func (*textMessage) String() string { return "" }
+func (*textMessage) ProtoMessage() {}
+
+func newTestMessage() *pb.MyMessage {
+ msg := &pb.MyMessage{
+ Count: proto.Int32(42),
+ Name: proto.String("Dave"),
+ Quote: proto.String(`"I didn't want to go."`),
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Inner: &pb.InnerMessage{
+ Host: proto.String("footrest.syd"),
+ Port: proto.Int32(7001),
+ Connected: proto.Bool(true),
+ },
+ Others: []*pb.OtherMessage{
+ {
+ Key: proto.Int64(0xdeadbeef),
+ Value: []byte{1, 65, 7, 12},
+ },
+ {
+ Weight: proto.Float32(6.022),
+ Inner: &pb.InnerMessage{
+ Host: proto.String("lesha.mtv"),
+ Port: proto.Int32(8002),
+ },
+ },
+ },
+ Bikeshed: pb.MyMessage_BLUE.Enum(),
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(8),
+ },
+ // One normally wouldn't do this.
+ // This is an undeclared tag 13, as a varint (wire type 0) with value 4.
+ XXX_unrecognized: []byte{13<<3 | 0, 4},
+ }
+ ext := &pb.Ext{
+ Data: proto.String("Big gobs for big rats"),
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
+ panic(err)
+ }
+ greetings := []string{"adg", "easy", "cow"}
+ if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
+ panic(err)
+ }
+
+ // Add an unknown extension. We marshal a pb.Ext, and fake the ID.
+ b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
+ if err != nil {
+ panic(err)
+ }
+ b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
+ proto.SetRawExtension(msg, 201, b)
+
+ // Extensions can be plain fields, too, so let's test that.
+ b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
+ proto.SetRawExtension(msg, 202, b)
+
+ return msg
+}
+
+const text = `count: 42
+name: "Dave"
+quote: "\"I didn't want to go.\""
+pet: "bunny"
+pet: "kitty"
+pet: "horsey"
+inner: <
+ host: "footrest.syd"
+ port: 7001
+ connected: true
+>
+others: <
+ key: 3735928559
+ value: "\001A\007\014"
+>
+others: <
+ weight: 6.022
+ inner: <
+ host: "lesha.mtv"
+ port: 8002
+ >
+>
+bikeshed: BLUE
+SomeGroup {
+ group_field: 8
+}
+/* 2 unknown bytes */
+13: 4
+[testdata.Ext.more]: <
+ data: "Big gobs for big rats"
+>
+[testdata.greeting]: "adg"
+[testdata.greeting]: "easy"
+[testdata.greeting]: "cow"
+/* 13 unknown bytes */
+201: "\t3G skiing"
+/* 3 unknown bytes */
+202: 19
+`
+
+func TestMarshalText(t *testing.T) {
+ buf := new(bytes.Buffer)
+ if err := proto.MarshalText(buf, newTestMessage()); err != nil {
+ t.Fatalf("proto.MarshalText: %v", err)
+ }
+ s := buf.String()
+ if s != text {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
+ }
+}
+
+func TestMarshalTextCustomMessage(t *testing.T) {
+ buf := new(bytes.Buffer)
+ if err := proto.MarshalText(buf, &textMessage{}); err != nil {
+ t.Fatalf("proto.MarshalText: %v", err)
+ }
+ s := buf.String()
+ if s != "custom" {
+ t.Errorf("Got %q, expected %q", s, "custom")
+ }
+}
+func TestMarshalTextNil(t *testing.T) {
+ want := ""
+ tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
+ for i, test := range tests {
+ buf := new(bytes.Buffer)
+ if err := proto.MarshalText(buf, test); err != nil {
+ t.Fatal(err)
+ }
+ if got := buf.String(); got != want {
+ t.Errorf("%d: got %q want %q", i, got, want)
+ }
+ }
+}
+
+func TestMarshalTextUnknownEnum(t *testing.T) {
+ // The Color enum only specifies values 0-2.
+ m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
+ got := m.String()
+ const want = `bikeshed:3 `
+ if got != want {
+ t.Errorf("\n got %q\nwant %q", got, want)
+ }
+}
+
+func BenchmarkMarshalTextBuffered(b *testing.B) {
+ buf := new(bytes.Buffer)
+ m := newTestMessage()
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ proto.MarshalText(buf, m)
+ }
+}
+
+func BenchmarkMarshalTextUnbuffered(b *testing.B) {
+ w := ioutil.Discard
+ m := newTestMessage()
+ for i := 0; i < b.N; i++ {
+ proto.MarshalText(w, m)
+ }
+}
+
+func compact(src string) string {
+ // s/[ \n]+/ /g; s/ $//;
+ dst := make([]byte, len(src))
+ space, comment := false, false
+ j := 0
+ for i := 0; i < len(src); i++ {
+ if strings.HasPrefix(src[i:], "/*") {
+ comment = true
+ i++
+ continue
+ }
+ if comment && strings.HasPrefix(src[i:], "*/") {
+ comment = false
+ i++
+ continue
+ }
+ if comment {
+ continue
+ }
+ c := src[i]
+ if c == ' ' || c == '\n' {
+ space = true
+ continue
+ }
+ if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
+ space = false
+ }
+ if c == '{' {
+ space = false
+ }
+ if space {
+ dst[j] = ' '
+ j++
+ space = false
+ }
+ dst[j] = c
+ j++
+ }
+ if space {
+ dst[j] = ' '
+ j++
+ }
+ return string(dst[0:j])
+}
+
+var compactText = compact(text)
+
+func TestCompactText(t *testing.T) {
+ s := proto.CompactTextString(newTestMessage())
+ if s != compactText {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
+ }
+}
+
+func TestStringEscaping(t *testing.T) {
+ testCases := []struct {
+ in *pb.Strings
+ out string
+ }{
+ {
+ // Test data from C++ test (TextFormatTest.StringEscape).
+ // Single divergence: we don't escape apostrophes.
+ &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
+ "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
+ },
+ {
+ // Test data from the same C++ test.
+ &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
+ "string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
+ },
+ {
+ // Some UTF-8.
+ &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
+ `string_field: "\000\001\377\201"` + "\n",
+ },
+ }
+
+ for i, tc := range testCases {
+ var buf bytes.Buffer
+ if err := proto.MarshalText(&buf, tc.in); err != nil {
+ t.Errorf("proto.MarsalText: %v", err)
+ continue
+ }
+ s := buf.String()
+ if s != tc.out {
+ t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
+ continue
+ }
+
+ // Check round-trip.
+ pb := new(pb.Strings)
+ if err := proto.UnmarshalText(s, pb); err != nil {
+ t.Errorf("#%d: UnmarshalText: %v", i, err)
+ continue
+ }
+ if !proto.Equal(pb, tc.in) {
+ t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
+ }
+ }
+}
+
+// A limitedWriter accepts some output before it fails.
+// This is a proxy for something like a nearly-full or imminently-failing disk,
+// or a network connection that is about to die.
+type limitedWriter struct {
+ b bytes.Buffer
+ limit int
+}
+
+var outOfSpace = errors.New("proto: insufficient space")
+
+func (w *limitedWriter) Write(p []byte) (n int, err error) {
+ var avail = w.limit - w.b.Len()
+ if avail <= 0 {
+ return 0, outOfSpace
+ }
+ if len(p) <= avail {
+ return w.b.Write(p)
+ }
+ n, _ = w.b.Write(p[:avail])
+ return n, outOfSpace
+}
+
+func TestMarshalTextFailing(t *testing.T) {
+ // Try lots of different sizes to exercise more error code-paths.
+ for lim := 0; lim < len(text); lim++ {
+ buf := new(limitedWriter)
+ buf.limit = lim
+ err := proto.MarshalText(buf, newTestMessage())
+ // We expect a certain error, but also some partial results in the buffer.
+ if err != outOfSpace {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
+ }
+ s := buf.b.String()
+ x := text[:buf.limit]
+ if s != x {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
+ }
+ }
+}
+
+func TestFloats(t *testing.T) {
+ tests := []struct {
+ f float64
+ want string
+ }{
+ {0, "0"},
+ {4.7, "4.7"},
+ {math.Inf(1), "inf"},
+ {math.Inf(-1), "-inf"},
+ {math.NaN(), "nan"},
+ }
+ for _, test := range tests {
+ msg := &pb.FloatingPoint{F: &test.f}
+ got := strings.TrimSpace(msg.String())
+ want := `f:` + test.want
+ if got != want {
+ t.Errorf("f=%f: got %q, want %q", test.f, got, want)
+ }
+ }
+}
+
+func TestRepeatedNilText(t *testing.T) {
+ m := &pb.MessageList{
+ Message: []*pb.MessageList_Message{
+ nil,
+ {
+ Name: proto.String("Horse"),
+ },
+ nil,
+ },
+ }
+ want := `Message
+Message {
+ name: "Horse"
+}
+Message
+`
+ if s := proto.MarshalTextString(m); s != want {
+ t.Errorf(" got: %s\nwant: %s", s, want)
+ }
+}
+
+func TestProto3Text(t *testing.T) {
+ tests := []struct {
+ m proto.Message
+ want string
+ }{
+ // zero message
+ {&proto3pb.Message{}, ``},
+ // zero message except for an empty byte slice
+ {&proto3pb.Message{Data: []byte{}}, ``},
+ // trivial case
+ {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
+ // empty map
+ {&pb.MessageWithMap{}, ``},
+ // non-empty map; current map format is the same as a repeated struct
+ {
+ &pb.MessageWithMap{NameMapping: map[int32]string{1234: "Feist"}},
+ `name_mapping:`,
+ },
+ }
+ for _, test := range tests {
+ got := strings.TrimSpace(test.m.String())
+ if got != test.want {
+ t.Errorf("\n got %s\nwant %s", got, test.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/LICENSE
new file mode 100644
index 000000000..37ec93a14
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/LICENSE
@@ -0,0 +1,191 @@
+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:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+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
+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/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/README b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/README
new file mode 100644
index 000000000..5f9c11485
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/README
@@ -0,0 +1,44 @@
+glog
+====
+
+Leveled execution logs for Go.
+
+This is an efficient pure Go implementation of leveled logs in the
+manner of the open source C++ package
+ http://code.google.com/p/google-glog
+
+By binding methods to booleans it is possible to use the log package
+without paying the expense of evaluating the arguments to the log.
+Through the -vmodule flag, the package also provides fine-grained
+control over logging at the file level.
+
+The comment from glog.go introduces the ideas:
+
+ Package glog implements logging analogous to the Google-internal
+ C++ INFO/ERROR/V setup. It provides functions Info, Warning,
+ Error, Fatal, plus formatting variants such as Infof. It
+ also provides V-style logging controlled by the -v and
+ -vmodule=file=2 flags.
+
+ Basic examples:
+
+ glog.Info("Prepare to repel boarders")
+
+ glog.Fatalf("Initialization failed: %s", err)
+
+ See the documentation for the V function for an explanation
+ of these examples:
+
+ if glog.V(2) {
+ glog.Info("Starting transaction...")
+ }
+
+ glog.V(2).Infoln("Processed", nItems, "elements")
+
+
+The repository contains an open source version of the log package
+used inside Google. The master copy of the source lives inside
+Google, not here. The code in this repo is for export only and is not itself
+under development. Feature requests will be ignored.
+
+Send bug reports to golang-nuts@googlegroups.com.
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog.go
new file mode 100644
index 000000000..3e63fffd5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog.go
@@ -0,0 +1,1177 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup.
+// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as
+// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags.
+//
+// Basic examples:
+//
+// glog.Info("Prepare to repel boarders")
+//
+// glog.Fatalf("Initialization failed: %s", err)
+//
+// See the documentation for the V function for an explanation of these examples:
+//
+// if glog.V(2) {
+// glog.Info("Starting transaction...")
+// }
+//
+// glog.V(2).Infoln("Processed", nItems, "elements")
+//
+// Log output is buffered and written periodically using Flush. Programs
+// should call Flush before exiting to guarantee all log output is written.
+//
+// By default, all log statements write to files in a temporary directory.
+// This package provides several flags that modify this behavior.
+// As a result, flag.Parse must be called before any logging is done.
+//
+// -logtostderr=false
+// Logs are written to standard error instead of to files.
+// -alsologtostderr=false
+// Logs are written to standard error as well as to files.
+// -stderrthreshold=ERROR
+// Log events at or above this severity are logged to standard
+// error as well as to files.
+// -log_dir=""
+// Log files will be written to this directory instead of the
+// default temporary directory.
+//
+// Other flags provide aids to debugging.
+//
+// -log_backtrace_at=""
+// When set to a file and line number holding a logging statement,
+// such as
+// -log_backtrace_at=gopherflakes.go:234
+// a stack trace will be written to the Info log whenever execution
+// hits that statement. (Unlike with -vmodule, the ".go" must be
+// present.)
+// -v=0
+// Enable V-leveled logging at the specified level.
+// -vmodule=""
+// The syntax of the argument is a comma-separated list of pattern=N,
+// where pattern is a literal file name (minus the ".go" suffix) or
+// "glob" pattern and N is a V level. For instance,
+// -vmodule=gopher*=3
+// sets the V level to 3 in all Go files whose names begin "gopher".
+//
+package glog
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "io"
+ stdLog "log"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "time"
+)
+
+// severity identifies the sort of log: info, warning etc. It also implements
+// the flag.Value interface. The -stderrthreshold flag is of type severity and
+// should be modified only through the flag.Value interface. The values match
+// the corresponding constants in C++.
+type severity int32 // sync/atomic int32
+
+// These constants identify the log levels in order of increasing severity.
+// A message written to a high-severity log file is also written to each
+// lower-severity log file.
+const (
+ infoLog severity = iota
+ warningLog
+ errorLog
+ fatalLog
+ numSeverity = 4
+)
+
+const severityChar = "IWEF"
+
+var severityName = []string{
+ infoLog: "INFO",
+ warningLog: "WARNING",
+ errorLog: "ERROR",
+ fatalLog: "FATAL",
+}
+
+// get returns the value of the severity.
+func (s *severity) get() severity {
+ return severity(atomic.LoadInt32((*int32)(s)))
+}
+
+// set sets the value of the severity.
+func (s *severity) set(val severity) {
+ atomic.StoreInt32((*int32)(s), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (s *severity) String() string {
+ return strconv.FormatInt(int64(*s), 10)
+}
+
+// Get is part of the flag.Value interface.
+func (s *severity) Get() interface{} {
+ return *s
+}
+
+// Set is part of the flag.Value interface.
+func (s *severity) Set(value string) error {
+ var threshold severity
+ // Is it a known name?
+ if v, ok := severityByName(value); ok {
+ threshold = v
+ } else {
+ v, err := strconv.Atoi(value)
+ if err != nil {
+ return err
+ }
+ threshold = severity(v)
+ }
+ logging.stderrThreshold.set(threshold)
+ return nil
+}
+
+func severityByName(s string) (severity, bool) {
+ s = strings.ToUpper(s)
+ for i, name := range severityName {
+ if name == s {
+ return severity(i), true
+ }
+ }
+ return 0, false
+}
+
+// OutputStats tracks the number of output lines and bytes written.
+type OutputStats struct {
+ lines int64
+ bytes int64
+}
+
+// Lines returns the number of lines written.
+func (s *OutputStats) Lines() int64 {
+ return atomic.LoadInt64(&s.lines)
+}
+
+// Bytes returns the number of bytes written.
+func (s *OutputStats) Bytes() int64 {
+ return atomic.LoadInt64(&s.bytes)
+}
+
+// Stats tracks the number of lines of output and number of bytes
+// per severity level. Values must be read with atomic.LoadInt64.
+var Stats struct {
+ Info, Warning, Error OutputStats
+}
+
+var severityStats = [numSeverity]*OutputStats{
+ infoLog: &Stats.Info,
+ warningLog: &Stats.Warning,
+ errorLog: &Stats.Error,
+}
+
+// Level is exported because it appears in the arguments to V and is
+// the type of the v flag, which can be set programmatically.
+// It's a distinct type because we want to discriminate it from logType.
+// Variables of type level are only changed under logging.mu.
+// The -v flag is read only with atomic ops, so the state of the logging
+// module is consistent.
+
+// Level is treated as a sync/atomic int32.
+
+// Level specifies a level of verbosity for V logs. *Level implements
+// flag.Value; the -v flag is of type Level and should be modified
+// only through the flag.Value interface.
+type Level int32
+
+// get returns the value of the Level.
+func (l *Level) get() Level {
+ return Level(atomic.LoadInt32((*int32)(l)))
+}
+
+// set sets the value of the Level.
+func (l *Level) set(val Level) {
+ atomic.StoreInt32((*int32)(l), int32(val))
+}
+
+// String is part of the flag.Value interface.
+func (l *Level) String() string {
+ return strconv.FormatInt(int64(*l), 10)
+}
+
+// Get is part of the flag.Value interface.
+func (l *Level) Get() interface{} {
+ return *l
+}
+
+// Set is part of the flag.Value interface.
+func (l *Level) Set(value string) error {
+ v, err := strconv.Atoi(value)
+ if err != nil {
+ return err
+ }
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ logging.setVState(Level(v), logging.vmodule.filter, false)
+ return nil
+}
+
+// moduleSpec represents the setting of the -vmodule flag.
+type moduleSpec struct {
+ filter []modulePat
+}
+
+// modulePat contains a filter for the -vmodule flag.
+// It holds a verbosity level and a file pattern to match.
+type modulePat struct {
+ pattern string
+ literal bool // The pattern is a literal string
+ level Level
+}
+
+// match reports whether the file matches the pattern. It uses a string
+// comparison if the pattern contains no metacharacters.
+func (m *modulePat) match(file string) bool {
+ if m.literal {
+ return file == m.pattern
+ }
+ match, _ := filepath.Match(m.pattern, file)
+ return match
+}
+
+func (m *moduleSpec) String() string {
+ // Lock because the type is not atomic. TODO: clean this up.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ var b bytes.Buffer
+ for i, f := range m.filter {
+ if i > 0 {
+ b.WriteRune(',')
+ }
+ fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
+ }
+ return b.String()
+}
+
+// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported.
+func (m *moduleSpec) Get() interface{} {
+ return nil
+}
+
+var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
+
+// Syntax: -vmodule=recordio=2,file=1,gfs*=3
+func (m *moduleSpec) Set(value string) error {
+ var filter []modulePat
+ for _, pat := range strings.Split(value, ",") {
+ if len(pat) == 0 {
+ // Empty strings such as from a trailing comma can be ignored.
+ continue
+ }
+ patLev := strings.Split(pat, "=")
+ if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
+ return errVmoduleSyntax
+ }
+ pattern := patLev[0]
+ v, err := strconv.Atoi(patLev[1])
+ if err != nil {
+ return errors.New("syntax error: expect comma-separated list of filename=N")
+ }
+ if v < 0 {
+ return errors.New("negative value for vmodule level")
+ }
+ if v == 0 {
+ continue // Ignore. It's harmless but no point in paying the overhead.
+ }
+ // TODO: check syntax of filter?
+ filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
+ }
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ logging.setVState(logging.verbosity, filter, true)
+ return nil
+}
+
+// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
+// that require filepath.Match to be called to match the pattern.
+func isLiteral(pattern string) bool {
+ return !strings.ContainsAny(pattern, `\*?[]`)
+}
+
+// traceLocation represents the setting of the -log_backtrace_at flag.
+type traceLocation struct {
+ file string
+ line int
+}
+
+// isSet reports whether the trace location has been specified.
+// logging.mu is held.
+func (t *traceLocation) isSet() bool {
+ return t.line > 0
+}
+
+// match reports whether the specified file and line matches the trace location.
+// The argument file name is the full path, not the basename specified in the flag.
+// logging.mu is held.
+func (t *traceLocation) match(file string, line int) bool {
+ if t.line != line {
+ return false
+ }
+ if i := strings.LastIndex(file, "/"); i >= 0 {
+ file = file[i+1:]
+ }
+ return t.file == file
+}
+
+func (t *traceLocation) String() string {
+ // Lock because the type is not atomic. TODO: clean this up.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ return fmt.Sprintf("%s:%d", t.file, t.line)
+}
+
+// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
+// struct is not exported
+func (t *traceLocation) Get() interface{} {
+ return nil
+}
+
+var errTraceSyntax = errors.New("syntax error: expect file.go:234")
+
+// Syntax: -log_backtrace_at=gopherflakes.go:234
+// Note that unlike vmodule the file extension is included here.
+func (t *traceLocation) Set(value string) error {
+ if value == "" {
+ // Unset.
+ t.line = 0
+ t.file = ""
+ }
+ fields := strings.Split(value, ":")
+ if len(fields) != 2 {
+ return errTraceSyntax
+ }
+ file, line := fields[0], fields[1]
+ if !strings.Contains(file, ".") {
+ return errTraceSyntax
+ }
+ v, err := strconv.Atoi(line)
+ if err != nil {
+ return errTraceSyntax
+ }
+ if v <= 0 {
+ return errors.New("negative or zero value for level")
+ }
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ t.line = v
+ t.file = file
+ return nil
+}
+
+// flushSyncWriter is the interface satisfied by logging destinations.
+type flushSyncWriter interface {
+ Flush() error
+ Sync() error
+ io.Writer
+}
+
+func init() {
+ flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files")
+ flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files")
+ flag.Var(&logging.verbosity, "v", "log level for V logs")
+ flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr")
+ flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging")
+ flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace")
+
+ // Default stderrThreshold is ERROR.
+ logging.stderrThreshold = errorLog
+
+ logging.setVState(0, nil, false)
+ go logging.flushDaemon()
+}
+
+// Flush flushes all pending log I/O.
+func Flush() {
+ logging.lockAndFlushAll()
+}
+
+// loggingT collects all the global state of the logging setup.
+type loggingT struct {
+ // Boolean flags. Not handled atomically because the flag.Value interface
+ // does not let us avoid the =true, and that shorthand is necessary for
+ // compatibility. TODO: does this matter enough to fix? Seems unlikely.
+ toStderr bool // The -logtostderr flag.
+ alsoToStderr bool // The -alsologtostderr flag.
+
+ // Level flag. Handled atomically.
+ stderrThreshold severity // The -stderrthreshold flag.
+
+ // freeList is a list of byte buffers, maintained under freeListMu.
+ freeList *buffer
+ // freeListMu maintains the free list. It is separate from the main mutex
+ // so buffers can be grabbed and printed to without holding the main lock,
+ // for better parallelization.
+ freeListMu sync.Mutex
+
+ // mu protects the remaining elements of this structure and is
+ // used to synchronize logging.
+ mu sync.Mutex
+ // file holds writer for each of the log types.
+ file [numSeverity]flushSyncWriter
+ // pcs is used in V to avoid an allocation when computing the caller's PC.
+ pcs [1]uintptr
+ // vmap is a cache of the V Level for each V() call site, identified by PC.
+ // It is wiped whenever the vmodule flag changes state.
+ vmap map[uintptr]Level
+ // filterLength stores the length of the vmodule filter chain. If greater
+ // than zero, it means vmodule is enabled. It may be read safely
+ // using sync.LoadInt32, but is only modified under mu.
+ filterLength int32
+ // traceLocation is the state of the -log_backtrace_at flag.
+ traceLocation traceLocation
+ // These flags are modified only under lock, although verbosity may be fetched
+ // safely using atomic.LoadInt32.
+ vmodule moduleSpec // The state of the -vmodule flag.
+ verbosity Level // V logging level, the value of the -v flag/
+}
+
+// buffer holds a byte Buffer for reuse. The zero value is ready for use.
+type buffer struct {
+ bytes.Buffer
+ tmp [64]byte // temporary byte array for creating headers.
+ next *buffer
+}
+
+var logging loggingT
+
+// setVState sets a consistent state for V logging.
+// l.mu is held.
+func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) {
+ // Turn verbosity off so V will not fire while we are in transition.
+ logging.verbosity.set(0)
+ // Ditto for filter length.
+ atomic.StoreInt32(&logging.filterLength, 0)
+
+ // Set the new filters and wipe the pc->Level map if the filter has changed.
+ if setFilter {
+ logging.vmodule.filter = filter
+ logging.vmap = make(map[uintptr]Level)
+ }
+
+ // Things are consistent now, so enable filtering and verbosity.
+ // They are enabled in order opposite to that in V.
+ atomic.StoreInt32(&logging.filterLength, int32(len(filter)))
+ logging.verbosity.set(verbosity)
+}
+
+// getBuffer returns a new, ready-to-use buffer.
+func (l *loggingT) getBuffer() *buffer {
+ l.freeListMu.Lock()
+ b := l.freeList
+ if b != nil {
+ l.freeList = b.next
+ }
+ l.freeListMu.Unlock()
+ if b == nil {
+ b = new(buffer)
+ } else {
+ b.next = nil
+ b.Reset()
+ }
+ return b
+}
+
+// putBuffer returns a buffer to the free list.
+func (l *loggingT) putBuffer(b *buffer) {
+ if b.Len() >= 256 {
+ // Let big buffers die a natural death.
+ return
+ }
+ l.freeListMu.Lock()
+ b.next = l.freeList
+ l.freeList = b
+ l.freeListMu.Unlock()
+}
+
+var timeNow = time.Now // Stubbed out for testing.
+
+/*
+header formats a log header as defined by the C++ implementation.
+It returns a buffer containing the formatted header and the user's file and line number.
+The depth specifies how many stack frames above lives the source line to be identified in the log message.
+
+Log lines have this form:
+ Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg...
+where the fields are defined as follows:
+ L A single character, representing the log level (eg 'I' for INFO)
+ mm The month (zero padded; ie May is '05')
+ dd The day (zero padded)
+ hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds
+ threadid The space-padded thread ID as returned by GetTID()
+ file The file name
+ line The line number
+ msg The user-supplied message
+*/
+func (l *loggingT) header(s severity, depth int) (*buffer, string, int) {
+ _, file, line, ok := runtime.Caller(3 + depth)
+ if !ok {
+ file = "???"
+ line = 1
+ } else {
+ slash := strings.LastIndex(file, "/")
+ if slash >= 0 {
+ file = file[slash+1:]
+ }
+ }
+ return l.formatHeader(s, file, line), file, line
+}
+
+// formatHeader formats a log header using the provided file name and line number.
+func (l *loggingT) formatHeader(s severity, file string, line int) *buffer {
+ now := timeNow()
+ if line < 0 {
+ line = 0 // not a real line number, but acceptable to someDigits
+ }
+ if s > fatalLog {
+ s = infoLog // for safety.
+ }
+ buf := l.getBuffer()
+
+ // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand.
+ // It's worth about 3X. Fprintf is hard.
+ _, month, day := now.Date()
+ hour, minute, second := now.Clock()
+ // Lmmdd hh:mm:ss.uuuuuu threadid file:line]
+ buf.tmp[0] = severityChar[s]
+ buf.twoDigits(1, int(month))
+ buf.twoDigits(3, day)
+ buf.tmp[5] = ' '
+ buf.twoDigits(6, hour)
+ buf.tmp[8] = ':'
+ buf.twoDigits(9, minute)
+ buf.tmp[11] = ':'
+ buf.twoDigits(12, second)
+ buf.tmp[14] = '.'
+ buf.nDigits(6, 15, now.Nanosecond()/1000, '0')
+ buf.tmp[21] = ' '
+ buf.nDigits(7, 22, pid, ' ') // TODO: should be TID
+ buf.tmp[29] = ' '
+ buf.Write(buf.tmp[:30])
+ buf.WriteString(file)
+ buf.tmp[0] = ':'
+ n := buf.someDigits(1, line)
+ buf.tmp[n+1] = ']'
+ buf.tmp[n+2] = ' '
+ buf.Write(buf.tmp[:n+3])
+ return buf
+}
+
+// Some custom tiny helper functions to print the log header efficiently.
+
+const digits = "0123456789"
+
+// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i].
+func (buf *buffer) twoDigits(i, d int) {
+ buf.tmp[i+1] = digits[d%10]
+ d /= 10
+ buf.tmp[i] = digits[d%10]
+}
+
+// nDigits formats an n-digit integer at buf.tmp[i],
+// padding with pad on the left.
+// It assumes d >= 0.
+func (buf *buffer) nDigits(n, i, d int, pad byte) {
+ j := n - 1
+ for ; j >= 0 && d > 0; j-- {
+ buf.tmp[i+j] = digits[d%10]
+ d /= 10
+ }
+ for ; j >= 0; j-- {
+ buf.tmp[i+j] = pad
+ }
+}
+
+// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i].
+func (buf *buffer) someDigits(i, d int) int {
+ // Print into the top, then copy down. We know there's space for at least
+ // a 10-digit number.
+ j := len(buf.tmp)
+ for {
+ j--
+ buf.tmp[j] = digits[d%10]
+ d /= 10
+ if d == 0 {
+ break
+ }
+ }
+ return copy(buf.tmp[i:], buf.tmp[j:])
+}
+
+func (l *loggingT) println(s severity, args ...interface{}) {
+ buf, file, line := l.header(s, 0)
+ fmt.Fprintln(buf, args...)
+ l.output(s, buf, file, line, false)
+}
+
+func (l *loggingT) print(s severity, args ...interface{}) {
+ l.printDepth(s, 1, args...)
+}
+
+func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) {
+ buf, file, line := l.header(s, depth)
+ fmt.Fprint(buf, args...)
+ if buf.Bytes()[buf.Len()-1] != '\n' {
+ buf.WriteByte('\n')
+ }
+ l.output(s, buf, file, line, false)
+}
+
+func (l *loggingT) printf(s severity, format string, args ...interface{}) {
+ buf, file, line := l.header(s, 0)
+ fmt.Fprintf(buf, format, args...)
+ if buf.Bytes()[buf.Len()-1] != '\n' {
+ buf.WriteByte('\n')
+ }
+ l.output(s, buf, file, line, false)
+}
+
+// printWithFileLine behaves like print but uses the provided file and line number. If
+// alsoLogToStderr is true, the log message always appears on standard error; it
+// will also appear in the log file unless --logtostderr is set.
+func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) {
+ buf := l.formatHeader(s, file, line)
+ fmt.Fprint(buf, args...)
+ if buf.Bytes()[buf.Len()-1] != '\n' {
+ buf.WriteByte('\n')
+ }
+ l.output(s, buf, file, line, alsoToStderr)
+}
+
+// output writes the data to the log files and releases the buffer.
+func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
+ l.mu.Lock()
+ if l.traceLocation.isSet() {
+ if l.traceLocation.match(file, line) {
+ buf.Write(stacks(false))
+ }
+ }
+ data := buf.Bytes()
+ if l.toStderr {
+ os.Stderr.Write(data)
+ } else {
+ if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
+ os.Stderr.Write(data)
+ }
+ if l.file[s] == nil {
+ if err := l.createFiles(s); err != nil {
+ os.Stderr.Write(data) // Make sure the message appears somewhere.
+ l.exit(err)
+ }
+ }
+ switch s {
+ case fatalLog:
+ l.file[fatalLog].Write(data)
+ fallthrough
+ case errorLog:
+ l.file[errorLog].Write(data)
+ fallthrough
+ case warningLog:
+ l.file[warningLog].Write(data)
+ fallthrough
+ case infoLog:
+ l.file[infoLog].Write(data)
+ }
+ }
+ if s == fatalLog {
+ // If we got here via Exit rather than Fatal, print no stacks.
+ if atomic.LoadUint32(&fatalNoStacks) > 0 {
+ l.mu.Unlock()
+ timeoutFlush(10 * time.Second)
+ os.Exit(1)
+ }
+ // Dump all goroutine stacks before exiting.
+ // First, make sure we see the trace for the current goroutine on standard error.
+ // If -logtostderr has been specified, the loop below will do that anyway
+ // as the first stack in the full dump.
+ if !l.toStderr {
+ os.Stderr.Write(stacks(false))
+ }
+ // Write the stack trace for all goroutines to the files.
+ trace := stacks(true)
+ logExitFunc = func(error) {} // If we get a write error, we'll still exit below.
+ for log := fatalLog; log >= infoLog; log-- {
+ if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set.
+ f.Write(trace)
+ }
+ }
+ l.mu.Unlock()
+ timeoutFlush(10 * time.Second)
+ os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway.
+ }
+ l.putBuffer(buf)
+ l.mu.Unlock()
+ if stats := severityStats[s]; stats != nil {
+ atomic.AddInt64(&stats.lines, 1)
+ atomic.AddInt64(&stats.bytes, int64(len(data)))
+ }
+}
+
+// timeoutFlush calls Flush and returns when it completes or after timeout
+// elapses, whichever happens first. This is needed because the hooks invoked
+// by Flush may deadlock when glog.Fatal is called from a hook that holds
+// a lock.
+func timeoutFlush(timeout time.Duration) {
+ done := make(chan bool, 1)
+ go func() {
+ Flush() // calls logging.lockAndFlushAll()
+ done <- true
+ }()
+ select {
+ case <-done:
+ case <-time.After(timeout):
+ fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout)
+ }
+}
+
+// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines.
+func stacks(all bool) []byte {
+ // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though.
+ n := 10000
+ if all {
+ n = 100000
+ }
+ var trace []byte
+ for i := 0; i < 5; i++ {
+ trace = make([]byte, n)
+ nbytes := runtime.Stack(trace, all)
+ if nbytes < len(trace) {
+ return trace[:nbytes]
+ }
+ n *= 2
+ }
+ return trace
+}
+
+// logExitFunc provides a simple mechanism to override the default behavior
+// of exiting on error. Used in testing and to guarantee we reach a required exit
+// for fatal logs. Instead, exit could be a function rather than a method but that
+// would make its use clumsier.
+var logExitFunc func(error)
+
+// exit is called if there is trouble creating or writing log files.
+// It flushes the logs and exits the program; there's no point in hanging around.
+// l.mu is held.
+func (l *loggingT) exit(err error) {
+ fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err)
+ // If logExitFunc is set, we do that instead of exiting.
+ if logExitFunc != nil {
+ logExitFunc(err)
+ return
+ }
+ l.flushAll()
+ os.Exit(2)
+}
+
+// syncBuffer joins a bufio.Writer to its underlying file, providing access to the
+// file's Sync method and providing a wrapper for the Write method that provides log
+// file rotation. There are conflicting methods, so the file cannot be embedded.
+// l.mu is held for all its methods.
+type syncBuffer struct {
+ logger *loggingT
+ *bufio.Writer
+ file *os.File
+ sev severity
+ nbytes uint64 // The number of bytes written to this file
+}
+
+func (sb *syncBuffer) Sync() error {
+ return sb.file.Sync()
+}
+
+func (sb *syncBuffer) Write(p []byte) (n int, err error) {
+ if sb.nbytes+uint64(len(p)) >= MaxSize {
+ if err := sb.rotateFile(time.Now()); err != nil {
+ sb.logger.exit(err)
+ }
+ }
+ n, err = sb.Writer.Write(p)
+ sb.nbytes += uint64(n)
+ if err != nil {
+ sb.logger.exit(err)
+ }
+ return
+}
+
+// rotateFile closes the syncBuffer's file and starts a new one.
+func (sb *syncBuffer) rotateFile(now time.Time) error {
+ if sb.file != nil {
+ sb.Flush()
+ sb.file.Close()
+ }
+ var err error
+ sb.file, _, err = create(severityName[sb.sev], now)
+ sb.nbytes = 0
+ if err != nil {
+ return err
+ }
+
+ sb.Writer = bufio.NewWriterSize(sb.file, bufferSize)
+
+ // Write header.
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05"))
+ fmt.Fprintf(&buf, "Running on machine: %s\n", host)
+ fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH)
+ fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n")
+ n, err := sb.file.Write(buf.Bytes())
+ sb.nbytes += uint64(n)
+ return err
+}
+
+// bufferSize sizes the buffer associated with each log file. It's large
+// so that log records can accumulate without the logging thread blocking
+// on disk I/O. The flushDaemon will block instead.
+const bufferSize = 256 * 1024
+
+// createFiles creates all the log files for severity from sev down to infoLog.
+// l.mu is held.
+func (l *loggingT) createFiles(sev severity) error {
+ now := time.Now()
+ // Files are created in decreasing severity order, so as soon as we find one
+ // has already been created, we can stop.
+ for s := sev; s >= infoLog && l.file[s] == nil; s-- {
+ sb := &syncBuffer{
+ logger: l,
+ sev: s,
+ }
+ if err := sb.rotateFile(now); err != nil {
+ return err
+ }
+ l.file[s] = sb
+ }
+ return nil
+}
+
+const flushInterval = 30 * time.Second
+
+// flushDaemon periodically flushes the log file buffers.
+func (l *loggingT) flushDaemon() {
+ for _ = range time.NewTicker(flushInterval).C {
+ l.lockAndFlushAll()
+ }
+}
+
+// lockAndFlushAll is like flushAll but locks l.mu first.
+func (l *loggingT) lockAndFlushAll() {
+ l.mu.Lock()
+ l.flushAll()
+ l.mu.Unlock()
+}
+
+// flushAll flushes all the logs and attempts to "sync" their data to disk.
+// l.mu is held.
+func (l *loggingT) flushAll() {
+ // Flush from fatal down, in case there's trouble flushing.
+ for s := fatalLog; s >= infoLog; s-- {
+ file := l.file[s]
+ if file != nil {
+ file.Flush() // ignore error
+ file.Sync() // ignore error
+ }
+ }
+}
+
+// CopyStandardLogTo arranges for messages written to the Go "log" package's
+// default logs to also appear in the Google logs for the named and lower
+// severities. Subsequent changes to the standard log's default output location
+// or format may break this behavior.
+//
+// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not
+// recognized, CopyStandardLogTo panics.
+func CopyStandardLogTo(name string) {
+ sev, ok := severityByName(name)
+ if !ok {
+ panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name))
+ }
+ // Set a log format that captures the user's file and line:
+ // d.go:23: message
+ stdLog.SetFlags(stdLog.Lshortfile)
+ stdLog.SetOutput(logBridge(sev))
+}
+
+// logBridge provides the Write method that enables CopyStandardLogTo to connect
+// Go's standard logs to the logs provided by this package.
+type logBridge severity
+
+// Write parses the standard logging line and passes its components to the
+// logger for severity(lb).
+func (lb logBridge) Write(b []byte) (n int, err error) {
+ var (
+ file = "???"
+ line = 1
+ text string
+ )
+ // Split "d.go:23: message" into "d.go", "23", and "message".
+ if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
+ text = fmt.Sprintf("bad log format: %s", b)
+ } else {
+ file = string(parts[0])
+ text = string(parts[2][1:]) // skip leading space
+ line, err = strconv.Atoi(string(parts[1]))
+ if err != nil {
+ text = fmt.Sprintf("bad line number: %s", b)
+ line = 1
+ }
+ }
+ // printWithFileLine with alsoToStderr=true, so standard log messages
+ // always appear on standard error.
+ logging.printWithFileLine(severity(lb), file, line, true, text)
+ return len(b), nil
+}
+
+// setV computes and remembers the V level for a given PC
+// when vmodule is enabled.
+// File pattern matching takes the basename of the file, stripped
+// of its .go suffix, and uses filepath.Match, which is a little more
+// general than the *? matching used in C++.
+// l.mu is held.
+func (l *loggingT) setV(pc uintptr) Level {
+ fn := runtime.FuncForPC(pc)
+ file, _ := fn.FileLine(pc)
+ // The file is something like /a/b/c/d.go. We want just the d.
+ if strings.HasSuffix(file, ".go") {
+ file = file[:len(file)-3]
+ }
+ if slash := strings.LastIndex(file, "/"); slash >= 0 {
+ file = file[slash+1:]
+ }
+ for _, filter := range l.vmodule.filter {
+ if filter.match(file) {
+ l.vmap[pc] = filter.level
+ return filter.level
+ }
+ }
+ l.vmap[pc] = 0
+ return 0
+}
+
+// Verbose is a boolean type that implements Infof (like Printf) etc.
+// See the documentation of V for more information.
+type Verbose bool
+
+// V reports whether verbosity at the call site is at least the requested level.
+// The returned value is a boolean of type Verbose, which implements Info, Infoln
+// and Infof. These methods will write to the Info log if called.
+// Thus, one may write either
+// if glog.V(2) { glog.Info("log this") }
+// or
+// glog.V(2).Info("log this")
+// The second form is shorter but the first is cheaper if logging is off because it does
+// not evaluate its arguments.
+//
+// Whether an individual call to V generates a log record depends on the setting of
+// the -v and --vmodule flags; both are off by default. If the level in the call to
+// V is at least the value of -v, or of -vmodule for the source file containing the
+// call, the V call will log.
+func V(level Level) Verbose {
+ // This function tries hard to be cheap unless there's work to do.
+ // The fast path is two atomic loads and compares.
+
+ // Here is a cheap but safe test to see if V logging is enabled globally.
+ if logging.verbosity.get() >= level {
+ return Verbose(true)
+ }
+
+ // It's off globally but it vmodule may still be set.
+ // Here is another cheap but safe test to see if vmodule is enabled.
+ if atomic.LoadInt32(&logging.filterLength) > 0 {
+ // Now we need a proper lock to use the logging structure. The pcs field
+ // is shared so we must lock before accessing it. This is fairly expensive,
+ // but if V logging is enabled we're slow anyway.
+ logging.mu.Lock()
+ defer logging.mu.Unlock()
+ if runtime.Callers(2, logging.pcs[:]) == 0 {
+ return Verbose(false)
+ }
+ v, ok := logging.vmap[logging.pcs[0]]
+ if !ok {
+ v = logging.setV(logging.pcs[0])
+ }
+ return Verbose(v >= level)
+ }
+ return Verbose(false)
+}
+
+// Info is equivalent to the global Info function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Info(args ...interface{}) {
+ if v {
+ logging.print(infoLog, args...)
+ }
+}
+
+// Infoln is equivalent to the global Infoln function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infoln(args ...interface{}) {
+ if v {
+ logging.println(infoLog, args...)
+ }
+}
+
+// Infof is equivalent to the global Infof function, guarded by the value of v.
+// See the documentation of V for usage.
+func (v Verbose) Infof(format string, args ...interface{}) {
+ if v {
+ logging.printf(infoLog, format, args...)
+ }
+}
+
+// Info logs to the INFO log.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Info(args ...interface{}) {
+ logging.print(infoLog, args...)
+}
+
+// InfoDepth acts as Info but uses depth to determine which call frame to log.
+// InfoDepth(0, "msg") is the same as Info("msg").
+func InfoDepth(depth int, args ...interface{}) {
+ logging.printDepth(infoLog, depth, args...)
+}
+
+// Infoln logs to the INFO log.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Infoln(args ...interface{}) {
+ logging.println(infoLog, args...)
+}
+
+// Infof logs to the INFO log.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Infof(format string, args ...interface{}) {
+ logging.printf(infoLog, format, args...)
+}
+
+// Warning logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Warning(args ...interface{}) {
+ logging.print(warningLog, args...)
+}
+
+// WarningDepth acts as Warning but uses depth to determine which call frame to log.
+// WarningDepth(0, "msg") is the same as Warning("msg").
+func WarningDepth(depth int, args ...interface{}) {
+ logging.printDepth(warningLog, depth, args...)
+}
+
+// Warningln logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Warningln(args ...interface{}) {
+ logging.println(warningLog, args...)
+}
+
+// Warningf logs to the WARNING and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Warningf(format string, args ...interface{}) {
+ logging.printf(warningLog, format, args...)
+}
+
+// Error logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Error(args ...interface{}) {
+ logging.print(errorLog, args...)
+}
+
+// ErrorDepth acts as Error but uses depth to determine which call frame to log.
+// ErrorDepth(0, "msg") is the same as Error("msg").
+func ErrorDepth(depth int, args ...interface{}) {
+ logging.printDepth(errorLog, depth, args...)
+}
+
+// Errorln logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Errorln(args ...interface{}) {
+ logging.println(errorLog, args...)
+}
+
+// Errorf logs to the ERROR, WARNING, and INFO logs.
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Errorf(format string, args ...interface{}) {
+ logging.printf(errorLog, format, args...)
+}
+
+// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Fatal(args ...interface{}) {
+ logging.print(fatalLog, args...)
+}
+
+// FatalDepth acts as Fatal but uses depth to determine which call frame to log.
+// FatalDepth(0, "msg") is the same as Fatal("msg").
+func FatalDepth(depth int, args ...interface{}) {
+ logging.printDepth(fatalLog, depth, args...)
+}
+
+// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Println; a newline is appended if missing.
+func Fatalln(args ...interface{}) {
+ logging.println(fatalLog, args...)
+}
+
+// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
+// including a stack trace of all running goroutines, then calls os.Exit(255).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Fatalf(format string, args ...interface{}) {
+ logging.printf(fatalLog, format, args...)
+}
+
+// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
+// It allows Exit and relatives to use the Fatal logs.
+var fatalNoStacks uint32
+
+// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
+func Exit(args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.print(fatalLog, args...)
+}
+
+// ExitDepth acts as Exit but uses depth to determine which call frame to log.
+// ExitDepth(0, "msg") is the same as Exit("msg").
+func ExitDepth(depth int, args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.printDepth(fatalLog, depth, args...)
+}
+
+// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+func Exitln(args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.println(fatalLog, args...)
+}
+
+// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
+// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
+func Exitf(format string, args ...interface{}) {
+ atomic.StoreUint32(&fatalNoStacks, 1)
+ logging.printf(fatalLog, format, args...)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog_file.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog_file.go
new file mode 100644
index 000000000..65075d281
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog_file.go
@@ -0,0 +1,124 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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.
+
+// File I/O for logs.
+
+package glog
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+)
+
+// MaxSize is the maximum size of a log file in bytes.
+var MaxSize uint64 = 1024 * 1024 * 1800
+
+// logDirs lists the candidate directories for new log files.
+var logDirs []string
+
+// If non-empty, overrides the choice of directory in which to write logs.
+// See createLogDirs for the full list of possible destinations.
+var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory")
+
+func createLogDirs() {
+ if *logDir != "" {
+ logDirs = append(logDirs, *logDir)
+ }
+ logDirs = append(logDirs, os.TempDir())
+}
+
+var (
+ pid = os.Getpid()
+ program = filepath.Base(os.Args[0])
+ host = "unknownhost"
+ userName = "unknownuser"
+)
+
+func init() {
+ h, err := os.Hostname()
+ if err == nil {
+ host = shortHostname(h)
+ }
+
+ current, err := user.Current()
+ if err == nil {
+ userName = current.Username
+ }
+
+ // Sanitize userName since it may contain filepath separators on Windows.
+ userName = strings.Replace(userName, `\`, "_", -1)
+}
+
+// shortHostname returns its argument, truncating at the first period.
+// For instance, given "www.google.com" it returns "www".
+func shortHostname(hostname string) string {
+ if i := strings.Index(hostname, "."); i >= 0 {
+ return hostname[:i]
+ }
+ return hostname
+}
+
+// logName returns a new log file name containing tag, with start time t, and
+// the name for the symlink for tag.
+func logName(tag string, t time.Time) (name, link string) {
+ name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
+ program,
+ host,
+ userName,
+ tag,
+ t.Year(),
+ t.Month(),
+ t.Day(),
+ t.Hour(),
+ t.Minute(),
+ t.Second(),
+ pid)
+ return name, program + "." + tag
+}
+
+var onceLogDirs sync.Once
+
+// create creates a new log file and returns the file and its filename, which
+// contains tag ("INFO", "FATAL", etc.) and t. If the file is created
+// successfully, create also attempts to update the symlink for that tag, ignoring
+// errors.
+func create(tag string, t time.Time) (f *os.File, filename string, err error) {
+ onceLogDirs.Do(createLogDirs)
+ if len(logDirs) == 0 {
+ return nil, "", errors.New("log: no log dirs")
+ }
+ name, link := logName(tag, t)
+ var lastErr error
+ for _, dir := range logDirs {
+ fname := filepath.Join(dir, name)
+ f, err := os.Create(fname)
+ if err == nil {
+ symlink := filepath.Join(dir, link)
+ os.Remove(symlink) // ignore err
+ os.Symlink(name, symlink) // ignore err
+ return f, fname, nil
+ }
+ lastErr = err
+ }
+ return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog_test.go
new file mode 100644
index 000000000..0fb376e1f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog/glog_test.go
@@ -0,0 +1,415 @@
+// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
+//
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 glog
+
+import (
+ "bytes"
+ "fmt"
+ stdLog "log"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+)
+
+// Test that shortHostname works as advertised.
+func TestShortHostname(t *testing.T) {
+ for hostname, expect := range map[string]string{
+ "": "",
+ "host": "host",
+ "host.google.com": "host",
+ } {
+ if got := shortHostname(hostname); expect != got {
+ t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
+ }
+ }
+}
+
+// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
+type flushBuffer struct {
+ bytes.Buffer
+}
+
+func (f *flushBuffer) Flush() error {
+ return nil
+}
+
+func (f *flushBuffer) Sync() error {
+ return nil
+}
+
+// swap sets the log writers and returns the old array.
+func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ old = l.file
+ for i, w := range writers {
+ logging.file[i] = w
+ }
+ return
+}
+
+// newBuffers sets the log writers to all new byte buffers and returns the old array.
+func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
+ return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
+}
+
+// contents returns the specified log value as a string.
+func contents(s severity) string {
+ return logging.file[s].(*flushBuffer).String()
+}
+
+// contains reports whether the string is contained in the log.
+func contains(s severity, str string, t *testing.T) bool {
+ return strings.Contains(contents(s), str)
+}
+
+// setFlags configures the logging flags how the test expects them.
+func setFlags() {
+ logging.toStderr = false
+}
+
+// Test that Info works as advertised.
+func TestInfo(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ Info("test")
+ if !contains(infoLog, "I", t) {
+ t.Errorf("Info has wrong character: %q", contents(infoLog))
+ }
+ if !contains(infoLog, "test", t) {
+ t.Error("Info failed")
+ }
+}
+
+func TestInfoDepth(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+
+ f := func() { InfoDepth(1, "depth-test1") }
+
+ // The next three lines must stay together
+ _, _, wantLine, _ := runtime.Caller(0)
+ InfoDepth(0, "depth-test0")
+ f()
+
+ msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n")
+ if len(msgs) != 2 {
+ t.Fatalf("Got %d lines, expected 2", len(msgs))
+ }
+
+ for i, m := range msgs {
+ if !strings.HasPrefix(m, "I") {
+ t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
+ }
+ w := fmt.Sprintf("depth-test%d", i)
+ if !strings.Contains(m, w) {
+ t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
+ }
+
+ // pull out the line number (between : and ])
+ msg := m[strings.LastIndex(m, ":")+1:]
+ x := strings.Index(msg, "]")
+ if x < 0 {
+ t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
+ continue
+ }
+ line, err := strconv.Atoi(msg[:x])
+ if err != nil {
+ t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
+ continue
+ }
+ wantLine++
+ if wantLine != line {
+ t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
+ }
+ }
+}
+
+func init() {
+ CopyStandardLogTo("INFO")
+}
+
+// Test that CopyStandardLogTo panics on bad input.
+func TestCopyStandardLogToPanic(t *testing.T) {
+ defer func() {
+ if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
+ t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
+ }
+ }()
+ CopyStandardLogTo("LOG")
+}
+
+// Test that using the standard log package logs to INFO.
+func TestStandardLog(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ stdLog.Print("test")
+ if !contains(infoLog, "I", t) {
+ t.Errorf("Info has wrong character: %q", contents(infoLog))
+ }
+ if !contains(infoLog, "test", t) {
+ t.Error("Info failed")
+ }
+}
+
+// Test that the header has the correct format.
+func TestHeader(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ defer func(previous func() time.Time) { timeNow = previous }(timeNow)
+ timeNow = func() time.Time {
+ return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
+ }
+ pid = 1234
+ Info("test")
+ var line int
+ format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n"
+ n, err := fmt.Sscanf(contents(infoLog), format, &line)
+ if n != 1 || err != nil {
+ t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
+ }
+ // Scanf treats multiple spaces as equivalent to a single space,
+ // so check for correct space-padding also.
+ want := fmt.Sprintf(format, line)
+ if contents(infoLog) != want {
+ t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want)
+ }
+}
+
+// Test that an Error log goes to Warning and Info.
+// Even in the Info log, the source character will be E, so the data should
+// all be identical.
+func TestError(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ Error("test")
+ if !contains(errorLog, "E", t) {
+ t.Errorf("Error has wrong character: %q", contents(errorLog))
+ }
+ if !contains(errorLog, "test", t) {
+ t.Error("Error failed")
+ }
+ str := contents(errorLog)
+ if !contains(warningLog, str, t) {
+ t.Error("Warning failed")
+ }
+ if !contains(infoLog, str, t) {
+ t.Error("Info failed")
+ }
+}
+
+// Test that a Warning log goes to Info.
+// Even in the Info log, the source character will be W, so the data should
+// all be identical.
+func TestWarning(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ Warning("test")
+ if !contains(warningLog, "W", t) {
+ t.Errorf("Warning has wrong character: %q", contents(warningLog))
+ }
+ if !contains(warningLog, "test", t) {
+ t.Error("Warning failed")
+ }
+ str := contents(warningLog)
+ if !contains(infoLog, str, t) {
+ t.Error("Info failed")
+ }
+}
+
+// Test that a V log goes to Info.
+func TestV(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ logging.verbosity.Set("2")
+ defer logging.verbosity.Set("0")
+ V(2).Info("test")
+ if !contains(infoLog, "I", t) {
+ t.Errorf("Info has wrong character: %q", contents(infoLog))
+ }
+ if !contains(infoLog, "test", t) {
+ t.Error("Info failed")
+ }
+}
+
+// Test that a vmodule enables a log in this file.
+func TestVmoduleOn(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ logging.vmodule.Set("glog_test=2")
+ defer logging.vmodule.Set("")
+ if !V(1) {
+ t.Error("V not enabled for 1")
+ }
+ if !V(2) {
+ t.Error("V not enabled for 2")
+ }
+ if V(3) {
+ t.Error("V enabled for 3")
+ }
+ V(2).Info("test")
+ if !contains(infoLog, "I", t) {
+ t.Errorf("Info has wrong character: %q", contents(infoLog))
+ }
+ if !contains(infoLog, "test", t) {
+ t.Error("Info failed")
+ }
+}
+
+// Test that a vmodule of another file does not enable a log in this file.
+func TestVmoduleOff(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ logging.vmodule.Set("notthisfile=2")
+ defer logging.vmodule.Set("")
+ for i := 1; i <= 3; i++ {
+ if V(Level(i)) {
+ t.Errorf("V enabled for %d", i)
+ }
+ }
+ V(2).Info("test")
+ if contents(infoLog) != "" {
+ t.Error("V logged incorrectly")
+ }
+}
+
+// vGlobs are patterns that match/don't match this file at V=2.
+var vGlobs = map[string]bool{
+ // Easy to test the numeric match here.
+ "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
+ "glog_test=2": true,
+ "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
+ // These all use 2 and check the patterns. All are true.
+ "*=2": true,
+ "?l*=2": true,
+ "????_*=2": true,
+ "??[mno]?_*t=2": true,
+ // These all use 2 and check the patterns. All are false.
+ "*x=2": false,
+ "m*=2": false,
+ "??_*=2": false,
+ "?[abc]?_*t=2": false,
+}
+
+// Test that vmodule globbing works as advertised.
+func testVmoduleGlob(pat string, match bool, t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ defer logging.vmodule.Set("")
+ logging.vmodule.Set(pat)
+ if V(2) != Verbose(match) {
+ t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
+ }
+}
+
+// Test that a vmodule globbing works as advertised.
+func TestVmoduleGlob(t *testing.T) {
+ for glob, match := range vGlobs {
+ testVmoduleGlob(glob, match, t)
+ }
+}
+
+func TestRollover(t *testing.T) {
+ setFlags()
+ var err error
+ defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
+ logExitFunc = func(e error) {
+ err = e
+ }
+ defer func(previous uint64) { MaxSize = previous }(MaxSize)
+ MaxSize = 512
+
+ Info("x") // Be sure we have a file.
+ info, ok := logging.file[infoLog].(*syncBuffer)
+ if !ok {
+ t.Fatal("info wasn't created")
+ }
+ if err != nil {
+ t.Fatalf("info has initial error: %v", err)
+ }
+ fname0 := info.file.Name()
+ Info(strings.Repeat("x", int(MaxSize))) // force a rollover
+ if err != nil {
+ t.Fatalf("info has error after big write: %v", err)
+ }
+
+ // Make sure the next log file gets a file name with a different
+ // time stamp.
+ //
+ // TODO: determine whether we need to support subsecond log
+ // rotation. C++ does not appear to handle this case (nor does it
+ // handle Daylight Savings Time properly).
+ time.Sleep(1 * time.Second)
+
+ Info("x") // create a new file
+ if err != nil {
+ t.Fatalf("error after rotation: %v", err)
+ }
+ fname1 := info.file.Name()
+ if fname0 == fname1 {
+ t.Errorf("info.f.Name did not change: %v", fname0)
+ }
+ if info.nbytes >= MaxSize {
+ t.Errorf("file size was not reset: %d", info.nbytes)
+ }
+}
+
+func TestLogBacktraceAt(t *testing.T) {
+ setFlags()
+ defer logging.swap(logging.newBuffers())
+ // The peculiar style of this code simplifies line counting and maintenance of the
+ // tracing block below.
+ var infoLine string
+ setTraceLocation := func(file string, line int, ok bool, delta int) {
+ if !ok {
+ t.Fatal("could not get file:line")
+ }
+ _, file = filepath.Split(file)
+ infoLine = fmt.Sprintf("%s:%d", file, line+delta)
+ err := logging.traceLocation.Set(infoLine)
+ if err != nil {
+ t.Fatal("error setting log_backtrace_at: ", err)
+ }
+ }
+ {
+ // Start of tracing block. These lines know about each other's relative position.
+ _, file, line, ok := runtime.Caller(0)
+ setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
+ Info("we want a stack trace here")
+ }
+ numAppearances := strings.Count(contents(infoLog), infoLine)
+ if numAppearances < 2 {
+ // Need 2 appearances, one in the log header and one in the trace:
+ // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
+ // ...
+ // github.com/glog/glog_test.go:280 (0x41ba91)
+ // ...
+ // We could be more precise but that would require knowing the details
+ // of the traceback format, which may not be dependable.
+ t.Fatal("got no trace back; log is ", contents(infoLog))
+ }
+}
+
+func BenchmarkHeader(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ buf, _, _ := logging.header(infoLog, 0)
+ logging.putBuffer(buf)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/Makefile
new file mode 100644
index 000000000..fb838ed2d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/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
+ make -C proto3_proto
+ make
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go
new file mode 100644
index 000000000..468e492be
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/all_test.go
@@ -0,0 +1,2059 @@
+// 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_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "math/rand"
+ "reflect"
+ "runtime/debug"
+ "strings"
+ "testing"
+ "time"
+
+ . "./testdata"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+var globalO *Buffer
+
+func old() *Buffer {
+ if globalO == nil {
+ globalO = NewBuffer(nil)
+ }
+ globalO.Reset()
+ return globalO
+}
+
+func equalbytes(b1, b2 []byte, t *testing.T) {
+ if len(b1) != len(b2) {
+ t.Errorf("wrong lengths: 2*%d != %d", len(b1), len(b2))
+ return
+ }
+ for i := 0; i < len(b1); i++ {
+ if b1[i] != b2[i] {
+ t.Errorf("bad byte[%d]:%x %x: %s %s", i, b1[i], b2[i], b1, b2)
+ }
+ }
+}
+
+func initGoTestField() *GoTestField {
+ f := new(GoTestField)
+ f.Label = String("label")
+ f.Type = String("type")
+ return f
+}
+
+// These are all structurally equivalent but the tag numbers differ.
+// (It's remarkable that required, optional, and repeated all have
+// 8 letters.)
+func initGoTest_RequiredGroup() *GoTest_RequiredGroup {
+ return &GoTest_RequiredGroup{
+ RequiredField: String("required"),
+ }
+}
+
+func initGoTest_OptionalGroup() *GoTest_OptionalGroup {
+ return &GoTest_OptionalGroup{
+ RequiredField: String("optional"),
+ }
+}
+
+func initGoTest_RepeatedGroup() *GoTest_RepeatedGroup {
+ return &GoTest_RepeatedGroup{
+ RequiredField: String("repeated"),
+ }
+}
+
+func initGoTest(setdefaults bool) *GoTest {
+ pb := new(GoTest)
+ if setdefaults {
+ pb.F_BoolDefaulted = Bool(Default_GoTest_F_BoolDefaulted)
+ pb.F_Int32Defaulted = Int32(Default_GoTest_F_Int32Defaulted)
+ pb.F_Int64Defaulted = Int64(Default_GoTest_F_Int64Defaulted)
+ pb.F_Fixed32Defaulted = Uint32(Default_GoTest_F_Fixed32Defaulted)
+ pb.F_Fixed64Defaulted = Uint64(Default_GoTest_F_Fixed64Defaulted)
+ pb.F_Uint32Defaulted = Uint32(Default_GoTest_F_Uint32Defaulted)
+ pb.F_Uint64Defaulted = Uint64(Default_GoTest_F_Uint64Defaulted)
+ pb.F_FloatDefaulted = Float32(Default_GoTest_F_FloatDefaulted)
+ pb.F_DoubleDefaulted = Float64(Default_GoTest_F_DoubleDefaulted)
+ pb.F_StringDefaulted = String(Default_GoTest_F_StringDefaulted)
+ pb.F_BytesDefaulted = Default_GoTest_F_BytesDefaulted
+ pb.F_Sint32Defaulted = Int32(Default_GoTest_F_Sint32Defaulted)
+ pb.F_Sint64Defaulted = Int64(Default_GoTest_F_Sint64Defaulted)
+ }
+
+ pb.Kind = GoTest_TIME.Enum()
+ pb.RequiredField = initGoTestField()
+ pb.F_BoolRequired = Bool(true)
+ pb.F_Int32Required = Int32(3)
+ pb.F_Int64Required = Int64(6)
+ pb.F_Fixed32Required = Uint32(32)
+ pb.F_Fixed64Required = Uint64(64)
+ pb.F_Uint32Required = Uint32(3232)
+ pb.F_Uint64Required = Uint64(6464)
+ pb.F_FloatRequired = Float32(3232)
+ pb.F_DoubleRequired = Float64(6464)
+ pb.F_StringRequired = String("string")
+ pb.F_BytesRequired = []byte("bytes")
+ pb.F_Sint32Required = Int32(-32)
+ pb.F_Sint64Required = Int64(-64)
+ pb.Requiredgroup = initGoTest_RequiredGroup()
+
+ return pb
+}
+
+func fail(msg string, b *bytes.Buffer, s string, t *testing.T) {
+ data := b.Bytes()
+ ld := len(data)
+ ls := len(s) / 2
+
+ fmt.Printf("fail %s ld=%d ls=%d\n", msg, ld, ls)
+
+ // find the interesting spot - n
+ n := ls
+ if ld < ls {
+ n = ld
+ }
+ j := 0
+ for i := 0; i < n; i++ {
+ bs := hex(s[j])*16 + hex(s[j+1])
+ j += 2
+ if data[i] == bs {
+ continue
+ }
+ n = i
+ break
+ }
+ l := n - 10
+ if l < 0 {
+ l = 0
+ }
+ h := n + 10
+
+ // find the interesting spot - n
+ fmt.Printf("is[%d]:", l)
+ for i := l; i < h; i++ {
+ if i >= ld {
+ fmt.Printf(" --")
+ continue
+ }
+ fmt.Printf(" %.2x", data[i])
+ }
+ fmt.Printf("\n")
+
+ fmt.Printf("sb[%d]:", l)
+ for i := l; i < h; i++ {
+ if i >= ls {
+ fmt.Printf(" --")
+ continue
+ }
+ bs := hex(s[j])*16 + hex(s[j+1])
+ j += 2
+ fmt.Printf(" %.2x", bs)
+ }
+ fmt.Printf("\n")
+
+ t.Fail()
+
+ // t.Errorf("%s: \ngood: %s\nbad: %x", msg, s, b.Bytes())
+ // Print the output in a partially-decoded format; can
+ // be helpful when updating the test. It produces the output
+ // that is pasted, with minor edits, into the argument to verify().
+ // data := b.Bytes()
+ // nesting := 0
+ // for b.Len() > 0 {
+ // start := len(data) - b.Len()
+ // var u uint64
+ // u, err := DecodeVarint(b)
+ // if err != nil {
+ // fmt.Printf("decode error on varint:", err)
+ // return
+ // }
+ // wire := u & 0x7
+ // tag := u >> 3
+ // switch wire {
+ // case WireVarint:
+ // v, err := DecodeVarint(b)
+ // if err != nil {
+ // fmt.Printf("decode error on varint:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n",
+ // data[start:len(data)-b.Len()], tag, wire, v)
+ // case WireFixed32:
+ // v, err := DecodeFixed32(b)
+ // if err != nil {
+ // fmt.Printf("decode error on fixed32:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n",
+ // data[start:len(data)-b.Len()], tag, wire, v)
+ // case WireFixed64:
+ // v, err := DecodeFixed64(b)
+ // if err != nil {
+ // fmt.Printf("decode error on fixed64:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n",
+ // data[start:len(data)-b.Len()], tag, wire, v)
+ // case WireBytes:
+ // nb, err := DecodeVarint(b)
+ // if err != nil {
+ // fmt.Printf("decode error on bytes:", err)
+ // return
+ // }
+ // after_tag := len(data) - b.Len()
+ // str := make([]byte, nb)
+ // _, err = b.Read(str)
+ // if err != nil {
+ // fmt.Printf("decode error on bytes:", err)
+ // return
+ // }
+ // fmt.Printf("\t\t\"%x\" \"%x\" // field %d, encoding %d (FIELD)\n",
+ // data[start:after_tag], str, tag, wire)
+ // case WireStartGroup:
+ // nesting++
+ // fmt.Printf("\t\t\"%x\"\t\t// start group field %d level %d\n",
+ // data[start:len(data)-b.Len()], tag, nesting)
+ // case WireEndGroup:
+ // fmt.Printf("\t\t\"%x\"\t\t// end group field %d level %d\n",
+ // data[start:len(data)-b.Len()], tag, nesting)
+ // nesting--
+ // default:
+ // fmt.Printf("unrecognized wire type %d\n", wire)
+ // return
+ // }
+ // }
+}
+
+func hex(c uint8) uint8 {
+ if '0' <= c && c <= '9' {
+ return c - '0'
+ }
+ if 'a' <= c && c <= 'f' {
+ return 10 + c - 'a'
+ }
+ if 'A' <= c && c <= 'F' {
+ return 10 + c - 'A'
+ }
+ return 0
+}
+
+func equal(b []byte, s string, t *testing.T) bool {
+ if 2*len(b) != len(s) {
+ // fail(fmt.Sprintf("wrong lengths: 2*%d != %d", len(b), len(s)), b, s, t)
+ fmt.Printf("wrong lengths: 2*%d != %d\n", len(b), len(s))
+ return false
+ }
+ for i, j := 0, 0; i < len(b); i, j = i+1, j+2 {
+ x := hex(s[j])*16 + hex(s[j+1])
+ if b[i] != x {
+ // fail(fmt.Sprintf("bad byte[%d]:%x %x", i, b[i], x), b, s, t)
+ fmt.Printf("bad byte[%d]:%x %x", i, b[i], x)
+ return false
+ }
+ }
+ return true
+}
+
+func overify(t *testing.T, pb *GoTest, expected string) {
+ o := old()
+ err := o.Marshal(pb)
+ if err != nil {
+ fmt.Printf("overify marshal-1 err = %v", err)
+ o.DebugPrint("", o.Bytes())
+ t.Fatalf("expected = %s", expected)
+ }
+ if !equal(o.Bytes(), expected, t) {
+ o.DebugPrint("overify neq 1", o.Bytes())
+ t.Fatalf("expected = %s", expected)
+ }
+
+ // Now test Unmarshal by recreating the original buffer.
+ pbd := new(GoTest)
+ err = o.Unmarshal(pbd)
+ if err != nil {
+ t.Fatalf("overify unmarshal err = %v", err)
+ o.DebugPrint("", o.Bytes())
+ t.Fatalf("string = %s", expected)
+ }
+ o.Reset()
+ err = o.Marshal(pbd)
+ if err != nil {
+ t.Errorf("overify marshal-2 err = %v", err)
+ o.DebugPrint("", o.Bytes())
+ t.Fatalf("string = %s", expected)
+ }
+ if !equal(o.Bytes(), expected, t) {
+ o.DebugPrint("overify neq 2", o.Bytes())
+ t.Fatalf("string = %s", expected)
+ }
+}
+
+// Simple tests for numeric encode/decode primitives (varint, etc.)
+func TestNumericPrimitives(t *testing.T) {
+ for i := uint64(0); i < 1e6; i += 111 {
+ o := old()
+ if o.EncodeVarint(i) != nil {
+ t.Error("EncodeVarint")
+ break
+ }
+ x, e := o.DecodeVarint()
+ if e != nil {
+ t.Fatal("DecodeVarint")
+ }
+ if x != i {
+ t.Fatal("varint decode fail:", i, x)
+ }
+
+ o = old()
+ if o.EncodeFixed32(i) != nil {
+ t.Fatal("encFixed32")
+ }
+ x, e = o.DecodeFixed32()
+ if e != nil {
+ t.Fatal("decFixed32")
+ }
+ if x != i {
+ t.Fatal("fixed32 decode fail:", i, x)
+ }
+
+ o = old()
+ if o.EncodeFixed64(i*1234567) != nil {
+ t.Error("encFixed64")
+ break
+ }
+ x, e = o.DecodeFixed64()
+ if e != nil {
+ t.Error("decFixed64")
+ break
+ }
+ if x != i*1234567 {
+ t.Error("fixed64 decode fail:", i*1234567, x)
+ break
+ }
+
+ o = old()
+ i32 := int32(i - 12345)
+ if o.EncodeZigzag32(uint64(i32)) != nil {
+ t.Fatal("EncodeZigzag32")
+ }
+ x, e = o.DecodeZigzag32()
+ if e != nil {
+ t.Fatal("DecodeZigzag32")
+ }
+ if x != uint64(uint32(i32)) {
+ t.Fatal("zigzag32 decode fail:", i32, x)
+ }
+
+ o = old()
+ i64 := int64(i - 12345)
+ if o.EncodeZigzag64(uint64(i64)) != nil {
+ t.Fatal("EncodeZigzag64")
+ }
+ x, e = o.DecodeZigzag64()
+ if e != nil {
+ t.Fatal("DecodeZigzag64")
+ }
+ if x != uint64(i64) {
+ t.Fatal("zigzag64 decode fail:", i64, x)
+ }
+ }
+}
+
+// fakeMarshaler is a simple struct implementing Marshaler and Message interfaces.
+type fakeMarshaler struct {
+ b []byte
+ err error
+}
+
+func (f fakeMarshaler) Marshal() ([]byte, error) {
+ return f.b, f.err
+}
+
+func (f fakeMarshaler) String() string {
+ return fmt.Sprintf("Bytes: %v Error: %v", f.b, f.err)
+}
+
+func (f fakeMarshaler) ProtoMessage() {}
+
+func (f fakeMarshaler) Reset() {}
+
+// Simple tests for proto messages that implement the Marshaler interface.
+func TestMarshalerEncoding(t *testing.T) {
+ tests := []struct {
+ name string
+ m Message
+ want []byte
+ wantErr error
+ }{
+ {
+ name: "Marshaler that fails",
+ m: fakeMarshaler{
+ err: errors.New("some marshal err"),
+ b: []byte{5, 6, 7},
+ },
+ // Since there's an error, nothing should be written to buffer.
+ want: nil,
+ wantErr: errors.New("some marshal err"),
+ },
+ {
+ name: "Marshaler that succeeds",
+ m: fakeMarshaler{
+ b: []byte{0, 1, 2, 3, 4, 127, 255},
+ },
+ want: []byte{0, 1, 2, 3, 4, 127, 255},
+ wantErr: nil,
+ },
+ }
+ for _, test := range tests {
+ b := NewBuffer(nil)
+ err := b.Marshal(test.m)
+ if !reflect.DeepEqual(test.wantErr, err) {
+ t.Errorf("%s: got err %v wanted %v", test.name, err, test.wantErr)
+ }
+ if !reflect.DeepEqual(test.want, b.Bytes()) {
+ t.Errorf("%s: got bytes %v wanted %v", test.name, b.Bytes(), test.want)
+ }
+ }
+}
+
+// Simple tests for bytes
+func TestBytesPrimitives(t *testing.T) {
+ o := old()
+ bytes := []byte{'n', 'o', 'w', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 't', 'i', 'm', 'e'}
+ if o.EncodeRawBytes(bytes) != nil {
+ t.Error("EncodeRawBytes")
+ }
+ decb, e := o.DecodeRawBytes(false)
+ if e != nil {
+ t.Error("DecodeRawBytes")
+ }
+ equalbytes(bytes, decb, t)
+}
+
+// Simple tests for strings
+func TestStringPrimitives(t *testing.T) {
+ o := old()
+ s := "now is the time"
+ if o.EncodeStringBytes(s) != nil {
+ t.Error("enc_string")
+ }
+ decs, e := o.DecodeStringBytes()
+ if e != nil {
+ t.Error("dec_string")
+ }
+ if s != decs {
+ t.Error("string encode/decode fail:", s, decs)
+ }
+}
+
+// Do we catch the "required bit not set" case?
+func TestRequiredBit(t *testing.T) {
+ o := old()
+ pb := new(GoTest)
+ err := o.Marshal(pb)
+ if err == nil {
+ t.Error("did not catch missing required fields")
+ } else if strings.Index(err.Error(), "Kind") < 0 {
+ t.Error("wrong error type:", err)
+ }
+}
+
+// Check that all fields are nil.
+// Clearly silly, and a residue from a more interesting test with an earlier,
+// different initialization property, but it once caught a compiler bug so
+// it lives.
+func checkInitialized(pb *GoTest, t *testing.T) {
+ if pb.F_BoolDefaulted != nil {
+ t.Error("New or Reset did not set boolean:", *pb.F_BoolDefaulted)
+ }
+ if pb.F_Int32Defaulted != nil {
+ t.Error("New or Reset did not set int32:", *pb.F_Int32Defaulted)
+ }
+ if pb.F_Int64Defaulted != nil {
+ t.Error("New or Reset did not set int64:", *pb.F_Int64Defaulted)
+ }
+ if pb.F_Fixed32Defaulted != nil {
+ t.Error("New or Reset did not set fixed32:", *pb.F_Fixed32Defaulted)
+ }
+ if pb.F_Fixed64Defaulted != nil {
+ t.Error("New or Reset did not set fixed64:", *pb.F_Fixed64Defaulted)
+ }
+ if pb.F_Uint32Defaulted != nil {
+ t.Error("New or Reset did not set uint32:", *pb.F_Uint32Defaulted)
+ }
+ if pb.F_Uint64Defaulted != nil {
+ t.Error("New or Reset did not set uint64:", *pb.F_Uint64Defaulted)
+ }
+ if pb.F_FloatDefaulted != nil {
+ t.Error("New or Reset did not set float:", *pb.F_FloatDefaulted)
+ }
+ if pb.F_DoubleDefaulted != nil {
+ t.Error("New or Reset did not set double:", *pb.F_DoubleDefaulted)
+ }
+ if pb.F_StringDefaulted != nil {
+ t.Error("New or Reset did not set string:", *pb.F_StringDefaulted)
+ }
+ if pb.F_BytesDefaulted != nil {
+ t.Error("New or Reset did not set bytes:", string(pb.F_BytesDefaulted))
+ }
+ if pb.F_Sint32Defaulted != nil {
+ t.Error("New or Reset did not set int32:", *pb.F_Sint32Defaulted)
+ }
+ if pb.F_Sint64Defaulted != nil {
+ t.Error("New or Reset did not set int64:", *pb.F_Sint64Defaulted)
+ }
+}
+
+// Does Reset() reset?
+func TestReset(t *testing.T) {
+ pb := initGoTest(true)
+ // muck with some values
+ pb.F_BoolDefaulted = Bool(false)
+ pb.F_Int32Defaulted = Int32(237)
+ pb.F_Int64Defaulted = Int64(12346)
+ pb.F_Fixed32Defaulted = Uint32(32000)
+ pb.F_Fixed64Defaulted = Uint64(666)
+ pb.F_Uint32Defaulted = Uint32(323232)
+ pb.F_Uint64Defaulted = nil
+ pb.F_FloatDefaulted = nil
+ pb.F_DoubleDefaulted = Float64(0)
+ pb.F_StringDefaulted = String("gotcha")
+ pb.F_BytesDefaulted = []byte("asdfasdf")
+ pb.F_Sint32Defaulted = Int32(123)
+ pb.F_Sint64Defaulted = Int64(789)
+ pb.Reset()
+ checkInitialized(pb, t)
+}
+
+// All required fields set, no defaults provided.
+func TestEncodeDecode1(t *testing.T) {
+ pb := initGoTest(false)
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 0x20
+ "714000000000000000"+ // field 14, encoding 1, value 0x40
+ "78a019"+ // field 15, encoding 0, value 0xca0 = 3232
+ "8001c032"+ // field 16, encoding 0, value 0x1940 = 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2, string "string"
+ "b304"+ // field 70, encoding 3, start group
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // field 70, encoding 4, end group
+ "aa0605"+"6279746573"+ // field 101, encoding 2, string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f") // field 103, encoding 0, 0x7f zigzag64
+}
+
+// All required fields set, defaults provided.
+func TestEncodeDecode2(t *testing.T) {
+ pb := initGoTest(true)
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All default fields set to their default value by hand
+func TestEncodeDecode3(t *testing.T) {
+ pb := initGoTest(false)
+ pb.F_BoolDefaulted = Bool(true)
+ pb.F_Int32Defaulted = Int32(32)
+ pb.F_Int64Defaulted = Int64(64)
+ pb.F_Fixed32Defaulted = Uint32(320)
+ pb.F_Fixed64Defaulted = Uint64(640)
+ pb.F_Uint32Defaulted = Uint32(3200)
+ pb.F_Uint64Defaulted = Uint64(6400)
+ pb.F_FloatDefaulted = Float32(314159)
+ pb.F_DoubleDefaulted = Float64(271828)
+ pb.F_StringDefaulted = String("hello, \"world!\"\n")
+ pb.F_BytesDefaulted = []byte("Bignose")
+ pb.F_Sint32Defaulted = Int32(-32)
+ pb.F_Sint64Defaulted = Int64(-64)
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All required fields set, defaults provided, all non-defaulted optional fields have values.
+func TestEncodeDecode4(t *testing.T) {
+ pb := initGoTest(true)
+ pb.Table = String("hello")
+ pb.Param = Int32(7)
+ pb.OptionalField = initGoTestField()
+ pb.F_BoolOptional = Bool(true)
+ pb.F_Int32Optional = Int32(32)
+ pb.F_Int64Optional = Int64(64)
+ pb.F_Fixed32Optional = Uint32(3232)
+ pb.F_Fixed64Optional = Uint64(6464)
+ pb.F_Uint32Optional = Uint32(323232)
+ pb.F_Uint64Optional = Uint64(646464)
+ pb.F_FloatOptional = Float32(32.)
+ pb.F_DoubleOptional = Float64(64.)
+ pb.F_StringOptional = String("hello")
+ pb.F_BytesOptional = []byte("Bignose")
+ pb.F_Sint32Optional = Int32(-32)
+ pb.F_Sint64Optional = Int64(-64)
+ pb.Optionalgroup = initGoTest_OptionalGroup()
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "1205"+"68656c6c6f"+ // field 2, encoding 2, string "hello"
+ "1807"+ // field 3, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "320d"+"0a056c6162656c120474797065"+ // field 6, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "f00101"+ // field 30, encoding 0, value 1
+ "f80120"+ // field 31, encoding 0, value 32
+ "800240"+ // field 32, encoding 0, value 64
+ "8d02a00c0000"+ // field 33, encoding 5, value 3232
+ "91024019000000000000"+ // field 34, encoding 1, value 6464
+ "9802a0dd13"+ // field 35, encoding 0, value 323232
+ "a002c0ba27"+ // field 36, encoding 0, value 646464
+ "ad0200000042"+ // field 37, encoding 5, value 32.0
+ "b1020000000000005040"+ // field 38, encoding 1, value 64.0
+ "ba0205"+"68656c6c6f"+ // field 39, encoding 2, string "hello"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "d305"+ // start group field 90 level 1
+ "da0508"+"6f7074696f6e616c"+ // field 91, encoding 2, string "optional"
+ "d405"+ // end group field 90 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "ea1207"+"4269676e6f7365"+ // field 301, encoding 2, string "Bignose"
+ "f0123f"+ // field 302, encoding 0, value 63
+ "f8127f"+ // field 303, encoding 0, value 127
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All required fields set, defaults provided, all repeated fields given two values.
+func TestEncodeDecode5(t *testing.T) {
+ pb := initGoTest(true)
+ pb.RepeatedField = []*GoTestField{initGoTestField(), initGoTestField()}
+ pb.F_BoolRepeated = []bool{false, true}
+ pb.F_Int32Repeated = []int32{32, 33}
+ pb.F_Int64Repeated = []int64{64, 65}
+ pb.F_Fixed32Repeated = []uint32{3232, 3333}
+ pb.F_Fixed64Repeated = []uint64{6464, 6565}
+ pb.F_Uint32Repeated = []uint32{323232, 333333}
+ pb.F_Uint64Repeated = []uint64{646464, 656565}
+ pb.F_FloatRepeated = []float32{32., 33.}
+ pb.F_DoubleRepeated = []float64{64., 65.}
+ pb.F_StringRepeated = []string{"hello", "sailor"}
+ pb.F_BytesRepeated = [][]byte{[]byte("big"), []byte("nose")}
+ pb.F_Sint32Repeated = []int32{32, -32}
+ pb.F_Sint64Repeated = []int64{64, -64}
+ pb.Repeatedgroup = []*GoTest_RepeatedGroup{initGoTest_RepeatedGroup(), initGoTest_RepeatedGroup()}
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField)
+ "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "a00100"+ // field 20, encoding 0, value 0
+ "a00101"+ // field 20, encoding 0, value 1
+ "a80120"+ // field 21, encoding 0, value 32
+ "a80121"+ // field 21, encoding 0, value 33
+ "b00140"+ // field 22, encoding 0, value 64
+ "b00141"+ // field 22, encoding 0, value 65
+ "bd01a00c0000"+ // field 23, encoding 5, value 3232
+ "bd01050d0000"+ // field 23, encoding 5, value 3333
+ "c1014019000000000000"+ // field 24, encoding 1, value 6464
+ "c101a519000000000000"+ // field 24, encoding 1, value 6565
+ "c801a0dd13"+ // field 25, encoding 0, value 323232
+ "c80195ac14"+ // field 25, encoding 0, value 333333
+ "d001c0ba27"+ // field 26, encoding 0, value 646464
+ "d001b58928"+ // field 26, encoding 0, value 656565
+ "dd0100000042"+ // field 27, encoding 5, value 32.0
+ "dd0100000442"+ // field 27, encoding 5, value 33.0
+ "e1010000000000005040"+ // field 28, encoding 1, value 64.0
+ "e1010000000000405040"+ // field 28, encoding 1, value 65.0
+ "ea0105"+"68656c6c6f"+ // field 29, encoding 2, string "hello"
+ "ea0106"+"7361696c6f72"+ // field 29, encoding 2, string "sailor"
+ "c00201"+ // field 40, encoding 0, value 1
+ "c80220"+ // field 41, encoding 0, value 32
+ "d00240"+ // field 42, encoding 0, value 64
+ "dd0240010000"+ // field 43, encoding 5, value 320
+ "e1028002000000000000"+ // field 44, encoding 1, value 640
+ "e8028019"+ // field 45, encoding 0, value 3200
+ "f0028032"+ // field 46, encoding 0, value 6400
+ "fd02e0659948"+ // field 47, encoding 5, value 314159.0
+ "81030000000050971041"+ // field 48, encoding 1, value 271828.0
+ "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n"
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "8305"+ // start group field 80 level 1
+ "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated"
+ "8405"+ // end group field 80 level 1
+ "8305"+ // start group field 80 level 1
+ "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated"
+ "8405"+ // end group field 80 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "ca0c03"+"626967"+ // field 201, encoding 2, string "big"
+ "ca0c04"+"6e6f7365"+ // field 201, encoding 2, string "nose"
+ "d00c40"+ // field 202, encoding 0, value 32
+ "d00c3f"+ // field 202, encoding 0, value -32
+ "d80c8001"+ // field 203, encoding 0, value 64
+ "d80c7f"+ // field 203, encoding 0, value -64
+ "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose"
+ "90193f"+ // field 402, encoding 0, value 63
+ "98197f") // field 403, encoding 0, value 127
+
+}
+
+// All required fields set, all packed repeated fields given two values.
+func TestEncodeDecode6(t *testing.T) {
+ pb := initGoTest(false)
+ pb.F_BoolRepeatedPacked = []bool{false, true}
+ pb.F_Int32RepeatedPacked = []int32{32, 33}
+ pb.F_Int64RepeatedPacked = []int64{64, 65}
+ pb.F_Fixed32RepeatedPacked = []uint32{3232, 3333}
+ pb.F_Fixed64RepeatedPacked = []uint64{6464, 6565}
+ pb.F_Uint32RepeatedPacked = []uint32{323232, 333333}
+ pb.F_Uint64RepeatedPacked = []uint64{646464, 656565}
+ pb.F_FloatRepeatedPacked = []float32{32., 33.}
+ pb.F_DoubleRepeatedPacked = []float64{64., 65.}
+ pb.F_Sint32RepeatedPacked = []int32{32, -32}
+ pb.F_Sint64RepeatedPacked = []int64{64, -64}
+
+ overify(t, pb,
+ "0807"+ // field 1, encoding 0, value 7
+ "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField)
+ "5001"+ // field 10, encoding 0, value 1
+ "5803"+ // field 11, encoding 0, value 3
+ "6006"+ // field 12, encoding 0, value 6
+ "6d20000000"+ // field 13, encoding 5, value 32
+ "714000000000000000"+ // field 14, encoding 1, value 64
+ "78a019"+ // field 15, encoding 0, value 3232
+ "8001c032"+ // field 16, encoding 0, value 6464
+ "8d0100004a45"+ // field 17, encoding 5, value 3232.0
+ "9101000000000040b940"+ // field 18, encoding 1, value 6464.0
+ "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string"
+ "9203020001"+ // field 50, encoding 2, 2 bytes, value 0, value 1
+ "9a03022021"+ // field 51, encoding 2, 2 bytes, value 32, value 33
+ "a203024041"+ // field 52, encoding 2, 2 bytes, value 64, value 65
+ "aa0308"+ // field 53, encoding 2, 8 bytes
+ "a00c0000050d0000"+ // value 3232, value 3333
+ "b20310"+ // field 54, encoding 2, 16 bytes
+ "4019000000000000a519000000000000"+ // value 6464, value 6565
+ "ba0306"+ // field 55, encoding 2, 6 bytes
+ "a0dd1395ac14"+ // value 323232, value 333333
+ "c20306"+ // field 56, encoding 2, 6 bytes
+ "c0ba27b58928"+ // value 646464, value 656565
+ "ca0308"+ // field 57, encoding 2, 8 bytes
+ "0000004200000442"+ // value 32.0, value 33.0
+ "d20310"+ // field 58, encoding 2, 16 bytes
+ "00000000000050400000000000405040"+ // value 64.0, value 65.0
+ "b304"+ // start group field 70 level 1
+ "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required"
+ "b404"+ // end group field 70 level 1
+ "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes"
+ "b0063f"+ // field 102, encoding 0, 0x3f zigzag32
+ "b8067f"+ // field 103, encoding 0, 0x7f zigzag64
+ "b21f02"+ // field 502, encoding 2, 2 bytes
+ "403f"+ // value 32, value -32
+ "ba1f03"+ // field 503, encoding 2, 3 bytes
+ "80017f") // value 64, value -64
+}
+
+// Test that we can encode empty bytes fields.
+func TestEncodeDecodeBytes1(t *testing.T) {
+ pb := initGoTest(false)
+
+ // Create our bytes
+ pb.F_BytesRequired = []byte{}
+ pb.F_BytesRepeated = [][]byte{{}}
+ pb.F_BytesOptional = []byte{}
+
+ d, err := Marshal(pb)
+ if err != nil {
+ t.Error(err)
+ }
+
+ pbd := new(GoTest)
+ if err := Unmarshal(d, pbd); err != nil {
+ t.Error(err)
+ }
+
+ if pbd.F_BytesRequired == nil || len(pbd.F_BytesRequired) != 0 {
+ t.Error("required empty bytes field is incorrect")
+ }
+ if pbd.F_BytesRepeated == nil || len(pbd.F_BytesRepeated) == 1 && pbd.F_BytesRepeated[0] == nil {
+ t.Error("repeated empty bytes field is incorrect")
+ }
+ if pbd.F_BytesOptional == nil || len(pbd.F_BytesOptional) != 0 {
+ t.Error("optional empty bytes field is incorrect")
+ }
+}
+
+// Test that we encode nil-valued fields of a repeated bytes field correctly.
+// Since entries in a repeated field cannot be nil, nil must mean empty value.
+func TestEncodeDecodeBytes2(t *testing.T) {
+ pb := initGoTest(false)
+
+ // Create our bytes
+ pb.F_BytesRepeated = [][]byte{nil}
+
+ d, err := Marshal(pb)
+ if err != nil {
+ t.Error(err)
+ }
+
+ pbd := new(GoTest)
+ if err := Unmarshal(d, pbd); err != nil {
+ t.Error(err)
+ }
+
+ if len(pbd.F_BytesRepeated) != 1 || pbd.F_BytesRepeated[0] == nil {
+ t.Error("Unexpected value for repeated bytes field")
+ }
+}
+
+// All required fields set, defaults provided, all repeated fields given two values.
+func TestSkippingUnrecognizedFields(t *testing.T) {
+ o := old()
+ pb := initGoTestField()
+
+ // Marshal it normally.
+ o.Marshal(pb)
+
+ // Now new a GoSkipTest record.
+ skip := &GoSkipTest{
+ SkipInt32: Int32(32),
+ SkipFixed32: Uint32(3232),
+ SkipFixed64: Uint64(6464),
+ SkipString: String("skipper"),
+ Skipgroup: &GoSkipTest_SkipGroup{
+ GroupInt32: Int32(75),
+ GroupString: String("wxyz"),
+ },
+ }
+
+ // Marshal it into same buffer.
+ o.Marshal(skip)
+
+ pbd := new(GoTestField)
+ o.Unmarshal(pbd)
+
+ // The __unrecognized field should be a marshaling of GoSkipTest
+ skipd := new(GoSkipTest)
+
+ o.SetBuf(pbd.XXX_unrecognized)
+ o.Unmarshal(skipd)
+
+ if *skipd.SkipInt32 != *skip.SkipInt32 {
+ t.Error("skip int32", skipd.SkipInt32)
+ }
+ if *skipd.SkipFixed32 != *skip.SkipFixed32 {
+ t.Error("skip fixed32", skipd.SkipFixed32)
+ }
+ if *skipd.SkipFixed64 != *skip.SkipFixed64 {
+ t.Error("skip fixed64", skipd.SkipFixed64)
+ }
+ if *skipd.SkipString != *skip.SkipString {
+ t.Error("skip string", *skipd.SkipString)
+ }
+ if *skipd.Skipgroup.GroupInt32 != *skip.Skipgroup.GroupInt32 {
+ t.Error("skip group int32", skipd.Skipgroup.GroupInt32)
+ }
+ if *skipd.Skipgroup.GroupString != *skip.Skipgroup.GroupString {
+ t.Error("skip group string", *skipd.Skipgroup.GroupString)
+ }
+}
+
+// Check that unrecognized fields of a submessage are preserved.
+func TestSubmessageUnrecognizedFields(t *testing.T) {
+ nm := &NewMessage{
+ Nested: &NewMessage_Nested{
+ Name: String("Nigel"),
+ FoodGroup: String("carbs"),
+ },
+ }
+ b, err := Marshal(nm)
+ if err != nil {
+ t.Fatalf("Marshal of NewMessage: %v", err)
+ }
+
+ // Unmarshal into an OldMessage.
+ om := new(OldMessage)
+ if err := Unmarshal(b, om); err != nil {
+ t.Fatalf("Unmarshal to OldMessage: %v", err)
+ }
+ exp := &OldMessage{
+ Nested: &OldMessage_Nested{
+ Name: String("Nigel"),
+ // normal protocol buffer users should not do this
+ XXX_unrecognized: []byte("\x12\x05carbs"),
+ },
+ }
+ if !Equal(om, exp) {
+ t.Errorf("om = %v, want %v", om, exp)
+ }
+
+ // Clone the OldMessage.
+ om = Clone(om).(*OldMessage)
+ if !Equal(om, exp) {
+ t.Errorf("Clone(om) = %v, want %v", om, exp)
+ }
+
+ // Marshal the OldMessage, then unmarshal it into an empty NewMessage.
+ if b, err = Marshal(om); err != nil {
+ t.Fatalf("Marshal of OldMessage: %v", err)
+ }
+ t.Logf("Marshal(%v) -> %q", om, b)
+ nm2 := new(NewMessage)
+ if err := Unmarshal(b, nm2); err != nil {
+ t.Fatalf("Unmarshal to NewMessage: %v", err)
+ }
+ if !Equal(nm, nm2) {
+ t.Errorf("NewMessage round-trip: %v => %v", nm, nm2)
+ }
+}
+
+// Check that an int32 field can be upgraded to an int64 field.
+func TestNegativeInt32(t *testing.T) {
+ om := &OldMessage{
+ Num: Int32(-1),
+ }
+ b, err := Marshal(om)
+ if err != nil {
+ t.Fatalf("Marshal of OldMessage: %v", err)
+ }
+
+ // Check the size. It should be 11 bytes;
+ // 1 for the field/wire type, and 10 for the negative number.
+ if len(b) != 11 {
+ t.Errorf("%v marshaled as %q, wanted 11 bytes", om, b)
+ }
+
+ // Unmarshal into a NewMessage.
+ nm := new(NewMessage)
+ if err := Unmarshal(b, nm); err != nil {
+ t.Fatalf("Unmarshal to NewMessage: %v", err)
+ }
+ want := &NewMessage{
+ Num: Int64(-1),
+ }
+ if !Equal(nm, want) {
+ t.Errorf("nm = %v, want %v", nm, want)
+ }
+}
+
+// Check that we can grow an array (repeated field) to have many elements.
+// This test doesn't depend only on our encoding; for variety, it makes sure
+// we create, encode, and decode the correct contents explicitly. It's therefore
+// a bit messier.
+// This test also uses (and hence tests) the Marshal/Unmarshal functions
+// instead of the methods.
+func TestBigRepeated(t *testing.T) {
+ pb := initGoTest(true)
+
+ // Create the arrays
+ const N = 50 // Internally the library starts much smaller.
+ pb.Repeatedgroup = make([]*GoTest_RepeatedGroup, N)
+ pb.F_Sint64Repeated = make([]int64, N)
+ pb.F_Sint32Repeated = make([]int32, N)
+ pb.F_BytesRepeated = make([][]byte, N)
+ pb.F_StringRepeated = make([]string, N)
+ pb.F_DoubleRepeated = make([]float64, N)
+ pb.F_FloatRepeated = make([]float32, N)
+ pb.F_Uint64Repeated = make([]uint64, N)
+ pb.F_Uint32Repeated = make([]uint32, N)
+ pb.F_Fixed64Repeated = make([]uint64, N)
+ pb.F_Fixed32Repeated = make([]uint32, N)
+ pb.F_Int64Repeated = make([]int64, N)
+ pb.F_Int32Repeated = make([]int32, N)
+ pb.F_BoolRepeated = make([]bool, N)
+ pb.RepeatedField = make([]*GoTestField, N)
+
+ // Fill in the arrays with checkable values.
+ igtf := initGoTestField()
+ igtrg := initGoTest_RepeatedGroup()
+ for i := 0; i < N; i++ {
+ pb.Repeatedgroup[i] = igtrg
+ pb.F_Sint64Repeated[i] = int64(i)
+ pb.F_Sint32Repeated[i] = int32(i)
+ s := fmt.Sprint(i)
+ pb.F_BytesRepeated[i] = []byte(s)
+ pb.F_StringRepeated[i] = s
+ pb.F_DoubleRepeated[i] = float64(i)
+ pb.F_FloatRepeated[i] = float32(i)
+ pb.F_Uint64Repeated[i] = uint64(i)
+ pb.F_Uint32Repeated[i] = uint32(i)
+ pb.F_Fixed64Repeated[i] = uint64(i)
+ pb.F_Fixed32Repeated[i] = uint32(i)
+ pb.F_Int64Repeated[i] = int64(i)
+ pb.F_Int32Repeated[i] = int32(i)
+ pb.F_BoolRepeated[i] = i%2 == 0
+ pb.RepeatedField[i] = igtf
+ }
+
+ // Marshal.
+ buf, _ := Marshal(pb)
+
+ // Now test Unmarshal by recreating the original buffer.
+ pbd := new(GoTest)
+ Unmarshal(buf, pbd)
+
+ // Check the checkable values
+ for i := uint64(0); i < N; i++ {
+ if pbd.Repeatedgroup[i] == nil { // TODO: more checking?
+ t.Error("pbd.Repeatedgroup bad")
+ }
+ var x uint64
+ x = uint64(pbd.F_Sint64Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Sint64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Sint32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Sint32Repeated bad", x, i)
+ }
+ s := fmt.Sprint(i)
+ equalbytes(pbd.F_BytesRepeated[i], []byte(s), t)
+ if pbd.F_StringRepeated[i] != s {
+ t.Error("pbd.F_Sint32Repeated bad", pbd.F_StringRepeated[i], i)
+ }
+ x = uint64(pbd.F_DoubleRepeated[i])
+ if x != i {
+ t.Error("pbd.F_DoubleRepeated bad", x, i)
+ }
+ x = uint64(pbd.F_FloatRepeated[i])
+ if x != i {
+ t.Error("pbd.F_FloatRepeated bad", x, i)
+ }
+ x = pbd.F_Uint64Repeated[i]
+ if x != i {
+ t.Error("pbd.F_Uint64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Uint32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Uint32Repeated bad", x, i)
+ }
+ x = pbd.F_Fixed64Repeated[i]
+ if x != i {
+ t.Error("pbd.F_Fixed64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Fixed32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Fixed32Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Int64Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Int64Repeated bad", x, i)
+ }
+ x = uint64(pbd.F_Int32Repeated[i])
+ if x != i {
+ t.Error("pbd.F_Int32Repeated bad", x, i)
+ }
+ if pbd.F_BoolRepeated[i] != (i%2 == 0) {
+ t.Error("pbd.F_BoolRepeated bad", x, i)
+ }
+ if pbd.RepeatedField[i] == nil { // TODO: more checking?
+ t.Error("pbd.RepeatedField bad")
+ }
+ }
+}
+
+// Verify we give a useful message when decoding to the wrong structure type.
+func TestTypeMismatch(t *testing.T) {
+ pb1 := initGoTest(true)
+
+ // Marshal
+ o := old()
+ o.Marshal(pb1)
+
+ // Now Unmarshal it to the wrong type.
+ pb2 := initGoTestField()
+ err := o.Unmarshal(pb2)
+ if err == nil {
+ t.Error("expected error, got no error")
+ } else if !strings.Contains(err.Error(), "bad wiretype") {
+ t.Error("expected bad wiretype error, got", err)
+ }
+}
+
+func encodeDecode(t *testing.T, in, out Message, msg string) {
+ buf, err := Marshal(in)
+ if err != nil {
+ t.Fatalf("failed marshaling %v: %v", msg, err)
+ }
+ if err := Unmarshal(buf, out); err != nil {
+ t.Fatalf("failed unmarshaling %v: %v", msg, err)
+ }
+}
+
+func TestPackedNonPackedDecoderSwitching(t *testing.T) {
+ np, p := new(NonPackedTest), new(PackedTest)
+
+ // non-packed -> packed
+ np.A = []int32{0, 1, 1, 2, 3, 5}
+ encodeDecode(t, np, p, "non-packed -> packed")
+ if !reflect.DeepEqual(np.A, p.B) {
+ t.Errorf("failed non-packed -> packed; np.A=%+v, p.B=%+v", np.A, p.B)
+ }
+
+ // packed -> non-packed
+ np.Reset()
+ p.B = []int32{3, 1, 4, 1, 5, 9}
+ encodeDecode(t, p, np, "packed -> non-packed")
+ if !reflect.DeepEqual(p.B, np.A) {
+ t.Errorf("failed packed -> non-packed; p.B=%+v, np.A=%+v", p.B, np.A)
+ }
+}
+
+func TestProto1RepeatedGroup(t *testing.T) {
+ pb := &MessageList{
+ Message: []*MessageList_Message{
+ {
+ Name: String("blah"),
+ Count: Int32(7),
+ },
+ // NOTE: pb.Message[1] is a nil
+ nil,
+ },
+ }
+
+ o := old()
+ if err := o.Marshal(pb); err != ErrRepeatedHasNil {
+ t.Fatalf("unexpected or no error when marshaling: %v", err)
+ }
+}
+
+// Test that enums work. Checks for a bug introduced by making enums
+// named types instead of int32: newInt32FromUint64 would crash with
+// a type mismatch in reflect.PointTo.
+func TestEnum(t *testing.T) {
+ pb := new(GoEnum)
+ pb.Foo = FOO_FOO1.Enum()
+ o := old()
+ if err := o.Marshal(pb); err != nil {
+ t.Fatal("error encoding enum:", err)
+ }
+ pb1 := new(GoEnum)
+ if err := o.Unmarshal(pb1); err != nil {
+ t.Fatal("error decoding enum:", err)
+ }
+ if *pb1.Foo != FOO_FOO1 {
+ t.Error("expected 7 but got ", *pb1.Foo)
+ }
+}
+
+// Enum types have String methods. Check that enum fields can be printed.
+// We don't care what the value actually is, just as long as it doesn't crash.
+func TestPrintingNilEnumFields(t *testing.T) {
+ pb := new(GoEnum)
+ fmt.Sprintf("%+v", pb)
+}
+
+// Verify that absent required fields cause Marshal/Unmarshal to return errors.
+func TestRequiredFieldEnforcement(t *testing.T) {
+ pb := new(GoTestField)
+ _, err := Marshal(pb)
+ if err == nil {
+ t.Error("marshal: expected error, got nil")
+ } else if strings.Index(err.Error(), "Label") < 0 {
+ t.Errorf("marshal: bad error type: %v", err)
+ }
+
+ // A slightly sneaky, yet valid, proto. It encodes the same required field twice,
+ // so simply counting the required fields is insufficient.
+ // field 1, encoding 2, value "hi"
+ buf := []byte("\x0A\x02hi\x0A\x02hi")
+ err = Unmarshal(buf, pb)
+ if err == nil {
+ t.Error("unmarshal: expected error, got nil")
+ } else if strings.Index(err.Error(), "{Unknown}") < 0 {
+ t.Errorf("unmarshal: bad error type: %v", err)
+ }
+}
+
+func TestTypedNilMarshal(t *testing.T) {
+ // A typed nil should return ErrNil and not crash.
+ _, err := Marshal((*GoEnum)(nil))
+ if err != ErrNil {
+ t.Errorf("Marshal: got err %v, want ErrNil", err)
+ }
+}
+
+// A type that implements the Marshaler interface, but is not nillable.
+type nonNillableInt uint64
+
+func (nni nonNillableInt) Marshal() ([]byte, error) {
+ return EncodeVarint(uint64(nni)), nil
+}
+
+type NNIMessage struct {
+ nni nonNillableInt
+}
+
+func (*NNIMessage) Reset() {}
+func (*NNIMessage) String() string { return "" }
+func (*NNIMessage) ProtoMessage() {}
+
+// A type that implements the Marshaler interface and is nillable.
+type nillableMessage struct {
+ x uint64
+}
+
+func (nm *nillableMessage) Marshal() ([]byte, error) {
+ return EncodeVarint(nm.x), nil
+}
+
+type NMMessage struct {
+ nm *nillableMessage
+}
+
+func (*NMMessage) Reset() {}
+func (*NMMessage) String() string { return "" }
+func (*NMMessage) ProtoMessage() {}
+
+// Verify a type that uses the Marshaler interface, but has a nil pointer.
+func TestNilMarshaler(t *testing.T) {
+ // Try a struct with a Marshaler field that is nil.
+ // It should be directly marshable.
+ nmm := new(NMMessage)
+ if _, err := Marshal(nmm); err != nil {
+ t.Error("unexpected error marshaling nmm: ", err)
+ }
+
+ // Try a struct with a Marshaler field that is not nillable.
+ nnim := new(NNIMessage)
+ nnim.nni = 7
+ var _ Marshaler = nnim.nni // verify it is truly a Marshaler
+ if _, err := Marshal(nnim); err != nil {
+ t.Error("unexpected error marshaling nnim: ", err)
+ }
+}
+
+func TestAllSetDefaults(t *testing.T) {
+ // Exercise SetDefaults with all scalar field types.
+ m := &Defaults{
+ // NaN != NaN, so override that here.
+ F_Nan: Float32(1.7),
+ }
+ expected := &Defaults{
+ F_Bool: Bool(true),
+ F_Int32: Int32(32),
+ F_Int64: Int64(64),
+ F_Fixed32: Uint32(320),
+ F_Fixed64: Uint64(640),
+ F_Uint32: Uint32(3200),
+ F_Uint64: Uint64(6400),
+ F_Float: Float32(314159),
+ F_Double: Float64(271828),
+ F_String: String(`hello, "world!"` + "\n"),
+ F_Bytes: []byte("Bignose"),
+ F_Sint32: Int32(-32),
+ F_Sint64: Int64(-64),
+ F_Enum: Defaults_GREEN.Enum(),
+ F_Pinf: Float32(float32(math.Inf(1))),
+ F_Ninf: Float32(float32(math.Inf(-1))),
+ F_Nan: Float32(1.7),
+ StrZero: String(""),
+ }
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("SetDefaults failed\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestSetDefaultsWithSetField(t *testing.T) {
+ // Check that a set value is not overridden.
+ m := &Defaults{
+ F_Int32: Int32(12),
+ }
+ SetDefaults(m)
+ if v := m.GetF_Int32(); v != 12 {
+ t.Errorf("m.FInt32 = %v, want 12", v)
+ }
+}
+
+func TestSetDefaultsWithSubMessage(t *testing.T) {
+ m := &OtherMessage{
+ Key: Int64(123),
+ Inner: &InnerMessage{
+ Host: String("gopher"),
+ },
+ }
+ expected := &OtherMessage{
+ Key: Int64(123),
+ Inner: &InnerMessage{
+ Host: String("gopher"),
+ Port: Int32(4000),
+ },
+ }
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestSetDefaultsWithRepeatedSubMessage(t *testing.T) {
+ m := &MyMessage{
+ RepInner: []*InnerMessage{{}},
+ }
+ expected := &MyMessage{
+ RepInner: []*InnerMessage{{
+ Port: Int32(4000),
+ }},
+ }
+ SetDefaults(m)
+ if !Equal(m, expected) {
+ t.Errorf("\n got %v\nwant %v", m, expected)
+ }
+}
+
+func TestMaximumTagNumber(t *testing.T) {
+ m := &MaxTag{
+ LastField: String("natural goat essence"),
+ }
+ buf, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("proto.Marshal failed: %v", err)
+ }
+ m2 := new(MaxTag)
+ if err := Unmarshal(buf, m2); err != nil {
+ t.Fatalf("proto.Unmarshal failed: %v", err)
+ }
+ if got, want := m2.GetLastField(), *m.LastField; got != want {
+ t.Errorf("got %q, want %q", got, want)
+ }
+}
+
+func TestJSON(t *testing.T) {
+ m := &MyMessage{
+ Count: Int32(4),
+ Pet: []string{"bunny", "kitty"},
+ Inner: &InnerMessage{
+ Host: String("cauchy"),
+ },
+ Bikeshed: MyMessage_GREEN.Enum(),
+ }
+ const expected = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"},"bikeshed":1}`
+
+ b, err := json.Marshal(m)
+ if err != nil {
+ t.Fatalf("json.Marshal failed: %v", err)
+ }
+ s := string(b)
+ if s != expected {
+ t.Errorf("got %s\nwant %s", s, expected)
+ }
+
+ received := new(MyMessage)
+ if err := json.Unmarshal(b, received); err != nil {
+ t.Fatalf("json.Unmarshal failed: %v", err)
+ }
+ if !Equal(received, m) {
+ t.Fatalf("got %s, want %s", received, m)
+ }
+
+ // Test unmarshalling of JSON with symbolic enum name.
+ const old = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"},"bikeshed":"GREEN"}`
+ received.Reset()
+ if err := json.Unmarshal([]byte(old), received); err != nil {
+ t.Fatalf("json.Unmarshal failed: %v", err)
+ }
+ if !Equal(received, m) {
+ t.Fatalf("got %s, want %s", received, m)
+ }
+}
+
+func TestBadWireType(t *testing.T) {
+ b := []byte{7<<3 | 6} // field 7, wire type 6
+ pb := new(OtherMessage)
+ if err := Unmarshal(b, pb); err == nil {
+ t.Errorf("Unmarshal did not fail")
+ } else if !strings.Contains(err.Error(), "unknown wire type") {
+ t.Errorf("wrong error: %v", err)
+ }
+}
+
+func TestBytesWithInvalidLength(t *testing.T) {
+ // If a byte sequence has an invalid (negative) length, Unmarshal should not panic.
+ b := []byte{2<<3 | WireBytes, 0xff, 0xff, 0xff, 0xff, 0xff, 0}
+ Unmarshal(b, new(MyMessage))
+}
+
+func TestLengthOverflow(t *testing.T) {
+ // Overflowing a length should not panic.
+ b := []byte{2<<3 | WireBytes, 1, 1, 3<<3 | WireBytes, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x01}
+ Unmarshal(b, new(MyMessage))
+}
+
+func TestVarintOverflow(t *testing.T) {
+ // Overflowing a 64-bit length should not be allowed.
+ b := []byte{1<<3 | WireVarint, 0x01, 3<<3 | WireBytes, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01}
+ if err := Unmarshal(b, new(MyMessage)); err == nil {
+ t.Fatalf("Overflowed uint64 length without error")
+ }
+}
+
+func TestUnmarshalFuzz(t *testing.T) {
+ const N = 1000
+ seed := time.Now().UnixNano()
+ t.Logf("RNG seed is %d", seed)
+ rng := rand.New(rand.NewSource(seed))
+ buf := make([]byte, 20)
+ for i := 0; i < N; i++ {
+ for j := range buf {
+ buf[j] = byte(rng.Intn(256))
+ }
+ fuzzUnmarshal(t, buf)
+ }
+}
+
+func TestMergeMessages(t *testing.T) {
+ pb := &MessageList{Message: []*MessageList_Message{{Name: String("x"), Count: Int32(1)}}}
+ data, err := Marshal(pb)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+
+ pb1 := new(MessageList)
+ if err := Unmarshal(data, pb1); err != nil {
+ t.Fatalf("first Unmarshal: %v", err)
+ }
+ if err := Unmarshal(data, pb1); err != nil {
+ t.Fatalf("second Unmarshal: %v", err)
+ }
+ if len(pb1.Message) != 1 {
+ t.Errorf("two Unmarshals produced %d Messages, want 1", len(pb1.Message))
+ }
+
+ pb2 := new(MessageList)
+ if err := UnmarshalMerge(data, pb2); err != nil {
+ t.Fatalf("first UnmarshalMerge: %v", err)
+ }
+ if err := UnmarshalMerge(data, pb2); err != nil {
+ t.Fatalf("second UnmarshalMerge: %v", err)
+ }
+ if len(pb2.Message) != 2 {
+ t.Errorf("two UnmarshalMerges produced %d Messages, want 2", len(pb2.Message))
+ }
+}
+
+func TestExtensionMarshalOrder(t *testing.T) {
+ m := &MyMessage{Count: Int(123)}
+ if err := SetExtension(m, E_Ext_More, &Ext{Data: String("alpha")}); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+ if err := SetExtension(m, E_Ext_Text, String("aleph")); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+ if err := SetExtension(m, E_Ext_Number, Int32(1)); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+
+ // Serialize m several times, and check we get the same bytes each time.
+ var orig []byte
+ for i := 0; i < 100; i++ {
+ b, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if i == 0 {
+ orig = b
+ continue
+ }
+ if !bytes.Equal(b, orig) {
+ t.Errorf("Bytes differ on attempt #%d", i)
+ }
+ }
+}
+
+// Many extensions, because small maps might not iterate differently on each iteration.
+var exts = []*ExtensionDesc{
+ E_X201,
+ E_X202,
+ E_X203,
+ E_X204,
+ E_X205,
+ E_X206,
+ E_X207,
+ E_X208,
+ E_X209,
+ E_X210,
+ E_X211,
+ E_X212,
+ E_X213,
+ E_X214,
+ E_X215,
+ E_X216,
+ E_X217,
+ E_X218,
+ E_X219,
+ E_X220,
+ E_X221,
+ E_X222,
+ E_X223,
+ E_X224,
+ E_X225,
+ E_X226,
+ E_X227,
+ E_X228,
+ E_X229,
+ E_X230,
+ E_X231,
+ E_X232,
+ E_X233,
+ E_X234,
+ E_X235,
+ E_X236,
+ E_X237,
+ E_X238,
+ E_X239,
+ E_X240,
+ E_X241,
+ E_X242,
+ E_X243,
+ E_X244,
+ E_X245,
+ E_X246,
+ E_X247,
+ E_X248,
+ E_X249,
+ E_X250,
+}
+
+func TestMessageSetMarshalOrder(t *testing.T) {
+ m := &MyMessageSet{}
+ for _, x := range exts {
+ if err := SetExtension(m, x, &Empty{}); err != nil {
+ t.Fatalf("SetExtension: %v", err)
+ }
+ }
+
+ buf, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+
+ // Serialize m several times, and check we get the same bytes each time.
+ for i := 0; i < 10; i++ {
+ b1, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ if !bytes.Equal(b1, buf) {
+ t.Errorf("Bytes differ on re-Marshal #%d", i)
+ }
+
+ m2 := &MyMessageSet{}
+ if err := Unmarshal(buf, m2); err != nil {
+ t.Errorf("Unmarshal: %v", err)
+ }
+ b2, err := Marshal(m2)
+ if err != nil {
+ t.Errorf("re-Marshal: %v", err)
+ }
+ if !bytes.Equal(b2, buf) {
+ t.Errorf("Bytes differ on round-trip #%d", i)
+ }
+ }
+}
+
+func TestUnmarshalMergesMessages(t *testing.T) {
+ // If a nested message occurs twice in the input,
+ // the fields should be merged when decoding.
+ a := &OtherMessage{
+ Key: Int64(123),
+ Inner: &InnerMessage{
+ Host: String("polhode"),
+ Port: Int32(1234),
+ },
+ }
+ aData, err := Marshal(a)
+ if err != nil {
+ t.Fatalf("Marshal(a): %v", err)
+ }
+ b := &OtherMessage{
+ Weight: Float32(1.2),
+ Inner: &InnerMessage{
+ Host: String("herpolhode"),
+ Connected: Bool(true),
+ },
+ }
+ bData, err := Marshal(b)
+ if err != nil {
+ t.Fatalf("Marshal(b): %v", err)
+ }
+ want := &OtherMessage{
+ Key: Int64(123),
+ Weight: Float32(1.2),
+ Inner: &InnerMessage{
+ Host: String("herpolhode"),
+ Port: Int32(1234),
+ Connected: Bool(true),
+ },
+ }
+ got := new(OtherMessage)
+ if err := Unmarshal(append(aData, bData...), got); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ if !Equal(got, want) {
+ t.Errorf("\n got %v\nwant %v", got, want)
+ }
+}
+
+func TestEncodingSizes(t *testing.T) {
+ tests := []struct {
+ m Message
+ n int
+ }{
+ {&Defaults{F_Int32: Int32(math.MaxInt32)}, 6},
+ {&Defaults{F_Int32: Int32(math.MinInt32)}, 11},
+ {&Defaults{F_Uint32: Uint32(uint32(math.MaxInt32) + 1)}, 6},
+ {&Defaults{F_Uint32: Uint32(math.MaxUint32)}, 6},
+ }
+ for _, test := range tests {
+ b, err := Marshal(test.m)
+ if err != nil {
+ t.Errorf("Marshal(%v): %v", test.m, err)
+ continue
+ }
+ if len(b) != test.n {
+ t.Errorf("Marshal(%v) yielded %d bytes, want %d bytes", test.m, len(b), test.n)
+ }
+ }
+}
+
+func TestRequiredNotSetError(t *testing.T) {
+ pb := initGoTest(false)
+ pb.RequiredField.Label = nil
+ pb.F_Int32Required = nil
+ pb.F_Int64Required = nil
+
+ expected := "0807" + // field 1, encoding 0, value 7
+ "2206" + "120474797065" + // field 4, encoding 2 (GoTestField)
+ "5001" + // field 10, encoding 0, value 1
+ "6d20000000" + // field 13, encoding 5, value 0x20
+ "714000000000000000" + // field 14, encoding 1, value 0x40
+ "78a019" + // field 15, encoding 0, value 0xca0 = 3232
+ "8001c032" + // field 16, encoding 0, value 0x1940 = 6464
+ "8d0100004a45" + // field 17, encoding 5, value 3232.0
+ "9101000000000040b940" + // field 18, encoding 1, value 6464.0
+ "9a0106" + "737472696e67" + // field 19, encoding 2, string "string"
+ "b304" + // field 70, encoding 3, start group
+ "ba0408" + "7265717569726564" + // field 71, encoding 2, string "required"
+ "b404" + // field 70, encoding 4, end group
+ "aa0605" + "6279746573" + // field 101, encoding 2, string "bytes"
+ "b0063f" + // field 102, encoding 0, 0x3f zigzag32
+ "b8067f" // field 103, encoding 0, 0x7f zigzag64
+
+ o := old()
+ bytes, err := Marshal(pb)
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ fmt.Printf("marshal-1 err = %v, want *RequiredNotSetError", err)
+ o.DebugPrint("", bytes)
+ t.Fatalf("expected = %s", expected)
+ }
+ if strings.Index(err.Error(), "RequiredField.Label") < 0 {
+ t.Errorf("marshal-1 wrong err msg: %v", err)
+ }
+ if !equal(bytes, expected, t) {
+ o.DebugPrint("neq 1", bytes)
+ t.Fatalf("expected = %s", expected)
+ }
+
+ // Now test Unmarshal by recreating the original buffer.
+ pbd := new(GoTest)
+ err = Unmarshal(bytes, pbd)
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ t.Fatalf("unmarshal err = %v, want *RequiredNotSetError", err)
+ o.DebugPrint("", bytes)
+ t.Fatalf("string = %s", expected)
+ }
+ if strings.Index(err.Error(), "RequiredField.{Unknown}") < 0 {
+ t.Errorf("unmarshal wrong err msg: %v", err)
+ }
+ bytes, err = Marshal(pbd)
+ if _, ok := err.(*RequiredNotSetError); !ok {
+ t.Errorf("marshal-2 err = %v, want *RequiredNotSetError", err)
+ o.DebugPrint("", bytes)
+ t.Fatalf("string = %s", expected)
+ }
+ if strings.Index(err.Error(), "RequiredField.Label") < 0 {
+ t.Errorf("marshal-2 wrong err msg: %v", err)
+ }
+ if !equal(bytes, expected, t) {
+ o.DebugPrint("neq 2", bytes)
+ t.Fatalf("string = %s", expected)
+ }
+}
+
+func fuzzUnmarshal(t *testing.T, data []byte) {
+ defer func() {
+ if e := recover(); e != nil {
+ t.Errorf("These bytes caused a panic: %+v", data)
+ t.Logf("Stack:\n%s", debug.Stack())
+ t.FailNow()
+ }
+ }()
+
+ pb := new(MyMessage)
+ Unmarshal(data, pb)
+}
+
+func TestMapFieldMarshal(t *testing.T) {
+ m := &MessageWithMap{
+ NameMapping: map[int32]string{
+ 1: "Rob",
+ 4: "Ian",
+ 8: "Dave",
+ },
+ }
+ b, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+
+ // b should be the concatenation of these three byte sequences in some order.
+ parts := []string{
+ "\n\a\b\x01\x12\x03Rob",
+ "\n\a\b\x04\x12\x03Ian",
+ "\n\b\b\x08\x12\x04Dave",
+ }
+ ok := false
+ for i := range parts {
+ for j := range parts {
+ if j == i {
+ continue
+ }
+ for k := range parts {
+ if k == i || k == j {
+ continue
+ }
+ try := parts[i] + parts[j] + parts[k]
+ if bytes.Equal(b, []byte(try)) {
+ ok = true
+ break
+ }
+ }
+ }
+ }
+ if !ok {
+ t.Fatalf("Incorrect Marshal output.\n got %q\nwant %q (or a permutation of that)", b, parts[0]+parts[1]+parts[2])
+ }
+ t.Logf("FYI b: %q", b)
+
+ (new(Buffer)).DebugPrint("Dump of b", b)
+}
+
+func TestMapFieldRoundTrips(t *testing.T) {
+ m := &MessageWithMap{
+ NameMapping: map[int32]string{
+ 1: "Rob",
+ 4: "Ian",
+ 8: "Dave",
+ },
+ MsgMapping: map[int64]*FloatingPoint{
+ 0x7001: &FloatingPoint{F: Float64(2.0)},
+ },
+ ByteMapping: map[bool][]byte{
+ false: []byte("that's not right!"),
+ true: []byte("aye, 'tis true!"),
+ },
+ }
+ b, err := Marshal(m)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ t.Logf("FYI b: %q", b)
+ m2 := new(MessageWithMap)
+ if err := Unmarshal(b, m2); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ for _, pair := range [][2]interface{}{
+ {m.NameMapping, m2.NameMapping},
+ {m.MsgMapping, m2.MsgMapping},
+ {m.ByteMapping, m2.ByteMapping},
+ } {
+ if !reflect.DeepEqual(pair[0], pair[1]) {
+ t.Errorf("Map did not survive a round trip.\ninitial: %v\n final: %v", pair[0], pair[1])
+ }
+ }
+}
+
+// Benchmarks
+
+func testMsg() *GoTest {
+ pb := initGoTest(true)
+ const N = 1000 // Internally the library starts much smaller.
+ pb.F_Int32Repeated = make([]int32, N)
+ pb.F_DoubleRepeated = make([]float64, N)
+ for i := 0; i < N; i++ {
+ pb.F_Int32Repeated[i] = int32(i)
+ pb.F_DoubleRepeated[i] = float64(i)
+ }
+ return pb
+}
+
+func bytesMsg() *GoTest {
+ pb := initGoTest(true)
+ buf := make([]byte, 4000)
+ for i := range buf {
+ buf[i] = byte(i)
+ }
+ pb.F_BytesDefaulted = buf
+ return pb
+}
+
+func benchmarkMarshal(b *testing.B, pb Message, marshal func(Message) ([]byte, error)) {
+ d, _ := marshal(pb)
+ b.SetBytes(int64(len(d)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ marshal(pb)
+ }
+}
+
+func benchmarkBufferMarshal(b *testing.B, pb Message) {
+ p := NewBuffer(nil)
+ benchmarkMarshal(b, pb, func(pb0 Message) ([]byte, error) {
+ p.Reset()
+ err := p.Marshal(pb0)
+ return p.Bytes(), err
+ })
+}
+
+func benchmarkSize(b *testing.B, pb Message) {
+ benchmarkMarshal(b, pb, func(pb0 Message) ([]byte, error) {
+ Size(pb)
+ return nil, nil
+ })
+}
+
+func newOf(pb Message) Message {
+ in := reflect.ValueOf(pb)
+ if in.IsNil() {
+ return pb
+ }
+ return reflect.New(in.Type().Elem()).Interface().(Message)
+}
+
+func benchmarkUnmarshal(b *testing.B, pb Message, unmarshal func([]byte, Message) error) {
+ d, _ := Marshal(pb)
+ b.SetBytes(int64(len(d)))
+ pbd := newOf(pb)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ unmarshal(d, pbd)
+ }
+}
+
+func benchmarkBufferUnmarshal(b *testing.B, pb Message) {
+ p := NewBuffer(nil)
+ benchmarkUnmarshal(b, pb, func(d []byte, pb0 Message) error {
+ p.SetBuf(d)
+ return p.Unmarshal(pb0)
+ })
+}
+
+// Benchmark{Marshal,BufferMarshal,Size,Unmarshal,BufferUnmarshal}{,Bytes}
+
+func BenchmarkMarshal(b *testing.B) {
+ benchmarkMarshal(b, testMsg(), Marshal)
+}
+
+func BenchmarkBufferMarshal(b *testing.B) {
+ benchmarkBufferMarshal(b, testMsg())
+}
+
+func BenchmarkSize(b *testing.B) {
+ benchmarkSize(b, testMsg())
+}
+
+func BenchmarkUnmarshal(b *testing.B) {
+ benchmarkUnmarshal(b, testMsg(), Unmarshal)
+}
+
+func BenchmarkBufferUnmarshal(b *testing.B) {
+ benchmarkBufferUnmarshal(b, testMsg())
+}
+
+func BenchmarkMarshalBytes(b *testing.B) {
+ benchmarkMarshal(b, bytesMsg(), Marshal)
+}
+
+func BenchmarkBufferMarshalBytes(b *testing.B) {
+ benchmarkBufferMarshal(b, bytesMsg())
+}
+
+func BenchmarkSizeBytes(b *testing.B) {
+ benchmarkSize(b, bytesMsg())
+}
+
+func BenchmarkUnmarshalBytes(b *testing.B) {
+ benchmarkUnmarshal(b, bytesMsg(), Unmarshal)
+}
+
+func BenchmarkBufferUnmarshalBytes(b *testing.B) {
+ benchmarkBufferUnmarshal(b, bytesMsg())
+}
+
+func BenchmarkUnmarshalUnrecognizedFields(b *testing.B) {
+ b.StopTimer()
+ pb := initGoTestField()
+ skip := &GoSkipTest{
+ SkipInt32: Int32(32),
+ SkipFixed32: Uint32(3232),
+ SkipFixed64: Uint64(6464),
+ SkipString: String("skipper"),
+ Skipgroup: &GoSkipTest_SkipGroup{
+ GroupInt32: Int32(75),
+ GroupString: String("wxyz"),
+ },
+ }
+
+ pbd := new(GoTestField)
+ p := NewBuffer(nil)
+ p.Marshal(pb)
+ p.Marshal(skip)
+ p2 := NewBuffer(nil)
+
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ p2.SetBuf(p.Bytes())
+ p2.Unmarshal(pbd)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go
new file mode 100644
index 000000000..6c6a7d95f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone.go
@@ -0,0 +1,197 @@
+// 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: MessageSet and 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) {
+ 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))
+ }
+
+ if emIn, ok := in.Addr().Interface().(extendableProto); ok {
+ emOut := out.Addr().Interface().(extendableProto)
+ mergeExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
+ }
+
+ 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...))
+ }
+}
+
+func mergeAny(out, in reflect.Value) {
+ 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:
+ out.Set(in)
+ 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))
+ 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())
+ case reflect.Slice:
+ if in.IsNil() {
+ return
+ }
+ if in.Type().Elem().Kind() == reflect.Uint8 {
+ // []byte is a scalar bytes field, not a repeated field.
+ // 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))
+ 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))
+ 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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go
new file mode 100644
index 000000000..cdc5dff6b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/clone_test.go
@@ -0,0 +1,227 @@
+// 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.
+
+package proto_test
+
+import (
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+
+ pb "./testdata"
+)
+
+var cloneTestMessage = &pb.MyMessage{
+ Count: proto.Int32(42),
+ Name: proto.String("Dave"),
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Inner: &pb.InnerMessage{
+ Host: proto.String("niles"),
+ Port: proto.Int32(9099),
+ Connected: proto.Bool(true),
+ },
+ Others: []*pb.OtherMessage{
+ {
+ Value: []byte("some bytes"),
+ },
+ },
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(6),
+ },
+ RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
+}
+
+func init() {
+ ext := &pb.Ext{
+ Data: proto.String("extension"),
+ }
+ if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil {
+ panic("SetExtension: " + err.Error())
+ }
+}
+
+func TestClone(t *testing.T) {
+ m := proto.Clone(cloneTestMessage).(*pb.MyMessage)
+ if !proto.Equal(m, cloneTestMessage) {
+ t.Errorf("Clone(%v) = %v", cloneTestMessage, m)
+ }
+
+ // Verify it was a deep copy.
+ *m.Inner.Port++
+ if proto.Equal(m, cloneTestMessage) {
+ t.Error("Mutating clone changed the original")
+ }
+ // Byte fields and repeated fields should be copied.
+ if &m.Pet[0] == &cloneTestMessage.Pet[0] {
+ t.Error("Pet: repeated field not copied")
+ }
+ if &m.Others[0] == &cloneTestMessage.Others[0] {
+ t.Error("Others: repeated field not copied")
+ }
+ if &m.Others[0].Value[0] == &cloneTestMessage.Others[0].Value[0] {
+ t.Error("Others[0].Value: bytes field not copied")
+ }
+ if &m.RepBytes[0] == &cloneTestMessage.RepBytes[0] {
+ t.Error("RepBytes: repeated field not copied")
+ }
+ if &m.RepBytes[0][0] == &cloneTestMessage.RepBytes[0][0] {
+ t.Error("RepBytes[0]: bytes field not copied")
+ }
+}
+
+func TestCloneNil(t *testing.T) {
+ var m *pb.MyMessage
+ if c := proto.Clone(m); !proto.Equal(m, c) {
+ t.Errorf("Clone(%v) = %v", m, c)
+ }
+}
+
+var mergeTests = []struct {
+ src, dst, want proto.Message
+}{
+ {
+ src: &pb.MyMessage{
+ Count: proto.Int32(42),
+ },
+ dst: &pb.MyMessage{
+ Name: proto.String("Dave"),
+ },
+ want: &pb.MyMessage{
+ Count: proto.Int32(42),
+ Name: proto.String("Dave"),
+ },
+ },
+ {
+ src: &pb.MyMessage{
+ Inner: &pb.InnerMessage{
+ Host: proto.String("hey"),
+ Connected: proto.Bool(true),
+ },
+ Pet: []string{"horsey"},
+ Others: []*pb.OtherMessage{
+ {
+ Value: []byte("some bytes"),
+ },
+ },
+ },
+ dst: &pb.MyMessage{
+ Inner: &pb.InnerMessage{
+ Host: proto.String("niles"),
+ Port: proto.Int32(9099),
+ },
+ Pet: []string{"bunny", "kitty"},
+ Others: []*pb.OtherMessage{
+ {
+ Key: proto.Int64(31415926535),
+ },
+ {
+ // Explicitly test a src=nil field
+ Inner: nil,
+ },
+ },
+ },
+ want: &pb.MyMessage{
+ Inner: &pb.InnerMessage{
+ Host: proto.String("hey"),
+ Connected: proto.Bool(true),
+ Port: proto.Int32(9099),
+ },
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Others: []*pb.OtherMessage{
+ {
+ Key: proto.Int64(31415926535),
+ },
+ {},
+ {
+ Value: []byte("some bytes"),
+ },
+ },
+ },
+ },
+ {
+ src: &pb.MyMessage{
+ RepBytes: [][]byte{[]byte("wow")},
+ },
+ dst: &pb.MyMessage{
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(6),
+ },
+ RepBytes: [][]byte{[]byte("sham")},
+ },
+ want: &pb.MyMessage{
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(6),
+ },
+ RepBytes: [][]byte{[]byte("sham"), []byte("wow")},
+ },
+ },
+ // Check that a scalar bytes field replaces rather than appends.
+ {
+ src: &pb.OtherMessage{Value: []byte("foo")},
+ dst: &pb.OtherMessage{Value: []byte("bar")},
+ want: &pb.OtherMessage{Value: []byte("foo")},
+ },
+ {
+ src: &pb.MessageWithMap{
+ NameMapping: map[int32]string{6: "Nigel"},
+ MsgMapping: map[int64]*pb.FloatingPoint{
+ 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
+ },
+ ByteMapping: map[bool][]byte{true: []byte("wowsa")},
+ },
+ dst: &pb.MessageWithMap{
+ NameMapping: map[int32]string{
+ 6: "Bruce", // should be overwritten
+ 7: "Andrew",
+ },
+ },
+ want: &pb.MessageWithMap{
+ NameMapping: map[int32]string{
+ 6: "Nigel",
+ 7: "Andrew",
+ },
+ MsgMapping: map[int64]*pb.FloatingPoint{
+ 0x4001: &pb.FloatingPoint{F: proto.Float64(2.0)},
+ },
+ ByteMapping: map[bool][]byte{true: []byte("wowsa")},
+ },
+ },
+}
+
+func TestMerge(t *testing.T) {
+ for _, m := range mergeTests {
+ got := proto.Clone(m.dst)
+ proto.Merge(got, m.src)
+ if !proto.Equal(got, m.want) {
+ t.Errorf("Merge(%v, %v)\n got %v\nwant %v\n", m.dst, m.src, got, m.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go
new file mode 100644
index 000000000..129792ed1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/decode.go
@@ -0,0 +1,821 @@
+// 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")
+
+// 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) {
+ // x, n already 0
+ 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
+}
+
+// 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) {
+ // x, err already 0
+
+ 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
+}
+
+// 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)
+}
+
+// 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.
+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 {
+ 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 := structPointer_Interface(base, st).(extendableProto); isExtensionField(e, int32(tag)) {
+ if err = o.skip(st, tag, wire); err == nil {
+ ext := e.ExtensionMap()[int32(tag)] // may be missing
+ ext.enc = append(ext.enc, o.buf[oi:o.index]...)
+ e.ExtensionMap()[int32(tag)] = ext
+ }
+ 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
+
+ y := *v
+ for i := 0; i < nb; i++ {
+ 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_Map(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])
+ }
+ }
+
+ v.SetMapIndex(keyptr.Elem(), valptr.Elem())
+ 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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go
new file mode 100644
index 000000000..1512d605b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/encode.go
@@ -0,0 +1,1283 @@
+// 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")
+
+ // ErrNil is the error returned if Marshal is called with nil.
+ ErrNil = errors.New("proto: Marshal called with nil")
+)
+
+// 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
+
+// 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
+}
+
+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(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+
+func sizeZigzag64(x uint64) int {
+ return sizeVarint(uint64((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)
+ var state errorState
+ if err != nil && !state.shouldContinue(err, nil) {
+ return nil, err
+ }
+ if p.buf == nil && err == nil {
+ // Return a non-nil slice on success.
+ return []byte{}, nil
+ }
+ return p.buf, 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()
+ if err != nil {
+ return err
+ }
+ p.buf = append(p.buf, data...)
+ return nil
+ }
+
+ 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++
+ }
+
+ 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++
+ }
+
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 == "" {
+ 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 nil
+ }
+
+ 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 {
+ 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 {
+ 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 += len(p.tagcode)
+ 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 {
+ v := *structPointer_ExtMap(base, p.field)
+ if err := encodeExtensionMap(v); err != nil {
+ return err
+ }
+ // 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 sizeExtensionMap(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_Map(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 {
+ return err
+ }
+ return nil
+ }
+
+ keys := v.MapKeys()
+ sort.Sort(mapKeys(keys))
+ for _, key := range keys {
+ 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_Map(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 are two bytes per map entry.
+ n += 2
+ n += p.mkeyprop.size(p.mkeyprop, keybase)
+ n += p.mvalprop.size(p.mvalprop, valbase)
+ }
+ 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 !state.shouldContinue(err, p) {
+ return err
+ }
+ }
+ }
+ }
+
+ // Add unrecognized fields at the end.
+ if prop.unrecField.IsValid() {
+ v := *structPointer_Bytes(base, prop.unrecField)
+ 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)
+ }
+
+ 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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go
new file mode 100644
index 000000000..d8673a3e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal.go
@@ -0,0 +1,256 @@
+// 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.
+// TODO: MessageSet.
+
+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.
+ - Two repeated fields are equal iff their lengths are the same,
+ and their corresponding elements are equal (a "bytes" field,
+ although represented by []byte, is not a repeated field)
+ - 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.
+ - 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 {
+ 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) {
+ return false
+ }
+ }
+
+ if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
+ em2 := v2.FieldByName("XXX_extensions")
+ if !equalExtensions(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.
+func equalAny(v1, v2 reflect.Value) 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.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) {
+ return false
+ }
+ }
+ return true
+ case reflect.Ptr:
+ return equalAny(v1.Elem(), v2.Elem())
+ case reflect.Slice:
+ if v1.Type().Elem().Kind() == reflect.Uint8 {
+ // short circuit: []byte
+ 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)) {
+ 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.
+// em1 and em2 are extension maps.
+func equalExtensions(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)) {
+ 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)) {
+ return false
+ }
+ }
+
+ return true
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go
new file mode 100644
index 000000000..6908df97f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/equal_test.go
@@ -0,0 +1,191 @@
+// 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.
+
+package proto_test
+
+import (
+ "testing"
+
+ pb "./testdata"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+// Four identical base messages.
+// The init function adds extensions to some of them.
+var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)}
+var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)}
+var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)}
+var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)}
+
+// Two messages with non-message extensions.
+var messageWithInt32Extension1 = &pb.MyMessage{Count: Int32(8)}
+var messageWithInt32Extension2 = &pb.MyMessage{Count: Int32(8)}
+
+func init() {
+ ext1 := &pb.Ext{Data: String("Kirk")}
+ ext2 := &pb.Ext{Data: String("Picard")}
+
+ // messageWithExtension1a has ext1, but never marshals it.
+ if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil {
+ panic("SetExtension on 1a failed: " + err.Error())
+ }
+
+ // messageWithExtension1b is the unmarshaled form of messageWithExtension1a.
+ if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil {
+ panic("SetExtension on 1b failed: " + err.Error())
+ }
+ buf, err := Marshal(messageWithExtension1b)
+ if err != nil {
+ panic("Marshal of 1b failed: " + err.Error())
+ }
+ messageWithExtension1b.Reset()
+ if err := Unmarshal(buf, messageWithExtension1b); err != nil {
+ panic("Unmarshal of 1b failed: " + err.Error())
+ }
+
+ // messageWithExtension2 has ext2.
+ if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil {
+ panic("SetExtension on 2 failed: " + err.Error())
+ }
+
+ if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(23)); err != nil {
+ panic("SetExtension on Int32-1 failed: " + err.Error())
+ }
+ if err := SetExtension(messageWithInt32Extension1, pb.E_Ext_Number, Int32(24)); err != nil {
+ panic("SetExtension on Int32-2 failed: " + err.Error())
+ }
+}
+
+var EqualTests = []struct {
+ desc string
+ a, b Message
+ exp bool
+}{
+ {"different types", &pb.GoEnum{}, &pb.GoTestField{}, false},
+ {"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true},
+ {"nil vs nil", nil, nil, true},
+ {"typed nil vs typed nil", (*pb.GoEnum)(nil), (*pb.GoEnum)(nil), true},
+ {"typed nil vs empty", (*pb.GoEnum)(nil), &pb.GoEnum{}, false},
+ {"different typed nil", (*pb.GoEnum)(nil), (*pb.GoTestField)(nil), false},
+
+ {"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false},
+ {"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false},
+ {"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false},
+ {"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true},
+
+ {"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false},
+ {"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false},
+ {"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false},
+ {"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true},
+ {"repeated, nil equal nil", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: nil}, true},
+ {"repeated, nil equal empty", &pb.GoTest{F_Int32Repeated: nil}, &pb.GoTest{F_Int32Repeated: []int32{}}, true},
+ {"repeated, empty equal nil", &pb.GoTest{F_Int32Repeated: []int32{}}, &pb.GoTest{F_Int32Repeated: nil}, true},
+
+ {
+ "nested, different",
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}},
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}},
+ false,
+ },
+ {
+ "nested, equal",
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
+ &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}},
+ true,
+ },
+
+ {"bytes", &pb.OtherMessage{Value: []byte("foo")}, &pb.OtherMessage{Value: []byte("foo")}, true},
+ {"bytes, empty", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: []byte{}}, true},
+ {"bytes, empty vs nil", &pb.OtherMessage{Value: []byte{}}, &pb.OtherMessage{Value: nil}, false},
+ {
+ "repeated bytes",
+ &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
+ &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}},
+ true,
+ },
+
+ {"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false},
+ {"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true},
+ {"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false},
+
+ {"int32 extension vs. itself", messageWithInt32Extension1, messageWithInt32Extension1, true},
+ {"int32 extension vs. a different int32", messageWithInt32Extension1, messageWithInt32Extension2, false},
+
+ {
+ "message with group",
+ &pb.MyMessage{
+ Count: Int32(1),
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: Int32(5),
+ },
+ },
+ &pb.MyMessage{
+ Count: Int32(1),
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: Int32(5),
+ },
+ },
+ true,
+ },
+
+ {
+ "map same",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ true,
+ },
+ {
+ "map different entry",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{2: "Rob"}},
+ false,
+ },
+ {
+ "map different key only",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{2: "Ken"}},
+ false,
+ },
+ {
+ "map different value only",
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Ken"}},
+ &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob"}},
+ false,
+ },
+}
+
+func TestEqual(t *testing.T) {
+ for _, tc := range EqualTests {
+ if res := Equal(tc.a, tc.b); res != tc.exp {
+ t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go
new file mode 100644
index 000000000..f7667fab4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions.go
@@ -0,0 +1,353 @@
+// 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"
+ "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 that may be extended.
+type extendableProto interface {
+ Message
+ ExtensionRangeArray() []ExtensionRange
+ ExtensionMap() map[int32]Extension
+}
+
+var extendableProtoType = reflect.TypeOf((*extendableProto)(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
+}
+
+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 extendableProto, id int32, b []byte) {
+ base.ExtensionMap()[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 {
+ // Check the extended type.
+ if a, b := reflect.TypeOf(pb), 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
+}
+
+// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
+func encodeExtensionMap(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 sizeExtensionMap(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 extendableProto, extension *ExtensionDesc) bool {
+ // TODO: Check types, field numbers, etc.?
+ _, ok := pb.ExtensionMap()[extension.Field]
+ return ok
+}
+
+// ClearExtension removes the given extension from pb.
+func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
+ // TODO: Check types, field numbers, etc.?
+ delete(pb.ExtensionMap(), extension.Field)
+}
+
+// GetExtension parses and returns the given extension of pb.
+// If the extension is not present it returns ErrMissingExtension.
+func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
+ if err := checkExtensionTypes(pb, extension); err != nil {
+ return nil, err
+ }
+
+ emap := pb.ExtensionMap()
+ e, ok := emap[extension.Field]
+ if !ok {
+ return nil, ErrMissingExtension
+ }
+ 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
+}
+
+// decodeExtension decodes an extension encoded in b.
+func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
+ o := NewBuffer(b)
+
+ t := reflect.TypeOf(extension.ExtensionType)
+ rep := extension.repeated()
+
+ 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 !rep || 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 := pb.(extendableProto)
+ if !ok {
+ err = errors.New("proto: not an extendable proto")
+ return
+ }
+ 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
+}
+
+// SetExtension sets the specified extension of pb to the specified value.
+func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
+ if err := checkExtensionTypes(pb, extension); err != nil {
+ return err
+ }
+ typ := reflect.TypeOf(extension.ExtensionType)
+ if typ != reflect.TypeOf(value) {
+ return errors.New("proto: bad extension value type")
+ }
+
+ pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
+ return nil
+}
+
+// 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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go
new file mode 100644
index 000000000..fcbd0e052
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/extensions_test.go
@@ -0,0 +1,137 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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_test
+
+import (
+ "testing"
+
+ pb "./testdata"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+func TestGetExtensionsWithMissingExtensions(t *testing.T) {
+ msg := &pb.MyMessage{}
+ ext1 := &pb.Ext{}
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
+ t.Fatalf("Could not set ext1: %s", ext1)
+ }
+ exts, err := proto.GetExtensions(msg, []*proto.ExtensionDesc{
+ pb.E_Ext_More,
+ pb.E_Ext_Text,
+ })
+ if err != nil {
+ t.Fatalf("GetExtensions() failed: %s", err)
+ }
+ if exts[0] != ext1 {
+ t.Errorf("ext1 not in returned extensions: %T %v", exts[0], exts[0])
+ }
+ if exts[1] != nil {
+ t.Errorf("ext2 in returned extensions: %T %v", exts[1], exts[1])
+ }
+}
+
+func TestGetExtensionStability(t *testing.T) {
+ check := func(m *pb.MyMessage) bool {
+ ext1, err := proto.GetExtension(m, pb.E_Ext_More)
+ if err != nil {
+ t.Fatalf("GetExtension() failed: %s", err)
+ }
+ ext2, err := proto.GetExtension(m, pb.E_Ext_More)
+ if err != nil {
+ t.Fatalf("GetExtension() failed: %s", err)
+ }
+ return ext1 == ext2
+ }
+ msg := &pb.MyMessage{Count: proto.Int32(4)}
+ ext0 := &pb.Ext{}
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext0); err != nil {
+ t.Fatalf("Could not set ext1: %s", ext0)
+ }
+ if !check(msg) {
+ t.Errorf("GetExtension() not stable before marshaling")
+ }
+ bb, err := proto.Marshal(msg)
+ if err != nil {
+ t.Fatalf("Marshal() failed: %s", err)
+ }
+ msg1 := &pb.MyMessage{}
+ err = proto.Unmarshal(bb, msg1)
+ if err != nil {
+ t.Fatalf("Unmarshal() failed: %s", err)
+ }
+ if !check(msg1) {
+ t.Errorf("GetExtension() not stable after unmarshaling")
+ }
+}
+
+func TestExtensionsRoundTrip(t *testing.T) {
+ msg := &pb.MyMessage{}
+ ext1 := &pb.Ext{
+ Data: proto.String("hi"),
+ }
+ ext2 := &pb.Ext{
+ Data: proto.String("there"),
+ }
+ exists := proto.HasExtension(msg, pb.E_Ext_More)
+ if exists {
+ t.Error("Extension More present unexpectedly")
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext1); err != nil {
+ t.Error(err)
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext2); err != nil {
+ t.Error(err)
+ }
+ e, err := proto.GetExtension(msg, pb.E_Ext_More)
+ if err != nil {
+ t.Error(err)
+ }
+ x, ok := e.(*pb.Ext)
+ if !ok {
+ t.Errorf("e has type %T, expected testdata.Ext", e)
+ } else if *x.Data != "there" {
+ t.Errorf("SetExtension failed to overwrite, got %+v, not 'there'", x)
+ }
+ proto.ClearExtension(msg, pb.E_Ext_More)
+ if _, err = proto.GetExtension(msg, pb.E_Ext_More); err != proto.ErrMissingExtension {
+ t.Errorf("got %v, expected ErrMissingExtension", e)
+ }
+ if _, err := proto.GetExtension(msg, pb.E_X215); err == nil {
+ t.Error("expected bad extension error, got nil")
+ }
+ if err := proto.SetExtension(msg, pb.E_X215, 12); err == nil {
+ t.Error("expected extension err")
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, 12); err == nil {
+ t.Error("expected some sort of type mismatch error, got nil")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go
new file mode 100644
index 000000000..f81052f2a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/lib.go
@@ -0,0 +1,759 @@
+// 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.
+ - Marshal and Unmarshal are functions to encode and decode the wire format.
+
+ 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;
+ }
+ }
+
+ 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"`
+ XXX_unrecognized []byte `json:"-"`
+ }
+ func (m *Test) Reset() { *m = Test{} }
+ func (m *Test) String() string { return proto.CompactTextString(m) }
+ func (*Test) ProtoMessage() {}
+ 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 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),
+ Optionalgroup: &pb.Test_OptionalGroup{
+ RequiredField: proto.String("good bye"),
+ },
+ }
+ 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())
+ }
+ // etc.
+ }
+*/
+package proto
+
+import (
+ "encoding/json"
+ "fmt"
+ "log"
+ "reflect"
+ "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 // write 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 (o *Buffer) DebugPrint(s string, b []byte) {
+ var u uint64
+
+ obuf := o.buf
+ index := o.index
+ o.buf = b
+ o.index = 0
+ depth := 0
+
+ fmt.Printf("\n--- %s ---\n", s)
+
+out:
+ for {
+ for i := 0; i < depth; i++ {
+ fmt.Print(" ")
+ }
+
+ index := o.index
+ if index == len(o.buf) {
+ break
+ }
+
+ op, err := o.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 = o.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 = o.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 = o.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)
+ break
+
+ case WireVarint:
+ u, err = o.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:
+ if err != nil {
+ fmt.Printf("%3d: t=%3d start err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d start\n", index, tag)
+ depth++
+
+ case WireEndGroup:
+ depth--
+ if err != nil {
+ fmt.Printf("%3d: t=%3d end err %v\n", index, tag, err)
+ break out
+ }
+ fmt.Printf("%3d: t=%3d end\n", index, tag)
+ }
+ }
+
+ if depth != 0 {
+ fmt.Printf("%3d: start-end not balanced %d\n", o.index, depth)
+ }
+ fmt.Printf("\n")
+
+ o.buf = obuf
+ o.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)
+ if f.IsNil() {
+ continue
+ }
+ // f is *T or []*T
+ if f.Kind() == reflect.Ptr {
+ setDefaults(f, recur, zeros)
+ } else {
+ for i := 0; i < f.Len(); i++ {
+ e := f.Index(i)
+ 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
+}
+
+func ptrToStruct(t reflect.Type) bool {
+ return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
+}
+
+// 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
+
+ // nested messages
+ if ptrToStruct(ft) || (ft.Kind() == reflect.Slice && ptrToStruct(ft.Elem())) {
+ dm.nested = append(dm.nested, fi)
+ continue
+ }
+
+ sf := scalarField{
+ index: fi,
+ kind: ft.Elem().Kind(),
+ }
+
+ // scalar fields without defaults
+ if !prop.HasDefault {
+ dm.scalars = append(dm.scalars, sf)
+ continue
+ }
+
+ // a scalar field: either *T or []byte
+ switch ft.Elem().Kind() {
+ case reflect.Bool:
+ x, err := strconv.ParseBool(prop.Default)
+ if err != nil {
+ log.Printf("proto: bad default bool %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = x
+ case reflect.Float32:
+ x, err := strconv.ParseFloat(prop.Default, 32)
+ if err != nil {
+ log.Printf("proto: bad default float32 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = float32(x)
+ case reflect.Float64:
+ x, err := strconv.ParseFloat(prop.Default, 64)
+ if err != nil {
+ log.Printf("proto: bad default float64 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = x
+ case reflect.Int32:
+ x, err := strconv.ParseInt(prop.Default, 10, 32)
+ if err != nil {
+ log.Printf("proto: bad default int32 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = int32(x)
+ case reflect.Int64:
+ x, err := strconv.ParseInt(prop.Default, 10, 64)
+ if err != nil {
+ log.Printf("proto: bad default int64 %q: %v", prop.Default, err)
+ continue
+ }
+ 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 {
+ log.Printf("proto: bad default uint32 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = uint32(x)
+ case reflect.Uint64:
+ x, err := strconv.ParseUint(prop.Default, 10, 64)
+ if err != nil {
+ log.Printf("proto: bad default uint64 %q: %v", prop.Default, err)
+ continue
+ }
+ sf.value = x
+ default:
+ log.Printf("proto: unhandled def kind %v", ft.Elem().Kind())
+ continue
+ }
+
+ dm.scalars = append(dm.scalars, sf)
+ }
+
+ return dm
+}
+
+// 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.
+
+type mapKeys []reflect.Value
+
+func (s mapKeys) Len() int { return len(s) }
+func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s mapKeys) Less(i, j int) bool {
+ return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go
new file mode 100644
index 000000000..9d912bce1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set.go
@@ -0,0 +1,287 @@
+// 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.
+//
+// When a proto1 proto has a field that looks like:
+// optional message info = 3;
+// the protocol compiler produces a field in the generated struct that looks like:
+// Info *_proto_.MessageSet `protobuf:"bytes,3,opt,name=info"`
+// The package is automatically inserted so there is no need for that proto file to
+// import 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(m map[int32]Extension) ([]byte, error) {
+ if err := encodeExtensionMap(m); err != nil {
+ return nil, err
+ }
+
+ // 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, m map[int32]Extension) error {
+ 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(m map[int32]Extension) ([]byte, error) {
+ 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, m map[int32]Extension) 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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go
new file mode 100644
index 000000000..7c29bccf4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/message_set_test.go
@@ -0,0 +1,66 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestUnmarshalMessageSetWithDuplicate(t *testing.T) {
+ // Check that a repeated message set entry will be concatenated.
+ in := &MessageSet{
+ Item: []*_MessageSet_Item{
+ {TypeId: Int32(12345), Message: []byte("hoo")},
+ {TypeId: Int32(12345), Message: []byte("hah")},
+ },
+ }
+ b, err := Marshal(in)
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ t.Logf("Marshaled bytes: %q", b)
+
+ m := make(map[int32]Extension)
+ if err := UnmarshalMessageSet(b, m); err != nil {
+ t.Fatalf("UnmarshalMessageSet: %v", err)
+ }
+ ext, ok := m[12345]
+ if !ok {
+ t.Fatalf("Didn't retrieve extension 12345; map is %v", m)
+ }
+ // Skip wire type/field number and length varints.
+ got := skipVarint(skipVarint(ext.enc))
+ if want := []byte("hoohah"); !bytes.Equal(got, want) {
+ t.Errorf("Combined extension is %q, want %q", got, want)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go
new file mode 100644
index 000000000..c68b12525
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_reflect.go
@@ -0,0 +1,479 @@
+// 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
+
+// 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)
+}
+
+// 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)
+}
+
+// Map returns the reflect.Value for the address of a map field in the struct.
+func structPointer_Map(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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go
new file mode 100644
index 000000000..48bc0fa48
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/pointer_unsafe.go
@@ -0,0 +1,266 @@
+// 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
+
+// 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_ExtMap(p structPointer, f field) *map[int32]Extension {
+ return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
+}
+
+// Map returns the reflect.Value for the address of a map field in the struct.
+func structPointer_Map(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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go
new file mode 100644
index 000000000..730a59579
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/properties.go
@@ -0,0 +1,724 @@
+// 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"
+ "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)
+
+// 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
+}
+
+// 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)
+ 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
+
+ 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"
+ }
+ if p.OrigName != p.Name {
+ s += ",name=" + p.OrigName
+ }
+ if p.proto3 {
+ s += ",proto3"
+ }
+ 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, "enum="):
+ p.Enum = f[5:]
+ case f == "proto3":
+ p.proto3 = 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.enc = (*Buffer).enc_slice_byte
+ p.dec = (*Buffer).dec_slice_byte
+ p.size = size_slice_byte
+ if p.proto3 {
+ p.enc = (*Buffer).enc_proto3_slice_byte
+ p.size = size_proto3_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 (
+ mutex sync.Mutex
+ 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")
+ }
+ mutex.Lock()
+ sprop := getPropertiesLocked(t)
+ mutex.Unlock()
+ return sprop
+}
+
+// getPropertiesLocked requires that mutex 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)
+ 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_extensions" { // special case
+ p.enc = (*Buffer).enc_map
+ p.dec = nil // not needed
+ p.size = size_map
+ }
+ if f.Name == "XXX_unrecognized" { // special case
+ prop.unrecField = toField(&f)
+ }
+ 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_") {
+ fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
+ }
+ }
+
+ // Re-order prop.order.
+ sort.Sort(prop)
+
+ // 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
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile
new file mode 100644
index 000000000..75144b582
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/Makefile
@@ -0,0 +1,44 @@
+# Go support for Protocol Buffers - Google's data interchange format
+#
+# Copyright 2014 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.
+
+
+include ../../Make.protobuf
+
+all: regenerate
+
+regenerate:
+ rm -f proto3.pb.go
+ make proto3.pb.go
+
+# The following rules are just aids to development. Not needed for typical testing.
+
+diff: regenerate
+ git diff proto3.pb.go
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto
new file mode 100644
index 000000000..3e327ded1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_proto/proto3.proto
@@ -0,0 +1,58 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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.
+
+syntax = "proto3";
+
+package proto3_proto;
+
+message Message {
+ enum Humour {
+ UNKNOWN = 0;
+ PUNS = 1;
+ SLAPSTICK = 2;
+ BILL_BAILEY = 3;
+ }
+
+ string name = 1;
+ Humour hilarity = 2;
+ uint32 height_in_cm = 3;
+ bytes data = 4;
+ int64 result_count = 7;
+ bool true_scotsman = 8;
+ float score = 9;
+
+ repeated uint64 key = 5;
+ Nested nested = 6;
+}
+
+message Nested {
+ string bunny = 1;
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go
new file mode 100644
index 000000000..3a5d1857e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/proto3_test.go
@@ -0,0 +1,93 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2014 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_test
+
+import (
+ "testing"
+
+ pb "./proto3_proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+func TestProto3ZeroValues(t *testing.T) {
+ tests := []struct {
+ desc string
+ m proto.Message
+ }{
+ {"zero message", &pb.Message{}},
+ {"empty bytes field", &pb.Message{Data: []byte{}}},
+ }
+ for _, test := range tests {
+ b, err := proto.Marshal(test.m)
+ if err != nil {
+ t.Errorf("%s: proto.Marshal: %v", test.desc, err)
+ continue
+ }
+ if len(b) > 0 {
+ t.Errorf("%s: Encoding is non-empty: %q", test.desc, b)
+ }
+ }
+}
+
+func TestRoundTripProto3(t *testing.T) {
+ m := &pb.Message{
+ Name: "David", // (2 | 1<<3): 0x0a 0x05 "David"
+ Hilarity: pb.Message_PUNS, // (0 | 2<<3): 0x10 0x01
+ HeightInCm: 178, // (0 | 3<<3): 0x18 0xb2 0x01
+ Data: []byte("roboto"), // (2 | 4<<3): 0x20 0x06 "roboto"
+ ResultCount: 47, // (0 | 7<<3): 0x38 0x2f
+ TrueScotsman: true, // (0 | 8<<3): 0x40 0x01
+ Score: 8.1, // (5 | 9<<3): 0x4d <8.1>
+
+ Key: []uint64{1, 0xdeadbeef},
+ Nested: &pb.Nested{
+ Bunny: "Monty",
+ },
+ }
+ t.Logf(" m: %v", m)
+
+ b, err := proto.Marshal(m)
+ if err != nil {
+ t.Fatalf("proto.Marshal: %v", err)
+ }
+ t.Logf(" b: %q", b)
+
+ m2 := new(pb.Message)
+ if err := proto.Unmarshal(b, m2); err != nil {
+ t.Fatalf("proto.Unmarshal: %v", err)
+ }
+ t.Logf("m2: %v", m2)
+
+ if !proto.Equal(m, m2) {
+ t.Errorf("proto.Equal returned false:\n m: %v\nm2: %v", m, m2)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go
new file mode 100644
index 000000000..a2729c39a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/size2_test.go
@@ -0,0 +1,63 @@
+// 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.
+
+package proto
+
+import (
+ "testing"
+)
+
+// This is a separate file and package from size_test.go because that one uses
+// generated messages and thus may not be in package proto without having a circular
+// dependency, whereas this file tests unexported details of size.go.
+
+func TestVarintSize(t *testing.T) {
+ // Check the edge cases carefully.
+ testCases := []struct {
+ n uint64
+ size int
+ }{
+ {0, 1},
+ {1, 1},
+ {127, 1},
+ {128, 2},
+ {16383, 2},
+ {16384, 3},
+ {1<<63 - 1, 9},
+ {1 << 63, 10},
+ }
+ for _, tc := range testCases {
+ size := sizeVarint(tc.n)
+ if size != tc.size {
+ t.Errorf("sizeVarint(%d) = %d, want %d", tc.n, size, tc.size)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go
new file mode 100644
index 000000000..329911c42
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/size_test.go
@@ -0,0 +1,135 @@
+// 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.
+
+package proto_test
+
+import (
+ "log"
+ "testing"
+
+ proto3pb "./proto3_proto"
+ pb "./testdata"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+var messageWithExtension1 = &pb.MyMessage{Count: Int32(7)}
+
+// messageWithExtension2 is in equal_test.go.
+var messageWithExtension3 = &pb.MyMessage{Count: Int32(8)}
+
+func init() {
+ if err := SetExtension(messageWithExtension1, pb.E_Ext_More, &pb.Ext{Data: String("Abbott")}); err != nil {
+ log.Panicf("SetExtension: %v", err)
+ }
+ if err := SetExtension(messageWithExtension3, pb.E_Ext_More, &pb.Ext{Data: String("Costello")}); err != nil {
+ log.Panicf("SetExtension: %v", err)
+ }
+
+ // Force messageWithExtension3 to have the extension encoded.
+ Marshal(messageWithExtension3)
+
+}
+
+var SizeTests = []struct {
+ desc string
+ pb Message
+}{
+ {"empty", &pb.OtherMessage{}},
+ // Basic types.
+ {"bool", &pb.Defaults{F_Bool: Bool(true)}},
+ {"int32", &pb.Defaults{F_Int32: Int32(12)}},
+ {"negative int32", &pb.Defaults{F_Int32: Int32(-1)}},
+ {"small int64", &pb.Defaults{F_Int64: Int64(1)}},
+ {"big int64", &pb.Defaults{F_Int64: Int64(1 << 20)}},
+ {"negative int64", &pb.Defaults{F_Int64: Int64(-1)}},
+ {"fixed32", &pb.Defaults{F_Fixed32: Uint32(71)}},
+ {"fixed64", &pb.Defaults{F_Fixed64: Uint64(72)}},
+ {"uint32", &pb.Defaults{F_Uint32: Uint32(123)}},
+ {"uint64", &pb.Defaults{F_Uint64: Uint64(124)}},
+ {"float", &pb.Defaults{F_Float: Float32(12.6)}},
+ {"double", &pb.Defaults{F_Double: Float64(13.9)}},
+ {"string", &pb.Defaults{F_String: String("niles")}},
+ {"bytes", &pb.Defaults{F_Bytes: []byte("wowsa")}},
+ {"bytes, empty", &pb.Defaults{F_Bytes: []byte{}}},
+ {"sint32", &pb.Defaults{F_Sint32: Int32(65)}},
+ {"sint64", &pb.Defaults{F_Sint64: Int64(67)}},
+ {"enum", &pb.Defaults{F_Enum: pb.Defaults_BLUE.Enum()}},
+ // Repeated.
+ {"empty repeated bool", &pb.MoreRepeated{Bools: []bool{}}},
+ {"repeated bool", &pb.MoreRepeated{Bools: []bool{false, true, true, false}}},
+ {"packed repeated bool", &pb.MoreRepeated{BoolsPacked: []bool{false, true, true, false, true, true, true}}},
+ {"repeated int32", &pb.MoreRepeated{Ints: []int32{1, 12203, 1729, -1}}},
+ {"repeated int32 packed", &pb.MoreRepeated{IntsPacked: []int32{1, 12203, 1729}}},
+ {"repeated int64 packed", &pb.MoreRepeated{Int64SPacked: []int64{
+ // Need enough large numbers to verify that the header is counting the number of bytes
+ // for the field, not the number of elements.
+ 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
+ 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62, 1 << 62,
+ }}},
+ {"repeated string", &pb.MoreRepeated{Strings: []string{"r", "ken", "gri"}}},
+ {"repeated fixed", &pb.MoreRepeated{Fixeds: []uint32{1, 2, 3, 4}}},
+ // Nested.
+ {"nested", &pb.OldMessage{Nested: &pb.OldMessage_Nested{Name: String("whatever")}}},
+ {"group", &pb.GroupOld{G: &pb.GroupOld_G{X: Int32(12345)}}},
+ // Other things.
+ {"unrecognized", &pb.MoreRepeated{XXX_unrecognized: []byte{13<<3 | 0, 4}}},
+ {"extension (unencoded)", messageWithExtension1},
+ {"extension (encoded)", messageWithExtension3},
+ // proto3 message
+ {"proto3 empty", &proto3pb.Message{}},
+ {"proto3 bool", &proto3pb.Message{TrueScotsman: true}},
+ {"proto3 int64", &proto3pb.Message{ResultCount: 1}},
+ {"proto3 uint32", &proto3pb.Message{HeightInCm: 123}},
+ {"proto3 float", &proto3pb.Message{Score: 12.6}},
+ {"proto3 string", &proto3pb.Message{Name: "Snezana"}},
+ {"proto3 bytes", &proto3pb.Message{Data: []byte("wowsa")}},
+ {"proto3 bytes, empty", &proto3pb.Message{Data: []byte{}}},
+ {"proto3 enum", &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
+
+ {"map field", &pb.MessageWithMap{NameMapping: map[int32]string{1: "Rob", 7: "Andrew"}}},
+ {"map field with message", &pb.MessageWithMap{MsgMapping: map[int64]*pb.FloatingPoint{0x7001: &pb.FloatingPoint{F: Float64(2.0)}}}},
+ {"map field with bytes", &pb.MessageWithMap{ByteMapping: map[bool][]byte{true: []byte("this time for sure")}}},
+}
+
+func TestSize(t *testing.T) {
+ for _, tc := range SizeTests {
+ size := Size(tc.pb)
+ b, err := Marshal(tc.pb)
+ if err != nil {
+ t.Errorf("%v: Marshal failed: %v", tc.desc, err)
+ continue
+ }
+ if size != len(b) {
+ t.Errorf("%v: Size(%v) = %d, want %d", tc.desc, tc.pb, size, len(b))
+ t.Logf("%v: bytes: %#v", tc.desc, b)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile
new file mode 100644
index 000000000..fc288628a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/Makefile
@@ -0,0 +1,50 @@
+# 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.
+
+
+include ../../Make.protobuf
+
+all: regenerate
+
+regenerate:
+ rm -f test.pb.go
+ make test.pb.go
+
+# The following rules are just aids to development. Not needed for typical testing.
+
+diff: regenerate
+ git diff test.pb.go
+
+restore:
+ cp test.pb.go.golden test.pb.go
+
+preserve:
+ cp test.pb.go test.pb.go.golden
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go
new file mode 100644
index 000000000..7172d0e96
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/golden_test.go
@@ -0,0 +1,86 @@
+// 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.
+
+// Verify that the compiler output for test.proto is unchanged.
+
+package testdata
+
+import (
+ "crypto/sha1"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+)
+
+// sum returns in string form (for easy comparison) the SHA-1 hash of the named file.
+func sum(t *testing.T, name string) string {
+ data, err := ioutil.ReadFile(name)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Logf("sum(%q): length is %d", name, len(data))
+ hash := sha1.New()
+ _, err = hash.Write(data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return fmt.Sprintf("% x", hash.Sum(nil))
+}
+
+func run(t *testing.T, name string, args ...string) {
+ cmd := exec.Command(name, args...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestGolden(t *testing.T) {
+ // Compute the original checksum.
+ goldenSum := sum(t, "test.pb.go")
+ // Run the proto compiler.
+ run(t, "protoc", "--go_out="+os.TempDir(), "test.proto")
+ newFile := filepath.Join(os.TempDir(), "test.pb.go")
+ defer os.Remove(newFile)
+ // Compute the new checksum.
+ newSum := sum(t, newFile)
+ // Verify
+ if newSum != goldenSum {
+ run(t, "diff", "-u", "test.pb.go", newFile)
+ t.Fatal("Code generated by protoc-gen-go has changed; update test.pb.go")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go
new file mode 100644
index 000000000..da777bf6e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.pb.go
@@ -0,0 +1,2389 @@
+// Code generated by protoc-gen-go.
+// source: test.proto
+// DO NOT EDIT!
+
+/*
+Package testdata is a generated protocol buffer package.
+
+It is generated from these files:
+ test.proto
+
+It has these top-level messages:
+ GoEnum
+ GoTestField
+ GoTest
+ GoSkipTest
+ NonPackedTest
+ PackedTest
+ MaxTag
+ OldMessage
+ NewMessage
+ InnerMessage
+ OtherMessage
+ MyMessage
+ Ext
+ MyMessageSet
+ Empty
+ MessageList
+ Strings
+ Defaults
+ SubDefaults
+ RepeatedEnum
+ MoreRepeated
+ GroupOld
+ GroupNew
+ FloatingPoint
+ MessageWithMap
+*/
+package testdata
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type FOO int32
+
+const (
+ FOO_FOO1 FOO = 1
+)
+
+var FOO_name = map[int32]string{
+ 1: "FOO1",
+}
+var FOO_value = map[string]int32{
+ "FOO1": 1,
+}
+
+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, "FOO")
+ if err != nil {
+ return err
+ }
+ *x = FOO(value)
+ return nil
+}
+
+// An enum, for completeness.
+type GoTest_KIND int32
+
+const (
+ GoTest_VOID GoTest_KIND = 0
+ // Basic types
+ GoTest_BOOL GoTest_KIND = 1
+ GoTest_BYTES GoTest_KIND = 2
+ GoTest_FINGERPRINT GoTest_KIND = 3
+ GoTest_FLOAT GoTest_KIND = 4
+ GoTest_INT GoTest_KIND = 5
+ GoTest_STRING GoTest_KIND = 6
+ GoTest_TIME GoTest_KIND = 7
+ // Groupings
+ GoTest_TUPLE GoTest_KIND = 8
+ GoTest_ARRAY GoTest_KIND = 9
+ GoTest_MAP GoTest_KIND = 10
+ // Table types
+ GoTest_TABLE GoTest_KIND = 11
+ // Functions
+ GoTest_FUNCTION GoTest_KIND = 12
+)
+
+var GoTest_KIND_name = map[int32]string{
+ 0: "VOID",
+ 1: "BOOL",
+ 2: "BYTES",
+ 3: "FINGERPRINT",
+ 4: "FLOAT",
+ 5: "INT",
+ 6: "STRING",
+ 7: "TIME",
+ 8: "TUPLE",
+ 9: "ARRAY",
+ 10: "MAP",
+ 11: "TABLE",
+ 12: "FUNCTION",
+}
+var GoTest_KIND_value = map[string]int32{
+ "VOID": 0,
+ "BOOL": 1,
+ "BYTES": 2,
+ "FINGERPRINT": 3,
+ "FLOAT": 4,
+ "INT": 5,
+ "STRING": 6,
+ "TIME": 7,
+ "TUPLE": 8,
+ "ARRAY": 9,
+ "MAP": 10,
+ "TABLE": 11,
+ "FUNCTION": 12,
+}
+
+func (x GoTest_KIND) Enum() *GoTest_KIND {
+ p := new(GoTest_KIND)
+ *p = x
+ return p
+}
+func (x GoTest_KIND) String() string {
+ return proto.EnumName(GoTest_KIND_name, int32(x))
+}
+func (x *GoTest_KIND) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(GoTest_KIND_value, data, "GoTest_KIND")
+ if err != nil {
+ return err
+ }
+ *x = GoTest_KIND(value)
+ return nil
+}
+
+type MyMessage_Color int32
+
+const (
+ MyMessage_RED MyMessage_Color = 0
+ MyMessage_GREEN MyMessage_Color = 1
+ MyMessage_BLUE MyMessage_Color = 2
+)
+
+var MyMessage_Color_name = map[int32]string{
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
+}
+var MyMessage_Color_value = map[string]int32{
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
+}
+
+func (x MyMessage_Color) Enum() *MyMessage_Color {
+ p := new(MyMessage_Color)
+ *p = x
+ return p
+}
+func (x MyMessage_Color) String() string {
+ return proto.EnumName(MyMessage_Color_name, int32(x))
+}
+func (x *MyMessage_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MyMessage_Color_value, data, "MyMessage_Color")
+ if err != nil {
+ return err
+ }
+ *x = MyMessage_Color(value)
+ return nil
+}
+
+type Defaults_Color int32
+
+const (
+ Defaults_RED Defaults_Color = 0
+ Defaults_GREEN Defaults_Color = 1
+ Defaults_BLUE Defaults_Color = 2
+)
+
+var Defaults_Color_name = map[int32]string{
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
+}
+var Defaults_Color_value = map[string]int32{
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
+}
+
+func (x Defaults_Color) Enum() *Defaults_Color {
+ p := new(Defaults_Color)
+ *p = x
+ return p
+}
+func (x Defaults_Color) String() string {
+ return proto.EnumName(Defaults_Color_name, int32(x))
+}
+func (x *Defaults_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(Defaults_Color_value, data, "Defaults_Color")
+ if err != nil {
+ return err
+ }
+ *x = Defaults_Color(value)
+ return nil
+}
+
+type RepeatedEnum_Color int32
+
+const (
+ RepeatedEnum_RED RepeatedEnum_Color = 1
+)
+
+var RepeatedEnum_Color_name = map[int32]string{
+ 1: "RED",
+}
+var RepeatedEnum_Color_value = map[string]int32{
+ "RED": 1,
+}
+
+func (x RepeatedEnum_Color) Enum() *RepeatedEnum_Color {
+ p := new(RepeatedEnum_Color)
+ *p = x
+ return p
+}
+func (x RepeatedEnum_Color) String() string {
+ return proto.EnumName(RepeatedEnum_Color_name, int32(x))
+}
+func (x *RepeatedEnum_Color) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(RepeatedEnum_Color_value, data, "RepeatedEnum_Color")
+ if err != nil {
+ return err
+ }
+ *x = RepeatedEnum_Color(value)
+ return nil
+}
+
+type GoEnum struct {
+ Foo *FOO `protobuf:"varint,1,req,name=foo,enum=testdata.FOO" json:"foo,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoEnum) Reset() { *m = GoEnum{} }
+func (m *GoEnum) String() string { return proto.CompactTextString(m) }
+func (*GoEnum) ProtoMessage() {}
+
+func (m *GoEnum) GetFoo() FOO {
+ if m != nil && m.Foo != nil {
+ return *m.Foo
+ }
+ return FOO_FOO1
+}
+
+type GoTestField struct {
+ Label *string `protobuf:"bytes,1,req" json:"Label,omitempty"`
+ Type *string `protobuf:"bytes,2,req" json:"Type,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTestField) Reset() { *m = GoTestField{} }
+func (m *GoTestField) String() string { return proto.CompactTextString(m) }
+func (*GoTestField) ProtoMessage() {}
+
+func (m *GoTestField) GetLabel() string {
+ if m != nil && m.Label != nil {
+ return *m.Label
+ }
+ return ""
+}
+
+func (m *GoTestField) GetType() string {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return ""
+}
+
+type GoTest struct {
+ // Some typical parameters
+ Kind *GoTest_KIND `protobuf:"varint,1,req,enum=testdata.GoTest_KIND" json:"Kind,omitempty"`
+ Table *string `protobuf:"bytes,2,opt" json:"Table,omitempty"`
+ Param *int32 `protobuf:"varint,3,opt" json:"Param,omitempty"`
+ // Required, repeated and optional foreign fields.
+ RequiredField *GoTestField `protobuf:"bytes,4,req" json:"RequiredField,omitempty"`
+ RepeatedField []*GoTestField `protobuf:"bytes,5,rep" json:"RepeatedField,omitempty"`
+ OptionalField *GoTestField `protobuf:"bytes,6,opt" json:"OptionalField,omitempty"`
+ // Required fields of all basic types
+ F_BoolRequired *bool `protobuf:"varint,10,req,name=F_Bool_required" json:"F_Bool_required,omitempty"`
+ F_Int32Required *int32 `protobuf:"varint,11,req,name=F_Int32_required" json:"F_Int32_required,omitempty"`
+ F_Int64Required *int64 `protobuf:"varint,12,req,name=F_Int64_required" json:"F_Int64_required,omitempty"`
+ F_Fixed32Required *uint32 `protobuf:"fixed32,13,req,name=F_Fixed32_required" json:"F_Fixed32_required,omitempty"`
+ F_Fixed64Required *uint64 `protobuf:"fixed64,14,req,name=F_Fixed64_required" json:"F_Fixed64_required,omitempty"`
+ F_Uint32Required *uint32 `protobuf:"varint,15,req,name=F_Uint32_required" json:"F_Uint32_required,omitempty"`
+ F_Uint64Required *uint64 `protobuf:"varint,16,req,name=F_Uint64_required" json:"F_Uint64_required,omitempty"`
+ F_FloatRequired *float32 `protobuf:"fixed32,17,req,name=F_Float_required" json:"F_Float_required,omitempty"`
+ F_DoubleRequired *float64 `protobuf:"fixed64,18,req,name=F_Double_required" json:"F_Double_required,omitempty"`
+ F_StringRequired *string `protobuf:"bytes,19,req,name=F_String_required" json:"F_String_required,omitempty"`
+ F_BytesRequired []byte `protobuf:"bytes,101,req,name=F_Bytes_required" json:"F_Bytes_required,omitempty"`
+ F_Sint32Required *int32 `protobuf:"zigzag32,102,req,name=F_Sint32_required" json:"F_Sint32_required,omitempty"`
+ F_Sint64Required *int64 `protobuf:"zigzag64,103,req,name=F_Sint64_required" json:"F_Sint64_required,omitempty"`
+ // Repeated fields of all basic types
+ F_BoolRepeated []bool `protobuf:"varint,20,rep,name=F_Bool_repeated" json:"F_Bool_repeated,omitempty"`
+ F_Int32Repeated []int32 `protobuf:"varint,21,rep,name=F_Int32_repeated" json:"F_Int32_repeated,omitempty"`
+ F_Int64Repeated []int64 `protobuf:"varint,22,rep,name=F_Int64_repeated" json:"F_Int64_repeated,omitempty"`
+ F_Fixed32Repeated []uint32 `protobuf:"fixed32,23,rep,name=F_Fixed32_repeated" json:"F_Fixed32_repeated,omitempty"`
+ F_Fixed64Repeated []uint64 `protobuf:"fixed64,24,rep,name=F_Fixed64_repeated" json:"F_Fixed64_repeated,omitempty"`
+ F_Uint32Repeated []uint32 `protobuf:"varint,25,rep,name=F_Uint32_repeated" json:"F_Uint32_repeated,omitempty"`
+ F_Uint64Repeated []uint64 `protobuf:"varint,26,rep,name=F_Uint64_repeated" json:"F_Uint64_repeated,omitempty"`
+ F_FloatRepeated []float32 `protobuf:"fixed32,27,rep,name=F_Float_repeated" json:"F_Float_repeated,omitempty"`
+ F_DoubleRepeated []float64 `protobuf:"fixed64,28,rep,name=F_Double_repeated" json:"F_Double_repeated,omitempty"`
+ F_StringRepeated []string `protobuf:"bytes,29,rep,name=F_String_repeated" json:"F_String_repeated,omitempty"`
+ F_BytesRepeated [][]byte `protobuf:"bytes,201,rep,name=F_Bytes_repeated" json:"F_Bytes_repeated,omitempty"`
+ F_Sint32Repeated []int32 `protobuf:"zigzag32,202,rep,name=F_Sint32_repeated" json:"F_Sint32_repeated,omitempty"`
+ F_Sint64Repeated []int64 `protobuf:"zigzag64,203,rep,name=F_Sint64_repeated" json:"F_Sint64_repeated,omitempty"`
+ // Optional fields of all basic types
+ F_BoolOptional *bool `protobuf:"varint,30,opt,name=F_Bool_optional" json:"F_Bool_optional,omitempty"`
+ F_Int32Optional *int32 `protobuf:"varint,31,opt,name=F_Int32_optional" json:"F_Int32_optional,omitempty"`
+ F_Int64Optional *int64 `protobuf:"varint,32,opt,name=F_Int64_optional" json:"F_Int64_optional,omitempty"`
+ F_Fixed32Optional *uint32 `protobuf:"fixed32,33,opt,name=F_Fixed32_optional" json:"F_Fixed32_optional,omitempty"`
+ F_Fixed64Optional *uint64 `protobuf:"fixed64,34,opt,name=F_Fixed64_optional" json:"F_Fixed64_optional,omitempty"`
+ F_Uint32Optional *uint32 `protobuf:"varint,35,opt,name=F_Uint32_optional" json:"F_Uint32_optional,omitempty"`
+ F_Uint64Optional *uint64 `protobuf:"varint,36,opt,name=F_Uint64_optional" json:"F_Uint64_optional,omitempty"`
+ F_FloatOptional *float32 `protobuf:"fixed32,37,opt,name=F_Float_optional" json:"F_Float_optional,omitempty"`
+ F_DoubleOptional *float64 `protobuf:"fixed64,38,opt,name=F_Double_optional" json:"F_Double_optional,omitempty"`
+ F_StringOptional *string `protobuf:"bytes,39,opt,name=F_String_optional" json:"F_String_optional,omitempty"`
+ F_BytesOptional []byte `protobuf:"bytes,301,opt,name=F_Bytes_optional" json:"F_Bytes_optional,omitempty"`
+ F_Sint32Optional *int32 `protobuf:"zigzag32,302,opt,name=F_Sint32_optional" json:"F_Sint32_optional,omitempty"`
+ F_Sint64Optional *int64 `protobuf:"zigzag64,303,opt,name=F_Sint64_optional" json:"F_Sint64_optional,omitempty"`
+ // Default-valued fields of all basic types
+ F_BoolDefaulted *bool `protobuf:"varint,40,opt,name=F_Bool_defaulted,def=1" json:"F_Bool_defaulted,omitempty"`
+ F_Int32Defaulted *int32 `protobuf:"varint,41,opt,name=F_Int32_defaulted,def=32" json:"F_Int32_defaulted,omitempty"`
+ F_Int64Defaulted *int64 `protobuf:"varint,42,opt,name=F_Int64_defaulted,def=64" json:"F_Int64_defaulted,omitempty"`
+ F_Fixed32Defaulted *uint32 `protobuf:"fixed32,43,opt,name=F_Fixed32_defaulted,def=320" json:"F_Fixed32_defaulted,omitempty"`
+ F_Fixed64Defaulted *uint64 `protobuf:"fixed64,44,opt,name=F_Fixed64_defaulted,def=640" json:"F_Fixed64_defaulted,omitempty"`
+ F_Uint32Defaulted *uint32 `protobuf:"varint,45,opt,name=F_Uint32_defaulted,def=3200" json:"F_Uint32_defaulted,omitempty"`
+ F_Uint64Defaulted *uint64 `protobuf:"varint,46,opt,name=F_Uint64_defaulted,def=6400" json:"F_Uint64_defaulted,omitempty"`
+ F_FloatDefaulted *float32 `protobuf:"fixed32,47,opt,name=F_Float_defaulted,def=314159" json:"F_Float_defaulted,omitempty"`
+ F_DoubleDefaulted *float64 `protobuf:"fixed64,48,opt,name=F_Double_defaulted,def=271828" json:"F_Double_defaulted,omitempty"`
+ F_StringDefaulted *string `protobuf:"bytes,49,opt,name=F_String_defaulted,def=hello, \"world!\"\n" json:"F_String_defaulted,omitempty"`
+ F_BytesDefaulted []byte `protobuf:"bytes,401,opt,name=F_Bytes_defaulted,def=Bignose" json:"F_Bytes_defaulted,omitempty"`
+ F_Sint32Defaulted *int32 `protobuf:"zigzag32,402,opt,name=F_Sint32_defaulted,def=-32" json:"F_Sint32_defaulted,omitempty"`
+ F_Sint64Defaulted *int64 `protobuf:"zigzag64,403,opt,name=F_Sint64_defaulted,def=-64" json:"F_Sint64_defaulted,omitempty"`
+ // Packed repeated fields (no string or bytes).
+ F_BoolRepeatedPacked []bool `protobuf:"varint,50,rep,packed,name=F_Bool_repeated_packed" json:"F_Bool_repeated_packed,omitempty"`
+ F_Int32RepeatedPacked []int32 `protobuf:"varint,51,rep,packed,name=F_Int32_repeated_packed" json:"F_Int32_repeated_packed,omitempty"`
+ F_Int64RepeatedPacked []int64 `protobuf:"varint,52,rep,packed,name=F_Int64_repeated_packed" json:"F_Int64_repeated_packed,omitempty"`
+ F_Fixed32RepeatedPacked []uint32 `protobuf:"fixed32,53,rep,packed,name=F_Fixed32_repeated_packed" json:"F_Fixed32_repeated_packed,omitempty"`
+ F_Fixed64RepeatedPacked []uint64 `protobuf:"fixed64,54,rep,packed,name=F_Fixed64_repeated_packed" json:"F_Fixed64_repeated_packed,omitempty"`
+ F_Uint32RepeatedPacked []uint32 `protobuf:"varint,55,rep,packed,name=F_Uint32_repeated_packed" json:"F_Uint32_repeated_packed,omitempty"`
+ F_Uint64RepeatedPacked []uint64 `protobuf:"varint,56,rep,packed,name=F_Uint64_repeated_packed" json:"F_Uint64_repeated_packed,omitempty"`
+ F_FloatRepeatedPacked []float32 `protobuf:"fixed32,57,rep,packed,name=F_Float_repeated_packed" json:"F_Float_repeated_packed,omitempty"`
+ F_DoubleRepeatedPacked []float64 `protobuf:"fixed64,58,rep,packed,name=F_Double_repeated_packed" json:"F_Double_repeated_packed,omitempty"`
+ F_Sint32RepeatedPacked []int32 `protobuf:"zigzag32,502,rep,packed,name=F_Sint32_repeated_packed" json:"F_Sint32_repeated_packed,omitempty"`
+ F_Sint64RepeatedPacked []int64 `protobuf:"zigzag64,503,rep,packed,name=F_Sint64_repeated_packed" json:"F_Sint64_repeated_packed,omitempty"`
+ Requiredgroup *GoTest_RequiredGroup `protobuf:"group,70,req,name=RequiredGroup" json:"requiredgroup,omitempty"`
+ Repeatedgroup []*GoTest_RepeatedGroup `protobuf:"group,80,rep,name=RepeatedGroup" json:"repeatedgroup,omitempty"`
+ Optionalgroup *GoTest_OptionalGroup `protobuf:"group,90,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest) Reset() { *m = GoTest{} }
+func (m *GoTest) String() string { return proto.CompactTextString(m) }
+func (*GoTest) ProtoMessage() {}
+
+const Default_GoTest_F_BoolDefaulted bool = true
+const Default_GoTest_F_Int32Defaulted int32 = 32
+const Default_GoTest_F_Int64Defaulted int64 = 64
+const Default_GoTest_F_Fixed32Defaulted uint32 = 320
+const Default_GoTest_F_Fixed64Defaulted uint64 = 640
+const Default_GoTest_F_Uint32Defaulted uint32 = 3200
+const Default_GoTest_F_Uint64Defaulted uint64 = 6400
+const Default_GoTest_F_FloatDefaulted float32 = 314159
+const Default_GoTest_F_DoubleDefaulted float64 = 271828
+const Default_GoTest_F_StringDefaulted string = "hello, \"world!\"\n"
+
+var Default_GoTest_F_BytesDefaulted []byte = []byte("Bignose")
+
+const Default_GoTest_F_Sint32Defaulted int32 = -32
+const Default_GoTest_F_Sint64Defaulted int64 = -64
+
+func (m *GoTest) GetKind() GoTest_KIND {
+ if m != nil && m.Kind != nil {
+ return *m.Kind
+ }
+ return GoTest_VOID
+}
+
+func (m *GoTest) GetTable() string {
+ if m != nil && m.Table != nil {
+ return *m.Table
+ }
+ return ""
+}
+
+func (m *GoTest) GetParam() int32 {
+ if m != nil && m.Param != nil {
+ return *m.Param
+ }
+ return 0
+}
+
+func (m *GoTest) GetRequiredField() *GoTestField {
+ if m != nil {
+ return m.RequiredField
+ }
+ return nil
+}
+
+func (m *GoTest) GetRepeatedField() []*GoTestField {
+ if m != nil {
+ return m.RepeatedField
+ }
+ return nil
+}
+
+func (m *GoTest) GetOptionalField() *GoTestField {
+ if m != nil {
+ return m.OptionalField
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BoolRequired() bool {
+ if m != nil && m.F_BoolRequired != nil {
+ return *m.F_BoolRequired
+ }
+ return false
+}
+
+func (m *GoTest) GetF_Int32Required() int32 {
+ if m != nil && m.F_Int32Required != nil {
+ return *m.F_Int32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Int64Required() int64 {
+ if m != nil && m.F_Int64Required != nil {
+ return *m.F_Int64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed32Required() uint32 {
+ if m != nil && m.F_Fixed32Required != nil {
+ return *m.F_Fixed32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed64Required() uint64 {
+ if m != nil && m.F_Fixed64Required != nil {
+ return *m.F_Fixed64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint32Required() uint32 {
+ if m != nil && m.F_Uint32Required != nil {
+ return *m.F_Uint32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint64Required() uint64 {
+ if m != nil && m.F_Uint64Required != nil {
+ return *m.F_Uint64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_FloatRequired() float32 {
+ if m != nil && m.F_FloatRequired != nil {
+ return *m.F_FloatRequired
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_DoubleRequired() float64 {
+ if m != nil && m.F_DoubleRequired != nil {
+ return *m.F_DoubleRequired
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_StringRequired() string {
+ if m != nil && m.F_StringRequired != nil {
+ return *m.F_StringRequired
+ }
+ return ""
+}
+
+func (m *GoTest) GetF_BytesRequired() []byte {
+ if m != nil {
+ return m.F_BytesRequired
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Required() int32 {
+ if m != nil && m.F_Sint32Required != nil {
+ return *m.F_Sint32Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Sint64Required() int64 {
+ if m != nil && m.F_Sint64Required != nil {
+ return *m.F_Sint64Required
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_BoolRepeated() []bool {
+ if m != nil {
+ return m.F_BoolRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int32Repeated() []int32 {
+ if m != nil {
+ return m.F_Int32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int64Repeated() []int64 {
+ if m != nil {
+ return m.F_Int64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed32Repeated() []uint32 {
+ if m != nil {
+ return m.F_Fixed32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed64Repeated() []uint64 {
+ if m != nil {
+ return m.F_Fixed64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint32Repeated() []uint32 {
+ if m != nil {
+ return m.F_Uint32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint64Repeated() []uint64 {
+ if m != nil {
+ return m.F_Uint64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_FloatRepeated() []float32 {
+ if m != nil {
+ return m.F_FloatRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_DoubleRepeated() []float64 {
+ if m != nil {
+ return m.F_DoubleRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_StringRepeated() []string {
+ if m != nil {
+ return m.F_StringRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BytesRepeated() [][]byte {
+ if m != nil {
+ return m.F_BytesRepeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Repeated() []int32 {
+ if m != nil {
+ return m.F_Sint32Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint64Repeated() []int64 {
+ if m != nil {
+ return m.F_Sint64Repeated
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_BoolOptional() bool {
+ if m != nil && m.F_BoolOptional != nil {
+ return *m.F_BoolOptional
+ }
+ return false
+}
+
+func (m *GoTest) GetF_Int32Optional() int32 {
+ if m != nil && m.F_Int32Optional != nil {
+ return *m.F_Int32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Int64Optional() int64 {
+ if m != nil && m.F_Int64Optional != nil {
+ return *m.F_Int64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed32Optional() uint32 {
+ if m != nil && m.F_Fixed32Optional != nil {
+ return *m.F_Fixed32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Fixed64Optional() uint64 {
+ if m != nil && m.F_Fixed64Optional != nil {
+ return *m.F_Fixed64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint32Optional() uint32 {
+ if m != nil && m.F_Uint32Optional != nil {
+ return *m.F_Uint32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Uint64Optional() uint64 {
+ if m != nil && m.F_Uint64Optional != nil {
+ return *m.F_Uint64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_FloatOptional() float32 {
+ if m != nil && m.F_FloatOptional != nil {
+ return *m.F_FloatOptional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_DoubleOptional() float64 {
+ if m != nil && m.F_DoubleOptional != nil {
+ return *m.F_DoubleOptional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_StringOptional() string {
+ if m != nil && m.F_StringOptional != nil {
+ return *m.F_StringOptional
+ }
+ return ""
+}
+
+func (m *GoTest) GetF_BytesOptional() []byte {
+ if m != nil {
+ return m.F_BytesOptional
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32Optional() int32 {
+ if m != nil && m.F_Sint32Optional != nil {
+ return *m.F_Sint32Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_Sint64Optional() int64 {
+ if m != nil && m.F_Sint64Optional != nil {
+ return *m.F_Sint64Optional
+ }
+ return 0
+}
+
+func (m *GoTest) GetF_BoolDefaulted() bool {
+ if m != nil && m.F_BoolDefaulted != nil {
+ return *m.F_BoolDefaulted
+ }
+ return Default_GoTest_F_BoolDefaulted
+}
+
+func (m *GoTest) GetF_Int32Defaulted() int32 {
+ if m != nil && m.F_Int32Defaulted != nil {
+ return *m.F_Int32Defaulted
+ }
+ return Default_GoTest_F_Int32Defaulted
+}
+
+func (m *GoTest) GetF_Int64Defaulted() int64 {
+ if m != nil && m.F_Int64Defaulted != nil {
+ return *m.F_Int64Defaulted
+ }
+ return Default_GoTest_F_Int64Defaulted
+}
+
+func (m *GoTest) GetF_Fixed32Defaulted() uint32 {
+ if m != nil && m.F_Fixed32Defaulted != nil {
+ return *m.F_Fixed32Defaulted
+ }
+ return Default_GoTest_F_Fixed32Defaulted
+}
+
+func (m *GoTest) GetF_Fixed64Defaulted() uint64 {
+ if m != nil && m.F_Fixed64Defaulted != nil {
+ return *m.F_Fixed64Defaulted
+ }
+ return Default_GoTest_F_Fixed64Defaulted
+}
+
+func (m *GoTest) GetF_Uint32Defaulted() uint32 {
+ if m != nil && m.F_Uint32Defaulted != nil {
+ return *m.F_Uint32Defaulted
+ }
+ return Default_GoTest_F_Uint32Defaulted
+}
+
+func (m *GoTest) GetF_Uint64Defaulted() uint64 {
+ if m != nil && m.F_Uint64Defaulted != nil {
+ return *m.F_Uint64Defaulted
+ }
+ return Default_GoTest_F_Uint64Defaulted
+}
+
+func (m *GoTest) GetF_FloatDefaulted() float32 {
+ if m != nil && m.F_FloatDefaulted != nil {
+ return *m.F_FloatDefaulted
+ }
+ return Default_GoTest_F_FloatDefaulted
+}
+
+func (m *GoTest) GetF_DoubleDefaulted() float64 {
+ if m != nil && m.F_DoubleDefaulted != nil {
+ return *m.F_DoubleDefaulted
+ }
+ return Default_GoTest_F_DoubleDefaulted
+}
+
+func (m *GoTest) GetF_StringDefaulted() string {
+ if m != nil && m.F_StringDefaulted != nil {
+ return *m.F_StringDefaulted
+ }
+ return Default_GoTest_F_StringDefaulted
+}
+
+func (m *GoTest) GetF_BytesDefaulted() []byte {
+ if m != nil && m.F_BytesDefaulted != nil {
+ return m.F_BytesDefaulted
+ }
+ return append([]byte(nil), Default_GoTest_F_BytesDefaulted...)
+}
+
+func (m *GoTest) GetF_Sint32Defaulted() int32 {
+ if m != nil && m.F_Sint32Defaulted != nil {
+ return *m.F_Sint32Defaulted
+ }
+ return Default_GoTest_F_Sint32Defaulted
+}
+
+func (m *GoTest) GetF_Sint64Defaulted() int64 {
+ if m != nil && m.F_Sint64Defaulted != nil {
+ return *m.F_Sint64Defaulted
+ }
+ return Default_GoTest_F_Sint64Defaulted
+}
+
+func (m *GoTest) GetF_BoolRepeatedPacked() []bool {
+ if m != nil {
+ return m.F_BoolRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int32RepeatedPacked() []int32 {
+ if m != nil {
+ return m.F_Int32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Int64RepeatedPacked() []int64 {
+ if m != nil {
+ return m.F_Int64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed32RepeatedPacked() []uint32 {
+ if m != nil {
+ return m.F_Fixed32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Fixed64RepeatedPacked() []uint64 {
+ if m != nil {
+ return m.F_Fixed64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint32RepeatedPacked() []uint32 {
+ if m != nil {
+ return m.F_Uint32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Uint64RepeatedPacked() []uint64 {
+ if m != nil {
+ return m.F_Uint64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_FloatRepeatedPacked() []float32 {
+ if m != nil {
+ return m.F_FloatRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_DoubleRepeatedPacked() []float64 {
+ if m != nil {
+ return m.F_DoubleRepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint32RepeatedPacked() []int32 {
+ if m != nil {
+ return m.F_Sint32RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetF_Sint64RepeatedPacked() []int64 {
+ if m != nil {
+ return m.F_Sint64RepeatedPacked
+ }
+ return nil
+}
+
+func (m *GoTest) GetRequiredgroup() *GoTest_RequiredGroup {
+ if m != nil {
+ return m.Requiredgroup
+ }
+ return nil
+}
+
+func (m *GoTest) GetRepeatedgroup() []*GoTest_RepeatedGroup {
+ if m != nil {
+ return m.Repeatedgroup
+ }
+ return nil
+}
+
+func (m *GoTest) GetOptionalgroup() *GoTest_OptionalGroup {
+ if m != nil {
+ return m.Optionalgroup
+ }
+ return nil
+}
+
+// Required, repeated, and optional groups.
+type GoTest_RequiredGroup struct {
+ RequiredField *string `protobuf:"bytes,71,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_RequiredGroup) Reset() { *m = GoTest_RequiredGroup{} }
+func (m *GoTest_RequiredGroup) String() string { return proto.CompactTextString(m) }
+func (*GoTest_RequiredGroup) ProtoMessage() {}
+
+func (m *GoTest_RequiredGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoTest_RepeatedGroup struct {
+ RequiredField *string `protobuf:"bytes,81,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_RepeatedGroup) Reset() { *m = GoTest_RepeatedGroup{} }
+func (m *GoTest_RepeatedGroup) String() string { return proto.CompactTextString(m) }
+func (*GoTest_RepeatedGroup) ProtoMessage() {}
+
+func (m *GoTest_RepeatedGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+type GoTest_OptionalGroup struct {
+ RequiredField *string `protobuf:"bytes,91,req" json:"RequiredField,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoTest_OptionalGroup) Reset() { *m = GoTest_OptionalGroup{} }
+func (m *GoTest_OptionalGroup) String() string { return proto.CompactTextString(m) }
+func (*GoTest_OptionalGroup) ProtoMessage() {}
+
+func (m *GoTest_OptionalGroup) GetRequiredField() string {
+ if m != nil && m.RequiredField != nil {
+ return *m.RequiredField
+ }
+ return ""
+}
+
+// For testing skipping of unrecognized fields.
+// Numbers are all big, larger than tag numbers in GoTestField,
+// the message used in the corresponding test.
+type GoSkipTest struct {
+ SkipInt32 *int32 `protobuf:"varint,11,req,name=skip_int32" json:"skip_int32,omitempty"`
+ SkipFixed32 *uint32 `protobuf:"fixed32,12,req,name=skip_fixed32" json:"skip_fixed32,omitempty"`
+ SkipFixed64 *uint64 `protobuf:"fixed64,13,req,name=skip_fixed64" json:"skip_fixed64,omitempty"`
+ SkipString *string `protobuf:"bytes,14,req,name=skip_string" json:"skip_string,omitempty"`
+ Skipgroup *GoSkipTest_SkipGroup `protobuf:"group,15,req,name=SkipGroup" json:"skipgroup,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoSkipTest) Reset() { *m = GoSkipTest{} }
+func (m *GoSkipTest) String() string { return proto.CompactTextString(m) }
+func (*GoSkipTest) ProtoMessage() {}
+
+func (m *GoSkipTest) GetSkipInt32() int32 {
+ if m != nil && m.SkipInt32 != nil {
+ return *m.SkipInt32
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipFixed32() uint32 {
+ if m != nil && m.SkipFixed32 != nil {
+ return *m.SkipFixed32
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipFixed64() uint64 {
+ if m != nil && m.SkipFixed64 != nil {
+ return *m.SkipFixed64
+ }
+ return 0
+}
+
+func (m *GoSkipTest) GetSkipString() string {
+ if m != nil && m.SkipString != nil {
+ return *m.SkipString
+ }
+ return ""
+}
+
+func (m *GoSkipTest) GetSkipgroup() *GoSkipTest_SkipGroup {
+ if m != nil {
+ return m.Skipgroup
+ }
+ return nil
+}
+
+type GoSkipTest_SkipGroup struct {
+ GroupInt32 *int32 `protobuf:"varint,16,req,name=group_int32" json:"group_int32,omitempty"`
+ GroupString *string `protobuf:"bytes,17,req,name=group_string" json:"group_string,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GoSkipTest_SkipGroup) Reset() { *m = GoSkipTest_SkipGroup{} }
+func (m *GoSkipTest_SkipGroup) String() string { return proto.CompactTextString(m) }
+func (*GoSkipTest_SkipGroup) ProtoMessage() {}
+
+func (m *GoSkipTest_SkipGroup) GetGroupInt32() int32 {
+ if m != nil && m.GroupInt32 != nil {
+ return *m.GroupInt32
+ }
+ return 0
+}
+
+func (m *GoSkipTest_SkipGroup) GetGroupString() string {
+ if m != nil && m.GroupString != nil {
+ return *m.GroupString
+ }
+ return ""
+}
+
+// For testing packed/non-packed decoder switching.
+// A serialized instance of one should be deserializable as the other.
+type NonPackedTest struct {
+ A []int32 `protobuf:"varint,1,rep,name=a" json:"a,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NonPackedTest) Reset() { *m = NonPackedTest{} }
+func (m *NonPackedTest) String() string { return proto.CompactTextString(m) }
+func (*NonPackedTest) ProtoMessage() {}
+
+func (m *NonPackedTest) GetA() []int32 {
+ if m != nil {
+ return m.A
+ }
+ return nil
+}
+
+type PackedTest struct {
+ B []int32 `protobuf:"varint,1,rep,packed,name=b" json:"b,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PackedTest) Reset() { *m = PackedTest{} }
+func (m *PackedTest) String() string { return proto.CompactTextString(m) }
+func (*PackedTest) ProtoMessage() {}
+
+func (m *PackedTest) GetB() []int32 {
+ if m != nil {
+ return m.B
+ }
+ return nil
+}
+
+type MaxTag struct {
+ // Maximum possible tag number.
+ LastField *string `protobuf:"bytes,536870911,opt,name=last_field" json:"last_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MaxTag) Reset() { *m = MaxTag{} }
+func (m *MaxTag) String() string { return proto.CompactTextString(m) }
+func (*MaxTag) ProtoMessage() {}
+
+func (m *MaxTag) GetLastField() string {
+ if m != nil && m.LastField != nil {
+ return *m.LastField
+ }
+ return ""
+}
+
+type OldMessage struct {
+ Nested *OldMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"`
+ Num *int32 `protobuf:"varint,2,opt,name=num" json:"num,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OldMessage) Reset() { *m = OldMessage{} }
+func (m *OldMessage) String() string { return proto.CompactTextString(m) }
+func (*OldMessage) ProtoMessage() {}
+
+func (m *OldMessage) GetNested() *OldMessage_Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+func (m *OldMessage) GetNum() int32 {
+ if m != nil && m.Num != nil {
+ return *m.Num
+ }
+ return 0
+}
+
+type OldMessage_Nested struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OldMessage_Nested) Reset() { *m = OldMessage_Nested{} }
+func (m *OldMessage_Nested) String() string { return proto.CompactTextString(m) }
+func (*OldMessage_Nested) ProtoMessage() {}
+
+func (m *OldMessage_Nested) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+// NewMessage is wire compatible with OldMessage;
+// imagine it as a future version.
+type NewMessage struct {
+ Nested *NewMessage_Nested `protobuf:"bytes,1,opt,name=nested" json:"nested,omitempty"`
+ // This is an int32 in OldMessage.
+ Num *int64 `protobuf:"varint,2,opt,name=num" json:"num,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NewMessage) Reset() { *m = NewMessage{} }
+func (m *NewMessage) String() string { return proto.CompactTextString(m) }
+func (*NewMessage) ProtoMessage() {}
+
+func (m *NewMessage) GetNested() *NewMessage_Nested {
+ if m != nil {
+ return m.Nested
+ }
+ return nil
+}
+
+func (m *NewMessage) GetNum() int64 {
+ if m != nil && m.Num != nil {
+ return *m.Num
+ }
+ return 0
+}
+
+type NewMessage_Nested struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ FoodGroup *string `protobuf:"bytes,2,opt,name=food_group" json:"food_group,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *NewMessage_Nested) Reset() { *m = NewMessage_Nested{} }
+func (m *NewMessage_Nested) String() string { return proto.CompactTextString(m) }
+func (*NewMessage_Nested) ProtoMessage() {}
+
+func (m *NewMessage_Nested) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *NewMessage_Nested) GetFoodGroup() string {
+ if m != nil && m.FoodGroup != nil {
+ return *m.FoodGroup
+ }
+ return ""
+}
+
+type InnerMessage struct {
+ Host *string `protobuf:"bytes,1,req,name=host" json:"host,omitempty"`
+ Port *int32 `protobuf:"varint,2,opt,name=port,def=4000" json:"port,omitempty"`
+ Connected *bool `protobuf:"varint,3,opt,name=connected" json:"connected,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *InnerMessage) Reset() { *m = InnerMessage{} }
+func (m *InnerMessage) String() string { return proto.CompactTextString(m) }
+func (*InnerMessage) ProtoMessage() {}
+
+const Default_InnerMessage_Port int32 = 4000
+
+func (m *InnerMessage) GetHost() string {
+ if m != nil && m.Host != nil {
+ return *m.Host
+ }
+ return ""
+}
+
+func (m *InnerMessage) GetPort() int32 {
+ if m != nil && m.Port != nil {
+ return *m.Port
+ }
+ return Default_InnerMessage_Port
+}
+
+func (m *InnerMessage) GetConnected() bool {
+ if m != nil && m.Connected != nil {
+ return *m.Connected
+ }
+ return false
+}
+
+type OtherMessage struct {
+ Key *int64 `protobuf:"varint,1,opt,name=key" json:"key,omitempty"`
+ Value []byte `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+ Weight *float32 `protobuf:"fixed32,3,opt,name=weight" json:"weight,omitempty"`
+ Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner" json:"inner,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *OtherMessage) Reset() { *m = OtherMessage{} }
+func (m *OtherMessage) String() string { return proto.CompactTextString(m) }
+func (*OtherMessage) ProtoMessage() {}
+
+func (m *OtherMessage) GetKey() int64 {
+ if m != nil && m.Key != nil {
+ return *m.Key
+ }
+ return 0
+}
+
+func (m *OtherMessage) GetValue() []byte {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *OtherMessage) GetWeight() float32 {
+ if m != nil && m.Weight != nil {
+ return *m.Weight
+ }
+ return 0
+}
+
+func (m *OtherMessage) GetInner() *InnerMessage {
+ if m != nil {
+ return m.Inner
+ }
+ return nil
+}
+
+type MyMessage struct {
+ Count *int32 `protobuf:"varint,1,req,name=count" json:"count,omitempty"`
+ Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
+ Quote *string `protobuf:"bytes,3,opt,name=quote" json:"quote,omitempty"`
+ Pet []string `protobuf:"bytes,4,rep,name=pet" json:"pet,omitempty"`
+ Inner *InnerMessage `protobuf:"bytes,5,opt,name=inner" json:"inner,omitempty"`
+ Others []*OtherMessage `protobuf:"bytes,6,rep,name=others" json:"others,omitempty"`
+ RepInner []*InnerMessage `protobuf:"bytes,12,rep,name=rep_inner" json:"rep_inner,omitempty"`
+ Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=testdata.MyMessage_Color" json:"bikeshed,omitempty"`
+ Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup,omitempty"`
+ // This field becomes [][]byte in the generated code.
+ RepBytes [][]byte `protobuf:"bytes,10,rep,name=rep_bytes" json:"rep_bytes,omitempty"`
+ Bigfloat *float64 `protobuf:"fixed64,11,opt,name=bigfloat" json:"bigfloat,omitempty"`
+ XXX_extensions map[int32]proto.Extension `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessage) Reset() { *m = MyMessage{} }
+func (m *MyMessage) String() string { return proto.CompactTextString(m) }
+func (*MyMessage) ProtoMessage() {}
+
+var extRange_MyMessage = []proto.ExtensionRange{
+ {100, 536870911},
+}
+
+func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange {
+ return extRange_MyMessage
+}
+func (m *MyMessage) ExtensionMap() map[int32]proto.Extension {
+ if m.XXX_extensions == nil {
+ m.XXX_extensions = make(map[int32]proto.Extension)
+ }
+ return m.XXX_extensions
+}
+
+func (m *MyMessage) GetCount() int32 {
+ if m != nil && m.Count != nil {
+ return *m.Count
+ }
+ return 0
+}
+
+func (m *MyMessage) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MyMessage) GetQuote() string {
+ if m != nil && m.Quote != nil {
+ return *m.Quote
+ }
+ return ""
+}
+
+func (m *MyMessage) GetPet() []string {
+ if m != nil {
+ return m.Pet
+ }
+ return nil
+}
+
+func (m *MyMessage) GetInner() *InnerMessage {
+ if m != nil {
+ return m.Inner
+ }
+ return nil
+}
+
+func (m *MyMessage) GetOthers() []*OtherMessage {
+ if m != nil {
+ return m.Others
+ }
+ return nil
+}
+
+func (m *MyMessage) GetRepInner() []*InnerMessage {
+ if m != nil {
+ return m.RepInner
+ }
+ return nil
+}
+
+func (m *MyMessage) GetBikeshed() MyMessage_Color {
+ if m != nil && m.Bikeshed != nil {
+ return *m.Bikeshed
+ }
+ return MyMessage_RED
+}
+
+func (m *MyMessage) GetSomegroup() *MyMessage_SomeGroup {
+ if m != nil {
+ return m.Somegroup
+ }
+ return nil
+}
+
+func (m *MyMessage) GetRepBytes() [][]byte {
+ if m != nil {
+ return m.RepBytes
+ }
+ return nil
+}
+
+func (m *MyMessage) GetBigfloat() float64 {
+ if m != nil && m.Bigfloat != nil {
+ return *m.Bigfloat
+ }
+ return 0
+}
+
+type MyMessage_SomeGroup struct {
+ GroupField *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessage_SomeGroup) Reset() { *m = MyMessage_SomeGroup{} }
+func (m *MyMessage_SomeGroup) String() string { return proto.CompactTextString(m) }
+func (*MyMessage_SomeGroup) ProtoMessage() {}
+
+func (m *MyMessage_SomeGroup) GetGroupField() int32 {
+ if m != nil && m.GroupField != nil {
+ return *m.GroupField
+ }
+ return 0
+}
+
+type Ext struct {
+ Data *string `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Ext) Reset() { *m = Ext{} }
+func (m *Ext) String() string { return proto.CompactTextString(m) }
+func (*Ext) ProtoMessage() {}
+
+func (m *Ext) GetData() string {
+ if m != nil && m.Data != nil {
+ return *m.Data
+ }
+ return ""
+}
+
+var E_Ext_More = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*Ext)(nil),
+ Field: 103,
+ Name: "testdata.Ext.more",
+ Tag: "bytes,103,opt,name=more",
+}
+
+var E_Ext_Text = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 104,
+ Name: "testdata.Ext.text",
+ Tag: "bytes,104,opt,name=text",
+}
+
+var E_Ext_Number = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: (*int32)(nil),
+ Field: 105,
+ Name: "testdata.Ext.number",
+ Tag: "varint,105,opt,name=number",
+}
+
+type MyMessageSet struct {
+ XXX_extensions map[int32]proto.Extension `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MyMessageSet) Reset() { *m = MyMessageSet{} }
+func (m *MyMessageSet) String() string { return proto.CompactTextString(m) }
+func (*MyMessageSet) ProtoMessage() {}
+
+func (m *MyMessageSet) Marshal() ([]byte, error) {
+ return proto.MarshalMessageSet(m.ExtensionMap())
+}
+func (m *MyMessageSet) Unmarshal(buf []byte) error {
+ return proto.UnmarshalMessageSet(buf, m.ExtensionMap())
+}
+func (m *MyMessageSet) MarshalJSON() ([]byte, error) {
+ return proto.MarshalMessageSetJSON(m.XXX_extensions)
+}
+func (m *MyMessageSet) UnmarshalJSON(buf []byte) error {
+ return proto.UnmarshalMessageSetJSON(buf, m.XXX_extensions)
+}
+
+// ensure MyMessageSet satisfies proto.Marshaler and proto.Unmarshaler
+var _ proto.Marshaler = (*MyMessageSet)(nil)
+var _ proto.Unmarshaler = (*MyMessageSet)(nil)
+
+var extRange_MyMessageSet = []proto.ExtensionRange{
+ {100, 2147483646},
+}
+
+func (*MyMessageSet) ExtensionRangeArray() []proto.ExtensionRange {
+ return extRange_MyMessageSet
+}
+func (m *MyMessageSet) ExtensionMap() map[int32]proto.Extension {
+ if m.XXX_extensions == nil {
+ m.XXX_extensions = make(map[int32]proto.Extension)
+ }
+ return m.XXX_extensions
+}
+
+type Empty struct {
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+
+type MessageList struct {
+ Message []*MessageList_Message `protobuf:"group,1,rep" json:"message,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageList) Reset() { *m = MessageList{} }
+func (m *MessageList) String() string { return proto.CompactTextString(m) }
+func (*MessageList) ProtoMessage() {}
+
+func (m *MessageList) GetMessage() []*MessageList_Message {
+ if m != nil {
+ return m.Message
+ }
+ return nil
+}
+
+type MessageList_Message struct {
+ Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
+ Count *int32 `protobuf:"varint,3,req,name=count" json:"count,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageList_Message) Reset() { *m = MessageList_Message{} }
+func (m *MessageList_Message) String() string { return proto.CompactTextString(m) }
+func (*MessageList_Message) ProtoMessage() {}
+
+func (m *MessageList_Message) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MessageList_Message) GetCount() int32 {
+ if m != nil && m.Count != nil {
+ return *m.Count
+ }
+ return 0
+}
+
+type Strings struct {
+ StringField *string `protobuf:"bytes,1,opt,name=string_field" json:"string_field,omitempty"`
+ BytesField []byte `protobuf:"bytes,2,opt,name=bytes_field" json:"bytes_field,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Strings) Reset() { *m = Strings{} }
+func (m *Strings) String() string { return proto.CompactTextString(m) }
+func (*Strings) ProtoMessage() {}
+
+func (m *Strings) GetStringField() string {
+ if m != nil && m.StringField != nil {
+ return *m.StringField
+ }
+ return ""
+}
+
+func (m *Strings) GetBytesField() []byte {
+ if m != nil {
+ return m.BytesField
+ }
+ return nil
+}
+
+type Defaults struct {
+ // Default-valued fields of all basic types.
+ // Same as GoTest, but copied here to make testing easier.
+ F_Bool *bool `protobuf:"varint,1,opt,def=1" json:"F_Bool,omitempty"`
+ F_Int32 *int32 `protobuf:"varint,2,opt,def=32" json:"F_Int32,omitempty"`
+ F_Int64 *int64 `protobuf:"varint,3,opt,def=64" json:"F_Int64,omitempty"`
+ F_Fixed32 *uint32 `protobuf:"fixed32,4,opt,def=320" json:"F_Fixed32,omitempty"`
+ F_Fixed64 *uint64 `protobuf:"fixed64,5,opt,def=640" json:"F_Fixed64,omitempty"`
+ F_Uint32 *uint32 `protobuf:"varint,6,opt,def=3200" json:"F_Uint32,omitempty"`
+ F_Uint64 *uint64 `protobuf:"varint,7,opt,def=6400" json:"F_Uint64,omitempty"`
+ F_Float *float32 `protobuf:"fixed32,8,opt,def=314159" json:"F_Float,omitempty"`
+ F_Double *float64 `protobuf:"fixed64,9,opt,def=271828" json:"F_Double,omitempty"`
+ F_String *string `protobuf:"bytes,10,opt,def=hello, \"world!\"\n" json:"F_String,omitempty"`
+ F_Bytes []byte `protobuf:"bytes,11,opt,def=Bignose" json:"F_Bytes,omitempty"`
+ F_Sint32 *int32 `protobuf:"zigzag32,12,opt,def=-32" json:"F_Sint32,omitempty"`
+ F_Sint64 *int64 `protobuf:"zigzag64,13,opt,def=-64" json:"F_Sint64,omitempty"`
+ F_Enum *Defaults_Color `protobuf:"varint,14,opt,enum=testdata.Defaults_Color,def=1" json:"F_Enum,omitempty"`
+ // More fields with crazy defaults.
+ F_Pinf *float32 `protobuf:"fixed32,15,opt,def=inf" json:"F_Pinf,omitempty"`
+ F_Ninf *float32 `protobuf:"fixed32,16,opt,def=-inf" json:"F_Ninf,omitempty"`
+ F_Nan *float32 `protobuf:"fixed32,17,opt,def=nan" json:"F_Nan,omitempty"`
+ // Sub-message.
+ Sub *SubDefaults `protobuf:"bytes,18,opt,name=sub" json:"sub,omitempty"`
+ // Redundant but explicit defaults.
+ StrZero *string `protobuf:"bytes,19,opt,name=str_zero,def=" json:"str_zero,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Defaults) Reset() { *m = Defaults{} }
+func (m *Defaults) String() string { return proto.CompactTextString(m) }
+func (*Defaults) ProtoMessage() {}
+
+const Default_Defaults_F_Bool bool = true
+const Default_Defaults_F_Int32 int32 = 32
+const Default_Defaults_F_Int64 int64 = 64
+const Default_Defaults_F_Fixed32 uint32 = 320
+const Default_Defaults_F_Fixed64 uint64 = 640
+const Default_Defaults_F_Uint32 uint32 = 3200
+const Default_Defaults_F_Uint64 uint64 = 6400
+const Default_Defaults_F_Float float32 = 314159
+const Default_Defaults_F_Double float64 = 271828
+const Default_Defaults_F_String string = "hello, \"world!\"\n"
+
+var Default_Defaults_F_Bytes []byte = []byte("Bignose")
+
+const Default_Defaults_F_Sint32 int32 = -32
+const Default_Defaults_F_Sint64 int64 = -64
+const Default_Defaults_F_Enum Defaults_Color = Defaults_GREEN
+
+var Default_Defaults_F_Pinf float32 = float32(math.Inf(1))
+var Default_Defaults_F_Ninf float32 = float32(math.Inf(-1))
+var Default_Defaults_F_Nan float32 = float32(math.NaN())
+
+func (m *Defaults) GetF_Bool() bool {
+ if m != nil && m.F_Bool != nil {
+ return *m.F_Bool
+ }
+ return Default_Defaults_F_Bool
+}
+
+func (m *Defaults) GetF_Int32() int32 {
+ if m != nil && m.F_Int32 != nil {
+ return *m.F_Int32
+ }
+ return Default_Defaults_F_Int32
+}
+
+func (m *Defaults) GetF_Int64() int64 {
+ if m != nil && m.F_Int64 != nil {
+ return *m.F_Int64
+ }
+ return Default_Defaults_F_Int64
+}
+
+func (m *Defaults) GetF_Fixed32() uint32 {
+ if m != nil && m.F_Fixed32 != nil {
+ return *m.F_Fixed32
+ }
+ return Default_Defaults_F_Fixed32
+}
+
+func (m *Defaults) GetF_Fixed64() uint64 {
+ if m != nil && m.F_Fixed64 != nil {
+ return *m.F_Fixed64
+ }
+ return Default_Defaults_F_Fixed64
+}
+
+func (m *Defaults) GetF_Uint32() uint32 {
+ if m != nil && m.F_Uint32 != nil {
+ return *m.F_Uint32
+ }
+ return Default_Defaults_F_Uint32
+}
+
+func (m *Defaults) GetF_Uint64() uint64 {
+ if m != nil && m.F_Uint64 != nil {
+ return *m.F_Uint64
+ }
+ return Default_Defaults_F_Uint64
+}
+
+func (m *Defaults) GetF_Float() float32 {
+ if m != nil && m.F_Float != nil {
+ return *m.F_Float
+ }
+ return Default_Defaults_F_Float
+}
+
+func (m *Defaults) GetF_Double() float64 {
+ if m != nil && m.F_Double != nil {
+ return *m.F_Double
+ }
+ return Default_Defaults_F_Double
+}
+
+func (m *Defaults) GetF_String() string {
+ if m != nil && m.F_String != nil {
+ return *m.F_String
+ }
+ return Default_Defaults_F_String
+}
+
+func (m *Defaults) GetF_Bytes() []byte {
+ if m != nil && m.F_Bytes != nil {
+ return m.F_Bytes
+ }
+ return append([]byte(nil), Default_Defaults_F_Bytes...)
+}
+
+func (m *Defaults) GetF_Sint32() int32 {
+ if m != nil && m.F_Sint32 != nil {
+ return *m.F_Sint32
+ }
+ return Default_Defaults_F_Sint32
+}
+
+func (m *Defaults) GetF_Sint64() int64 {
+ if m != nil && m.F_Sint64 != nil {
+ return *m.F_Sint64
+ }
+ return Default_Defaults_F_Sint64
+}
+
+func (m *Defaults) GetF_Enum() Defaults_Color {
+ if m != nil && m.F_Enum != nil {
+ return *m.F_Enum
+ }
+ return Default_Defaults_F_Enum
+}
+
+func (m *Defaults) GetF_Pinf() float32 {
+ if m != nil && m.F_Pinf != nil {
+ return *m.F_Pinf
+ }
+ return Default_Defaults_F_Pinf
+}
+
+func (m *Defaults) GetF_Ninf() float32 {
+ if m != nil && m.F_Ninf != nil {
+ return *m.F_Ninf
+ }
+ return Default_Defaults_F_Ninf
+}
+
+func (m *Defaults) GetF_Nan() float32 {
+ if m != nil && m.F_Nan != nil {
+ return *m.F_Nan
+ }
+ return Default_Defaults_F_Nan
+}
+
+func (m *Defaults) GetSub() *SubDefaults {
+ if m != nil {
+ return m.Sub
+ }
+ return nil
+}
+
+func (m *Defaults) GetStrZero() string {
+ if m != nil && m.StrZero != nil {
+ return *m.StrZero
+ }
+ return ""
+}
+
+type SubDefaults struct {
+ N *int64 `protobuf:"varint,1,opt,name=n,def=7" json:"n,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SubDefaults) Reset() { *m = SubDefaults{} }
+func (m *SubDefaults) String() string { return proto.CompactTextString(m) }
+func (*SubDefaults) ProtoMessage() {}
+
+const Default_SubDefaults_N int64 = 7
+
+func (m *SubDefaults) GetN() int64 {
+ if m != nil && m.N != nil {
+ return *m.N
+ }
+ return Default_SubDefaults_N
+}
+
+type RepeatedEnum struct {
+ Color []RepeatedEnum_Color `protobuf:"varint,1,rep,name=color,enum=testdata.RepeatedEnum_Color" json:"color,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RepeatedEnum) Reset() { *m = RepeatedEnum{} }
+func (m *RepeatedEnum) String() string { return proto.CompactTextString(m) }
+func (*RepeatedEnum) ProtoMessage() {}
+
+func (m *RepeatedEnum) GetColor() []RepeatedEnum_Color {
+ if m != nil {
+ return m.Color
+ }
+ return nil
+}
+
+type MoreRepeated struct {
+ Bools []bool `protobuf:"varint,1,rep,name=bools" json:"bools,omitempty"`
+ BoolsPacked []bool `protobuf:"varint,2,rep,packed,name=bools_packed" json:"bools_packed,omitempty"`
+ Ints []int32 `protobuf:"varint,3,rep,name=ints" json:"ints,omitempty"`
+ IntsPacked []int32 `protobuf:"varint,4,rep,packed,name=ints_packed" json:"ints_packed,omitempty"`
+ Int64SPacked []int64 `protobuf:"varint,7,rep,packed,name=int64s_packed" json:"int64s_packed,omitempty"`
+ Strings []string `protobuf:"bytes,5,rep,name=strings" json:"strings,omitempty"`
+ Fixeds []uint32 `protobuf:"fixed32,6,rep,name=fixeds" json:"fixeds,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MoreRepeated) Reset() { *m = MoreRepeated{} }
+func (m *MoreRepeated) String() string { return proto.CompactTextString(m) }
+func (*MoreRepeated) ProtoMessage() {}
+
+func (m *MoreRepeated) GetBools() []bool {
+ if m != nil {
+ return m.Bools
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetBoolsPacked() []bool {
+ if m != nil {
+ return m.BoolsPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetInts() []int32 {
+ if m != nil {
+ return m.Ints
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetIntsPacked() []int32 {
+ if m != nil {
+ return m.IntsPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetInt64SPacked() []int64 {
+ if m != nil {
+ return m.Int64SPacked
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetStrings() []string {
+ if m != nil {
+ return m.Strings
+ }
+ return nil
+}
+
+func (m *MoreRepeated) GetFixeds() []uint32 {
+ if m != nil {
+ return m.Fixeds
+ }
+ return nil
+}
+
+type GroupOld struct {
+ G *GroupOld_G `protobuf:"group,101,opt" json:"g,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupOld) Reset() { *m = GroupOld{} }
+func (m *GroupOld) String() string { return proto.CompactTextString(m) }
+func (*GroupOld) ProtoMessage() {}
+
+func (m *GroupOld) GetG() *GroupOld_G {
+ if m != nil {
+ return m.G
+ }
+ return nil
+}
+
+type GroupOld_G struct {
+ X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupOld_G) Reset() { *m = GroupOld_G{} }
+func (m *GroupOld_G) String() string { return proto.CompactTextString(m) }
+func (*GroupOld_G) ProtoMessage() {}
+
+func (m *GroupOld_G) GetX() int32 {
+ if m != nil && m.X != nil {
+ return *m.X
+ }
+ return 0
+}
+
+type GroupNew struct {
+ G *GroupNew_G `protobuf:"group,101,opt" json:"g,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupNew) Reset() { *m = GroupNew{} }
+func (m *GroupNew) String() string { return proto.CompactTextString(m) }
+func (*GroupNew) ProtoMessage() {}
+
+func (m *GroupNew) GetG() *GroupNew_G {
+ if m != nil {
+ return m.G
+ }
+ return nil
+}
+
+type GroupNew_G struct {
+ X *int32 `protobuf:"varint,2,opt,name=x" json:"x,omitempty"`
+ Y *int32 `protobuf:"varint,3,opt,name=y" json:"y,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GroupNew_G) Reset() { *m = GroupNew_G{} }
+func (m *GroupNew_G) String() string { return proto.CompactTextString(m) }
+func (*GroupNew_G) ProtoMessage() {}
+
+func (m *GroupNew_G) GetX() int32 {
+ if m != nil && m.X != nil {
+ return *m.X
+ }
+ return 0
+}
+
+func (m *GroupNew_G) GetY() int32 {
+ if m != nil && m.Y != nil {
+ return *m.Y
+ }
+ return 0
+}
+
+type FloatingPoint struct {
+ F *float64 `protobuf:"fixed64,1,req,name=f" json:"f,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *FloatingPoint) Reset() { *m = FloatingPoint{} }
+func (m *FloatingPoint) String() string { return proto.CompactTextString(m) }
+func (*FloatingPoint) ProtoMessage() {}
+
+func (m *FloatingPoint) GetF() float64 {
+ if m != nil && m.F != nil {
+ return *m.F
+ }
+ return 0
+}
+
+type MessageWithMap struct {
+ NameMapping map[int32]string `protobuf:"bytes,1,rep,name=name_mapping" json:"name_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ MsgMapping map[int64]*FloatingPoint `protobuf:"bytes,2,rep,name=msg_mapping" json:"msg_mapping,omitempty" protobuf_key:"zigzag64,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ ByteMapping map[bool][]byte `protobuf:"bytes,3,rep,name=byte_mapping" json:"byte_mapping,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MessageWithMap) Reset() { *m = MessageWithMap{} }
+func (m *MessageWithMap) String() string { return proto.CompactTextString(m) }
+func (*MessageWithMap) ProtoMessage() {}
+
+func (m *MessageWithMap) GetNameMapping() map[int32]string {
+ if m != nil {
+ return m.NameMapping
+ }
+ return nil
+}
+
+func (m *MessageWithMap) GetMsgMapping() map[int64]*FloatingPoint {
+ if m != nil {
+ return m.MsgMapping
+ }
+ return nil
+}
+
+func (m *MessageWithMap) GetByteMapping() map[bool][]byte {
+ if m != nil {
+ return m.ByteMapping
+ }
+ return nil
+}
+
+var E_Greeting = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessage)(nil),
+ ExtensionType: ([]string)(nil),
+ Field: 106,
+ Name: "testdata.greeting",
+ Tag: "bytes,106,rep,name=greeting",
+}
+
+var E_X201 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 201,
+ Name: "testdata.x201",
+ Tag: "bytes,201,opt,name=x201",
+}
+
+var E_X202 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 202,
+ Name: "testdata.x202",
+ Tag: "bytes,202,opt,name=x202",
+}
+
+var E_X203 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 203,
+ Name: "testdata.x203",
+ Tag: "bytes,203,opt,name=x203",
+}
+
+var E_X204 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 204,
+ Name: "testdata.x204",
+ Tag: "bytes,204,opt,name=x204",
+}
+
+var E_X205 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 205,
+ Name: "testdata.x205",
+ Tag: "bytes,205,opt,name=x205",
+}
+
+var E_X206 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 206,
+ Name: "testdata.x206",
+ Tag: "bytes,206,opt,name=x206",
+}
+
+var E_X207 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 207,
+ Name: "testdata.x207",
+ Tag: "bytes,207,opt,name=x207",
+}
+
+var E_X208 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 208,
+ Name: "testdata.x208",
+ Tag: "bytes,208,opt,name=x208",
+}
+
+var E_X209 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 209,
+ Name: "testdata.x209",
+ Tag: "bytes,209,opt,name=x209",
+}
+
+var E_X210 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 210,
+ Name: "testdata.x210",
+ Tag: "bytes,210,opt,name=x210",
+}
+
+var E_X211 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 211,
+ Name: "testdata.x211",
+ Tag: "bytes,211,opt,name=x211",
+}
+
+var E_X212 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 212,
+ Name: "testdata.x212",
+ Tag: "bytes,212,opt,name=x212",
+}
+
+var E_X213 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 213,
+ Name: "testdata.x213",
+ Tag: "bytes,213,opt,name=x213",
+}
+
+var E_X214 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 214,
+ Name: "testdata.x214",
+ Tag: "bytes,214,opt,name=x214",
+}
+
+var E_X215 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 215,
+ Name: "testdata.x215",
+ Tag: "bytes,215,opt,name=x215",
+}
+
+var E_X216 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 216,
+ Name: "testdata.x216",
+ Tag: "bytes,216,opt,name=x216",
+}
+
+var E_X217 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 217,
+ Name: "testdata.x217",
+ Tag: "bytes,217,opt,name=x217",
+}
+
+var E_X218 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 218,
+ Name: "testdata.x218",
+ Tag: "bytes,218,opt,name=x218",
+}
+
+var E_X219 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 219,
+ Name: "testdata.x219",
+ Tag: "bytes,219,opt,name=x219",
+}
+
+var E_X220 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 220,
+ Name: "testdata.x220",
+ Tag: "bytes,220,opt,name=x220",
+}
+
+var E_X221 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 221,
+ Name: "testdata.x221",
+ Tag: "bytes,221,opt,name=x221",
+}
+
+var E_X222 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 222,
+ Name: "testdata.x222",
+ Tag: "bytes,222,opt,name=x222",
+}
+
+var E_X223 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 223,
+ Name: "testdata.x223",
+ Tag: "bytes,223,opt,name=x223",
+}
+
+var E_X224 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 224,
+ Name: "testdata.x224",
+ Tag: "bytes,224,opt,name=x224",
+}
+
+var E_X225 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 225,
+ Name: "testdata.x225",
+ Tag: "bytes,225,opt,name=x225",
+}
+
+var E_X226 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 226,
+ Name: "testdata.x226",
+ Tag: "bytes,226,opt,name=x226",
+}
+
+var E_X227 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 227,
+ Name: "testdata.x227",
+ Tag: "bytes,227,opt,name=x227",
+}
+
+var E_X228 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 228,
+ Name: "testdata.x228",
+ Tag: "bytes,228,opt,name=x228",
+}
+
+var E_X229 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 229,
+ Name: "testdata.x229",
+ Tag: "bytes,229,opt,name=x229",
+}
+
+var E_X230 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 230,
+ Name: "testdata.x230",
+ Tag: "bytes,230,opt,name=x230",
+}
+
+var E_X231 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 231,
+ Name: "testdata.x231",
+ Tag: "bytes,231,opt,name=x231",
+}
+
+var E_X232 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 232,
+ Name: "testdata.x232",
+ Tag: "bytes,232,opt,name=x232",
+}
+
+var E_X233 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 233,
+ Name: "testdata.x233",
+ Tag: "bytes,233,opt,name=x233",
+}
+
+var E_X234 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 234,
+ Name: "testdata.x234",
+ Tag: "bytes,234,opt,name=x234",
+}
+
+var E_X235 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 235,
+ Name: "testdata.x235",
+ Tag: "bytes,235,opt,name=x235",
+}
+
+var E_X236 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 236,
+ Name: "testdata.x236",
+ Tag: "bytes,236,opt,name=x236",
+}
+
+var E_X237 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 237,
+ Name: "testdata.x237",
+ Tag: "bytes,237,opt,name=x237",
+}
+
+var E_X238 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 238,
+ Name: "testdata.x238",
+ Tag: "bytes,238,opt,name=x238",
+}
+
+var E_X239 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 239,
+ Name: "testdata.x239",
+ Tag: "bytes,239,opt,name=x239",
+}
+
+var E_X240 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 240,
+ Name: "testdata.x240",
+ Tag: "bytes,240,opt,name=x240",
+}
+
+var E_X241 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 241,
+ Name: "testdata.x241",
+ Tag: "bytes,241,opt,name=x241",
+}
+
+var E_X242 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 242,
+ Name: "testdata.x242",
+ Tag: "bytes,242,opt,name=x242",
+}
+
+var E_X243 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 243,
+ Name: "testdata.x243",
+ Tag: "bytes,243,opt,name=x243",
+}
+
+var E_X244 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 244,
+ Name: "testdata.x244",
+ Tag: "bytes,244,opt,name=x244",
+}
+
+var E_X245 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 245,
+ Name: "testdata.x245",
+ Tag: "bytes,245,opt,name=x245",
+}
+
+var E_X246 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 246,
+ Name: "testdata.x246",
+ Tag: "bytes,246,opt,name=x246",
+}
+
+var E_X247 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 247,
+ Name: "testdata.x247",
+ Tag: "bytes,247,opt,name=x247",
+}
+
+var E_X248 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 248,
+ Name: "testdata.x248",
+ Tag: "bytes,248,opt,name=x248",
+}
+
+var E_X249 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 249,
+ Name: "testdata.x249",
+ Tag: "bytes,249,opt,name=x249",
+}
+
+var E_X250 = &proto.ExtensionDesc{
+ ExtendedType: (*MyMessageSet)(nil),
+ ExtensionType: (*Empty)(nil),
+ Field: 250,
+ Name: "testdata.x250",
+ Tag: "bytes,250,opt,name=x250",
+}
+
+func init() {
+ proto.RegisterEnum("testdata.FOO", FOO_name, FOO_value)
+ proto.RegisterEnum("testdata.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value)
+ proto.RegisterEnum("testdata.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value)
+ proto.RegisterEnum("testdata.Defaults_Color", Defaults_Color_name, Defaults_Color_value)
+ proto.RegisterEnum("testdata.RepeatedEnum_Color", RepeatedEnum_Color_name, RepeatedEnum_Color_value)
+ proto.RegisterExtension(E_Ext_More)
+ proto.RegisterExtension(E_Ext_Text)
+ proto.RegisterExtension(E_Ext_Number)
+ proto.RegisterExtension(E_Greeting)
+ proto.RegisterExtension(E_X201)
+ proto.RegisterExtension(E_X202)
+ proto.RegisterExtension(E_X203)
+ proto.RegisterExtension(E_X204)
+ proto.RegisterExtension(E_X205)
+ proto.RegisterExtension(E_X206)
+ proto.RegisterExtension(E_X207)
+ proto.RegisterExtension(E_X208)
+ proto.RegisterExtension(E_X209)
+ proto.RegisterExtension(E_X210)
+ proto.RegisterExtension(E_X211)
+ proto.RegisterExtension(E_X212)
+ proto.RegisterExtension(E_X213)
+ proto.RegisterExtension(E_X214)
+ proto.RegisterExtension(E_X215)
+ proto.RegisterExtension(E_X216)
+ proto.RegisterExtension(E_X217)
+ proto.RegisterExtension(E_X218)
+ proto.RegisterExtension(E_X219)
+ proto.RegisterExtension(E_X220)
+ proto.RegisterExtension(E_X221)
+ proto.RegisterExtension(E_X222)
+ proto.RegisterExtension(E_X223)
+ proto.RegisterExtension(E_X224)
+ proto.RegisterExtension(E_X225)
+ proto.RegisterExtension(E_X226)
+ proto.RegisterExtension(E_X227)
+ proto.RegisterExtension(E_X228)
+ proto.RegisterExtension(E_X229)
+ proto.RegisterExtension(E_X230)
+ proto.RegisterExtension(E_X231)
+ proto.RegisterExtension(E_X232)
+ proto.RegisterExtension(E_X233)
+ proto.RegisterExtension(E_X234)
+ proto.RegisterExtension(E_X235)
+ proto.RegisterExtension(E_X236)
+ proto.RegisterExtension(E_X237)
+ proto.RegisterExtension(E_X238)
+ proto.RegisterExtension(E_X239)
+ proto.RegisterExtension(E_X240)
+ proto.RegisterExtension(E_X241)
+ proto.RegisterExtension(E_X242)
+ proto.RegisterExtension(E_X243)
+ proto.RegisterExtension(E_X244)
+ proto.RegisterExtension(E_X245)
+ proto.RegisterExtension(E_X246)
+ proto.RegisterExtension(E_X247)
+ proto.RegisterExtension(E_X248)
+ proto.RegisterExtension(E_X249)
+ proto.RegisterExtension(E_X250)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto
new file mode 100644
index 000000000..6cc755bae
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata/test.proto
@@ -0,0 +1,434 @@
+// 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.
+
+// A feature-rich test file for the protocol compiler and libraries.
+
+syntax = "proto2";
+
+package testdata;
+
+enum FOO { FOO1 = 1; };
+
+message GoEnum {
+ required FOO foo = 1;
+}
+
+message GoTestField {
+ required string Label = 1;
+ required string Type = 2;
+}
+
+message GoTest {
+ // An enum, for completeness.
+ enum KIND {
+ VOID = 0;
+
+ // Basic types
+ BOOL = 1;
+ BYTES = 2;
+ FINGERPRINT = 3;
+ FLOAT = 4;
+ INT = 5;
+ STRING = 6;
+ TIME = 7;
+
+ // Groupings
+ TUPLE = 8;
+ ARRAY = 9;
+ MAP = 10;
+
+ // Table types
+ TABLE = 11;
+
+ // Functions
+ FUNCTION = 12; // last tag
+ };
+
+ // Some typical parameters
+ required KIND Kind = 1;
+ optional string Table = 2;
+ optional int32 Param = 3;
+
+ // Required, repeated and optional foreign fields.
+ required GoTestField RequiredField = 4;
+ repeated GoTestField RepeatedField = 5;
+ optional GoTestField OptionalField = 6;
+
+ // Required fields of all basic types
+ required bool F_Bool_required = 10;
+ required int32 F_Int32_required = 11;
+ required int64 F_Int64_required = 12;
+ required fixed32 F_Fixed32_required = 13;
+ required fixed64 F_Fixed64_required = 14;
+ required uint32 F_Uint32_required = 15;
+ required uint64 F_Uint64_required = 16;
+ required float F_Float_required = 17;
+ required double F_Double_required = 18;
+ required string F_String_required = 19;
+ required bytes F_Bytes_required = 101;
+ required sint32 F_Sint32_required = 102;
+ required sint64 F_Sint64_required = 103;
+
+ // Repeated fields of all basic types
+ repeated bool F_Bool_repeated = 20;
+ repeated int32 F_Int32_repeated = 21;
+ repeated int64 F_Int64_repeated = 22;
+ repeated fixed32 F_Fixed32_repeated = 23;
+ repeated fixed64 F_Fixed64_repeated = 24;
+ repeated uint32 F_Uint32_repeated = 25;
+ repeated uint64 F_Uint64_repeated = 26;
+ repeated float F_Float_repeated = 27;
+ repeated double F_Double_repeated = 28;
+ repeated string F_String_repeated = 29;
+ repeated bytes F_Bytes_repeated = 201;
+ repeated sint32 F_Sint32_repeated = 202;
+ repeated sint64 F_Sint64_repeated = 203;
+
+ // Optional fields of all basic types
+ optional bool F_Bool_optional = 30;
+ optional int32 F_Int32_optional = 31;
+ optional int64 F_Int64_optional = 32;
+ optional fixed32 F_Fixed32_optional = 33;
+ optional fixed64 F_Fixed64_optional = 34;
+ optional uint32 F_Uint32_optional = 35;
+ optional uint64 F_Uint64_optional = 36;
+ optional float F_Float_optional = 37;
+ optional double F_Double_optional = 38;
+ optional string F_String_optional = 39;
+ optional bytes F_Bytes_optional = 301;
+ optional sint32 F_Sint32_optional = 302;
+ optional sint64 F_Sint64_optional = 303;
+
+ // Default-valued fields of all basic types
+ optional bool F_Bool_defaulted = 40 [default=true];
+ optional int32 F_Int32_defaulted = 41 [default=32];
+ optional int64 F_Int64_defaulted = 42 [default=64];
+ optional fixed32 F_Fixed32_defaulted = 43 [default=320];
+ optional fixed64 F_Fixed64_defaulted = 44 [default=640];
+ optional uint32 F_Uint32_defaulted = 45 [default=3200];
+ optional uint64 F_Uint64_defaulted = 46 [default=6400];
+ optional float F_Float_defaulted = 47 [default=314159.];
+ optional double F_Double_defaulted = 48 [default=271828.];
+ optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"];
+ optional bytes F_Bytes_defaulted = 401 [default="Bignose"];
+ optional sint32 F_Sint32_defaulted = 402 [default = -32];
+ optional sint64 F_Sint64_defaulted = 403 [default = -64];
+
+ // Packed repeated fields (no string or bytes).
+ repeated bool F_Bool_repeated_packed = 50 [packed=true];
+ repeated int32 F_Int32_repeated_packed = 51 [packed=true];
+ repeated int64 F_Int64_repeated_packed = 52 [packed=true];
+ repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true];
+ repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true];
+ repeated uint32 F_Uint32_repeated_packed = 55 [packed=true];
+ repeated uint64 F_Uint64_repeated_packed = 56 [packed=true];
+ repeated float F_Float_repeated_packed = 57 [packed=true];
+ repeated double F_Double_repeated_packed = 58 [packed=true];
+ repeated sint32 F_Sint32_repeated_packed = 502 [packed=true];
+ repeated sint64 F_Sint64_repeated_packed = 503 [packed=true];
+
+ // Required, repeated, and optional groups.
+ required group RequiredGroup = 70 {
+ required string RequiredField = 71;
+ };
+
+ repeated group RepeatedGroup = 80 {
+ required string RequiredField = 81;
+ };
+
+ optional group OptionalGroup = 90 {
+ required string RequiredField = 91;
+ };
+}
+
+// For testing skipping of unrecognized fields.
+// Numbers are all big, larger than tag numbers in GoTestField,
+// the message used in the corresponding test.
+message GoSkipTest {
+ required int32 skip_int32 = 11;
+ required fixed32 skip_fixed32 = 12;
+ required fixed64 skip_fixed64 = 13;
+ required string skip_string = 14;
+ required group SkipGroup = 15 {
+ required int32 group_int32 = 16;
+ required string group_string = 17;
+ }
+}
+
+// For testing packed/non-packed decoder switching.
+// A serialized instance of one should be deserializable as the other.
+message NonPackedTest {
+ repeated int32 a = 1;
+}
+
+message PackedTest {
+ repeated int32 b = 1 [packed=true];
+}
+
+message MaxTag {
+ // Maximum possible tag number.
+ optional string last_field = 536870911;
+}
+
+message OldMessage {
+ message Nested {
+ optional string name = 1;
+ }
+ optional Nested nested = 1;
+
+ optional int32 num = 2;
+}
+
+// NewMessage is wire compatible with OldMessage;
+// imagine it as a future version.
+message NewMessage {
+ message Nested {
+ optional string name = 1;
+ optional string food_group = 2;
+ }
+ optional Nested nested = 1;
+
+ // This is an int32 in OldMessage.
+ optional int64 num = 2;
+}
+
+// Smaller tests for ASCII formatting.
+
+message InnerMessage {
+ required string host = 1;
+ optional int32 port = 2 [default=4000];
+ optional bool connected = 3;
+}
+
+message OtherMessage {
+ optional int64 key = 1;
+ optional bytes value = 2;
+ optional float weight = 3;
+ optional InnerMessage inner = 4;
+}
+
+message MyMessage {
+ required int32 count = 1;
+ optional string name = 2;
+ optional string quote = 3;
+ repeated string pet = 4;
+ optional InnerMessage inner = 5;
+ repeated OtherMessage others = 6;
+ repeated InnerMessage rep_inner = 12;
+
+ enum Color {
+ RED = 0;
+ GREEN = 1;
+ BLUE = 2;
+ };
+ optional Color bikeshed = 7;
+
+ optional group SomeGroup = 8 {
+ optional int32 group_field = 9;
+ }
+
+ // This field becomes [][]byte in the generated code.
+ repeated bytes rep_bytes = 10;
+
+ optional double bigfloat = 11;
+
+ extensions 100 to max;
+}
+
+message Ext {
+ extend MyMessage {
+ optional Ext more = 103;
+ optional string text = 104;
+ optional int32 number = 105;
+ }
+
+ optional string data = 1;
+}
+
+extend MyMessage {
+ repeated string greeting = 106;
+}
+
+message MyMessageSet {
+ option message_set_wire_format = true;
+ extensions 100 to max;
+}
+
+message Empty {
+}
+
+extend MyMessageSet {
+ optional Empty x201 = 201;
+ optional Empty x202 = 202;
+ optional Empty x203 = 203;
+ optional Empty x204 = 204;
+ optional Empty x205 = 205;
+ optional Empty x206 = 206;
+ optional Empty x207 = 207;
+ optional Empty x208 = 208;
+ optional Empty x209 = 209;
+ optional Empty x210 = 210;
+ optional Empty x211 = 211;
+ optional Empty x212 = 212;
+ optional Empty x213 = 213;
+ optional Empty x214 = 214;
+ optional Empty x215 = 215;
+ optional Empty x216 = 216;
+ optional Empty x217 = 217;
+ optional Empty x218 = 218;
+ optional Empty x219 = 219;
+ optional Empty x220 = 220;
+ optional Empty x221 = 221;
+ optional Empty x222 = 222;
+ optional Empty x223 = 223;
+ optional Empty x224 = 224;
+ optional Empty x225 = 225;
+ optional Empty x226 = 226;
+ optional Empty x227 = 227;
+ optional Empty x228 = 228;
+ optional Empty x229 = 229;
+ optional Empty x230 = 230;
+ optional Empty x231 = 231;
+ optional Empty x232 = 232;
+ optional Empty x233 = 233;
+ optional Empty x234 = 234;
+ optional Empty x235 = 235;
+ optional Empty x236 = 236;
+ optional Empty x237 = 237;
+ optional Empty x238 = 238;
+ optional Empty x239 = 239;
+ optional Empty x240 = 240;
+ optional Empty x241 = 241;
+ optional Empty x242 = 242;
+ optional Empty x243 = 243;
+ optional Empty x244 = 244;
+ optional Empty x245 = 245;
+ optional Empty x246 = 246;
+ optional Empty x247 = 247;
+ optional Empty x248 = 248;
+ optional Empty x249 = 249;
+ optional Empty x250 = 250;
+}
+
+message MessageList {
+ repeated group Message = 1 {
+ required string name = 2;
+ required int32 count = 3;
+ }
+}
+
+message Strings {
+ optional string string_field = 1;
+ optional bytes bytes_field = 2;
+}
+
+message Defaults {
+ enum Color {
+ RED = 0;
+ GREEN = 1;
+ BLUE = 2;
+ }
+
+ // Default-valued fields of all basic types.
+ // Same as GoTest, but copied here to make testing easier.
+ optional bool F_Bool = 1 [default=true];
+ optional int32 F_Int32 = 2 [default=32];
+ optional int64 F_Int64 = 3 [default=64];
+ optional fixed32 F_Fixed32 = 4 [default=320];
+ optional fixed64 F_Fixed64 = 5 [default=640];
+ optional uint32 F_Uint32 = 6 [default=3200];
+ optional uint64 F_Uint64 = 7 [default=6400];
+ optional float F_Float = 8 [default=314159.];
+ optional double F_Double = 9 [default=271828.];
+ optional string F_String = 10 [default="hello, \"world!\"\n"];
+ optional bytes F_Bytes = 11 [default="Bignose"];
+ optional sint32 F_Sint32 = 12 [default=-32];
+ optional sint64 F_Sint64 = 13 [default=-64];
+ optional Color F_Enum = 14 [default=GREEN];
+
+ // More fields with crazy defaults.
+ optional float F_Pinf = 15 [default=inf];
+ optional float F_Ninf = 16 [default=-inf];
+ optional float F_Nan = 17 [default=nan];
+
+ // Sub-message.
+ optional SubDefaults sub = 18;
+
+ // Redundant but explicit defaults.
+ optional string str_zero = 19 [default=""];
+}
+
+message SubDefaults {
+ optional int64 n = 1 [default=7];
+}
+
+message RepeatedEnum {
+ enum Color {
+ RED = 1;
+ }
+ repeated Color color = 1;
+}
+
+message MoreRepeated {
+ repeated bool bools = 1;
+ repeated bool bools_packed = 2 [packed=true];
+ repeated int32 ints = 3;
+ repeated int32 ints_packed = 4 [packed=true];
+ repeated int64 int64s_packed = 7 [packed=true];
+ repeated string strings = 5;
+ repeated fixed32 fixeds = 6;
+}
+
+// GroupOld and GroupNew have the same wire format.
+// GroupNew has a new field inside a group.
+
+message GroupOld {
+ optional group G = 101 {
+ optional int32 x = 2;
+ }
+}
+
+message GroupNew {
+ optional group G = 101 {
+ optional int32 x = 2;
+ optional int32 y = 3;
+ }
+}
+
+message FloatingPoint {
+ required double f = 1;
+}
+
+message MessageWithMap {
+ map name_mapping = 1;
+ map msg_mapping = 2;
+ map byte_mapping = 3;
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go
new file mode 100644
index 000000000..720eac470
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text.go
@@ -0,0 +1,789 @@
+// 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"
+ "fmt"
+ "io"
+ "log"
+ "math"
+ "os"
+ "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.Printf("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
+}
+
+var (
+ messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem()
+)
+
+// raw is the interface satisfied by RawMessage.
+type raw interface {
+ Bytes() []byte
+}
+
+func writeStruct(w *textWriter, sv reflect.Value) error {
+ if sv.Type() == messageSetType {
+ return writeMessageSet(w, sv.Addr().Interface().(*MessageSet))
+ }
+
+ 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 := 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() // TODO: should we sort these for deterministic output?
+ 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 := writeAny(w, key, props.mkeyprop); err != nil {
+ return err
+ }
+ if err := w.WriteByte('\n'); err != nil {
+ return err
+ }
+ // value
+ if _, err := w.WriteString("value:"); err != nil {
+ return err
+ }
+ if !w.compact {
+ if err := w.WriteByte(' '); err != nil {
+ return err
+ }
+ }
+ if err := 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
+ switch fv.Kind() {
+ case reflect.Bool:
+ if !fv.Bool() {
+ continue
+ }
+ case reflect.Int32, reflect.Int64:
+ if fv.Int() == 0 {
+ continue
+ }
+ case reflect.Uint32, reflect.Uint64:
+ if fv.Uint() == 0 {
+ continue
+ }
+ case reflect.Float32, reflect.Float64:
+ if fv.Float() == 0 {
+ continue
+ }
+ case reflect.String:
+ if fv.String() == "" {
+ continue
+ }
+ }
+ }
+
+ 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 := 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 pv.Type().Implements(extendableProtoType) {
+ if err := 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 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.Interface().([]byte))); 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 tm, ok := v.Interface().(encoding.TextMarshaler); ok {
+ text, err := tm.MarshalText()
+ if err != nil {
+ return err
+ }
+ if _, err = w.Write(text); err != nil {
+ return err
+ }
+ } else if err := 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 writeMessageSet(w *textWriter, ms *MessageSet) error {
+ for _, item := range ms.Item {
+ id := *item.TypeId
+ if msd, ok := messageSetMap[id]; ok {
+ // Known message set type.
+ if _, err := fmt.Fprintf(w, "[%s]: <\n", msd.name); err != nil {
+ return err
+ }
+ w.indent()
+
+ pb := reflect.New(msd.t.Elem())
+ if err := Unmarshal(item.Message, pb.Interface().(Message)); err != nil {
+ if _, err := fmt.Fprintf(w, "/* bad message: %v */\n", err); err != nil {
+ return err
+ }
+ } else {
+ if err := writeStruct(w, pb.Elem()); err != nil {
+ return err
+ }
+ }
+ } else {
+ // Unknown type.
+ if _, err := fmt.Fprintf(w, "[%d]: <\n", id); err != nil {
+ return err
+ }
+ w.indent()
+ if err := writeUnknownStruct(w, item.Message); err != nil {
+ return err
+ }
+ }
+ w.unindent()
+ if _, err := w.Write(gtNewline); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+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 writeExtensions(w *textWriter, pv reflect.Value) error {
+ emap := extensionMaps[pv.Type().Elem()]
+ ep := pv.Interface().(extendableProto)
+
+ // Order the extensions by ID.
+ // This isn't strictly necessary, but it will give us
+ // canonical output, which will also make testing easier.
+ m := ep.ExtensionMap()
+ ids := make([]int32, 0, len(m))
+ for id := range m {
+ ids = append(ids, id)
+ }
+ sort.Sort(int32Slice(ids))
+
+ 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 {
+ if _, err := fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err); err != nil {
+ return err
+ }
+ continue
+ }
+
+ // Repeated extensions will appear as a slice.
+ if !desc.repeated() {
+ if err := writeExtension(w, desc.Name, pb); err != nil {
+ return err
+ }
+ } else {
+ v := reflect.ValueOf(pb)
+ for i := 0; i < v.Len(); i++ {
+ if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func 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 := 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
+}
+
+func marshalText(w io.Writer, pb Message, compact bool) 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: compact,
+ }
+
+ if tm, ok := pb.(encoding.TextMarshaler); ok {
+ text, err := tm.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 := writeStruct(aw, v); err != nil {
+ return err
+ }
+ if bw != nil {
+ return bw.Flush()
+ }
+ return nil
+}
+
+// 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 marshalText(w, pb, false)
+}
+
+// MarshalTextString is the same as MarshalText, but returns the string directly.
+func MarshalTextString(pb Message) string {
+ var buf bytes.Buffer
+ marshalText(&buf, pb, false)
+ return buf.String()
+}
+
+// CompactText writes a given protocol buffer in compact text format (one line).
+func CompactText(w io.Writer, pb Message) error { return marshalText(w, pb, true) }
+
+// CompactTextString is the same as CompactText, but returns the string directly.
+func CompactTextString(pb Message) string {
+ var buf bytes.Buffer
+ marshalText(&buf, pb, true)
+ return buf.String()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go
new file mode 100644
index 000000000..d1caeff5d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser.go
@@ -0,0 +1,757 @@
+// 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"
+)
+
+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 (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 %v", p.s[0:i+1])
+ 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 && p.cur.value[0] == '"' {
+ // Look for multiple quoted strings separated by whitespace,
+ // and concatenate them.
+ cat := p.cur
+ for {
+ p.skipWhitespace()
+ if p.done || 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(st reflect.Type, name string) (int, *Properties, bool) {
+ sprops := GetProperties(st)
+ 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()
+ reqCount := GetProperties(st).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]".
+ for {
+ tok := p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value == terminator {
+ break
+ }
+ if tok.value == "[" {
+ // Looks like an extension.
+ //
+ // TODO: Check whether we need to handle
+ // namespace rooted names (e.g. ".something.Foo").
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ 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 == tok.value {
+ desc = d
+ break
+ }
+ }
+ if desc == nil {
+ return p.errorf("unrecognized extension %q", tok.value)
+ }
+ // Check the extension terminator.
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != "]" {
+ return p.errorf("unrecognized extension terminator %q", tok.value)
+ }
+
+ 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().(extendableProto)
+ 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())
+ }
+ } else {
+ // This is a normal, non-extension field.
+ name := tok.value
+ fi, props, ok := structFieldByName(st, name)
+ if !ok {
+ return p.errorf("unknown field name %q in %v", name, st)
+ }
+
+ dst := sv.Field(fi)
+
+ 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 >
+ // Technically the "key" and "value" could come in any order,
+ // but in practice they won't.
+
+ tok := p.next()
+ var terminator string
+ switch tok.value {
+ case "<":
+ terminator = ">"
+ case "{":
+ terminator = "}"
+ default:
+ return p.errorf("expected '{' or '<', found %q", tok.value)
+ }
+ if err := p.consumeToken("key"); err != nil {
+ return err
+ }
+ if err := p.consumeToken(":"); err != nil {
+ return err
+ }
+ if err := p.readAny(key, props.mkeyprop); err != nil {
+ return err
+ }
+ if err := p.consumeToken("value"); err != nil {
+ return err
+ }
+ 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.consumeToken(terminator); err != nil {
+ return err
+ }
+
+ 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, st.Field(fi).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
+ } else if props.Required {
+ reqCount--
+ }
+ }
+
+ // For backward compatibility, permit a semicolon or comma after a field.
+ tok = p.next()
+ if tok.err != nil {
+ return tok.err
+ }
+ if tok.value != ";" && tok.value != "," {
+ p.back()
+ }
+ }
+
+ if reqCount > 0 {
+ return p.missingRequiredFieldError(sv)
+ }
+ return reqFieldErr
+}
+
+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. May already exist.
+ flen := fv.Len()
+ if flen == fv.Cap() {
+ nav := reflect.MakeSlice(at, flen, 2*flen+1)
+ reflect.Copy(nav, fv)
+ fv.Set(nav)
+ }
+ fv.SetLen(flen + 1)
+
+ // Read one.
+ p.back()
+ return p.readAny(fv.Index(flen), props)
+ case reflect.Bool:
+ // Either "true", "false", 1 or 0.
+ switch tok.value {
+ case "true", "1":
+ fv.SetBool(true)
+ return nil
+ case "false", "0":
+ 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(uint64(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/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go
new file mode 100644
index 000000000..590f6d4dd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_parser_test.go
@@ -0,0 +1,511 @@
+// 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_test
+
+import (
+ "math"
+ "reflect"
+ "testing"
+
+ proto3pb "./proto3_proto"
+ . "./testdata"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+type UnmarshalTextTest struct {
+ in string
+ err string // if "", no error expected
+ out *MyMessage
+}
+
+func buildExtStructTest(text string) UnmarshalTextTest {
+ msg := &MyMessage{
+ Count: Int32(42),
+ }
+ SetExtension(msg, E_Ext_More, &Ext{
+ Data: String("Hello, world!"),
+ })
+ return UnmarshalTextTest{in: text, out: msg}
+}
+
+func buildExtDataTest(text string) UnmarshalTextTest {
+ msg := &MyMessage{
+ Count: Int32(42),
+ }
+ SetExtension(msg, E_Ext_Text, String("Hello, world!"))
+ SetExtension(msg, E_Ext_Number, Int32(1729))
+ return UnmarshalTextTest{in: text, out: msg}
+}
+
+func buildExtRepStringTest(text string) UnmarshalTextTest {
+ msg := &MyMessage{
+ Count: Int32(42),
+ }
+ if err := SetExtension(msg, E_Greeting, []string{"bula", "hola"}); err != nil {
+ panic(err)
+ }
+ return UnmarshalTextTest{in: text, out: msg}
+}
+
+var unMarshalTextTests = []UnmarshalTextTest{
+ // Basic
+ {
+ in: " count:42\n name:\"Dave\" ",
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("Dave"),
+ },
+ },
+
+ // Empty quoted string
+ {
+ in: `count:42 name:""`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String(""),
+ },
+ },
+
+ // Quoted string concatenation
+ {
+ in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("My name is elsewhere"),
+ },
+ },
+
+ // Quoted string with escaped apostrophe
+ {
+ in: `count:42 name: "HOLIDAY - New Year\'s Day"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("HOLIDAY - New Year's Day"),
+ },
+ },
+
+ // Quoted string with single quote
+ {
+ in: `count:42 name: 'Roger "The Ramster" Ramjet'`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String(`Roger "The Ramster" Ramjet`),
+ },
+ },
+
+ // Quoted string with all the accepted special characters from the C++ test
+ {
+ in: `count:42 name: ` + "\"\\\"A string with \\' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"",
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces"),
+ },
+ },
+
+ // Quoted string with quoted backslash
+ {
+ in: `count:42 name: "\\'xyz"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String(`\'xyz`),
+ },
+ },
+
+ // Quoted string with UTF-8 bytes.
+ {
+ in: "count:42 name: '\303\277\302\201\xAB'",
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("\303\277\302\201\xAB"),
+ },
+ },
+
+ // Bad quoted string
+ {
+ in: `inner: < host: "\0" >` + "\n",
+ err: `line 1.15: invalid quoted string "\0"`,
+ },
+
+ // Number too large for int64
+ {
+ in: "count: 1 others { key: 123456789012345678901 }",
+ err: "line 1.23: invalid int64: 123456789012345678901",
+ },
+
+ // Number too large for int32
+ {
+ in: "count: 1234567890123",
+ err: "line 1.7: invalid int32: 1234567890123",
+ },
+
+ // Number in hexadecimal
+ {
+ in: "count: 0x2beef",
+ out: &MyMessage{
+ Count: Int32(0x2beef),
+ },
+ },
+
+ // Number in octal
+ {
+ in: "count: 024601",
+ out: &MyMessage{
+ Count: Int32(024601),
+ },
+ },
+
+ // Floating point number with "f" suffix
+ {
+ in: "count: 4 others:< weight: 17.0f >",
+ out: &MyMessage{
+ Count: Int32(4),
+ Others: []*OtherMessage{
+ {
+ Weight: Float32(17),
+ },
+ },
+ },
+ },
+
+ // Floating point positive infinity
+ {
+ in: "count: 4 bigfloat: inf",
+ out: &MyMessage{
+ Count: Int32(4),
+ Bigfloat: Float64(math.Inf(1)),
+ },
+ },
+
+ // Floating point negative infinity
+ {
+ in: "count: 4 bigfloat: -inf",
+ out: &MyMessage{
+ Count: Int32(4),
+ Bigfloat: Float64(math.Inf(-1)),
+ },
+ },
+
+ // Number too large for float32
+ {
+ in: "others:< weight: 12345678901234567890123456789012345678901234567890 >",
+ err: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890",
+ },
+
+ // Number posing as a quoted string
+ {
+ in: `inner: < host: 12 >` + "\n",
+ err: `line 1.15: invalid string: 12`,
+ },
+
+ // Quoted string posing as int32
+ {
+ in: `count: "12"`,
+ err: `line 1.7: invalid int32: "12"`,
+ },
+
+ // Quoted string posing a float32
+ {
+ in: `others:< weight: "17.4" >`,
+ err: `line 1.17: invalid float32: "17.4"`,
+ },
+
+ // Enum
+ {
+ in: `count:42 bikeshed: BLUE`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Bikeshed: MyMessage_BLUE.Enum(),
+ },
+ },
+
+ // Repeated field
+ {
+ in: `count:42 pet: "horsey" pet:"bunny"`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Pet: []string{"horsey", "bunny"},
+ },
+ },
+
+ // Repeated message with/without colon and <>/{}
+ {
+ in: `count:42 others:{} others{} others:<> others:{}`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Others: []*OtherMessage{
+ {},
+ {},
+ {},
+ {},
+ },
+ },
+ },
+
+ // Missing colon for inner message
+ {
+ in: `count:42 inner < host: "cauchy.syd" >`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Inner: &InnerMessage{
+ Host: String("cauchy.syd"),
+ },
+ },
+ },
+
+ // Missing colon for string field
+ {
+ in: `name "Dave"`,
+ err: `line 1.5: expected ':', found "\"Dave\""`,
+ },
+
+ // Missing colon for int32 field
+ {
+ in: `count 42`,
+ err: `line 1.6: expected ':', found "42"`,
+ },
+
+ // Missing required field
+ {
+ in: `name: "Pawel"`,
+ err: `proto: required field "testdata.MyMessage.count" not set`,
+ out: &MyMessage{
+ Name: String("Pawel"),
+ },
+ },
+
+ // Repeated non-repeated field
+ {
+ in: `name: "Rob" name: "Russ"`,
+ err: `line 1.12: non-repeated field "name" was repeated`,
+ },
+
+ // Group
+ {
+ in: `count: 17 SomeGroup { group_field: 12 }`,
+ out: &MyMessage{
+ Count: Int32(17),
+ Somegroup: &MyMessage_SomeGroup{
+ GroupField: Int32(12),
+ },
+ },
+ },
+
+ // Semicolon between fields
+ {
+ in: `count:3;name:"Calvin"`,
+ out: &MyMessage{
+ Count: Int32(3),
+ Name: String("Calvin"),
+ },
+ },
+ // Comma between fields
+ {
+ in: `count:4,name:"Ezekiel"`,
+ out: &MyMessage{
+ Count: Int32(4),
+ Name: String("Ezekiel"),
+ },
+ },
+
+ // Extension
+ buildExtStructTest(`count: 42 [testdata.Ext.more]:`),
+ buildExtStructTest(`count: 42 [testdata.Ext.more] {data:"Hello, world!"}`),
+ buildExtDataTest(`count: 42 [testdata.Ext.text]:"Hello, world!" [testdata.Ext.number]:1729`),
+ buildExtRepStringTest(`count: 42 [testdata.greeting]:"bula" [testdata.greeting]:"hola"`),
+
+ // Big all-in-one
+ {
+ in: "count:42 # Meaning\n" +
+ `name:"Dave" ` +
+ `quote:"\"I didn't want to go.\"" ` +
+ `pet:"bunny" ` +
+ `pet:"kitty" ` +
+ `pet:"horsey" ` +
+ `inner:<` +
+ ` host:"footrest.syd" ` +
+ ` port:7001 ` +
+ ` connected:true ` +
+ `> ` +
+ `others:<` +
+ ` key:3735928559 ` +
+ ` value:"\x01A\a\f" ` +
+ `> ` +
+ `others:<` +
+ " weight:58.9 # Atomic weight of Co\n" +
+ ` inner:<` +
+ ` host:"lesha.mtv" ` +
+ ` port:8002 ` +
+ ` >` +
+ `>`,
+ out: &MyMessage{
+ Count: Int32(42),
+ Name: String("Dave"),
+ Quote: String(`"I didn't want to go."`),
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Inner: &InnerMessage{
+ Host: String("footrest.syd"),
+ Port: Int32(7001),
+ Connected: Bool(true),
+ },
+ Others: []*OtherMessage{
+ {
+ Key: Int64(3735928559),
+ Value: []byte{0x1, 'A', '\a', '\f'},
+ },
+ {
+ Weight: Float32(58.9),
+ Inner: &InnerMessage{
+ Host: String("lesha.mtv"),
+ Port: Int32(8002),
+ },
+ },
+ },
+ },
+ },
+}
+
+func TestUnmarshalText(t *testing.T) {
+ for i, test := range unMarshalTextTests {
+ pb := new(MyMessage)
+ err := UnmarshalText(test.in, pb)
+ if test.err == "" {
+ // We don't expect failure.
+ if err != nil {
+ t.Errorf("Test %d: Unexpected error: %v", i, err)
+ } else if !reflect.DeepEqual(pb, test.out) {
+ t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
+ i, pb, test.out)
+ }
+ } else {
+ // We do expect failure.
+ if err == nil {
+ t.Errorf("Test %d: Didn't get expected error: %v", i, test.err)
+ } else if err.Error() != test.err {
+ t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v",
+ i, err.Error(), test.err)
+ } else if _, ok := err.(*RequiredNotSetError); ok && test.out != nil && !reflect.DeepEqual(pb, test.out) {
+ t.Errorf("Test %d: Incorrect populated \nHave: %v\nWant: %v",
+ i, pb, test.out)
+ }
+ }
+ }
+}
+
+func TestUnmarshalTextCustomMessage(t *testing.T) {
+ msg := &textMessage{}
+ if err := UnmarshalText("custom", msg); err != nil {
+ t.Errorf("Unexpected error from custom unmarshal: %v", err)
+ }
+ if UnmarshalText("not custom", msg) == nil {
+ t.Errorf("Didn't get expected error from custom unmarshal")
+ }
+}
+
+// Regression test; this caused a panic.
+func TestRepeatedEnum(t *testing.T) {
+ pb := new(RepeatedEnum)
+ if err := UnmarshalText("color: RED", pb); err != nil {
+ t.Fatal(err)
+ }
+ exp := &RepeatedEnum{
+ Color: []RepeatedEnum_Color{RepeatedEnum_RED},
+ }
+ if !Equal(pb, exp) {
+ t.Errorf("Incorrect populated \nHave: %v\nWant: %v", pb, exp)
+ }
+}
+
+func TestProto3TextParsing(t *testing.T) {
+ m := new(proto3pb.Message)
+ const in = `name: "Wallace" true_scotsman: true`
+ want := &proto3pb.Message{
+ Name: "Wallace",
+ TrueScotsman: true,
+ }
+ if err := UnmarshalText(in, m); err != nil {
+ t.Fatal(err)
+ }
+ if !Equal(m, want) {
+ t.Errorf("\n got %v\nwant %v", m, want)
+ }
+}
+
+func TestMapParsing(t *testing.T) {
+ m := new(MessageWithMap)
+ const in = `name_mapping: name_mapping:` +
+ `msg_mapping:>` +
+ `msg_mapping>` + // no colon after "value"
+ `byte_mapping:`
+ want := &MessageWithMap{
+ NameMapping: map[int32]string{
+ 1: "Beatles",
+ 1234: "Feist",
+ },
+ MsgMapping: map[int64]*FloatingPoint{
+ -4: {F: Float64(2.0)},
+ -2: {F: Float64(4.0)},
+ },
+ ByteMapping: map[bool][]byte{
+ true: []byte("so be it"),
+ },
+ }
+ if err := UnmarshalText(in, m); err != nil {
+ t.Fatal(err)
+ }
+ if !Equal(m, want) {
+ t.Errorf("\n got %v\nwant %v", m, want)
+ }
+}
+
+var benchInput string
+
+func init() {
+ benchInput = "count: 4\n"
+ for i := 0; i < 1000; i++ {
+ benchInput += "pet: \"fido\"\n"
+ }
+
+ // Check it is valid input.
+ pb := new(MyMessage)
+ err := UnmarshalText(benchInput, pb)
+ if err != nil {
+ panic("Bad benchmark input: " + err.Error())
+ }
+}
+
+func BenchmarkUnmarshalText(b *testing.B) {
+ pb := new(MyMessage)
+ for i := 0; i < b.N; i++ {
+ UnmarshalText(benchInput, pb)
+ }
+ b.SetBytes(int64(len(benchInput)))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go
new file mode 100644
index 000000000..888eacd2c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/text_test.go
@@ -0,0 +1,436 @@
+// 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_test
+
+import (
+ "bytes"
+ "errors"
+ "io/ioutil"
+ "math"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+
+ proto3pb "./proto3_proto"
+ pb "./testdata"
+)
+
+// textMessage implements the methods that allow it to marshal and unmarshal
+// itself as text.
+type textMessage struct {
+}
+
+func (*textMessage) MarshalText() ([]byte, error) {
+ return []byte("custom"), nil
+}
+
+func (*textMessage) UnmarshalText(bytes []byte) error {
+ if string(bytes) != "custom" {
+ return errors.New("expected 'custom'")
+ }
+ return nil
+}
+
+func (*textMessage) Reset() {}
+func (*textMessage) String() string { return "" }
+func (*textMessage) ProtoMessage() {}
+
+func newTestMessage() *pb.MyMessage {
+ msg := &pb.MyMessage{
+ Count: proto.Int32(42),
+ Name: proto.String("Dave"),
+ Quote: proto.String(`"I didn't want to go."`),
+ Pet: []string{"bunny", "kitty", "horsey"},
+ Inner: &pb.InnerMessage{
+ Host: proto.String("footrest.syd"),
+ Port: proto.Int32(7001),
+ Connected: proto.Bool(true),
+ },
+ Others: []*pb.OtherMessage{
+ {
+ Key: proto.Int64(0xdeadbeef),
+ Value: []byte{1, 65, 7, 12},
+ },
+ {
+ Weight: proto.Float32(6.022),
+ Inner: &pb.InnerMessage{
+ Host: proto.String("lesha.mtv"),
+ Port: proto.Int32(8002),
+ },
+ },
+ },
+ Bikeshed: pb.MyMessage_BLUE.Enum(),
+ Somegroup: &pb.MyMessage_SomeGroup{
+ GroupField: proto.Int32(8),
+ },
+ // One normally wouldn't do this.
+ // This is an undeclared tag 13, as a varint (wire type 0) with value 4.
+ XXX_unrecognized: []byte{13<<3 | 0, 4},
+ }
+ ext := &pb.Ext{
+ Data: proto.String("Big gobs for big rats"),
+ }
+ if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil {
+ panic(err)
+ }
+ greetings := []string{"adg", "easy", "cow"}
+ if err := proto.SetExtension(msg, pb.E_Greeting, greetings); err != nil {
+ panic(err)
+ }
+
+ // Add an unknown extension. We marshal a pb.Ext, and fake the ID.
+ b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")})
+ if err != nil {
+ panic(err)
+ }
+ b = append(proto.EncodeVarint(201<<3|proto.WireBytes), b...)
+ proto.SetRawExtension(msg, 201, b)
+
+ // Extensions can be plain fields, too, so let's test that.
+ b = append(proto.EncodeVarint(202<<3|proto.WireVarint), 19)
+ proto.SetRawExtension(msg, 202, b)
+
+ return msg
+}
+
+const text = `count: 42
+name: "Dave"
+quote: "\"I didn't want to go.\""
+pet: "bunny"
+pet: "kitty"
+pet: "horsey"
+inner: <
+ host: "footrest.syd"
+ port: 7001
+ connected: true
+>
+others: <
+ key: 3735928559
+ value: "\001A\007\014"
+>
+others: <
+ weight: 6.022
+ inner: <
+ host: "lesha.mtv"
+ port: 8002
+ >
+>
+bikeshed: BLUE
+SomeGroup {
+ group_field: 8
+}
+/* 2 unknown bytes */
+13: 4
+[testdata.Ext.more]: <
+ data: "Big gobs for big rats"
+>
+[testdata.greeting]: "adg"
+[testdata.greeting]: "easy"
+[testdata.greeting]: "cow"
+/* 13 unknown bytes */
+201: "\t3G skiing"
+/* 3 unknown bytes */
+202: 19
+`
+
+func TestMarshalText(t *testing.T) {
+ buf := new(bytes.Buffer)
+ if err := proto.MarshalText(buf, newTestMessage()); err != nil {
+ t.Fatalf("proto.MarshalText: %v", err)
+ }
+ s := buf.String()
+ if s != text {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text)
+ }
+}
+
+func TestMarshalTextCustomMessage(t *testing.T) {
+ buf := new(bytes.Buffer)
+ if err := proto.MarshalText(buf, &textMessage{}); err != nil {
+ t.Fatalf("proto.MarshalText: %v", err)
+ }
+ s := buf.String()
+ if s != "custom" {
+ t.Errorf("Got %q, expected %q", s, "custom")
+ }
+}
+func TestMarshalTextNil(t *testing.T) {
+ want := ""
+ tests := []proto.Message{nil, (*pb.MyMessage)(nil)}
+ for i, test := range tests {
+ buf := new(bytes.Buffer)
+ if err := proto.MarshalText(buf, test); err != nil {
+ t.Fatal(err)
+ }
+ if got := buf.String(); got != want {
+ t.Errorf("%d: got %q want %q", i, got, want)
+ }
+ }
+}
+
+func TestMarshalTextUnknownEnum(t *testing.T) {
+ // The Color enum only specifies values 0-2.
+ m := &pb.MyMessage{Bikeshed: pb.MyMessage_Color(3).Enum()}
+ got := m.String()
+ const want = `bikeshed:3 `
+ if got != want {
+ t.Errorf("\n got %q\nwant %q", got, want)
+ }
+}
+
+func BenchmarkMarshalTextBuffered(b *testing.B) {
+ buf := new(bytes.Buffer)
+ m := newTestMessage()
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ proto.MarshalText(buf, m)
+ }
+}
+
+func BenchmarkMarshalTextUnbuffered(b *testing.B) {
+ w := ioutil.Discard
+ m := newTestMessage()
+ for i := 0; i < b.N; i++ {
+ proto.MarshalText(w, m)
+ }
+}
+
+func compact(src string) string {
+ // s/[ \n]+/ /g; s/ $//;
+ dst := make([]byte, len(src))
+ space, comment := false, false
+ j := 0
+ for i := 0; i < len(src); i++ {
+ if strings.HasPrefix(src[i:], "/*") {
+ comment = true
+ i++
+ continue
+ }
+ if comment && strings.HasPrefix(src[i:], "*/") {
+ comment = false
+ i++
+ continue
+ }
+ if comment {
+ continue
+ }
+ c := src[i]
+ if c == ' ' || c == '\n' {
+ space = true
+ continue
+ }
+ if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') {
+ space = false
+ }
+ if c == '{' {
+ space = false
+ }
+ if space {
+ dst[j] = ' '
+ j++
+ space = false
+ }
+ dst[j] = c
+ j++
+ }
+ if space {
+ dst[j] = ' '
+ j++
+ }
+ return string(dst[0:j])
+}
+
+var compactText = compact(text)
+
+func TestCompactText(t *testing.T) {
+ s := proto.CompactTextString(newTestMessage())
+ if s != compactText {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText)
+ }
+}
+
+func TestStringEscaping(t *testing.T) {
+ testCases := []struct {
+ in *pb.Strings
+ out string
+ }{
+ {
+ // Test data from C++ test (TextFormatTest.StringEscape).
+ // Single divergence: we don't escape apostrophes.
+ &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")},
+ "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n",
+ },
+ {
+ // Test data from the same C++ test.
+ &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")},
+ "string_field: \"\\350\\260\\267\\346\\255\\214\"\n",
+ },
+ {
+ // Some UTF-8.
+ &pb.Strings{StringField: proto.String("\x00\x01\xff\x81")},
+ `string_field: "\000\001\377\201"` + "\n",
+ },
+ }
+
+ for i, tc := range testCases {
+ var buf bytes.Buffer
+ if err := proto.MarshalText(&buf, tc.in); err != nil {
+ t.Errorf("proto.MarsalText: %v", err)
+ continue
+ }
+ s := buf.String()
+ if s != tc.out {
+ t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out)
+ continue
+ }
+
+ // Check round-trip.
+ pb := new(pb.Strings)
+ if err := proto.UnmarshalText(s, pb); err != nil {
+ t.Errorf("#%d: UnmarshalText: %v", i, err)
+ continue
+ }
+ if !proto.Equal(pb, tc.in) {
+ t.Errorf("#%d: Round-trip failed:\nstart: %v\n end: %v", i, tc.in, pb)
+ }
+ }
+}
+
+// A limitedWriter accepts some output before it fails.
+// This is a proxy for something like a nearly-full or imminently-failing disk,
+// or a network connection that is about to die.
+type limitedWriter struct {
+ b bytes.Buffer
+ limit int
+}
+
+var outOfSpace = errors.New("proto: insufficient space")
+
+func (w *limitedWriter) Write(p []byte) (n int, err error) {
+ var avail = w.limit - w.b.Len()
+ if avail <= 0 {
+ return 0, outOfSpace
+ }
+ if len(p) <= avail {
+ return w.b.Write(p)
+ }
+ n, _ = w.b.Write(p[:avail])
+ return n, outOfSpace
+}
+
+func TestMarshalTextFailing(t *testing.T) {
+ // Try lots of different sizes to exercise more error code-paths.
+ for lim := 0; lim < len(text); lim++ {
+ buf := new(limitedWriter)
+ buf.limit = lim
+ err := proto.MarshalText(buf, newTestMessage())
+ // We expect a certain error, but also some partial results in the buffer.
+ if err != outOfSpace {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", err, outOfSpace)
+ }
+ s := buf.b.String()
+ x := text[:buf.limit]
+ if s != x {
+ t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, x)
+ }
+ }
+}
+
+func TestFloats(t *testing.T) {
+ tests := []struct {
+ f float64
+ want string
+ }{
+ {0, "0"},
+ {4.7, "4.7"},
+ {math.Inf(1), "inf"},
+ {math.Inf(-1), "-inf"},
+ {math.NaN(), "nan"},
+ }
+ for _, test := range tests {
+ msg := &pb.FloatingPoint{F: &test.f}
+ got := strings.TrimSpace(msg.String())
+ want := `f:` + test.want
+ if got != want {
+ t.Errorf("f=%f: got %q, want %q", test.f, got, want)
+ }
+ }
+}
+
+func TestRepeatedNilText(t *testing.T) {
+ m := &pb.MessageList{
+ Message: []*pb.MessageList_Message{
+ nil,
+ &pb.MessageList_Message{
+ Name: proto.String("Horse"),
+ },
+ nil,
+ },
+ }
+ want := `Message
+Message {
+ name: "Horse"
+}
+Message
+`
+ if s := proto.MarshalTextString(m); s != want {
+ t.Errorf(" got: %s\nwant: %s", s, want)
+ }
+}
+
+func TestProto3Text(t *testing.T) {
+ tests := []struct {
+ m proto.Message
+ want string
+ }{
+ // zero message
+ {&proto3pb.Message{}, ``},
+ // zero message except for an empty byte slice
+ {&proto3pb.Message{Data: []byte{}}, ``},
+ // trivial case
+ {&proto3pb.Message{Name: "Rob", HeightInCm: 175}, `name:"Rob" height_in_cm:175`},
+ // empty map
+ {&pb.MessageWithMap{}, ``},
+ // non-empty map; current map format is the same as a repeated struct
+ {
+ &pb.MessageWithMap{NameMapping: map[int32]string{1234: "Feist"}},
+ `name_mapping:`,
+ },
+ }
+ for _, test := range tests {
+ got := strings.TrimSpace(test.m.String())
+ if got != test.want {
+ t.Errorf("\n got %s\nwant %s", got, test.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/LICENSE
new file mode 100644
index 000000000..d64569567
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/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/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/README.md
new file mode 100644
index 000000000..26d687ef8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/README.md
@@ -0,0 +1,12 @@
+# BTree implementation for Go
+
+
+
+This package provides an in-memory B-Tree implementation for Go, useful as a
+an ordered, mutable data structure.
+
+The API is based off of the wonderful
+http://godoc.org/github.com/petar/GoLLRB/llrb, and is meant to allow btree to
+act as a drop-in replacement for gollrb trees.
+
+See http://godoc.org/github.com/google/btree for documentation.
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree.go
new file mode 100644
index 000000000..eb1f75cda
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree.go
@@ -0,0 +1,571 @@
+// 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
+// implmentation 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 or backwards iteration.
+package btree
+
+import (
+ "fmt"
+ "io"
+ "sort"
+ "strings"
+)
+
+// 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).
+ Less(than Item) bool
+}
+
+// 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) *BTree {
+ if degree <= 1 {
+ panic("bad degree")
+ }
+ return &BTree{
+ degree: degree,
+ freelist: make([]*node, 0, 32),
+ }
+}
+
+// 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 = (*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 = (*s)[index], (*s)[:index]
+ return
+}
+
+// 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) (index int, found bool) {
+ i := sort.Search(len(s), func(i int) bool {
+ return item.Less(s[i])
+ })
+ if i > 0 && !s[i-1].Less(item) {
+ 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 = (*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 = (*s)[index], (*s)[:index]
+ return
+}
+
+// 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
+ t *BTree
+}
+
+// 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.t.newNode()
+ next.items = append(next.items, n.items[i+1:]...)
+ n.items = n.items[:i]
+ if len(n.children) > 0 {
+ next.children = append(next.children, n.children[i+1:]...)
+ n.children = n.children[: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.children[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) Item {
+ i, found := n.items.find(item)
+ 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):
+ // no change, we want first split node
+ case inTree.Less(item):
+ i++ // we want second split node
+ default:
+ out := n.items[i]
+ n.items[i] = item
+ return out
+ }
+ }
+ return n.children[i].insert(item, maxItems)
+}
+
+// get finds the given key in the subtree and returns it.
+func (n *node) get(key Item) Item {
+ i, found := n.items.find(key)
+ if found {
+ return n.items[i]
+ } else if len(n.children) > 0 {
+ return n.children[i].get(key)
+ }
+ return nil
+}
+
+// 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) 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)
+ 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.
+ child := n.children[i]
+ if len(child.items) <= minItems {
+ return n.growChildAndRemove(i, item, minItems, typ)
+ }
+ // 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)
+ 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)
+}
+
+// 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) Item {
+ child := n.children[i]
+ if i > 0 && len(n.children[i-1].items) > minItems {
+ // Steal from left child
+ stealFrom := n.children[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
+ stealFrom := n.children[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.children[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.t.freeNode(mergeChild)
+ }
+ return n.remove(item, minItems, typ)
+}
+
+// iterate provides a simple method for iterating over elements in the tree.
+// It could probably use some work to be extra-efficient (it calls from() a
+// little more than it should), but it works pretty well for now.
+//
+// It requires that 'from' and 'to' both return true for values we should hit
+// with the iterator. It should also be the case that 'from' returns true for
+// values less than or equal to values 'to' returns true for, and 'to'
+// returns true for values greater than or equal to those that 'from'
+// does.
+func (n *node) iterate(from, to func(Item) bool, iter ItemIterator) bool {
+ for i, item := range n.items {
+ if !from(item) {
+ continue
+ }
+ if len(n.children) > 0 && !n.children[i].iterate(from, to, iter) {
+ return false
+ }
+ if !to(item) {
+ return false
+ }
+ if !iter(item) {
+ return false
+ }
+ }
+ if len(n.children) > 0 {
+ return n.children[len(n.children)-1].iterate(from, to, iter)
+ }
+ return 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
+ freelist []*node
+}
+
+// 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 (t *BTree) newNode() (n *node) {
+ index := len(t.freelist) - 1
+ if index < 0 {
+ return &node{t: t}
+ }
+ t.freelist, n = t.freelist[:index], t.freelist[index]
+ return
+}
+
+func (t *BTree) freeNode(n *node) {
+ if len(t.freelist) < cap(t.freelist) {
+ for i := range n.items {
+ n.items[i] = nil // clear to allow GC
+ }
+ n.items = n.items[:0]
+ for i := range n.children {
+ n.children[i] = nil // clear to allow GC
+ }
+ n.children = n.children[:0]
+ t.freelist = append(t.freelist, 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.newNode()
+ t.root.items = append(t.root.items, item)
+ t.length++
+ return nil
+ } else if len(t.root.items) >= t.maxItems() {
+ item2, second := t.root.split(t.maxItems() / 2)
+ oldroot := t.root
+ t.root = t.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())
+ 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)
+}
+
+// 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)
+}
+
+// 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)
+}
+
+func (t *BTree) deleteItem(item Item, typ toRemove) Item {
+ if t.root == nil || len(t.root.items) == 0 {
+ return nil
+ }
+ out := t.root.remove(item, t.minItems(), typ)
+ if len(t.root.items) == 0 && len(t.root.children) > 0 {
+ oldroot := t.root
+ t.root = t.root.children[0]
+ t.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(
+ func(a Item) bool { return !a.Less(greaterOrEqual) },
+ func(a Item) bool { return a.Less(lessThan) },
+ iterator)
+}
+
+// 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(
+ func(a Item) bool { return true },
+ func(a Item) bool { return a.Less(pivot) },
+ iterator)
+}
+
+// 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(
+ func(a Item) bool { return !a.Less(pivot) },
+ func(a Item) bool { return true },
+ iterator)
+}
+
+// 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(
+ func(a Item) bool { return true },
+ func(a Item) bool { return true },
+ iterator)
+}
+
+// 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)
+}
+
+// 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) bool {
+ return a < b.(Int)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree_mem.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree_mem.go
new file mode 100644
index 000000000..36717e60d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree_mem.go
@@ -0,0 +1,76 @@
+// 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.
+
+// +build ignore
+
+// This binary compares memory usage between btree and gollrb.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "math/rand"
+ "runtime"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree"
+ "github.com/petar/GoLLRB/llrb"
+)
+
+var (
+ size = flag.Int("size", 1000000, "size of the tree to build")
+ degree = flag.Int("degree", 8, "degree of btree")
+ gollrb = flag.Bool("llrb", false, "use llrb instead of btree")
+)
+
+func main() {
+ flag.Parse()
+ vals := rand.Perm(*size)
+ var t, v interface{}
+ v = vals
+ var stats runtime.MemStats
+ for i := 0; i < 10; i++ {
+ runtime.GC()
+ }
+ fmt.Println("-------- BEFORE ----------")
+ runtime.ReadMemStats(&stats)
+ fmt.Printf("%+v\n", stats)
+ start := time.Now()
+ if *gollrb {
+ tr := llrb.New()
+ for _, v := range vals {
+ tr.ReplaceOrInsert(llrb.Int(v))
+ }
+ t = tr // keep it around
+ } else {
+ tr := btree.New(*degree)
+ for _, v := range vals {
+ tr.ReplaceOrInsert(btree.Int(v))
+ }
+ t = tr // keep it around
+ }
+ fmt.Printf("%v inserts in %v\n", *size, time.Since(start))
+ fmt.Println("-------- AFTER ----------")
+ runtime.ReadMemStats(&stats)
+ fmt.Printf("%+v\n", stats)
+ for i := 0; i < 10; i++ {
+ runtime.GC()
+ }
+ fmt.Println("-------- AFTER GC ----------")
+ runtime.ReadMemStats(&stats)
+ fmt.Printf("%+v\n", stats)
+ if t == v {
+ fmt.Println("to make sure vals and tree aren't GC'd")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree_test.go
new file mode 100644
index 000000000..43dcea63b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree/btree_test.go
@@ -0,0 +1,293 @@
+// 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
+
+import (
+ "flag"
+ "fmt"
+ "math/rand"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func init() {
+ seed := time.Now().Unix()
+ fmt.Println(seed)
+ rand.Seed(seed)
+}
+
+// perm returns a random permutation of n Int items in the range [0, n).
+func perm(n int) (out []Item) {
+ for _, v := range rand.Perm(n) {
+ out = append(out, Int(v))
+ }
+ return
+}
+
+// rang returns an ordered list of Int items in the range [0, n).
+func rang(n int) (out []Item) {
+ for i := 0; i < n; i++ {
+ out = append(out, Int(i))
+ }
+ return
+}
+
+// all extracts all items from a tree in order as a slice.
+func all(t *BTree) (out []Item) {
+ t.Ascend(func(a Item) bool {
+ out = append(out, a)
+ return true
+ })
+ return
+}
+
+var btreeDegree = flag.Int("degree", 32, "B-Tree degree")
+
+func TestBTree(t *testing.T) {
+ tr := New(*btreeDegree)
+ const treeSize = 10000
+ for i := 0; i < 10; i++ {
+ for _, item := range perm(treeSize) {
+ if x := tr.ReplaceOrInsert(item); x != nil {
+ t.Fatal("insert found item", item)
+ }
+ }
+ for _, item := range perm(treeSize) {
+ if x := tr.ReplaceOrInsert(item); x == nil {
+ t.Fatal("insert didn't find item", item)
+ }
+ }
+ got := all(tr)
+ want := rang(treeSize)
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
+ }
+ for _, item := range perm(treeSize) {
+ if x := tr.Delete(item); x == nil {
+ t.Fatalf("didn't find %v", item)
+ }
+ }
+ if got = all(tr); len(got) > 0 {
+ t.Fatalf("some left!: %v", got)
+ }
+ }
+}
+
+func ExampleBTree() {
+ tr := New(*btreeDegree)
+ for i := Int(0); i < 10; i++ {
+ tr.ReplaceOrInsert(i)
+ }
+ fmt.Println("len: ", tr.Len())
+ fmt.Println("get3: ", tr.Get(Int(3)))
+ fmt.Println("get100: ", tr.Get(Int(100)))
+ fmt.Println("del4: ", tr.Delete(Int(4)))
+ fmt.Println("del100: ", tr.Delete(Int(100)))
+ fmt.Println("replace5: ", tr.ReplaceOrInsert(Int(5)))
+ fmt.Println("replace100:", tr.ReplaceOrInsert(Int(100)))
+ fmt.Println("delmin: ", tr.DeleteMin())
+ fmt.Println("delmax: ", tr.DeleteMax())
+ fmt.Println("len: ", tr.Len())
+ // Output:
+ // len: 10
+ // get3: 3
+ // get100:
+ // del4: 4
+ // del100:
+ // replace5: 5
+ // replace100:
+ // delmin: 0
+ // delmax: 100
+ // len: 8
+}
+
+func TestDeleteMin(t *testing.T) {
+ tr := New(3)
+ for _, v := range perm(100) {
+ tr.ReplaceOrInsert(v)
+ }
+ var got []Item
+ for v := tr.DeleteMin(); v != nil; v = tr.DeleteMin() {
+ got = append(got, v)
+ }
+ if want := rang(100); !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+}
+
+func TestDeleteMax(t *testing.T) {
+ tr := New(3)
+ for _, v := range perm(100) {
+ tr.ReplaceOrInsert(v)
+ }
+ var got []Item
+ for v := tr.DeleteMax(); v != nil; v = tr.DeleteMax() {
+ got = append(got, v)
+ }
+ // Reverse our list.
+ for i := 0; i < len(got)/2; i++ {
+ got[i], got[len(got)-i-1] = got[len(got)-i-1], got[i]
+ }
+ if want := rang(100); !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+}
+
+func TestAscendRange(t *testing.T) {
+ tr := New(2)
+ for _, v := range perm(100) {
+ tr.ReplaceOrInsert(v)
+ }
+ var got []Item
+ tr.AscendRange(Int(40), Int(60), func(a Item) bool {
+ got = append(got, a)
+ return true
+ })
+ if want := rang(100)[40:60]; !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+ got = got[:0]
+ tr.AscendRange(Int(40), Int(60), func(a Item) bool {
+ if a.(Int) > 50 {
+ return false
+ }
+ got = append(got, a)
+ return true
+ })
+ if want := rang(100)[40:51]; !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+}
+
+func TestAscendLessThan(t *testing.T) {
+ tr := New(*btreeDegree)
+ for _, v := range perm(100) {
+ tr.ReplaceOrInsert(v)
+ }
+ var got []Item
+ tr.AscendLessThan(Int(60), func(a Item) bool {
+ got = append(got, a)
+ return true
+ })
+ if want := rang(100)[:60]; !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+ got = got[:0]
+ tr.AscendLessThan(Int(60), func(a Item) bool {
+ if a.(Int) > 50 {
+ return false
+ }
+ got = append(got, a)
+ return true
+ })
+ if want := rang(100)[:51]; !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+}
+
+func TestAscendGreaterOrEqual(t *testing.T) {
+ tr := New(*btreeDegree)
+ for _, v := range perm(100) {
+ tr.ReplaceOrInsert(v)
+ }
+ var got []Item
+ tr.AscendGreaterOrEqual(Int(40), func(a Item) bool {
+ got = append(got, a)
+ return true
+ })
+ if want := rang(100)[40:]; !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+ got = got[:0]
+ tr.AscendGreaterOrEqual(Int(40), func(a Item) bool {
+ if a.(Int) > 50 {
+ return false
+ }
+ got = append(got, a)
+ return true
+ })
+ if want := rang(100)[40:51]; !reflect.DeepEqual(got, want) {
+ t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
+ }
+}
+
+const benchmarkTreeSize = 10000
+
+func BenchmarkInsert(b *testing.B) {
+ b.StopTimer()
+ insertP := perm(benchmarkTreeSize)
+ b.StartTimer()
+ i := 0
+ for i < b.N {
+ tr := New(*btreeDegree)
+ for _, item := range insertP {
+ tr.ReplaceOrInsert(item)
+ i++
+ if i >= b.N {
+ return
+ }
+ }
+ }
+}
+
+func BenchmarkDelete(b *testing.B) {
+ b.StopTimer()
+ insertP := perm(benchmarkTreeSize)
+ removeP := perm(benchmarkTreeSize)
+ b.StartTimer()
+ i := 0
+ for i < b.N {
+ b.StopTimer()
+ tr := New(*btreeDegree)
+ for _, v := range insertP {
+ tr.ReplaceOrInsert(v)
+ }
+ b.StartTimer()
+ for _, item := range removeP {
+ tr.Delete(item)
+ i++
+ if i >= b.N {
+ return
+ }
+ }
+ if tr.Len() > 0 {
+ panic(tr.Len())
+ }
+ }
+}
+
+func BenchmarkGet(b *testing.B) {
+ b.StopTimer()
+ insertP := perm(benchmarkTreeSize)
+ removeP := perm(benchmarkTreeSize)
+ b.StartTimer()
+ i := 0
+ for i < b.N {
+ b.StopTimer()
+ tr := New(*btreeDegree)
+ for _, v := range insertP {
+ tr.ReplaceOrInsert(v)
+ }
+ b.StartTimer()
+ for _, item := range removeP {
+ tr.Get(item)
+ i++
+ if i >= b.N {
+ return
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/LICENSE
new file mode 100644
index 000000000..5c304d1a4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/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/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/README.md
new file mode 100644
index 000000000..236c86a24
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/README.md
@@ -0,0 +1,58 @@
+clockwork
+=========
+
+a simple fake clock for golang
+
+# Usage
+
+Replace uses of the `time` package with the `clockwork.Clock` interface instead.
+
+For example, instead of using `time.Sleep` directly:
+
+```
+func my_func() {
+ time.Sleep(3 * time.Second)
+ do_something()
+}
+```
+
+inject a clock and use its `Sleep` method instead:
+
+```
+func my_func(clock clockwork.Clock) {
+ clock.Sleep(3 * time.Second)
+ do_something()
+}
+```
+
+Now you can easily test `my_func` with a `FakeClock`:
+
+```
+func TestMyFunc(t *testing.T) {
+ c := clockwork.NewFakeClock()
+
+ // Start our sleepy function
+ my_func(c)
+
+ // Ensure we wait until my_func is sleeping
+ c.BlockUntil(1)
+
+ assert_state()
+
+ // Advance the FakeClock forward in time
+ c.Advance(3)
+
+ assert_state()
+}
+```
+
+and in production builds, simply inject the real clock instead:
+```
+my_func(clockwork.NewRealClock())
+```
+
+See [example_test.go](example_test.go) for a full example.
+
+# Credits
+
+Inspired by @wickman's [threaded fake clock](https://gist.github.com/wickman/3840816), and the [Golang playground](http://blog.golang.org/playground#Faking time)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/clockwork.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/clockwork.go
new file mode 100644
index 000000000..4b6918b52
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/clockwork.go
@@ -0,0 +1,161 @@
+package clockwork
+
+import (
+ "sync"
+ "time"
+)
+
+// Clock provides an interface that packages can use instead of directly
+// using the time module, so that chronology-related behavior can be tested
+type Clock interface {
+ After(d time.Duration) <-chan time.Time
+ Sleep(d time.Duration)
+ Now() time.Time
+}
+
+// FakeClock provides an interface for a clock which can be
+// manually advanced through time
+type FakeClock interface {
+ Clock
+ // Advance advances the FakeClock to a new point in time, ensuring any existing
+ // sleepers are notified appropriately before returning
+ Advance(d time.Duration)
+ // BlockUntil will block until the FakeClock has the given number of
+ // sleepers (callers of Sleep or After)
+ BlockUntil(n int)
+}
+
+// NewRealClock returns a Clock which simply delegates calls to the actual time
+// package; it should be used by packages in production.
+func NewRealClock() Clock {
+ return &realClock{}
+}
+
+// NewFakeClock returns a FakeClock implementation which can be
+// manually advanced through time for testing.
+func NewFakeClock() FakeClock {
+ return &fakeClock{
+ l: sync.RWMutex{},
+ }
+}
+
+type realClock struct{}
+
+func (rc *realClock) After(d time.Duration) <-chan time.Time {
+ return time.After(d)
+}
+
+func (rc *realClock) Sleep(d time.Duration) {
+ time.Sleep(d)
+}
+
+func (rc *realClock) Now() time.Time {
+ return time.Now()
+}
+
+type fakeClock struct {
+ sleepers []*sleeper
+ blockers []*blocker
+ time time.Time
+
+ l sync.RWMutex
+}
+
+// sleeper represents a caller of After or Sleep
+type sleeper struct {
+ until time.Time
+ done chan time.Time
+}
+
+// blocker represents a caller of BlockUntil
+type blocker struct {
+ count int
+ ch chan struct{}
+}
+
+// After mimics time.After; it waits for the given duration to elapse on the
+// fakeClock, then sends the current time on the returned channel.
+func (fc *fakeClock) After(d time.Duration) <-chan time.Time {
+ fc.l.Lock()
+ defer fc.l.Unlock()
+ now := fc.time
+ done := make(chan time.Time, 1)
+ if d.Nanoseconds() == 0 {
+ // special case - trigger immediately
+ done <- now
+ } else {
+ // otherwise, add to the set of sleepers
+ s := &sleeper{
+ until: now.Add(d),
+ done: done,
+ }
+ fc.sleepers = append(fc.sleepers, s)
+ // and notify any blockers
+ fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers))
+ }
+ return done
+}
+
+// notifyBlockers notifies all the blockers waiting until the
+// given number of sleepers are waiting on the fakeClock. It
+// returns an updated slice of blockers (i.e. those still waiting)
+func notifyBlockers(blockers []*blocker, count int) (newBlockers []*blocker) {
+ for _, b := range blockers {
+ if b.count == count {
+ close(b.ch)
+ } else {
+ newBlockers = append(newBlockers, b)
+ }
+ }
+ return
+}
+
+// Sleep blocks until the given duration has passed on the fakeClock
+func (fc *fakeClock) Sleep(d time.Duration) {
+ <-fc.After(d)
+}
+
+// Time returns the current time of the fakeClock
+func (fc *fakeClock) Now() time.Time {
+ fc.l.Lock()
+ defer fc.l.Unlock()
+ return fc.time
+}
+
+// Advance advances fakeClock to a new point in time, ensuring channels from any
+// previous invocations of After are notified appropriately before returning
+func (fc *fakeClock) Advance(d time.Duration) {
+ fc.l.Lock()
+ defer fc.l.Unlock()
+ end := fc.time.Add(d)
+ var newSleepers []*sleeper
+ for _, s := range fc.sleepers {
+ if end.Sub(s.until) >= 0 {
+ s.done <- end
+ } else {
+ newSleepers = append(newSleepers, s)
+ }
+ }
+ fc.sleepers = newSleepers
+ fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers))
+ fc.time = end
+}
+
+// BlockUntil will block until the fakeClock has the given number of sleepers
+// (callers of Sleep or After)
+func (fc *fakeClock) BlockUntil(n int) {
+ fc.l.Lock()
+ // Fast path: current number of sleepers is what we're looking for
+ if len(fc.sleepers) == n {
+ fc.l.Unlock()
+ return
+ }
+ // Otherwise, set up a new blocker
+ b := &blocker{
+ count: n,
+ ch: make(chan struct{}),
+ }
+ fc.blockers = append(fc.blockers, b)
+ fc.l.Unlock()
+ <-b.ch
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/clockwork_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/clockwork_test.go
new file mode 100644
index 000000000..95f886632
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/clockwork_test.go
@@ -0,0 +1,106 @@
+package clockwork
+
+import (
+ "testing"
+ "time"
+)
+
+func TestFakeClockAfter(t *testing.T) {
+ fc := &fakeClock{}
+
+ zero := fc.After(0)
+ select {
+ case <-zero:
+ default:
+ t.Errorf("zero did not return!")
+ }
+ one := fc.After(1)
+ two := fc.After(2)
+ six := fc.After(6)
+ ten := fc.After(10)
+ fc.Advance(1)
+ select {
+ case <-one:
+ default:
+ t.Errorf("one did not return!")
+ }
+ select {
+ case <-two:
+ t.Errorf("two returned prematurely!")
+ case <-six:
+ t.Errorf("six returned prematurely!")
+ case <-ten:
+ t.Errorf("ten returned prematurely!")
+ default:
+ }
+ fc.Advance(1)
+ select {
+ case <-two:
+ default:
+ t.Errorf("two did not return!")
+ }
+ select {
+ case <-six:
+ t.Errorf("six returned prematurely!")
+ case <-ten:
+ t.Errorf("ten returned prematurely!")
+ default:
+ }
+ fc.Advance(1)
+ select {
+ case <-six:
+ t.Errorf("six returned prematurely!")
+ case <-ten:
+ t.Errorf("ten returned prematurely!")
+ default:
+ }
+ fc.Advance(3)
+ select {
+ case <-six:
+ default:
+ t.Errorf("six did not return!")
+ }
+ select {
+ case <-ten:
+ t.Errorf("ten returned prematurely!")
+ default:
+ }
+ fc.Advance(100)
+ select {
+ case <-ten:
+ default:
+ t.Errorf("ten did not return!")
+ }
+}
+
+func TestNotifyBlockers(t *testing.T) {
+ b1 := &blocker{1, make(chan struct{})}
+ b2 := &blocker{2, make(chan struct{})}
+ b3 := &blocker{5, make(chan struct{})}
+ b4 := &blocker{10, make(chan struct{})}
+ b5 := &blocker{10, make(chan struct{})}
+ bs := []*blocker{b1, b2, b3, b4, b5}
+ bs1 := notifyBlockers(bs, 2)
+ if n := len(bs1); n != 4 {
+ t.Fatalf("got %d blockers, want %d", n, 4)
+ }
+ select {
+ case <-b2.ch:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for channel close!")
+ }
+ bs2 := notifyBlockers(bs1, 10)
+ if n := len(bs2); n != 2 {
+ t.Fatalf("got %d blockers, want %d", n, 2)
+ }
+ select {
+ case <-b4.ch:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for channel close!")
+ }
+ select {
+ case <-b5.ch:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for channel close!")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/example_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/example_test.go
new file mode 100644
index 000000000..6a41bd86b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork/example_test.go
@@ -0,0 +1,49 @@
+package clockwork
+
+import (
+ "sync"
+ "testing"
+ "time"
+)
+
+// my_func is an example of a time-dependent function, using an
+// injected clock
+func my_func(clock Clock, i *int) {
+ clock.Sleep(3 * time.Second)
+ *i += 1
+}
+
+// assert_state is an example of a state assertion in a test
+func assert_state(t *testing.T, i, j int) {
+ if i != j {
+ t.Fatalf("i %d, j %d", i, j)
+ }
+}
+
+// TestMyFunc tests my_func's behaviour with a FakeClock
+func TestMyFunc(t *testing.T) {
+ var i int
+ c := NewFakeClock()
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ my_func(c, &i)
+ wg.Done()
+ }()
+
+ // Wait until my_func is actually sleeping on the clock
+ c.BlockUntil(1)
+
+ // Assert the initial state
+ assert_state(t, i, 0)
+
+ // Now advance the clock forward in time
+ c.Advance(1 * time.Hour)
+
+ // Wait until the function completes
+ wg.Wait()
+
+ // Assert the final state
+ assert_state(t, i, 1)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go
new file mode 100644
index 000000000..2830ba9bd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/all_test.go
@@ -0,0 +1,320 @@
+// Copyright 2013 Matt T. Proud
+//
+// 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 pbutil
+
+import (
+ "bytes"
+ "math/rand"
+ "reflect"
+ "testing"
+ "testing/quick"
+
+ "github.com/matttproud/golang_protobuf_extensions/pbtest"
+
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata"
+)
+
+func TestWriteDelimited(t *testing.T) {
+ for _, test := range []struct {
+ msg Message
+ buf []byte
+ n int
+ err error
+ }{
+ {
+ msg: &Empty{},
+ n: 1,
+ buf: []byte{0},
+ },
+ {
+ msg: &GoEnum{Foo: FOO_FOO1.Enum()},
+ n: 3,
+ buf: []byte{2, 8, 1},
+ },
+ {
+ msg: &Strings{
+ StringField: String(`This is my gigantic, unhappy string. It exceeds
+the encoding size of a single byte varint. We are using it to fuzz test the
+correctness of the header decoding mechanisms, which may prove problematic.
+I expect it may. Let's hope you enjoy testing as much as we do.`),
+ },
+ n: 271,
+ buf: []byte{141, 2, 10, 138, 2, 84, 104, 105, 115, 32, 105, 115, 32, 109,
+ 121, 32, 103, 105, 103, 97, 110, 116, 105, 99, 44, 32, 117, 110, 104,
+ 97, 112, 112, 121, 32, 115, 116, 114, 105, 110, 103, 46, 32, 32, 73,
+ 116, 32, 101, 120, 99, 101, 101, 100, 115, 10, 116, 104, 101, 32, 101,
+ 110, 99, 111, 100, 105, 110, 103, 32, 115, 105, 122, 101, 32, 111, 102,
+ 32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 98, 121, 116, 101, 32,
+ 118, 97, 114, 105, 110, 116, 46, 32, 32, 87, 101, 32, 97, 114, 101, 32,
+ 117, 115, 105, 110, 103, 32, 105, 116, 32, 116, 111, 32, 102, 117, 122,
+ 122, 32, 116, 101, 115, 116, 32, 116, 104, 101, 10, 99, 111, 114, 114,
+ 101, 99, 116, 110, 101, 115, 115, 32, 111, 102, 32, 116, 104, 101, 32,
+ 104, 101, 97, 100, 101, 114, 32, 100, 101, 99, 111, 100, 105, 110, 103,
+ 32, 109, 101, 99, 104, 97, 110, 105, 115, 109, 115, 44, 32, 119, 104,
+ 105, 99, 104, 32, 109, 97, 121, 32, 112, 114, 111, 118, 101, 32, 112,
+ 114, 111, 98, 108, 101, 109, 97, 116, 105, 99, 46, 10, 73, 32, 101, 120,
+ 112, 101, 99, 116, 32, 105, 116, 32, 109, 97, 121, 46, 32, 32, 76, 101,
+ 116, 39, 115, 32, 104, 111, 112, 101, 32, 121, 111, 117, 32, 101, 110,
+ 106, 111, 121, 32, 116, 101, 115, 116, 105, 110, 103, 32, 97, 115, 32,
+ 109, 117, 99, 104, 32, 97, 115, 32, 119, 101, 32, 100, 111, 46},
+ },
+ } {
+ var buf bytes.Buffer
+ if n, err := WriteDelimited(&buf, test.msg); n != test.n || err != test.err {
+ t.Fatalf("WriteDelimited(buf, %#v) = %v, %v; want %v, %v", test.msg, n, err, test.n, test.err)
+ }
+ if out := buf.Bytes(); !bytes.Equal(out, test.buf) {
+ t.Fatalf("WriteDelimited(buf, %#v); buf = %v; want %v", test.msg, out, test.buf)
+ }
+ }
+}
+
+func TestReadDelimited(t *testing.T) {
+ for _, test := range []struct {
+ buf []byte
+ msg Message
+ n int
+ err error
+ }{
+ {
+ buf: []byte{0},
+ msg: &Empty{},
+ n: 1,
+ },
+ {
+ n: 3,
+ buf: []byte{2, 8, 1},
+ msg: &GoEnum{Foo: FOO_FOO1.Enum()},
+ },
+ {
+ buf: []byte{141, 2, 10, 138, 2, 84, 104, 105, 115, 32, 105, 115, 32, 109,
+ 121, 32, 103, 105, 103, 97, 110, 116, 105, 99, 44, 32, 117, 110, 104,
+ 97, 112, 112, 121, 32, 115, 116, 114, 105, 110, 103, 46, 32, 32, 73,
+ 116, 32, 101, 120, 99, 101, 101, 100, 115, 10, 116, 104, 101, 32, 101,
+ 110, 99, 111, 100, 105, 110, 103, 32, 115, 105, 122, 101, 32, 111, 102,
+ 32, 97, 32, 115, 105, 110, 103, 108, 101, 32, 98, 121, 116, 101, 32,
+ 118, 97, 114, 105, 110, 116, 46, 32, 32, 87, 101, 32, 97, 114, 101, 32,
+ 117, 115, 105, 110, 103, 32, 105, 116, 32, 116, 111, 32, 102, 117, 122,
+ 122, 32, 116, 101, 115, 116, 32, 116, 104, 101, 10, 99, 111, 114, 114,
+ 101, 99, 116, 110, 101, 115, 115, 32, 111, 102, 32, 116, 104, 101, 32,
+ 104, 101, 97, 100, 101, 114, 32, 100, 101, 99, 111, 100, 105, 110, 103,
+ 32, 109, 101, 99, 104, 97, 110, 105, 115, 109, 115, 44, 32, 119, 104,
+ 105, 99, 104, 32, 109, 97, 121, 32, 112, 114, 111, 118, 101, 32, 112,
+ 114, 111, 98, 108, 101, 109, 97, 116, 105, 99, 46, 10, 73, 32, 101, 120,
+ 112, 101, 99, 116, 32, 105, 116, 32, 109, 97, 121, 46, 32, 32, 76, 101,
+ 116, 39, 115, 32, 104, 111, 112, 101, 32, 121, 111, 117, 32, 101, 110,
+ 106, 111, 121, 32, 116, 101, 115, 116, 105, 110, 103, 32, 97, 115, 32,
+ 109, 117, 99, 104, 32, 97, 115, 32, 119, 101, 32, 100, 111, 46},
+ msg: &Strings{
+ StringField: String(`This is my gigantic, unhappy string. It exceeds
+the encoding size of a single byte varint. We are using it to fuzz test the
+correctness of the header decoding mechanisms, which may prove problematic.
+I expect it may. Let's hope you enjoy testing as much as we do.`),
+ },
+ n: 271,
+ },
+ } {
+ msg := Clone(test.msg)
+ msg.Reset()
+ if n, err := ReadDelimited(bytes.NewBuffer(test.buf), msg); n != test.n || err != test.err {
+ t.Fatalf("ReadDelimited(%v, msg) = %v, %v; want %v, %v", test.buf, n, err, test.n, test.err)
+ }
+ if !Equal(msg, test.msg) {
+ t.Fatalf("ReadDelimited(%v, msg); msg = %v; want %v", test.buf, msg, test.msg)
+ }
+ }
+}
+
+func TestEndToEndValid(t *testing.T) {
+ for _, test := range [][]Message{
+ {&Empty{}},
+ {&GoEnum{Foo: FOO_FOO1.Enum()}, &Empty{}, &GoEnum{Foo: FOO_FOO1.Enum()}},
+ {&GoEnum{Foo: FOO_FOO1.Enum()}},
+ {&Strings{
+ StringField: String(`This is my gigantic, unhappy string. It exceeds
+the encoding size of a single byte varint. We are using it to fuzz test the
+correctness of the header decoding mechanisms, which may prove problematic.
+I expect it may. Let's hope you enjoy testing as much as we do.`),
+ }},
+ } {
+ var buf bytes.Buffer
+ var written int
+ for i, msg := range test {
+ n, err := WriteDelimited(&buf, msg)
+ if err != nil {
+ // Assumption: TestReadDelimited and TestWriteDelimited are sufficient
+ // and inputs for this test are explicitly exercised there.
+ t.Fatalf("WriteDelimited(buf, %v[%d]) = ?, %v; wanted ?, nil", test, i, err)
+ }
+ written += n
+ }
+ var read int
+ for i, msg := range test {
+ out := Clone(msg)
+ out.Reset()
+ n, _ := ReadDelimited(&buf, out)
+ // Decide to do EOF checking?
+ read += n
+ if !Equal(out, msg) {
+ t.Fatalf("out = %v; want %v[%d] = %#v", out, test, i, msg)
+ }
+ }
+ if read != written {
+ t.Fatalf("%v read = %d; want %d", test, read, written)
+ }
+ }
+}
+
+// rndMessage generates a random valid Protocol Buffer message.
+func rndMessage(r *rand.Rand) Message {
+ var t reflect.Type
+ switch v := rand.Intn(23); v {
+ // TODO(br): Uncomment the elements below once fix is incorporated, except
+ // for the elements marked as patently incompatible.
+ // case 0:
+ // t = reflect.TypeOf(&GoEnum{})
+ // break
+ // case 1:
+ // t = reflect.TypeOf(&GoTestField{})
+ // break
+ case 2:
+ t = reflect.TypeOf(&GoTest{})
+ break
+ // case 3:
+ // t = reflect.TypeOf(&GoSkipTest{})
+ // break
+ // case 4:
+ // t = reflect.TypeOf(&NonPackedTest{})
+ // break
+ // case 5:
+ // t = reflect.TypeOf(&PackedTest{})
+ // break
+ case 6:
+ t = reflect.TypeOf(&MaxTag{})
+ break
+ case 7:
+ t = reflect.TypeOf(&OldMessage{})
+ break
+ case 8:
+ t = reflect.TypeOf(&NewMessage{})
+ break
+ case 9:
+ t = reflect.TypeOf(&InnerMessage{})
+ break
+ case 10:
+ t = reflect.TypeOf(&OtherMessage{})
+ break
+ case 11:
+ // PATENTLY INVALID FOR FUZZ GENERATION
+ // t = reflect.TypeOf(&MyMessage{})
+ break
+ // case 12:
+ // t = reflect.TypeOf(&Ext{})
+ // break
+ case 13:
+ // PATENTLY INVALID FOR FUZZ GENERATION
+ // t = reflect.TypeOf(&MyMessageSet{})
+ break
+ // case 14:
+ // t = reflect.TypeOf(&Empty{})
+ // break
+ // case 15:
+ // t = reflect.TypeOf(&MessageList{})
+ // break
+ // case 16:
+ // t = reflect.TypeOf(&Strings{})
+ // break
+ // case 17:
+ // t = reflect.TypeOf(&Defaults{})
+ // break
+ // case 17:
+ // t = reflect.TypeOf(&SubDefaults{})
+ // break
+ // case 18:
+ // t = reflect.TypeOf(&RepeatedEnum{})
+ // break
+ case 19:
+ t = reflect.TypeOf(&MoreRepeated{})
+ break
+ // case 20:
+ // t = reflect.TypeOf(&GroupOld{})
+ // break
+ // case 21:
+ // t = reflect.TypeOf(&GroupNew{})
+ // break
+ case 22:
+ t = reflect.TypeOf(&FloatingPoint{})
+ break
+ default:
+ // TODO(br): Replace with an unreachable once fixed.
+ t = reflect.TypeOf(&GoTest{})
+ break
+ }
+ if t == nil {
+ t = reflect.TypeOf(&GoTest{})
+ }
+ v, ok := quick.Value(t, r)
+ if !ok {
+ panic("attempt to generate illegal item; consult item 11")
+ }
+ if err := pbtest.SanitizeGenerated(v.Interface().(Message)); err != nil {
+ panic(err)
+ }
+ return v.Interface().(Message)
+}
+
+// rndMessages generates several random Protocol Buffer messages.
+func rndMessages(r *rand.Rand) []Message {
+ n := r.Intn(128)
+ out := make([]Message, 0, n)
+ for i := 0; i < n; i++ {
+ out = append(out, rndMessage(r))
+ }
+ return out
+}
+
+func TestFuzz(t *testing.T) {
+ rnd := rand.New(rand.NewSource(42))
+ check := func() bool {
+ messages := rndMessages(rnd)
+ var buf bytes.Buffer
+ var written int
+ for i, msg := range messages {
+ n, err := WriteDelimited(&buf, msg)
+ if err != nil {
+ t.Fatalf("WriteDelimited(buf, %v[%d]) = ?, %v; wanted ?, nil", messages, i, err)
+ }
+ written += n
+ }
+ var read int
+ for i, msg := range messages {
+ out := Clone(msg)
+ out.Reset()
+ n, _ := ReadDelimited(&buf, out)
+ read += n
+ if !Equal(out, msg) {
+ t.Fatalf("out = %v; want %v[%d] = %#v", out, messages, i, msg)
+ }
+ }
+ if read != written {
+ t.Fatalf("%v read = %d; want %d", messages, read, written)
+ }
+ return true
+ }
+ if err := quick.Check(check, nil); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
new file mode 100644
index 000000000..bbeb76acb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/decode.go
@@ -0,0 +1,75 @@
+// Copyright 2013 Matt T. Proud
+//
+// 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 pbutil
+
+import (
+ "encoding/binary"
+ "errors"
+ "io"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+var errInvalidVarint = errors.New("invalid varint32 encountered")
+
+// ReadDelimited decodes a message from the provided length-delimited stream,
+// where the length is encoded as 32-bit varint prefix to the message body.
+// It returns the total number of bytes read and any applicable error. This is
+// roughly equivalent to the companion Java API's
+// MessageLite#parseDelimitedFrom. As per the reader contract, this function
+// calls r.Read repeatedly as required until exactly one message including its
+// prefix is read and decoded (or an error has occurred). The function never
+// reads more bytes from the stream than required. The function never returns
+// an error if a message has been read and decoded correctly, even if the end
+// of the stream has been reached in doing so. In that case, any subsequent
+// calls return (0, io.EOF).
+func ReadDelimited(r io.Reader, m proto.Message) (n int, err error) {
+ // Per AbstractParser#parsePartialDelimitedFrom with
+ // CodedInputStream#readRawVarint32.
+ headerBuf := make([]byte, binary.MaxVarintLen32)
+ var bytesRead, varIntBytes int
+ var messageLength uint64
+ for varIntBytes == 0 { // i.e. no varint has been decoded yet.
+ if bytesRead >= len(headerBuf) {
+ return bytesRead, errInvalidVarint
+ }
+ // We have to read byte by byte here to avoid reading more bytes
+ // than required. Each read byte is appended to what we have
+ // read before.
+ newBytesRead, err := r.Read(headerBuf[bytesRead : bytesRead+1])
+ if newBytesRead == 0 {
+ if err != nil {
+ return bytesRead, err
+ }
+ // A Reader should not return (0, nil), but if it does,
+ // it should be treated as no-op (according to the
+ // Reader contract). So let's go on...
+ continue
+ }
+ bytesRead += newBytesRead
+ // Now present everything read so far to the varint decoder and
+ // see if a varint can be decoded already.
+ messageLength, varIntBytes = proto.DecodeVarint(headerBuf[:bytesRead])
+ }
+
+ messageBuf := make([]byte, messageLength)
+ newBytesRead, err := io.ReadFull(r, messageBuf)
+ bytesRead += newBytesRead
+ if err != nil {
+ return bytesRead, err
+ }
+
+ return bytesRead, proto.Unmarshal(messageBuf, m)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go
new file mode 100644
index 000000000..c318385cb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/doc.go
@@ -0,0 +1,16 @@
+// Copyright 2013 Matt T. Proud
+//
+// 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 pbutil provides record length-delimited Protocol Buffer streaming.
+package pbutil
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
new file mode 100644
index 000000000..6922ce58f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/encode.go
@@ -0,0 +1,46 @@
+// Copyright 2013 Matt T. Proud
+//
+// 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 pbutil
+
+import (
+ "encoding/binary"
+ "io"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+)
+
+// WriteDelimited encodes and dumps a message to the provided writer prefixed
+// with a 32-bit varint indicating the length of the encoded message, producing
+// a length-delimited record stream, which can be used to chain together
+// encoded messages of the same type together in a file. It returns the total
+// number of bytes written and any applicable error. This is roughly
+// equivalent to the companion Java API's MessageLite#writeDelimitedTo.
+func WriteDelimited(w io.Writer, m proto.Message) (n int, err error) {
+ buffer, err := proto.Marshal(m)
+ if err != nil {
+ return 0, err
+ }
+
+ buf := make([]byte, binary.MaxVarintLen32)
+ encodedLength := binary.PutUvarint(buf, uint64(len(buffer)))
+
+ sync, err := w.Write(buf[:encodedLength])
+ if err != nil {
+ return sync, err
+ }
+
+ n, err = w.Write(buffer)
+ return n + sync, err
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go
new file mode 100644
index 000000000..fd1fb9631
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil/fixtures_test.go
@@ -0,0 +1,103 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// http://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 pbutil
+
+import (
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ . "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto/testdata"
+)
+
+// FROM https://github.com/golang/protobuf/blob/master/proto/all_test.go.
+
+func initGoTestField() *GoTestField {
+ f := new(GoTestField)
+ f.Label = String("label")
+ f.Type = String("type")
+ return f
+}
+
+// These are all structurally equivalent but the tag numbers differ.
+// (It's remarkable that required, optional, and repeated all have
+// 8 letters.)
+func initGoTest_RequiredGroup() *GoTest_RequiredGroup {
+ return &GoTest_RequiredGroup{
+ RequiredField: String("required"),
+ }
+}
+
+func initGoTest_OptionalGroup() *GoTest_OptionalGroup {
+ return &GoTest_OptionalGroup{
+ RequiredField: String("optional"),
+ }
+}
+
+func initGoTest_RepeatedGroup() *GoTest_RepeatedGroup {
+ return &GoTest_RepeatedGroup{
+ RequiredField: String("repeated"),
+ }
+}
+
+func initGoTest(setdefaults bool) *GoTest {
+ pb := new(GoTest)
+ if setdefaults {
+ pb.F_BoolDefaulted = Bool(Default_GoTest_F_BoolDefaulted)
+ pb.F_Int32Defaulted = Int32(Default_GoTest_F_Int32Defaulted)
+ pb.F_Int64Defaulted = Int64(Default_GoTest_F_Int64Defaulted)
+ pb.F_Fixed32Defaulted = Uint32(Default_GoTest_F_Fixed32Defaulted)
+ pb.F_Fixed64Defaulted = Uint64(Default_GoTest_F_Fixed64Defaulted)
+ pb.F_Uint32Defaulted = Uint32(Default_GoTest_F_Uint32Defaulted)
+ pb.F_Uint64Defaulted = Uint64(Default_GoTest_F_Uint64Defaulted)
+ pb.F_FloatDefaulted = Float32(Default_GoTest_F_FloatDefaulted)
+ pb.F_DoubleDefaulted = Float64(Default_GoTest_F_DoubleDefaulted)
+ pb.F_StringDefaulted = String(Default_GoTest_F_StringDefaulted)
+ pb.F_BytesDefaulted = Default_GoTest_F_BytesDefaulted
+ pb.F_Sint32Defaulted = Int32(Default_GoTest_F_Sint32Defaulted)
+ pb.F_Sint64Defaulted = Int64(Default_GoTest_F_Sint64Defaulted)
+ }
+
+ pb.Kind = GoTest_TIME.Enum()
+ pb.RequiredField = initGoTestField()
+ pb.F_BoolRequired = Bool(true)
+ pb.F_Int32Required = Int32(3)
+ pb.F_Int64Required = Int64(6)
+ pb.F_Fixed32Required = Uint32(32)
+ pb.F_Fixed64Required = Uint64(64)
+ pb.F_Uint32Required = Uint32(3232)
+ pb.F_Uint64Required = Uint64(6464)
+ pb.F_FloatRequired = Float32(3232)
+ pb.F_DoubleRequired = Float64(6464)
+ pb.F_StringRequired = String("string")
+ pb.F_BytesRequired = []byte("bytes")
+ pb.F_Sint32Required = Int32(-32)
+ pb.F_Sint64Required = Int64(-64)
+ pb.Requiredgroup = initGoTest_RequiredGroup()
+
+ return pb
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/README.md
new file mode 100644
index 000000000..81032bed8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/README.md
@@ -0,0 +1,53 @@
+# Overview
+This is the [Prometheus](http://www.prometheus.io) telemetric
+instrumentation client [Go](http://golang.org) client library. It
+enable authors to define process-space metrics for their servers and
+expose them through a web service interface for extraction,
+aggregation, and a whole slew of other post processing techniques.
+
+# Installing
+ $ go get github.com/prometheus/client_golang/prometheus
+
+# Example
+```go
+package main
+
+import (
+ "net/http"
+
+ "github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+ indexed = prometheus.NewCounter(prometheus.CounterOpts{
+ Namespace: "my_company",
+ Subsystem: "indexer",
+ Name: "documents_indexed",
+ Help: "The number of documents indexed.",
+ })
+ size = prometheus.NewGauge(prometheus.GaugeOpts{
+ Namespace: "my_company",
+ Subsystem: "storage",
+ Name: "documents_total_size_bytes",
+ Help: "The total size of all documents in the storage.",
+ })
+)
+
+func main() {
+ http.Handle("/metrics", prometheus.Handler())
+
+ indexed.Inc()
+ size.Set(5)
+
+ http.ListenAndServe(":8080", nil)
+}
+
+func init() {
+ prometheus.MustRegister(indexed)
+ prometheus.MustRegister(size)
+}
+```
+
+# Documentation
+
+[](https://godoc.org/github.com/prometheus/client_golang)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go
new file mode 100644
index 000000000..6ae7333fc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/benchmark_test.go
@@ -0,0 +1,159 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "testing"
+)
+
+func BenchmarkCounterWithLabelValues(b *testing.B) {
+ m := NewCounterVec(
+ CounterOpts{
+ Name: "benchmark_counter",
+ Help: "A counter to benchmark it.",
+ },
+ []string{"one", "two", "three"},
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.WithLabelValues("eins", "zwei", "drei").Inc()
+ }
+}
+
+func BenchmarkCounterWithMappedLabels(b *testing.B) {
+ m := NewCounterVec(
+ CounterOpts{
+ Name: "benchmark_counter",
+ Help: "A counter to benchmark it.",
+ },
+ []string{"one", "two", "three"},
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.With(Labels{"two": "zwei", "one": "eins", "three": "drei"}).Inc()
+ }
+}
+
+func BenchmarkCounterWithPreparedMappedLabels(b *testing.B) {
+ m := NewCounterVec(
+ CounterOpts{
+ Name: "benchmark_counter",
+ Help: "A counter to benchmark it.",
+ },
+ []string{"one", "two", "three"},
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ labels := Labels{"two": "zwei", "one": "eins", "three": "drei"}
+ for i := 0; i < b.N; i++ {
+ m.With(labels).Inc()
+ }
+}
+
+func BenchmarkCounterNoLabels(b *testing.B) {
+ m := NewCounter(CounterOpts{
+ Name: "benchmark_counter",
+ Help: "A counter to benchmark it.",
+ })
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.Inc()
+ }
+}
+
+func BenchmarkGaugeWithLabelValues(b *testing.B) {
+ m := NewGaugeVec(
+ GaugeOpts{
+ Name: "benchmark_gauge",
+ Help: "A gauge to benchmark it.",
+ },
+ []string{"one", "two", "three"},
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.WithLabelValues("eins", "zwei", "drei").Set(3.1415)
+ }
+}
+
+func BenchmarkGaugeNoLabels(b *testing.B) {
+ m := NewGauge(GaugeOpts{
+ Name: "benchmark_gauge",
+ Help: "A gauge to benchmark it.",
+ })
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.Set(3.1415)
+ }
+}
+
+func BenchmarkSummaryWithLabelValues(b *testing.B) {
+ m := NewSummaryVec(
+ SummaryOpts{
+ Name: "benchmark_summary",
+ Help: "A summary to benchmark it.",
+ },
+ []string{"one", "two", "three"},
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.WithLabelValues("eins", "zwei", "drei").Observe(3.1415)
+ }
+}
+
+func BenchmarkSummaryNoLabels(b *testing.B) {
+ m := NewSummary(SummaryOpts{
+ Name: "benchmark_summary",
+ Help: "A summary to benchmark it.",
+ },
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.Observe(3.1415)
+ }
+}
+
+func BenchmarkHistogramWithLabelValues(b *testing.B) {
+ m := NewHistogramVec(
+ HistogramOpts{
+ Name: "benchmark_histogram",
+ Help: "A histogram to benchmark it.",
+ },
+ []string{"one", "two", "three"},
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.WithLabelValues("eins", "zwei", "drei").Observe(3.1415)
+ }
+}
+
+func BenchmarkHistogramNoLabels(b *testing.B) {
+ m := NewHistogram(HistogramOpts{
+ Name: "benchmark_histogram",
+ Help: "A histogram to benchmark it.",
+ },
+ )
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ m.Observe(3.1415)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/collector.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/collector.go
new file mode 100644
index 000000000..c04688009
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/collector.go
@@ -0,0 +1,75 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+// Collector is the interface implemented by anything that can be used by
+// Prometheus to collect metrics. A Collector has to be registered for
+// collection. See Register, MustRegister, RegisterOrGet, and MustRegisterOrGet.
+//
+// The stock metrics provided by this package (like Gauge, Counter, Summary) are
+// also Collectors (which only ever collect one metric, namely itself). An
+// implementer of Collector may, however, collect multiple metrics in a
+// coordinated fashion and/or create metrics on the fly. Examples for collectors
+// already implemented in this library are the metric vectors (i.e. collection
+// of multiple instances of the same Metric but with different label values)
+// like GaugeVec or SummaryVec, and the ExpvarCollector.
+type Collector interface {
+ // Describe sends the super-set of all possible descriptors of metrics
+ // collected by this Collector to the provided channel and returns once
+ // the last descriptor has been sent. The sent descriptors fulfill the
+ // consistency and uniqueness requirements described in the Desc
+ // documentation. (It is valid if one and the same Collector sends
+ // duplicate descriptors. Those duplicates are simply ignored. However,
+ // two different Collectors must not send duplicate descriptors.) This
+ // method idempotently sends the same descriptors throughout the
+ // lifetime of the Collector. If a Collector encounters an error while
+ // executing this method, it must send an invalid descriptor (created
+ // with NewInvalidDesc) to signal the error to the registry.
+ Describe(chan<- *Desc)
+ // Collect is called by Prometheus when collecting metrics. The
+ // implementation sends each collected metric via the provided channel
+ // and returns once the last metric has been sent. The descriptor of
+ // each sent metric is one of those returned by Describe. Returned
+ // metrics that share the same descriptor must differ in their variable
+ // label values. This method may be called concurrently and must
+ // therefore be implemented in a concurrency safe way. Blocking occurs
+ // at the expense of total performance of rendering all registered
+ // metrics. Ideally, Collector implementations support concurrent
+ // readers.
+ Collect(chan<- Metric)
+}
+
+// SelfCollector implements Collector for a single Metric so that that the
+// Metric collects itself. Add it as an anonymous field to a struct that
+// implements Metric, and call Init with the Metric itself as an argument.
+type SelfCollector struct {
+ self Metric
+}
+
+// Init provides the SelfCollector with a reference to the metric it is supposed
+// to collect. It is usually called within the factory function to create a
+// metric. See example.
+func (c *SelfCollector) Init(self Metric) {
+ c.self = self
+}
+
+// Describe implements Collector.
+func (c *SelfCollector) Describe(ch chan<- *Desc) {
+ ch <- c.self.Desc()
+}
+
+// Collect implements Collector.
+func (c *SelfCollector) Collect(ch chan<- Metric) {
+ ch <- c.self
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go
new file mode 100644
index 000000000..a2952d1c8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter.go
@@ -0,0 +1,175 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "errors"
+ "hash/fnv"
+)
+
+// Counter is a Metric that represents a single numerical value that only ever
+// goes up. That implies that it cannot be used to count items whose number can
+// also go down, e.g. the number of currently running goroutines. Those
+// "counters" are represented by Gauges.
+//
+// A Counter is typically used to count requests served, tasks completed, errors
+// occurred, etc.
+//
+// To create Counter instances, use NewCounter.
+type Counter interface {
+ Metric
+ Collector
+
+ // Set is used to set the Counter to an arbitrary value. It is only used
+ // if you have to transfer a value from an external counter into this
+ // Prometheus metric. Do not use it for regular handling of a
+ // Prometheus counter (as it can be used to break the contract of
+ // monotonically increasing values).
+ Set(float64)
+ // Inc increments the counter by 1.
+ Inc()
+ // Add adds the given value to the counter. It panics if the value is <
+ // 0.
+ Add(float64)
+}
+
+// CounterOpts is an alias for Opts. See there for doc comments.
+type CounterOpts Opts
+
+// NewCounter creates a new Counter based on the provided CounterOpts.
+func NewCounter(opts CounterOpts) Counter {
+ desc := NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ )
+ result := &counter{value: value{desc: desc, valType: CounterValue, labelPairs: desc.constLabelPairs}}
+ result.Init(result) // Init self-collection.
+ return result
+}
+
+type counter struct {
+ value
+}
+
+func (c *counter) Add(v float64) {
+ if v < 0 {
+ panic(errors.New("counter cannot decrease in value"))
+ }
+ c.value.Add(v)
+}
+
+// CounterVec is a Collector that bundles a set of Counters that all share the
+// same Desc, but have different values for their variable labels. This is used
+// if you want to count the same thing partitioned by various dimensions
+// (e.g. number of HTTP requests, partitioned by response code and
+// method). Create instances with NewCounterVec.
+//
+// CounterVec embeds MetricVec. See there for a full list of methods with
+// detailed documentation.
+type CounterVec struct {
+ MetricVec
+}
+
+// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
+// partitioned by the given label names. At least one label name must be
+// provided.
+func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
+ desc := NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ labelNames,
+ opts.ConstLabels,
+ )
+ return &CounterVec{
+ MetricVec: MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ result := &counter{value: value{
+ desc: desc,
+ valType: CounterValue,
+ labelPairs: makeLabelPairs(desc, lvs),
+ }}
+ result.Init(result) // Init self-collection.
+ return result
+ },
+ },
+ }
+}
+
+// GetMetricWithLabelValues replaces the method of the same name in
+// MetricVec. The difference is that this method returns a Counter and not a
+// Metric so that no type conversion is required.
+func (m *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
+ metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
+ if metric != nil {
+ return metric.(Counter), err
+ }
+ return nil, err
+}
+
+// GetMetricWith replaces the method of the same name in MetricVec. The
+// difference is that this method returns a Counter and not a Metric so that no
+// type conversion is required.
+func (m *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
+ metric, err := m.MetricVec.GetMetricWith(labels)
+ if metric != nil {
+ return metric.(Counter), err
+ }
+ return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. By not returning an
+// error, WithLabelValues allows shortcuts like
+// myVec.WithLabelValues("404", "GET").Add(42)
+func (m *CounterVec) WithLabelValues(lvs ...string) Counter {
+ return m.MetricVec.WithLabelValues(lvs...).(Counter)
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. By not returning an error, With allows shortcuts like
+// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
+func (m *CounterVec) With(labels Labels) Counter {
+ return m.MetricVec.With(labels).(Counter)
+}
+
+// CounterFunc is a Counter whose value is determined at collect time by calling a
+// provided function.
+//
+// To create CounterFunc instances, use NewCounterFunc.
+type CounterFunc interface {
+ Metric
+ Collector
+}
+
+// NewCounterFunc creates a new CounterFunc based on the provided
+// CounterOpts. The value reported is determined by calling the given function
+// from within the Write method. Take into account that metric collection may
+// happen concurrently. If that results in concurrent calls to Write, like in
+// the case where a CounterFunc is directly registered with Prometheus, the
+// provided function must be concurrency-safe. The function should also honor
+// the contract for a Counter (values only go up, not down), but compliance will
+// not be checked.
+func NewCounterFunc(opts CounterOpts, function func() float64) CounterFunc {
+ return newValueFunc(NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ), CounterValue, function)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go
new file mode 100644
index 000000000..7f20f881a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/counter_test.go
@@ -0,0 +1,58 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "math"
+ "testing"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func TestCounterAdd(t *testing.T) {
+ counter := NewCounter(CounterOpts{
+ Name: "test",
+ Help: "test help",
+ ConstLabels: Labels{"a": "1", "b": "2"},
+ }).(*counter)
+ counter.Inc()
+ if expected, got := 1., math.Float64frombits(counter.valBits); expected != got {
+ t.Errorf("Expected %f, got %f.", expected, got)
+ }
+ counter.Add(42)
+ if expected, got := 43., math.Float64frombits(counter.valBits); expected != got {
+ t.Errorf("Expected %f, got %f.", expected, got)
+ }
+
+ if expected, got := "counter cannot decrease in value", decreaseCounter(counter).Error(); expected != got {
+ t.Errorf("Expected error %q, got %q.", expected, got)
+ }
+
+ m := &dto.Metric{}
+ counter.Write(m)
+
+ if expected, got := `label: label: counter: `, m.String(); expected != got {
+ t.Errorf("expected %q, got %q", expected, got)
+ }
+}
+
+func decreaseCounter(c *counter) (err error) {
+ defer func() {
+ if e := recover(); e != nil {
+ err = e.(error)
+ }
+ }()
+ c.Add(-1)
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go
new file mode 100644
index 000000000..98c702417
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/desc.go
@@ -0,0 +1,200 @@
+package prometheus
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "hash/fnv"
+ "regexp"
+ "sort"
+ "strings"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+var (
+ metricNameRE = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_:]*$`)
+ labelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
+)
+
+// reservedLabelPrefix is a prefix which is not legal in user-supplied
+// label names.
+const reservedLabelPrefix = "__"
+
+// Labels represents a collection of label name -> value mappings. This type is
+// commonly used with the With(Labels) and GetMetricWith(Labels) methods of
+// metric vector Collectors, e.g.:
+// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
+//
+// The other use-case is the specification of constant label pairs in Opts or to
+// create a Desc.
+type Labels map[string]string
+
+// Desc is the descriptor used by every Prometheus Metric. It is essentially
+// the immutable meta-data of a Metric. The normal Metric implementations
+// included in this package manage their Desc under the hood. Users only have to
+// deal with Desc if they use advanced features like the ExpvarCollector or
+// custom Collectors and Metrics.
+//
+// Descriptors registered with the same registry have to fulfill certain
+// consistency and uniqueness criteria if they share the same fully-qualified
+// name: They must have the same help string and the same label names (aka label
+// dimensions) in each, constLabels and variableLabels, but they must differ in
+// the values of the constLabels.
+//
+// Descriptors that share the same fully-qualified names and the same label
+// values of their constLabels are considered equal.
+//
+// Use NewDesc to create new Desc instances.
+type Desc struct {
+ // fqName has been built from Namespace, Subsystem, and Name.
+ fqName string
+ // help provides some helpful information about this metric.
+ help string
+ // constLabelPairs contains precalculated DTO label pairs based on
+ // the constant labels.
+ constLabelPairs []*dto.LabelPair
+ // VariableLabels contains names of labels for which the metric
+ // maintains variable values.
+ variableLabels []string
+ // id is a hash of the values of the ConstLabels and fqName. This
+ // must be unique among all registered descriptors and can therefore be
+ // used as an identifier of the descriptor.
+ id uint64
+ // dimHash is a hash of the label names (preset and variable) and the
+ // Help string. Each Desc with the same fqName must have the same
+ // dimHash.
+ dimHash uint64
+ // err is an error that occured during construction. It is reported on
+ // registration time.
+ err error
+}
+
+// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
+// and will be reported on registration time. variableLabels and constLabels can
+// be nil if no such labels should be set. fqName and help must not be empty.
+//
+// variableLabels only contain the label names. Their label values are variable
+// and therefore not part of the Desc. (They are managed within the Metric.)
+//
+// For constLabels, the label values are constant. Therefore, they are fully
+// specified in the Desc. See the Opts documentation for the implications of
+// constant labels.
+func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
+ d := &Desc{
+ fqName: fqName,
+ help: help,
+ variableLabels: variableLabels,
+ }
+ if help == "" {
+ d.err = errors.New("empty help string")
+ return d
+ }
+ if !metricNameRE.MatchString(fqName) {
+ d.err = fmt.Errorf("%q is not a valid metric name", fqName)
+ return d
+ }
+ // labelValues contains the label values of const labels (in order of
+ // their sorted label names) plus the fqName (at position 0).
+ labelValues := make([]string, 1, len(constLabels)+1)
+ labelValues[0] = fqName
+ labelNames := make([]string, 0, len(constLabels)+len(variableLabels))
+ labelNameSet := map[string]struct{}{}
+ // First add only the const label names and sort them...
+ for labelName := range constLabels {
+ if !checkLabelName(labelName) {
+ d.err = fmt.Errorf("%q is not a valid label name", labelName)
+ return d
+ }
+ labelNames = append(labelNames, labelName)
+ labelNameSet[labelName] = struct{}{}
+ }
+ sort.Strings(labelNames)
+ // ... so that we can now add const label values in the order of their names.
+ for _, labelName := range labelNames {
+ labelValues = append(labelValues, constLabels[labelName])
+ }
+ // Now add the variable label names, but prefix them with something that
+ // cannot be in a regular label name. That prevents matching the label
+ // dimension with a different mix between preset and variable labels.
+ for _, labelName := range variableLabels {
+ if !checkLabelName(labelName) {
+ d.err = fmt.Errorf("%q is not a valid label name", labelName)
+ return d
+ }
+ labelNames = append(labelNames, "$"+labelName)
+ labelNameSet[labelName] = struct{}{}
+ }
+ if len(labelNames) != len(labelNameSet) {
+ d.err = errors.New("duplicate label names")
+ return d
+ }
+ h := fnv.New64a()
+ var b bytes.Buffer // To copy string contents into, avoiding []byte allocations.
+ for _, val := range labelValues {
+ b.Reset()
+ b.WriteString(val)
+ b.WriteByte(separatorByte)
+ h.Write(b.Bytes())
+ }
+ d.id = h.Sum64()
+ // Sort labelNames so that order doesn't matter for the hash.
+ sort.Strings(labelNames)
+ // Now hash together (in this order) the help string and the sorted
+ // label names.
+ h.Reset()
+ b.Reset()
+ b.WriteString(help)
+ b.WriteByte(separatorByte)
+ h.Write(b.Bytes())
+ for _, labelName := range labelNames {
+ b.Reset()
+ b.WriteString(labelName)
+ b.WriteByte(separatorByte)
+ h.Write(b.Bytes())
+ }
+ d.dimHash = h.Sum64()
+
+ d.constLabelPairs = make([]*dto.LabelPair, 0, len(constLabels))
+ for n, v := range constLabels {
+ d.constLabelPairs = append(d.constLabelPairs, &dto.LabelPair{
+ Name: proto.String(n),
+ Value: proto.String(v),
+ })
+ }
+ sort.Sort(LabelPairSorter(d.constLabelPairs))
+ return d
+}
+
+// NewInvalidDesc returns an invalid descriptor, i.e. a descriptor with the
+// provided error set. If a collector returning such a descriptor is registered,
+// registration will fail with the provided error. NewInvalidDesc can be used by
+// a Collector to signal inability to describe itself.
+func NewInvalidDesc(err error) *Desc {
+ return &Desc{
+ err: err,
+ }
+}
+
+func (d *Desc) String() string {
+ lpStrings := make([]string, 0, len(d.constLabelPairs))
+ for _, lp := range d.constLabelPairs {
+ lpStrings = append(
+ lpStrings,
+ fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
+ )
+ }
+ return fmt.Sprintf(
+ "Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
+ d.fqName,
+ d.help,
+ strings.Join(lpStrings, ","),
+ d.variableLabels,
+ )
+}
+
+func checkLabelName(l string) bool {
+ return labelNameRE.MatchString(l) &&
+ !strings.HasPrefix(l, reservedLabelPrefix)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go
new file mode 100644
index 000000000..425fe8793
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/doc.go
@@ -0,0 +1,109 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package prometheus provides embeddable metric primitives for servers and
+// standardized exposition of telemetry through a web services interface.
+//
+// All exported functions and methods are safe to be used concurrently unless
+// specified otherwise.
+//
+// To expose metrics registered with the Prometheus registry, an HTTP server
+// needs to know about the Prometheus handler. The usual endpoint is "/metrics".
+//
+// http.Handle("/metrics", prometheus.Handler())
+//
+// As a starting point a very basic usage example:
+//
+// package main
+//
+// import (
+// "net/http"
+//
+// "github.com/prometheus/client_golang/prometheus"
+// )
+//
+// var (
+// cpuTemp = prometheus.NewGauge(prometheus.GaugeOpts{
+// Name: "cpu_temperature_celsius",
+// Help: "Current temperature of the CPU.",
+// })
+// hdFailures = prometheus.NewCounter(prometheus.CounterOpts{
+// Name: "hd_errors_total",
+// Help: "Number of hard-disk errors.",
+// })
+// )
+//
+// func init() {
+// prometheus.MustRegister(cpuTemp)
+// prometheus.MustRegister(hdFailures)
+// }
+//
+// func main() {
+// cpuTemp.Set(65.3)
+// hdFailures.Inc()
+//
+// http.Handle("/metrics", prometheus.Handler())
+// http.ListenAndServe(":8080", nil)
+// }
+//
+//
+// This is a complete program that exports two metrics, a Gauge and a Counter.
+// It also exports some stats about the HTTP usage of the /metrics
+// endpoint. (See the Handler function for more detail.)
+//
+// Two more advanced metric types are the Summary and Histogram.
+//
+// In addition to the fundamental metric types Gauge, Counter, Summary, and
+// Histogram, a very important part of the Prometheus data model is the
+// partitioning of samples along dimensions called labels, which results in
+// metric vectors. The fundamental types are GaugeVec, CounterVec, SummaryVec,
+// and HistogramVec.
+//
+// Those are all the parts needed for basic usage. Detailed documentation and
+// examples are provided below.
+//
+// Everything else this package offers is essentially for "power users" only. A
+// few pointers to "power user features":
+//
+// All the various ...Opts structs have a ConstLabels field for labels that
+// never change their value (which is only useful under special circumstances,
+// see documentation of the Opts type).
+//
+// The Untyped metric behaves like a Gauge, but signals the Prometheus server
+// not to assume anything about its type.
+//
+// Functions to fine-tune how the metric registry works: EnableCollectChecks,
+// PanicOnCollectError, Register, Unregister, SetMetricFamilyInjectionHook.
+//
+// For custom metric collection, there are two entry points: Custom Metric
+// implementations and custom Collector implementations. A Metric is the
+// fundamental unit in the Prometheus data model: a sample at a point in time
+// together with its meta-data (like its fully-qualified name and any number of
+// pairs of label name and label value) that knows how to marshal itself into a
+// data transfer object (aka DTO, implemented as a protocol buffer). A Collector
+// gets registered with the Prometheus registry and manages the collection of
+// one or more Metrics. Many parts of this package are building blocks for
+// Metrics and Collectors. Desc is the metric descriptor, actually used by all
+// metrics under the hood, and by Collectors to describe the Metrics to be
+// collected, but only to be dealt with by users if they implement their own
+// Metrics or Collectors. To create a Desc, the BuildFQName function will come
+// in handy. Other useful components for Metric and Collector implementation
+// include: LabelPairSorter to sort the DTO version of label pairs,
+// NewConstMetric and MustNewConstMetric to create "throw away" Metrics at
+// collection time, MetricVec to bundle custom Metrics into a metric vector
+// Collector, SelfCollector to make a custom Metric collect itself.
+//
+// A good example for a custom Collector is the ExpVarCollector included in this
+// package, which exports variables exported via the "expvar" package as
+// Prometheus metrics.
+package prometheus
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
new file mode 100644
index 000000000..70bc986a5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_clustermanager_test.go
@@ -0,0 +1,130 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus_test
+
+import (
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+)
+
+// ClusterManager is an example for a system that might have been built without
+// Prometheus in mind. It models a central manager of jobs running in a
+// cluster. To turn it into something that collects Prometheus metrics, we
+// simply add the two methods required for the Collector interface.
+//
+// An additional challenge is that multiple instances of the ClusterManager are
+// run within the same binary, each in charge of a different zone. We need to
+// make use of ConstLabels to be able to register each ClusterManager instance
+// with Prometheus.
+type ClusterManager struct {
+ Zone string
+ OOMCount *prometheus.CounterVec
+ RAMUsage *prometheus.GaugeVec
+ mtx sync.Mutex // Protects OOMCount and RAMUsage.
+ // ... many more fields
+}
+
+// ReallyExpensiveAssessmentOfTheSystemState is a mock for the data gathering a
+// real cluster manager would have to do. Since it may actually be really
+// expensive, it must only be called once per collection. This implementation,
+// obviously, only returns some made-up data.
+func (c *ClusterManager) ReallyExpensiveAssessmentOfTheSystemState() (
+ oomCountByHost map[string]int, ramUsageByHost map[string]float64,
+) {
+ // Just example fake data.
+ oomCountByHost = map[string]int{
+ "foo.example.org": 42,
+ "bar.example.org": 2001,
+ }
+ ramUsageByHost = map[string]float64{
+ "foo.example.org": 6.023e23,
+ "bar.example.org": 3.14,
+ }
+ return
+}
+
+// Describe faces the interesting challenge that the two metric vectors that are
+// used in this example are already Collectors themselves. However, thanks to
+// the use of channels, it is really easy to "chain" Collectors. Here we simply
+// call the Describe methods of the two metric vectors.
+func (c *ClusterManager) Describe(ch chan<- *prometheus.Desc) {
+ c.OOMCount.Describe(ch)
+ c.RAMUsage.Describe(ch)
+}
+
+// Collect first triggers the ReallyExpensiveAssessmentOfTheSystemState. Then it
+// sets the retrieved values in the two metric vectors and then sends all their
+// metrics to the channel (again using a chaining technique as in the Describe
+// method). Since Collect could be called multiple times concurrently, that part
+// is protected by a mutex.
+func (c *ClusterManager) Collect(ch chan<- prometheus.Metric) {
+ oomCountByHost, ramUsageByHost := c.ReallyExpensiveAssessmentOfTheSystemState()
+ c.mtx.Lock()
+ defer c.mtx.Unlock()
+ for host, oomCount := range oomCountByHost {
+ c.OOMCount.WithLabelValues(host).Set(float64(oomCount))
+ }
+ for host, ramUsage := range ramUsageByHost {
+ c.RAMUsage.WithLabelValues(host).Set(ramUsage)
+ }
+ c.OOMCount.Collect(ch)
+ c.RAMUsage.Collect(ch)
+ // All metrics in OOMCount and RAMUsage are sent to the channel now. We
+ // can safely reset the two metric vectors now, so that we can start
+ // fresh in the next Collect cycle. (Imagine a host disappears from the
+ // cluster. If we did not reset here, its Metric would stay in the
+ // metric vectors forever.)
+ c.OOMCount.Reset()
+ c.RAMUsage.Reset()
+}
+
+// NewClusterManager creates the two metric vectors OOMCount and RAMUsage. Note
+// that the zone is set as a ConstLabel. (It's different in each instance of the
+// ClusterManager, but constant over the lifetime of an instance.) The reported
+// values are partitioned by host, which is therefore a variable label.
+func NewClusterManager(zone string) *ClusterManager {
+ return &ClusterManager{
+ Zone: zone,
+ OOMCount: prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: "clustermanager",
+ Name: "oom_count",
+ Help: "number of OOM crashes",
+ ConstLabels: prometheus.Labels{"zone": zone},
+ },
+ []string{"host"},
+ ),
+ RAMUsage: prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Subsystem: "clustermanager",
+ Name: "ram_usage_bytes",
+ Help: "RAM usage as reported to the cluster manager",
+ ConstLabels: prometheus.Labels{"zone": zone},
+ },
+ []string{"host"},
+ ),
+ }
+}
+
+func ExampleCollector_clustermanager() {
+ workerDB := NewClusterManager("db")
+ workerCA := NewClusterManager("ca")
+ prometheus.MustRegister(workerDB)
+ prometheus.MustRegister(workerCA)
+
+ // Since we are dealing with custom Collector implementations, it might
+ // be a good idea to enable the collect checks in the registry.
+ prometheus.EnableCollectChecks(true)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go
new file mode 100644
index 000000000..cc19ae0f9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_memstats_test.go
@@ -0,0 +1,87 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus_test
+
+import (
+ "runtime"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+ allocDesc = prometheus.NewDesc(
+ prometheus.BuildFQName("", "memstats", "alloc_bytes"),
+ "bytes allocated and still in use",
+ nil, nil,
+ )
+ totalAllocDesc = prometheus.NewDesc(
+ prometheus.BuildFQName("", "memstats", "total_alloc_bytes"),
+ "bytes allocated (even if freed)",
+ nil, nil,
+ )
+ numGCDesc = prometheus.NewDesc(
+ prometheus.BuildFQName("", "memstats", "num_gc_total"),
+ "number of GCs run",
+ nil, nil,
+ )
+)
+
+// MemStatsCollector is an example for a custom Collector that solves the
+// problem of feeding into multiple metrics at the same time. The
+// runtime.ReadMemStats should happen only once, and then the results need to be
+// fed into a number of separate Metrics. In this example, only a few of the
+// values reported by ReadMemStats are used. For each, there is a Desc provided
+// as a var, so the MemStatsCollector itself needs nothing else in the
+// struct. Only the methods need to be implemented.
+type MemStatsCollector struct{}
+
+// Describe just sends the three Desc objects for the Metrics we intend to
+// collect.
+func (_ MemStatsCollector) Describe(ch chan<- *prometheus.Desc) {
+ ch <- allocDesc
+ ch <- totalAllocDesc
+ ch <- numGCDesc
+}
+
+// Collect does the trick by calling ReadMemStats once and then constructing
+// three different Metrics on the fly.
+func (_ MemStatsCollector) Collect(ch chan<- prometheus.Metric) {
+ var ms runtime.MemStats
+ runtime.ReadMemStats(&ms)
+ ch <- prometheus.MustNewConstMetric(
+ allocDesc,
+ prometheus.GaugeValue,
+ float64(ms.Alloc),
+ )
+ ch <- prometheus.MustNewConstMetric(
+ totalAllocDesc,
+ prometheus.GaugeValue,
+ float64(ms.TotalAlloc),
+ )
+ ch <- prometheus.MustNewConstMetric(
+ numGCDesc,
+ prometheus.CounterValue,
+ float64(ms.NumGC),
+ )
+ // To avoid new allocations on each collection, you could also keep
+ // metric objects around and return the same objects each time, just
+ // with new values set.
+}
+
+func ExampleCollector_memstats() {
+ prometheus.MustRegister(&MemStatsCollector{})
+ // Since we are dealing with custom Collector implementations, it might
+ // be a good idea to enable the collect checks in the registry.
+ prometheus.EnableCollectChecks(true)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go
new file mode 100644
index 000000000..df7849cd1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/example_selfcollector_test.go
@@ -0,0 +1,67 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus_test
+
+import (
+ "runtime"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func NewCallbackMetric(desc *prometheus.Desc, callback func() float64) *CallbackMetric {
+ result := &CallbackMetric{desc: desc, callback: callback}
+ result.Init(result) // Initialize the SelfCollector.
+ return result
+}
+
+// TODO: Come up with a better example.
+
+// CallbackMetric is an example for a user-defined Metric that exports the
+// result of a function call as a metric of type "untyped" without any
+// labels. It uses SelfCollector to turn the Metric into a Collector so that it
+// can be registered with Prometheus.
+//
+// Note that this example is pretty much academic as the prometheus package
+// already provides an UntypedFunc type.
+type CallbackMetric struct {
+ prometheus.SelfCollector
+
+ desc *prometheus.Desc
+ callback func() float64
+}
+
+func (cm *CallbackMetric) Desc() *prometheus.Desc {
+ return cm.desc
+}
+
+func (cm *CallbackMetric) Write(m *dto.Metric) error {
+ m.Untyped = &dto.Untyped{Value: proto.Float64(cm.callback())}
+ return nil
+}
+
+func ExampleSelfCollector() {
+ m := NewCallbackMetric(
+ prometheus.NewDesc(
+ "runtime_goroutines_count",
+ "Total number of goroutines that currently exist.",
+ nil, nil, // No labels, these must be nil.
+ ),
+ func() float64 {
+ return float64(runtime.NumGoroutine())
+ },
+ )
+ prometheus.MustRegister(m)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go
new file mode 100644
index 000000000..2bdcc2ad0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/examples_test.go
@@ -0,0 +1,647 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus_test
+
+import (
+ "flag"
+ "fmt"
+ "math"
+ "net/http"
+ "os"
+ "runtime"
+ "sort"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func ExampleGauge() {
+ opsQueued := prometheus.NewGauge(prometheus.GaugeOpts{
+ Namespace: "our_company",
+ Subsystem: "blob_storage",
+ Name: "ops_queued",
+ Help: "Number of blob storage operations waiting to be processed.",
+ })
+ prometheus.MustRegister(opsQueued)
+
+ // 10 operations queued by the goroutine managing incoming requests.
+ opsQueued.Add(10)
+ // A worker goroutine has picked up a waiting operation.
+ opsQueued.Dec()
+ // And once more...
+ opsQueued.Dec()
+}
+
+func ExampleGaugeVec() {
+ binaryVersion := flag.String("binary_version", "debug", "Version of the binary: debug, canary, production.")
+ flag.Parse()
+
+ opsQueued := prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Namespace: "our_company",
+ Subsystem: "blob_storage",
+ Name: "ops_queued",
+ Help: "Number of blob storage operations waiting to be processed, partitioned by user and type.",
+ ConstLabels: prometheus.Labels{"binary_version": *binaryVersion},
+ },
+ []string{
+ // Which user has requested the operation?
+ "user",
+ // Of what type is the operation?
+ "type",
+ },
+ )
+ prometheus.MustRegister(opsQueued)
+
+ // Increase a value using compact (but order-sensitive!) WithLabelValues().
+ opsQueued.WithLabelValues("bob", "put").Add(4)
+ // Increase a value with a map using WithLabels. More verbose, but order
+ // doesn't matter anymore.
+ opsQueued.With(prometheus.Labels{"type": "delete", "user": "alice"}).Inc()
+}
+
+func ExampleGaugeFunc() {
+ if err := prometheus.Register(prometheus.NewGaugeFunc(
+ prometheus.GaugeOpts{
+ Subsystem: "runtime",
+ Name: "goroutines_count",
+ Help: "Number of goroutines that currently exist.",
+ },
+ func() float64 { return float64(runtime.NumGoroutine()) },
+ )); err == nil {
+ fmt.Println("GaugeFunc 'goroutines_count' registered.")
+ }
+ // Note that the count of goroutines is a gauge (and not a counter) as
+ // it can go up and down.
+
+ // Output:
+ // GaugeFunc 'goroutines_count' registered.
+}
+
+func ExampleCounter() {
+ pushCounter := prometheus.NewCounter(prometheus.CounterOpts{
+ Name: "repository_pushes", // Note: No help string...
+ })
+ err := prometheus.Register(pushCounter) // ... so this will return an error.
+ if err != nil {
+ fmt.Println("Push counter couldn't be registered, no counting will happen:", err)
+ return
+ }
+
+ // Try it once more, this time with a help string.
+ pushCounter = prometheus.NewCounter(prometheus.CounterOpts{
+ Name: "repository_pushes",
+ Help: "Number of pushes to external repository.",
+ })
+ err = prometheus.Register(pushCounter)
+ if err != nil {
+ fmt.Println("Push counter couldn't be registered AGAIN, no counting will happen:", err)
+ return
+ }
+
+ pushComplete := make(chan struct{})
+ // TODO: Start a goroutine that performs repository pushes and reports
+ // each completion via the channel.
+ for _ = range pushComplete {
+ pushCounter.Inc()
+ }
+ // Output:
+ // Push counter couldn't be registered, no counting will happen: descriptor Desc{fqName: "repository_pushes", help: "", constLabels: {}, variableLabels: []} is invalid: empty help string
+}
+
+func ExampleCounterVec() {
+ binaryVersion := flag.String("environment", "test", "Execution environment: test, staging, production.")
+ flag.Parse()
+
+ httpReqs := prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "http_requests_total",
+ Help: "How many HTTP requests processed, partitioned by status code and HTTP method.",
+ ConstLabels: prometheus.Labels{"env": *binaryVersion},
+ },
+ []string{"code", "method"},
+ )
+ prometheus.MustRegister(httpReqs)
+
+ httpReqs.WithLabelValues("404", "POST").Add(42)
+
+ // If you have to access the same set of labels very frequently, it
+ // might be good to retrieve the metric only once and keep a handle to
+ // it. But beware of deletion of that metric, see below!
+ m := httpReqs.WithLabelValues("200", "GET")
+ for i := 0; i < 1000000; i++ {
+ m.Inc()
+ }
+ // Delete a metric from the vector. If you have previously kept a handle
+ // to that metric (as above), future updates via that handle will go
+ // unseen (even if you re-create a metric with the same label set
+ // later).
+ httpReqs.DeleteLabelValues("200", "GET")
+ // Same thing with the more verbose Labels syntax.
+ httpReqs.Delete(prometheus.Labels{"method": "GET", "code": "200"})
+}
+
+func ExampleInstrumentHandler() {
+ // Handle the "/doc" endpoint with the standard http.FileServer handler.
+ // By wrapping the handler with InstrumentHandler, request count,
+ // request and response sizes, and request latency are automatically
+ // exported to Prometheus, partitioned by HTTP status code and method
+ // and by the handler name (here "fileserver").
+ http.Handle("/doc", prometheus.InstrumentHandler(
+ "fileserver", http.FileServer(http.Dir("/usr/share/doc")),
+ ))
+ // The Prometheus handler still has to be registered to handle the
+ // "/metrics" endpoint. The handler returned by prometheus.Handler() is
+ // already instrumented - with "prometheus" as the handler name. In this
+ // example, we want the handler name to be "metrics", so we instrument
+ // the uninstrumented Prometheus handler ourselves.
+ http.Handle("/metrics", prometheus.InstrumentHandler(
+ "metrics", prometheus.UninstrumentedHandler(),
+ ))
+}
+
+func ExampleLabelPairSorter() {
+ labelPairs := []*dto.LabelPair{
+ &dto.LabelPair{Name: proto.String("status"), Value: proto.String("404")},
+ &dto.LabelPair{Name: proto.String("method"), Value: proto.String("get")},
+ }
+
+ sort.Sort(prometheus.LabelPairSorter(labelPairs))
+
+ fmt.Println(labelPairs)
+ // Output:
+ // [name:"method" value:"get" name:"status" value:"404" ]
+}
+
+func ExampleRegister() {
+ // Imagine you have a worker pool and want to count the tasks completed.
+ taskCounter := prometheus.NewCounter(prometheus.CounterOpts{
+ Subsystem: "worker_pool",
+ Name: "completed_tasks_total",
+ Help: "Total number of tasks completed.",
+ })
+ // This will register fine.
+ if err := prometheus.Register(taskCounter); err != nil {
+ fmt.Println(err)
+ } else {
+ fmt.Println("taskCounter registered.")
+ }
+ // Don't forget to tell the HTTP server about the Prometheus handler.
+ // (In a real program, you still need to start the HTTP server...)
+ http.Handle("/metrics", prometheus.Handler())
+
+ // Now you can start workers and give every one of them a pointer to
+ // taskCounter and let it increment it whenever it completes a task.
+ taskCounter.Inc() // This has to happen somewhere in the worker code.
+
+ // But wait, you want to see how individual workers perform. So you need
+ // a vector of counters, with one element for each worker.
+ taskCounterVec := prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: "worker_pool",
+ Name: "completed_tasks_total",
+ Help: "Total number of tasks completed.",
+ },
+ []string{"worker_id"},
+ )
+
+ // Registering will fail because we already have a metric of that name.
+ if err := prometheus.Register(taskCounterVec); err != nil {
+ fmt.Println("taskCounterVec not registered:", err)
+ } else {
+ fmt.Println("taskCounterVec registered.")
+ }
+
+ // To fix, first unregister the old taskCounter.
+ if prometheus.Unregister(taskCounter) {
+ fmt.Println("taskCounter unregistered.")
+ }
+
+ // Try registering taskCounterVec again.
+ if err := prometheus.Register(taskCounterVec); err != nil {
+ fmt.Println("taskCounterVec not registered:", err)
+ } else {
+ fmt.Println("taskCounterVec registered.")
+ }
+ // Bummer! Still doesn't work.
+
+ // Prometheus will not allow you to ever export metrics with
+ // inconsistent help strings or label names. After unregistering, the
+ // unregistered metrics will cease to show up in the /metrics HTTP
+ // response, but the registry still remembers that those metrics had
+ // been exported before. For this example, we will now choose a
+ // different name. (In a real program, you would obviously not export
+ // the obsolete metric in the first place.)
+ taskCounterVec = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Subsystem: "worker_pool",
+ Name: "completed_tasks_by_id",
+ Help: "Total number of tasks completed.",
+ },
+ []string{"worker_id"},
+ )
+ if err := prometheus.Register(taskCounterVec); err != nil {
+ fmt.Println("taskCounterVec not registered:", err)
+ } else {
+ fmt.Println("taskCounterVec registered.")
+ }
+ // Finally it worked!
+
+ // The workers have to tell taskCounterVec their id to increment the
+ // right element in the metric vector.
+ taskCounterVec.WithLabelValues("42").Inc() // Code from worker 42.
+
+ // Each worker could also keep a reference to their own counter element
+ // around. Pick the counter at initialization time of the worker.
+ myCounter := taskCounterVec.WithLabelValues("42") // From worker 42 initialization code.
+ myCounter.Inc() // Somewhere in the code of that worker.
+
+ // Note that something like WithLabelValues("42", "spurious arg") would
+ // panic (because you have provided too many label values). If you want
+ // to get an error instead, use GetMetricWithLabelValues(...) instead.
+ notMyCounter, err := taskCounterVec.GetMetricWithLabelValues("42", "spurious arg")
+ if err != nil {
+ fmt.Println("Worker initialization failed:", err)
+ }
+ if notMyCounter == nil {
+ fmt.Println("notMyCounter is nil.")
+ }
+
+ // A different (and somewhat tricky) approach is to use
+ // ConstLabels. ConstLabels are pairs of label names and label values
+ // that never change. You might ask what those labels are good for (and
+ // rightfully so - if they never change, they could as well be part of
+ // the metric name). There are essentially two use-cases: The first is
+ // if labels are constant throughout the lifetime of a binary execution,
+ // but they vary over time or between different instances of a running
+ // binary. The second is what we have here: Each worker creates and
+ // registers an own Counter instance where the only difference is in the
+ // value of the ConstLabels. Those Counters can all be registered
+ // because the different ConstLabel values guarantee that each worker
+ // will increment a different Counter metric.
+ counterOpts := prometheus.CounterOpts{
+ Subsystem: "worker_pool",
+ Name: "completed_tasks",
+ Help: "Total number of tasks completed.",
+ ConstLabels: prometheus.Labels{"worker_id": "42"},
+ }
+ taskCounterForWorker42 := prometheus.NewCounter(counterOpts)
+ if err := prometheus.Register(taskCounterForWorker42); err != nil {
+ fmt.Println("taskCounterVForWorker42 not registered:", err)
+ } else {
+ fmt.Println("taskCounterForWorker42 registered.")
+ }
+ // Obviously, in real code, taskCounterForWorker42 would be a member
+ // variable of a worker struct, and the "42" would be retrieved with a
+ // GetId() method or something. The Counter would be created and
+ // registered in the initialization code of the worker.
+
+ // For the creation of the next Counter, we can recycle
+ // counterOpts. Just change the ConstLabels.
+ counterOpts.ConstLabels = prometheus.Labels{"worker_id": "2001"}
+ taskCounterForWorker2001 := prometheus.NewCounter(counterOpts)
+ if err := prometheus.Register(taskCounterForWorker2001); err != nil {
+ fmt.Println("taskCounterVForWorker2001 not registered:", err)
+ } else {
+ fmt.Println("taskCounterForWorker2001 registered.")
+ }
+
+ taskCounterForWorker2001.Inc()
+ taskCounterForWorker42.Inc()
+ taskCounterForWorker2001.Inc()
+
+ // Yet another approach would be to turn the workers themselves into
+ // Collectors and register them. See the Collector example for details.
+
+ // Output:
+ // taskCounter registered.
+ // taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
+ // taskCounter unregistered.
+ // taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [worker_id]} has different label names or a different help string
+ // taskCounterVec registered.
+ // Worker initialization failed: inconsistent label cardinality
+ // notMyCounter is nil.
+ // taskCounterForWorker42 registered.
+ // taskCounterForWorker2001 registered.
+}
+
+func ExampleSummary() {
+ temps := prometheus.NewSummary(prometheus.SummaryOpts{
+ Name: "pond_temperature_celsius",
+ Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
+ })
+
+ // Simulate some observations.
+ for i := 0; i < 1000; i++ {
+ temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
+ }
+
+ // Just for demonstration, let's check the state of the summary by
+ // (ab)using its Write method (which is usually only used by Prometheus
+ // internally).
+ metric := &dto.Metric{}
+ temps.Write(metric)
+ fmt.Println(proto.MarshalTextString(metric))
+
+ // Output:
+ // summary: <
+ // sample_count: 1000
+ // sample_sum: 29969.50000000001
+ // quantile: <
+ // quantile: 0.5
+ // value: 31.1
+ // >
+ // quantile: <
+ // quantile: 0.9
+ // value: 41.3
+ // >
+ // quantile: <
+ // quantile: 0.99
+ // value: 41.9
+ // >
+ // >
+}
+
+func ExampleSummaryVec() {
+ temps := prometheus.NewSummaryVec(
+ prometheus.SummaryOpts{
+ Name: "pond_temperature_celsius",
+ Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
+ },
+ []string{"species"},
+ )
+
+ // Simulate some observations.
+ for i := 0; i < 1000; i++ {
+ temps.WithLabelValues("litoria-caerulea").Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
+ temps.WithLabelValues("lithobates-catesbeianus").Observe(32 + math.Floor(100*math.Cos(float64(i)*0.11))/10)
+ }
+
+ // Create a Summary without any observations.
+ temps.WithLabelValues("leiopelma-hochstetteri")
+
+ // Just for demonstration, let's check the state of the summary vector
+ // by (ab)using its Collect method and the Write method of its elements
+ // (which is usually only used by Prometheus internally - code like the
+ // following will never appear in your own code).
+ metricChan := make(chan prometheus.Metric)
+ go func() {
+ defer close(metricChan)
+ temps.Collect(metricChan)
+ }()
+
+ metricStrings := []string{}
+ for metric := range metricChan {
+ dtoMetric := &dto.Metric{}
+ metric.Write(dtoMetric)
+ metricStrings = append(metricStrings, proto.MarshalTextString(dtoMetric))
+ }
+ sort.Strings(metricStrings) // For reproducible print order.
+ fmt.Println(metricStrings)
+
+ // Output:
+ // [label: <
+ // name: "species"
+ // value: "leiopelma-hochstetteri"
+ // >
+ // summary: <
+ // sample_count: 0
+ // sample_sum: 0
+ // quantile: <
+ // quantile: 0.5
+ // value: nan
+ // >
+ // quantile: <
+ // quantile: 0.9
+ // value: nan
+ // >
+ // quantile: <
+ // quantile: 0.99
+ // value: nan
+ // >
+ // >
+ // label: <
+ // name: "species"
+ // value: "lithobates-catesbeianus"
+ // >
+ // summary: <
+ // sample_count: 1000
+ // sample_sum: 31956.100000000017
+ // quantile: <
+ // quantile: 0.5
+ // value: 32.4
+ // >
+ // quantile: <
+ // quantile: 0.9
+ // value: 41.4
+ // >
+ // quantile: <
+ // quantile: 0.99
+ // value: 41.9
+ // >
+ // >
+ // label: <
+ // name: "species"
+ // value: "litoria-caerulea"
+ // >
+ // summary: <
+ // sample_count: 1000
+ // sample_sum: 29969.50000000001
+ // quantile: <
+ // quantile: 0.5
+ // value: 31.1
+ // >
+ // quantile: <
+ // quantile: 0.9
+ // value: 41.3
+ // >
+ // quantile: <
+ // quantile: 0.99
+ // value: 41.9
+ // >
+ // >
+ // ]
+}
+
+func ExampleConstSummary() {
+ desc := prometheus.NewDesc(
+ "http_request_duration_seconds",
+ "A summary of the HTTP request durations.",
+ []string{"code", "method"},
+ prometheus.Labels{"owner": "example"},
+ )
+
+ // Create a constant summary from values we got from a 3rd party telemetry system.
+ s := prometheus.MustNewConstSummary(
+ desc,
+ 4711, 403.34,
+ map[float64]float64{0.5: 42.3, 0.9: 323.3},
+ "200", "get",
+ )
+
+ // Just for demonstration, let's check the state of the summary by
+ // (ab)using its Write method (which is usually only used by Prometheus
+ // internally).
+ metric := &dto.Metric{}
+ s.Write(metric)
+ fmt.Println(proto.MarshalTextString(metric))
+
+ // Output:
+ // label: <
+ // name: "code"
+ // value: "200"
+ // >
+ // label: <
+ // name: "method"
+ // value: "get"
+ // >
+ // label: <
+ // name: "owner"
+ // value: "example"
+ // >
+ // summary: <
+ // sample_count: 4711
+ // sample_sum: 403.34
+ // quantile: <
+ // quantile: 0.5
+ // value: 42.3
+ // >
+ // quantile: <
+ // quantile: 0.9
+ // value: 323.3
+ // >
+ // >
+}
+
+func ExampleHistogram() {
+ temps := prometheus.NewHistogram(prometheus.HistogramOpts{
+ Name: "pond_temperature_celsius",
+ Help: "The temperature of the frog pond.", // Sorry, we can't measure how badly it smells.
+ Buckets: prometheus.LinearBuckets(20, 5, 5), // 5 buckets, each 5 centigrade wide.
+ })
+
+ // Simulate some observations.
+ for i := 0; i < 1000; i++ {
+ temps.Observe(30 + math.Floor(120*math.Sin(float64(i)*0.1))/10)
+ }
+
+ // Just for demonstration, let's check the state of the histogram by
+ // (ab)using its Write method (which is usually only used by Prometheus
+ // internally).
+ metric := &dto.Metric{}
+ temps.Write(metric)
+ fmt.Println(proto.MarshalTextString(metric))
+
+ // Output:
+ // histogram: <
+ // sample_count: 1000
+ // sample_sum: 29969.50000000001
+ // bucket: <
+ // cumulative_count: 192
+ // upper_bound: 20
+ // >
+ // bucket: <
+ // cumulative_count: 366
+ // upper_bound: 25
+ // >
+ // bucket: <
+ // cumulative_count: 501
+ // upper_bound: 30
+ // >
+ // bucket: <
+ // cumulative_count: 638
+ // upper_bound: 35
+ // >
+ // bucket: <
+ // cumulative_count: 816
+ // upper_bound: 40
+ // >
+ // >
+}
+
+func ExampleConstHistogram() {
+ desc := prometheus.NewDesc(
+ "http_request_duration_seconds",
+ "A histogram of the HTTP request durations.",
+ []string{"code", "method"},
+ prometheus.Labels{"owner": "example"},
+ )
+
+ // Create a constant histogram from values we got from a 3rd party telemetry system.
+ h := prometheus.MustNewConstHistogram(
+ desc,
+ 4711, 403.34,
+ map[float64]uint64{25: 121, 50: 2403, 100: 3221, 200: 4233},
+ "200", "get",
+ )
+
+ // Just for demonstration, let's check the state of the histogram by
+ // (ab)using its Write method (which is usually only used by Prometheus
+ // internally).
+ metric := &dto.Metric{}
+ h.Write(metric)
+ fmt.Println(proto.MarshalTextString(metric))
+
+ // Output:
+ // label: <
+ // name: "code"
+ // value: "200"
+ // >
+ // label: <
+ // name: "method"
+ // value: "get"
+ // >
+ // label: <
+ // name: "owner"
+ // value: "example"
+ // >
+ // histogram: <
+ // sample_count: 4711
+ // sample_sum: 403.34
+ // bucket: <
+ // cumulative_count: 121
+ // upper_bound: 25
+ // >
+ // bucket: <
+ // cumulative_count: 2403
+ // upper_bound: 50
+ // >
+ // bucket: <
+ // cumulative_count: 3221
+ // upper_bound: 100
+ // >
+ // bucket: <
+ // cumulative_count: 4233
+ // upper_bound: 200
+ // >
+ // >
+}
+
+func ExamplePushCollectors() {
+ hostname, _ := os.Hostname()
+ completionTime := prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "db_backup_last_completion_time",
+ Help: "The timestamp of the last succesful completion of a DB backup.",
+ })
+ completionTime.Set(float64(time.Now().Unix()))
+ if err := prometheus.PushCollectors(
+ "db_backup", hostname,
+ "http://pushgateway:9091",
+ completionTime,
+ ); err != nil {
+ fmt.Println("Could not push completion time to Pushgateway:", err)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar.go
new file mode 100644
index 000000000..0f7630d53
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar.go
@@ -0,0 +1,119 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "encoding/json"
+ "expvar"
+)
+
+// ExpvarCollector collects metrics from the expvar interface. It provides a
+// quick way to expose numeric values that are already exported via expvar as
+// Prometheus metrics. Note that the data models of expvar and Prometheus are
+// fundamentally different, and that the ExpvarCollector is inherently
+// slow. Thus, the ExpvarCollector is probably great for experiments and
+// prototying, but you should seriously consider a more direct implementation of
+// Prometheus metrics for monitoring production systems.
+//
+// Use NewExpvarCollector to create new instances.
+type ExpvarCollector struct {
+ exports map[string]*Desc
+}
+
+// NewExpvarCollector returns a newly allocated ExpvarCollector that still has
+// to be registered with the Prometheus registry.
+//
+// The exports map has the following meaning:
+//
+// The keys in the map correspond to expvar keys, i.e. for every expvar key you
+// want to export as Prometheus metric, you need an entry in the exports
+// map. The descriptor mapped to each key describes how to export the expvar
+// value. It defines the name and the help string of the Prometheus metric
+// proxying the expvar value. The type will always be Untyped.
+//
+// For descriptors without variable labels, the expvar value must be a number or
+// a bool. The number is then directly exported as the Prometheus sample
+// value. (For a bool, 'false' translates to 0 and 'true' to 1). Expvar values
+// that are not numbers or bools are silently ignored.
+//
+// If the descriptor has one variable label, the expvar value must be an expvar
+// map. The keys in the expvar map become the various values of the one
+// Prometheus label. The values in the expvar map must be numbers or bools again
+// as above.
+//
+// For descriptors with more than one variable label, the expvar must be a
+// nested expvar map, i.e. where the values of the topmost map are maps again
+// etc. until a depth is reached that corresponds to the number of labels. The
+// leaves of that structure must be numbers or bools as above to serve as the
+// sample values.
+//
+// Anything that does not fit into the scheme above is silently ignored.
+func NewExpvarCollector(exports map[string]*Desc) *ExpvarCollector {
+ return &ExpvarCollector{
+ exports: exports,
+ }
+}
+
+// Describe implements Collector.
+func (e *ExpvarCollector) Describe(ch chan<- *Desc) {
+ for _, desc := range e.exports {
+ ch <- desc
+ }
+}
+
+// Collect implements Collector.
+func (e *ExpvarCollector) Collect(ch chan<- Metric) {
+ for name, desc := range e.exports {
+ var m Metric
+ expVar := expvar.Get(name)
+ if expVar == nil {
+ continue
+ }
+ var v interface{}
+ labels := make([]string, len(desc.variableLabels))
+ if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
+ ch <- NewInvalidMetric(desc, err)
+ continue
+ }
+ var processValue func(v interface{}, i int)
+ processValue = func(v interface{}, i int) {
+ if i >= len(labels) {
+ copiedLabels := append(make([]string, 0, len(labels)), labels...)
+ switch v := v.(type) {
+ case float64:
+ m = MustNewConstMetric(desc, UntypedValue, v, copiedLabels...)
+ case bool:
+ if v {
+ m = MustNewConstMetric(desc, UntypedValue, 1, copiedLabels...)
+ } else {
+ m = MustNewConstMetric(desc, UntypedValue, 0, copiedLabels...)
+ }
+ default:
+ return
+ }
+ ch <- m
+ return
+ }
+ vm, ok := v.(map[string]interface{})
+ if !ok {
+ return
+ }
+ for lv, val := range vm {
+ labels[i] = lv
+ processValue(val, i+1)
+ }
+ }
+ processValue(v, 0)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go
new file mode 100644
index 000000000..9d2d322b9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/expvar_test.go
@@ -0,0 +1,96 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus_test
+
+import (
+ "expvar"
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func ExampleExpvarCollector() {
+ expvarCollector := prometheus.NewExpvarCollector(map[string]*prometheus.Desc{
+ "memstats": prometheus.NewDesc(
+ "expvar_memstats",
+ "All numeric memstats as one metric family. Not a good role-model, actually... ;-)",
+ []string{"type"}, nil,
+ ),
+ "lone-int": prometheus.NewDesc(
+ "expvar_lone_int",
+ "Just an expvar int as an example.",
+ nil, nil,
+ ),
+ "http-request-map": prometheus.NewDesc(
+ "expvar_http_request_total",
+ "How many http requests processed, partitioned by status code and http method.",
+ []string{"code", "method"}, nil,
+ ),
+ })
+ prometheus.MustRegister(expvarCollector)
+
+ // The Prometheus part is done here. But to show that this example is
+ // doing anything, we have to manually export something via expvar. In
+ // real-life use-cases, some library would already have exported via
+ // expvar what we want to re-export as Prometheus metrics.
+ expvar.NewInt("lone-int").Set(42)
+ expvarMap := expvar.NewMap("http-request-map")
+ var (
+ expvarMap1, expvarMap2 expvar.Map
+ expvarInt11, expvarInt12, expvarInt21, expvarInt22 expvar.Int
+ )
+ expvarMap1.Init()
+ expvarMap2.Init()
+ expvarInt11.Set(3)
+ expvarInt12.Set(13)
+ expvarInt21.Set(11)
+ expvarInt22.Set(212)
+ expvarMap1.Set("POST", &expvarInt11)
+ expvarMap1.Set("GET", &expvarInt12)
+ expvarMap2.Set("POST", &expvarInt21)
+ expvarMap2.Set("GET", &expvarInt22)
+ expvarMap.Set("404", &expvarMap1)
+ expvarMap.Set("200", &expvarMap2)
+ // Results in the following expvar map:
+ // "http-request-count": {"200": {"POST": 11, "GET": 212}, "404": {"POST": 3, "GET": 13}}
+
+ // Let's see what the scrape would yield, but exclude the memstats metrics.
+ metricStrings := []string{}
+ metric := dto.Metric{}
+ metricChan := make(chan prometheus.Metric)
+ go func() {
+ expvarCollector.Collect(metricChan)
+ close(metricChan)
+ }()
+ for m := range metricChan {
+ if strings.Index(m.Desc().String(), "expvar_memstats") == -1 {
+ metric.Reset()
+ m.Write(&metric)
+ metricStrings = append(metricStrings, metric.String())
+ }
+ }
+ sort.Strings(metricStrings)
+ for _, s := range metricStrings {
+ fmt.Println(strings.TrimRight(s, " "))
+ }
+ // Output:
+ // label: label: untyped:
+ // label: label: untyped:
+ // label: label: untyped:
+ // label: label: untyped:
+ // untyped:
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge.go
new file mode 100644
index 000000000..ba8a402ca
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge.go
@@ -0,0 +1,147 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import "hash/fnv"
+
+// Gauge is a Metric that represents a single numerical value that can
+// arbitrarily go up and down.
+//
+// A Gauge is typically used for measured values like temperatures or current
+// memory usage, but also "counts" that can go up and down, like the number of
+// running goroutines.
+//
+// To create Gauge instances, use NewGauge.
+type Gauge interface {
+ Metric
+ Collector
+
+ // Set sets the Gauge to an arbitrary value.
+ Set(float64)
+ // Inc increments the Gauge by 1.
+ Inc()
+ // Dec decrements the Gauge by 1.
+ Dec()
+ // Add adds the given value to the Gauge. (The value can be
+ // negative, resulting in a decrease of the Gauge.)
+ Add(float64)
+ // Sub subtracts the given value from the Gauge. (The value can be
+ // negative, resulting in an increase of the Gauge.)
+ Sub(float64)
+}
+
+// GaugeOpts is an alias for Opts. See there for doc comments.
+type GaugeOpts Opts
+
+// NewGauge creates a new Gauge based on the provided GaugeOpts.
+func NewGauge(opts GaugeOpts) Gauge {
+ return newValue(NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ), GaugeValue, 0)
+}
+
+// GaugeVec is a Collector that bundles a set of Gauges that all share the same
+// Desc, but have different values for their variable labels. This is used if
+// you want to count the same thing partitioned by various dimensions
+// (e.g. number of operations queued, partitioned by user and operation
+// type). Create instances with NewGaugeVec.
+type GaugeVec struct {
+ MetricVec
+}
+
+// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
+// partitioned by the given label names. At least one label name must be
+// provided.
+func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
+ desc := NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ labelNames,
+ opts.ConstLabels,
+ )
+ return &GaugeVec{
+ MetricVec: MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ return newValue(desc, GaugeValue, 0, lvs...)
+ },
+ },
+ }
+}
+
+// GetMetricWithLabelValues replaces the method of the same name in
+// MetricVec. The difference is that this method returns a Gauge and not a
+// Metric so that no type conversion is required.
+func (m *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
+ metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
+ if metric != nil {
+ return metric.(Gauge), err
+ }
+ return nil, err
+}
+
+// GetMetricWith replaces the method of the same name in MetricVec. The
+// difference is that this method returns a Gauge and not a Metric so that no
+// type conversion is required.
+func (m *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
+ metric, err := m.MetricVec.GetMetricWith(labels)
+ if metric != nil {
+ return metric.(Gauge), err
+ }
+ return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. By not returning an
+// error, WithLabelValues allows shortcuts like
+// myVec.WithLabelValues("404", "GET").Add(42)
+func (m *GaugeVec) WithLabelValues(lvs ...string) Gauge {
+ return m.MetricVec.WithLabelValues(lvs...).(Gauge)
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. By not returning an error, With allows shortcuts like
+// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
+func (m *GaugeVec) With(labels Labels) Gauge {
+ return m.MetricVec.With(labels).(Gauge)
+}
+
+// GaugeFunc is a Gauge whose value is determined at collect time by calling a
+// provided function.
+//
+// To create GaugeFunc instances, use NewGaugeFunc.
+type GaugeFunc interface {
+ Metric
+ Collector
+}
+
+// NewGaugeFunc creates a new GaugeFunc based on the provided GaugeOpts. The
+// value reported is determined by calling the given function from within the
+// Write method. Take into account that metric collection may happen
+// concurrently. If that results in concurrent calls to Write, like in the case
+// where a GaugeFunc is directly registered with Prometheus, the provided
+// function must be concurrency-safe.
+func NewGaugeFunc(opts GaugeOpts, function func() float64) GaugeFunc {
+ return newValueFunc(NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ), GaugeValue, function)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go
new file mode 100644
index 000000000..f2dc45787
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/gauge_test.go
@@ -0,0 +1,182 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "math"
+ "math/rand"
+ "sync"
+ "testing"
+ "testing/quick"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func listenGaugeStream(vals, result chan float64, done chan struct{}) {
+ var sum float64
+outer:
+ for {
+ select {
+ case <-done:
+ close(vals)
+ for v := range vals {
+ sum += v
+ }
+ break outer
+ case v := <-vals:
+ sum += v
+ }
+ }
+ result <- sum
+ close(result)
+}
+
+func TestGaugeConcurrency(t *testing.T) {
+ it := func(n uint32) bool {
+ mutations := int(n % 10000)
+ concLevel := int(n%15 + 1)
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sStream := make(chan float64, mutations*concLevel)
+ result := make(chan float64)
+ done := make(chan struct{})
+
+ go listenGaugeStream(sStream, result, done)
+ go func() {
+ end.Wait()
+ close(done)
+ }()
+
+ gge := NewGauge(GaugeOpts{
+ Name: "test_gauge",
+ Help: "no help can be found here",
+ })
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ for j := 0; j < mutations; j++ {
+ vals[j] = rand.Float64() - 0.5
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for _, v := range vals {
+ sStream <- v
+ gge.Add(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ start.Done()
+
+ if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 {
+ t.Fatalf("expected approx. %f, got %f", expected, got)
+ return false
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestGaugeVecConcurrency(t *testing.T) {
+ it := func(n uint32) bool {
+ mutations := int(n % 10000)
+ concLevel := int(n%15 + 1)
+ vecLength := int(n%5 + 1)
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sStreams := make([]chan float64, vecLength)
+ results := make([]chan float64, vecLength)
+ done := make(chan struct{})
+
+ for i := 0; i < vecLength; i++ {
+ sStreams[i] = make(chan float64, mutations*concLevel)
+ results[i] = make(chan float64)
+ go listenGaugeStream(sStreams[i], results[i], done)
+ }
+
+ go func() {
+ end.Wait()
+ close(done)
+ }()
+
+ gge := NewGaugeVec(
+ GaugeOpts{
+ Name: "test_gauge",
+ Help: "no help can be found here",
+ },
+ []string{"label"},
+ )
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ pick := make([]int, mutations)
+ for j := 0; j < mutations; j++ {
+ vals[j] = rand.Float64() - 0.5
+ pick[j] = rand.Intn(vecLength)
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for i, v := range vals {
+ sStreams[pick[i]] <- v
+ gge.WithLabelValues(string('A' + pick[i])).Add(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ start.Done()
+
+ for i := range sStreams {
+ if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 {
+ t.Fatalf("expected approx. %f, got %f", expected, got)
+ return false
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestGaugeFunc(t *testing.T) {
+ gf := NewGaugeFunc(
+ GaugeOpts{
+ Name: "test_name",
+ Help: "test help",
+ ConstLabels: Labels{"a": "1", "b": "2"},
+ },
+ func() float64 { return 3.1415 },
+ )
+
+ if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: []}`, gf.Desc().String(); expected != got {
+ t.Errorf("expected %q, got %q", expected, got)
+ }
+
+ m := &dto.Metric{}
+ gf.Write(m)
+
+ if expected, got := `label: label: gauge: `, m.String(); expected != got {
+ t.Errorf("expected %q, got %q", expected, got)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go
new file mode 100644
index 000000000..8be247695
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector.go
@@ -0,0 +1,263 @@
+package prometheus
+
+import (
+ "fmt"
+ "runtime"
+ "runtime/debug"
+ "time"
+)
+
+type goCollector struct {
+ goroutines Gauge
+ gcDesc *Desc
+
+ // metrics to describe and collect
+ metrics memStatsMetrics
+}
+
+// NewGoCollector returns a collector which exports metrics about the current
+// go process.
+func NewGoCollector() *goCollector {
+ return &goCollector{
+ goroutines: NewGauge(GaugeOpts{
+ Namespace: "go",
+ Name: "goroutines",
+ Help: "Number of goroutines that currently exist.",
+ }),
+ gcDesc: NewDesc(
+ "go_gc_duration_seconds",
+ "A summary of the GC invocation durations.",
+ nil, nil),
+ metrics: memStatsMetrics{
+ {
+ desc: NewDesc(
+ memstatNamespace("alloc_bytes"),
+ "Number of bytes allocated and still in use.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.Alloc) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("alloc_bytes_total"),
+ "Total number of bytes allocated, even if freed.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.TotalAlloc) },
+ valType: CounterValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("sys_bytes"),
+ "Number of bytes obtained by system. Sum of all system allocations.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.Sys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("lookups_total"),
+ "Total number of pointer lookups.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.Lookups) },
+ valType: CounterValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("mallocs_total"),
+ "Total number of mallocs.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.Mallocs) },
+ valType: CounterValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("frees_total"),
+ "Total number of frees.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.Frees) },
+ valType: CounterValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("heap_alloc_bytes"),
+ "Number of heap bytes allocated and still in use.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapAlloc) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("heap_sys_bytes"),
+ "Number of heap bytes obtained from system.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("heap_idle_bytes"),
+ "Number of heap bytes waiting to be used.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapIdle) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("heap_inuse_bytes"),
+ "Number of heap bytes that are in use.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapInuse) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("heap_released_bytes_total"),
+ "Total number of heap bytes released to OS.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapReleased) },
+ valType: CounterValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("heap_objects"),
+ "Number of allocated objects.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.HeapObjects) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("stack_inuse_bytes"),
+ "Number of bytes in use by the stack allocator.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackInuse) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("stack_sys_bytes"),
+ "Number of bytes obtained from system for stack allocator.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.StackSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("mspan_inuse_bytes"),
+ "Number of bytes in use by mspan structures.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanInuse) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("mspan_sys_bytes"),
+ "Number of bytes used for mspan structures obtained from system.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.MSpanSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("mcache_inuse_bytes"),
+ "Number of bytes in use by mcache structures.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheInuse) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("mcache_sys_bytes"),
+ "Number of bytes used for mcache structures obtained from system.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.MCacheSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("buck_hash_sys_bytes"),
+ "Number of bytes used by the profiling bucket hash table.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.BuckHashSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("gc_sys_bytes"),
+ "Number of bytes used for garbage collection system metadata.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.GCSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("other_sys_bytes"),
+ "Number of bytes used for other system allocations.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.OtherSys) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("next_gc_bytes"),
+ "Number of heap bytes when next garbage collection will take place.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.NextGC) },
+ valType: GaugeValue,
+ }, {
+ desc: NewDesc(
+ memstatNamespace("last_gc_time_seconds"),
+ "Number of seconds since 1970 of last garbage collection.",
+ nil, nil,
+ ),
+ eval: func(ms *runtime.MemStats) float64 { return float64(ms.LastGC*10 ^ 9) },
+ valType: GaugeValue,
+ },
+ },
+ }
+}
+
+func memstatNamespace(s string) string {
+ return fmt.Sprintf("go_memstats_%s", s)
+}
+
+// Describe returns all descriptions of the collector.
+func (c *goCollector) Describe(ch chan<- *Desc) {
+ ch <- c.goroutines.Desc()
+ ch <- c.gcDesc
+
+ for _, i := range c.metrics {
+ ch <- i.desc
+ }
+}
+
+// Collect returns the current state of all metrics of the collector.
+func (c *goCollector) Collect(ch chan<- Metric) {
+ c.goroutines.Set(float64(runtime.NumGoroutine()))
+ ch <- c.goroutines
+
+ var stats debug.GCStats
+ stats.PauseQuantiles = make([]time.Duration, 5)
+ debug.ReadGCStats(&stats)
+
+ quantiles := make(map[float64]float64)
+ for idx, pq := range stats.PauseQuantiles[1:] {
+ quantiles[float64(idx+1)/float64(len(stats.PauseQuantiles)-1)] = pq.Seconds()
+ }
+ quantiles[0.0] = stats.PauseQuantiles[0].Seconds()
+ ch <- MustNewConstSummary(c.gcDesc, uint64(stats.NumGC), float64(stats.PauseTotal.Seconds()), quantiles)
+
+ ms := &runtime.MemStats{}
+ runtime.ReadMemStats(ms)
+ for _, i := range c.metrics {
+ ch <- MustNewConstMetric(i.desc, i.valType, i.eval(ms))
+ }
+}
+
+// memStatsMetrics provide description, value, and value type for memstat metrics.
+type memStatsMetrics []struct {
+ desc *Desc
+ eval func(*runtime.MemStats) float64
+ valType ValueType
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go
new file mode 100644
index 000000000..682589421
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/go_collector_test.go
@@ -0,0 +1,123 @@
+package prometheus
+
+import (
+ "runtime"
+ "testing"
+ "time"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func TestGoCollector(t *testing.T) {
+ var (
+ c = NewGoCollector()
+ ch = make(chan Metric)
+ waitc = make(chan struct{})
+ closec = make(chan struct{})
+ old = -1
+ )
+ defer close(closec)
+
+ go func() {
+ c.Collect(ch)
+ go func(c <-chan struct{}) {
+ <-c
+ }(closec)
+ <-waitc
+ c.Collect(ch)
+ }()
+
+ for {
+ select {
+ case metric := <-ch:
+ switch m := metric.(type) {
+ // Attention, this also catches Counter...
+ case Gauge:
+ pb := &dto.Metric{}
+ m.Write(pb)
+ if pb.GetGauge() == nil {
+ continue
+ }
+
+ if old == -1 {
+ old = int(pb.GetGauge().GetValue())
+ close(waitc)
+ continue
+ }
+
+ if diff := int(pb.GetGauge().GetValue()) - old; diff != 1 {
+ // TODO: This is flaky in highly concurrent situations.
+ t.Errorf("want 1 new goroutine, got %d", diff)
+ }
+
+ // GoCollector performs two sends per call.
+ // On line 27 we need to receive the second send
+ // to shut down cleanly.
+ <-ch
+ return
+ }
+ case <-time.After(1 * time.Second):
+ t.Fatalf("expected collect timed out")
+ }
+ }
+}
+
+func TestGCCollector(t *testing.T) {
+ var (
+ c = NewGoCollector()
+ ch = make(chan Metric)
+ waitc = make(chan struct{})
+ closec = make(chan struct{})
+ oldGC uint64
+ oldPause float64
+ )
+ defer close(closec)
+
+ go func() {
+ c.Collect(ch)
+ // force GC
+ runtime.GC()
+ <-waitc
+ c.Collect(ch)
+ }()
+
+ first := true
+ for {
+ select {
+ case metric := <-ch:
+ switch m := metric.(type) {
+ case *constSummary, *value:
+ pb := &dto.Metric{}
+ m.Write(pb)
+ if pb.GetSummary() == nil {
+ continue
+ }
+
+ if len(pb.GetSummary().Quantile) != 5 {
+ t.Errorf("expected 4 buckets, got %d", len(pb.GetSummary().Quantile))
+ }
+ for idx, want := range []float64{0.0, 0.25, 0.5, 0.75, 1.0} {
+ if *pb.GetSummary().Quantile[idx].Quantile != want {
+ t.Errorf("bucket #%d is off, got %f, want %f", idx, *pb.GetSummary().Quantile[idx].Quantile, want)
+ }
+ }
+ if first {
+ first = false
+ oldGC = *pb.GetSummary().SampleCount
+ oldPause = *pb.GetSummary().SampleSum
+ close(waitc)
+ continue
+ }
+ if diff := *pb.GetSummary().SampleCount - oldGC; diff != 1 {
+ t.Errorf("want 1 new garbage collection run, got %d", diff)
+ }
+ if diff := *pb.GetSummary().SampleSum - oldPause; diff <= 0 {
+ t.Errorf("want moar pause, got %f", diff)
+ }
+ return
+ }
+ case <-time.After(1 * time.Second):
+ t.Fatalf("expected collect timed out")
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go
new file mode 100644
index 000000000..0d1970c0b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram.go
@@ -0,0 +1,449 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "fmt"
+ "hash/fnv"
+ "math"
+ "sort"
+ "sync/atomic"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+// A Histogram counts individual observations from an event or sample stream in
+// configurable buckets. Similar to a summary, it also provides a sum of
+// observations and an observation count.
+//
+// On the Prometheus server, quantiles can be calculated from a Histogram using
+// the histogram_quantile function in the query language.
+//
+// Note that Histograms, in contrast to Summaries, can be aggregated with the
+// Prometheus query language (see the documentation for detailed
+// procedures). However, Histograms require the user to pre-define suitable
+// buckets, and they are in general less accurate. The Observe method of a
+// Histogram has a very low performance overhead in comparison with the Observe
+// method of a Summary.
+//
+// To create Histogram instances, use NewHistogram.
+type Histogram interface {
+ Metric
+ Collector
+
+ // Observe adds a single observation to the histogram.
+ Observe(float64)
+}
+
+// bucketLabel is used for the label that defines the upper bound of a
+// bucket of a histogram ("le" -> "less or equal").
+const bucketLabel = "le"
+
+var (
+ // DefBuckets are the default Histogram buckets. The default buckets are
+ // tailored to broadly measure the response time (in seconds) of a
+ // network service. Most likely, however, you will be required to define
+ // buckets customized to your use case.
+ DefBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10}
+
+ errBucketLabelNotAllowed = fmt.Errorf(
+ "%q is not allowed as label name in histograms", bucketLabel,
+ )
+)
+
+// LinearBuckets creates 'count' buckets, each 'width' wide, where the lowest
+// bucket has an upper bound of 'start'. The final +Inf bucket is not counted
+// and not included in the returned slice. The returned slice is meant to be
+// used for the Buckets field of HistogramOpts.
+//
+// The function panics if 'count' is zero or negative.
+func LinearBuckets(start, width float64, count int) []float64 {
+ if count < 1 {
+ panic("LinearBuckets needs a positive count")
+ }
+ buckets := make([]float64, count)
+ for i := range buckets {
+ buckets[i] = start
+ start += width
+ }
+ return buckets
+}
+
+// ExponentialBuckets creates 'count' buckets, where the lowest bucket has an
+// upper bound of 'start' and each following bucket's upper bound is 'factor'
+// times the previous bucket's upper bound. The final +Inf bucket is not counted
+// and not included in the returned slice. The returned slice is meant to be
+// used for the Buckets field of HistogramOpts.
+//
+// The function panics if 'count' is 0 or negative, if 'start' is 0 or negative,
+// or if 'factor' is less than or equal 1.
+func ExponentialBuckets(start, factor float64, count int) []float64 {
+ if count < 1 {
+ panic("ExponentialBuckets needs a positive count")
+ }
+ if start <= 0 {
+ panic("ExponentialBuckets needs a positive start value")
+ }
+ if factor <= 1 {
+ panic("ExponentialBuckets needs a factor greater than 1")
+ }
+ buckets := make([]float64, count)
+ for i := range buckets {
+ buckets[i] = start
+ start *= factor
+ }
+ return buckets
+}
+
+// HistogramOpts bundles the options for creating a Histogram metric. It is
+// mandatory to set Name and Help to a non-empty string. All other fields are
+// optional and can safely be left at their zero value.
+type HistogramOpts struct {
+ // Namespace, Subsystem, and Name are components of the fully-qualified
+ // name of the Histogram (created by joining these components with
+ // "_"). Only Name is mandatory, the others merely help structuring the
+ // name. Note that the fully-qualified name of the Histogram must be a
+ // valid Prometheus metric name.
+ Namespace string
+ Subsystem string
+ Name string
+
+ // Help provides information about this Histogram. Mandatory!
+ //
+ // Metrics with the same fully-qualified name must have the same Help
+ // string.
+ Help string
+
+ // ConstLabels are used to attach fixed labels to this
+ // Histogram. Histograms with the same fully-qualified name must have the
+ // same label names in their ConstLabels.
+ //
+ // Note that in most cases, labels have a value that varies during the
+ // lifetime of a process. Those labels are usually managed with a
+ // HistogramVec. ConstLabels serve only special purposes. One is for the
+ // special case where the value of a label does not change during the
+ // lifetime of a process, e.g. if the revision of the running binary is
+ // put into a label. Another, more advanced purpose is if more than one
+ // Collector needs to collect Histograms with the same fully-qualified
+ // name. In that case, those Summaries must differ in the values of
+ // their ConstLabels. See the Collector examples.
+ //
+ // If the value of a label never changes (not even between binaries),
+ // that label most likely should not be a label at all (but part of the
+ // metric name).
+ ConstLabels Labels
+
+ // Buckets defines the buckets into which observations are counted. Each
+ // element in the slice is the upper inclusive bound of a bucket. The
+ // values must be sorted in strictly increasing order. There is no need
+ // to add a highest bucket with +Inf bound, it will be added
+ // implicitly. The default value is DefBuckets.
+ Buckets []float64
+}
+
+// NewHistogram creates a new Histogram based on the provided HistogramOpts. It
+// panics if the buckets in HistogramOpts are not in strictly increasing order.
+func NewHistogram(opts HistogramOpts) Histogram {
+ return newHistogram(
+ NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ),
+ opts,
+ )
+}
+
+func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
+ if len(desc.variableLabels) != len(labelValues) {
+ panic(errInconsistentCardinality)
+ }
+
+ for _, n := range desc.variableLabels {
+ if n == bucketLabel {
+ panic(errBucketLabelNotAllowed)
+ }
+ }
+ for _, lp := range desc.constLabelPairs {
+ if lp.GetName() == bucketLabel {
+ panic(errBucketLabelNotAllowed)
+ }
+ }
+
+ if len(opts.Buckets) == 0 {
+ opts.Buckets = DefBuckets
+ }
+
+ h := &histogram{
+ desc: desc,
+ upperBounds: opts.Buckets,
+ labelPairs: makeLabelPairs(desc, labelValues),
+ }
+ for i, upperBound := range h.upperBounds {
+ if i < len(h.upperBounds)-1 {
+ if upperBound >= h.upperBounds[i+1] {
+ panic(fmt.Errorf(
+ "histogram buckets must be in increasing order: %f >= %f",
+ upperBound, h.upperBounds[i+1],
+ ))
+ }
+ } else {
+ if math.IsInf(upperBound, +1) {
+ // The +Inf bucket is implicit. Remove it here.
+ h.upperBounds = h.upperBounds[:i]
+ }
+ }
+ }
+ // Finally we know the final length of h.upperBounds and can make counts.
+ h.counts = make([]uint64, len(h.upperBounds))
+
+ h.Init(h) // Init self-collection.
+ return h
+}
+
+type histogram struct {
+ // sumBits contains the bits of the float64 representing the sum of all
+ // observations. sumBits and count have to go first in the struct to
+ // guarantee alignment for atomic operations.
+ // http://golang.org/pkg/sync/atomic/#pkg-note-BUG
+ sumBits uint64
+ count uint64
+
+ SelfCollector
+ // Note that there is no mutex required.
+
+ desc *Desc
+
+ upperBounds []float64
+ counts []uint64
+
+ labelPairs []*dto.LabelPair
+}
+
+func (h *histogram) Desc() *Desc {
+ return h.desc
+}
+
+func (h *histogram) Observe(v float64) {
+ // TODO(beorn7): For small numbers of buckets (<30), a linear search is
+ // slightly faster than the binary search. If we really care, we could
+ // switch from one search strategy to the other depending on the number
+ // of buckets.
+ //
+ // Microbenchmarks (BenchmarkHistogramNoLabels):
+ // 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op
+ // 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op
+ // 300 buckets: 154 ns/op linear - binary 61.6 ns/op
+ i := sort.SearchFloat64s(h.upperBounds, v)
+ if i < len(h.counts) {
+ atomic.AddUint64(&h.counts[i], 1)
+ }
+ atomic.AddUint64(&h.count, 1)
+ for {
+ oldBits := atomic.LoadUint64(&h.sumBits)
+ newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
+ if atomic.CompareAndSwapUint64(&h.sumBits, oldBits, newBits) {
+ break
+ }
+ }
+}
+
+func (h *histogram) Write(out *dto.Metric) error {
+ his := &dto.Histogram{}
+ buckets := make([]*dto.Bucket, len(h.upperBounds))
+
+ his.SampleSum = proto.Float64(math.Float64frombits(atomic.LoadUint64(&h.sumBits)))
+ his.SampleCount = proto.Uint64(atomic.LoadUint64(&h.count))
+ var count uint64
+ for i, upperBound := range h.upperBounds {
+ count += atomic.LoadUint64(&h.counts[i])
+ buckets[i] = &dto.Bucket{
+ CumulativeCount: proto.Uint64(count),
+ UpperBound: proto.Float64(upperBound),
+ }
+ }
+ his.Bucket = buckets
+ out.Histogram = his
+ out.Label = h.labelPairs
+ return nil
+}
+
+// HistogramVec is a Collector that bundles a set of Histograms that all share the
+// same Desc, but have different values for their variable labels. This is used
+// if you want to count the same thing partitioned by various dimensions
+// (e.g. HTTP request latencies, partitioned by status code and method). Create
+// instances with NewHistogramVec.
+type HistogramVec struct {
+ MetricVec
+}
+
+// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
+// partitioned by the given label names. At least one label name must be
+// provided.
+func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
+ desc := NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ labelNames,
+ opts.ConstLabels,
+ )
+ return &HistogramVec{
+ MetricVec: MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ return newHistogram(desc, opts, lvs...)
+ },
+ },
+ }
+}
+
+// GetMetricWithLabelValues replaces the method of the same name in
+// MetricVec. The difference is that this method returns a Histogram and not a
+// Metric so that no type conversion is required.
+func (m *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Histogram, error) {
+ metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
+ if metric != nil {
+ return metric.(Histogram), err
+ }
+ return nil, err
+}
+
+// GetMetricWith replaces the method of the same name in MetricVec. The
+// difference is that this method returns a Histogram and not a Metric so that no
+// type conversion is required.
+func (m *HistogramVec) GetMetricWith(labels Labels) (Histogram, error) {
+ metric, err := m.MetricVec.GetMetricWith(labels)
+ if metric != nil {
+ return metric.(Histogram), err
+ }
+ return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. By not returning an
+// error, WithLabelValues allows shortcuts like
+// myVec.WithLabelValues("404", "GET").Observe(42.21)
+func (m *HistogramVec) WithLabelValues(lvs ...string) Histogram {
+ return m.MetricVec.WithLabelValues(lvs...).(Histogram)
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. By not returning an error, With allows shortcuts like
+// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
+func (m *HistogramVec) With(labels Labels) Histogram {
+ return m.MetricVec.With(labels).(Histogram)
+}
+
+type constHistogram struct {
+ desc *Desc
+ count uint64
+ sum float64
+ buckets map[float64]uint64
+ labelPairs []*dto.LabelPair
+}
+
+func (h *constHistogram) Desc() *Desc {
+ return h.desc
+}
+
+func (h *constHistogram) Write(out *dto.Metric) error {
+ his := &dto.Histogram{}
+ buckets := make([]*dto.Bucket, 0, len(h.buckets))
+
+ his.SampleCount = proto.Uint64(h.count)
+ his.SampleSum = proto.Float64(h.sum)
+
+ for upperBound, count := range h.buckets {
+ buckets = append(buckets, &dto.Bucket{
+ CumulativeCount: proto.Uint64(count),
+ UpperBound: proto.Float64(upperBound),
+ })
+ }
+
+ if len(buckets) > 0 {
+ sort.Sort(buckSort(buckets))
+ }
+ his.Bucket = buckets
+
+ out.Histogram = his
+ out.Label = h.labelPairs
+
+ return nil
+}
+
+// NewConstHistogram returns a metric representing a Prometheus histogram with
+// fixed values for the count, sum, and bucket counts. As those parameters
+// cannot be changed, the returned value does not implement the Histogram
+// interface (but only the Metric interface). Users of this package will not
+// have much use for it in regular operations. However, when implementing custom
+// Collectors, it is useful as a throw-away metric that is generated on the fly
+// to send it to Prometheus in the Collect method.
+//
+// buckets is a map of upper bounds to cumulative counts, excluding the +Inf
+// bucket.
+//
+// NewConstHistogram returns an error if the length of labelValues is not
+// consistent with the variable labels in Desc.
+func NewConstHistogram(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ buckets map[float64]uint64,
+ labelValues ...string,
+) (Metric, error) {
+ if len(desc.variableLabels) != len(labelValues) {
+ return nil, errInconsistentCardinality
+ }
+ return &constHistogram{
+ desc: desc,
+ count: count,
+ sum: sum,
+ buckets: buckets,
+ labelPairs: makeLabelPairs(desc, labelValues),
+ }, nil
+}
+
+// MustNewConstHistogram is a version of NewConstHistogram that panics where
+// NewConstMetric would have returned an error.
+func MustNewConstHistogram(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ buckets map[float64]uint64,
+ labelValues ...string,
+) Metric {
+ m, err := NewConstHistogram(desc, count, sum, buckets, labelValues...)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
+type buckSort []*dto.Bucket
+
+func (s buckSort) Len() int {
+ return len(s)
+}
+
+func (s buckSort) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s buckSort) Less(i, j int) bool {
+ return s[i].GetUpperBound() < s[j].GetUpperBound()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go
new file mode 100644
index 000000000..aa9cc2687
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/histogram_test.go
@@ -0,0 +1,326 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "math"
+ "math/rand"
+ "reflect"
+ "sort"
+ "sync"
+ "testing"
+ "testing/quick"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func benchmarkHistogramObserve(w int, b *testing.B) {
+ b.StopTimer()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(w)
+
+ g := new(sync.WaitGroup)
+ g.Add(1)
+
+ s := NewHistogram(HistogramOpts{})
+
+ for i := 0; i < w; i++ {
+ go func() {
+ g.Wait()
+
+ for i := 0; i < b.N; i++ {
+ s.Observe(float64(i))
+ }
+
+ wg.Done()
+ }()
+ }
+
+ b.StartTimer()
+ g.Done()
+ wg.Wait()
+}
+
+func BenchmarkHistogramObserve1(b *testing.B) {
+ benchmarkHistogramObserve(1, b)
+}
+
+func BenchmarkHistogramObserve2(b *testing.B) {
+ benchmarkHistogramObserve(2, b)
+}
+
+func BenchmarkHistogramObserve4(b *testing.B) {
+ benchmarkHistogramObserve(4, b)
+}
+
+func BenchmarkHistogramObserve8(b *testing.B) {
+ benchmarkHistogramObserve(8, b)
+}
+
+func benchmarkHistogramWrite(w int, b *testing.B) {
+ b.StopTimer()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(w)
+
+ g := new(sync.WaitGroup)
+ g.Add(1)
+
+ s := NewHistogram(HistogramOpts{})
+
+ for i := 0; i < 1000000; i++ {
+ s.Observe(float64(i))
+ }
+
+ for j := 0; j < w; j++ {
+ outs := make([]dto.Metric, b.N)
+
+ go func(o []dto.Metric) {
+ g.Wait()
+
+ for i := 0; i < b.N; i++ {
+ s.Write(&o[i])
+ }
+
+ wg.Done()
+ }(outs)
+ }
+
+ b.StartTimer()
+ g.Done()
+ wg.Wait()
+}
+
+func BenchmarkHistogramWrite1(b *testing.B) {
+ benchmarkHistogramWrite(1, b)
+}
+
+func BenchmarkHistogramWrite2(b *testing.B) {
+ benchmarkHistogramWrite(2, b)
+}
+
+func BenchmarkHistogramWrite4(b *testing.B) {
+ benchmarkHistogramWrite(4, b)
+}
+
+func BenchmarkHistogramWrite8(b *testing.B) {
+ benchmarkHistogramWrite(8, b)
+}
+
+// Intentionally adding +Inf here to test if that case is handled correctly.
+// Also, getCumulativeCounts depends on it.
+var testBuckets = []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)}
+
+func TestHistogramConcurrency(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ }
+
+ rand.Seed(42)
+
+ it := func(n uint32) bool {
+ mutations := int(n%1e4 + 1e4)
+ concLevel := int(n%5 + 1)
+ total := mutations * concLevel
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sum := NewHistogram(HistogramOpts{
+ Name: "test_histogram",
+ Help: "helpless",
+ Buckets: testBuckets,
+ })
+
+ allVars := make([]float64, total)
+ var sampleSum float64
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ for j := 0; j < mutations; j++ {
+ v := rand.NormFloat64()
+ vals[j] = v
+ allVars[i*mutations+j] = v
+ sampleSum += v
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for _, v := range vals {
+ sum.Observe(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ sort.Float64s(allVars)
+ start.Done()
+ end.Wait()
+
+ m := &dto.Metric{}
+ sum.Write(m)
+ if got, want := int(*m.Histogram.SampleCount), total; got != want {
+ t.Errorf("got sample count %d, want %d", got, want)
+ }
+ if got, want := *m.Histogram.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 {
+ t.Errorf("got sample sum %f, want %f", got, want)
+ }
+
+ wantCounts := getCumulativeCounts(allVars)
+
+ if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
+ t.Errorf("got %d buckets in protobuf, want %d", got, want)
+ }
+ for i, wantBound := range testBuckets {
+ if i == len(testBuckets)-1 {
+ break // No +Inf bucket in protobuf.
+ }
+ if gotBound := *m.Histogram.Bucket[i].UpperBound; gotBound != wantBound {
+ t.Errorf("got bound %f, want %f", gotBound, wantBound)
+ }
+ if gotCount, wantCount := *m.Histogram.Bucket[i].CumulativeCount, wantCounts[i]; gotCount != wantCount {
+ t.Errorf("got count %d, want %d", gotCount, wantCount)
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestHistogramVecConcurrency(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ }
+
+ rand.Seed(42)
+
+ objectives := make([]float64, 0, len(DefObjectives))
+ for qu := range DefObjectives {
+
+ objectives = append(objectives, qu)
+ }
+ sort.Float64s(objectives)
+
+ it := func(n uint32) bool {
+ mutations := int(n%1e4 + 1e4)
+ concLevel := int(n%7 + 1)
+ vecLength := int(n%3 + 1)
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ his := NewHistogramVec(
+ HistogramOpts{
+ Name: "test_histogram",
+ Help: "helpless",
+ Buckets: []float64{-2, -1, -0.5, 0, 0.5, 1, 2, math.Inf(+1)},
+ },
+ []string{"label"},
+ )
+
+ allVars := make([][]float64, vecLength)
+ sampleSums := make([]float64, vecLength)
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ picks := make([]int, mutations)
+ for j := 0; j < mutations; j++ {
+ v := rand.NormFloat64()
+ vals[j] = v
+ pick := rand.Intn(vecLength)
+ picks[j] = pick
+ allVars[pick] = append(allVars[pick], v)
+ sampleSums[pick] += v
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for i, v := range vals {
+ his.WithLabelValues(string('A' + picks[i])).Observe(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ for _, vars := range allVars {
+ sort.Float64s(vars)
+ }
+ start.Done()
+ end.Wait()
+
+ for i := 0; i < vecLength; i++ {
+ m := &dto.Metric{}
+ s := his.WithLabelValues(string('A' + i))
+ s.Write(m)
+
+ if got, want := len(m.Histogram.Bucket), len(testBuckets)-1; got != want {
+ t.Errorf("got %d buckets in protobuf, want %d", got, want)
+ }
+ if got, want := int(*m.Histogram.SampleCount), len(allVars[i]); got != want {
+ t.Errorf("got sample count %d, want %d", got, want)
+ }
+ if got, want := *m.Histogram.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 {
+ t.Errorf("got sample sum %f, want %f", got, want)
+ }
+
+ wantCounts := getCumulativeCounts(allVars[i])
+
+ for j, wantBound := range testBuckets {
+ if j == len(testBuckets)-1 {
+ break // No +Inf bucket in protobuf.
+ }
+ if gotBound := *m.Histogram.Bucket[j].UpperBound; gotBound != wantBound {
+ t.Errorf("got bound %f, want %f", gotBound, wantBound)
+ }
+ if gotCount, wantCount := *m.Histogram.Bucket[j].CumulativeCount, wantCounts[j]; gotCount != wantCount {
+ t.Errorf("got count %d, want %d", gotCount, wantCount)
+ }
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func getCumulativeCounts(vars []float64) []uint64 {
+ counts := make([]uint64, len(testBuckets))
+ for _, v := range vars {
+ for i := len(testBuckets) - 1; i >= 0; i-- {
+ if v > testBuckets[i] {
+ break
+ }
+ counts[i]++
+ }
+ }
+ return counts
+}
+
+func TestBuckets(t *testing.T) {
+ got := LinearBuckets(-15, 5, 6)
+ want := []float64{-15, -10, -5, 0, 5, 10}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("linear buckets: got %v, want %v", got, want)
+ }
+
+ got = ExponentialBuckets(100, 1.2, 3)
+ want = []float64{100, 120, 144}
+ if !reflect.DeepEqual(got, want) {
+ t.Errorf("linear buckets: got %v, want %v", got, want)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go
new file mode 100644
index 000000000..eabe60246
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http.go
@@ -0,0 +1,361 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "bufio"
+ "io"
+ "net"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+)
+
+var instLabels = []string{"method", "code"}
+
+type nower interface {
+ Now() time.Time
+}
+
+type nowFunc func() time.Time
+
+func (n nowFunc) Now() time.Time {
+ return n()
+}
+
+var now nower = nowFunc(func() time.Time {
+ return time.Now()
+})
+
+func nowSeries(t ...time.Time) nower {
+ return nowFunc(func() time.Time {
+ defer func() {
+ t = t[1:]
+ }()
+
+ return t[0]
+ })
+}
+
+// InstrumentHandler wraps the given HTTP handler for instrumentation. It
+// registers four metric collectors (if not already done) and reports HTTP
+// metrics to the (newly or already) registered collectors: http_requests_total
+// (CounterVec), http_request_duration_microseconds (Summary),
+// http_request_size_bytes (Summary), http_response_size_bytes (Summary). Each
+// has a constant label named "handler" with the provided handlerName as
+// value. http_requests_total is a metric vector partitioned by HTTP method
+// (label name "method") and HTTP status code (label name "code").
+func InstrumentHandler(handlerName string, handler http.Handler) http.HandlerFunc {
+ return InstrumentHandlerFunc(handlerName, handler.ServeHTTP)
+}
+
+// InstrumentHandlerFunc wraps the given function for instrumentation. It
+// otherwise works in the same way as InstrumentHandler.
+func InstrumentHandlerFunc(handlerName string, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
+ return InstrumentHandlerFuncWithOpts(
+ SummaryOpts{
+ Subsystem: "http",
+ ConstLabels: Labels{"handler": handlerName},
+ },
+ handlerFunc,
+ )
+}
+
+// InstrumentHandlerWithOpts works like InstrumentHandler but provides more
+// flexibility (at the cost of a more complex call syntax). As
+// InstrumentHandler, this function registers four metric collectors, but it
+// uses the provided SummaryOpts to create them. However, the fields "Name" and
+// "Help" in the SummaryOpts are ignored. "Name" is replaced by
+// "requests_total", "request_duration_microseconds", "request_size_bytes", and
+// "response_size_bytes", respectively. "Help" is replaced by an appropriate
+// help string. The names of the variable labels of the http_requests_total
+// CounterVec are "method" (get, post, etc.), and "code" (HTTP status code).
+//
+// If InstrumentHandlerWithOpts is called as follows, it mimics exactly the
+// behavior of InstrumentHandler:
+//
+// prometheus.InstrumentHandlerWithOpts(
+// prometheus.SummaryOpts{
+// Subsystem: "http",
+// ConstLabels: prometheus.Labels{"handler": handlerName},
+// },
+// handler,
+// )
+//
+// Technical detail: "requests_total" is a CounterVec, not a SummaryVec, so it
+// cannot use SummaryOpts. Instead, a CounterOpts struct is created internally,
+// and all its fields are set to the equally named fields in the provided
+// SummaryOpts.
+func InstrumentHandlerWithOpts(opts SummaryOpts, handler http.Handler) http.HandlerFunc {
+ return InstrumentHandlerFuncWithOpts(opts, handler.ServeHTTP)
+}
+
+// InstrumentHandlerFuncWithOpts works like InstrumentHandlerFunc but provides
+// more flexibility (at the cost of a more complex call syntax). See
+// InstrumentHandlerWithOpts for details how the provided SummaryOpts are used.
+func InstrumentHandlerFuncWithOpts(opts SummaryOpts, handlerFunc func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
+ reqCnt := NewCounterVec(
+ CounterOpts{
+ Namespace: opts.Namespace,
+ Subsystem: opts.Subsystem,
+ Name: "requests_total",
+ Help: "Total number of HTTP requests made.",
+ ConstLabels: opts.ConstLabels,
+ },
+ instLabels,
+ )
+
+ opts.Name = "request_duration_microseconds"
+ opts.Help = "The HTTP request latencies in microseconds."
+ reqDur := NewSummary(opts)
+
+ opts.Name = "request_size_bytes"
+ opts.Help = "The HTTP request sizes in bytes."
+ reqSz := NewSummary(opts)
+
+ opts.Name = "response_size_bytes"
+ opts.Help = "The HTTP response sizes in bytes."
+ resSz := NewSummary(opts)
+
+ regReqCnt := MustRegisterOrGet(reqCnt).(*CounterVec)
+ regReqDur := MustRegisterOrGet(reqDur).(Summary)
+ regReqSz := MustRegisterOrGet(reqSz).(Summary)
+ regResSz := MustRegisterOrGet(resSz).(Summary)
+
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ now := time.Now()
+
+ delegate := &responseWriterDelegator{ResponseWriter: w}
+ out := make(chan int)
+ urlLen := 0
+ if r.URL != nil {
+ urlLen = len(r.URL.String())
+ }
+ go computeApproximateRequestSize(r, out, urlLen)
+
+ _, cn := w.(http.CloseNotifier)
+ _, fl := w.(http.Flusher)
+ _, hj := w.(http.Hijacker)
+ _, rf := w.(io.ReaderFrom)
+ var rw http.ResponseWriter
+ if cn && fl && hj && rf {
+ rw = &fancyResponseWriterDelegator{delegate}
+ } else {
+ rw = delegate
+ }
+ handlerFunc(rw, r)
+
+ elapsed := float64(time.Since(now)) / float64(time.Microsecond)
+
+ method := sanitizeMethod(r.Method)
+ code := sanitizeCode(delegate.status)
+ regReqCnt.WithLabelValues(method, code).Inc()
+ regReqDur.Observe(elapsed)
+ regResSz.Observe(float64(delegate.written))
+ regReqSz.Observe(float64(<-out))
+ })
+}
+
+func computeApproximateRequestSize(r *http.Request, out chan int, s int) {
+ s += len(r.Method)
+ s += len(r.Proto)
+ for name, values := range r.Header {
+ s += len(name)
+ for _, value := range values {
+ s += len(value)
+ }
+ }
+ s += len(r.Host)
+
+ // N.B. r.Form and r.MultipartForm are assumed to be included in r.URL.
+
+ if r.ContentLength != -1 {
+ s += int(r.ContentLength)
+ }
+ out <- s
+}
+
+type responseWriterDelegator struct {
+ http.ResponseWriter
+
+ handler, method string
+ status int
+ written int64
+ wroteHeader bool
+}
+
+func (r *responseWriterDelegator) WriteHeader(code int) {
+ r.status = code
+ r.wroteHeader = true
+ r.ResponseWriter.WriteHeader(code)
+}
+
+func (r *responseWriterDelegator) Write(b []byte) (int, error) {
+ if !r.wroteHeader {
+ r.WriteHeader(http.StatusOK)
+ }
+ n, err := r.ResponseWriter.Write(b)
+ r.written += int64(n)
+ return n, err
+}
+
+type fancyResponseWriterDelegator struct {
+ *responseWriterDelegator
+}
+
+func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
+ return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
+}
+
+func (f *fancyResponseWriterDelegator) Flush() {
+ f.ResponseWriter.(http.Flusher).Flush()
+}
+
+func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+ return f.ResponseWriter.(http.Hijacker).Hijack()
+}
+
+func (f *fancyResponseWriterDelegator) ReadFrom(r io.Reader) (int64, error) {
+ if !f.wroteHeader {
+ f.WriteHeader(http.StatusOK)
+ }
+ n, err := f.ResponseWriter.(io.ReaderFrom).ReadFrom(r)
+ f.written += n
+ return n, err
+}
+
+func sanitizeMethod(m string) string {
+ switch m {
+ case "GET", "get":
+ return "get"
+ case "PUT", "put":
+ return "put"
+ case "HEAD", "head":
+ return "head"
+ case "POST", "post":
+ return "post"
+ case "DELETE", "delete":
+ return "delete"
+ case "CONNECT", "connect":
+ return "connect"
+ case "OPTIONS", "options":
+ return "options"
+ case "NOTIFY", "notify":
+ return "notify"
+ default:
+ return strings.ToLower(m)
+ }
+}
+
+func sanitizeCode(s int) string {
+ switch s {
+ case 100:
+ return "100"
+ case 101:
+ return "101"
+
+ case 200:
+ return "200"
+ case 201:
+ return "201"
+ case 202:
+ return "202"
+ case 203:
+ return "203"
+ case 204:
+ return "204"
+ case 205:
+ return "205"
+ case 206:
+ return "206"
+
+ case 300:
+ return "300"
+ case 301:
+ return "301"
+ case 302:
+ return "302"
+ case 304:
+ return "304"
+ case 305:
+ return "305"
+ case 307:
+ return "307"
+
+ case 400:
+ return "400"
+ case 401:
+ return "401"
+ case 402:
+ return "402"
+ case 403:
+ return "403"
+ case 404:
+ return "404"
+ case 405:
+ return "405"
+ case 406:
+ return "406"
+ case 407:
+ return "407"
+ case 408:
+ return "408"
+ case 409:
+ return "409"
+ case 410:
+ return "410"
+ case 411:
+ return "411"
+ case 412:
+ return "412"
+ case 413:
+ return "413"
+ case 414:
+ return "414"
+ case 415:
+ return "415"
+ case 416:
+ return "416"
+ case 417:
+ return "417"
+ case 418:
+ return "418"
+
+ case 500:
+ return "500"
+ case 501:
+ return "501"
+ case 502:
+ return "502"
+ case 503:
+ return "503"
+ case 504:
+ return "504"
+ case 505:
+ return "505"
+
+ case 428:
+ return "428"
+ case 429:
+ return "429"
+ case 431:
+ return "431"
+ case 511:
+ return "511"
+
+ default:
+ return strconv.Itoa(s)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go
new file mode 100644
index 000000000..835e58450
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/http_test.go
@@ -0,0 +1,121 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+type respBody string
+
+func (b respBody) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusTeapot)
+ w.Write([]byte(b))
+}
+
+func TestInstrumentHandler(t *testing.T) {
+ defer func(n nower) {
+ now = n.(nower)
+ }(now)
+
+ instant := time.Now()
+ end := instant.Add(30 * time.Second)
+ now = nowSeries(instant, end)
+ respBody := respBody("Howdy there!")
+
+ hndlr := InstrumentHandler("test-handler", respBody)
+
+ opts := SummaryOpts{
+ Subsystem: "http",
+ ConstLabels: Labels{"handler": "test-handler"},
+ }
+
+ reqCnt := MustRegisterOrGet(NewCounterVec(
+ CounterOpts{
+ Namespace: opts.Namespace,
+ Subsystem: opts.Subsystem,
+ Name: "requests_total",
+ Help: "Total number of HTTP requests made.",
+ ConstLabels: opts.ConstLabels,
+ },
+ instLabels,
+ )).(*CounterVec)
+
+ opts.Name = "request_duration_microseconds"
+ opts.Help = "The HTTP request latencies in microseconds."
+ reqDur := MustRegisterOrGet(NewSummary(opts)).(Summary)
+
+ opts.Name = "request_size_bytes"
+ opts.Help = "The HTTP request sizes in bytes."
+ MustRegisterOrGet(NewSummary(opts))
+
+ opts.Name = "response_size_bytes"
+ opts.Help = "The HTTP response sizes in bytes."
+ MustRegisterOrGet(NewSummary(opts))
+
+ reqCnt.Reset()
+
+ resp := httptest.NewRecorder()
+ req := &http.Request{
+ Method: "GET",
+ }
+
+ hndlr.ServeHTTP(resp, req)
+
+ if resp.Code != http.StatusTeapot {
+ t.Fatalf("expected status %d, got %d", http.StatusTeapot, resp.Code)
+ }
+ if string(resp.Body.Bytes()) != "Howdy there!" {
+ t.Fatalf("expected body %s, got %s", "Howdy there!", string(resp.Body.Bytes()))
+ }
+
+ out := &dto.Metric{}
+ reqDur.Write(out)
+ if want, got := "test-handler", out.Label[0].GetValue(); want != got {
+ t.Errorf("want label value %q in reqDur, got %q", want, got)
+ }
+ if want, got := uint64(1), out.Summary.GetSampleCount(); want != got {
+ t.Errorf("want sample count %d in reqDur, got %d", want, got)
+ }
+
+ out.Reset()
+ if want, got := 1, len(reqCnt.children); want != got {
+ t.Errorf("want %d children in reqCnt, got %d", want, got)
+ }
+ cnt, err := reqCnt.GetMetricWithLabelValues("get", "418")
+ if err != nil {
+ t.Fatal(err)
+ }
+ cnt.Write(out)
+ if want, got := "418", out.Label[0].GetValue(); want != got {
+ t.Errorf("want label value %q in reqCnt, got %q", want, got)
+ }
+ if want, got := "test-handler", out.Label[1].GetValue(); want != got {
+ t.Errorf("want label value %q in reqCnt, got %q", want, got)
+ }
+ if want, got := "get", out.Label[2].GetValue(); want != got {
+ t.Errorf("want label value %q in reqCnt, got %q", want, got)
+ }
+ if out.Counter == nil {
+ t.Fatal("expected non-nil counter in reqCnt")
+ }
+ if want, got := 1., out.Counter.GetValue(); want != got {
+ t.Errorf("want reqCnt of %f, got %f", want, got)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go
new file mode 100644
index 000000000..1191de36c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric.go
@@ -0,0 +1,166 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "strings"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+const separatorByte byte = 255
+
+// A Metric models a single sample value with its meta data being exported to
+// Prometheus. Implementers of Metric in this package inclued Gauge, Counter,
+// Untyped, and Summary. Users can implement their own Metric types, but that
+// should be rarely needed. See the example for SelfCollector, which is also an
+// example for a user-implemented Metric.
+type Metric interface {
+ // Desc returns the descriptor for the Metric. This method idempotently
+ // returns the same descriptor throughout the lifetime of the
+ // Metric. The returned descriptor is immutable by contract. A Metric
+ // unable to describe itself must return an invalid descriptor (created
+ // with NewInvalidDesc).
+ Desc() *Desc
+ // Write encodes the Metric into a "Metric" Protocol Buffer data
+ // transmission object.
+ //
+ // Implementers of custom Metric types must observe concurrency safety
+ // as reads of this metric may occur at any time, and any blocking
+ // occurs at the expense of total performance of rendering all
+ // registered metrics. Ideally Metric implementations should support
+ // concurrent readers.
+ //
+ // The Prometheus client library attempts to minimize memory allocations
+ // and will provide a pre-existing reset dto.Metric pointer. Prometheus
+ // may recycle the dto.Metric proto message, so Metric implementations
+ // should just populate the provided dto.Metric and then should not keep
+ // any reference to it.
+ //
+ // While populating dto.Metric, labels must be sorted lexicographically.
+ // (Implementers may find LabelPairSorter useful for that.)
+ Write(*dto.Metric) error
+}
+
+// Opts bundles the options for creating most Metric types. Each metric
+// implementation XXX has its own XXXOpts type, but in most cases, it is just be
+// an alias of this type (which might change when the requirement arises.)
+//
+// It is mandatory to set Name and Help to a non-empty string. All other fields
+// are optional and can safely be left at their zero value.
+type Opts struct {
+ // Namespace, Subsystem, and Name are components of the fully-qualified
+ // name of the Metric (created by joining these components with
+ // "_"). Only Name is mandatory, the others merely help structuring the
+ // name. Note that the fully-qualified name of the metric must be a
+ // valid Prometheus metric name.
+ Namespace string
+ Subsystem string
+ Name string
+
+ // Help provides information about this metric. Mandatory!
+ //
+ // Metrics with the same fully-qualified name must have the same Help
+ // string.
+ Help string
+
+ // ConstLabels are used to attach fixed labels to this metric. Metrics
+ // with the same fully-qualified name must have the same label names in
+ // their ConstLabels.
+ //
+ // Note that in most cases, labels have a value that varies during the
+ // lifetime of a process. Those labels are usually managed with a metric
+ // vector collector (like CounterVec, GaugeVec, UntypedVec). ConstLabels
+ // serve only special purposes. One is for the special case where the
+ // value of a label does not change during the lifetime of a process,
+ // e.g. if the revision of the running binary is put into a
+ // label. Another, more advanced purpose is if more than one Collector
+ // needs to collect Metrics with the same fully-qualified name. In that
+ // case, those Metrics must differ in the values of their
+ // ConstLabels. See the Collector examples.
+ //
+ // If the value of a label never changes (not even between binaries),
+ // that label most likely should not be a label at all (but part of the
+ // metric name).
+ ConstLabels Labels
+}
+
+// BuildFQName joins the given three name components by "_". Empty name
+// components are ignored. If the name parameter itself is empty, an empty
+// string is returned, no matter what. Metric implementations included in this
+// library use this function internally to generate the fully-qualified metric
+// name from the name component in their Opts. Users of the library will only
+// need this function if they implement their own Metric or instantiate a Desc
+// (with NewDesc) directly.
+func BuildFQName(namespace, subsystem, name string) string {
+ if name == "" {
+ return ""
+ }
+ switch {
+ case namespace != "" && subsystem != "":
+ return strings.Join([]string{namespace, subsystem, name}, "_")
+ case namespace != "":
+ return strings.Join([]string{namespace, name}, "_")
+ case subsystem != "":
+ return strings.Join([]string{subsystem, name}, "_")
+ }
+ return name
+}
+
+// LabelPairSorter implements sort.Interface. It is used to sort a slice of
+// dto.LabelPair pointers. This is useful for implementing the Write method of
+// custom metrics.
+type LabelPairSorter []*dto.LabelPair
+
+func (s LabelPairSorter) Len() int {
+ return len(s)
+}
+
+func (s LabelPairSorter) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s LabelPairSorter) Less(i, j int) bool {
+ return s[i].GetName() < s[j].GetName()
+}
+
+type hashSorter []uint64
+
+func (s hashSorter) Len() int {
+ return len(s)
+}
+
+func (s hashSorter) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s hashSorter) Less(i, j int) bool {
+ return s[i] < s[j]
+}
+
+type invalidMetric struct {
+ desc *Desc
+ err error
+}
+
+// NewInvalidMetric returns a metric whose Write method always returns the
+// provided error. It is useful if a Collector finds itself unable to collect
+// a metric and wishes to report an error to the registry.
+func NewInvalidMetric(desc *Desc, err error) Metric {
+ return &invalidMetric{desc, err}
+}
+
+func (m *invalidMetric) Desc() *Desc { return m.desc }
+
+func (m *invalidMetric) Write(*dto.Metric) error { return m.err }
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go
new file mode 100644
index 000000000..7145f5e53
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/metric_test.go
@@ -0,0 +1,35 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import "testing"
+
+func TestBuildFQName(t *testing.T) {
+ scenarios := []struct{ namespace, subsystem, name, result string }{
+ {"a", "b", "c", "a_b_c"},
+ {"", "b", "c", "b_c"},
+ {"a", "", "c", "a_c"},
+ {"", "", "c", "c"},
+ {"a", "b", "", ""},
+ {"a", "", "", ""},
+ {"", "b", "", ""},
+ {" ", "", "", ""},
+ }
+
+ for i, s := range scenarios {
+ if want, got := s.result, BuildFQName(s.namespace, s.subsystem, s.name); want != got {
+ t.Errorf("%d. want %s, got %s", i, want, got)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector.go
new file mode 100644
index 000000000..c7e2db660
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector.go
@@ -0,0 +1,142 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs"
+
+type processCollector struct {
+ pid int
+ collectFn func(chan<- Metric)
+ pidFn func() (int, error)
+ cpuTotal Counter
+ openFDs, maxFDs Gauge
+ vsize, rss Gauge
+ startTime Gauge
+}
+
+// NewProcessCollector returns a collector which exports the current state of
+// process metrics including cpu, memory and file descriptor usage as well as
+// the process start time for the given process id under the given namespace.
+func NewProcessCollector(pid int, namespace string) *processCollector {
+ return NewProcessCollectorPIDFn(
+ func() (int, error) { return pid, nil },
+ namespace,
+ )
+}
+
+// NewProcessCollectorPIDFn returns a collector which exports the current state
+// of process metrics including cpu, memory and file descriptor usage as well
+// as the process start time under the given namespace. The given pidFn is
+// called on each collect and is used to determine the process to export
+// metrics for.
+func NewProcessCollectorPIDFn(
+ pidFn func() (int, error),
+ namespace string,
+) *processCollector {
+ c := processCollector{
+ pidFn: pidFn,
+ collectFn: func(chan<- Metric) {},
+
+ cpuTotal: NewCounter(CounterOpts{
+ Namespace: namespace,
+ Name: "process_cpu_seconds_total",
+ Help: "Total user and system CPU time spent in seconds.",
+ }),
+ openFDs: NewGauge(GaugeOpts{
+ Namespace: namespace,
+ Name: "process_open_fds",
+ Help: "Number of open file descriptors.",
+ }),
+ maxFDs: NewGauge(GaugeOpts{
+ Namespace: namespace,
+ Name: "process_max_fds",
+ Help: "Maximum number of open file descriptors.",
+ }),
+ vsize: NewGauge(GaugeOpts{
+ Namespace: namespace,
+ Name: "process_virtual_memory_bytes",
+ Help: "Virtual memory size in bytes.",
+ }),
+ rss: NewGauge(GaugeOpts{
+ Namespace: namespace,
+ Name: "process_resident_memory_bytes",
+ Help: "Resident memory size in bytes.",
+ }),
+ startTime: NewGauge(GaugeOpts{
+ Namespace: namespace,
+ Name: "process_start_time_seconds",
+ Help: "Start time of the process since unix epoch in seconds.",
+ }),
+ }
+
+ // Set up process metric collection if supported by the runtime.
+ if _, err := procfs.NewStat(); err == nil {
+ c.collectFn = c.processCollect
+ }
+
+ return &c
+}
+
+// Describe returns all descriptions of the collector.
+func (c *processCollector) Describe(ch chan<- *Desc) {
+ ch <- c.cpuTotal.Desc()
+ ch <- c.openFDs.Desc()
+ ch <- c.maxFDs.Desc()
+ ch <- c.vsize.Desc()
+ ch <- c.rss.Desc()
+ ch <- c.startTime.Desc()
+}
+
+// Collect returns the current state of all metrics of the collector.
+func (c *processCollector) Collect(ch chan<- Metric) {
+ c.collectFn(ch)
+}
+
+// TODO(ts): Bring back error reporting by reverting 7faf9e7 as soon as the
+// client allows users to configure the error behavior.
+func (c *processCollector) processCollect(ch chan<- Metric) {
+ pid, err := c.pidFn()
+ if err != nil {
+ return
+ }
+
+ p, err := procfs.NewProc(pid)
+ if err != nil {
+ return
+ }
+
+ if stat, err := p.NewStat(); err == nil {
+ c.cpuTotal.Set(stat.CPUTime())
+ ch <- c.cpuTotal
+ c.vsize.Set(float64(stat.VirtualMemory()))
+ ch <- c.vsize
+ c.rss.Set(float64(stat.ResidentMemory()))
+ ch <- c.rss
+
+ if startTime, err := stat.StartTime(); err == nil {
+ c.startTime.Set(startTime)
+ ch <- c.startTime
+ }
+ }
+
+ if fds, err := p.FileDescriptorsLen(); err == nil {
+ c.openFDs.Set(float64(fds))
+ ch <- c.openFDs
+ }
+
+ if limits, err := p.NewLimits(); err == nil {
+ c.maxFDs.Set(float64(limits.OpenFiles))
+ ch <- c.maxFDs
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go
new file mode 100644
index 000000000..1af824ef5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/process_collector_test.go
@@ -0,0 +1,54 @@
+package prometheus
+
+import (
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "regexp"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs"
+)
+
+func TestProcessCollector(t *testing.T) {
+ if _, err := procfs.Self(); err != nil {
+ t.Skipf("skipping TestProcessCollector, procfs not available: %s", err)
+ }
+
+ registry := newRegistry()
+ registry.Register(NewProcessCollector(os.Getpid(), ""))
+ registry.Register(NewProcessCollectorPIDFn(
+ func() (int, error) { return os.Getpid(), nil }, "foobar"))
+
+ s := httptest.NewServer(InstrumentHandler("prometheus", registry))
+ defer s.Close()
+ r, err := http.Get(s.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Body.Close()
+ body, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, re := range []*regexp.Regexp{
+ regexp.MustCompile("process_cpu_seconds_total [0-9]"),
+ regexp.MustCompile("process_max_fds [0-9]{2,}"),
+ regexp.MustCompile("process_open_fds [1-9]"),
+ regexp.MustCompile("process_virtual_memory_bytes [1-9]"),
+ regexp.MustCompile("process_resident_memory_bytes [1-9]"),
+ regexp.MustCompile("process_start_time_seconds [0-9.]{10,}"),
+ regexp.MustCompile("foobar_process_cpu_seconds_total [0-9]"),
+ regexp.MustCompile("foobar_process_max_fds [0-9]{2,}"),
+ regexp.MustCompile("foobar_process_open_fds [1-9]"),
+ regexp.MustCompile("foobar_process_virtual_memory_bytes [1-9]"),
+ regexp.MustCompile("foobar_process_resident_memory_bytes [1-9]"),
+ regexp.MustCompile("foobar_process_start_time_seconds [0-9.]{10,}"),
+ } {
+ if !re.Match(body) {
+ t.Errorf("want body to match %s\n%s", re, body)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/push.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/push.go
new file mode 100644
index 000000000..1c33848a3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/push.go
@@ -0,0 +1,65 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright (c) 2013, The Prometheus 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 prometheus
+
+// Push triggers a metric collection by the default registry and pushes all
+// collected metrics to the Pushgateway specified by addr. See the Pushgateway
+// documentation for detailed implications of the job and instance
+// parameter. instance can be left empty. You can use just host:port or ip:port
+// as url, in which case 'http://' is added automatically. You can also include
+// the schema in the URL. However, do not include the '/metrics/jobs/...' part.
+//
+// Note that all previously pushed metrics with the same job and instance will
+// be replaced with the metrics pushed by this call. (It uses HTTP method 'PUT'
+// to push to the Pushgateway.)
+func Push(job, instance, url string) error {
+ return defRegistry.Push(job, instance, url, "PUT")
+}
+
+// PushAdd works like Push, but only previously pushed metrics with the same
+// name (and the same job and instance) will be replaced. (It uses HTTP method
+// 'POST' to push to the Pushgateway.)
+func PushAdd(job, instance, url string) error {
+ return defRegistry.Push(job, instance, url, "POST")
+}
+
+// PushCollectors works like Push, but it does not collect from the default
+// registry. Instead, it collects from the provided collectors. It is a
+// convenient way to push only a few metrics.
+func PushCollectors(job, instance, url string, collectors ...Collector) error {
+ return pushCollectors(job, instance, url, "PUT", collectors...)
+}
+
+// PushAddCollectors works like PushAdd, but it does not collect from the
+// default registry. Instead, it collects from the provided collectors. It is a
+// convenient way to push only a few metrics.
+func PushAddCollectors(job, instance, url string, collectors ...Collector) error {
+ return pushCollectors(job, instance, url, "POST", collectors...)
+}
+
+func pushCollectors(job, instance, url, method string, collectors ...Collector) error {
+ r := newRegistry()
+ for _, collector := range collectors {
+ if _, err := r.Register(collector); err != nil {
+ return err
+ }
+ }
+ return r.Push(job, instance, url, method)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go
new file mode 100644
index 000000000..754173805
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry.go
@@ -0,0 +1,725 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright (c) 2013, The Prometheus 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 prometheus
+
+import (
+ "bytes"
+ "compress/gzip"
+ "errors"
+ "fmt"
+ "hash/fnv"
+ "io"
+ "net/http"
+ "net/url"
+ "os"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt"
+)
+
+var (
+ defRegistry = newDefaultRegistry()
+ errAlreadyReg = errors.New("duplicate metrics collector registration attempted")
+)
+
+// Constants relevant to the HTTP interface.
+const (
+ // APIVersion is the version of the format of the exported data. This
+ // will match this library's version, which subscribes to the Semantic
+ // Versioning scheme.
+ APIVersion = "0.0.4"
+
+ // DelimitedTelemetryContentType is the content type set on telemetry
+ // data responses in delimited protobuf format.
+ DelimitedTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`
+ // TextTelemetryContentType is the content type set on telemetry data
+ // responses in text format.
+ TextTelemetryContentType = `text/plain; version=` + APIVersion
+ // ProtoTextTelemetryContentType is the content type set on telemetry
+ // data responses in protobuf text format. (Only used for debugging.)
+ ProtoTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`
+ // ProtoCompactTextTelemetryContentType is the content type set on
+ // telemetry data responses in protobuf compact text format. (Only used
+ // for debugging.)
+ ProtoCompactTextTelemetryContentType = `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`
+
+ // Constants for object pools.
+ numBufs = 4
+ numMetricFamilies = 1000
+ numMetrics = 10000
+
+ // Capacity for the channel to collect metrics and descriptors.
+ capMetricChan = 1000
+ capDescChan = 10
+
+ contentTypeHeader = "Content-Type"
+ contentLengthHeader = "Content-Length"
+ contentEncodingHeader = "Content-Encoding"
+
+ acceptEncodingHeader = "Accept-Encoding"
+ acceptHeader = "Accept"
+)
+
+// Handler returns the HTTP handler for the global Prometheus registry. It is
+// already instrumented with InstrumentHandler (using "prometheus" as handler
+// name). Usually the handler is used to handle the "/metrics" endpoint.
+func Handler() http.Handler {
+ return InstrumentHandler("prometheus", defRegistry)
+}
+
+// UninstrumentedHandler works in the same way as Handler, but the returned HTTP
+// handler is not instrumented. This is useful if no instrumentation is desired
+// (for whatever reason) or if the instrumentation has to happen with a
+// different handler name (or with a different instrumentation approach
+// altogether). See the InstrumentHandler example.
+func UninstrumentedHandler() http.Handler {
+ return defRegistry
+}
+
+// Register registers a new Collector to be included in metrics collection. It
+// returns an error if the descriptors provided by the Collector are invalid or
+// if they - in combination with descriptors of already registered Collectors -
+// do not fulfill the consistency and uniqueness criteria described in the Desc
+// documentation.
+//
+// Do not register the same Collector multiple times concurrently. (Registering
+// the same Collector twice would result in an error anyway, but on top of that,
+// it is not safe to do so concurrently.)
+func Register(m Collector) error {
+ _, err := defRegistry.Register(m)
+ return err
+}
+
+// MustRegister works like Register but panics where Register would have
+// returned an error.
+func MustRegister(m Collector) {
+ err := Register(m)
+ if err != nil {
+ panic(err)
+ }
+}
+
+// RegisterOrGet works like Register but does not return an error if a Collector
+// is registered that equals a previously registered Collector. (Two Collectors
+// are considered equal if their Describe method yields the same set of
+// descriptors.) Instead, the previously registered Collector is returned (which
+// is helpful if the new and previously registered Collectors are equal but not
+// identical, i.e. not pointers to the same object).
+//
+// As for Register, it is still not safe to call RegisterOrGet with the same
+// Collector multiple times concurrently.
+func RegisterOrGet(m Collector) (Collector, error) {
+ return defRegistry.RegisterOrGet(m)
+}
+
+// MustRegisterOrGet works like Register but panics where RegisterOrGet would
+// have returned an error.
+func MustRegisterOrGet(m Collector) Collector {
+ existing, err := RegisterOrGet(m)
+ if err != nil {
+ panic(err)
+ }
+ return existing
+}
+
+// Unregister unregisters the Collector that equals the Collector passed in as
+// an argument. (Two Collectors are considered equal if their Describe method
+// yields the same set of descriptors.) The function returns whether a Collector
+// was unregistered.
+func Unregister(c Collector) bool {
+ return defRegistry.Unregister(c)
+}
+
+// SetMetricFamilyInjectionHook sets a function that is called whenever metrics
+// are collected. The hook function must be set before metrics collection begins
+// (i.e. call SetMetricFamilyInjectionHook before setting the HTTP handler.) The
+// MetricFamily protobufs returned by the hook function are merged with the
+// metrics collected in the usual way.
+//
+// This is a way to directly inject MetricFamily protobufs managed and owned by
+// the caller. The caller has full responsibility. As no registration of the
+// injected metrics has happened, there is no descriptor to check against, and
+// there are no registration-time checks. If collect-time checks are disabled
+// (see function EnableCollectChecks), no sanity checks are performed on the
+// returned protobufs at all. If collect-checks are enabled, type and uniqueness
+// checks are performed, but no further consistency checks (which would require
+// knowledge of a metric descriptor).
+//
+// Sorting concerns: The caller is responsible for sorting the label pairs in
+// each metric. However, the order of metrics will be sorted by the registry as
+// it is required anyway after merging with the metric families collected
+// conventionally.
+//
+// The function must be callable at any time and concurrently.
+func SetMetricFamilyInjectionHook(hook func() []*dto.MetricFamily) {
+ defRegistry.metricFamilyInjectionHook = hook
+}
+
+// PanicOnCollectError sets the behavior whether a panic is caused upon an error
+// while metrics are collected and served to the HTTP endpoint. By default, an
+// internal server error (status code 500) is served with an error message.
+func PanicOnCollectError(b bool) {
+ defRegistry.panicOnCollectError = b
+}
+
+// EnableCollectChecks enables (or disables) additional consistency checks
+// during metrics collection. These additional checks are not enabled by default
+// because they inflict a performance penalty and the errors they check for can
+// only happen if the used Metric and Collector types have internal programming
+// errors. It can be helpful to enable these checks while working with custom
+// Collectors or Metrics whose correctness is not well established yet.
+func EnableCollectChecks(b bool) {
+ defRegistry.collectChecksEnabled = b
+}
+
+// encoder is a function that writes a dto.MetricFamily to an io.Writer in a
+// certain encoding. It returns the number of bytes written and any error
+// encountered. Note that pbutil.WriteDelimited and pbutil.MetricFamilyToText
+// are encoders.
+type encoder func(io.Writer, *dto.MetricFamily) (int, error)
+
+type registry struct {
+ mtx sync.RWMutex
+ collectorsByID map[uint64]Collector // ID is a hash of the descIDs.
+ descIDs map[uint64]struct{}
+ dimHashesByName map[string]uint64
+ bufPool chan *bytes.Buffer
+ metricFamilyPool chan *dto.MetricFamily
+ metricPool chan *dto.Metric
+ metricFamilyInjectionHook func() []*dto.MetricFamily
+
+ panicOnCollectError, collectChecksEnabled bool
+}
+
+func (r *registry) Register(c Collector) (Collector, error) {
+ descChan := make(chan *Desc, capDescChan)
+ go func() {
+ c.Describe(descChan)
+ close(descChan)
+ }()
+
+ newDescIDs := map[uint64]struct{}{}
+ newDimHashesByName := map[string]uint64{}
+ var collectorID uint64 // Just a sum of all desc IDs.
+ var duplicateDescErr error
+
+ r.mtx.Lock()
+ defer r.mtx.Unlock()
+ // Coduct various tests...
+ for desc := range descChan {
+
+ // Is the descriptor valid at all?
+ if desc.err != nil {
+ return c, fmt.Errorf("descriptor %s is invalid: %s", desc, desc.err)
+ }
+
+ // Is the descID unique?
+ // (In other words: Is the fqName + constLabel combination unique?)
+ if _, exists := r.descIDs[desc.id]; exists {
+ duplicateDescErr = fmt.Errorf("descriptor %s already exists with the same fully-qualified name and const label values", desc)
+ }
+ // If it is not a duplicate desc in this collector, add it to
+ // the collectorID. (We allow duplicate descs within the same
+ // collector, but their existence must be a no-op.)
+ if _, exists := newDescIDs[desc.id]; !exists {
+ newDescIDs[desc.id] = struct{}{}
+ collectorID += desc.id
+ }
+
+ // Are all the label names and the help string consistent with
+ // previous descriptors of the same name?
+ // First check existing descriptors...
+ if dimHash, exists := r.dimHashesByName[desc.fqName]; exists {
+ if dimHash != desc.dimHash {
+ return nil, fmt.Errorf("a previously registered descriptor with the same fully-qualified name as %s has different label names or a different help string", desc)
+ }
+ } else {
+ // ...then check the new descriptors already seen.
+ if dimHash, exists := newDimHashesByName[desc.fqName]; exists {
+ if dimHash != desc.dimHash {
+ return nil, fmt.Errorf("descriptors reported by collector have inconsistent label names or help strings for the same fully-qualified name, offender is %s", desc)
+ }
+ } else {
+ newDimHashesByName[desc.fqName] = desc.dimHash
+ }
+ }
+ }
+ // Did anything happen at all?
+ if len(newDescIDs) == 0 {
+ return nil, errors.New("collector has no descriptors")
+ }
+ if existing, exists := r.collectorsByID[collectorID]; exists {
+ return existing, errAlreadyReg
+ }
+ // If the collectorID is new, but at least one of the descs existed
+ // before, we are in trouble.
+ if duplicateDescErr != nil {
+ return nil, duplicateDescErr
+ }
+
+ // Only after all tests have passed, actually register.
+ r.collectorsByID[collectorID] = c
+ for hash := range newDescIDs {
+ r.descIDs[hash] = struct{}{}
+ }
+ for name, dimHash := range newDimHashesByName {
+ r.dimHashesByName[name] = dimHash
+ }
+ return c, nil
+}
+
+func (r *registry) RegisterOrGet(m Collector) (Collector, error) {
+ existing, err := r.Register(m)
+ if err != nil && err != errAlreadyReg {
+ return nil, err
+ }
+ return existing, nil
+}
+
+func (r *registry) Unregister(c Collector) bool {
+ descChan := make(chan *Desc, capDescChan)
+ go func() {
+ c.Describe(descChan)
+ close(descChan)
+ }()
+
+ descIDs := map[uint64]struct{}{}
+ var collectorID uint64 // Just a sum of the desc IDs.
+ for desc := range descChan {
+ if _, exists := descIDs[desc.id]; !exists {
+ collectorID += desc.id
+ descIDs[desc.id] = struct{}{}
+ }
+ }
+
+ r.mtx.RLock()
+ if _, exists := r.collectorsByID[collectorID]; !exists {
+ r.mtx.RUnlock()
+ return false
+ }
+ r.mtx.RUnlock()
+
+ r.mtx.Lock()
+ defer r.mtx.Unlock()
+
+ delete(r.collectorsByID, collectorID)
+ for id := range descIDs {
+ delete(r.descIDs, id)
+ }
+ // dimHashesByName is left untouched as those must be consistent
+ // throughout the lifetime of a program.
+ return true
+}
+
+func (r *registry) Push(job, instance, pushURL, method string) error {
+ if !strings.Contains(pushURL, "://") {
+ pushURL = "http://" + pushURL
+ }
+ pushURL = fmt.Sprintf("%s/metrics/jobs/%s", pushURL, url.QueryEscape(job))
+ if instance != "" {
+ pushURL += "/instances/" + url.QueryEscape(instance)
+ }
+ buf := r.getBuf()
+ defer r.giveBuf(buf)
+ if err := r.writePB(expfmt.NewEncoder(buf, expfmt.FmtProtoDelim)); err != nil {
+ if r.panicOnCollectError {
+ panic(err)
+ }
+ return err
+ }
+ req, err := http.NewRequest(method, pushURL, buf)
+ if err != nil {
+ return err
+ }
+ req.Header.Set(contentTypeHeader, DelimitedTelemetryContentType)
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ if resp.StatusCode != 202 {
+ return fmt.Errorf("unexpected status code %d while pushing to %s", resp.StatusCode, pushURL)
+ }
+ return nil
+}
+
+func (r *registry) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ contentType := expfmt.Negotiate(req.Header)
+ buf := r.getBuf()
+ defer r.giveBuf(buf)
+ writer, encoding := decorateWriter(req, buf)
+ if err := r.writePB(expfmt.NewEncoder(writer, contentType)); err != nil {
+ if r.panicOnCollectError {
+ panic(err)
+ }
+ http.Error(w, "An error has occurred:\n\n"+err.Error(), http.StatusInternalServerError)
+ return
+ }
+ if closer, ok := writer.(io.Closer); ok {
+ closer.Close()
+ }
+ header := w.Header()
+ header.Set(contentTypeHeader, string(contentType))
+ header.Set(contentLengthHeader, fmt.Sprint(buf.Len()))
+ if encoding != "" {
+ header.Set(contentEncodingHeader, encoding)
+ }
+ w.Write(buf.Bytes())
+}
+
+func (r *registry) writePB(encoder expfmt.Encoder) error {
+ var metricHashes map[uint64]struct{}
+ if r.collectChecksEnabled {
+ metricHashes = make(map[uint64]struct{})
+ }
+ metricChan := make(chan Metric, capMetricChan)
+ wg := sync.WaitGroup{}
+
+ r.mtx.RLock()
+ metricFamiliesByName := make(map[string]*dto.MetricFamily, len(r.dimHashesByName))
+
+ // Scatter.
+ // (Collectors could be complex and slow, so we call them all at once.)
+ wg.Add(len(r.collectorsByID))
+ go func() {
+ wg.Wait()
+ close(metricChan)
+ }()
+ for _, collector := range r.collectorsByID {
+ go func(collector Collector) {
+ defer wg.Done()
+ collector.Collect(metricChan)
+ }(collector)
+ }
+ r.mtx.RUnlock()
+
+ // Drain metricChan in case of premature return.
+ defer func() {
+ for _ = range metricChan {
+ }
+ }()
+
+ // Gather.
+ for metric := range metricChan {
+ // This could be done concurrently, too, but it required locking
+ // of metricFamiliesByName (and of metricHashes if checks are
+ // enabled). Most likely not worth it.
+ desc := metric.Desc()
+ metricFamily, ok := metricFamiliesByName[desc.fqName]
+ if !ok {
+ metricFamily = r.getMetricFamily()
+ defer r.giveMetricFamily(metricFamily)
+ metricFamily.Name = proto.String(desc.fqName)
+ metricFamily.Help = proto.String(desc.help)
+ metricFamiliesByName[desc.fqName] = metricFamily
+ }
+ dtoMetric := r.getMetric()
+ defer r.giveMetric(dtoMetric)
+ if err := metric.Write(dtoMetric); err != nil {
+ // TODO: Consider different means of error reporting so
+ // that a single erroneous metric could be skipped
+ // instead of blowing up the whole collection.
+ return fmt.Errorf("error collecting metric %v: %s", desc, err)
+ }
+ switch {
+ case metricFamily.Type != nil:
+ // Type already set. We are good.
+ case dtoMetric.Gauge != nil:
+ metricFamily.Type = dto.MetricType_GAUGE.Enum()
+ case dtoMetric.Counter != nil:
+ metricFamily.Type = dto.MetricType_COUNTER.Enum()
+ case dtoMetric.Summary != nil:
+ metricFamily.Type = dto.MetricType_SUMMARY.Enum()
+ case dtoMetric.Untyped != nil:
+ metricFamily.Type = dto.MetricType_UNTYPED.Enum()
+ case dtoMetric.Histogram != nil:
+ metricFamily.Type = dto.MetricType_HISTOGRAM.Enum()
+ default:
+ return fmt.Errorf("empty metric collected: %s", dtoMetric)
+ }
+ if r.collectChecksEnabled {
+ if err := r.checkConsistency(metricFamily, dtoMetric, desc, metricHashes); err != nil {
+ return err
+ }
+ }
+ metricFamily.Metric = append(metricFamily.Metric, dtoMetric)
+ }
+
+ if r.metricFamilyInjectionHook != nil {
+ for _, mf := range r.metricFamilyInjectionHook() {
+ existingMF, exists := metricFamiliesByName[mf.GetName()]
+ if !exists {
+ metricFamiliesByName[mf.GetName()] = mf
+ if r.collectChecksEnabled {
+ for _, m := range mf.Metric {
+ if err := r.checkConsistency(mf, m, nil, metricHashes); err != nil {
+ return err
+ }
+ }
+ }
+ continue
+ }
+ for _, m := range mf.Metric {
+ if r.collectChecksEnabled {
+ if err := r.checkConsistency(existingMF, m, nil, metricHashes); err != nil {
+ return err
+ }
+ }
+ existingMF.Metric = append(existingMF.Metric, m)
+ }
+ }
+ }
+
+ // Now that MetricFamilies are all set, sort their Metrics
+ // lexicographically by their label values.
+ for _, mf := range metricFamiliesByName {
+ sort.Sort(metricSorter(mf.Metric))
+ }
+
+ // Write out MetricFamilies sorted by their name.
+ names := make([]string, 0, len(metricFamiliesByName))
+ for name := range metricFamiliesByName {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+
+ for _, name := range names {
+ if err := encoder.Encode(metricFamiliesByName[name]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (r *registry) checkConsistency(metricFamily *dto.MetricFamily, dtoMetric *dto.Metric, desc *Desc, metricHashes map[uint64]struct{}) error {
+
+ // Type consistency with metric family.
+ if metricFamily.GetType() == dto.MetricType_GAUGE && dtoMetric.Gauge == nil ||
+ metricFamily.GetType() == dto.MetricType_COUNTER && dtoMetric.Counter == nil ||
+ metricFamily.GetType() == dto.MetricType_SUMMARY && dtoMetric.Summary == nil ||
+ metricFamily.GetType() == dto.MetricType_HISTOGRAM && dtoMetric.Histogram == nil ||
+ metricFamily.GetType() == dto.MetricType_UNTYPED && dtoMetric.Untyped == nil {
+ return fmt.Errorf(
+ "collected metric %s %s is not a %s",
+ metricFamily.GetName(), dtoMetric, metricFamily.GetType(),
+ )
+ }
+
+ // Is the metric unique (i.e. no other metric with the same name and the same label values)?
+ h := fnv.New64a()
+ var buf bytes.Buffer
+ buf.WriteString(metricFamily.GetName())
+ buf.WriteByte(separatorByte)
+ h.Write(buf.Bytes())
+ // Make sure label pairs are sorted. We depend on it for the consistency
+ // check. Label pairs must be sorted by contract. But the point of this
+ // method is to check for contract violations. So we better do the sort
+ // now.
+ sort.Sort(LabelPairSorter(dtoMetric.Label))
+ for _, lp := range dtoMetric.Label {
+ buf.Reset()
+ buf.WriteString(lp.GetValue())
+ buf.WriteByte(separatorByte)
+ h.Write(buf.Bytes())
+ }
+ metricHash := h.Sum64()
+ if _, exists := metricHashes[metricHash]; exists {
+ return fmt.Errorf(
+ "collected metric %s %s was collected before with the same name and label values",
+ metricFamily.GetName(), dtoMetric,
+ )
+ }
+ metricHashes[metricHash] = struct{}{}
+
+ if desc == nil {
+ return nil // Nothing left to check if we have no desc.
+ }
+
+ // Desc consistency with metric family.
+ if metricFamily.GetName() != desc.fqName {
+ return fmt.Errorf(
+ "collected metric %s %s has name %q but should have %q",
+ metricFamily.GetName(), dtoMetric, metricFamily.GetName(), desc.fqName,
+ )
+ }
+ if metricFamily.GetHelp() != desc.help {
+ return fmt.Errorf(
+ "collected metric %s %s has help %q but should have %q",
+ metricFamily.GetName(), dtoMetric, metricFamily.GetHelp(), desc.help,
+ )
+ }
+
+ // Is the desc consistent with the content of the metric?
+ lpsFromDesc := make([]*dto.LabelPair, 0, len(dtoMetric.Label))
+ lpsFromDesc = append(lpsFromDesc, desc.constLabelPairs...)
+ for _, l := range desc.variableLabels {
+ lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
+ Name: proto.String(l),
+ })
+ }
+ if len(lpsFromDesc) != len(dtoMetric.Label) {
+ return fmt.Errorf(
+ "labels in collected metric %s %s are inconsistent with descriptor %s",
+ metricFamily.GetName(), dtoMetric, desc,
+ )
+ }
+ sort.Sort(LabelPairSorter(lpsFromDesc))
+ for i, lpFromDesc := range lpsFromDesc {
+ lpFromMetric := dtoMetric.Label[i]
+ if lpFromDesc.GetName() != lpFromMetric.GetName() ||
+ lpFromDesc.Value != nil && lpFromDesc.GetValue() != lpFromMetric.GetValue() {
+ return fmt.Errorf(
+ "labels in collected metric %s %s are inconsistent with descriptor %s",
+ metricFamily.GetName(), dtoMetric, desc,
+ )
+ }
+ }
+
+ r.mtx.RLock() // Remaining checks need the read lock.
+ defer r.mtx.RUnlock()
+
+ // Is the desc registered?
+ if _, exist := r.descIDs[desc.id]; !exist {
+ return fmt.Errorf(
+ "collected metric %s %s with unregistered descriptor %s",
+ metricFamily.GetName(), dtoMetric, desc,
+ )
+ }
+
+ return nil
+}
+
+func (r *registry) getBuf() *bytes.Buffer {
+ select {
+ case buf := <-r.bufPool:
+ return buf
+ default:
+ return &bytes.Buffer{}
+ }
+}
+
+func (r *registry) giveBuf(buf *bytes.Buffer) {
+ buf.Reset()
+ select {
+ case r.bufPool <- buf:
+ default:
+ }
+}
+
+func (r *registry) getMetricFamily() *dto.MetricFamily {
+ select {
+ case mf := <-r.metricFamilyPool:
+ return mf
+ default:
+ return &dto.MetricFamily{}
+ }
+}
+
+func (r *registry) giveMetricFamily(mf *dto.MetricFamily) {
+ mf.Reset()
+ select {
+ case r.metricFamilyPool <- mf:
+ default:
+ }
+}
+
+func (r *registry) getMetric() *dto.Metric {
+ select {
+ case m := <-r.metricPool:
+ return m
+ default:
+ return &dto.Metric{}
+ }
+}
+
+func (r *registry) giveMetric(m *dto.Metric) {
+ m.Reset()
+ select {
+ case r.metricPool <- m:
+ default:
+ }
+}
+
+func newRegistry() *registry {
+ return ®istry{
+ collectorsByID: map[uint64]Collector{},
+ descIDs: map[uint64]struct{}{},
+ dimHashesByName: map[string]uint64{},
+ bufPool: make(chan *bytes.Buffer, numBufs),
+ metricFamilyPool: make(chan *dto.MetricFamily, numMetricFamilies),
+ metricPool: make(chan *dto.Metric, numMetrics),
+ }
+}
+
+func newDefaultRegistry() *registry {
+ r := newRegistry()
+ r.Register(NewProcessCollector(os.Getpid(), ""))
+ r.Register(NewGoCollector())
+ return r
+}
+
+// decorateWriter wraps a writer to handle gzip compression if requested. It
+// returns the decorated writer and the appropriate "Content-Encoding" header
+// (which is empty if no compression is enabled).
+func decorateWriter(request *http.Request, writer io.Writer) (io.Writer, string) {
+ header := request.Header.Get(acceptEncodingHeader)
+ parts := strings.Split(header, ",")
+ for _, part := range parts {
+ part := strings.TrimSpace(part)
+ if part == "gzip" || strings.HasPrefix(part, "gzip;") {
+ return gzip.NewWriter(writer), "gzip"
+ }
+ }
+ return writer, ""
+}
+
+type metricSorter []*dto.Metric
+
+func (s metricSorter) Len() int {
+ return len(s)
+}
+
+func (s metricSorter) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s metricSorter) Less(i, j int) bool {
+ if len(s[i].Label) != len(s[j].Label) {
+ // This should not happen. The metrics are
+ // inconsistent. However, we have to deal with the fact, as
+ // people might use custom collectors or metric family injection
+ // to create inconsistent metrics. So let's simply compare the
+ // number of labels in this case. That will still yield
+ // reproducible sorting.
+ return len(s[i].Label) < len(s[j].Label)
+ }
+ for n, lp := range s[i].Label {
+ vi := lp.GetValue()
+ vj := s[j].Label[n].GetValue()
+ if vi != vj {
+ return vi < vj
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go
new file mode 100644
index 000000000..ea61e0123
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/registry_test.go
@@ -0,0 +1,535 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright (c) 2013, The Prometheus 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 prometheus
+
+import (
+ "bytes"
+ "encoding/binary"
+ "net/http"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+type fakeResponseWriter struct {
+ header http.Header
+ body bytes.Buffer
+}
+
+func (r *fakeResponseWriter) Header() http.Header {
+ return r.header
+}
+
+func (r *fakeResponseWriter) Write(d []byte) (l int, err error) {
+ return r.body.Write(d)
+}
+
+func (r *fakeResponseWriter) WriteHeader(c int) {
+}
+
+func testHandler(t testing.TB) {
+
+ metricVec := NewCounterVec(
+ CounterOpts{
+ Name: "name",
+ Help: "docstring",
+ ConstLabels: Labels{"constname": "constvalue"},
+ },
+ []string{"labelname"},
+ )
+
+ metricVec.WithLabelValues("val1").Inc()
+ metricVec.WithLabelValues("val2").Inc()
+
+ varintBuf := make([]byte, binary.MaxVarintLen32)
+
+ externalMetricFamily := &dto.MetricFamily{
+ Name: proto.String("externalname"),
+ Help: proto.String("externaldocstring"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("externalconstname"),
+ Value: proto.String("externalconstvalue"),
+ },
+ {
+ Name: proto.String("externallabelname"),
+ Value: proto.String("externalval1"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(1),
+ },
+ },
+ },
+ }
+ marshaledExternalMetricFamily, err := proto.Marshal(externalMetricFamily)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var externalBuf bytes.Buffer
+ l := binary.PutUvarint(varintBuf, uint64(len(marshaledExternalMetricFamily)))
+ _, err = externalBuf.Write(varintBuf[:l])
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = externalBuf.Write(marshaledExternalMetricFamily)
+ if err != nil {
+ t.Fatal(err)
+ }
+ externalMetricFamilyAsBytes := externalBuf.Bytes()
+ externalMetricFamilyAsText := []byte(`# HELP externalname externaldocstring
+# TYPE externalname counter
+externalname{externalconstname="externalconstvalue",externallabelname="externalval1"} 1
+`)
+ externalMetricFamilyAsProtoText := []byte(`name: "externalname"
+help: "externaldocstring"
+type: COUNTER
+metric: <
+ label: <
+ name: "externalconstname"
+ value: "externalconstvalue"
+ >
+ label: <
+ name: "externallabelname"
+ value: "externalval1"
+ >
+ counter: <
+ value: 1
+ >
+>
+
+`)
+ externalMetricFamilyAsProtoCompactText := []byte(`name:"externalname" help:"externaldocstring" type:COUNTER metric: label: counter: >
+`)
+
+ expectedMetricFamily := &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("docstring"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("constname"),
+ Value: proto.String("constvalue"),
+ },
+ {
+ Name: proto.String("labelname"),
+ Value: proto.String("val1"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(1),
+ },
+ },
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("constname"),
+ Value: proto.String("constvalue"),
+ },
+ {
+ Name: proto.String("labelname"),
+ Value: proto.String("val2"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(1),
+ },
+ },
+ },
+ }
+ marshaledExpectedMetricFamily, err := proto.Marshal(expectedMetricFamily)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var buf bytes.Buffer
+ l = binary.PutUvarint(varintBuf, uint64(len(marshaledExpectedMetricFamily)))
+ _, err = buf.Write(varintBuf[:l])
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = buf.Write(marshaledExpectedMetricFamily)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expectedMetricFamilyAsBytes := buf.Bytes()
+ expectedMetricFamilyAsText := []byte(`# HELP name docstring
+# TYPE name counter
+name{constname="constvalue",labelname="val1"} 1
+name{constname="constvalue",labelname="val2"} 1
+`)
+ expectedMetricFamilyAsProtoText := []byte(`name: "name"
+help: "docstring"
+type: COUNTER
+metric: <
+ label: <
+ name: "constname"
+ value: "constvalue"
+ >
+ label: <
+ name: "labelname"
+ value: "val1"
+ >
+ counter: <
+ value: 1
+ >
+>
+metric: <
+ label: <
+ name: "constname"
+ value: "constvalue"
+ >
+ label: <
+ name: "labelname"
+ value: "val2"
+ >
+ counter: <
+ value: 1
+ >
+>
+
+`)
+ expectedMetricFamilyAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric: label: counter: > metric: label: counter: >
+`)
+
+ externalMetricFamilyWithSameName := &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("inconsistent help string does not matter here"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("constname"),
+ Value: proto.String("constvalue"),
+ },
+ {
+ Name: proto.String("labelname"),
+ Value: proto.String("different_val"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(42),
+ },
+ },
+ },
+ }
+
+ expectedMetricFamilyMergedWithExternalAsProtoCompactText := []byte(`name:"name" help:"docstring" type:COUNTER metric: label: counter: > metric: label: counter: > metric: label: counter: >
+`)
+
+ type output struct {
+ headers map[string]string
+ body []byte
+ }
+
+ var scenarios = []struct {
+ headers map[string]string
+ out output
+ collector Collector
+ externalMF []*dto.MetricFamily
+ }{
+ { // 0
+ headers: map[string]string{
+ "Accept": "foo/bar;q=0.2, dings/bums;q=0.8",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: []byte{},
+ },
+ },
+ { // 1
+ headers: map[string]string{
+ "Accept": "foo/bar;q=0.2, application/quark;q=0.8",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: []byte{},
+ },
+ },
+ { // 2
+ headers: map[string]string{
+ "Accept": "foo/bar;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.8",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: []byte{},
+ },
+ },
+ { // 3
+ headers: map[string]string{
+ "Accept": "text/plain;q=0.2, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.8",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
+ },
+ body: []byte{},
+ },
+ },
+ { // 4
+ headers: map[string]string{
+ "Accept": "application/json",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: expectedMetricFamilyAsText,
+ },
+ collector: metricVec,
+ },
+ { // 5
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
+ },
+ body: expectedMetricFamilyAsBytes,
+ },
+ collector: metricVec,
+ },
+ { // 6
+ headers: map[string]string{
+ "Accept": "application/json",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: externalMetricFamilyAsText,
+ },
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 7
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
+ },
+ body: externalMetricFamilyAsBytes,
+ },
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 8
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
+ },
+ body: bytes.Join(
+ [][]byte{
+ externalMetricFamilyAsBytes,
+ expectedMetricFamilyAsBytes,
+ },
+ []byte{},
+ ),
+ },
+ collector: metricVec,
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 9
+ headers: map[string]string{
+ "Accept": "text/plain",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: []byte{},
+ },
+ },
+ { // 10
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: expectedMetricFamilyAsText,
+ },
+ collector: metricVec,
+ },
+ { // 11
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=bla;q=0.2, text/plain;q=0.5;version=0.0.4",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `text/plain; version=0.0.4`,
+ },
+ body: bytes.Join(
+ [][]byte{
+ externalMetricFamilyAsText,
+ expectedMetricFamilyAsText,
+ },
+ []byte{},
+ ),
+ },
+ collector: metricVec,
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 12
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.2, text/plain;q=0.5;version=0.0.2",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited`,
+ },
+ body: bytes.Join(
+ [][]byte{
+ externalMetricFamilyAsBytes,
+ expectedMetricFamilyAsBytes,
+ },
+ []byte{},
+ ),
+ },
+ collector: metricVec,
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 13
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=text;q=0.5, application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=delimited;q=0.4",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=text`,
+ },
+ body: bytes.Join(
+ [][]byte{
+ externalMetricFamilyAsProtoText,
+ expectedMetricFamilyAsProtoText,
+ },
+ []byte{},
+ ),
+ },
+ collector: metricVec,
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 14
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`,
+ },
+ body: bytes.Join(
+ [][]byte{
+ externalMetricFamilyAsProtoCompactText,
+ expectedMetricFamilyAsProtoCompactText,
+ },
+ []byte{},
+ ),
+ },
+ collector: metricVec,
+ externalMF: []*dto.MetricFamily{externalMetricFamily},
+ },
+ { // 15
+ headers: map[string]string{
+ "Accept": "application/vnd.google.protobuf;proto=io.prometheus.client.MetricFamily;encoding=compact-text",
+ },
+ out: output{
+ headers: map[string]string{
+ "Content-Type": `application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text`,
+ },
+ body: bytes.Join(
+ [][]byte{
+ externalMetricFamilyAsProtoCompactText,
+ expectedMetricFamilyMergedWithExternalAsProtoCompactText,
+ },
+ []byte{},
+ ),
+ },
+ collector: metricVec,
+ externalMF: []*dto.MetricFamily{
+ externalMetricFamily,
+ externalMetricFamilyWithSameName,
+ },
+ },
+ }
+ for i, scenario := range scenarios {
+ registry := newRegistry()
+ registry.collectChecksEnabled = true
+
+ if scenario.collector != nil {
+ registry.Register(scenario.collector)
+ }
+ if scenario.externalMF != nil {
+ registry.metricFamilyInjectionHook = func() []*dto.MetricFamily {
+ return scenario.externalMF
+ }
+ }
+ writer := &fakeResponseWriter{
+ header: http.Header{},
+ }
+ handler := InstrumentHandler("prometheus", registry)
+ request, _ := http.NewRequest("GET", "/", nil)
+ for key, value := range scenario.headers {
+ request.Header.Add(key, value)
+ }
+ handler(writer, request)
+
+ for key, value := range scenario.out.headers {
+ if writer.Header().Get(key) != value {
+ t.Errorf(
+ "%d. expected %q for header %q, got %q",
+ i, value, key, writer.Header().Get(key),
+ )
+ }
+ }
+
+ if !bytes.Equal(scenario.out.body, writer.body.Bytes()) {
+ t.Errorf(
+ "%d. expected %q for body, got %q",
+ i, scenario.out.body, writer.body.Bytes(),
+ )
+ }
+ }
+}
+
+func TestHandler(t *testing.T) {
+ testHandler(t)
+}
+
+func BenchmarkHandler(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testHandler(b)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go
new file mode 100644
index 000000000..5f78e2059
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary.go
@@ -0,0 +1,539 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "fmt"
+ "hash/fnv"
+ "math"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/beorn7/perks/quantile"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+// quantileLabel is used for the label that defines the quantile in a
+// summary.
+const quantileLabel = "quantile"
+
+// A Summary captures individual observations from an event or sample stream and
+// summarizes them in a manner similar to traditional summary statistics: 1. sum
+// of observations, 2. observation count, 3. rank estimations.
+//
+// A typical use-case is the observation of request latencies. By default, a
+// Summary provides the median, the 90th and the 99th percentile of the latency
+// as rank estimations.
+//
+// Note that the rank estimations cannot be aggregated in a meaningful way with
+// the Prometheus query language (i.e. you cannot average or add them). If you
+// need aggregatable quantiles (e.g. you want the 99th percentile latency of all
+// queries served across all instances of a service), consider the Histogram
+// metric type. See the Prometheus documentation for more details.
+//
+// To create Summary instances, use NewSummary.
+type Summary interface {
+ Metric
+ Collector
+
+ // Observe adds a single observation to the summary.
+ Observe(float64)
+}
+
+var (
+ // DefObjectives are the default Summary quantile values.
+ DefObjectives = map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}
+
+ errQuantileLabelNotAllowed = fmt.Errorf(
+ "%q is not allowed as label name in summaries", quantileLabel,
+ )
+)
+
+// Default values for SummaryOpts.
+const (
+ // DefMaxAge is the default duration for which observations stay
+ // relevant.
+ DefMaxAge time.Duration = 10 * time.Minute
+ // DefAgeBuckets is the default number of buckets used to calculate the
+ // age of observations.
+ DefAgeBuckets = 5
+ // DefBufCap is the standard buffer size for collecting Summary observations.
+ DefBufCap = 500
+)
+
+// SummaryOpts bundles the options for creating a Summary metric. It is
+// mandatory to set Name and Help to a non-empty string. All other fields are
+// optional and can safely be left at their zero value.
+type SummaryOpts struct {
+ // Namespace, Subsystem, and Name are components of the fully-qualified
+ // name of the Summary (created by joining these components with
+ // "_"). Only Name is mandatory, the others merely help structuring the
+ // name. Note that the fully-qualified name of the Summary must be a
+ // valid Prometheus metric name.
+ Namespace string
+ Subsystem string
+ Name string
+
+ // Help provides information about this Summary. Mandatory!
+ //
+ // Metrics with the same fully-qualified name must have the same Help
+ // string.
+ Help string
+
+ // ConstLabels are used to attach fixed labels to this
+ // Summary. Summaries with the same fully-qualified name must have the
+ // same label names in their ConstLabels.
+ //
+ // Note that in most cases, labels have a value that varies during the
+ // lifetime of a process. Those labels are usually managed with a
+ // SummaryVec. ConstLabels serve only special purposes. One is for the
+ // special case where the value of a label does not change during the
+ // lifetime of a process, e.g. if the revision of the running binary is
+ // put into a label. Another, more advanced purpose is if more than one
+ // Collector needs to collect Summaries with the same fully-qualified
+ // name. In that case, those Summaries must differ in the values of
+ // their ConstLabels. See the Collector examples.
+ //
+ // If the value of a label never changes (not even between binaries),
+ // that label most likely should not be a label at all (but part of the
+ // metric name).
+ ConstLabels Labels
+
+ // Objectives defines the quantile rank estimates with their respective
+ // absolute error. If Objectives[q] = e, then the value reported
+ // for q will be the φ-quantile value for some φ between q-e and q+e.
+ // The default value is DefObjectives.
+ Objectives map[float64]float64
+
+ // MaxAge defines the duration for which an observation stays relevant
+ // for the summary. Must be positive. The default value is DefMaxAge.
+ MaxAge time.Duration
+
+ // AgeBuckets is the number of buckets used to exclude observations that
+ // are older than MaxAge from the summary. A higher number has a
+ // resource penalty, so only increase it if the higher resolution is
+ // really required. For very high observation rates, you might want to
+ // reduce the number of age buckets. With only one age bucket, you will
+ // effectively see a complete reset of the summary each time MaxAge has
+ // passed. The default value is DefAgeBuckets.
+ AgeBuckets uint32
+
+ // BufCap defines the default sample stream buffer size. The default
+ // value of DefBufCap should suffice for most uses. If there is a need
+ // to increase the value, a multiple of 500 is recommended (because that
+ // is the internal buffer size of the underlying package
+ // "github.com/bmizerany/perks/quantile").
+ BufCap uint32
+}
+
+// TODO: Great fuck-up with the sliding-window decay algorithm... The Merge
+// method of perk/quantile is actually not working as advertised - and it might
+// be unfixable, as the underlying algorithm is apparently not capable of
+// merging summaries in the first place. To avoid using Merge, we are currently
+// adding observations to _each_ age bucket, i.e. the effort to add a sample is
+// essentially multiplied by the number of age buckets. When rotating age
+// buckets, we empty the previous head stream. On scrape time, we simply take
+// the quantiles from the head stream (no merging required). Result: More effort
+// on observation time, less effort on scrape time, which is exactly the
+// opposite of what we try to accomplish, but at least the results are correct.
+//
+// The quite elegant previous contraption to merge the age buckets efficiently
+// on scrape time (see code up commit 6b9530d72ea715f0ba612c0120e6e09fbf1d49d0)
+// can't be used anymore.
+
+// NewSummary creates a new Summary based on the provided SummaryOpts.
+func NewSummary(opts SummaryOpts) Summary {
+ return newSummary(
+ NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ),
+ opts,
+ )
+}
+
+func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
+ if len(desc.variableLabels) != len(labelValues) {
+ panic(errInconsistentCardinality)
+ }
+
+ for _, n := range desc.variableLabels {
+ if n == quantileLabel {
+ panic(errQuantileLabelNotAllowed)
+ }
+ }
+ for _, lp := range desc.constLabelPairs {
+ if lp.GetName() == quantileLabel {
+ panic(errQuantileLabelNotAllowed)
+ }
+ }
+
+ if len(opts.Objectives) == 0 {
+ opts.Objectives = DefObjectives
+ }
+
+ if opts.MaxAge < 0 {
+ panic(fmt.Errorf("illegal max age MaxAge=%v", opts.MaxAge))
+ }
+ if opts.MaxAge == 0 {
+ opts.MaxAge = DefMaxAge
+ }
+
+ if opts.AgeBuckets == 0 {
+ opts.AgeBuckets = DefAgeBuckets
+ }
+
+ if opts.BufCap == 0 {
+ opts.BufCap = DefBufCap
+ }
+
+ s := &summary{
+ desc: desc,
+
+ objectives: opts.Objectives,
+ sortedObjectives: make([]float64, 0, len(opts.Objectives)),
+
+ labelPairs: makeLabelPairs(desc, labelValues),
+
+ hotBuf: make([]float64, 0, opts.BufCap),
+ coldBuf: make([]float64, 0, opts.BufCap),
+ streamDuration: opts.MaxAge / time.Duration(opts.AgeBuckets),
+ }
+ s.headStreamExpTime = time.Now().Add(s.streamDuration)
+ s.hotBufExpTime = s.headStreamExpTime
+
+ for i := uint32(0); i < opts.AgeBuckets; i++ {
+ s.streams = append(s.streams, s.newStream())
+ }
+ s.headStream = s.streams[0]
+
+ for qu := range s.objectives {
+ s.sortedObjectives = append(s.sortedObjectives, qu)
+ }
+ sort.Float64s(s.sortedObjectives)
+
+ s.Init(s) // Init self-collection.
+ return s
+}
+
+type summary struct {
+ SelfCollector
+
+ bufMtx sync.Mutex // Protects hotBuf and hotBufExpTime.
+ mtx sync.Mutex // Protects every other moving part.
+ // Lock bufMtx before mtx if both are needed.
+
+ desc *Desc
+
+ objectives map[float64]float64
+ sortedObjectives []float64
+
+ labelPairs []*dto.LabelPair
+
+ sum float64
+ cnt uint64
+
+ hotBuf, coldBuf []float64
+
+ streams []*quantile.Stream
+ streamDuration time.Duration
+ headStream *quantile.Stream
+ headStreamIdx int
+ headStreamExpTime, hotBufExpTime time.Time
+}
+
+func (s *summary) Desc() *Desc {
+ return s.desc
+}
+
+func (s *summary) Observe(v float64) {
+ s.bufMtx.Lock()
+ defer s.bufMtx.Unlock()
+
+ now := time.Now()
+ if now.After(s.hotBufExpTime) {
+ s.asyncFlush(now)
+ }
+ s.hotBuf = append(s.hotBuf, v)
+ if len(s.hotBuf) == cap(s.hotBuf) {
+ s.asyncFlush(now)
+ }
+}
+
+func (s *summary) Write(out *dto.Metric) error {
+ sum := &dto.Summary{}
+ qs := make([]*dto.Quantile, 0, len(s.objectives))
+
+ s.bufMtx.Lock()
+ s.mtx.Lock()
+ // Swap bufs even if hotBuf is empty to set new hotBufExpTime.
+ s.swapBufs(time.Now())
+ s.bufMtx.Unlock()
+
+ s.flushColdBuf()
+ sum.SampleCount = proto.Uint64(s.cnt)
+ sum.SampleSum = proto.Float64(s.sum)
+
+ for _, rank := range s.sortedObjectives {
+ var q float64
+ if s.headStream.Count() == 0 {
+ q = math.NaN()
+ } else {
+ q = s.headStream.Query(rank)
+ }
+ qs = append(qs, &dto.Quantile{
+ Quantile: proto.Float64(rank),
+ Value: proto.Float64(q),
+ })
+ }
+
+ s.mtx.Unlock()
+
+ if len(qs) > 0 {
+ sort.Sort(quantSort(qs))
+ }
+ sum.Quantile = qs
+
+ out.Summary = sum
+ out.Label = s.labelPairs
+ return nil
+}
+
+func (s *summary) newStream() *quantile.Stream {
+ return quantile.NewTargeted(s.objectives)
+}
+
+// asyncFlush needs bufMtx locked.
+func (s *summary) asyncFlush(now time.Time) {
+ s.mtx.Lock()
+ s.swapBufs(now)
+
+ // Unblock the original goroutine that was responsible for the mutation
+ // that triggered the compaction. But hold onto the global non-buffer
+ // state mutex until the operation finishes.
+ go func() {
+ s.flushColdBuf()
+ s.mtx.Unlock()
+ }()
+}
+
+// rotateStreams needs mtx AND bufMtx locked.
+func (s *summary) maybeRotateStreams() {
+ for !s.hotBufExpTime.Equal(s.headStreamExpTime) {
+ s.headStream.Reset()
+ s.headStreamIdx++
+ if s.headStreamIdx >= len(s.streams) {
+ s.headStreamIdx = 0
+ }
+ s.headStream = s.streams[s.headStreamIdx]
+ s.headStreamExpTime = s.headStreamExpTime.Add(s.streamDuration)
+ }
+}
+
+// flushColdBuf needs mtx locked.
+func (s *summary) flushColdBuf() {
+ for _, v := range s.coldBuf {
+ for _, stream := range s.streams {
+ stream.Insert(v)
+ }
+ s.cnt++
+ s.sum += v
+ }
+ s.coldBuf = s.coldBuf[0:0]
+ s.maybeRotateStreams()
+}
+
+// swapBufs needs mtx AND bufMtx locked, coldBuf must be empty.
+func (s *summary) swapBufs(now time.Time) {
+ if len(s.coldBuf) != 0 {
+ panic("coldBuf is not empty")
+ }
+ s.hotBuf, s.coldBuf = s.coldBuf, s.hotBuf
+ // hotBuf is now empty and gets new expiration set.
+ for now.After(s.hotBufExpTime) {
+ s.hotBufExpTime = s.hotBufExpTime.Add(s.streamDuration)
+ }
+}
+
+type quantSort []*dto.Quantile
+
+func (s quantSort) Len() int {
+ return len(s)
+}
+
+func (s quantSort) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+func (s quantSort) Less(i, j int) bool {
+ return s[i].GetQuantile() < s[j].GetQuantile()
+}
+
+// SummaryVec is a Collector that bundles a set of Summaries that all share the
+// same Desc, but have different values for their variable labels. This is used
+// if you want to count the same thing partitioned by various dimensions
+// (e.g. HTTP request latencies, partitioned by status code and method). Create
+// instances with NewSummaryVec.
+type SummaryVec struct {
+ MetricVec
+}
+
+// NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and
+// partitioned by the given label names. At least one label name must be
+// provided.
+func NewSummaryVec(opts SummaryOpts, labelNames []string) *SummaryVec {
+ desc := NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ labelNames,
+ opts.ConstLabels,
+ )
+ return &SummaryVec{
+ MetricVec: MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ return newSummary(desc, opts, lvs...)
+ },
+ },
+ }
+}
+
+// GetMetricWithLabelValues replaces the method of the same name in
+// MetricVec. The difference is that this method returns a Summary and not a
+// Metric so that no type conversion is required.
+func (m *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Summary, error) {
+ metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
+ if metric != nil {
+ return metric.(Summary), err
+ }
+ return nil, err
+}
+
+// GetMetricWith replaces the method of the same name in MetricVec. The
+// difference is that this method returns a Summary and not a Metric so that no
+// type conversion is required.
+func (m *SummaryVec) GetMetricWith(labels Labels) (Summary, error) {
+ metric, err := m.MetricVec.GetMetricWith(labels)
+ if metric != nil {
+ return metric.(Summary), err
+ }
+ return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. By not returning an
+// error, WithLabelValues allows shortcuts like
+// myVec.WithLabelValues("404", "GET").Observe(42.21)
+func (m *SummaryVec) WithLabelValues(lvs ...string) Summary {
+ return m.MetricVec.WithLabelValues(lvs...).(Summary)
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. By not returning an error, With allows shortcuts like
+// myVec.With(Labels{"code": "404", "method": "GET"}).Observe(42.21)
+func (m *SummaryVec) With(labels Labels) Summary {
+ return m.MetricVec.With(labels).(Summary)
+}
+
+type constSummary struct {
+ desc *Desc
+ count uint64
+ sum float64
+ quantiles map[float64]float64
+ labelPairs []*dto.LabelPair
+}
+
+func (s *constSummary) Desc() *Desc {
+ return s.desc
+}
+
+func (s *constSummary) Write(out *dto.Metric) error {
+ sum := &dto.Summary{}
+ qs := make([]*dto.Quantile, 0, len(s.quantiles))
+
+ sum.SampleCount = proto.Uint64(s.count)
+ sum.SampleSum = proto.Float64(s.sum)
+
+ for rank, q := range s.quantiles {
+ qs = append(qs, &dto.Quantile{
+ Quantile: proto.Float64(rank),
+ Value: proto.Float64(q),
+ })
+ }
+
+ if len(qs) > 0 {
+ sort.Sort(quantSort(qs))
+ }
+ sum.Quantile = qs
+
+ out.Summary = sum
+ out.Label = s.labelPairs
+
+ return nil
+}
+
+// NewConstSummary returns a metric representing a Prometheus summary with fixed
+// values for the count, sum, and quantiles. As those parameters cannot be
+// changed, the returned value does not implement the Summary interface (but
+// only the Metric interface). Users of this package will not have much use for
+// it in regular operations. However, when implementing custom Collectors, it is
+// useful as a throw-away metric that is generated on the fly to send it to
+// Prometheus in the Collect method.
+//
+// quantiles maps ranks to quantile values. For example, a median latency of
+// 0.23s and a 99th percentile latency of 0.56s would be expressed as:
+// map[float64]float64{0.5: 0.23, 0.99: 0.56}
+//
+// NewConstSummary returns an error if the length of labelValues is not
+// consistent with the variable labels in Desc.
+func NewConstSummary(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ quantiles map[float64]float64,
+ labelValues ...string,
+) (Metric, error) {
+ if len(desc.variableLabels) != len(labelValues) {
+ return nil, errInconsistentCardinality
+ }
+ return &constSummary{
+ desc: desc,
+ count: count,
+ sum: sum,
+ quantiles: quantiles,
+ labelPairs: makeLabelPairs(desc, labelValues),
+ }, nil
+}
+
+// MustNewConstSummary is a version of NewConstSummary that panics where
+// NewConstMetric would have returned an error.
+func MustNewConstSummary(
+ desc *Desc,
+ count uint64,
+ sum float64,
+ quantiles map[float64]float64,
+ labelValues ...string,
+) Metric {
+ m, err := NewConstSummary(desc, count, sum, quantiles, labelValues...)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go
new file mode 100644
index 000000000..7fc7638c3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/summary_test.go
@@ -0,0 +1,347 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "math"
+ "math/rand"
+ "sort"
+ "sync"
+ "testing"
+ "testing/quick"
+ "time"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func benchmarkSummaryObserve(w int, b *testing.B) {
+ b.StopTimer()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(w)
+
+ g := new(sync.WaitGroup)
+ g.Add(1)
+
+ s := NewSummary(SummaryOpts{})
+
+ for i := 0; i < w; i++ {
+ go func() {
+ g.Wait()
+
+ for i := 0; i < b.N; i++ {
+ s.Observe(float64(i))
+ }
+
+ wg.Done()
+ }()
+ }
+
+ b.StartTimer()
+ g.Done()
+ wg.Wait()
+}
+
+func BenchmarkSummaryObserve1(b *testing.B) {
+ benchmarkSummaryObserve(1, b)
+}
+
+func BenchmarkSummaryObserve2(b *testing.B) {
+ benchmarkSummaryObserve(2, b)
+}
+
+func BenchmarkSummaryObserve4(b *testing.B) {
+ benchmarkSummaryObserve(4, b)
+}
+
+func BenchmarkSummaryObserve8(b *testing.B) {
+ benchmarkSummaryObserve(8, b)
+}
+
+func benchmarkSummaryWrite(w int, b *testing.B) {
+ b.StopTimer()
+
+ wg := new(sync.WaitGroup)
+ wg.Add(w)
+
+ g := new(sync.WaitGroup)
+ g.Add(1)
+
+ s := NewSummary(SummaryOpts{})
+
+ for i := 0; i < 1000000; i++ {
+ s.Observe(float64(i))
+ }
+
+ for j := 0; j < w; j++ {
+ outs := make([]dto.Metric, b.N)
+
+ go func(o []dto.Metric) {
+ g.Wait()
+
+ for i := 0; i < b.N; i++ {
+ s.Write(&o[i])
+ }
+
+ wg.Done()
+ }(outs)
+ }
+
+ b.StartTimer()
+ g.Done()
+ wg.Wait()
+}
+
+func BenchmarkSummaryWrite1(b *testing.B) {
+ benchmarkSummaryWrite(1, b)
+}
+
+func BenchmarkSummaryWrite2(b *testing.B) {
+ benchmarkSummaryWrite(2, b)
+}
+
+func BenchmarkSummaryWrite4(b *testing.B) {
+ benchmarkSummaryWrite(4, b)
+}
+
+func BenchmarkSummaryWrite8(b *testing.B) {
+ benchmarkSummaryWrite(8, b)
+}
+
+func TestSummaryConcurrency(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ }
+
+ rand.Seed(42)
+
+ it := func(n uint32) bool {
+ mutations := int(n%1e4 + 1e4)
+ concLevel := int(n%5 + 1)
+ total := mutations * concLevel
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sum := NewSummary(SummaryOpts{
+ Name: "test_summary",
+ Help: "helpless",
+ })
+
+ allVars := make([]float64, total)
+ var sampleSum float64
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ for j := 0; j < mutations; j++ {
+ v := rand.NormFloat64()
+ vals[j] = v
+ allVars[i*mutations+j] = v
+ sampleSum += v
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for _, v := range vals {
+ sum.Observe(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ sort.Float64s(allVars)
+ start.Done()
+ end.Wait()
+
+ m := &dto.Metric{}
+ sum.Write(m)
+ if got, want := int(*m.Summary.SampleCount), total; got != want {
+ t.Errorf("got sample count %d, want %d", got, want)
+ }
+ if got, want := *m.Summary.SampleSum, sampleSum; math.Abs((got-want)/want) > 0.001 {
+ t.Errorf("got sample sum %f, want %f", got, want)
+ }
+
+ objectives := make([]float64, 0, len(DefObjectives))
+ for qu := range DefObjectives {
+ objectives = append(objectives, qu)
+ }
+ sort.Float64s(objectives)
+
+ for i, wantQ := range objectives {
+ ε := DefObjectives[wantQ]
+ gotQ := *m.Summary.Quantile[i].Quantile
+ gotV := *m.Summary.Quantile[i].Value
+ min, max := getBounds(allVars, wantQ, ε)
+ if gotQ != wantQ {
+ t.Errorf("got quantile %f, want %f", gotQ, wantQ)
+ }
+ if gotV < min || gotV > max {
+ t.Errorf("got %f for quantile %f, want [%f,%f]", gotV, gotQ, min, max)
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestSummaryVecConcurrency(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ }
+
+ rand.Seed(42)
+
+ objectives := make([]float64, 0, len(DefObjectives))
+ for qu := range DefObjectives {
+
+ objectives = append(objectives, qu)
+ }
+ sort.Float64s(objectives)
+
+ it := func(n uint32) bool {
+ mutations := int(n%1e4 + 1e4)
+ concLevel := int(n%7 + 1)
+ vecLength := int(n%3 + 1)
+
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ sum := NewSummaryVec(
+ SummaryOpts{
+ Name: "test_summary",
+ Help: "helpless",
+ },
+ []string{"label"},
+ )
+
+ allVars := make([][]float64, vecLength)
+ sampleSums := make([]float64, vecLength)
+ for i := 0; i < concLevel; i++ {
+ vals := make([]float64, mutations)
+ picks := make([]int, mutations)
+ for j := 0; j < mutations; j++ {
+ v := rand.NormFloat64()
+ vals[j] = v
+ pick := rand.Intn(vecLength)
+ picks[j] = pick
+ allVars[pick] = append(allVars[pick], v)
+ sampleSums[pick] += v
+ }
+
+ go func(vals []float64) {
+ start.Wait()
+ for i, v := range vals {
+ sum.WithLabelValues(string('A' + picks[i])).Observe(v)
+ }
+ end.Done()
+ }(vals)
+ }
+ for _, vars := range allVars {
+ sort.Float64s(vars)
+ }
+ start.Done()
+ end.Wait()
+
+ for i := 0; i < vecLength; i++ {
+ m := &dto.Metric{}
+ s := sum.WithLabelValues(string('A' + i))
+ s.Write(m)
+ if got, want := int(*m.Summary.SampleCount), len(allVars[i]); got != want {
+ t.Errorf("got sample count %d for label %c, want %d", got, 'A'+i, want)
+ }
+ if got, want := *m.Summary.SampleSum, sampleSums[i]; math.Abs((got-want)/want) > 0.001 {
+ t.Errorf("got sample sum %f for label %c, want %f", got, 'A'+i, want)
+ }
+ for j, wantQ := range objectives {
+ ε := DefObjectives[wantQ]
+ gotQ := *m.Summary.Quantile[j].Quantile
+ gotV := *m.Summary.Quantile[j].Value
+ min, max := getBounds(allVars[i], wantQ, ε)
+ if gotQ != wantQ {
+ t.Errorf("got quantile %f for label %c, want %f", gotQ, 'A'+i, wantQ)
+ }
+ if gotV < min || gotV > max {
+ t.Errorf("got %f for quantile %f for label %c, want [%f,%f]", gotV, gotQ, 'A'+i, min, max)
+ }
+ }
+ }
+ return true
+ }
+
+ if err := quick.Check(it, nil); err != nil {
+ t.Error(err)
+ }
+}
+
+func TestSummaryDecay(t *testing.T) {
+ if testing.Short() {
+ t.Skip("Skipping test in short mode.")
+ // More because it depends on timing than because it is particularly long...
+ }
+
+ sum := NewSummary(SummaryOpts{
+ Name: "test_summary",
+ Help: "helpless",
+ MaxAge: 100 * time.Millisecond,
+ Objectives: map[float64]float64{0.1: 0.001},
+ AgeBuckets: 10,
+ })
+
+ m := &dto.Metric{}
+ i := 0
+ tick := time.NewTicker(time.Millisecond)
+ for _ = range tick.C {
+ i++
+ sum.Observe(float64(i))
+ if i%10 == 0 {
+ sum.Write(m)
+ if got, want := *m.Summary.Quantile[0].Value, math.Max(float64(i)/10, float64(i-90)); math.Abs(got-want) > 20 {
+ t.Errorf("%d. got %f, want %f", i, got, want)
+ }
+ m.Reset()
+ }
+ if i >= 1000 {
+ break
+ }
+ }
+ tick.Stop()
+ // Wait for MaxAge without observations and make sure quantiles are NaN.
+ time.Sleep(100 * time.Millisecond)
+ sum.Write(m)
+ if got := *m.Summary.Quantile[0].Value; !math.IsNaN(got) {
+ t.Errorf("got %f, want NaN after expiration", got)
+ }
+}
+
+func getBounds(vars []float64, q, ε float64) (min, max float64) {
+ // TODO: This currently tolerates an error of up to 2*ε. The error must
+ // be at most ε, but for some reason, it's sometimes slightly
+ // higher. That's a bug.
+ n := float64(len(vars))
+ lower := int((q - 2*ε) * n)
+ upper := int(math.Ceil((q + 2*ε) * n))
+ min = vars[0]
+ if lower > 1 {
+ min = vars[lower-1]
+ }
+ max = vars[len(vars)-1]
+ if upper < len(vars) {
+ max = vars[upper-1]
+ }
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go
new file mode 100644
index 000000000..c65ab1c53
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/untyped.go
@@ -0,0 +1,145 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import "hash/fnv"
+
+// Untyped is a Metric that represents a single numerical value that can
+// arbitrarily go up and down.
+//
+// An Untyped metric works the same as a Gauge. The only difference is that to
+// no type information is implied.
+//
+// To create Untyped instances, use NewUntyped.
+type Untyped interface {
+ Metric
+ Collector
+
+ // Set sets the Untyped metric to an arbitrary value.
+ Set(float64)
+ // Inc increments the Untyped metric by 1.
+ Inc()
+ // Dec decrements the Untyped metric by 1.
+ Dec()
+ // Add adds the given value to the Untyped metric. (The value can be
+ // negative, resulting in a decrease.)
+ Add(float64)
+ // Sub subtracts the given value from the Untyped metric. (The value can
+ // be negative, resulting in an increase.)
+ Sub(float64)
+}
+
+// UntypedOpts is an alias for Opts. See there for doc comments.
+type UntypedOpts Opts
+
+// NewUntyped creates a new Untyped metric from the provided UntypedOpts.
+func NewUntyped(opts UntypedOpts) Untyped {
+ return newValue(NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ), UntypedValue, 0)
+}
+
+// UntypedVec is a Collector that bundles a set of Untyped metrics that all
+// share the same Desc, but have different values for their variable
+// labels. This is used if you want to count the same thing partitioned by
+// various dimensions. Create instances with NewUntypedVec.
+type UntypedVec struct {
+ MetricVec
+}
+
+// NewUntypedVec creates a new UntypedVec based on the provided UntypedOpts and
+// partitioned by the given label names. At least one label name must be
+// provided.
+func NewUntypedVec(opts UntypedOpts, labelNames []string) *UntypedVec {
+ desc := NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ labelNames,
+ opts.ConstLabels,
+ )
+ return &UntypedVec{
+ MetricVec: MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ return newValue(desc, UntypedValue, 0, lvs...)
+ },
+ },
+ }
+}
+
+// GetMetricWithLabelValues replaces the method of the same name in
+// MetricVec. The difference is that this method returns an Untyped and not a
+// Metric so that no type conversion is required.
+func (m *UntypedVec) GetMetricWithLabelValues(lvs ...string) (Untyped, error) {
+ metric, err := m.MetricVec.GetMetricWithLabelValues(lvs...)
+ if metric != nil {
+ return metric.(Untyped), err
+ }
+ return nil, err
+}
+
+// GetMetricWith replaces the method of the same name in MetricVec. The
+// difference is that this method returns an Untyped and not a Metric so that no
+// type conversion is required.
+func (m *UntypedVec) GetMetricWith(labels Labels) (Untyped, error) {
+ metric, err := m.MetricVec.GetMetricWith(labels)
+ if metric != nil {
+ return metric.(Untyped), err
+ }
+ return nil, err
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics where
+// GetMetricWithLabelValues would have returned an error. By not returning an
+// error, WithLabelValues allows shortcuts like
+// myVec.WithLabelValues("404", "GET").Add(42)
+func (m *UntypedVec) WithLabelValues(lvs ...string) Untyped {
+ return m.MetricVec.WithLabelValues(lvs...).(Untyped)
+}
+
+// With works as GetMetricWith, but panics where GetMetricWithLabels would have
+// returned an error. By not returning an error, With allows shortcuts like
+// myVec.With(Labels{"code": "404", "method": "GET"}).Add(42)
+func (m *UntypedVec) With(labels Labels) Untyped {
+ return m.MetricVec.With(labels).(Untyped)
+}
+
+// UntypedFunc is an Untyped whose value is determined at collect time by
+// calling a provided function.
+//
+// To create UntypedFunc instances, use NewUntypedFunc.
+type UntypedFunc interface {
+ Metric
+ Collector
+}
+
+// NewUntypedFunc creates a new UntypedFunc based on the provided
+// UntypedOpts. The value reported is determined by calling the given function
+// from within the Write method. Take into account that metric collection may
+// happen concurrently. If that results in concurrent calls to Write, like in
+// the case where an UntypedFunc is directly registered with Prometheus, the
+// provided function must be concurrency-safe.
+func NewUntypedFunc(opts UntypedOpts, function func() float64) UntypedFunc {
+ return newValueFunc(NewDesc(
+ BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
+ opts.Help,
+ nil,
+ opts.ConstLabels,
+ ), UntypedValue, function)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go
new file mode 100644
index 000000000..fc8aaf3d8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/value.go
@@ -0,0 +1,233 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "sort"
+ "sync/atomic"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+// ValueType is an enumeration of metric types that represent a simple value.
+type ValueType int
+
+// Possible values for the ValueType enum.
+const (
+ _ ValueType = iota
+ CounterValue
+ GaugeValue
+ UntypedValue
+)
+
+var errInconsistentCardinality = errors.New("inconsistent label cardinality")
+
+// value is a generic metric for simple values. It implements Metric, Collector,
+// Counter, Gauge, and Untyped. Its effective type is determined by
+// ValueType. This is a low-level building block used by the library to back the
+// implementations of Counter, Gauge, and Untyped.
+type value struct {
+ // valBits containst the bits of the represented float64 value. It has
+ // to go first in the struct to guarantee alignment for atomic
+ // operations. http://golang.org/pkg/sync/atomic/#pkg-note-BUG
+ valBits uint64
+
+ SelfCollector
+
+ desc *Desc
+ valType ValueType
+ labelPairs []*dto.LabelPair
+}
+
+// newValue returns a newly allocated value with the given Desc, ValueType,
+// sample value and label values. It panics if the number of label
+// values is different from the number of variable labels in Desc.
+func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...string) *value {
+ if len(labelValues) != len(desc.variableLabels) {
+ panic(errInconsistentCardinality)
+ }
+ result := &value{
+ desc: desc,
+ valType: valueType,
+ valBits: math.Float64bits(val),
+ labelPairs: makeLabelPairs(desc, labelValues),
+ }
+ result.Init(result)
+ return result
+}
+
+func (v *value) Desc() *Desc {
+ return v.desc
+}
+
+func (v *value) Set(val float64) {
+ atomic.StoreUint64(&v.valBits, math.Float64bits(val))
+}
+
+func (v *value) Inc() {
+ v.Add(1)
+}
+
+func (v *value) Dec() {
+ v.Add(-1)
+}
+
+func (v *value) Add(val float64) {
+ for {
+ oldBits := atomic.LoadUint64(&v.valBits)
+ newBits := math.Float64bits(math.Float64frombits(oldBits) + val)
+ if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) {
+ return
+ }
+ }
+}
+
+func (v *value) Sub(val float64) {
+ v.Add(val * -1)
+}
+
+func (v *value) Write(out *dto.Metric) error {
+ val := math.Float64frombits(atomic.LoadUint64(&v.valBits))
+ return populateMetric(v.valType, val, v.labelPairs, out)
+}
+
+// valueFunc is a generic metric for simple values retrieved on collect time
+// from a function. It implements Metric and Collector. Its effective type is
+// determined by ValueType. This is a low-level building block used by the
+// library to back the implementations of CounterFunc, GaugeFunc, and
+// UntypedFunc.
+type valueFunc struct {
+ SelfCollector
+
+ desc *Desc
+ valType ValueType
+ function func() float64
+ labelPairs []*dto.LabelPair
+}
+
+// newValueFunc returns a newly allocated valueFunc with the given Desc and
+// ValueType. The value reported is determined by calling the given function
+// from within the Write method. Take into account that metric collection may
+// happen concurrently. If that results in concurrent calls to Write, like in
+// the case where a valueFunc is directly registered with Prometheus, the
+// provided function must be concurrency-safe.
+func newValueFunc(desc *Desc, valueType ValueType, function func() float64) *valueFunc {
+ result := &valueFunc{
+ desc: desc,
+ valType: valueType,
+ function: function,
+ labelPairs: makeLabelPairs(desc, nil),
+ }
+ result.Init(result)
+ return result
+}
+
+func (v *valueFunc) Desc() *Desc {
+ return v.desc
+}
+
+func (v *valueFunc) Write(out *dto.Metric) error {
+ return populateMetric(v.valType, v.function(), v.labelPairs, out)
+}
+
+// NewConstMetric returns a metric with one fixed value that cannot be
+// changed. Users of this package will not have much use for it in regular
+// operations. However, when implementing custom Collectors, it is useful as a
+// throw-away metric that is generated on the fly to send it to Prometheus in
+// the Collect method. NewConstMetric returns an error if the length of
+// labelValues is not consistent with the variable labels in Desc.
+func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) (Metric, error) {
+ if len(desc.variableLabels) != len(labelValues) {
+ return nil, errInconsistentCardinality
+ }
+ return &constMetric{
+ desc: desc,
+ valType: valueType,
+ val: value,
+ labelPairs: makeLabelPairs(desc, labelValues),
+ }, nil
+}
+
+// MustNewConstMetric is a version of NewConstMetric that panics where
+// NewConstMetric would have returned an error.
+func MustNewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues ...string) Metric {
+ m, err := NewConstMetric(desc, valueType, value, labelValues...)
+ if err != nil {
+ panic(err)
+ }
+ return m
+}
+
+type constMetric struct {
+ desc *Desc
+ valType ValueType
+ val float64
+ labelPairs []*dto.LabelPair
+}
+
+func (m *constMetric) Desc() *Desc {
+ return m.desc
+}
+
+func (m *constMetric) Write(out *dto.Metric) error {
+ return populateMetric(m.valType, m.val, m.labelPairs, out)
+}
+
+func populateMetric(
+ t ValueType,
+ v float64,
+ labelPairs []*dto.LabelPair,
+ m *dto.Metric,
+) error {
+ m.Label = labelPairs
+ switch t {
+ case CounterValue:
+ m.Counter = &dto.Counter{Value: proto.Float64(v)}
+ case GaugeValue:
+ m.Gauge = &dto.Gauge{Value: proto.Float64(v)}
+ case UntypedValue:
+ m.Untyped = &dto.Untyped{Value: proto.Float64(v)}
+ default:
+ return fmt.Errorf("encountered unknown type %v", t)
+ }
+ return nil
+}
+
+func makeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
+ totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
+ if totalLen == 0 {
+ // Super fast path.
+ return nil
+ }
+ if len(desc.variableLabels) == 0 {
+ // Moderately fast path.
+ return desc.constLabelPairs
+ }
+ labelPairs := make([]*dto.LabelPair, 0, totalLen)
+ for i, n := range desc.variableLabels {
+ labelPairs = append(labelPairs, &dto.LabelPair{
+ Name: proto.String(n),
+ Value: proto.String(labelValues[i]),
+ })
+ }
+ for _, lp := range desc.constLabelPairs {
+ labelPairs = append(labelPairs, lp)
+ }
+ sort.Sort(LabelPairSorter(labelPairs))
+ return labelPairs
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go
new file mode 100644
index 000000000..a1f3bdf37
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec.go
@@ -0,0 +1,247 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "bytes"
+ "fmt"
+ "hash"
+ "sync"
+)
+
+// MetricVec is a Collector to bundle metrics of the same name that
+// differ in their label values. MetricVec is usually not used directly but as a
+// building block for implementations of vectors of a given metric
+// type. GaugeVec, CounterVec, SummaryVec, and UntypedVec are examples already
+// provided in this package.
+type MetricVec struct {
+ mtx sync.RWMutex // Protects not only children, but also hash and buf.
+ children map[uint64]Metric
+ desc *Desc
+
+ // hash is our own hash instance to avoid repeated allocations.
+ hash hash.Hash64
+ // buf is used to copy string contents into it for hashing,
+ // again to avoid allocations.
+ buf bytes.Buffer
+
+ newMetric func(labelValues ...string) Metric
+}
+
+// Describe implements Collector. The length of the returned slice
+// is always one.
+func (m *MetricVec) Describe(ch chan<- *Desc) {
+ ch <- m.desc
+}
+
+// Collect implements Collector.
+func (m *MetricVec) Collect(ch chan<- Metric) {
+ m.mtx.RLock()
+ defer m.mtx.RUnlock()
+
+ for _, metric := range m.children {
+ ch <- metric
+ }
+}
+
+// GetMetricWithLabelValues returns the Metric for the given slice of label
+// values (same order as the VariableLabels in Desc). If that combination of
+// label values is accessed for the first time, a new Metric is created.
+//
+// It is possible to call this method without using the returned Metric to only
+// create the new Metric but leave it at its start value (e.g. a Summary or
+// Histogram without any observations). See also the SummaryVec example.
+//
+// Keeping the Metric for later use is possible (and should be considered if
+// performance is critical), but keep in mind that Reset, DeleteLabelValues and
+// Delete can be used to delete the Metric from the MetricVec. In that case, the
+// Metric will still exist, but it will not be exported anymore, even if a
+// Metric with the same label values is created later. See also the CounterVec
+// example.
+//
+// An error is returned if the number of label values is not the same as the
+// number of VariableLabels in Desc.
+//
+// Note that for more than one label value, this method is prone to mistakes
+// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
+// an alternative to avoid that type of mistake. For higher label numbers, the
+// latter has a much more readable (albeit more verbose) syntax, but it comes
+// with a performance overhead (for creating and processing the Labels map).
+// See also the GaugeVec example.
+func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
+ m.mtx.Lock()
+ defer m.mtx.Unlock()
+
+ h, err := m.hashLabelValues(lvs)
+ if err != nil {
+ return nil, err
+ }
+ return m.getOrCreateMetric(h, lvs...), nil
+}
+
+// GetMetricWith returns the Metric for the given Labels map (the label names
+// must match those of the VariableLabels in Desc). If that label map is
+// accessed for the first time, a new Metric is created. Implications of
+// creating a Metric without using it and keeping the Metric for later use are
+// the same as for GetMetricWithLabelValues.
+//
+// An error is returned if the number and names of the Labels are inconsistent
+// with those of the VariableLabels in Desc.
+//
+// This method is used for the same purpose as
+// GetMetricWithLabelValues(...string). See there for pros and cons of the two
+// methods.
+func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
+ m.mtx.Lock()
+ defer m.mtx.Unlock()
+
+ h, err := m.hashLabels(labels)
+ if err != nil {
+ return nil, err
+ }
+ lvs := make([]string, len(labels))
+ for i, label := range m.desc.variableLabels {
+ lvs[i] = labels[label]
+ }
+ return m.getOrCreateMetric(h, lvs...), nil
+}
+
+// WithLabelValues works as GetMetricWithLabelValues, but panics if an error
+// occurs. The method allows neat syntax like:
+// httpReqs.WithLabelValues("404", "POST").Inc()
+func (m *MetricVec) WithLabelValues(lvs ...string) Metric {
+ metric, err := m.GetMetricWithLabelValues(lvs...)
+ if err != nil {
+ panic(err)
+ }
+ return metric
+}
+
+// With works as GetMetricWith, but panics if an error occurs. The method allows
+// neat syntax like:
+// httpReqs.With(Labels{"status":"404", "method":"POST"}).Inc()
+func (m *MetricVec) With(labels Labels) Metric {
+ metric, err := m.GetMetricWith(labels)
+ if err != nil {
+ panic(err)
+ }
+ return metric
+}
+
+// DeleteLabelValues removes the metric where the variable labels are the same
+// as those passed in as labels (same order as the VariableLabels in Desc). It
+// returns true if a metric was deleted.
+//
+// It is not an error if the number of label values is not the same as the
+// number of VariableLabels in Desc. However, such inconsistent label count can
+// never match an actual Metric, so the method will always return false in that
+// case.
+//
+// Note that for more than one label value, this method is prone to mistakes
+// caused by an incorrect order of arguments. Consider Delete(Labels) as an
+// alternative to avoid that type of mistake. For higher label numbers, the
+// latter has a much more readable (albeit more verbose) syntax, but it comes
+// with a performance overhead (for creating and processing the Labels map).
+// See also the CounterVec example.
+func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
+ m.mtx.Lock()
+ defer m.mtx.Unlock()
+
+ h, err := m.hashLabelValues(lvs)
+ if err != nil {
+ return false
+ }
+ if _, has := m.children[h]; !has {
+ return false
+ }
+ delete(m.children, h)
+ return true
+}
+
+// Delete deletes the metric where the variable labels are the same as those
+// passed in as labels. It returns true if a metric was deleted.
+//
+// It is not an error if the number and names of the Labels are inconsistent
+// with those of the VariableLabels in the Desc of the MetricVec. However, such
+// inconsistent Labels can never match an actual Metric, so the method will
+// always return false in that case.
+//
+// This method is used for the same purpose as DeleteLabelValues(...string). See
+// there for pros and cons of the two methods.
+func (m *MetricVec) Delete(labels Labels) bool {
+ m.mtx.Lock()
+ defer m.mtx.Unlock()
+
+ h, err := m.hashLabels(labels)
+ if err != nil {
+ return false
+ }
+ if _, has := m.children[h]; !has {
+ return false
+ }
+ delete(m.children, h)
+ return true
+}
+
+// Reset deletes all metrics in this vector.
+func (m *MetricVec) Reset() {
+ m.mtx.Lock()
+ defer m.mtx.Unlock()
+
+ for h := range m.children {
+ delete(m.children, h)
+ }
+}
+
+func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
+ if len(vals) != len(m.desc.variableLabels) {
+ return 0, errInconsistentCardinality
+ }
+ m.hash.Reset()
+ for _, val := range vals {
+ m.buf.Reset()
+ m.buf.WriteString(val)
+ m.hash.Write(m.buf.Bytes())
+ }
+ return m.hash.Sum64(), nil
+}
+
+func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
+ if len(labels) != len(m.desc.variableLabels) {
+ return 0, errInconsistentCardinality
+ }
+ m.hash.Reset()
+ for _, label := range m.desc.variableLabels {
+ val, ok := labels[label]
+ if !ok {
+ return 0, fmt.Errorf("label name %q missing in label map", label)
+ }
+ m.buf.Reset()
+ m.buf.WriteString(val)
+ m.hash.Write(m.buf.Bytes())
+ }
+ return m.hash.Sum64(), nil
+}
+
+func (m *MetricVec) getOrCreateMetric(hash uint64, labelValues ...string) Metric {
+ metric, ok := m.children[hash]
+ if !ok {
+ // Copy labelValues. Otherwise, they would be allocated even if we don't go
+ // down this code path.
+ copiedLabelValues := append(make([]string, 0, len(labelValues)), labelValues...)
+ metric = m.newMetric(copiedLabelValues...)
+ m.children[hash] = metric
+ }
+ return metric
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go
new file mode 100644
index 000000000..0e9431e65
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus/vec_test.go
@@ -0,0 +1,91 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prometheus
+
+import (
+ "hash/fnv"
+ "testing"
+)
+
+func TestDelete(t *testing.T) {
+ desc := NewDesc("test", "helpless", []string{"l1", "l2"}, nil)
+ vec := MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ return newValue(desc, UntypedValue, 0, lvs...)
+ },
+ }
+
+ if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+
+ vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+ if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), true; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+
+ vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+ if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), true; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if got, want := vec.Delete(Labels{"l2": "v2", "l1": "v1"}), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+
+ vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+ if got, want := vec.Delete(Labels{"l2": "v1", "l1": "v2"}), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if got, want := vec.Delete(Labels{"l1": "v1"}), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+}
+
+func TestDeleteLabelValues(t *testing.T) {
+ desc := NewDesc("test", "helpless", []string{"l1", "l2"}, nil)
+ vec := MetricVec{
+ children: map[uint64]Metric{},
+ desc: desc,
+ hash: fnv.New64a(),
+ newMetric: func(lvs ...string) Metric {
+ return newValue(desc, UntypedValue, 0, lvs...)
+ },
+ }
+
+ if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+
+ vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+ if got, want := vec.DeleteLabelValues("v1", "v2"), true; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+
+ vec.With(Labels{"l1": "v1", "l2": "v2"}).(Untyped).Set(42)
+ if got, want := vec.DeleteLabelValues("v2", "v1"), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if got, want := vec.DeleteLabelValues("v1"), false; got != want {
+ t.Errorf("got %v, want %v", got, want)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go/metrics.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go/metrics.pb.go
new file mode 100644
index 000000000..1b0802c34
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go/metrics.pb.go
@@ -0,0 +1,364 @@
+// Code generated by protoc-gen-go.
+// source: metrics.proto
+// DO NOT EDIT!
+
+/*
+Package io_prometheus_client is a generated protocol buffer package.
+
+It is generated from these files:
+ metrics.proto
+
+It has these top-level messages:
+ LabelPair
+ Gauge
+ Counter
+ Quantile
+ Summary
+ Untyped
+ Histogram
+ Bucket
+ Metric
+ MetricFamily
+*/
+package io_prometheus_client
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type MetricType int32
+
+const (
+ MetricType_COUNTER MetricType = 0
+ MetricType_GAUGE MetricType = 1
+ MetricType_SUMMARY MetricType = 2
+ MetricType_UNTYPED MetricType = 3
+ MetricType_HISTOGRAM MetricType = 4
+)
+
+var MetricType_name = map[int32]string{
+ 0: "COUNTER",
+ 1: "GAUGE",
+ 2: "SUMMARY",
+ 3: "UNTYPED",
+ 4: "HISTOGRAM",
+}
+var MetricType_value = map[string]int32{
+ "COUNTER": 0,
+ "GAUGE": 1,
+ "SUMMARY": 2,
+ "UNTYPED": 3,
+ "HISTOGRAM": 4,
+}
+
+func (x MetricType) Enum() *MetricType {
+ p := new(MetricType)
+ *p = x
+ return p
+}
+func (x MetricType) String() string {
+ return proto.EnumName(MetricType_name, int32(x))
+}
+func (x *MetricType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MetricType_value, data, "MetricType")
+ if err != nil {
+ return err
+ }
+ *x = MetricType(value)
+ return nil
+}
+
+type LabelPair struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ Value *string `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *LabelPair) Reset() { *m = LabelPair{} }
+func (m *LabelPair) String() string { return proto.CompactTextString(m) }
+func (*LabelPair) ProtoMessage() {}
+
+func (m *LabelPair) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *LabelPair) GetValue() string {
+ if m != nil && m.Value != nil {
+ return *m.Value
+ }
+ return ""
+}
+
+type Gauge struct {
+ Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Gauge) Reset() { *m = Gauge{} }
+func (m *Gauge) String() string { return proto.CompactTextString(m) }
+func (*Gauge) ProtoMessage() {}
+
+func (m *Gauge) GetValue() float64 {
+ if m != nil && m.Value != nil {
+ return *m.Value
+ }
+ return 0
+}
+
+type Counter struct {
+ Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Counter) Reset() { *m = Counter{} }
+func (m *Counter) String() string { return proto.CompactTextString(m) }
+func (*Counter) ProtoMessage() {}
+
+func (m *Counter) GetValue() float64 {
+ if m != nil && m.Value != nil {
+ return *m.Value
+ }
+ return 0
+}
+
+type Quantile struct {
+ Quantile *float64 `protobuf:"fixed64,1,opt,name=quantile" json:"quantile,omitempty"`
+ Value *float64 `protobuf:"fixed64,2,opt,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Quantile) Reset() { *m = Quantile{} }
+func (m *Quantile) String() string { return proto.CompactTextString(m) }
+func (*Quantile) ProtoMessage() {}
+
+func (m *Quantile) GetQuantile() float64 {
+ if m != nil && m.Quantile != nil {
+ return *m.Quantile
+ }
+ return 0
+}
+
+func (m *Quantile) GetValue() float64 {
+ if m != nil && m.Value != nil {
+ return *m.Value
+ }
+ return 0
+}
+
+type Summary struct {
+ SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count" json:"sample_count,omitempty"`
+ SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum" json:"sample_sum,omitempty"`
+ Quantile []*Quantile `protobuf:"bytes,3,rep,name=quantile" json:"quantile,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Summary) Reset() { *m = Summary{} }
+func (m *Summary) String() string { return proto.CompactTextString(m) }
+func (*Summary) ProtoMessage() {}
+
+func (m *Summary) GetSampleCount() uint64 {
+ if m != nil && m.SampleCount != nil {
+ return *m.SampleCount
+ }
+ return 0
+}
+
+func (m *Summary) GetSampleSum() float64 {
+ if m != nil && m.SampleSum != nil {
+ return *m.SampleSum
+ }
+ return 0
+}
+
+func (m *Summary) GetQuantile() []*Quantile {
+ if m != nil {
+ return m.Quantile
+ }
+ return nil
+}
+
+type Untyped struct {
+ Value *float64 `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Untyped) Reset() { *m = Untyped{} }
+func (m *Untyped) String() string { return proto.CompactTextString(m) }
+func (*Untyped) ProtoMessage() {}
+
+func (m *Untyped) GetValue() float64 {
+ if m != nil && m.Value != nil {
+ return *m.Value
+ }
+ return 0
+}
+
+type Histogram struct {
+ SampleCount *uint64 `protobuf:"varint,1,opt,name=sample_count" json:"sample_count,omitempty"`
+ SampleSum *float64 `protobuf:"fixed64,2,opt,name=sample_sum" json:"sample_sum,omitempty"`
+ Bucket []*Bucket `protobuf:"bytes,3,rep,name=bucket" json:"bucket,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Histogram) Reset() { *m = Histogram{} }
+func (m *Histogram) String() string { return proto.CompactTextString(m) }
+func (*Histogram) ProtoMessage() {}
+
+func (m *Histogram) GetSampleCount() uint64 {
+ if m != nil && m.SampleCount != nil {
+ return *m.SampleCount
+ }
+ return 0
+}
+
+func (m *Histogram) GetSampleSum() float64 {
+ if m != nil && m.SampleSum != nil {
+ return *m.SampleSum
+ }
+ return 0
+}
+
+func (m *Histogram) GetBucket() []*Bucket {
+ if m != nil {
+ return m.Bucket
+ }
+ return nil
+}
+
+type Bucket struct {
+ CumulativeCount *uint64 `protobuf:"varint,1,opt,name=cumulative_count" json:"cumulative_count,omitempty"`
+ UpperBound *float64 `protobuf:"fixed64,2,opt,name=upper_bound" json:"upper_bound,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Bucket) Reset() { *m = Bucket{} }
+func (m *Bucket) String() string { return proto.CompactTextString(m) }
+func (*Bucket) ProtoMessage() {}
+
+func (m *Bucket) GetCumulativeCount() uint64 {
+ if m != nil && m.CumulativeCount != nil {
+ return *m.CumulativeCount
+ }
+ return 0
+}
+
+func (m *Bucket) GetUpperBound() float64 {
+ if m != nil && m.UpperBound != nil {
+ return *m.UpperBound
+ }
+ return 0
+}
+
+type Metric struct {
+ Label []*LabelPair `protobuf:"bytes,1,rep,name=label" json:"label,omitempty"`
+ Gauge *Gauge `protobuf:"bytes,2,opt,name=gauge" json:"gauge,omitempty"`
+ Counter *Counter `protobuf:"bytes,3,opt,name=counter" json:"counter,omitempty"`
+ Summary *Summary `protobuf:"bytes,4,opt,name=summary" json:"summary,omitempty"`
+ Untyped *Untyped `protobuf:"bytes,5,opt,name=untyped" json:"untyped,omitempty"`
+ Histogram *Histogram `protobuf:"bytes,7,opt,name=histogram" json:"histogram,omitempty"`
+ TimestampMs *int64 `protobuf:"varint,6,opt,name=timestamp_ms" json:"timestamp_ms,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Metric) Reset() { *m = Metric{} }
+func (m *Metric) String() string { return proto.CompactTextString(m) }
+func (*Metric) ProtoMessage() {}
+
+func (m *Metric) GetLabel() []*LabelPair {
+ if m != nil {
+ return m.Label
+ }
+ return nil
+}
+
+func (m *Metric) GetGauge() *Gauge {
+ if m != nil {
+ return m.Gauge
+ }
+ return nil
+}
+
+func (m *Metric) GetCounter() *Counter {
+ if m != nil {
+ return m.Counter
+ }
+ return nil
+}
+
+func (m *Metric) GetSummary() *Summary {
+ if m != nil {
+ return m.Summary
+ }
+ return nil
+}
+
+func (m *Metric) GetUntyped() *Untyped {
+ if m != nil {
+ return m.Untyped
+ }
+ return nil
+}
+
+func (m *Metric) GetHistogram() *Histogram {
+ if m != nil {
+ return m.Histogram
+ }
+ return nil
+}
+
+func (m *Metric) GetTimestampMs() int64 {
+ if m != nil && m.TimestampMs != nil {
+ return *m.TimestampMs
+ }
+ return 0
+}
+
+type MetricFamily struct {
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ Help *string `protobuf:"bytes,2,opt,name=help" json:"help,omitempty"`
+ Type *MetricType `protobuf:"varint,3,opt,name=type,enum=io.prometheus.client.MetricType" json:"type,omitempty"`
+ Metric []*Metric `protobuf:"bytes,4,rep,name=metric" json:"metric,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MetricFamily) Reset() { *m = MetricFamily{} }
+func (m *MetricFamily) String() string { return proto.CompactTextString(m) }
+func (*MetricFamily) ProtoMessage() {}
+
+func (m *MetricFamily) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *MetricFamily) GetHelp() string {
+ if m != nil && m.Help != nil {
+ return *m.Help
+ }
+ return ""
+}
+
+func (m *MetricFamily) GetType() MetricType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return MetricType_COUNTER
+}
+
+func (m *MetricFamily) GetMetric() []*Metric {
+ if m != nil {
+ return m.Metric
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("io.prometheus.client.MetricType", MetricType_name, MetricType_value)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go
new file mode 100644
index 000000000..2437d515d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/bench_test.go
@@ -0,0 +1,170 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "bytes"
+ "compress/gzip"
+ "io"
+ "io/ioutil"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+var parser TextParser
+
+// Benchmarks to show how much penalty text format parsing actually inflicts.
+//
+// Example results on Linux 3.13.0, Intel(R) Core(TM) i7-4700MQ CPU @ 2.40GHz, go1.4.
+//
+// BenchmarkParseText 1000 1188535 ns/op 205085 B/op 6135 allocs/op
+// BenchmarkParseTextGzip 1000 1376567 ns/op 246224 B/op 6151 allocs/op
+// BenchmarkParseProto 10000 172790 ns/op 52258 B/op 1160 allocs/op
+// BenchmarkParseProtoGzip 5000 324021 ns/op 94931 B/op 1211 allocs/op
+// BenchmarkParseProtoMap 10000 187946 ns/op 58714 B/op 1203 allocs/op
+//
+// CONCLUSION: The overhead for the map is negligible. Text format needs ~5x more allocations.
+// Without compression, it needs ~7x longer, but with compression (the more relevant scenario),
+// the difference becomes less relevant, only ~4x.
+//
+// The test data contains 248 samples.
+//
+// BenchmarkProcessor002ParseOnly in the extraction package is not quite
+// comparable to the benchmarks here, but it gives an idea: JSON parsing is even
+// slower than text parsing and needs a comparable amount of allocs.
+
+// BenchmarkParseText benchmarks the parsing of a text-format scrape into metric
+// family DTOs.
+func BenchmarkParseText(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("testdata/text")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ if _, err := parser.TextToMetricFamilies(bytes.NewReader(data)); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// BenchmarkParseTextGzip benchmarks the parsing of a gzipped text-format scrape
+// into metric family DTOs.
+func BenchmarkParseTextGzip(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("testdata/text.gz")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ in, err := gzip.NewReader(bytes.NewReader(data))
+ if err != nil {
+ b.Fatal(err)
+ }
+ if _, err := parser.TextToMetricFamilies(in); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
+
+// BenchmarkParseProto benchmarks the parsing of a protobuf-format scrape into
+// metric family DTOs. Note that this does not build a map of metric families
+// (as the text version does), because it is not required for Prometheus
+// ingestion either. (However, it is required for the text-format parsing, as
+// the metric family might be sprinkled all over the text, while the
+// protobuf-format guarantees bundling at one place.)
+func BenchmarkParseProto(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("testdata/protobuf")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ family := &dto.MetricFamily{}
+ in := bytes.NewReader(data)
+ for {
+ family.Reset()
+ if _, err := pbutil.ReadDelimited(in, family); err != nil {
+ if err == io.EOF {
+ break
+ }
+ b.Fatal(err)
+ }
+ }
+ }
+}
+
+// BenchmarkParseProtoGzip is like BenchmarkParseProto above, but parses gzipped
+// protobuf format.
+func BenchmarkParseProtoGzip(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("testdata/protobuf.gz")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ family := &dto.MetricFamily{}
+ in, err := gzip.NewReader(bytes.NewReader(data))
+ if err != nil {
+ b.Fatal(err)
+ }
+ for {
+ family.Reset()
+ if _, err := pbutil.ReadDelimited(in, family); err != nil {
+ if err == io.EOF {
+ break
+ }
+ b.Fatal(err)
+ }
+ }
+ }
+}
+
+// BenchmarkParseProtoMap is like BenchmarkParseProto but DOES put the parsed
+// metric family DTOs into a map. This is not happening during Prometheus
+// ingestion. It is just here to measure the overhead of that map creation and
+// separate it from the overhead of the text format parsing.
+func BenchmarkParseProtoMap(b *testing.B) {
+ b.StopTimer()
+ data, err := ioutil.ReadFile("testdata/protobuf")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ families := map[string]*dto.MetricFamily{}
+ in := bytes.NewReader(data)
+ for {
+ family := &dto.MetricFamily{}
+ if _, err := pbutil.ReadDelimited(in, family); err != nil {
+ if err == io.EOF {
+ break
+ }
+ b.Fatal(err)
+ }
+ families[family.GetName()] = family
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode.go
new file mode 100644
index 000000000..47c4ef88d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode.go
@@ -0,0 +1,410 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "mime"
+ "net/http"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model"
+)
+
+// Decoder types decode an input stream into metric families.
+type Decoder interface {
+ Decode(*dto.MetricFamily) error
+}
+
+type DecodeOptions struct {
+ // Timestamp is added to each value from the stream that has no explicit timestamp set.
+ Timestamp model.Time
+}
+
+// ResponseFormat extracts the correct format from a HTTP response header.
+// If no matching format can be found FormatUnknown is returned.
+func ResponseFormat(h http.Header) Format {
+ ct := h.Get(hdrContentType)
+
+ mediatype, params, err := mime.ParseMediaType(ct)
+ if err != nil {
+ return FmtUnknown
+ }
+
+ const (
+ textType = "text/plain"
+ jsonType = "application/json"
+ )
+
+ switch mediatype {
+ case ProtoType:
+ if p, ok := params["proto"]; ok && p != ProtoProtocol {
+ return FmtUnknown
+ }
+ if e, ok := params["encoding"]; ok && e != "delimited" {
+ return FmtUnknown
+ }
+ return FmtProtoDelim
+
+ case textType:
+ if v, ok := params["version"]; ok && v != TextVersion {
+ return FmtUnknown
+ }
+ return FmtText
+
+ case jsonType:
+ var prometheusAPIVersion string
+
+ if params["schema"] == "prometheus/telemetry" && params["version"] != "" {
+ prometheusAPIVersion = params["version"]
+ } else {
+ prometheusAPIVersion = h.Get("X-Prometheus-API-Version")
+ }
+
+ switch prometheusAPIVersion {
+ case "0.0.2", "":
+ return fmtJSON2
+ default:
+ return FmtUnknown
+ }
+ }
+
+ return FmtUnknown
+}
+
+// NewDecoder returns a new decoder based on the given input format.
+// If the input format does not imply otherwise, a text format decoder is returned.
+func NewDecoder(r io.Reader, format Format) Decoder {
+ switch format {
+ case FmtProtoDelim:
+ return &protoDecoder{r: r}
+ case fmtJSON2:
+ return newJSON2Decoder(r)
+ }
+ return &textDecoder{r: r}
+}
+
+// protoDecoder implements the Decoder interface for protocol buffers.
+type protoDecoder struct {
+ r io.Reader
+}
+
+// Decode implements the Decoder interface.
+func (d *protoDecoder) Decode(v *dto.MetricFamily) error {
+ _, err := pbutil.ReadDelimited(d.r, v)
+ return err
+}
+
+// textDecoder implements the Decoder interface for the text protcol.
+type textDecoder struct {
+ r io.Reader
+ p TextParser
+ fams []*dto.MetricFamily
+}
+
+// Decode implements the Decoder interface.
+func (d *textDecoder) Decode(v *dto.MetricFamily) error {
+ // TODO(fabxc): Wrap this as a line reader to make streaming safer.
+ if len(d.fams) == 0 {
+ // No cached metric families, read everything and parse metrics.
+ fams, err := d.p.TextToMetricFamilies(d.r)
+ if err != nil {
+ return err
+ }
+ if len(fams) == 0 {
+ return io.EOF
+ }
+ d.fams = make([]*dto.MetricFamily, 0, len(fams))
+ for _, f := range fams {
+ d.fams = append(d.fams, f)
+ }
+ }
+
+ *v = *d.fams[0]
+ d.fams = d.fams[1:]
+
+ return nil
+}
+
+type SampleDecoder struct {
+ Dec Decoder
+ Opts *DecodeOptions
+
+ f dto.MetricFamily
+}
+
+func (sd *SampleDecoder) Decode(s *model.Vector) error {
+ if err := sd.Dec.Decode(&sd.f); err != nil {
+ return err
+ }
+ *s = extractSamples(&sd.f, sd.Opts)
+ return nil
+}
+
+// Extract samples builds a slice of samples from the provided metric families.
+func ExtractSamples(o *DecodeOptions, fams ...*dto.MetricFamily) model.Vector {
+ var all model.Vector
+ for _, f := range fams {
+ all = append(all, extractSamples(f, o)...)
+ }
+ return all
+}
+
+func extractSamples(f *dto.MetricFamily, o *DecodeOptions) model.Vector {
+ switch f.GetType() {
+ case dto.MetricType_COUNTER:
+ return extractCounter(o, f)
+ case dto.MetricType_GAUGE:
+ return extractGauge(o, f)
+ case dto.MetricType_SUMMARY:
+ return extractSummary(o, f)
+ case dto.MetricType_UNTYPED:
+ return extractUntyped(o, f)
+ case dto.MetricType_HISTOGRAM:
+ return extractHistogram(o, f)
+ }
+ panic("expfmt.extractSamples: unknown metric family type")
+}
+
+func extractCounter(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
+ samples := make(model.Vector, 0, len(f.Metric))
+
+ for _, m := range f.Metric {
+ if m.Counter == nil {
+ continue
+ }
+
+ lset := make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
+
+ smpl := &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Counter.GetValue()),
+ }
+
+ if m.TimestampMs != nil {
+ smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
+ } else {
+ smpl.Timestamp = o.Timestamp
+ }
+
+ samples = append(samples, smpl)
+ }
+
+ return samples
+}
+
+func extractGauge(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
+ samples := make(model.Vector, 0, len(f.Metric))
+
+ for _, m := range f.Metric {
+ if m.Gauge == nil {
+ continue
+ }
+
+ lset := make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
+
+ smpl := &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Gauge.GetValue()),
+ }
+
+ if m.TimestampMs != nil {
+ smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
+ } else {
+ smpl.Timestamp = o.Timestamp
+ }
+
+ samples = append(samples, smpl)
+ }
+
+ return samples
+}
+
+func extractUntyped(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
+ samples := make(model.Vector, 0, len(f.Metric))
+
+ for _, m := range f.Metric {
+ if m.Untyped == nil {
+ continue
+ }
+
+ lset := make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
+
+ smpl := &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Untyped.GetValue()),
+ }
+
+ if m.TimestampMs != nil {
+ smpl.Timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
+ } else {
+ smpl.Timestamp = o.Timestamp
+ }
+
+ samples = append(samples, smpl)
+ }
+
+ return samples
+}
+
+func extractSummary(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
+ samples := make(model.Vector, 0, len(f.Metric))
+
+ for _, m := range f.Metric {
+ if m.Summary == nil {
+ continue
+ }
+
+ timestamp := o.Timestamp
+ if m.TimestampMs != nil {
+ timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
+ }
+
+ for _, q := range m.Summary.Quantile {
+ lset := make(model.LabelSet, len(m.Label)+2)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ // BUG(matt): Update other names to "quantile".
+ lset[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile()))
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName())
+
+ samples = append(samples, &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(q.GetValue()),
+ Timestamp: timestamp,
+ })
+ }
+
+ lset := make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
+
+ samples = append(samples, &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Summary.GetSampleSum()),
+ Timestamp: timestamp,
+ })
+
+ lset = make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
+
+ samples = append(samples, &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Summary.GetSampleCount()),
+ Timestamp: timestamp,
+ })
+ }
+
+ return samples
+}
+
+func extractHistogram(o *DecodeOptions, f *dto.MetricFamily) model.Vector {
+ samples := make(model.Vector, 0, len(f.Metric))
+
+ for _, m := range f.Metric {
+ if m.Histogram == nil {
+ continue
+ }
+
+ timestamp := o.Timestamp
+ if m.TimestampMs != nil {
+ timestamp = model.TimeFromUnixNano(*m.TimestampMs * 1000000)
+ }
+
+ infSeen := false
+
+ for _, q := range m.Histogram.Bucket {
+ lset := make(model.LabelSet, len(m.Label)+2)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound()))
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
+
+ if math.IsInf(q.GetUpperBound(), +1) {
+ infSeen = true
+ }
+
+ samples = append(samples, &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(q.GetCumulativeCount()),
+ Timestamp: timestamp,
+ })
+ }
+
+ lset := make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum")
+
+ samples = append(samples, &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Histogram.GetSampleSum()),
+ Timestamp: timestamp,
+ })
+
+ lset = make(model.LabelSet, len(m.Label)+1)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count")
+
+ count := &model.Sample{
+ Metric: model.Metric(lset),
+ Value: model.SampleValue(m.Histogram.GetSampleCount()),
+ Timestamp: timestamp,
+ }
+ samples = append(samples, count)
+
+ if !infSeen {
+ // Append an infinity bucket sample.
+ lset := make(model.LabelSet, len(m.Label)+2)
+ for _, p := range m.Label {
+ lset[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue())
+ }
+ lset[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf")
+ lset[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket")
+
+ samples = append(samples, &model.Sample{
+ Metric: model.Metric(lset),
+ Value: count.Value,
+ Timestamp: timestamp,
+ })
+ }
+ }
+
+ return samples
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go
new file mode 100644
index 000000000..23ba36138
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/decode_test.go
@@ -0,0 +1,356 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "io"
+ "net/http"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model"
+)
+
+func TestTextDecoder(t *testing.T) {
+ var (
+ ts = model.Now()
+ in = `
+# Only a quite simple scenario with two metric families.
+# More complicated tests of the parser itself can be found in the text package.
+# TYPE mf2 counter
+mf2 3
+mf1{label="value1"} -3.14 123456
+mf1{label="value2"} 42
+mf2 4
+`
+ out = model.Vector{
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "mf1",
+ "label": "value1",
+ },
+ Value: -3.14,
+ Timestamp: 123456,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "mf1",
+ "label": "value2",
+ },
+ Value: 42,
+ Timestamp: ts,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "mf2",
+ },
+ Value: 3,
+ Timestamp: ts,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "mf2",
+ },
+ Value: 4,
+ Timestamp: ts,
+ },
+ }
+ )
+
+ dec := &SampleDecoder{
+ Dec: &textDecoder{r: strings.NewReader(in)},
+ Opts: &DecodeOptions{
+ Timestamp: ts,
+ },
+ }
+ var all model.Vector
+ for {
+ var smpls model.Vector
+ err := dec.Decode(&smpls)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ all = append(all, smpls...)
+ }
+ sort.Sort(all)
+ sort.Sort(out)
+ if !reflect.DeepEqual(all, out) {
+ t.Fatalf("output does not match")
+ }
+}
+
+func TestProtoDecoder(t *testing.T) {
+
+ var testTime = model.Now()
+
+ scenarios := []struct {
+ in string
+ expected model.Vector
+ }{
+ {
+ in: "",
+ },
+ {
+ in: "\x8f\x01\n\rrequest_count\x12\x12Number of requests\x18\x00\"0\n#\n\x0fsome_label_name\x12\x10some_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00E\xc0\"6\n)\n\x12another_label_name\x12\x13another_label_value\x1a\t\t\x00\x00\x00\x00\x00\x00U@",
+ expected: model.Vector{
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count",
+ "some_label_name": "some_label_value",
+ },
+ Value: -42,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count",
+ "another_label_name": "another_label_value",
+ },
+ Value: 84,
+ Timestamp: testTime,
+ },
+ },
+ },
+ {
+ in: "\xb9\x01\n\rrequest_count\x12\x12Number of requests\x18\x02\"O\n#\n\x0fsome_label_name\x12\x10some_label_value\"(\x1a\x12\t\xaeG\xe1z\x14\xae\xef?\x11\x00\x00\x00\x00\x00\x00E\xc0\x1a\x12\t+\x87\x16\xd9\xce\xf7\xef?\x11\x00\x00\x00\x00\x00\x00U\xc0\"A\n)\n\x12another_label_name\x12\x13another_label_value\"\x14\x1a\x12\t\x00\x00\x00\x00\x00\x00\xe0?\x11\x00\x00\x00\x00\x00\x00$@",
+ expected: model.Vector{
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count_count",
+ "some_label_name": "some_label_value",
+ },
+ Value: 0,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count_sum",
+ "some_label_name": "some_label_value",
+ },
+ Value: 0,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count",
+ "some_label_name": "some_label_value",
+ "quantile": "0.99",
+ },
+ Value: -42,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count",
+ "some_label_name": "some_label_value",
+ "quantile": "0.999",
+ },
+ Value: -84,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count_count",
+ "another_label_name": "another_label_value",
+ },
+ Value: 0,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count_sum",
+ "another_label_name": "another_label_value",
+ },
+ Value: 0,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count",
+ "another_label_name": "another_label_value",
+ "quantile": "0.5",
+ },
+ Value: 10,
+ Timestamp: testTime,
+ },
+ },
+ },
+ {
+ in: "\x8d\x01\n\x1drequest_duration_microseconds\x12\x15The response latency.\x18\x04\"S:Q\b\x85\x15\x11\xcd\xcc\xccL\x8f\xcb:A\x1a\v\b{\x11\x00\x00\x00\x00\x00\x00Y@\x1a\f\b\x9c\x03\x11\x00\x00\x00\x00\x00\x00^@\x1a\f\b\xd0\x04\x11\x00\x00\x00\x00\x00\x00b@\x1a\f\b\xf4\v\x11\x9a\x99\x99\x99\x99\x99e@\x1a\f\b\x85\x15\x11\x00\x00\x00\x00\x00\x00\xf0\u007f",
+ expected: model.Vector{
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_bucket",
+ "le": "100",
+ },
+ Value: 123,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_bucket",
+ "le": "120",
+ },
+ Value: 412,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_bucket",
+ "le": "144",
+ },
+ Value: 592,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_bucket",
+ "le": "172.8",
+ },
+ Value: 1524,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_bucket",
+ "le": "+Inf",
+ },
+ Value: 2693,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_sum",
+ },
+ Value: 1756047.3,
+ Timestamp: testTime,
+ },
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_duration_microseconds_count",
+ },
+ Value: 2693,
+ Timestamp: testTime,
+ },
+ },
+ },
+ {
+ // The metric type is unset in this protobuf, which needs to be handled
+ // correctly by the decoder.
+ in: "\x1c\n\rrequest_count\"\v\x1a\t\t\x00\x00\x00\x00\x00\x00\xf0?",
+ expected: model.Vector{
+ &model.Sample{
+ Metric: model.Metric{
+ model.MetricNameLabel: "request_count",
+ },
+ Value: 1,
+ Timestamp: testTime,
+ },
+ },
+ },
+ }
+
+ for i, scenario := range scenarios {
+ dec := &SampleDecoder{
+ Dec: &protoDecoder{r: strings.NewReader(scenario.in)},
+ Opts: &DecodeOptions{
+ Timestamp: testTime,
+ },
+ }
+
+ var all model.Vector
+ for {
+ var smpls model.Vector
+ err := dec.Decode(&smpls)
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ all = append(all, smpls...)
+ }
+ sort.Sort(all)
+ sort.Sort(scenario.expected)
+ if !reflect.DeepEqual(all, scenario.expected) {
+ t.Fatalf("%d. output does not match, want: %#v, got %#v", i, scenario.expected, all)
+ }
+ }
+}
+
+func testDiscriminatorHTTPHeader(t testing.TB) {
+ var scenarios = []struct {
+ input map[string]string
+ output Format
+ err error
+ }{
+ {
+ input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="delimited"`},
+ output: FmtProtoDelim,
+ },
+ {
+ input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="illegal"; encoding="delimited"`},
+ output: FmtUnknown,
+ },
+ {
+ input: map[string]string{"Content-Type": `application/vnd.google.protobuf; proto="io.prometheus.client.MetricFamily"; encoding="illegal"`},
+ output: FmtUnknown,
+ },
+ {
+ input: map[string]string{"Content-Type": `text/plain; version=0.0.4`},
+ output: FmtText,
+ },
+ {
+ input: map[string]string{"Content-Type": `text/plain`},
+ output: FmtText,
+ },
+ {
+ input: map[string]string{"Content-Type": `text/plain; version=0.0.3`},
+ output: FmtUnknown,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ var header http.Header
+
+ if len(scenario.input) > 0 {
+ header = http.Header{}
+ }
+
+ for key, value := range scenario.input {
+ header.Add(key, value)
+ }
+
+ actual := ResponseFormat(header)
+
+ if scenario.output != actual {
+ t.Errorf("%d. expected %s, got %s", i, scenario.output, actual)
+ }
+ }
+}
+
+func TestDiscriminatorHTTPHeader(t *testing.T) {
+ testDiscriminatorHTTPHeader(t)
+}
+
+func BenchmarkDiscriminatorHTTPHeader(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testDiscriminatorHTTPHeader(b)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/encode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/encode.go
new file mode 100644
index 000000000..9154048da
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/encode.go
@@ -0,0 +1,87 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/bitbucket.org/ww/goautoneg"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/matttproud/golang_protobuf_extensions/pbutil"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+// Encoder types encode metric families into an underlying wire protocol.
+type Encoder interface {
+ Encode(*dto.MetricFamily) error
+}
+
+type encoder func(*dto.MetricFamily) error
+
+func (e encoder) Encode(v *dto.MetricFamily) error {
+ return e(v)
+}
+
+// Negotiate returns the Content-Type based on the given Accept header.
+// If no appropriate accepted type is found, FmtText is returned.
+func Negotiate(h http.Header) Format {
+ for _, ac := range goautoneg.ParseAccept(h.Get(hdrAccept)) {
+ // Check for protocol buffer
+ if ac.Type+"/"+ac.SubType == ProtoType && ac.Params["proto"] == ProtoProtocol {
+ switch ac.Params["encoding"] {
+ case "delimited":
+ return FmtProtoDelim
+ case "text":
+ return FmtProtoText
+ case "compact-text":
+ return FmtProtoCompact
+ }
+ }
+ // Check for text format.
+ ver := ac.Params["version"]
+ if ac.Type == "text" && ac.SubType == "plain" && (ver == TextVersion || ver == "") {
+ return FmtText
+ }
+ }
+ return FmtText
+}
+
+// NewEncoder returns a new encoder based on content type negotiation.
+func NewEncoder(w io.Writer, format Format) Encoder {
+ switch format {
+ case FmtProtoDelim:
+ return encoder(func(v *dto.MetricFamily) error {
+ _, err := pbutil.WriteDelimited(w, v)
+ return err
+ })
+ case FmtProtoCompact:
+ return encoder(func(v *dto.MetricFamily) error {
+ _, err := fmt.Fprintln(w, v.String())
+ return err
+ })
+ case FmtProtoText:
+ return encoder(func(v *dto.MetricFamily) error {
+ _, err := fmt.Fprintln(w, proto.MarshalTextString(v))
+ return err
+ })
+ case FmtText:
+ return encoder(func(v *dto.MetricFamily) error {
+ _, err := MetricFamilyToText(w, v)
+ return err
+ })
+ }
+ panic("expfmt.NewEncoder: unknown format")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/expfmt.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/expfmt.go
new file mode 100644
index 000000000..366fbde98
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/expfmt.go
@@ -0,0 +1,40 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// A package for reading and writing Prometheus metrics.
+package expfmt
+
+type Format string
+
+const (
+ TextVersion = "0.0.4"
+
+ ProtoType = `application/vnd.google.protobuf`
+ ProtoProtocol = `io.prometheus.client.MetricFamily`
+ ProtoFmt = ProtoType + "; proto=" + ProtoProtocol + ";"
+
+ // The Content-Type values for the different wire protocols.
+ FmtUnknown Format = ``
+ FmtText Format = `text/plain; version=` + TextVersion
+ FmtProtoDelim Format = ProtoFmt + ` encoding=delimited`
+ FmtProtoText Format = ProtoFmt + ` encoding=text`
+ FmtProtoCompact Format = ProtoFmt + ` encoding=compact-text`
+
+ // fmtJSON2 is hidden as it is deprecated.
+ fmtJSON2 Format = `application/json; version=0.0.2`
+)
+
+const (
+ hdrContentType = "Content-Type"
+ hdrAccept = "Accept"
+)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz.go
new file mode 100644
index 000000000..14f920146
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz.go
@@ -0,0 +1,36 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Build only when actually fuzzing
+// +build gofuzz
+
+package expfmt
+
+import "bytes"
+
+// Fuzz text metric parser with with github.com/dvyukov/go-fuzz:
+//
+// go-fuzz-build github.com/prometheus/client_golang/text
+// go-fuzz -bin text-fuzz.zip -workdir fuzz
+//
+// Further input samples should go in the folder fuzz/corpus.
+func Fuzz(in []byte) int {
+ parser := TextParser{}
+ _, err := parser.TextToMetricFamilies(bytes.NewReader(in))
+
+ if err != nil {
+ return 0
+ }
+
+ return 1
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0
new file mode 100644
index 000000000..139597f9c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_0
@@ -0,0 +1,2 @@
+
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1
new file mode 100644
index 000000000..2ae870679
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_1
@@ -0,0 +1,6 @@
+
+minimal_metric 1.234
+another_metric -3e3 103948
+# Even that:
+no_labels{} 3
+# HELP line for non-existing metric will be ignored.
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2
new file mode 100644
index 000000000..5c351db36
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_2
@@ -0,0 +1,12 @@
+
+# A normal comment.
+#
+# TYPE name counter
+name{labelname="val1",basename="basevalue"} NaN
+name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890
+# HELP name two-line\n doc str\\ing
+
+ # HELP name2 doc str"ing 2
+ # TYPE name2 gauge
+name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321
+name2{ labelname = "val1" , }-Inf
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3
new file mode 100644
index 000000000..0b3c345aa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_3
@@ -0,0 +1,22 @@
+
+# TYPE my_summary summary
+my_summary{n1="val1",quantile="0.5"} 110
+decoy -1 -2
+my_summary{n1="val1",quantile="0.9"} 140 1
+my_summary_count{n1="val1"} 42
+# Latest timestamp wins in case of a summary.
+my_summary_sum{n1="val1"} 4711 2
+fake_sum{n1="val1"} 2001
+# TYPE another_summary summary
+another_summary_count{n2="val2",n1="val1"} 20
+my_summary_count{n2="val2",n1="val1"} 5 5
+another_summary{n1="val1",n2="val2",quantile=".3"} -1.2
+my_summary_sum{n1="val2"} 08 15
+my_summary{n1="val3", quantile="0.2"} 4711
+ my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN
+# some
+# funny comments
+# HELP
+# HELP
+# HELP my_summary
+# HELP my_summary
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4
new file mode 100644
index 000000000..bde0a387a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_4
@@ -0,0 +1,10 @@
+
+# HELP request_duration_microseconds The response latency.
+# TYPE request_duration_microseconds histogram
+request_duration_microseconds_bucket{le="100"} 123
+request_duration_microseconds_bucket{le="120"} 412
+request_duration_microseconds_bucket{le="144"} 592
+request_duration_microseconds_bucket{le="172.8"} 1524
+request_duration_microseconds_bucket{le="+Inf"} 2693
+request_duration_microseconds_sum 1.7560473e+06
+request_duration_microseconds_count 2693
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0
new file mode 100644
index 000000000..4c67f9a19
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_0
@@ -0,0 +1 @@
+bla 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1
new file mode 100644
index 000000000..b853478ee
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_1
@@ -0,0 +1 @@
+metric{label="\t"} 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10
new file mode 100644
index 000000000..b5fe5f5a6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_10
@@ -0,0 +1 @@
+metric{label="bla"} 3.14 2 3
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11
new file mode 100644
index 000000000..57c7fbc0b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_11
@@ -0,0 +1 @@
+metric{label="bla"} blubb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12
new file mode 100644
index 000000000..0a9df79a1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_12
@@ -0,0 +1,3 @@
+
+# HELP metric one
+# HELP metric two
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13
new file mode 100644
index 000000000..5bc742781
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_13
@@ -0,0 +1,3 @@
+
+# TYPE metric counter
+# TYPE metric untyped
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14
new file mode 100644
index 000000000..a9a24265b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_14
@@ -0,0 +1,3 @@
+
+metric 4.12
+# TYPE metric counter
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15
new file mode 100644
index 000000000..7e95ca8f4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_15
@@ -0,0 +1,2 @@
+
+# TYPE metric bla
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16
new file mode 100644
index 000000000..7825f8887
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_16
@@ -0,0 +1,2 @@
+
+# TYPE met-ric
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17
new file mode 100644
index 000000000..8f35cae0c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_17
@@ -0,0 +1 @@
+@invalidmetric{label="bla"} 3.14 2
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18
new file mode 100644
index 000000000..7ca2cc268
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_18
@@ -0,0 +1 @@
+{label="bla"} 3.14 2
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19
new file mode 100644
index 000000000..7a6ccc0dd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_19
@@ -0,0 +1,3 @@
+
+# TYPE metric histogram
+metric_bucket{le="bla"} 3.14
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2
new file mode 100644
index 000000000..726d0017c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_2
@@ -0,0 +1,3 @@
+
+metric{label="new
+line"} 3.14
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3
new file mode 100644
index 000000000..6aa9e3081
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_3
@@ -0,0 +1 @@
+metric{@="bla"} 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4
new file mode 100644
index 000000000..d112cb902
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_4
@@ -0,0 +1 @@
+metric{__name__="bla"} 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5
new file mode 100644
index 000000000..b34554a8d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_5
@@ -0,0 +1 @@
+metric{label+="bla"} 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6
new file mode 100644
index 000000000..c4d7df3d1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_6
@@ -0,0 +1 @@
+metric{label=bla} 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7
new file mode 100644
index 000000000..97eafc4a6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_7
@@ -0,0 +1,3 @@
+
+# TYPE metric summary
+metric{quantile="bla"} 3.14
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8
new file mode 100644
index 000000000..fc706496b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_8
@@ -0,0 +1 @@
+metric{label="bla"+} 3.14
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9
new file mode 100644
index 000000000..57b4879c0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/from_test_parse_error_9
@@ -0,0 +1 @@
+metric{label="bla"} 3.14 2.72
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal
new file mode 100644
index 000000000..be1e6a369
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/fuzz/corpus/minimal
@@ -0,0 +1 @@
+m{} 0
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode.go
new file mode 100644
index 000000000..459ae3617
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode.go
@@ -0,0 +1,161 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "sort"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model"
+)
+
+type json2Decoder struct {
+ dec *json.Decoder
+ fams []*dto.MetricFamily
+}
+
+func newJSON2Decoder(r io.Reader) Decoder {
+ return &json2Decoder{
+ dec: json.NewDecoder(r),
+ }
+}
+
+type histogram002 struct {
+ Labels model.LabelSet `json:"labels"`
+ Values map[string]float64 `json:"value"`
+}
+
+type counter002 struct {
+ Labels model.LabelSet `json:"labels"`
+ Value float64 `json:"value"`
+}
+
+func protoLabelSet(base, ext model.LabelSet) []*dto.LabelPair {
+ labels := base.Clone().Merge(ext)
+ delete(labels, model.MetricNameLabel)
+
+ names := make([]string, 0, len(labels))
+ for ln := range labels {
+ names = append(names, string(ln))
+ }
+ sort.Strings(names)
+
+ pairs := make([]*dto.LabelPair, 0, len(labels))
+
+ for _, ln := range names {
+ lv := labels[model.LabelName(ln)]
+
+ pairs = append(pairs, &dto.LabelPair{
+ Name: proto.String(ln),
+ Value: proto.String(string(lv)),
+ })
+ }
+
+ return pairs
+}
+
+func (d *json2Decoder) more() error {
+ var entities []struct {
+ BaseLabels model.LabelSet `json:"baseLabels"`
+ Docstring string `json:"docstring"`
+ Metric struct {
+ Type string `json:"type"`
+ Values json.RawMessage `json:"value"`
+ } `json:"metric"`
+ }
+
+ if err := d.dec.Decode(&entities); err != nil {
+ return err
+ }
+ for _, e := range entities {
+ f := &dto.MetricFamily{
+ Name: proto.String(string(e.BaseLabels[model.MetricNameLabel])),
+ Help: proto.String(e.Docstring),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{},
+ }
+
+ d.fams = append(d.fams, f)
+
+ switch e.Metric.Type {
+ case "counter", "gauge":
+ var values []counter002
+
+ if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
+ return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
+ }
+
+ for _, ctr := range values {
+ f.Metric = append(f.Metric, &dto.Metric{
+ Label: protoLabelSet(e.BaseLabels, ctr.Labels),
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(ctr.Value),
+ },
+ })
+ }
+
+ case "histogram":
+ var values []histogram002
+
+ if err := json.Unmarshal(e.Metric.Values, &values); err != nil {
+ return fmt.Errorf("could not extract %s value: %s", e.Metric.Type, err)
+ }
+
+ for _, hist := range values {
+ quants := make([]string, 0, len(values))
+ for q := range hist.Values {
+ quants = append(quants, q)
+ }
+
+ sort.Strings(quants)
+
+ for _, q := range quants {
+ value := hist.Values[q]
+ // The correct label is "quantile" but to not break old expressions
+ // this remains "percentile"
+ hist.Labels["percentile"] = model.LabelValue(q)
+
+ f.Metric = append(f.Metric, &dto.Metric{
+ Label: protoLabelSet(e.BaseLabels, hist.Labels),
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(value),
+ },
+ })
+ }
+ }
+
+ default:
+ return fmt.Errorf("unknown metric type %q", e.Metric.Type)
+ }
+ }
+ return nil
+}
+
+// Decode implements the Decoder interface.
+func (d *json2Decoder) Decode(v *dto.MetricFamily) error {
+ if len(d.fams) == 0 {
+ if err := d.more(); err != nil {
+ return err
+ }
+ }
+
+ *v = *d.fams[0]
+ d.fams = d.fams[1:]
+
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go
new file mode 100644
index 000000000..22657f022
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/json_decode_test.go
@@ -0,0 +1,124 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "os"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func TestJSON2Decode(t *testing.T) {
+ f, err := os.Open("testdata/json2")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+
+ dec := newJSON2Decoder(f)
+
+ var v1 dto.MetricFamily
+ if err := dec.Decode(&v1); err != nil {
+ t.Fatal(err)
+ }
+
+ exp1 := dto.MetricFamily{
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Help: proto.String("RPC calls."),
+ Name: proto.String("rpc_calls_total"),
+ Metric: []*dto.Metric{
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("job"),
+ Value: proto.String("batch_job"),
+ }, {
+ Name: proto.String("service"),
+ Value: proto.String("zed"),
+ },
+ },
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(25),
+ },
+ },
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("job"),
+ Value: proto.String("batch_job"),
+ }, {
+ Name: proto.String("service"),
+ Value: proto.String("bar"),
+ },
+ },
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(24),
+ },
+ },
+ },
+ }
+
+ if !reflect.DeepEqual(v1, exp1) {
+ t.Fatalf("Expected %v, got %v", exp1, v1)
+ }
+
+ var v2 dto.MetricFamily
+ if err := dec.Decode(&v2); err != nil {
+ t.Fatal(err)
+ }
+
+ exp2 := dto.MetricFamily{
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Help: proto.String("RPC latency."),
+ Name: proto.String("rpc_latency_microseconds"),
+ Metric: []*dto.Metric{
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("percentile"),
+ Value: proto.String("0.010000"),
+ }, {
+ Name: proto.String("service"),
+ Value: proto.String("foo"),
+ },
+ },
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(15),
+ },
+ },
+ {
+ Label: []*dto.LabelPair{
+ {
+ Name: proto.String("percentile"),
+ Value: proto.String("0.990000"),
+ }, {
+ Name: proto.String("service"),
+ Value: proto.String("foo"),
+ },
+ },
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(17),
+ },
+ },
+ },
+ }
+
+ if !reflect.DeepEqual(v2, exp2) {
+ t.Fatalf("Expected %v, got %v", exp2, v2)
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2 b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2
new file mode 100644
index 000000000..b914c9386
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/json2
@@ -0,0 +1,46 @@
+[
+ {
+ "baseLabels": {
+ "__name__": "rpc_calls_total",
+ "job": "batch_job"
+ },
+ "docstring": "RPC calls.",
+ "metric": {
+ "type": "counter",
+ "value": [
+ {
+ "labels": {
+ "service": "zed"
+ },
+ "value": 25
+ },
+ {
+ "labels": {
+ "service": "bar"
+ },
+ "value": 24
+ }
+ ]
+ }
+ },
+ {
+ "baseLabels": {
+ "__name__": "rpc_latency_microseconds"
+ },
+ "docstring": "RPC latency.",
+ "metric": {
+ "type": "histogram",
+ "value": [
+ {
+ "labels": {
+ "service": "foo"
+ },
+ "value": {
+ "0.010000": 15,
+ "0.990000": 17
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf
new file mode 100644
index 000000000..d5aae5091
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf
@@ -0,0 +1,516 @@
+fc08 0a22 6874 7470 5f72 6571 7565 7374
+5f64 7572 6174 696f 6e5f 6d69 6372 6f73
+6563 6f6e 6473 122b 5468 6520 4854 5450
+2072 6571 7565 7374 206c 6174 656e 6369
+6573 2069 6e20 6d69 6372 6f73 6563 6f6e
+6473 2e18 0222 570a 0c0a 0768 616e 646c
+6572 1201 2f22 4708 0011 0000 0000 0000
+0000 1a12 0900 0000 0000 00e0 3f11 0000
+0000 0000 0000 1a12 09cd cccc cccc ccec
+3f11 0000 0000 0000 0000 1a12 09ae 47e1
+7a14 aeef 3f11 0000 0000 0000 0000 225d
+0a12 0a07 6861 6e64 6c65 7212 072f 616c
+6572 7473 2247 0800 1100 0000 0000 0000
+001a 1209 0000 0000 0000 e03f 1100 0000
+0000 0000 001a 1209 cdcc cccc cccc ec3f
+1100 0000 0000 0000 001a 1209 ae47 e17a
+14ae ef3f 1100 0000 0000 0000 0022 620a
+170a 0768 616e 646c 6572 120c 2f61 7069
+2f6d 6574 7269 6373 2247 0800 1100 0000
+0000 0000 001a 1209 0000 0000 0000 e03f
+1100 0000 0000 0000 001a 1209 cdcc cccc
+cccc ec3f 1100 0000 0000 0000 001a 1209
+ae47 e17a 14ae ef3f 1100 0000 0000 0000
+0022 600a 150a 0768 616e 646c 6572 120a
+2f61 7069 2f71 7565 7279 2247 0800 1100
+0000 0000 0000 001a 1209 0000 0000 0000
+e03f 1100 0000 0000 0000 001a 1209 cdcc
+cccc cccc ec3f 1100 0000 0000 0000 001a
+1209 ae47 e17a 14ae ef3f 1100 0000 0000
+0000 0022 660a 1b0a 0768 616e 646c 6572
+1210 2f61 7069 2f71 7565 7279 5f72 616e
+6765 2247 0800 1100 0000 0000 0000 001a
+1209 0000 0000 0000 e03f 1100 0000 0000
+0000 001a 1209 cdcc cccc cccc ec3f 1100
+0000 0000 0000 001a 1209 ae47 e17a 14ae
+ef3f 1100 0000 0000 0000 0022 620a 170a
+0768 616e 646c 6572 120c 2f61 7069 2f74
+6172 6765 7473 2247 0800 1100 0000 0000
+0000 001a 1209 0000 0000 0000 e03f 1100
+0000 0000 0000 001a 1209 cdcc cccc cccc
+ec3f 1100 0000 0000 0000 001a 1209 ae47
+e17a 14ae ef3f 1100 0000 0000 0000 0022
+600a 150a 0768 616e 646c 6572 120a 2f63
+6f6e 736f 6c65 732f 2247 0800 1100 0000
+0000 0000 001a 1209 0000 0000 0000 e03f
+1100 0000 0000 0000 001a 1209 cdcc cccc
+cccc ec3f 1100 0000 0000 0000 001a 1209
+ae47 e17a 14ae ef3f 1100 0000 0000 0000
+0022 5c0a 110a 0768 616e 646c 6572 1206
+2f67 7261 7068 2247 0800 1100 0000 0000
+0000 001a 1209 0000 0000 0000 e03f 1100
+0000 0000 0000 001a 1209 cdcc cccc cccc
+ec3f 1100 0000 0000 0000 001a 1209 ae47
+e17a 14ae ef3f 1100 0000 0000 0000 0022
+5b0a 100a 0768 616e 646c 6572 1205 2f68
+6561 7022 4708 0011 0000 0000 0000 0000
+1a12 0900 0000 0000 00e0 3f11 0000 0000
+0000 0000 1a12 09cd cccc cccc ccec 3f11
+0000 0000 0000 0000 1a12 09ae 47e1 7a14
+aeef 3f11 0000 0000 0000 0000 225e 0a13
+0a07 6861 6e64 6c65 7212 082f 7374 6174
+6963 2f22 4708 0011 0000 0000 0000 0000
+1a12 0900 0000 0000 00e0 3f11 0000 0000
+0000 0000 1a12 09cd cccc cccc ccec 3f11
+0000 0000 0000 0000 1a12 09ae 47e1 7a14
+aeef 3f11 0000 0000 0000 0000 2260 0a15
+0a07 6861 6e64 6c65 7212 0a70 726f 6d65
+7468 6575 7322 4708 3b11 5b8f c2f5 083f
+f440 1a12 0900 0000 0000 00e0 3f11 e17a
+14ae c7af 9340 1a12 09cd cccc cccc ccec
+3f11 2fdd 2406 81f0 9640 1a12 09ae 47e1
+7a14 aeef 3f11 3d0a d7a3 b095 a740 e608
+0a17 6874 7470 5f72 6571 7565 7374 5f73
+697a 655f 6279 7465 7312 2054 6865 2048
+5454 5020 7265 7175 6573 7420 7369 7a65
+7320 696e 2062 7974 6573 2e18 0222 570a
+0c0a 0768 616e 646c 6572 1201 2f22 4708
+0011 0000 0000 0000 0000 1a12 0900 0000
+0000 00e0 3f11 0000 0000 0000 0000 1a12
+09cd cccc cccc ccec 3f11 0000 0000 0000
+0000 1a12 09ae 47e1 7a14 aeef 3f11 0000
+0000 0000 0000 225d 0a12 0a07 6861 6e64
+6c65 7212 072f 616c 6572 7473 2247 0800
+1100 0000 0000 0000 001a 1209 0000 0000
+0000 e03f 1100 0000 0000 0000 001a 1209
+cdcc cccc cccc ec3f 1100 0000 0000 0000
+001a 1209 ae47 e17a 14ae ef3f 1100 0000
+0000 0000 0022 620a 170a 0768 616e 646c
+6572 120c 2f61 7069 2f6d 6574 7269 6373
+2247 0800 1100 0000 0000 0000 001a 1209
+0000 0000 0000 e03f 1100 0000 0000 0000
+001a 1209 cdcc cccc cccc ec3f 1100 0000
+0000 0000 001a 1209 ae47 e17a 14ae ef3f
+1100 0000 0000 0000 0022 600a 150a 0768
+616e 646c 6572 120a 2f61 7069 2f71 7565
+7279 2247 0800 1100 0000 0000 0000 001a
+1209 0000 0000 0000 e03f 1100 0000 0000
+0000 001a 1209 cdcc cccc cccc ec3f 1100
+0000 0000 0000 001a 1209 ae47 e17a 14ae
+ef3f 1100 0000 0000 0000 0022 660a 1b0a
+0768 616e 646c 6572 1210 2f61 7069 2f71
+7565 7279 5f72 616e 6765 2247 0800 1100
+0000 0000 0000 001a 1209 0000 0000 0000
+e03f 1100 0000 0000 0000 001a 1209 cdcc
+cccc cccc ec3f 1100 0000 0000 0000 001a
+1209 ae47 e17a 14ae ef3f 1100 0000 0000
+0000 0022 620a 170a 0768 616e 646c 6572
+120c 2f61 7069 2f74 6172 6765 7473 2247
+0800 1100 0000 0000 0000 001a 1209 0000
+0000 0000 e03f 1100 0000 0000 0000 001a
+1209 cdcc cccc cccc ec3f 1100 0000 0000
+0000 001a 1209 ae47 e17a 14ae ef3f 1100
+0000 0000 0000 0022 600a 150a 0768 616e
+646c 6572 120a 2f63 6f6e 736f 6c65 732f
+2247 0800 1100 0000 0000 0000 001a 1209
+0000 0000 0000 e03f 1100 0000 0000 0000
+001a 1209 cdcc cccc cccc ec3f 1100 0000
+0000 0000 001a 1209 ae47 e17a 14ae ef3f
+1100 0000 0000 0000 0022 5c0a 110a 0768
+616e 646c 6572 1206 2f67 7261 7068 2247
+0800 1100 0000 0000 0000 001a 1209 0000
+0000 0000 e03f 1100 0000 0000 0000 001a
+1209 cdcc cccc cccc ec3f 1100 0000 0000
+0000 001a 1209 ae47 e17a 14ae ef3f 1100
+0000 0000 0000 0022 5b0a 100a 0768 616e
+646c 6572 1205 2f68 6561 7022 4708 0011
+0000 0000 0000 0000 1a12 0900 0000 0000
+00e0 3f11 0000 0000 0000 0000 1a12 09cd
+cccc cccc ccec 3f11 0000 0000 0000 0000
+1a12 09ae 47e1 7a14 aeef 3f11 0000 0000
+0000 0000 225e 0a13 0a07 6861 6e64 6c65
+7212 082f 7374 6174 6963 2f22 4708 0011
+0000 0000 0000 0000 1a12 0900 0000 0000
+00e0 3f11 0000 0000 0000 0000 1a12 09cd
+cccc cccc ccec 3f11 0000 0000 0000 0000
+1a12 09ae 47e1 7a14 aeef 3f11 0000 0000
+0000 0000 2260 0a15 0a07 6861 6e64 6c65
+7212 0a70 726f 6d65 7468 6575 7322 4708
+3b11 0000 0000 40c4 d040 1a12 0900 0000
+0000 00e0 3f11 0000 0000 0030 7240 1a12
+09cd cccc cccc ccec 3f11 0000 0000 0030
+7240 1a12 09ae 47e1 7a14 aeef 3f11 0000
+0000 0030 7240 7c0a 1368 7474 705f 7265
+7175 6573 7473 5f74 6f74 616c 1223 546f
+7461 6c20 6e75 6d62 6572 206f 6620 4854
+5450 2072 6571 7565 7374 7320 6d61 6465
+2e18 0022 3e0a 0b0a 0463 6f64 6512 0332
+3030 0a15 0a07 6861 6e64 6c65 7212 0a70
+726f 6d65 7468 6575 730a 0d0a 066d 6574
+686f 6412 0367 6574 1a09 0900 0000 0000
+804d 40e8 080a 1868 7474 705f 7265 7370
+6f6e 7365 5f73 697a 655f 6279 7465 7312
+2154 6865 2048 5454 5020 7265 7370 6f6e
+7365 2073 697a 6573 2069 6e20 6279 7465
+732e 1802 2257 0a0c 0a07 6861 6e64 6c65
+7212 012f 2247 0800 1100 0000 0000 0000
+001a 1209 0000 0000 0000 e03f 1100 0000
+0000 0000 001a 1209 cdcc cccc cccc ec3f
+1100 0000 0000 0000 001a 1209 ae47 e17a
+14ae ef3f 1100 0000 0000 0000 0022 5d0a
+120a 0768 616e 646c 6572 1207 2f61 6c65
+7274 7322 4708 0011 0000 0000 0000 0000
+1a12 0900 0000 0000 00e0 3f11 0000 0000
+0000 0000 1a12 09cd cccc cccc ccec 3f11
+0000 0000 0000 0000 1a12 09ae 47e1 7a14
+aeef 3f11 0000 0000 0000 0000 2262 0a17
+0a07 6861 6e64 6c65 7212 0c2f 6170 692f
+6d65 7472 6963 7322 4708 0011 0000 0000
+0000 0000 1a12 0900 0000 0000 00e0 3f11
+0000 0000 0000 0000 1a12 09cd cccc cccc
+ccec 3f11 0000 0000 0000 0000 1a12 09ae
+47e1 7a14 aeef 3f11 0000 0000 0000 0000
+2260 0a15 0a07 6861 6e64 6c65 7212 0a2f
+6170 692f 7175 6572 7922 4708 0011 0000
+0000 0000 0000 1a12 0900 0000 0000 00e0
+3f11 0000 0000 0000 0000 1a12 09cd cccc
+cccc ccec 3f11 0000 0000 0000 0000 1a12
+09ae 47e1 7a14 aeef 3f11 0000 0000 0000
+0000 2266 0a1b 0a07 6861 6e64 6c65 7212
+102f 6170 692f 7175 6572 795f 7261 6e67
+6522 4708 0011 0000 0000 0000 0000 1a12
+0900 0000 0000 00e0 3f11 0000 0000 0000
+0000 1a12 09cd cccc cccc ccec 3f11 0000
+0000 0000 0000 1a12 09ae 47e1 7a14 aeef
+3f11 0000 0000 0000 0000 2262 0a17 0a07
+6861 6e64 6c65 7212 0c2f 6170 692f 7461
+7267 6574 7322 4708 0011 0000 0000 0000
+0000 1a12 0900 0000 0000 00e0 3f11 0000
+0000 0000 0000 1a12 09cd cccc cccc ccec
+3f11 0000 0000 0000 0000 1a12 09ae 47e1
+7a14 aeef 3f11 0000 0000 0000 0000 2260
+0a15 0a07 6861 6e64 6c65 7212 0a2f 636f
+6e73 6f6c 6573 2f22 4708 0011 0000 0000
+0000 0000 1a12 0900 0000 0000 00e0 3f11
+0000 0000 0000 0000 1a12 09cd cccc cccc
+ccec 3f11 0000 0000 0000 0000 1a12 09ae
+47e1 7a14 aeef 3f11 0000 0000 0000 0000
+225c 0a11 0a07 6861 6e64 6c65 7212 062f
+6772 6170 6822 4708 0011 0000 0000 0000
+0000 1a12 0900 0000 0000 00e0 3f11 0000
+0000 0000 0000 1a12 09cd cccc cccc ccec
+3f11 0000 0000 0000 0000 1a12 09ae 47e1
+7a14 aeef 3f11 0000 0000 0000 0000 225b
+0a10 0a07 6861 6e64 6c65 7212 052f 6865
+6170 2247 0800 1100 0000 0000 0000 001a
+1209 0000 0000 0000 e03f 1100 0000 0000
+0000 001a 1209 cdcc cccc cccc ec3f 1100
+0000 0000 0000 001a 1209 ae47 e17a 14ae
+ef3f 1100 0000 0000 0000 0022 5e0a 130a
+0768 616e 646c 6572 1208 2f73 7461 7469
+632f 2247 0800 1100 0000 0000 0000 001a
+1209 0000 0000 0000 e03f 1100 0000 0000
+0000 001a 1209 cdcc cccc cccc ec3f 1100
+0000 0000 0000 001a 1209 ae47 e17a 14ae
+ef3f 1100 0000 0000 0000 0022 600a 150a
+0768 616e 646c 6572 120a 7072 6f6d 6574
+6865 7573 2247 083b 1100 0000 00e0 b4fc
+401a 1209 0000 0000 0000 e03f 1100 0000
+0000 349f 401a 1209 cdcc cccc cccc ec3f
+1100 0000 0000 08a0 401a 1209 ae47 e17a
+14ae ef3f 1100 0000 0000 0aa0 405c 0a19
+7072 6f63 6573 735f 6370 755f 7365 636f
+6e64 735f 746f 7461 6c12 3054 6f74 616c
+2075 7365 7220 616e 6420 7379 7374 656d
+2043 5055 2074 696d 6520 7370 656e 7420
+696e 2073 6563 6f6e 6473 2e18 0022 0b1a
+0909 a470 3d0a d7a3 d03f 4f0a 1270 726f
+6365 7373 5f67 6f72 6f75 7469 6e65 7312
+2a4e 756d 6265 7220 6f66 2067 6f72 6f75
+7469 6e65 7320 7468 6174 2063 7572 7265
+6e74 6c79 2065 7869 7374 2e18 0122 0b12
+0909 0000 0000 0000 5140 4a0a 0f70 726f
+6365 7373 5f6d 6178 5f66 6473 1228 4d61
+7869 6d75 6d20 6e75 6d62 6572 206f 6620
+6f70 656e 2066 696c 6520 6465 7363 7269
+7074 6f72 732e 1801 220b 1209 0900 0000
+0000 00c0 4043 0a10 7072 6f63 6573 735f
+6f70 656e 5f66 6473 1220 4e75 6d62 6572
+206f 6620 6f70 656e 2066 696c 6520 6465
+7363 7269 7074 6f72 732e 1801 220b 1209
+0900 0000 0000 003d 404e 0a1d 7072 6f63
+6573 735f 7265 7369 6465 6e74 5f6d 656d
+6f72 795f 6279 7465 7312 1e52 6573 6964
+656e 7420 6d65 6d6f 7279 2073 697a 6520
+696e 2062 7974 6573 2e18 0122 0b12 0909
+0000 0000 004b 8841 630a 1a70 726f 6365
+7373 5f73 7461 7274 5f74 696d 655f 7365
+636f 6e64 7312 3653 7461 7274 2074 696d
+6520 6f66 2074 6865 2070 726f 6365 7373
+2073 696e 6365 2075 6e69 7820 6570 6f63
+6820 696e 2073 6563 6f6e 6473 2e18 0122
+0b12 0909 3d0a 172d e831 d541 4c0a 1c70
+726f 6365 7373 5f76 6972 7475 616c 5f6d
+656d 6f72 795f 6279 7465 7312 1d56 6972
+7475 616c 206d 656d 6f72 7920 7369 7a65
+2069 6e20 6279 7465 732e 1801 220b 1209
+0900 0000 0020 12c0 415f 0a27 7072 6f6d
+6574 6865 7573 5f64 6e73 5f73 645f 6c6f
+6f6b 7570 5f66 6169 6c75 7265 735f 746f
+7461 6c12 2554 6865 206e 756d 6265 7220
+6f66 2044 4e53 2d53 4420 6c6f 6f6b 7570
+2066 6169 6c75 7265 732e 1800 220b 1a09
+0900 0000 0000 0000 004f 0a1f 7072 6f6d
+6574 6865 7573 5f64 6e73 5f73 645f 6c6f
+6f6b 7570 735f 746f 7461 6c12 1d54 6865
+206e 756d 6265 7220 6f66 2044 4e53 2d53
+4420 6c6f 6f6b 7570 732e 1800 220b 1a09
+0900 0000 0000 0008 40cf 010a 2a70 726f
+6d65 7468 6575 735f 6576 616c 7561 746f
+725f 6475 7261 7469 6f6e 5f6d 696c 6c69
+7365 636f 6e64 7312 2c54 6865 2064 7572
+6174 696f 6e20 666f 7220 616c 6c20 6576
+616c 7561 7469 6f6e 7320 746f 2065 7865
+6375 7465 2e18 0222 7122 6f08 0b11 0000
+0000 0000 2240 1a12 097b 14ae 47e1 7a84
+3f11 0000 0000 0000 0000 1a12 099a 9999
+9999 99a9 3f11 0000 0000 0000 0000 1a12
+0900 0000 0000 00e0 3f11 0000 0000 0000
+0000 1a12 09cd cccc cccc ccec 3f11 0000
+0000 0000 f03f 1a12 09ae 47e1 7a14 aeef
+3f11 0000 0000 0000 f03f a301 0a39 7072
+6f6d 6574 6865 7573 5f6c 6f63 616c 5f73
+746f 7261 6765 5f63 6865 636b 706f 696e
+745f 6475 7261 7469 6f6e 5f6d 696c 6c69
+7365 636f 6e64 7312 5754 6865 2064 7572
+6174 696f 6e20 2869 6e20 6d69 6c6c 6973
+6563 6f6e 6473 2920 6974 2074 6f6f 6b20
+746f 2063 6865 636b 706f 696e 7420 696e
+2d6d 656d 6f72 7920 6d65 7472 6963 7320
+616e 6420 6865 6164 2063 6875 6e6b 732e
+1801 220b 1209 0900 0000 0000 0000 00f2
+010a 2870 726f 6d65 7468 6575 735f 6c6f
+6361 6c5f 7374 6f72 6167 655f 6368 756e
+6b5f 6f70 735f 746f 7461 6c12 3354 6865
+2074 6f74 616c 206e 756d 6265 7220 6f66
+2063 6875 6e6b 206f 7065 7261 7469 6f6e
+7320 6279 2074 6865 6972 2074 7970 652e
+1800 221b 0a0e 0a04 7479 7065 1206 6372
+6561 7465 1a09 0900 0000 0000 b880 4022
+1c0a 0f0a 0474 7970 6512 0770 6572 7369
+7374 1a09 0900 0000 0000 c05b 4022 180a
+0b0a 0474 7970 6512 0370 696e 1a09 0900
+0000 0000 807b 4022 1e0a 110a 0474 7970
+6512 0974 7261 6e73 636f 6465 1a09 0900
+0000 0000 a06b 4022 1a0a 0d0a 0474 7970
+6512 0575 6e70 696e 1a09 0900 0000 0000
+807b 40c4 010a 3c70 726f 6d65 7468 6575
+735f 6c6f 6361 6c5f 7374 6f72 6167 655f
+696e 6465 7869 6e67 5f62 6174 6368 5f6c
+6174 656e 6379 5f6d 696c 6c69 7365 636f
+6e64 7312 3751 7561 6e74 696c 6573 2066
+6f72 2062 6174 6368 2069 6e64 6578 696e
+6720 6c61 7465 6e63 6965 7320 696e 206d
+696c 6c69 7365 636f 6e64 732e 1802 2249
+2247 0801 1100 0000 0000 0000 001a 1209
+0000 0000 0000 e03f 1100 0000 0000 0000
+001a 1209 cdcc cccc cccc ec3f 1100 0000
+0000 0000 001a 1209 ae47 e17a 14ae ef3f
+1100 0000 0000 0000 00bf 010a 2d70 726f
+6d65 7468 6575 735f 6c6f 6361 6c5f 7374
+6f72 6167 655f 696e 6465 7869 6e67 5f62
+6174 6368 5f73 697a 6573 1241 5175 616e
+7469 6c65 7320 666f 7220 696e 6465 7869
+6e67 2062 6174 6368 2073 697a 6573 2028
+6e75 6d62 6572 206f 6620 6d65 7472 6963
+7320 7065 7220 6261 7463 6829 2e18 0222
+4922 4708 0111 0000 0000 0000 0040 1a12
+0900 0000 0000 00e0 3f11 0000 0000 0000
+0040 1a12 09cd cccc cccc ccec 3f11 0000
+0000 0000 0040 1a12 09ae 47e1 7a14 aeef
+3f11 0000 0000 0000 0040 660a 3070 726f
+6d65 7468 6575 735f 6c6f 6361 6c5f 7374
+6f72 6167 655f 696e 6465 7869 6e67 5f71
+7565 7565 5f63 6170 6163 6974 7912 2354
+6865 2063 6170 6163 6974 7920 6f66 2074
+6865 2069 6e64 6578 696e 6720 7175 6575
+652e 1801 220b 1209 0900 0000 0000 00d0
+406d 0a2e 7072 6f6d 6574 6865 7573 5f6c
+6f63 616c 5f73 746f 7261 6765 5f69 6e64
+6578 696e 675f 7175 6575 655f 6c65 6e67
+7468 122c 5468 6520 6e75 6d62 6572 206f
+6620 6d65 7472 6963 7320 7761 6974 696e
+6720 746f 2062 6520 696e 6465 7865 642e
+1801 220b 1209 0900 0000 0000 0000 0067
+0a2f 7072 6f6d 6574 6865 7573 5f6c 6f63
+616c 5f73 746f 7261 6765 5f69 6e67 6573
+7465 645f 7361 6d70 6c65 735f 746f 7461
+6c12 2554 6865 2074 6f74 616c 206e 756d
+6265 7220 6f66 2073 616d 706c 6573 2069
+6e67 6573 7465 642e 1800 220b 1a09 0900
+0000 0080 27cd 40c3 010a 3770 726f 6d65
+7468 6575 735f 6c6f 6361 6c5f 7374 6f72
+6167 655f 696e 7661 6c69 645f 7072 656c
+6f61 645f 7265 7175 6573 7473 5f74 6f74
+616c 1279 5468 6520 746f 7461 6c20 6e75
+6d62 6572 206f 6620 7072 656c 6f61 6420
+7265 7175 6573 7473 2072 6566 6572 7269
+6e67 2074 6f20 6120 6e6f 6e2d 6578 6973
+7465 6e74 2073 6572 6965 732e 2054 6869
+7320 6973 2061 6e20 696e 6469 6361 7469
+6f6e 206f 6620 6f75 7464 6174 6564 206c
+6162 656c 2069 6e64 6578 6573 2e18 0022
+0b1a 0909 0000 0000 0000 0000 6f0a 2a70
+726f 6d65 7468 6575 735f 6c6f 6361 6c5f
+7374 6f72 6167 655f 6d65 6d6f 7279 5f63
+6875 6e6b 6465 7363 7312 3254 6865 2063
+7572 7265 6e74 206e 756d 6265 7220 6f66
+2063 6875 6e6b 2064 6573 6372 6970 746f
+7273 2069 6e20 6d65 6d6f 7279 2e18 0122
+0b12 0909 0000 0000 0020 8f40 9c01 0a26
+7072 6f6d 6574 6865 7573 5f6c 6f63 616c
+5f73 746f 7261 6765 5f6d 656d 6f72 795f
+6368 756e 6b73 1263 5468 6520 6375 7272
+656e 7420 6e75 6d62 6572 206f 6620 6368
+756e 6b73 2069 6e20 6d65 6d6f 7279 2c20
+6578 636c 7564 696e 6720 636c 6f6e 6564
+2063 6875 6e6b 7320 2869 2e65 2e20 6368
+756e 6b73 2077 6974 686f 7574 2061 2064
+6573 6372 6970 746f 7229 2e18 0122 0b12
+0909 0000 0000 00e8 8d40 600a 2670 726f
+6d65 7468 6575 735f 6c6f 6361 6c5f 7374
+6f72 6167 655f 6d65 6d6f 7279 5f73 6572
+6965 7312 2754 6865 2063 7572 7265 6e74
+206e 756d 6265 7220 6f66 2073 6572 6965
+7320 696e 206d 656d 6f72 792e 1801 220b
+1209 0900 0000 0000 807a 40b7 010a 3570
+726f 6d65 7468 6575 735f 6c6f 6361 6c5f
+7374 6f72 6167 655f 7065 7273 6973 745f
+6c61 7465 6e63 795f 6d69 6372 6f73 6563
+6f6e 6473 1231 4120 7375 6d6d 6172 7920
+6f66 206c 6174 656e 6369 6573 2066 6f72
+2070 6572 7369 7374 696e 6720 6561 6368
+2063 6875 6e6b 2e18 0222 4922 4708 6f11
+1c2f dd24 e68c cc40 1a12 0900 0000 0000
+00e0 3f11 8d97 6e12 8360 3e40 1a12 09cd
+cccc cccc ccec 3f11 0ad7 a370 3d62 6b40
+1a12 09ae 47e1 7a14 aeef 3f11 7b14 ae47
+e1b6 7240 6a0a 2f70 726f 6d65 7468 6575
+735f 6c6f 6361 6c5f 7374 6f72 6167 655f
+7065 7273 6973 745f 7175 6575 655f 6361
+7061 6369 7479 1228 5468 6520 746f 7461
+6c20 6361 7061 6369 7479 206f 6620 7468
+6520 7065 7273 6973 7420 7175 6575 652e
+1801 220b 1209 0900 0000 0000 0090 407a
+0a2d 7072 6f6d 6574 6865 7573 5f6c 6f63
+616c 5f73 746f 7261 6765 5f70 6572 7369
+7374 5f71 7565 7565 5f6c 656e 6774 6812
+3a54 6865 2063 7572 7265 6e74 206e 756d
+6265 7220 6f66 2063 6875 6e6b 7320 7761
+6974 696e 6720 696e 2074 6865 2070 6572
+7369 7374 2071 7565 7565 2e18 0122 0b12
+0909 0000 0000 0000 0000 ac01 0a29 7072
+6f6d 6574 6865 7573 5f6c 6f63 616c 5f73
+746f 7261 6765 5f73 6572 6965 735f 6f70
+735f 746f 7461 6c12 3454 6865 2074 6f74
+616c 206e 756d 6265 7220 6f66 2073 6572
+6965 7320 6f70 6572 6174 696f 6e73 2062
+7920 7468 6569 7220 7479 7065 2e18 0022
+1b0a 0e0a 0474 7970 6512 0663 7265 6174
+651a 0909 0000 0000 0000 0040 222a 0a1d
+0a04 7479 7065 1215 6d61 696e 7465 6e61
+6e63 655f 696e 5f6d 656d 6f72 791a 0909
+0000 0000 0000 1440 d601 0a2d 7072 6f6d
+6574 6865 7573 5f6e 6f74 6966 6963 6174
+696f 6e73 5f6c 6174 656e 6379 5f6d 696c
+6c69 7365 636f 6e64 7312 584c 6174 656e
+6379 2071 7561 6e74 696c 6573 2066 6f72
+2073 656e 6469 6e67 2061 6c65 7274 206e
+6f74 6966 6963 6174 696f 6e73 2028 6e6f
+7420 696e 636c 7564 696e 6720 6472 6f70
+7065 6420 6e6f 7469 6669 6361 7469 6f6e
+7329 2e18 0222 4922 4708 0011 0000 0000
+0000 0000 1a12 0900 0000 0000 00e0 3f11
+0000 0000 0000 0000 1a12 09cd cccc cccc
+ccec 3f11 0000 0000 0000 0000 1a12 09ae
+47e1 7a14 aeef 3f11 0000 0000 0000 0000
+680a 2770 726f 6d65 7468 6575 735f 6e6f
+7469 6669 6361 7469 6f6e 735f 7175 6575
+655f 6361 7061 6369 7479 122e 5468 6520
+6361 7061 6369 7479 206f 6620 7468 6520
+616c 6572 7420 6e6f 7469 6669 6361 7469
+6f6e 7320 7175 6575 652e 1801 220b 1209
+0900 0000 0000 0059 4067 0a25 7072 6f6d
+6574 6865 7573 5f6e 6f74 6966 6963 6174
+696f 6e73 5f71 7565 7565 5f6c 656e 6774
+6812 2f54 6865 206e 756d 6265 7220 6f66
+2061 6c65 7274 206e 6f74 6966 6963 6174
+696f 6e73 2069 6e20 7468 6520 7175 6575
+652e 1801 220b 1209 0900 0000 0000 0000
+009e 020a 3070 726f 6d65 7468 6575 735f
+7275 6c65 5f65 7661 6c75 6174 696f 6e5f
+6475 7261 7469 6f6e 5f6d 696c 6c69 7365
+636f 6e64 7312 2354 6865 2064 7572 6174
+696f 6e20 666f 7220 6120 7275 6c65 2074
+6f20 6578 6563 7574 652e 1802 2260 0a15
+0a09 7275 6c65 5f74 7970 6512 0861 6c65
+7274 696e 6722 4708 3711 0000 0000 0000
+2840 1a12 0900 0000 0000 00e0 3f11 0000
+0000 0000 0000 1a12 09cd cccc cccc ccec
+3f11 0000 0000 0000 0000 1a12 09ae 47e1
+7a14 aeef 3f11 0000 0000 0000 0840 2261
+0a16 0a09 7275 6c65 5f74 7970 6512 0972
+6563 6f72 6469 6e67 2247 0837 1100 0000
+0000 002e 401a 1209 0000 0000 0000 e03f
+1100 0000 0000 0000 001a 1209 cdcc cccc
+cccc ec3f 1100 0000 0000 0000 001a 1209
+ae47 e17a 14ae ef3f 1100 0000 0000 0008
+4069 0a29 7072 6f6d 6574 6865 7573 5f72
+756c 655f 6576 616c 7561 7469 6f6e 5f66
+6169 6c75 7265 735f 746f 7461 6c12 2d54
+6865 2074 6f74 616c 206e 756d 6265 7220
+6f66 2072 756c 6520 6576 616c 7561 7469
+6f6e 2066 6169 6c75 7265 732e 1800 220b
+1a09 0900 0000 0000 0000 0060 0a21 7072
+6f6d 6574 6865 7573 5f73 616d 706c 6573
+5f71 7565 7565 5f63 6170 6163 6974 7912
+2c43 6170 6163 6974 7920 6f66 2074 6865
+2071 7565 7565 2066 6f72 2075 6e77 7269
+7474 656e 2073 616d 706c 6573 2e18 0122
+0b12 0909 0000 0000 0000 b040 da01 0a1f
+7072 6f6d 6574 6865 7573 5f73 616d 706c
+6573 5f71 7565 7565 5f6c 656e 6774 6812
+a701 4375 7272 656e 7420 6e75 6d62 6572
+206f 6620 6974 656d 7320 696e 2074 6865
+2071 7565 7565 2066 6f72 2075 6e77 7269
+7474 656e 2073 616d 706c 6573 2e20 4561
+6368 2069 7465 6d20 636f 6d70 7269 7365
+7320 616c 6c20 7361 6d70 6c65 7320 6578
+706f 7365 6420 6279 206f 6e65 2074 6172
+6765 7420 6173 206f 6e65 206d 6574 7269
+6320 6661 6d69 6c79 2028 692e 652e 206d
+6574 7269 6373 206f 6620 7468 6520 7361
+6d65 206e 616d 6529 2e18 0122 0b12 0909
+0000 0000 0000 0000 d902 0a29 7072 6f6d
+6574 6865 7573 5f74 6172 6765 745f 696e
+7465 7276 616c 5f6c 656e 6774 685f 7365
+636f 6e64 7312 2141 6374 7561 6c20 696e
+7465 7276 616c 7320 6265 7477 6565 6e20
+7363 7261 7065 732e 1802 2282 010a 0f0a
+0869 6e74 6572 7661 6c12 0331 3573 226f
+0804 1100 0000 0000 804d 401a 1209 7b14
+ae47 e17a 843f 1100 0000 0000 002c 401a
+1209 9a99 9999 9999 a93f 1100 0000 0000
+002c 401a 1209 0000 0000 0000 e03f 1100
+0000 0000 002e 401a 1209 cdcc cccc cccc
+ec3f 1100 0000 0000 002e 401a 1209 ae47
+e17a 14ae ef3f 1100 0000 0000 002e 4022
+8101 0a0e 0a08 696e 7465 7276 616c 1202
+3173 226f 083a 1100 0000 0000 003c 401a
+1209 7b14 ae47 e17a 843f 1100 0000 0000
+0000 001a 1209 9a99 9999 9999 a93f 1100
+0000 0000 0000 001a 1209 0000 0000 0000
+e03f 1100 0000 0000 0000 001a 1209 cdcc
+cccc cccc ec3f 1100 0000 0000 00f0 3f1a
+1209 ae47 e17a 14ae ef3f 1100 0000 0000
+00f0 3f
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz
new file mode 100644
index 000000000..62fccb616
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/protobuf.gz
@@ -0,0 +1,129 @@
+1f8b 0808 efa0 c754 0003 7072 6f74 6f62
+7566 00ed 594d 8c1c c515 9eb1 8d3d 5b86
+6037 265e 8c4d ca03 c4bb ceee cc9a 9f58
+01cc f6ca 4424 041b 8837 21c8 24ed daee
+9a99 cef6 1f55 d578 c7e4 b004 0e39 8088
+8448 048a 124b 4442 9110 e110 25b9 c54a
+9072 01c5 9724 4a24 2472 413e 448a 8592
+1b87 bcea aeda eeea 99d9 3530 49a4 68e7
+b0bb 5355 fdde abf7 bef7 bdf7 7a3f 6ca0
+664f 88c4 61f4 8994 72e1 7829 23c2 8f23
+27f4 5d16 73ea c691 c7ad cf2d f628 fed2
+e2e2 c358 9dc3 0111 3472 7dca b11f e1f2
+d9d6 e496 e6a3 e86a b4a3 4722 2fa0 ccaa
+b79b f737 6abb 6bea b3cf 9ac8 ff78 6fbe
+bcf6 cedb f2f3 7763 ed8d fbff 766e cf1b
+ff28 d69a df44 5621 7847 9bc0 2fc1 c727
+7e09 ed2d c45f dd26 89df 0ea9 60be 3b46
+1d67 d0f5 850e 94e9 008f b2fe f834 74d0
+8d85 865d 8506 8791 a84b ffa3 de12 8475
+e938 2352 f116 208c c701 e563 84d4 e368
+77a1 617b bbcb 48d2 1b9f f4d3 6857 21fd
+aa76 8f92 647c c2bf 85ae 2b84 37da 5c40
+e6ba 6374 8de9 fc84 c590 0c3d 9aca f0de
+bdfb f40b bffd 5763 fe9f 7659 8314 f0fb
+9fbf 6897 35b4 dfbd 65fb d397 7f60 9735
+1c43 7f7e f5cd 975e b3df 6fa0 bd06 fb70
+ff1c 7596 fa82 720b 0f50 8edc cce8 263b
+b0c9 339b 3cb3 c933 5afa ff2f cfc8 13f6
+5b17 ed01 0d73 cc1e d090 af99 1a60 ed3b
+e8ba 32cd 7047 c482 04d6 cd8b f217 8ed2
+7089 321c 770c bae1 3824 1e6d 4dd6 9af7
+a29d 689b 1b7b d4da 7adb dcdc 085b d135
+68bb fc33 f6ac ad00 cd7d 13b9 b5ab 27ec
+4b0d 34a9 b4f3 0470 45cb 2c77 b0c4 72f9
+ee26 cd7d 02ec 6cd2 dc26 cd7d 6ce1 ff73
+9a7b ef17 1f0e d2dc 1d3f 19a4 b9c6 f941
+9a43 e7ed c7d1 0d20 d5a5 9c3b 6e92 3a6a
+2053 6437 9793 5dca 81ea c006 ccfb 5cd0
+101f 7ff8 6b58 f821 d04e 4223 2169 676d
+8eab 3577 028d fd34 91dd dac5 f987 90a5
+8577 6316 a7c2 8f80 bf0e 9f5c 23cf 6215
+8b1e 11d8 4d19 0391 411f d315 9f8b d664
+bdb9 d352 b458 7bc4 7e00 5dab e585 64c5
+e9c0 9439 7582 acf8 611a 9618 3906 ab70
+c70f 28f6 2877 999f 8898 7153 d405 fb38
+daa5 45c9 f399 2c7c f2a3 c838 669f 4407
+b40c 6062 df03 cb9d 9086 31e4 79ce d437
+7d55 2de3 7c39 e3e9 124d 97c4 7de5 7b0b
+2eda a7c5 018e 9870 a48f 7544 accf 9f92
+6bb9 dfc1 4040 0156 a741 6ae4 529c 46fe
+0aa6 49ec f68c 88e4 3a8e a1bd b397 8efc
+71e1 41b4 5feb 78d2 6722 2581 69f1 81af
+e7ab 1b1a 8cad 0b0b 0e3a 5420 d2f1 22b0
+db73 8238 5e4e 13a7 43fc 2005 af28 24dd
+2a6b 5611 a2fb 4e9e 9a3d 751f cecf 627d
+56c3 47a3 ff21 f499 51f2 b5dc 03eb c8ad
+c86b d87f a8a3 c325 81f4 4912 a404 025b
+7e81 1104 bef6 f88c 94ad b770 2786 1c08
+02ac 9e82 25c0 6c0c 38a5 6e2a a82c b94f
+34e3 c64e 95ba 4d99 6c4f ed91 e9f6 ac91
+e2af bc2c 3f3f 9bff 88f4 7079 7e90 1e2e
+cfbf 5a47 5f28 5d28 885d 8827 871b 912e
+75dc 1e75 9793 d88f c488 fb3d 6adc 6f2a
+7b27 536c 4f63 1fd0 068e 94b7 2c64 0118
+6615 3654 5dce 9801 58d5 8353 69b4 5cc9
+925a ed83 3a9a 5ac7 4878 0432 50c7 f376
+6993 a8b4 58d9 2199 924c f97d a92f f1ef
+332c fa49 d66e dd88 3e85 b6c9 2fd6 7697
+5122 a88e faaf 57ed e67e 74ad dadc 0122
+38f0 8ade bd70 da6e 4eca 4e2d dbdd 9af8
+d15a 0ff6 94dd bc09 ca52 be33 21a0 6e73
+d9ce e9fd f3cb 7673 1ff4 6ff9 fe55 6964
+3efb 561d dd33 f2ce 7ee4 01bb 455d 6789
+08b7 e7e4 6fc5 fa66 6c8e 3e92 9248 00ff
+f00c 78d9 49ac 1fac be48 2b9e 9330 fc32
+d486 fa58 aacf 6fea 68f6 4a6f 9175 a0d6
+8269 f69a c1b9 fd79 973a 5504 5623 08c2
+921f 991e b8c0 6071 cbd7 aa17 182c 6eb0
+d641 731b db0f 8d59 0a40 2409 717d d187
+061f 10a8 bf69 a65d bb48 76d8 44f8 453b
+44ad 2b55 13d0 a82b 7a39 b50c fae1 2cf1
+85d4 0219 b7a4 9452 af9a 4f5d d45e 475b
+17c6 10ea 399c 8449 60b2 6f35 abd4 11ac
+9f29 b3e5 eaa1 77ec dfd5 d1d1 7514 010d
+fa9e 9330 1ac4 c4ab 4e49 fd61 0ad5 d962
+5862 b443 1953 1726 388a a3d9 acec cb82
+092d 07e0 bb85 177b 3e98 2849 46fa c377
+73b2 9215 3a15 1ea4 8107 c9b0 4403 e5ac
+8112 121b 8c6f de41 15be 8c5d 6495 e7d6
+6d59 ecf3 1e64 807f 4a8d 4096 76d9 d346
+70f0 0bf6 8fea e8b3 57a4 905b ee3a ca4a
+1a66 a0c4 b841 ea49 37b9 411c 51cd b3c0
+d82d dad2 5fce fa30 47a6 02dc 58d8 396d
+5877 e979 fbcc c6c6 e57e b70e 0d37 2edf
+1d71 fdd5 73f6 afea e8ce 911a 14f9 9608
+aff4 df82 230b 98a7 6148 5896 7305 c149
+1a51 0f4a 0f50 023c 925d 5933 45bc 7b7f
+fbdd 5bde 7fee 6d83 299e ff61 643d 73e6
+5e83 29a0 254d 8e2d 2d1b 4c91 95e8 5f32
+fbdb eb24 95b6 bb42 1453 05c6 ab74 a19e
+18c6 16df b7cf ad43 aaa6 2a45 1677 ad0b
+14cd 1910 930d 54d7 6aaf d7d1 f448 dd79
+6c4b b5f8 8ea1 ac91 23e0 6315 6360 e4e6
+6174 406d 5e1f 12e8 2768 44a0 7905 3e51
+005c 3bbb c7fe 9359 7ea2 58f8 1d45 007c
+78d5 fcc6 83f9 2adc be5c 8638 8db2 f4c9
+de55 6043 0e54 a358 f634 3ac3 3c16 2709
+a498 7168 ad2a 8d67 a8eb 196d b379 ad0a
+c65a c38a d1b0 6b0c 09f7 6376 17dd ba81
+2285 b0b6 598e 8629 50f0 1a0a ab1f 6f31
+ea2c 4b03 ea14 6df2 88ee f3e6 c1ee 1acb
+272b 4db5 1c80 2732 8919 681a 996d 1029
+88c6 51e5 d1a9 613d c215 46a3 6137 09fa
+7459 c304 0303 9967 aa68 7d22 15be 9175
+55f7 5426 a5d9 6159 9739 a678 66e4 c474
+061d 2c69 d24d 4005 5433 c72b 80ca f6b3
+10a4 d159 e60b c821 dd1d 98a1 7ed3 fe6b
+dd98 c94c 0d0a 4daf d58f 0f90 952f 6868
+8268 843e fc45 c9f0 f238 76e3 3061 8017
+9ecd 5dba 5da1 2b09 140d 4fd2 0e14 439c
+bfee c284 67df f246 0adc 0350 ebab 02a9
+9b2b 7559 9003 5887 1fd3 5518 ff65 8b11
+a75c b223 398a 81e7 d5ed d6e6 f183 0b6e
+3628 eb7d 2042 2ace 5279 1597 9124 7f0b
+fbdd 3acc 1e0d 7dc4 da7a e44e 0e43 e2b6
+1c19 ab27 860c 8933 f6e0 9038 3304 7dad
+214d 706b 4813 dcb2 9b4f d781 900b 23b6
+1c91 36dc a5f6 eff9 af0c aaff 06f1 48e5
+4433 2000 00
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz
new file mode 100644
index 000000000..3f8199dfb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/test.gz
@@ -0,0 +1,163 @@
+1f8b 0808 2aa1 c754 0003 7465 7874 00b5
+5b5d 939b 3816 7def 5fa1 ea79 99a9 4d3c
+601b db3c f4c3 5426 55f3 309b ca6e 7ab7
+6a9e 281a d436 150c 04c4 a4bd 5df3 dff7
+4a88 361f 025d 094f 1e92 34e8 1cae 8ea4
+ab7b 04fd 03f9 ede3 ef9f c989 b122 28e9
+b79a 562c 88eb 3264 499e 05e7 242a f38a
+4679 1657 e4f1 44c9 6f8f 8f9f 896c 46d2
+90d1 2c4a 6845 928c 749b aeee 7e20 8f7f
+7cfe 8861 adea f339 2c2f 77fa a6af a730
+8b53 5a3e dcff 7cff ee5b 1d66 2c49 e9c3
+bdb3 f2ee ff22 ce12 027f 3101 9621 80ee
+7659 90a8 28af 3366 8eeb 2042 f887 558b
+7553 d158 a8a7 a4b1 d450 7259 2a69 84ee
+e28a e4e7 3365 6512 dd40 d429 2e1b 6527
+b96c e5ed 10da 6a6c 4c31 0043 cbf2 7213
+9915 4c96 22ab 9816 48dc d02d 10d8 8440
+050d ca30 3bd2 db89 ace2 5b22 b592 6fa9
+e092 74a9 ec46 3403 0216 9647 7a8b cc3c
+c565 29ba 9a6b 81e0 2de1 02b1 cd28 3a60
+f8b9 ca53 5a2d 2f1c 2698 2c44 9e62 b294
+f84a 6729 b029 4107 7a2c c3e2 b458 5a05
+8b85 ac2a 164b 491b 2a4b 394d c01d d889
+86c5 6225 c724 1642 2a48 2c75 144c 9632
+1a60 3ba8 8ac1 ed68 f96a 57f2 5868 a9e6
+b194 b325 b354 d40c 7e05 1665 0e45 dc89
+d68a bdca dd38 fbd5 7aef dd84 90cb e21e
+bcc3 6ab7 59df 8690 336e 9cc3 7eb5 396c
+8df5 eeb0 425c 7bff 70d8 ad3c 47fe 712d
+46a0 4fe8 fa60 96c7 16bc 4afe 4783 a70b
+a30a dfcd ef09 cf2d eeab cd76 07af 74d8
+d7fb 26b6 1a81 524c 6a0c 6a16 a675 cd9d
+a67a abac 0c07 e98f d158 ac0c 5827 3c29
+c694 819d 9144 0fb1 34ba 6604 6889 4c2c
+edb4 4e73 2674 4e2c 1cce cab1 9ac0 4dd4
+427a d359 ad26 fca4 4629 2d6a 81f5 3427
+31d6 0c6b 32f5 ca4d 5942 8c7e 7aac a587
+3423 3051 0fed 1667 959b f477 1ad5 1038
+2b33 6802 c7aa 6560 fb26 b59a b16a 334a
+a150 c6ae 0e0b c5ea 83f4 6f93 da4c f8ae
+195d b408 537b 8644 6215 c119 b149 41d4
+0e6a 460f 1dc0 c267 e1c1 5851 d08e 6a52
+9749 1f34 230d 0283 334c 6bdf b527 f017
+1368 1866 0cd0 66bb 3d1c b07a 619c 4e15
+b09c 8529 7914 7f67 f5f9 8996 247f ee39
+9e8a 9cc3 982a 8d4e 0b17 4fa6 e59d e2de
+6b94 c7d0 edb5 e3dc bf53 4ac3 ff93 c70f
+f7b0 8728 e3ac 0ac8 9c74 c292 3537 359e
+6ccc 3030 65a3 0638 5786 87f9 96b0 79dc
+8c31 1bb7 9d73 6673 1169 ad99 2918 ad85
+de9c e914 195b 2dbd 2e08 8cb1 3fb3 62c0
+eb84 7368 5ab1 d456 0ba1 1812 6868 d22c
+f046 9269 6d1a 46b0 91e3 c2c9 a587 5939
+356b 1673 e1f4 5e0d 2ddf d870 1988 8800
+1bdb 352b 0623 0911 860d 239f c279 e1a4
+c300 0d3d 9b05 1e2d 19ca b5e9 0453 1a30
+bd5c 3898 8171 33c4 a245 d25a 379d 4023
+27a6 1747 0fc1 bb37 3328 5a16 9d7f d3a9
+32f4 637a 51b4 0823 0b67 8c46 2b83 3071
+3a71 148e 4caf 0f06 84f4 71ce d65f 4021
+7c98 e31d 9650 341c bb2d 52b1 9e27 5b6f
+f79d 7758 5ae1 a6fc 1c5c 8f68 05cd 8b3a
+685f 7a75 5d5d 5d81 a703 1252 5d2a 46cf
+e4c3 e7ff 1096 9cc1 3515 3463 dc35 0d3f
+1c9d 666c 8dde 740b 1819 6f18 d931 2ff3
+9a25 1938 af4f 6f16 b373 919d 4246 a2ba
+2c21 9ef4 42e8 4b52 b151 309d f6c7 b03e
+d23b c58d bd33 7cf4 397c 099e e38a fc33
+7c49 cef5 b963 7173 e83d 7986 7124 31ad
+a232 2958 5e8e 2568 f1fd 47b6 570f aebf
+1e3e 91f3 8a9b 9f0c 1ff5 06ec 3feb edf2
+7a34 e230 6992 1834 0bce f49c 432d d498
+db7f cbab a4b9 2acc f1d8 1bcf 73f4 4350
+b7f1 569b c3de f1fc 35fd 87b3 1f86 068b
+bc64 019f 66ed fc20 5ff8 a566 e681 2630
+91db c610 6116 5152 67c9 0ba1 451e 9de6
+e6a4 82b8 1fac a281 bbda aed7 9bdd c1df
+1e36 3b88 7624 e49f 49c9 ea30 edf7 efbf
+cd45 9c8c 4a86 7e60 ca26 de6a eb6e f707
+dfe5 2a1e 3a71 c9a5 1ec4 1974 290e d23c
+ff5a 17c1 7398 a435 0c47 bbc0 41c4 eb8c
+fef5 d397 f75f 7e25 4d53 d236 ed86 8a22
+edac 7154 7b47 1735 225a 7d94 d8e8 da76
+7b45 54f4 cf30 ad43 587c dd4f 05d2 34e9
+7e63 dfde 21cf 3964 cd34 2512 0497 2051
+e590 9c68 5433 aa8a 5747 df9e 3ae1 21af
+ddbd c671 c596 698b f696 a017 81c5 2725
+d660 5334 df70 89bb 3641 8839 45d6 1bc5
+9449 f308 966c 05d8 f048 83e8 44a3 af45
+9e64 0c33 837e 14bf 9871 bdfb 1349 20ff
+c12c e5f3 e84a 0549 e5bd cc31 f218 45ec
+d650 46c6 d0aa cebe 2a17 8761 606f a9c8
+12af 5ae4 430a 0815 76ab ee6a 6783 6365
+d186 6f87 a55c 504f 17be 1124 2561 9742
+b9a6 e69f a148 06b3 8057 fe98 87fb a8a4
+21e3 8706 9e7f 30c5 42ec 1594 27e2 6ba4
+ad31 38c9 00e8 af1d 5320 2bc3 ace2 27e9
+00df ba9e 29bc ceae 4fd6 8d63 92c5 5080
+65c7 e029 64d1 2968 7ecd e8d2 9f0d ff92
+0bb4 1259 5234 242d 6ef8 8b49 5798 7e7c
+31cf 5664 5163 92f9 dcb6 8cce bf31 dd72
+3e91 1117 5234 29d2 359d 3dcd 8b99 fe74
+799b 28cd bc69 9afc 784d 126d 1284 95d6
+34f9 c978 e234 9ca6 3345 a046 5363 bd00
+ef2f c55b 1088 d136 c518 0fef b79a d690
+6dc2 228c 1276 11c9 feed 0759 ddbf 8db3
+686b 3086 036e cdd6 3505 7377 fc7b 53c3
+0ea5 343b b2d3 a052 6d27 e4f7 3061 bc3f
+b07b 3fc9 eed1 d8b8 5ff2 1166 bd92 204c
+f63e 5270 f971 5085 e722 a573 9bb1 6c41
+5a08 a627 4a72 ed2e 3c81 db38 dbbd bee6
+4a32 a8de 9238 284a 9ae6 613c 7a73 ade8
+996c 7a7d 815d d267 5a96 72ec 4292 e5d9
+7b71 c8c0 5d72 454b d8ab 5640 9480 16bc
+f6e2 439b 444d 0dc7 dd7b cd62 4889 316c
+6c4f 3495 e38e dacc 6603 47a8 368b d7cf
+0569 3445 49c0 0f1e 9af2 549e b38c aab2
+ced1 84d8 b805 58df cbf1 4334 337b 0c70
+1dcf 37ea cc6c 473a d1bf 03b7 16a5 75cc
+073e 4af3 8cb6 0535 94e6 2bba 6a7f f89e
+b013 0c32 4c8c ab06 883d a71f 9141 af79
+8f11 8598 8434 f373 a2c7 f2a6 f978 4920
+2e6a d978 bbd6 e753 591e 778a 88ce 6f9b
+ffd2 6ec9 3cf4 6b99 c88b 0289 e323 4543
+a80a 8450 fade cc3e 4ebb ffcf a147 75c0
+c659 6df6 fb1b 9035 47c6 9b95 b7f1 6fc1
+26e8 76eb dd6a bbdb d8f1 3515 8303 c3bb
+9af5 16b3 1cb2 82d8 e3a7 88a2 8490 9971
+5048 4800 b68e 98e0 d74c f509 14ac 54d3
+1e75 6a88 c914 d596 12b0 7017 f710 5750
+2831 fa24 d42c 7d8d ad97 f9c1 ded7 8f9e
+a2dd 1c87 88a1 b39f 2980 27a0 e730 8147
+6661 16f1 ad57 a63e f1a6 4521 5296 b3e4
+59d6 0895 daa7 fede 5c24 df7a e6a7 a299
+d88e c467 46a4 4703 1e28 e787 41ed 8e15
+9779 51c0 96d5 6ba4 dc97 10d1 2872 a11e
+356f 930d f123 1f6b 8ab7 2018 3b5f 04a6
+c964 aaa5 d107 232c 906a 9427 d7f8 2cfb
+6875 cfb6 761d 6cf8 4ac3 a30a 5b66 2aa3
+e8a7 32d3 4c5b 55dc 659d d2e0 7a0c 8f3e
+bc27 1ca8 39b3 c771 2b56 0f0a f82a 5a35
+f945 880a eb5a f5ae fff6 bca3 c572 2bde
+d189 048a 58bc 0557 91ff 3538 aac7 b135
+6fc6 27f8 fa25 8c71 bf4b b854 c67f c340
+4d10 2f1f a929 62f1 8bb7 8b87 eaca 0eda
+9a4b 3b1e ab1e a1eb 2116 bce2 ade7 b004
+114b fd0a 997d fba9 a157 d41e 1a84 2a69
+b547 1d83 ccfc 61b0 4388 db22 5dd5 d9f7
+3261 b01f b507 33aa d027 5847 1976 a2dd
+d6f1 77da 5865 26fe 30aa 5d13 46cf fd8d
+6022 70f2 915b 38de 1cc4 3c17 25cc 854a
+bc4b 6d8f 9ce8 4b01 c621 e665 22b8 72d2
+7c8e 48c2 4afc d41c b7c1 08c2 34ba 48a7
+de1e c149 d580 07f6 2bf8 4b59 0e29 bba3
+9168 66fb 69a2 0b78 7558 c214 904d df3e
+2ef8 2512 5f09 b4b7 a1f6 a5ec 3be5 6a44
+6558 a887 5143 a9d8 6ee6 11af edf5 877b
+d71b 7ca2 245e 1bbb db1b 9179 3724 f346
+19c5 9ecb bf25 9729 9948 997d 42fe 7ad0
+84a1 c992 238e b55d 8f54 53c0 b90d d568
+1fb4 a6ba 1dd3 e813 017b 2643 aae1 c8f3
+41f3 168d 7bf3 71df feee ff2d f9e8 431a
+5200 00
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text
new file mode 100644
index 000000000..f3d8c3784
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/testdata/text
@@ -0,0 +1,322 @@
+# HELP http_request_duration_microseconds The HTTP request latencies in microseconds.
+# TYPE http_request_duration_microseconds summary
+http_request_duration_microseconds{handler="/",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/"} 0
+http_request_duration_microseconds_count{handler="/"} 0
+http_request_duration_microseconds{handler="/alerts",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/alerts",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/alerts",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/alerts"} 0
+http_request_duration_microseconds_count{handler="/alerts"} 0
+http_request_duration_microseconds{handler="/api/metrics",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/api/metrics",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/api/metrics",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/api/metrics"} 0
+http_request_duration_microseconds_count{handler="/api/metrics"} 0
+http_request_duration_microseconds{handler="/api/query",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/api/query",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/api/query",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/api/query"} 0
+http_request_duration_microseconds_count{handler="/api/query"} 0
+http_request_duration_microseconds{handler="/api/query_range",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/api/query_range",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/api/query_range",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/api/query_range"} 0
+http_request_duration_microseconds_count{handler="/api/query_range"} 0
+http_request_duration_microseconds{handler="/api/targets",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/api/targets",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/api/targets",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/api/targets"} 0
+http_request_duration_microseconds_count{handler="/api/targets"} 0
+http_request_duration_microseconds{handler="/consoles/",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/consoles/",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/consoles/",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/consoles/"} 0
+http_request_duration_microseconds_count{handler="/consoles/"} 0
+http_request_duration_microseconds{handler="/graph",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/graph",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/graph",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/graph"} 0
+http_request_duration_microseconds_count{handler="/graph"} 0
+http_request_duration_microseconds{handler="/heap",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/heap",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/heap",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/heap"} 0
+http_request_duration_microseconds_count{handler="/heap"} 0
+http_request_duration_microseconds{handler="/static/",quantile="0.5"} 0
+http_request_duration_microseconds{handler="/static/",quantile="0.9"} 0
+http_request_duration_microseconds{handler="/static/",quantile="0.99"} 0
+http_request_duration_microseconds_sum{handler="/static/"} 0
+http_request_duration_microseconds_count{handler="/static/"} 0
+http_request_duration_microseconds{handler="prometheus",quantile="0.5"} 1307.275
+http_request_duration_microseconds{handler="prometheus",quantile="0.9"} 1858.632
+http_request_duration_microseconds{handler="prometheus",quantile="0.99"} 3087.384
+http_request_duration_microseconds_sum{handler="prometheus"} 179886.5000000001
+http_request_duration_microseconds_count{handler="prometheus"} 119
+# HELP http_request_size_bytes The HTTP request sizes in bytes.
+# TYPE http_request_size_bytes summary
+http_request_size_bytes{handler="/",quantile="0.5"} 0
+http_request_size_bytes{handler="/",quantile="0.9"} 0
+http_request_size_bytes{handler="/",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/"} 0
+http_request_size_bytes_count{handler="/"} 0
+http_request_size_bytes{handler="/alerts",quantile="0.5"} 0
+http_request_size_bytes{handler="/alerts",quantile="0.9"} 0
+http_request_size_bytes{handler="/alerts",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/alerts"} 0
+http_request_size_bytes_count{handler="/alerts"} 0
+http_request_size_bytes{handler="/api/metrics",quantile="0.5"} 0
+http_request_size_bytes{handler="/api/metrics",quantile="0.9"} 0
+http_request_size_bytes{handler="/api/metrics",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/api/metrics"} 0
+http_request_size_bytes_count{handler="/api/metrics"} 0
+http_request_size_bytes{handler="/api/query",quantile="0.5"} 0
+http_request_size_bytes{handler="/api/query",quantile="0.9"} 0
+http_request_size_bytes{handler="/api/query",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/api/query"} 0
+http_request_size_bytes_count{handler="/api/query"} 0
+http_request_size_bytes{handler="/api/query_range",quantile="0.5"} 0
+http_request_size_bytes{handler="/api/query_range",quantile="0.9"} 0
+http_request_size_bytes{handler="/api/query_range",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/api/query_range"} 0
+http_request_size_bytes_count{handler="/api/query_range"} 0
+http_request_size_bytes{handler="/api/targets",quantile="0.5"} 0
+http_request_size_bytes{handler="/api/targets",quantile="0.9"} 0
+http_request_size_bytes{handler="/api/targets",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/api/targets"} 0
+http_request_size_bytes_count{handler="/api/targets"} 0
+http_request_size_bytes{handler="/consoles/",quantile="0.5"} 0
+http_request_size_bytes{handler="/consoles/",quantile="0.9"} 0
+http_request_size_bytes{handler="/consoles/",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/consoles/"} 0
+http_request_size_bytes_count{handler="/consoles/"} 0
+http_request_size_bytes{handler="/graph",quantile="0.5"} 0
+http_request_size_bytes{handler="/graph",quantile="0.9"} 0
+http_request_size_bytes{handler="/graph",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/graph"} 0
+http_request_size_bytes_count{handler="/graph"} 0
+http_request_size_bytes{handler="/heap",quantile="0.5"} 0
+http_request_size_bytes{handler="/heap",quantile="0.9"} 0
+http_request_size_bytes{handler="/heap",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/heap"} 0
+http_request_size_bytes_count{handler="/heap"} 0
+http_request_size_bytes{handler="/static/",quantile="0.5"} 0
+http_request_size_bytes{handler="/static/",quantile="0.9"} 0
+http_request_size_bytes{handler="/static/",quantile="0.99"} 0
+http_request_size_bytes_sum{handler="/static/"} 0
+http_request_size_bytes_count{handler="/static/"} 0
+http_request_size_bytes{handler="prometheus",quantile="0.5"} 291
+http_request_size_bytes{handler="prometheus",quantile="0.9"} 291
+http_request_size_bytes{handler="prometheus",quantile="0.99"} 291
+http_request_size_bytes_sum{handler="prometheus"} 34488
+http_request_size_bytes_count{handler="prometheus"} 119
+# HELP http_requests_total Total number of HTTP requests made.
+# TYPE http_requests_total counter
+http_requests_total{code="200",handler="prometheus",method="get"} 119
+# HELP http_response_size_bytes The HTTP response sizes in bytes.
+# TYPE http_response_size_bytes summary
+http_response_size_bytes{handler="/",quantile="0.5"} 0
+http_response_size_bytes{handler="/",quantile="0.9"} 0
+http_response_size_bytes{handler="/",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/"} 0
+http_response_size_bytes_count{handler="/"} 0
+http_response_size_bytes{handler="/alerts",quantile="0.5"} 0
+http_response_size_bytes{handler="/alerts",quantile="0.9"} 0
+http_response_size_bytes{handler="/alerts",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/alerts"} 0
+http_response_size_bytes_count{handler="/alerts"} 0
+http_response_size_bytes{handler="/api/metrics",quantile="0.5"} 0
+http_response_size_bytes{handler="/api/metrics",quantile="0.9"} 0
+http_response_size_bytes{handler="/api/metrics",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/api/metrics"} 0
+http_response_size_bytes_count{handler="/api/metrics"} 0
+http_response_size_bytes{handler="/api/query",quantile="0.5"} 0
+http_response_size_bytes{handler="/api/query",quantile="0.9"} 0
+http_response_size_bytes{handler="/api/query",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/api/query"} 0
+http_response_size_bytes_count{handler="/api/query"} 0
+http_response_size_bytes{handler="/api/query_range",quantile="0.5"} 0
+http_response_size_bytes{handler="/api/query_range",quantile="0.9"} 0
+http_response_size_bytes{handler="/api/query_range",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/api/query_range"} 0
+http_response_size_bytes_count{handler="/api/query_range"} 0
+http_response_size_bytes{handler="/api/targets",quantile="0.5"} 0
+http_response_size_bytes{handler="/api/targets",quantile="0.9"} 0
+http_response_size_bytes{handler="/api/targets",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/api/targets"} 0
+http_response_size_bytes_count{handler="/api/targets"} 0
+http_response_size_bytes{handler="/consoles/",quantile="0.5"} 0
+http_response_size_bytes{handler="/consoles/",quantile="0.9"} 0
+http_response_size_bytes{handler="/consoles/",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/consoles/"} 0
+http_response_size_bytes_count{handler="/consoles/"} 0
+http_response_size_bytes{handler="/graph",quantile="0.5"} 0
+http_response_size_bytes{handler="/graph",quantile="0.9"} 0
+http_response_size_bytes{handler="/graph",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/graph"} 0
+http_response_size_bytes_count{handler="/graph"} 0
+http_response_size_bytes{handler="/heap",quantile="0.5"} 0
+http_response_size_bytes{handler="/heap",quantile="0.9"} 0
+http_response_size_bytes{handler="/heap",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/heap"} 0
+http_response_size_bytes_count{handler="/heap"} 0
+http_response_size_bytes{handler="/static/",quantile="0.5"} 0
+http_response_size_bytes{handler="/static/",quantile="0.9"} 0
+http_response_size_bytes{handler="/static/",quantile="0.99"} 0
+http_response_size_bytes_sum{handler="/static/"} 0
+http_response_size_bytes_count{handler="/static/"} 0
+http_response_size_bytes{handler="prometheus",quantile="0.5"} 2049
+http_response_size_bytes{handler="prometheus",quantile="0.9"} 2058
+http_response_size_bytes{handler="prometheus",quantile="0.99"} 2064
+http_response_size_bytes_sum{handler="prometheus"} 247001
+http_response_size_bytes_count{handler="prometheus"} 119
+# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
+# TYPE process_cpu_seconds_total counter
+process_cpu_seconds_total 0.55
+# HELP go_goroutines Number of goroutines that currently exist.
+# TYPE go_goroutines gauge
+go_goroutines 70
+# HELP process_max_fds Maximum number of open file descriptors.
+# TYPE process_max_fds gauge
+process_max_fds 8192
+# HELP process_open_fds Number of open file descriptors.
+# TYPE process_open_fds gauge
+process_open_fds 29
+# HELP process_resident_memory_bytes Resident memory size in bytes.
+# TYPE process_resident_memory_bytes gauge
+process_resident_memory_bytes 5.3870592e+07
+# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
+# TYPE process_start_time_seconds gauge
+process_start_time_seconds 1.42236894836e+09
+# HELP process_virtual_memory_bytes Virtual memory size in bytes.
+# TYPE process_virtual_memory_bytes gauge
+process_virtual_memory_bytes 5.41478912e+08
+# HELP prometheus_dns_sd_lookup_failures_total The number of DNS-SD lookup failures.
+# TYPE prometheus_dns_sd_lookup_failures_total counter
+prometheus_dns_sd_lookup_failures_total 0
+# HELP prometheus_dns_sd_lookups_total The number of DNS-SD lookups.
+# TYPE prometheus_dns_sd_lookups_total counter
+prometheus_dns_sd_lookups_total 7
+# HELP prometheus_evaluator_duration_milliseconds The duration for all evaluations to execute.
+# TYPE prometheus_evaluator_duration_milliseconds summary
+prometheus_evaluator_duration_milliseconds{quantile="0.01"} 0
+prometheus_evaluator_duration_milliseconds{quantile="0.05"} 0
+prometheus_evaluator_duration_milliseconds{quantile="0.5"} 0
+prometheus_evaluator_duration_milliseconds{quantile="0.9"} 1
+prometheus_evaluator_duration_milliseconds{quantile="0.99"} 1
+prometheus_evaluator_duration_milliseconds_sum 12
+prometheus_evaluator_duration_milliseconds_count 23
+# HELP prometheus_local_storage_checkpoint_duration_milliseconds The duration (in milliseconds) it took to checkpoint in-memory metrics and head chunks.
+# TYPE prometheus_local_storage_checkpoint_duration_milliseconds gauge
+prometheus_local_storage_checkpoint_duration_milliseconds 0
+# HELP prometheus_local_storage_chunk_ops_total The total number of chunk operations by their type.
+# TYPE prometheus_local_storage_chunk_ops_total counter
+prometheus_local_storage_chunk_ops_total{type="create"} 598
+prometheus_local_storage_chunk_ops_total{type="persist"} 174
+prometheus_local_storage_chunk_ops_total{type="pin"} 920
+prometheus_local_storage_chunk_ops_total{type="transcode"} 415
+prometheus_local_storage_chunk_ops_total{type="unpin"} 920
+# HELP prometheus_local_storage_indexing_batch_latency_milliseconds Quantiles for batch indexing latencies in milliseconds.
+# TYPE prometheus_local_storage_indexing_batch_latency_milliseconds summary
+prometheus_local_storage_indexing_batch_latency_milliseconds{quantile="0.5"} 0
+prometheus_local_storage_indexing_batch_latency_milliseconds{quantile="0.9"} 0
+prometheus_local_storage_indexing_batch_latency_milliseconds{quantile="0.99"} 0
+prometheus_local_storage_indexing_batch_latency_milliseconds_sum 0
+prometheus_local_storage_indexing_batch_latency_milliseconds_count 1
+# HELP prometheus_local_storage_indexing_batch_sizes Quantiles for indexing batch sizes (number of metrics per batch).
+# TYPE prometheus_local_storage_indexing_batch_sizes summary
+prometheus_local_storage_indexing_batch_sizes{quantile="0.5"} 2
+prometheus_local_storage_indexing_batch_sizes{quantile="0.9"} 2
+prometheus_local_storage_indexing_batch_sizes{quantile="0.99"} 2
+prometheus_local_storage_indexing_batch_sizes_sum 2
+prometheus_local_storage_indexing_batch_sizes_count 1
+# HELP prometheus_local_storage_indexing_queue_capacity The capacity of the indexing queue.
+# TYPE prometheus_local_storage_indexing_queue_capacity gauge
+prometheus_local_storage_indexing_queue_capacity 16384
+# HELP prometheus_local_storage_indexing_queue_length The number of metrics waiting to be indexed.
+# TYPE prometheus_local_storage_indexing_queue_length gauge
+prometheus_local_storage_indexing_queue_length 0
+# HELP prometheus_local_storage_ingested_samples_total The total number of samples ingested.
+# TYPE prometheus_local_storage_ingested_samples_total counter
+prometheus_local_storage_ingested_samples_total 30473
+# HELP prometheus_local_storage_invalid_preload_requests_total The total number of preload requests referring to a non-existent series. This is an indication of outdated label indexes.
+# TYPE prometheus_local_storage_invalid_preload_requests_total counter
+prometheus_local_storage_invalid_preload_requests_total 0
+# HELP prometheus_local_storage_memory_chunkdescs The current number of chunk descriptors in memory.
+# TYPE prometheus_local_storage_memory_chunkdescs gauge
+prometheus_local_storage_memory_chunkdescs 1059
+# HELP prometheus_local_storage_memory_chunks The current number of chunks in memory, excluding cloned chunks (i.e. chunks without a descriptor).
+# TYPE prometheus_local_storage_memory_chunks gauge
+prometheus_local_storage_memory_chunks 1020
+# HELP prometheus_local_storage_memory_series The current number of series in memory.
+# TYPE prometheus_local_storage_memory_series gauge
+prometheus_local_storage_memory_series 424
+# HELP prometheus_local_storage_persist_latency_microseconds A summary of latencies for persisting each chunk.
+# TYPE prometheus_local_storage_persist_latency_microseconds summary
+prometheus_local_storage_persist_latency_microseconds{quantile="0.5"} 30.377
+prometheus_local_storage_persist_latency_microseconds{quantile="0.9"} 203.539
+prometheus_local_storage_persist_latency_microseconds{quantile="0.99"} 2626.463
+prometheus_local_storage_persist_latency_microseconds_sum 20424.415
+prometheus_local_storage_persist_latency_microseconds_count 174
+# HELP prometheus_local_storage_persist_queue_capacity The total capacity of the persist queue.
+# TYPE prometheus_local_storage_persist_queue_capacity gauge
+prometheus_local_storage_persist_queue_capacity 1024
+# HELP prometheus_local_storage_persist_queue_length The current number of chunks waiting in the persist queue.
+# TYPE prometheus_local_storage_persist_queue_length gauge
+prometheus_local_storage_persist_queue_length 0
+# HELP prometheus_local_storage_series_ops_total The total number of series operations by their type.
+# TYPE prometheus_local_storage_series_ops_total counter
+prometheus_local_storage_series_ops_total{type="create"} 2
+prometheus_local_storage_series_ops_total{type="maintenance_in_memory"} 11
+# HELP prometheus_notifications_latency_milliseconds Latency quantiles for sending alert notifications (not including dropped notifications).
+# TYPE prometheus_notifications_latency_milliseconds summary
+prometheus_notifications_latency_milliseconds{quantile="0.5"} 0
+prometheus_notifications_latency_milliseconds{quantile="0.9"} 0
+prometheus_notifications_latency_milliseconds{quantile="0.99"} 0
+prometheus_notifications_latency_milliseconds_sum 0
+prometheus_notifications_latency_milliseconds_count 0
+# HELP prometheus_notifications_queue_capacity The capacity of the alert notifications queue.
+# TYPE prometheus_notifications_queue_capacity gauge
+prometheus_notifications_queue_capacity 100
+# HELP prometheus_notifications_queue_length The number of alert notifications in the queue.
+# TYPE prometheus_notifications_queue_length gauge
+prometheus_notifications_queue_length 0
+# HELP prometheus_rule_evaluation_duration_milliseconds The duration for a rule to execute.
+# TYPE prometheus_rule_evaluation_duration_milliseconds summary
+prometheus_rule_evaluation_duration_milliseconds{rule_type="alerting",quantile="0.5"} 0
+prometheus_rule_evaluation_duration_milliseconds{rule_type="alerting",quantile="0.9"} 0
+prometheus_rule_evaluation_duration_milliseconds{rule_type="alerting",quantile="0.99"} 2
+prometheus_rule_evaluation_duration_milliseconds_sum{rule_type="alerting"} 12
+prometheus_rule_evaluation_duration_milliseconds_count{rule_type="alerting"} 115
+prometheus_rule_evaluation_duration_milliseconds{rule_type="recording",quantile="0.5"} 0
+prometheus_rule_evaluation_duration_milliseconds{rule_type="recording",quantile="0.9"} 0
+prometheus_rule_evaluation_duration_milliseconds{rule_type="recording",quantile="0.99"} 3
+prometheus_rule_evaluation_duration_milliseconds_sum{rule_type="recording"} 15
+prometheus_rule_evaluation_duration_milliseconds_count{rule_type="recording"} 115
+# HELP prometheus_rule_evaluation_failures_total The total number of rule evaluation failures.
+# TYPE prometheus_rule_evaluation_failures_total counter
+prometheus_rule_evaluation_failures_total 0
+# HELP prometheus_samples_queue_capacity Capacity of the queue for unwritten samples.
+# TYPE prometheus_samples_queue_capacity gauge
+prometheus_samples_queue_capacity 4096
+# HELP prometheus_samples_queue_length Current number of items in the queue for unwritten samples. Each item comprises all samples exposed by one target as one metric family (i.e. metrics of the same name).
+# TYPE prometheus_samples_queue_length gauge
+prometheus_samples_queue_length 0
+# HELP prometheus_target_interval_length_seconds Actual intervals between scrapes.
+# TYPE prometheus_target_interval_length_seconds summary
+prometheus_target_interval_length_seconds{interval="15s",quantile="0.01"} 14
+prometheus_target_interval_length_seconds{interval="15s",quantile="0.05"} 14
+prometheus_target_interval_length_seconds{interval="15s",quantile="0.5"} 15
+prometheus_target_interval_length_seconds{interval="15s",quantile="0.9"} 15
+prometheus_target_interval_length_seconds{interval="15s",quantile="0.99"} 15
+prometheus_target_interval_length_seconds_sum{interval="15s"} 175
+prometheus_target_interval_length_seconds_count{interval="15s"} 12
+prometheus_target_interval_length_seconds{interval="1s",quantile="0.01"} 0
+prometheus_target_interval_length_seconds{interval="1s",quantile="0.05"} 0
+prometheus_target_interval_length_seconds{interval="1s",quantile="0.5"} 0
+prometheus_target_interval_length_seconds{interval="1s",quantile="0.9"} 1
+prometheus_target_interval_length_seconds{interval="1s",quantile="0.99"} 1
+prometheus_target_interval_length_seconds_sum{interval="1s"} 55
+prometheus_target_interval_length_seconds_count{interval="1s"} 117
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create.go
new file mode 100644
index 000000000..7d652dc7c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create.go
@@ -0,0 +1,305 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math"
+ "strings"
+
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model"
+)
+
+// MetricFamilyToText converts a MetricFamily proto message into text format and
+// writes the resulting lines to 'out'. It returns the number of bytes written
+// and any error encountered. This function does not perform checks on the
+// content of the metric and label names, i.e. invalid metric or label names
+// will result in invalid text format output.
+// This method fulfills the type 'prometheus.encoder'.
+func MetricFamilyToText(out io.Writer, in *dto.MetricFamily) (int, error) {
+ var written int
+
+ // Fail-fast checks.
+ if len(in.Metric) == 0 {
+ return written, fmt.Errorf("MetricFamily has no metrics: %s", in)
+ }
+ name := in.GetName()
+ if name == "" {
+ return written, fmt.Errorf("MetricFamily has no name: %s", in)
+ }
+
+ // Comments, first HELP, then TYPE.
+ if in.Help != nil {
+ n, err := fmt.Fprintf(
+ out, "# HELP %s %s\n",
+ name, escapeString(*in.Help, false),
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+ }
+ metricType := in.GetType()
+ n, err := fmt.Fprintf(
+ out, "# TYPE %s %s\n",
+ name, strings.ToLower(metricType.String()),
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+
+ // Finally the samples, one line for each.
+ for _, metric := range in.Metric {
+ switch metricType {
+ case dto.MetricType_COUNTER:
+ if metric.Counter == nil {
+ return written, fmt.Errorf(
+ "expected counter in metric %s %s", name, metric,
+ )
+ }
+ n, err = writeSample(
+ name, metric, "", "",
+ metric.Counter.GetValue(),
+ out,
+ )
+ case dto.MetricType_GAUGE:
+ if metric.Gauge == nil {
+ return written, fmt.Errorf(
+ "expected gauge in metric %s %s", name, metric,
+ )
+ }
+ n, err = writeSample(
+ name, metric, "", "",
+ metric.Gauge.GetValue(),
+ out,
+ )
+ case dto.MetricType_UNTYPED:
+ if metric.Untyped == nil {
+ return written, fmt.Errorf(
+ "expected untyped in metric %s %s", name, metric,
+ )
+ }
+ n, err = writeSample(
+ name, metric, "", "",
+ metric.Untyped.GetValue(),
+ out,
+ )
+ case dto.MetricType_SUMMARY:
+ if metric.Summary == nil {
+ return written, fmt.Errorf(
+ "expected summary in metric %s %s", name, metric,
+ )
+ }
+ for _, q := range metric.Summary.Quantile {
+ n, err = writeSample(
+ name, metric,
+ model.QuantileLabel, fmt.Sprint(q.GetQuantile()),
+ q.GetValue(),
+ out,
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+ }
+ n, err = writeSample(
+ name+"_sum", metric, "", "",
+ metric.Summary.GetSampleSum(),
+ out,
+ )
+ if err != nil {
+ return written, err
+ }
+ written += n
+ n, err = writeSample(
+ name+"_count", metric, "", "",
+ float64(metric.Summary.GetSampleCount()),
+ out,
+ )
+ case dto.MetricType_HISTOGRAM:
+ if metric.Histogram == nil {
+ return written, fmt.Errorf(
+ "expected histogram in metric %s %s", name, metric,
+ )
+ }
+ infSeen := false
+ for _, q := range metric.Histogram.Bucket {
+ n, err = writeSample(
+ name+"_bucket", metric,
+ model.BucketLabel, fmt.Sprint(q.GetUpperBound()),
+ float64(q.GetCumulativeCount()),
+ out,
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+ if math.IsInf(q.GetUpperBound(), +1) {
+ infSeen = true
+ }
+ }
+ if !infSeen {
+ n, err = writeSample(
+ name+"_bucket", metric,
+ model.BucketLabel, "+Inf",
+ float64(metric.Histogram.GetSampleCount()),
+ out,
+ )
+ if err != nil {
+ return written, err
+ }
+ written += n
+ }
+ n, err = writeSample(
+ name+"_sum", metric, "", "",
+ metric.Histogram.GetSampleSum(),
+ out,
+ )
+ if err != nil {
+ return written, err
+ }
+ written += n
+ n, err = writeSample(
+ name+"_count", metric, "", "",
+ float64(metric.Histogram.GetSampleCount()),
+ out,
+ )
+ default:
+ return written, fmt.Errorf(
+ "unexpected type in metric %s %s", name, metric,
+ )
+ }
+ written += n
+ if err != nil {
+ return written, err
+ }
+ }
+ return written, nil
+}
+
+// writeSample writes a single sample in text format to out, given the metric
+// name, the metric proto message itself, optionally an additional label name
+// and value (use empty strings if not required), and the value. The function
+// returns the number of bytes written and any error encountered.
+func writeSample(
+ name string,
+ metric *dto.Metric,
+ additionalLabelName, additionalLabelValue string,
+ value float64,
+ out io.Writer,
+) (int, error) {
+ var written int
+ n, err := fmt.Fprint(out, name)
+ written += n
+ if err != nil {
+ return written, err
+ }
+ n, err = labelPairsToText(
+ metric.Label,
+ additionalLabelName, additionalLabelValue,
+ out,
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+ n, err = fmt.Fprintf(out, " %v", value)
+ written += n
+ if err != nil {
+ return written, err
+ }
+ if metric.TimestampMs != nil {
+ n, err = fmt.Fprintf(out, " %v", *metric.TimestampMs)
+ written += n
+ if err != nil {
+ return written, err
+ }
+ }
+ n, err = out.Write([]byte{'\n'})
+ written += n
+ if err != nil {
+ return written, err
+ }
+ return written, nil
+}
+
+// labelPairsToText converts a slice of LabelPair proto messages plus the
+// explicitly given additional label pair into text formatted as required by the
+// text format and writes it to 'out'. An empty slice in combination with an
+// empty string 'additionalLabelName' results in nothing being
+// written. Otherwise, the label pairs are written, escaped as required by the
+// text format, and enclosed in '{...}'. The function returns the number of
+// bytes written and any error encountered.
+func labelPairsToText(
+ in []*dto.LabelPair,
+ additionalLabelName, additionalLabelValue string,
+ out io.Writer,
+) (int, error) {
+ if len(in) == 0 && additionalLabelName == "" {
+ return 0, nil
+ }
+ var written int
+ separator := '{'
+ for _, lp := range in {
+ n, err := fmt.Fprintf(
+ out, `%c%s="%s"`,
+ separator, lp.GetName(), escapeString(lp.GetValue(), true),
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+ separator = ','
+ }
+ if additionalLabelName != "" {
+ n, err := fmt.Fprintf(
+ out, `%c%s="%s"`,
+ separator, additionalLabelName,
+ escapeString(additionalLabelValue, true),
+ )
+ written += n
+ if err != nil {
+ return written, err
+ }
+ }
+ n, err := out.Write([]byte{'}'})
+ written += n
+ if err != nil {
+ return written, err
+ }
+ return written, nil
+}
+
+// escapeString replaces '\' by '\\', new line character by '\n', and - if
+// includeDoubleQuote is true - '"' by '\"'.
+func escapeString(v string, includeDoubleQuote bool) string {
+ result := bytes.NewBuffer(make([]byte, 0, len(v)))
+ for _, c := range v {
+ switch {
+ case c == '\\':
+ result.WriteString(`\\`)
+ case includeDoubleQuote && c == '"':
+ result.WriteString(`\"`)
+ case c == '\n':
+ result.WriteString(`\n`)
+ default:
+ result.WriteRune(c)
+ }
+ }
+ return result.String()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go
new file mode 100644
index 000000000..fe971d0d6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_create_test.go
@@ -0,0 +1,442 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "bytes"
+ "math"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func testCreate(t testing.TB) {
+ var scenarios = []struct {
+ in *dto.MetricFamily
+ out string
+ }{
+ // 0: Counter, NaN as value, timestamp given.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("two-line\n doc str\\ing"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("labelname"),
+ Value: proto.String("val1"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("basename"),
+ Value: proto.String("basevalue"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(math.NaN()),
+ },
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("labelname"),
+ Value: proto.String("val2"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("basename"),
+ Value: proto.String("basevalue"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(.23),
+ },
+ TimestampMs: proto.Int64(1234567890),
+ },
+ },
+ },
+ out: `# HELP name two-line\n doc str\\ing
+# TYPE name counter
+name{labelname="val1",basename="basevalue"} NaN
+name{labelname="val2",basename="basevalue"} 0.23 1234567890
+`,
+ },
+ // 1: Gauge, some escaping required, +Inf as value, multi-byte characters in label values.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("gauge_name"),
+ Help: proto.String("gauge\ndoc\nstr\"ing"),
+ Type: dto.MetricType_GAUGE.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("name_1"),
+ Value: proto.String("val with\nnew line"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("name_2"),
+ Value: proto.String("val with \\backslash and \"quotes\""),
+ },
+ },
+ Gauge: &dto.Gauge{
+ Value: proto.Float64(math.Inf(+1)),
+ },
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("name_1"),
+ Value: proto.String("Björn"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("name_2"),
+ Value: proto.String("佖佥"),
+ },
+ },
+ Gauge: &dto.Gauge{
+ Value: proto.Float64(3.14E42),
+ },
+ },
+ },
+ },
+ out: `# HELP gauge_name gauge\ndoc\nstr"ing
+# TYPE gauge_name gauge
+gauge_name{name_1="val with\nnew line",name_2="val with \\backslash and \"quotes\""} +Inf
+gauge_name{name_1="Björn",name_2="佖佥"} 3.14e+42
+`,
+ },
+ // 2: Untyped, no help, one sample with no labels and -Inf as value, another sample with one label.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("untyped_name"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(math.Inf(-1)),
+ },
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("name_1"),
+ Value: proto.String("value 1"),
+ },
+ },
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(-1.23e-45),
+ },
+ },
+ },
+ },
+ out: `# TYPE untyped_name untyped
+untyped_name -Inf
+untyped_name{name_1="value 1"} -1.23e-45
+`,
+ },
+ // 3: Summary.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("summary_name"),
+ Help: proto.String("summary docstring"),
+ Type: dto.MetricType_SUMMARY.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Summary: &dto.Summary{
+ SampleCount: proto.Uint64(42),
+ SampleSum: proto.Float64(-3.4567),
+ Quantile: []*dto.Quantile{
+ &dto.Quantile{
+ Quantile: proto.Float64(0.5),
+ Value: proto.Float64(-1.23),
+ },
+ &dto.Quantile{
+ Quantile: proto.Float64(0.9),
+ Value: proto.Float64(.2342354),
+ },
+ &dto.Quantile{
+ Quantile: proto.Float64(0.99),
+ Value: proto.Float64(0),
+ },
+ },
+ },
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("name_1"),
+ Value: proto.String("value 1"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("name_2"),
+ Value: proto.String("value 2"),
+ },
+ },
+ Summary: &dto.Summary{
+ SampleCount: proto.Uint64(4711),
+ SampleSum: proto.Float64(2010.1971),
+ Quantile: []*dto.Quantile{
+ &dto.Quantile{
+ Quantile: proto.Float64(0.5),
+ Value: proto.Float64(1),
+ },
+ &dto.Quantile{
+ Quantile: proto.Float64(0.9),
+ Value: proto.Float64(2),
+ },
+ &dto.Quantile{
+ Quantile: proto.Float64(0.99),
+ Value: proto.Float64(3),
+ },
+ },
+ },
+ },
+ },
+ },
+ out: `# HELP summary_name summary docstring
+# TYPE summary_name summary
+summary_name{quantile="0.5"} -1.23
+summary_name{quantile="0.9"} 0.2342354
+summary_name{quantile="0.99"} 0
+summary_name_sum -3.4567
+summary_name_count 42
+summary_name{name_1="value 1",name_2="value 2",quantile="0.5"} 1
+summary_name{name_1="value 1",name_2="value 2",quantile="0.9"} 2
+summary_name{name_1="value 1",name_2="value 2",quantile="0.99"} 3
+summary_name_sum{name_1="value 1",name_2="value 2"} 2010.1971
+summary_name_count{name_1="value 1",name_2="value 2"} 4711
+`,
+ },
+ // 4: Histogram
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("request_duration_microseconds"),
+ Help: proto.String("The response latency."),
+ Type: dto.MetricType_HISTOGRAM.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Histogram: &dto.Histogram{
+ SampleCount: proto.Uint64(2693),
+ SampleSum: proto.Float64(1756047.3),
+ Bucket: []*dto.Bucket{
+ &dto.Bucket{
+ UpperBound: proto.Float64(100),
+ CumulativeCount: proto.Uint64(123),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(120),
+ CumulativeCount: proto.Uint64(412),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(144),
+ CumulativeCount: proto.Uint64(592),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(172.8),
+ CumulativeCount: proto.Uint64(1524),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(math.Inf(+1)),
+ CumulativeCount: proto.Uint64(2693),
+ },
+ },
+ },
+ },
+ },
+ },
+ out: `# HELP request_duration_microseconds The response latency.
+# TYPE request_duration_microseconds histogram
+request_duration_microseconds_bucket{le="100"} 123
+request_duration_microseconds_bucket{le="120"} 412
+request_duration_microseconds_bucket{le="144"} 592
+request_duration_microseconds_bucket{le="172.8"} 1524
+request_duration_microseconds_bucket{le="+Inf"} 2693
+request_duration_microseconds_sum 1.7560473e+06
+request_duration_microseconds_count 2693
+`,
+ },
+ // 5: Histogram with missing +Inf bucket.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("request_duration_microseconds"),
+ Help: proto.String("The response latency."),
+ Type: dto.MetricType_HISTOGRAM.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Histogram: &dto.Histogram{
+ SampleCount: proto.Uint64(2693),
+ SampleSum: proto.Float64(1756047.3),
+ Bucket: []*dto.Bucket{
+ &dto.Bucket{
+ UpperBound: proto.Float64(100),
+ CumulativeCount: proto.Uint64(123),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(120),
+ CumulativeCount: proto.Uint64(412),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(144),
+ CumulativeCount: proto.Uint64(592),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(172.8),
+ CumulativeCount: proto.Uint64(1524),
+ },
+ },
+ },
+ },
+ },
+ },
+ out: `# HELP request_duration_microseconds The response latency.
+# TYPE request_duration_microseconds histogram
+request_duration_microseconds_bucket{le="100"} 123
+request_duration_microseconds_bucket{le="120"} 412
+request_duration_microseconds_bucket{le="144"} 592
+request_duration_microseconds_bucket{le="172.8"} 1524
+request_duration_microseconds_bucket{le="+Inf"} 2693
+request_duration_microseconds_sum 1.7560473e+06
+request_duration_microseconds_count 2693
+`,
+ },
+ // 6: No metric type, should result in default type Counter.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("doc string"),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Counter: &dto.Counter{
+ Value: proto.Float64(math.Inf(-1)),
+ },
+ },
+ },
+ },
+ out: `# HELP name doc string
+# TYPE name counter
+name -Inf
+`,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ out := bytes.NewBuffer(make([]byte, 0, len(scenario.out)))
+ n, err := MetricFamilyToText(out, scenario.in)
+ if err != nil {
+ t.Errorf("%d. error: %s", i, err)
+ continue
+ }
+ if expected, got := len(scenario.out), n; expected != got {
+ t.Errorf(
+ "%d. expected %d bytes written, got %d",
+ i, expected, got,
+ )
+ }
+ if expected, got := scenario.out, out.String(); expected != got {
+ t.Errorf(
+ "%d. expected out=%q, got %q",
+ i, expected, got,
+ )
+ }
+ }
+
+}
+
+func TestCreate(t *testing.T) {
+ testCreate(t)
+}
+
+func BenchmarkCreate(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testCreate(b)
+ }
+}
+
+func testCreateError(t testing.TB) {
+ var scenarios = []struct {
+ in *dto.MetricFamily
+ err string
+ }{
+ // 0: No metric.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("doc string"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{},
+ },
+ err: "MetricFamily has no metrics",
+ },
+ // 1: No metric name.
+ {
+ in: &dto.MetricFamily{
+ Help: proto.String("doc string"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(math.Inf(-1)),
+ },
+ },
+ },
+ },
+ err: "MetricFamily has no name",
+ },
+ // 2: Wrong type.
+ {
+ in: &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("doc string"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(math.Inf(-1)),
+ },
+ },
+ },
+ },
+ err: "expected counter in metric",
+ },
+ }
+
+ for i, scenario := range scenarios {
+ var out bytes.Buffer
+ _, err := MetricFamilyToText(&out, scenario.in)
+ if err == nil {
+ t.Errorf("%d. expected error, got nil", i)
+ continue
+ }
+ if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 {
+ t.Errorf(
+ "%d. expected error starting with %q, got %q",
+ i, expected, got,
+ )
+ }
+ }
+
+}
+
+func TestCreateError(t *testing.T) {
+ testCreateError(t)
+}
+
+func BenchmarkCreateError(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testCreateError(b)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse.go
new file mode 100644
index 000000000..e315f0cce
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse.go
@@ -0,0 +1,745 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "math"
+ "strconv"
+ "strings"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model"
+)
+
+// A stateFn is a function that represents a state in a state machine. By
+// executing it, the state is progressed to the next state. The stateFn returns
+// another stateFn, which represents the new state. The end state is represented
+// by nil.
+type stateFn func() stateFn
+
+// ParseError signals errors while parsing the simple and flat text-based
+// exchange format.
+type ParseError struct {
+ Line int
+ Msg string
+}
+
+// Error implements the error interface.
+func (e ParseError) Error() string {
+ return fmt.Sprintf("text format parsing error in line %d: %s", e.Line, e.Msg)
+}
+
+// TextParser is used to parse the simple and flat text-based exchange format. Its
+// nil value is ready to use.
+type TextParser struct {
+ metricFamiliesByName map[string]*dto.MetricFamily
+ buf *bufio.Reader // Where the parsed input is read through.
+ err error // Most recent error.
+ lineCount int // Tracks the line count for error messages.
+ currentByte byte // The most recent byte read.
+ currentToken bytes.Buffer // Re-used each time a token has to be gathered from multiple bytes.
+ currentMF *dto.MetricFamily
+ currentMetric *dto.Metric
+ currentLabelPair *dto.LabelPair
+
+ // The remaining member variables are only used for summaries/histograms.
+ currentLabels map[string]string // All labels including '__name__' but excluding 'quantile'/'le'
+ // Summary specific.
+ summaries map[uint64]*dto.Metric // Key is created with LabelsToSignature.
+ currentQuantile float64
+ // Histogram specific.
+ histograms map[uint64]*dto.Metric // Key is created with LabelsToSignature.
+ currentBucket float64
+ // These tell us if the currently processed line ends on '_count' or
+ // '_sum' respectively and belong to a summary/histogram, representing the sample
+ // count and sum of that summary/histogram.
+ currentIsSummaryCount, currentIsSummarySum bool
+ currentIsHistogramCount, currentIsHistogramSum bool
+}
+
+// TextToMetricFamilies reads 'in' as the simple and flat text-based exchange
+// format and creates MetricFamily proto messages. It returns the MetricFamily
+// proto messages in a map where the metric names are the keys, along with any
+// error encountered.
+//
+// If the input contains duplicate metrics (i.e. lines with the same metric name
+// and exactly the same label set), the resulting MetricFamily will contain
+// duplicate Metric proto messages. Similar is true for duplicate label
+// names. Checks for duplicates have to be performed separately, if required.
+// Also note that neither the metrics within each MetricFamily are sorted nor
+// the label pairs within each Metric. Sorting is not required for the most
+// frequent use of this method, which is sample ingestion in the Prometheus
+// server. However, for presentation purposes, you might want to sort the
+// metrics, and in some cases, you must sort the labels, e.g. for consumption by
+// the metric family injection hook of the Prometheus registry.
+//
+// Summaries and histograms are rather special beasts. You would probably not
+// use them in the simple text format anyway. This method can deal with
+// summaries and histograms if they are presented in exactly the way the
+// text.Create function creates them.
+//
+// This method must not be called concurrently. If you want to parse different
+// input concurrently, instantiate a separate Parser for each goroutine.
+func (p *TextParser) TextToMetricFamilies(in io.Reader) (map[string]*dto.MetricFamily, error) {
+ p.reset(in)
+ for nextState := p.startOfLine; nextState != nil; nextState = nextState() {
+ // Magic happens here...
+ }
+ // Get rid of empty metric families.
+ for k, mf := range p.metricFamiliesByName {
+ if len(mf.GetMetric()) == 0 {
+ delete(p.metricFamiliesByName, k)
+ }
+ }
+ return p.metricFamiliesByName, p.err
+}
+
+func (p *TextParser) reset(in io.Reader) {
+ p.metricFamiliesByName = map[string]*dto.MetricFamily{}
+ if p.buf == nil {
+ p.buf = bufio.NewReader(in)
+ } else {
+ p.buf.Reset(in)
+ }
+ p.err = nil
+ p.lineCount = 0
+ if p.summaries == nil || len(p.summaries) > 0 {
+ p.summaries = map[uint64]*dto.Metric{}
+ }
+ if p.histograms == nil || len(p.histograms) > 0 {
+ p.histograms = map[uint64]*dto.Metric{}
+ }
+ p.currentQuantile = math.NaN()
+ p.currentBucket = math.NaN()
+}
+
+// startOfLine represents the state where the next byte read from p.buf is the
+// start of a line (or whitespace leading up to it).
+func (p *TextParser) startOfLine() stateFn {
+ p.lineCount++
+ if p.skipBlankTab(); p.err != nil {
+ // End of input reached. This is the only case where
+ // that is not an error but a signal that we are done.
+ p.err = nil
+ return nil
+ }
+ switch p.currentByte {
+ case '#':
+ return p.startComment
+ case '\n':
+ return p.startOfLine // Empty line, start the next one.
+ }
+ return p.readingMetricName
+}
+
+// startComment represents the state where the next byte read from p.buf is the
+// start of a comment (or whitespace leading up to it).
+func (p *TextParser) startComment() stateFn {
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentByte == '\n' {
+ return p.startOfLine
+ }
+ if p.readTokenUntilWhitespace(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ // If we have hit the end of line already, there is nothing left
+ // to do. This is not considered a syntax error.
+ if p.currentByte == '\n' {
+ return p.startOfLine
+ }
+ keyword := p.currentToken.String()
+ if keyword != "HELP" && keyword != "TYPE" {
+ // Generic comment, ignore by fast forwarding to end of line.
+ for p.currentByte != '\n' {
+ if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ }
+ return p.startOfLine
+ }
+ // There is something. Next has to be a metric name.
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.readTokenAsMetricName(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentByte == '\n' {
+ // At the end of the line already.
+ // Again, this is not considered a syntax error.
+ return p.startOfLine
+ }
+ if !isBlankOrTab(p.currentByte) {
+ p.parseError("invalid metric name in comment")
+ return nil
+ }
+ p.setOrCreateCurrentMF()
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentByte == '\n' {
+ // At the end of the line already.
+ // Again, this is not considered a syntax error.
+ return p.startOfLine
+ }
+ switch keyword {
+ case "HELP":
+ return p.readingHelp
+ case "TYPE":
+ return p.readingType
+ }
+ panic(fmt.Sprintf("code error: unexpected keyword %q", keyword))
+}
+
+// readingMetricName represents the state where the last byte read (now in
+// p.currentByte) is the first byte of a metric name.
+func (p *TextParser) readingMetricName() stateFn {
+ if p.readTokenAsMetricName(); p.err != nil {
+ return nil
+ }
+ if p.currentToken.Len() == 0 {
+ p.parseError("invalid metric name")
+ return nil
+ }
+ p.setOrCreateCurrentMF()
+ // Now is the time to fix the type if it hasn't happened yet.
+ if p.currentMF.Type == nil {
+ p.currentMF.Type = dto.MetricType_UNTYPED.Enum()
+ }
+ p.currentMetric = &dto.Metric{}
+ // Do not append the newly created currentMetric to
+ // currentMF.Metric right now. First wait if this is a summary,
+ // and the metric exists already, which we can only know after
+ // having read all the labels.
+ if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ return p.readingLabels
+}
+
+// readingLabels represents the state where the last byte read (now in
+// p.currentByte) is either the first byte of the label set (i.e. a '{'), or the
+// first byte of the value (otherwise).
+func (p *TextParser) readingLabels() stateFn {
+ // Summaries/histograms are special. We have to reset the
+ // currentLabels map, currentQuantile and currentBucket before starting to
+ // read labels.
+ if p.currentMF.GetType() == dto.MetricType_SUMMARY || p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
+ p.currentLabels = map[string]string{}
+ p.currentLabels[string(model.MetricNameLabel)] = p.currentMF.GetName()
+ p.currentQuantile = math.NaN()
+ p.currentBucket = math.NaN()
+ }
+ if p.currentByte != '{' {
+ return p.readingValue
+ }
+ return p.startLabelName
+}
+
+// startLabelName represents the state where the next byte read from p.buf is
+// the start of a label name (or whitespace leading up to it).
+func (p *TextParser) startLabelName() stateFn {
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentByte == '}' {
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ return p.readingValue
+ }
+ if p.readTokenAsLabelName(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentToken.Len() == 0 {
+ p.parseError(fmt.Sprintf("invalid label name for metric %q", p.currentMF.GetName()))
+ return nil
+ }
+ p.currentLabelPair = &dto.LabelPair{Name: proto.String(p.currentToken.String())}
+ if p.currentLabelPair.GetName() == string(model.MetricNameLabel) {
+ p.parseError(fmt.Sprintf("label name %q is reserved", model.MetricNameLabel))
+ return nil
+ }
+ // Special summary/histogram treatment. Don't add 'quantile' and 'le'
+ // labels to 'real' labels.
+ if !(p.currentMF.GetType() == dto.MetricType_SUMMARY && p.currentLabelPair.GetName() == model.QuantileLabel) &&
+ !(p.currentMF.GetType() == dto.MetricType_HISTOGRAM && p.currentLabelPair.GetName() == model.BucketLabel) {
+ p.currentMetric.Label = append(p.currentMetric.Label, p.currentLabelPair)
+ }
+ if p.skipBlankTabIfCurrentBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentByte != '=' {
+ p.parseError(fmt.Sprintf("expected '=' after label name, found %q", p.currentByte))
+ return nil
+ }
+ return p.startLabelValue
+}
+
+// startLabelValue represents the state where the next byte read from p.buf is
+// the start of a (quoted) label value (or whitespace leading up to it).
+func (p *TextParser) startLabelValue() stateFn {
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentByte != '"' {
+ p.parseError(fmt.Sprintf("expected '\"' at start of label value, found %q", p.currentByte))
+ return nil
+ }
+ if p.readTokenAsLabelValue(); p.err != nil {
+ return nil
+ }
+ p.currentLabelPair.Value = proto.String(p.currentToken.String())
+ // Special treatment of summaries:
+ // - Quantile labels are special, will result in dto.Quantile later.
+ // - Other labels have to be added to currentLabels for signature calculation.
+ if p.currentMF.GetType() == dto.MetricType_SUMMARY {
+ if p.currentLabelPair.GetName() == model.QuantileLabel {
+ if p.currentQuantile, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
+ // Create a more helpful error message.
+ p.parseError(fmt.Sprintf("expected float as value for 'quantile' label, got %q", p.currentLabelPair.GetValue()))
+ return nil
+ }
+ } else {
+ p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
+ }
+ }
+ // Similar special treatment of histograms.
+ if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
+ if p.currentLabelPair.GetName() == model.BucketLabel {
+ if p.currentBucket, p.err = strconv.ParseFloat(p.currentLabelPair.GetValue(), 64); p.err != nil {
+ // Create a more helpful error message.
+ p.parseError(fmt.Sprintf("expected float as value for 'le' label, got %q", p.currentLabelPair.GetValue()))
+ return nil
+ }
+ } else {
+ p.currentLabels[p.currentLabelPair.GetName()] = p.currentLabelPair.GetValue()
+ }
+ }
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ switch p.currentByte {
+ case ',':
+ return p.startLabelName
+
+ case '}':
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ return p.readingValue
+ default:
+ p.parseError(fmt.Sprintf("unexpected end of label value %q", p.currentLabelPair.Value))
+ return nil
+ }
+}
+
+// readingValue represents the state where the last byte read (now in
+// p.currentByte) is the first byte of the sample value (i.e. a float).
+func (p *TextParser) readingValue() stateFn {
+ // When we are here, we have read all the labels, so for the
+ // special case of a summary/histogram, we can finally find out
+ // if the metric already exists.
+ if p.currentMF.GetType() == dto.MetricType_SUMMARY {
+ signature := model.LabelsToSignature(p.currentLabels)
+ if summary := p.summaries[signature]; summary != nil {
+ p.currentMetric = summary
+ } else {
+ p.summaries[signature] = p.currentMetric
+ p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
+ }
+ } else if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
+ signature := model.LabelsToSignature(p.currentLabels)
+ if histogram := p.histograms[signature]; histogram != nil {
+ p.currentMetric = histogram
+ } else {
+ p.histograms[signature] = p.currentMetric
+ p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
+ }
+ } else {
+ p.currentMF.Metric = append(p.currentMF.Metric, p.currentMetric)
+ }
+ if p.readTokenUntilWhitespace(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ value, err := strconv.ParseFloat(p.currentToken.String(), 64)
+ if err != nil {
+ // Create a more helpful error message.
+ p.parseError(fmt.Sprintf("expected float as value, got %q", p.currentToken.String()))
+ return nil
+ }
+ switch p.currentMF.GetType() {
+ case dto.MetricType_COUNTER:
+ p.currentMetric.Counter = &dto.Counter{Value: proto.Float64(value)}
+ case dto.MetricType_GAUGE:
+ p.currentMetric.Gauge = &dto.Gauge{Value: proto.Float64(value)}
+ case dto.MetricType_UNTYPED:
+ p.currentMetric.Untyped = &dto.Untyped{Value: proto.Float64(value)}
+ case dto.MetricType_SUMMARY:
+ // *sigh*
+ if p.currentMetric.Summary == nil {
+ p.currentMetric.Summary = &dto.Summary{}
+ }
+ switch {
+ case p.currentIsSummaryCount:
+ p.currentMetric.Summary.SampleCount = proto.Uint64(uint64(value))
+ case p.currentIsSummarySum:
+ p.currentMetric.Summary.SampleSum = proto.Float64(value)
+ case !math.IsNaN(p.currentQuantile):
+ p.currentMetric.Summary.Quantile = append(
+ p.currentMetric.Summary.Quantile,
+ &dto.Quantile{
+ Quantile: proto.Float64(p.currentQuantile),
+ Value: proto.Float64(value),
+ },
+ )
+ }
+ case dto.MetricType_HISTOGRAM:
+ // *sigh*
+ if p.currentMetric.Histogram == nil {
+ p.currentMetric.Histogram = &dto.Histogram{}
+ }
+ switch {
+ case p.currentIsHistogramCount:
+ p.currentMetric.Histogram.SampleCount = proto.Uint64(uint64(value))
+ case p.currentIsHistogramSum:
+ p.currentMetric.Histogram.SampleSum = proto.Float64(value)
+ case !math.IsNaN(p.currentBucket):
+ p.currentMetric.Histogram.Bucket = append(
+ p.currentMetric.Histogram.Bucket,
+ &dto.Bucket{
+ UpperBound: proto.Float64(p.currentBucket),
+ CumulativeCount: proto.Uint64(uint64(value)),
+ },
+ )
+ }
+ default:
+ p.err = fmt.Errorf("unexpected type for metric name %q", p.currentMF.GetName())
+ }
+ if p.currentByte == '\n' {
+ return p.startOfLine
+ }
+ return p.startTimestamp
+}
+
+// startTimestamp represents the state where the next byte read from p.buf is
+// the start of the timestamp (or whitespace leading up to it).
+func (p *TextParser) startTimestamp() stateFn {
+ if p.skipBlankTab(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.readTokenUntilWhitespace(); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ timestamp, err := strconv.ParseInt(p.currentToken.String(), 10, 64)
+ if err != nil {
+ // Create a more helpful error message.
+ p.parseError(fmt.Sprintf("expected integer as timestamp, got %q", p.currentToken.String()))
+ return nil
+ }
+ p.currentMetric.TimestampMs = proto.Int64(timestamp)
+ if p.readTokenUntilNewline(false); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ if p.currentToken.Len() > 0 {
+ p.parseError(fmt.Sprintf("spurious string after timestamp: %q", p.currentToken.String()))
+ return nil
+ }
+ return p.startOfLine
+}
+
+// readingHelp represents the state where the last byte read (now in
+// p.currentByte) is the first byte of the docstring after 'HELP'.
+func (p *TextParser) readingHelp() stateFn {
+ if p.currentMF.Help != nil {
+ p.parseError(fmt.Sprintf("second HELP line for metric name %q", p.currentMF.GetName()))
+ return nil
+ }
+ // Rest of line is the docstring.
+ if p.readTokenUntilNewline(true); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ p.currentMF.Help = proto.String(p.currentToken.String())
+ return p.startOfLine
+}
+
+// readingType represents the state where the last byte read (now in
+// p.currentByte) is the first byte of the type hint after 'HELP'.
+func (p *TextParser) readingType() stateFn {
+ if p.currentMF.Type != nil {
+ p.parseError(fmt.Sprintf("second TYPE line for metric name %q, or TYPE reported after samples", p.currentMF.GetName()))
+ return nil
+ }
+ // Rest of line is the type.
+ if p.readTokenUntilNewline(false); p.err != nil {
+ return nil // Unexpected end of input.
+ }
+ metricType, ok := dto.MetricType_value[strings.ToUpper(p.currentToken.String())]
+ if !ok {
+ p.parseError(fmt.Sprintf("unknown metric type %q", p.currentToken.String()))
+ return nil
+ }
+ p.currentMF.Type = dto.MetricType(metricType).Enum()
+ return p.startOfLine
+}
+
+// parseError sets p.err to a ParseError at the current line with the given
+// message.
+func (p *TextParser) parseError(msg string) {
+ p.err = ParseError{
+ Line: p.lineCount,
+ Msg: msg,
+ }
+}
+
+// skipBlankTab reads (and discards) bytes from p.buf until it encounters a byte
+// that is neither ' ' nor '\t'. That byte is left in p.currentByte.
+func (p *TextParser) skipBlankTab() {
+ for {
+ if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil || !isBlankOrTab(p.currentByte) {
+ return
+ }
+ }
+}
+
+// skipBlankTabIfCurrentBlankTab works exactly as skipBlankTab but doesn't do
+// anything if p.currentByte is neither ' ' nor '\t'.
+func (p *TextParser) skipBlankTabIfCurrentBlankTab() {
+ if isBlankOrTab(p.currentByte) {
+ p.skipBlankTab()
+ }
+}
+
+// readTokenUntilWhitespace copies bytes from p.buf into p.currentToken. The
+// first byte considered is the byte already read (now in p.currentByte). The
+// first whitespace byte encountered is still copied into p.currentByte, but not
+// into p.currentToken.
+func (p *TextParser) readTokenUntilWhitespace() {
+ p.currentToken.Reset()
+ for p.err == nil && !isBlankOrTab(p.currentByte) && p.currentByte != '\n' {
+ p.currentToken.WriteByte(p.currentByte)
+ p.currentByte, p.err = p.buf.ReadByte()
+ }
+}
+
+// readTokenUntilNewline copies bytes from p.buf into p.currentToken. The first
+// byte considered is the byte already read (now in p.currentByte). The first
+// newline byte encountered is still copied into p.currentByte, but not into
+// p.currentToken. If recognizeEscapeSequence is true, two escape sequences are
+// recognized: '\\' tranlates into '\', and '\n' into a line-feed character. All
+// other escape sequences are invalid and cause an error.
+func (p *TextParser) readTokenUntilNewline(recognizeEscapeSequence bool) {
+ p.currentToken.Reset()
+ escaped := false
+ for p.err == nil {
+ if recognizeEscapeSequence && escaped {
+ switch p.currentByte {
+ case '\\':
+ p.currentToken.WriteByte(p.currentByte)
+ case 'n':
+ p.currentToken.WriteByte('\n')
+ default:
+ p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
+ return
+ }
+ escaped = false
+ } else {
+ switch p.currentByte {
+ case '\n':
+ return
+ case '\\':
+ escaped = true
+ default:
+ p.currentToken.WriteByte(p.currentByte)
+ }
+ }
+ p.currentByte, p.err = p.buf.ReadByte()
+ }
+}
+
+// readTokenAsMetricName copies a metric name from p.buf into p.currentToken.
+// The first byte considered is the byte already read (now in p.currentByte).
+// The first byte not part of a metric name is still copied into p.currentByte,
+// but not into p.currentToken.
+func (p *TextParser) readTokenAsMetricName() {
+ p.currentToken.Reset()
+ if !isValidMetricNameStart(p.currentByte) {
+ return
+ }
+ for {
+ p.currentToken.WriteByte(p.currentByte)
+ p.currentByte, p.err = p.buf.ReadByte()
+ if p.err != nil || !isValidMetricNameContinuation(p.currentByte) {
+ return
+ }
+ }
+}
+
+// readTokenAsLabelName copies a label name from p.buf into p.currentToken.
+// The first byte considered is the byte already read (now in p.currentByte).
+// The first byte not part of a label name is still copied into p.currentByte,
+// but not into p.currentToken.
+func (p *TextParser) readTokenAsLabelName() {
+ p.currentToken.Reset()
+ if !isValidLabelNameStart(p.currentByte) {
+ return
+ }
+ for {
+ p.currentToken.WriteByte(p.currentByte)
+ p.currentByte, p.err = p.buf.ReadByte()
+ if p.err != nil || !isValidLabelNameContinuation(p.currentByte) {
+ return
+ }
+ }
+}
+
+// readTokenAsLabelValue copies a label value from p.buf into p.currentToken.
+// In contrast to the other 'readTokenAs...' functions, which start with the
+// last read byte in p.currentByte, this method ignores p.currentByte and starts
+// with reading a new byte from p.buf. The first byte not part of a label value
+// is still copied into p.currentByte, but not into p.currentToken.
+func (p *TextParser) readTokenAsLabelValue() {
+ p.currentToken.Reset()
+ escaped := false
+ for {
+ if p.currentByte, p.err = p.buf.ReadByte(); p.err != nil {
+ return
+ }
+ if escaped {
+ switch p.currentByte {
+ case '"', '\\':
+ p.currentToken.WriteByte(p.currentByte)
+ case 'n':
+ p.currentToken.WriteByte('\n')
+ default:
+ p.parseError(fmt.Sprintf("invalid escape sequence '\\%c'", p.currentByte))
+ return
+ }
+ escaped = false
+ continue
+ }
+ switch p.currentByte {
+ case '"':
+ return
+ case '\n':
+ p.parseError(fmt.Sprintf("label value %q contains unescaped new-line", p.currentToken.String()))
+ return
+ case '\\':
+ escaped = true
+ default:
+ p.currentToken.WriteByte(p.currentByte)
+ }
+ }
+}
+
+func (p *TextParser) setOrCreateCurrentMF() {
+ p.currentIsSummaryCount = false
+ p.currentIsSummarySum = false
+ p.currentIsHistogramCount = false
+ p.currentIsHistogramSum = false
+ name := p.currentToken.String()
+ if p.currentMF = p.metricFamiliesByName[name]; p.currentMF != nil {
+ return
+ }
+ // Try out if this is a _sum or _count for a summary/histogram.
+ summaryName := summaryMetricName(name)
+ if p.currentMF = p.metricFamiliesByName[summaryName]; p.currentMF != nil {
+ if p.currentMF.GetType() == dto.MetricType_SUMMARY {
+ if isCount(name) {
+ p.currentIsSummaryCount = true
+ }
+ if isSum(name) {
+ p.currentIsSummarySum = true
+ }
+ return
+ }
+ }
+ histogramName := histogramMetricName(name)
+ if p.currentMF = p.metricFamiliesByName[histogramName]; p.currentMF != nil {
+ if p.currentMF.GetType() == dto.MetricType_HISTOGRAM {
+ if isCount(name) {
+ p.currentIsHistogramCount = true
+ }
+ if isSum(name) {
+ p.currentIsHistogramSum = true
+ }
+ return
+ }
+ }
+ p.currentMF = &dto.MetricFamily{Name: proto.String(name)}
+ p.metricFamiliesByName[name] = p.currentMF
+}
+
+func isValidLabelNameStart(b byte) bool {
+ return (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
+}
+
+func isValidLabelNameContinuation(b byte) bool {
+ return isValidLabelNameStart(b) || (b >= '0' && b <= '9')
+}
+
+func isValidMetricNameStart(b byte) bool {
+ return isValidLabelNameStart(b) || b == ':'
+}
+
+func isValidMetricNameContinuation(b byte) bool {
+ return isValidLabelNameContinuation(b) || b == ':'
+}
+
+func isBlankOrTab(b byte) bool {
+ return b == ' ' || b == '\t'
+}
+
+func isCount(name string) bool {
+ return len(name) > 6 && name[len(name)-6:] == "_count"
+}
+
+func isSum(name string) bool {
+ return len(name) > 4 && name[len(name)-4:] == "_sum"
+}
+
+func isBucket(name string) bool {
+ return len(name) > 7 && name[len(name)-7:] == "_bucket"
+}
+
+func summaryMetricName(name string) string {
+ switch {
+ case isCount(name):
+ return name[:len(name)-6]
+ case isSum(name):
+ return name[:len(name)-4]
+ default:
+ return name
+ }
+}
+
+func histogramMetricName(name string) string {
+ switch {
+ case isCount(name):
+ return name[:len(name)-6]
+ case isSum(name):
+ return name[:len(name)-4]
+ case isBucket(name):
+ return name[:len(name)-7]
+ default:
+ return name
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go
new file mode 100644
index 000000000..81a0a8f27
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/expfmt/text_parse_test.go
@@ -0,0 +1,586 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package expfmt
+
+import (
+ "math"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ dto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_model/go"
+)
+
+func testTextParse(t testing.TB) {
+ var scenarios = []struct {
+ in string
+ out []*dto.MetricFamily
+ }{
+ // 0: Empty lines as input.
+ {
+ in: `
+
+`,
+ out: []*dto.MetricFamily{},
+ },
+ // 1: Minimal case.
+ {
+ in: `
+minimal_metric 1.234
+another_metric -3e3 103948
+# Even that:
+no_labels{} 3
+# HELP line for non-existing metric will be ignored.
+`,
+ out: []*dto.MetricFamily{
+ &dto.MetricFamily{
+ Name: proto.String("minimal_metric"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(1.234),
+ },
+ },
+ },
+ },
+ &dto.MetricFamily{
+ Name: proto.String("another_metric"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(-3e3),
+ },
+ TimestampMs: proto.Int64(103948),
+ },
+ },
+ },
+ &dto.MetricFamily{
+ Name: proto.String("no_labels"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(3),
+ },
+ },
+ },
+ },
+ },
+ },
+ // 2: Counters & gauges, docstrings, various whitespace, escape sequences.
+ {
+ in: `
+# A normal comment.
+#
+# TYPE name counter
+name{labelname="val1",basename="basevalue"} NaN
+name {labelname="val2",basename="base\"v\\al\nue"} 0.23 1234567890
+# HELP name two-line\n doc str\\ing
+
+ # HELP name2 doc str"ing 2
+ # TYPE name2 gauge
+name2{labelname="val2" ,basename = "basevalue2" } +Inf 54321
+name2{ labelname = "val1" , }-Inf
+`,
+ out: []*dto.MetricFamily{
+ &dto.MetricFamily{
+ Name: proto.String("name"),
+ Help: proto.String("two-line\n doc str\\ing"),
+ Type: dto.MetricType_COUNTER.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("labelname"),
+ Value: proto.String("val1"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("basename"),
+ Value: proto.String("basevalue"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(math.NaN()),
+ },
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("labelname"),
+ Value: proto.String("val2"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("basename"),
+ Value: proto.String("base\"v\\al\nue"),
+ },
+ },
+ Counter: &dto.Counter{
+ Value: proto.Float64(.23),
+ },
+ TimestampMs: proto.Int64(1234567890),
+ },
+ },
+ },
+ &dto.MetricFamily{
+ Name: proto.String("name2"),
+ Help: proto.String("doc str\"ing 2"),
+ Type: dto.MetricType_GAUGE.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("labelname"),
+ Value: proto.String("val2"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("basename"),
+ Value: proto.String("basevalue2"),
+ },
+ },
+ Gauge: &dto.Gauge{
+ Value: proto.Float64(math.Inf(+1)),
+ },
+ TimestampMs: proto.Int64(54321),
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("labelname"),
+ Value: proto.String("val1"),
+ },
+ },
+ Gauge: &dto.Gauge{
+ Value: proto.Float64(math.Inf(-1)),
+ },
+ },
+ },
+ },
+ },
+ },
+ // 3: The evil summary, mixed with other types and funny comments.
+ {
+ in: `
+# TYPE my_summary summary
+my_summary{n1="val1",quantile="0.5"} 110
+decoy -1 -2
+my_summary{n1="val1",quantile="0.9"} 140 1
+my_summary_count{n1="val1"} 42
+# Latest timestamp wins in case of a summary.
+my_summary_sum{n1="val1"} 4711 2
+fake_sum{n1="val1"} 2001
+# TYPE another_summary summary
+another_summary_count{n2="val2",n1="val1"} 20
+my_summary_count{n2="val2",n1="val1"} 5 5
+another_summary{n1="val1",n2="val2",quantile=".3"} -1.2
+my_summary_sum{n1="val2"} 08 15
+my_summary{n1="val3", quantile="0.2"} 4711
+ my_summary{n1="val1",n2="val2",quantile="-12.34",} NaN
+# some
+# funny comments
+# HELP
+# HELP
+# HELP my_summary
+# HELP my_summary
+`,
+ out: []*dto.MetricFamily{
+ &dto.MetricFamily{
+ Name: proto.String("fake_sum"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("n1"),
+ Value: proto.String("val1"),
+ },
+ },
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(2001),
+ },
+ },
+ },
+ },
+ &dto.MetricFamily{
+ Name: proto.String("decoy"),
+ Type: dto.MetricType_UNTYPED.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Untyped: &dto.Untyped{
+ Value: proto.Float64(-1),
+ },
+ TimestampMs: proto.Int64(-2),
+ },
+ },
+ },
+ &dto.MetricFamily{
+ Name: proto.String("my_summary"),
+ Type: dto.MetricType_SUMMARY.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("n1"),
+ Value: proto.String("val1"),
+ },
+ },
+ Summary: &dto.Summary{
+ SampleCount: proto.Uint64(42),
+ SampleSum: proto.Float64(4711),
+ Quantile: []*dto.Quantile{
+ &dto.Quantile{
+ Quantile: proto.Float64(0.5),
+ Value: proto.Float64(110),
+ },
+ &dto.Quantile{
+ Quantile: proto.Float64(0.9),
+ Value: proto.Float64(140),
+ },
+ },
+ },
+ TimestampMs: proto.Int64(2),
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("n2"),
+ Value: proto.String("val2"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("n1"),
+ Value: proto.String("val1"),
+ },
+ },
+ Summary: &dto.Summary{
+ SampleCount: proto.Uint64(5),
+ Quantile: []*dto.Quantile{
+ &dto.Quantile{
+ Quantile: proto.Float64(-12.34),
+ Value: proto.Float64(math.NaN()),
+ },
+ },
+ },
+ TimestampMs: proto.Int64(5),
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("n1"),
+ Value: proto.String("val2"),
+ },
+ },
+ Summary: &dto.Summary{
+ SampleSum: proto.Float64(8),
+ },
+ TimestampMs: proto.Int64(15),
+ },
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("n1"),
+ Value: proto.String("val3"),
+ },
+ },
+ Summary: &dto.Summary{
+ Quantile: []*dto.Quantile{
+ &dto.Quantile{
+ Quantile: proto.Float64(0.2),
+ Value: proto.Float64(4711),
+ },
+ },
+ },
+ },
+ },
+ },
+ &dto.MetricFamily{
+ Name: proto.String("another_summary"),
+ Type: dto.MetricType_SUMMARY.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Label: []*dto.LabelPair{
+ &dto.LabelPair{
+ Name: proto.String("n2"),
+ Value: proto.String("val2"),
+ },
+ &dto.LabelPair{
+ Name: proto.String("n1"),
+ Value: proto.String("val1"),
+ },
+ },
+ Summary: &dto.Summary{
+ SampleCount: proto.Uint64(20),
+ Quantile: []*dto.Quantile{
+ &dto.Quantile{
+ Quantile: proto.Float64(0.3),
+ Value: proto.Float64(-1.2),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ // 4: The histogram.
+ {
+ in: `
+# HELP request_duration_microseconds The response latency.
+# TYPE request_duration_microseconds histogram
+request_duration_microseconds_bucket{le="100"} 123
+request_duration_microseconds_bucket{le="120"} 412
+request_duration_microseconds_bucket{le="144"} 592
+request_duration_microseconds_bucket{le="172.8"} 1524
+request_duration_microseconds_bucket{le="+Inf"} 2693
+request_duration_microseconds_sum 1.7560473e+06
+request_duration_microseconds_count 2693
+`,
+ out: []*dto.MetricFamily{
+ {
+ Name: proto.String("request_duration_microseconds"),
+ Help: proto.String("The response latency."),
+ Type: dto.MetricType_HISTOGRAM.Enum(),
+ Metric: []*dto.Metric{
+ &dto.Metric{
+ Histogram: &dto.Histogram{
+ SampleCount: proto.Uint64(2693),
+ SampleSum: proto.Float64(1756047.3),
+ Bucket: []*dto.Bucket{
+ &dto.Bucket{
+ UpperBound: proto.Float64(100),
+ CumulativeCount: proto.Uint64(123),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(120),
+ CumulativeCount: proto.Uint64(412),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(144),
+ CumulativeCount: proto.Uint64(592),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(172.8),
+ CumulativeCount: proto.Uint64(1524),
+ },
+ &dto.Bucket{
+ UpperBound: proto.Float64(math.Inf(+1)),
+ CumulativeCount: proto.Uint64(2693),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ for i, scenario := range scenarios {
+ out, err := parser.TextToMetricFamilies(strings.NewReader(scenario.in))
+ if err != nil {
+ t.Errorf("%d. error: %s", i, err)
+ continue
+ }
+ if expected, got := len(scenario.out), len(out); expected != got {
+ t.Errorf(
+ "%d. expected %d MetricFamilies, got %d",
+ i, expected, got,
+ )
+ }
+ for _, expected := range scenario.out {
+ got, ok := out[expected.GetName()]
+ if !ok {
+ t.Errorf(
+ "%d. expected MetricFamily %q, found none",
+ i, expected.GetName(),
+ )
+ continue
+ }
+ if expected.String() != got.String() {
+ t.Errorf(
+ "%d. expected MetricFamily %s, got %s",
+ i, expected, got,
+ )
+ }
+ }
+ }
+}
+
+func TestTextParse(t *testing.T) {
+ testTextParse(t)
+}
+
+func BenchmarkTextParse(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testTextParse(b)
+ }
+}
+
+func testTextParseError(t testing.TB) {
+ var scenarios = []struct {
+ in string
+ err string
+ }{
+ // 0: No new-line at end of input.
+ {
+ in: `bla 3.14`,
+ err: "EOF",
+ },
+ // 1: Invalid escape sequence in label value.
+ {
+ in: `metric{label="\t"} 3.14`,
+ err: "text format parsing error in line 1: invalid escape sequence",
+ },
+ // 2: Newline in label value.
+ {
+ in: `
+metric{label="new
+line"} 3.14
+`,
+ err: `text format parsing error in line 2: label value "new" contains unescaped new-line`,
+ },
+ // 3:
+ {
+ in: `metric{@="bla"} 3.14`,
+ err: "text format parsing error in line 1: invalid label name for metric",
+ },
+ // 4:
+ {
+ in: `metric{__name__="bla"} 3.14`,
+ err: `text format parsing error in line 1: label name "__name__" is reserved`,
+ },
+ // 5:
+ {
+ in: `metric{label+="bla"} 3.14`,
+ err: "text format parsing error in line 1: expected '=' after label name",
+ },
+ // 6:
+ {
+ in: `metric{label=bla} 3.14`,
+ err: "text format parsing error in line 1: expected '\"' at start of label value",
+ },
+ // 7:
+ {
+ in: `
+# TYPE metric summary
+metric{quantile="bla"} 3.14
+`,
+ err: "text format parsing error in line 3: expected float as value for 'quantile' label",
+ },
+ // 8:
+ {
+ in: `metric{label="bla"+} 3.14`,
+ err: "text format parsing error in line 1: unexpected end of label value",
+ },
+ // 9:
+ {
+ in: `metric{label="bla"} 3.14 2.72
+`,
+ err: "text format parsing error in line 1: expected integer as timestamp",
+ },
+ // 10:
+ {
+ in: `metric{label="bla"} 3.14 2 3
+`,
+ err: "text format parsing error in line 1: spurious string after timestamp",
+ },
+ // 11:
+ {
+ in: `metric{label="bla"} blubb
+`,
+ err: "text format parsing error in line 1: expected float as value",
+ },
+ // 12:
+ {
+ in: `
+# HELP metric one
+# HELP metric two
+`,
+ err: "text format parsing error in line 3: second HELP line for metric name",
+ },
+ // 13:
+ {
+ in: `
+# TYPE metric counter
+# TYPE metric untyped
+`,
+ err: `text format parsing error in line 3: second TYPE line for metric name "metric", or TYPE reported after samples`,
+ },
+ // 14:
+ {
+ in: `
+metric 4.12
+# TYPE metric counter
+`,
+ err: `text format parsing error in line 3: second TYPE line for metric name "metric", or TYPE reported after samples`,
+ },
+ // 14:
+ {
+ in: `
+# TYPE metric bla
+`,
+ err: "text format parsing error in line 2: unknown metric type",
+ },
+ // 15:
+ {
+ in: `
+# TYPE met-ric
+`,
+ err: "text format parsing error in line 2: invalid metric name in comment",
+ },
+ // 16:
+ {
+ in: `@invalidmetric{label="bla"} 3.14 2`,
+ err: "text format parsing error in line 1: invalid metric name",
+ },
+ // 17:
+ {
+ in: `{label="bla"} 3.14 2`,
+ err: "text format parsing error in line 1: invalid metric name",
+ },
+ // 18:
+ {
+ in: `
+# TYPE metric histogram
+metric_bucket{le="bla"} 3.14
+`,
+ err: "text format parsing error in line 3: expected float as value for 'le' label",
+ },
+ }
+
+ for i, scenario := range scenarios {
+ _, err := parser.TextToMetricFamilies(strings.NewReader(scenario.in))
+ if err == nil {
+ t.Errorf("%d. expected error, got nil", i)
+ continue
+ }
+ if expected, got := scenario.err, err.Error(); strings.Index(got, expected) != 0 {
+ t.Errorf(
+ "%d. expected error starting with %q, got %q",
+ i, expected, got,
+ )
+ }
+ }
+
+}
+
+func TestTextParseError(t *testing.T) {
+ testTextParseError(t)
+}
+
+func BenchmarkParseError(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testTextParseError(b)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/alert.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/alert.go
new file mode 100644
index 000000000..b027e9f3d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/alert.go
@@ -0,0 +1,109 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "time"
+)
+
+type AlertStatus string
+
+const (
+ AlertFiring AlertStatus = "firing"
+ AlertResolved AlertStatus = "resolved"
+)
+
+// Alert is a generic representation of an alert in the Prometheus eco-system.
+type Alert struct {
+ // Label value pairs for purpose of aggregation, matching, and disposition
+ // dispatching. This must minimally include an "alertname" label.
+ Labels LabelSet `json:"labels"`
+
+ // Extra key/value information which does not define alert identity.
+ Annotations LabelSet `json:"annotations"`
+
+ // The known time range for this alert. Both ends are optional.
+ StartsAt time.Time `json:"startsAt,omitempty"`
+ EndsAt time.Time `json:"endsAt,omitempty"`
+}
+
+// Name returns the name of the alert. It is equivalent to the "alertname" label.
+func (a *Alert) Name() string {
+ return string(a.Labels[AlertNameLabel])
+}
+
+// Fingerprint returns a unique hash for the alert. It is equivalent to
+// the fingerprint of the alert's label set.
+func (a *Alert) Fingerprint() Fingerprint {
+ return a.Labels.Fingerprint()
+}
+
+func (a *Alert) String() string {
+ s := fmt.Sprintf("%s[%s]", a.Name(), a.Fingerprint().String()[:7])
+ if a.Resolved() {
+ return s + "[resolved]"
+ }
+ return s + "[active]"
+}
+
+// Resolved returns true iff the activity interval ended in the past.
+func (a *Alert) Resolved() bool {
+ if a.EndsAt.IsZero() {
+ return false
+ }
+ return !a.EndsAt.After(time.Now())
+}
+
+// Status returns the status of the alert.
+func (a *Alert) Status() AlertStatus {
+ if a.Resolved() {
+ return AlertResolved
+ }
+ return AlertFiring
+}
+
+// Alert is a list of alerts that can be sorted in chronological order.
+type Alerts []*Alert
+
+func (as Alerts) Len() int { return len(as) }
+func (as Alerts) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
+
+func (as Alerts) Less(i, j int) bool {
+ if as[i].StartsAt.Before(as[j].StartsAt) {
+ return true
+ }
+ if as[i].EndsAt.Before(as[j].EndsAt) {
+ return true
+ }
+ return as[i].Fingerprint() < as[j].Fingerprint()
+}
+
+// HasFiring returns true iff one of the alerts is not resolved.
+func (as Alerts) HasFiring() bool {
+ for _, a := range as {
+ if !a.Resolved() {
+ return true
+ }
+ }
+ return false
+}
+
+// Status returns StatusFiring iff at least one of the alerts is firing.
+func (as Alerts) Status() AlertStatus {
+ if as.HasFiring() {
+ return AlertFiring
+ }
+ return AlertResolved
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/fingerprinting.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/fingerprinting.go
new file mode 100644
index 000000000..fc4de4106
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/fingerprinting.go
@@ -0,0 +1,105 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "strconv"
+)
+
+// Fingerprint provides a hash-capable representation of a Metric.
+// For our purposes, FNV-1A 64-bit is used.
+type Fingerprint uint64
+
+// FingerprintFromString transforms a string representation into a Fingerprint.
+func FingerprintFromString(s string) (Fingerprint, error) {
+ num, err := strconv.ParseUint(s, 16, 64)
+ return Fingerprint(num), err
+}
+
+// ParseFingerprint parses the input string into a fingerprint.
+func ParseFingerprint(s string) (Fingerprint, error) {
+ num, err := strconv.ParseUint(s, 16, 64)
+ if err != nil {
+ return 0, err
+ }
+ return Fingerprint(num), nil
+}
+
+func (f Fingerprint) String() string {
+ return fmt.Sprintf("%016x", uint64(f))
+}
+
+// Fingerprints represents a collection of Fingerprint subject to a given
+// natural sorting scheme. It implements sort.Interface.
+type Fingerprints []Fingerprint
+
+// Len implements sort.Interface.
+func (f Fingerprints) Len() int {
+ return len(f)
+}
+
+// Less implements sort.Interface.
+func (f Fingerprints) Less(i, j int) bool {
+ return f[i] < f[j]
+}
+
+// Swap implements sort.Interface.
+func (f Fingerprints) Swap(i, j int) {
+ f[i], f[j] = f[j], f[i]
+}
+
+// FingerprintSet is a set of Fingerprints.
+type FingerprintSet map[Fingerprint]struct{}
+
+// Equal returns true if both sets contain the same elements (and not more).
+func (s FingerprintSet) Equal(o FingerprintSet) bool {
+ if len(s) != len(o) {
+ return false
+ }
+
+ for k := range s {
+ if _, ok := o[k]; !ok {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Intersection returns the elements contained in both sets.
+func (s FingerprintSet) Intersection(o FingerprintSet) FingerprintSet {
+ myLength, otherLength := len(s), len(o)
+ if myLength == 0 || otherLength == 0 {
+ return FingerprintSet{}
+ }
+
+ subSet := s
+ superSet := o
+
+ if otherLength < myLength {
+ subSet = o
+ superSet = s
+ }
+
+ out := FingerprintSet{}
+
+ for k := range subSet {
+ if _, ok := superSet[k]; ok {
+ out[k] = struct{}{}
+ }
+ }
+
+ return out
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go
new file mode 100644
index 000000000..6459c8f79
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labels.go
@@ -0,0 +1,188 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+const (
+ // AlertNameLabel is the name of the label containing the an alert's name.
+ AlertNameLabel = "alertname"
+
+ // ExportedLabelPrefix is the prefix to prepend to the label names present in
+ // exported metrics if a label of the same name is added by the server.
+ ExportedLabelPrefix = "exported_"
+
+ // MetricNameLabel is the label name indicating the metric name of a
+ // timeseries.
+ MetricNameLabel = "__name__"
+
+ // SchemeLabel is the name of the label that holds the scheme on which to
+ // scrape a target.
+ SchemeLabel = "__scheme__"
+
+ // AddressLabel is the name of the label that holds the address of
+ // a scrape target.
+ AddressLabel = "__address__"
+
+ // MetricsPathLabel is the name of the label that holds the path on which to
+ // scrape a target.
+ MetricsPathLabel = "__metrics_path__"
+
+ // ReservedLabelPrefix is a prefix which is not legal in user-supplied
+ // label names.
+ ReservedLabelPrefix = "__"
+
+ // MetaLabelPrefix is a prefix for labels that provide meta information.
+ // Labels with this prefix are used for intermediate label processing and
+ // will not be attached to time series.
+ MetaLabelPrefix = "__meta_"
+
+ // TmpLabelPrefix is a prefix for temporary labels as part of relabelling.
+ // Labels with this prefix are used for intermediate label processing and
+ // will not be attached to time series. This is reserved for use in
+ // Prometheus configuration files by users.
+ TmpLabelPrefix = "__tmp_"
+
+ // ParamLabelPrefix is a prefix for labels that provide URL parameters
+ // used to scrape a target.
+ ParamLabelPrefix = "__param_"
+
+ // JobLabel is the label name indicating the job from which a timeseries
+ // was scraped.
+ JobLabel = "job"
+
+ // InstanceLabel is the label name used for the instance label.
+ InstanceLabel = "instance"
+
+ // BucketLabel is used for the label that defines the upper bound of a
+ // bucket of a histogram ("le" -> "less or equal").
+ BucketLabel = "le"
+
+ // QuantileLabel is used for the label that defines the quantile in a
+ // summary.
+ QuantileLabel = "quantile"
+)
+
+// LabelNameRE is a regular expression matching valid label names.
+var LabelNameRE = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
+
+// A LabelName is a key for a LabelSet or Metric. It has a value associated
+// therewith.
+type LabelName string
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (ln *LabelName) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var s string
+ if err := unmarshal(&s); err != nil {
+ return err
+ }
+ if !LabelNameRE.MatchString(s) {
+ return fmt.Errorf("%q is not a valid label name", s)
+ }
+ *ln = LabelName(s)
+ return nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (ln *LabelName) UnmarshalJSON(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ if !LabelNameRE.MatchString(s) {
+ return fmt.Errorf("%q is not a valid label name", s)
+ }
+ *ln = LabelName(s)
+ return nil
+}
+
+// LabelNames is a sortable LabelName slice. In implements sort.Interface.
+type LabelNames []LabelName
+
+func (l LabelNames) Len() int {
+ return len(l)
+}
+
+func (l LabelNames) Less(i, j int) bool {
+ return l[i] < l[j]
+}
+
+func (l LabelNames) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+func (l LabelNames) String() string {
+ labelStrings := make([]string, 0, len(l))
+ for _, label := range l {
+ labelStrings = append(labelStrings, string(label))
+ }
+ return strings.Join(labelStrings, ", ")
+}
+
+// A LabelValue is an associated value for a LabelName.
+type LabelValue string
+
+// LabelValues is a sortable LabelValue slice. It implements sort.Interface.
+type LabelValues []LabelValue
+
+func (l LabelValues) Len() int {
+ return len(l)
+}
+
+func (l LabelValues) Less(i, j int) bool {
+ return sort.StringsAreSorted([]string{string(l[i]), string(l[j])})
+}
+
+func (l LabelValues) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+// LabelPair pairs a name with a value.
+type LabelPair struct {
+ Name LabelName
+ Value LabelValue
+}
+
+// LabelPairs is a sortable slice of LabelPair pointers. It implements
+// sort.Interface.
+type LabelPairs []*LabelPair
+
+func (l LabelPairs) Len() int {
+ return len(l)
+}
+
+func (l LabelPairs) Less(i, j int) bool {
+ switch {
+ case l[i].Name > l[j].Name:
+ return false
+ case l[i].Name < l[j].Name:
+ return true
+ case l[i].Value > l[j].Value:
+ return false
+ case l[i].Value < l[j].Value:
+ return true
+ default:
+ return false
+ }
+}
+
+func (l LabelPairs) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go
new file mode 100644
index 000000000..ab17025c7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labels_test.go
@@ -0,0 +1,91 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "sort"
+ "testing"
+)
+
+func testLabelNames(t testing.TB) {
+ var scenarios = []struct {
+ in LabelNames
+ out LabelNames
+ }{
+ {
+ in: LabelNames{"ZZZ", "zzz"},
+ out: LabelNames{"ZZZ", "zzz"},
+ },
+ {
+ in: LabelNames{"aaa", "AAA"},
+ out: LabelNames{"AAA", "aaa"},
+ },
+ }
+
+ for i, scenario := range scenarios {
+ sort.Sort(scenario.in)
+
+ for j, expected := range scenario.out {
+ if expected != scenario.in[j] {
+ t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j])
+ }
+ }
+ }
+}
+
+func TestLabelNames(t *testing.T) {
+ testLabelNames(t)
+}
+
+func BenchmarkLabelNames(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testLabelNames(b)
+ }
+}
+
+func testLabelValues(t testing.TB) {
+ var scenarios = []struct {
+ in LabelValues
+ out LabelValues
+ }{
+ {
+ in: LabelValues{"ZZZ", "zzz"},
+ out: LabelValues{"ZZZ", "zzz"},
+ },
+ {
+ in: LabelValues{"aaa", "AAA"},
+ out: LabelValues{"AAA", "aaa"},
+ },
+ }
+
+ for i, scenario := range scenarios {
+ sort.Sort(scenario.in)
+
+ for j, expected := range scenario.out {
+ if expected != scenario.in[j] {
+ t.Errorf("%d.%d expected %s, got %s", i, j, expected, scenario.in[j])
+ }
+ }
+ }
+}
+
+func TestLabelValues(t *testing.T) {
+ testLabelValues(t)
+}
+
+func BenchmarkLabelValues(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testLabelValues(b)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labelset.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labelset.go
new file mode 100644
index 000000000..142b9d1e2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/labelset.go
@@ -0,0 +1,153 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "sort"
+ "strings"
+)
+
+// A LabelSet is a collection of LabelName and LabelValue pairs. The LabelSet
+// may be fully-qualified down to the point where it may resolve to a single
+// Metric in the data store or not. All operations that occur within the realm
+// of a LabelSet can emit a vector of Metric entities to which the LabelSet may
+// match.
+type LabelSet map[LabelName]LabelValue
+
+func (ls LabelSet) Equal(o LabelSet) bool {
+ if len(ls) != len(o) {
+ return false
+ }
+ for ln, lv := range ls {
+ olv, ok := o[ln]
+ if !ok {
+ return false
+ }
+ if olv != lv {
+ return false
+ }
+ }
+ return true
+}
+
+// Before compares the metrics, using the following criteria:
+//
+// If m has fewer labels than o, it is before o. If it has more, it is not.
+//
+// If the number of labels is the same, the superset of all label names is
+// sorted alphanumerically. The first differing label pair found in that order
+// determines the outcome: If the label does not exist at all in m, then m is
+// before o, and vice versa. Otherwise the label value is compared
+// alphanumerically.
+//
+// If m and o are equal, the method returns false.
+func (ls LabelSet) Before(o LabelSet) bool {
+ if len(ls) < len(o) {
+ return true
+ }
+ if len(ls) > len(o) {
+ return false
+ }
+
+ lns := make(LabelNames, 0, len(ls)+len(o))
+ for ln := range ls {
+ lns = append(lns, ln)
+ }
+ for ln := range o {
+ lns = append(lns, ln)
+ }
+ // It's probably not worth it to de-dup lns.
+ sort.Sort(lns)
+ for _, ln := range lns {
+ mlv, ok := ls[ln]
+ if !ok {
+ return true
+ }
+ olv, ok := o[ln]
+ if !ok {
+ return false
+ }
+ if mlv < olv {
+ return true
+ }
+ if mlv > olv {
+ return false
+ }
+ }
+ return false
+}
+
+func (ls LabelSet) Clone() LabelSet {
+ lsn := make(LabelSet, len(ls))
+ for ln, lv := range ls {
+ lsn[ln] = lv
+ }
+ return lsn
+}
+
+// Merge is a helper function to non-destructively merge two label sets.
+func (l LabelSet) Merge(other LabelSet) LabelSet {
+ result := make(LabelSet, len(l))
+
+ for k, v := range l {
+ result[k] = v
+ }
+
+ for k, v := range other {
+ result[k] = v
+ }
+
+ return result
+}
+
+func (l LabelSet) String() string {
+ lstrs := make([]string, 0, len(l))
+ for l, v := range l {
+ lstrs = append(lstrs, fmt.Sprintf("%s=%q", l, v))
+ }
+
+ sort.Strings(lstrs)
+ return fmt.Sprintf("{%s}", strings.Join(lstrs, ", "))
+}
+
+// Fingerprint returns the LabelSet's fingerprint.
+func (ls LabelSet) Fingerprint() Fingerprint {
+ return labelSetToFingerprint(ls)
+}
+
+// FastFingerprint returns the LabelSet's Fingerprint calculated by a faster hashing
+// algorithm, which is, however, more susceptible to hash collisions.
+func (ls LabelSet) FastFingerprint() Fingerprint {
+ return labelSetToFastFingerprint(ls)
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (l *LabelSet) UnmarshalJSON(b []byte) error {
+ var m map[LabelName]LabelValue
+ if err := json.Unmarshal(b, &m); err != nil {
+ return err
+ }
+ // encoding/json only unmarshals maps of the form map[string]T. It treats
+ // LabelName as a string and does not call its UnmarshalJSON method.
+ // Thus, we have to replicate the behavior here.
+ for ln := range m {
+ if !LabelNameRE.MatchString(string(ln)) {
+ return fmt.Errorf("%q is not a valid label name", ln)
+ }
+ }
+ *l = LabelSet(m)
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go
new file mode 100644
index 000000000..25fc3c942
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/metric.go
@@ -0,0 +1,81 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+var separator = []byte{0}
+
+// A Metric is similar to a LabelSet, but the key difference is that a Metric is
+// a singleton and refers to one and only one stream of samples.
+type Metric LabelSet
+
+// Equal compares the metrics.
+func (m Metric) Equal(o Metric) bool {
+ return LabelSet(m).Equal(LabelSet(o))
+}
+
+// Before compares the metrics' underlying label sets.
+func (m Metric) Before(o Metric) bool {
+ return LabelSet(m).Before(LabelSet(o))
+}
+
+// Clone returns a copy of the Metric.
+func (m Metric) Clone() Metric {
+ clone := Metric{}
+ for k, v := range m {
+ clone[k] = v
+ }
+ return clone
+}
+
+func (m Metric) String() string {
+ metricName, hasName := m[MetricNameLabel]
+ numLabels := len(m) - 1
+ if !hasName {
+ numLabels = len(m)
+ }
+ labelStrings := make([]string, 0, numLabels)
+ for label, value := range m {
+ if label != MetricNameLabel {
+ labelStrings = append(labelStrings, fmt.Sprintf("%s=%q", label, value))
+ }
+ }
+
+ switch numLabels {
+ case 0:
+ if hasName {
+ return string(metricName)
+ }
+ return "{}"
+ default:
+ sort.Strings(labelStrings)
+ return fmt.Sprintf("%s{%s}", metricName, strings.Join(labelStrings, ", "))
+ }
+}
+
+// Fingerprint returns a Metric's Fingerprint.
+func (m Metric) Fingerprint() Fingerprint {
+ return LabelSet(m).Fingerprint()
+}
+
+// FastFingerprint returns a Metric's Fingerprint calculated by a faster hashing
+// algorithm, which is, however, more susceptible to hash collisions.
+func (m Metric) FastFingerprint() Fingerprint {
+ return LabelSet(m).FastFingerprint()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go
new file mode 100644
index 000000000..5c7cfceaf
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/metric_test.go
@@ -0,0 +1,83 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import "testing"
+
+func testMetric(t testing.TB) {
+ var scenarios = []struct {
+ input LabelSet
+ fingerprint Fingerprint
+ fastFingerprint Fingerprint
+ }{
+ {
+ input: LabelSet{},
+ fingerprint: 14695981039346656037,
+ fastFingerprint: 14695981039346656037,
+ },
+ {
+ input: LabelSet{
+ "first_name": "electro",
+ "occupation": "robot",
+ "manufacturer": "westinghouse",
+ },
+ fingerprint: 5911716720268894962,
+ fastFingerprint: 11310079640881077873,
+ },
+ {
+ input: LabelSet{
+ "x": "y",
+ },
+ fingerprint: 8241431561484471700,
+ fastFingerprint: 13948396922932177635,
+ },
+ {
+ input: LabelSet{
+ "a": "bb",
+ "b": "c",
+ },
+ fingerprint: 3016285359649981711,
+ fastFingerprint: 3198632812309449502,
+ },
+ {
+ input: LabelSet{
+ "a": "b",
+ "bb": "c",
+ },
+ fingerprint: 7122421792099404749,
+ fastFingerprint: 5774953389407657638,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ input := Metric(scenario.input)
+
+ if scenario.fingerprint != input.Fingerprint() {
+ t.Errorf("%d. expected %d, got %d", i, scenario.fingerprint, input.Fingerprint())
+ }
+ if scenario.fastFingerprint != input.FastFingerprint() {
+ t.Errorf("%d. expected %d, got %d", i, scenario.fastFingerprint, input.FastFingerprint())
+ }
+ }
+}
+
+func TestMetric(t *testing.T) {
+ testMetric(t)
+}
+
+func BenchmarkMetric(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ testMetric(b)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/model.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/model.go
new file mode 100644
index 000000000..88f013a47
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/model.go
@@ -0,0 +1,16 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package model contains common data structures that are shared across
+// Prometheus componenets and libraries.
+package model
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/signature.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/signature.go
new file mode 100644
index 000000000..28f370065
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/signature.go
@@ -0,0 +1,190 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "bytes"
+ "hash"
+ "hash/fnv"
+ "sort"
+ "sync"
+)
+
+// SeparatorByte is a byte that cannot occur in valid UTF-8 sequences and is
+// used to separate label names, label values, and other strings from each other
+// when calculating their combined hash value (aka signature aka fingerprint).
+const SeparatorByte byte = 255
+
+var (
+ // cache the signature of an empty label set.
+ emptyLabelSignature = fnv.New64a().Sum64()
+
+ hashAndBufPool sync.Pool
+)
+
+type hashAndBuf struct {
+ h hash.Hash64
+ b bytes.Buffer
+}
+
+func getHashAndBuf() *hashAndBuf {
+ hb := hashAndBufPool.Get()
+ if hb == nil {
+ return &hashAndBuf{h: fnv.New64a()}
+ }
+ return hb.(*hashAndBuf)
+}
+
+func putHashAndBuf(hb *hashAndBuf) {
+ hb.h.Reset()
+ hb.b.Reset()
+ hashAndBufPool.Put(hb)
+}
+
+// LabelsToSignature returns a quasi-unique signature (i.e., fingerprint) for a
+// given label set. (Collisions are possible but unlikely if the number of label
+// sets the function is applied to is small.)
+func LabelsToSignature(labels map[string]string) uint64 {
+ if len(labels) == 0 {
+ return emptyLabelSignature
+ }
+
+ labelNames := make([]string, 0, len(labels))
+ for labelName := range labels {
+ labelNames = append(labelNames, labelName)
+ }
+ sort.Strings(labelNames)
+
+ hb := getHashAndBuf()
+ defer putHashAndBuf(hb)
+
+ for _, labelName := range labelNames {
+ hb.b.WriteString(labelName)
+ hb.b.WriteByte(SeparatorByte)
+ hb.b.WriteString(labels[labelName])
+ hb.b.WriteByte(SeparatorByte)
+ hb.h.Write(hb.b.Bytes())
+ hb.b.Reset()
+ }
+ return hb.h.Sum64()
+}
+
+// labelSetToFingerprint works exactly as LabelsToSignature but takes a LabelSet as
+// parameter (rather than a label map) and returns a Fingerprint.
+func labelSetToFingerprint(ls LabelSet) Fingerprint {
+ if len(ls) == 0 {
+ return Fingerprint(emptyLabelSignature)
+ }
+
+ labelNames := make(LabelNames, 0, len(ls))
+ for labelName := range ls {
+ labelNames = append(labelNames, labelName)
+ }
+ sort.Sort(labelNames)
+
+ hb := getHashAndBuf()
+ defer putHashAndBuf(hb)
+
+ for _, labelName := range labelNames {
+ hb.b.WriteString(string(labelName))
+ hb.b.WriteByte(SeparatorByte)
+ hb.b.WriteString(string(ls[labelName]))
+ hb.b.WriteByte(SeparatorByte)
+ hb.h.Write(hb.b.Bytes())
+ hb.b.Reset()
+ }
+ return Fingerprint(hb.h.Sum64())
+}
+
+// labelSetToFastFingerprint works similar to labelSetToFingerprint but uses a
+// faster and less allocation-heavy hash function, which is more susceptible to
+// create hash collisions. Therefore, collision detection should be applied.
+func labelSetToFastFingerprint(ls LabelSet) Fingerprint {
+ if len(ls) == 0 {
+ return Fingerprint(emptyLabelSignature)
+ }
+
+ var result uint64
+ hb := getHashAndBuf()
+ defer putHashAndBuf(hb)
+
+ for labelName, labelValue := range ls {
+ hb.b.WriteString(string(labelName))
+ hb.b.WriteByte(SeparatorByte)
+ hb.b.WriteString(string(labelValue))
+ hb.h.Write(hb.b.Bytes())
+ result ^= hb.h.Sum64()
+ hb.h.Reset()
+ hb.b.Reset()
+ }
+ return Fingerprint(result)
+}
+
+// SignatureForLabels works like LabelsToSignature but takes a Metric as
+// parameter (rather than a label map) and only includes the labels with the
+// specified LabelNames into the signature calculation. The labels passed in
+// will be sorted by this function.
+func SignatureForLabels(m Metric, labels ...LabelName) uint64 {
+ if len(m) == 0 || len(labels) == 0 {
+ return emptyLabelSignature
+ }
+
+ sort.Sort(LabelNames(labels))
+
+ hb := getHashAndBuf()
+ defer putHashAndBuf(hb)
+
+ for _, label := range labels {
+ hb.b.WriteString(string(label))
+ hb.b.WriteByte(SeparatorByte)
+ hb.b.WriteString(string(m[label]))
+ hb.b.WriteByte(SeparatorByte)
+ hb.h.Write(hb.b.Bytes())
+ hb.b.Reset()
+ }
+ return hb.h.Sum64()
+}
+
+// SignatureWithoutLabels works like LabelsToSignature but takes a Metric as
+// parameter (rather than a label map) and excludes the labels with any of the
+// specified LabelNames from the signature calculation.
+func SignatureWithoutLabels(m Metric, labels map[LabelName]struct{}) uint64 {
+ if len(m) == 0 {
+ return emptyLabelSignature
+ }
+
+ labelNames := make(LabelNames, 0, len(m))
+ for labelName := range m {
+ if _, exclude := labels[labelName]; !exclude {
+ labelNames = append(labelNames, labelName)
+ }
+ }
+ if len(labelNames) == 0 {
+ return emptyLabelSignature
+ }
+ sort.Sort(labelNames)
+
+ hb := getHashAndBuf()
+ defer putHashAndBuf(hb)
+
+ for _, labelName := range labelNames {
+ hb.b.WriteString(string(labelName))
+ hb.b.WriteByte(SeparatorByte)
+ hb.b.WriteString(string(m[labelName]))
+ hb.b.WriteByte(SeparatorByte)
+ hb.h.Write(hb.b.Bytes())
+ hb.b.Reset()
+ }
+ return hb.h.Sum64()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go
new file mode 100644
index 000000000..d9c665f8c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/signature_test.go
@@ -0,0 +1,304 @@
+// Copyright 2014 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "runtime"
+ "sync"
+ "testing"
+)
+
+func TestLabelsToSignature(t *testing.T) {
+ var scenarios = []struct {
+ in map[string]string
+ out uint64
+ }{
+ {
+ in: map[string]string{},
+ out: 14695981039346656037,
+ },
+ {
+ in: map[string]string{"name": "garland, briggs", "fear": "love is not enough"},
+ out: 5799056148416392346,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ actual := LabelsToSignature(scenario.in)
+
+ if actual != scenario.out {
+ t.Errorf("%d. expected %d, got %d", i, scenario.out, actual)
+ }
+ }
+}
+
+func TestMetricToFingerprint(t *testing.T) {
+ var scenarios = []struct {
+ in LabelSet
+ out Fingerprint
+ }{
+ {
+ in: LabelSet{},
+ out: 14695981039346656037,
+ },
+ {
+ in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"},
+ out: 5799056148416392346,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ actual := labelSetToFingerprint(scenario.in)
+
+ if actual != scenario.out {
+ t.Errorf("%d. expected %d, got %d", i, scenario.out, actual)
+ }
+ }
+}
+
+func TestMetricToFastFingerprint(t *testing.T) {
+ var scenarios = []struct {
+ in LabelSet
+ out Fingerprint
+ }{
+ {
+ in: LabelSet{},
+ out: 14695981039346656037,
+ },
+ {
+ in: LabelSet{"name": "garland, briggs", "fear": "love is not enough"},
+ out: 12952432476264840823,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ actual := labelSetToFastFingerprint(scenario.in)
+
+ if actual != scenario.out {
+ t.Errorf("%d. expected %d, got %d", i, scenario.out, actual)
+ }
+ }
+}
+
+func TestSignatureForLabels(t *testing.T) {
+ var scenarios = []struct {
+ in Metric
+ labels LabelNames
+ out uint64
+ }{
+ {
+ in: Metric{},
+ labels: nil,
+ out: 14695981039346656037,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
+ labels: LabelNames{"fear", "name"},
+ out: 5799056148416392346,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"},
+ labels: LabelNames{"fear", "name"},
+ out: 5799056148416392346,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
+ labels: LabelNames{},
+ out: 14695981039346656037,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
+ labels: nil,
+ out: 14695981039346656037,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ actual := SignatureForLabels(scenario.in, scenario.labels...)
+
+ if actual != scenario.out {
+ t.Errorf("%d. expected %d, got %d", i, scenario.out, actual)
+ }
+ }
+}
+
+func TestSignatureWithoutLabels(t *testing.T) {
+ var scenarios = []struct {
+ in Metric
+ labels map[LabelName]struct{}
+ out uint64
+ }{
+ {
+ in: Metric{},
+ labels: nil,
+ out: 14695981039346656037,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
+ labels: map[LabelName]struct{}{"fear": struct{}{}, "name": struct{}{}},
+ out: 14695981039346656037,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough", "foo": "bar"},
+ labels: map[LabelName]struct{}{"foo": struct{}{}},
+ out: 5799056148416392346,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
+ labels: map[LabelName]struct{}{},
+ out: 5799056148416392346,
+ },
+ {
+ in: Metric{"name": "garland, briggs", "fear": "love is not enough"},
+ labels: nil,
+ out: 5799056148416392346,
+ },
+ }
+
+ for i, scenario := range scenarios {
+ actual := SignatureWithoutLabels(scenario.in, scenario.labels)
+
+ if actual != scenario.out {
+ t.Errorf("%d. expected %d, got %d", i, scenario.out, actual)
+ }
+ }
+}
+
+func benchmarkLabelToSignature(b *testing.B, l map[string]string, e uint64) {
+ for i := 0; i < b.N; i++ {
+ if a := LabelsToSignature(l); a != e {
+ b.Fatalf("expected signature of %d for %s, got %d", e, l, a)
+ }
+ }
+}
+
+func BenchmarkLabelToSignatureScalar(b *testing.B) {
+ benchmarkLabelToSignature(b, nil, 14695981039346656037)
+}
+
+func BenchmarkLabelToSignatureSingle(b *testing.B) {
+ benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value"}, 5146282821936882169)
+}
+
+func BenchmarkLabelToSignatureDouble(b *testing.B) {
+ benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717)
+}
+
+func BenchmarkLabelToSignatureTriple(b *testing.B) {
+ benchmarkLabelToSignature(b, map[string]string{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121)
+}
+
+func benchmarkMetricToFingerprint(b *testing.B, ls LabelSet, e Fingerprint) {
+ for i := 0; i < b.N; i++ {
+ if a := labelSetToFingerprint(ls); a != e {
+ b.Fatalf("expected signature of %d for %s, got %d", e, ls, a)
+ }
+ }
+}
+
+func BenchmarkMetricToFingerprintScalar(b *testing.B) {
+ benchmarkMetricToFingerprint(b, nil, 14695981039346656037)
+}
+
+func BenchmarkMetricToFingerprintSingle(b *testing.B) {
+ benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5146282821936882169)
+}
+
+func BenchmarkMetricToFingerprintDouble(b *testing.B) {
+ benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 3195800080984914717)
+}
+
+func BenchmarkMetricToFingerprintTriple(b *testing.B) {
+ benchmarkMetricToFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 13843036195897128121)
+}
+
+func benchmarkMetricToFastFingerprint(b *testing.B, ls LabelSet, e Fingerprint) {
+ for i := 0; i < b.N; i++ {
+ if a := labelSetToFastFingerprint(ls); a != e {
+ b.Fatalf("expected signature of %d for %s, got %d", e, ls, a)
+ }
+ }
+}
+
+func BenchmarkMetricToFastFingerprintScalar(b *testing.B) {
+ benchmarkMetricToFastFingerprint(b, nil, 14695981039346656037)
+}
+
+func BenchmarkMetricToFastFingerprintSingle(b *testing.B) {
+ benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value"}, 5147259542624943964)
+}
+
+func BenchmarkMetricToFastFingerprintDouble(b *testing.B) {
+ benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value"}, 18269973311206963528)
+}
+
+func BenchmarkMetricToFastFingerprintTriple(b *testing.B) {
+ benchmarkMetricToFastFingerprint(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676)
+}
+
+func BenchmarkEmptyLabelSignature(b *testing.B) {
+ input := []map[string]string{nil, {}}
+
+ var ms runtime.MemStats
+ runtime.ReadMemStats(&ms)
+
+ alloc := ms.Alloc
+
+ for _, labels := range input {
+ LabelsToSignature(labels)
+ }
+
+ runtime.ReadMemStats(&ms)
+
+ if got := ms.Alloc; alloc != got {
+ b.Fatal("expected LabelsToSignature with empty labels not to perform allocations")
+ }
+}
+
+func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerprint, concLevel int) {
+ var start, end sync.WaitGroup
+ start.Add(1)
+ end.Add(concLevel)
+
+ for i := 0; i < concLevel; i++ {
+ go func() {
+ start.Wait()
+ for j := b.N / concLevel; j >= 0; j-- {
+ if a := labelSetToFastFingerprint(ls); a != e {
+ b.Fatalf("expected signature of %d for %s, got %d", e, ls, a)
+ }
+ }
+ end.Done()
+ }()
+ }
+ b.ResetTimer()
+ start.Done()
+ end.Wait()
+}
+
+func BenchmarkMetricToFastFingerprintTripleConc1(b *testing.B) {
+ benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 1)
+}
+
+func BenchmarkMetricToFastFingerprintTripleConc2(b *testing.B) {
+ benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 2)
+}
+
+func BenchmarkMetricToFastFingerprintTripleConc4(b *testing.B) {
+ benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 4)
+}
+
+func BenchmarkMetricToFastFingerprintTripleConc8(b *testing.B) {
+ benchmarkMetricToFastFingerprintConc(b, LabelSet{"first-label": "first-label-value", "second-label": "second-label-value", "third-label": "third-label-value"}, 15738406913934009676, 8)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/silence.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/silence.go
new file mode 100644
index 000000000..b4b96eae9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/silence.go
@@ -0,0 +1,60 @@
+// Copyright 2015 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "time"
+)
+
+// Matcher describes a matches the value of a given label.
+type Matcher struct {
+ Name LabelName `json:"name"`
+ Value string `json:"value"`
+ IsRegex bool `json:"isRegex"`
+}
+
+func (m *Matcher) UnmarshalJSON(b []byte) error {
+ type plain Matcher
+ if err := json.Unmarshal(b, (*plain)(m)); err != nil {
+ return err
+ }
+
+ if len(m.Name) == 0 {
+ return fmt.Errorf("label name in matcher must not be empty")
+ }
+ if m.IsRegex {
+ if _, err := regexp.Compile(m.Value); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Silence defines the representation of a silence definiton
+// in the Prometheus eco-system.
+type Silence struct {
+ ID uint64 `json:"id,omitempty"`
+
+ Matchers []*Matcher `json:"matchers"`
+
+ StartsAt time.Time `json:"startsAt"`
+ EndsAt time.Time `json:"endsAt"`
+
+ CreatedAt time.Time `json:"createdAt,omitempty"`
+ CreatedBy string `json:"createdBy"`
+ Comment string `json:"comment,omitempty"`
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/time.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/time.go
new file mode 100644
index 000000000..ebc8bf6cc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/time.go
@@ -0,0 +1,230 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "fmt"
+ "math"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const (
+ // MinimumTick is the minimum supported time resolution. This has to be
+ // at least time.Second in order for the code below to work.
+ minimumTick = time.Millisecond
+ // second is the Time duration equivalent to one second.
+ second = int64(time.Second / minimumTick)
+ // The number of nanoseconds per minimum tick.
+ nanosPerTick = int64(minimumTick / time.Nanosecond)
+
+ // Earliest is the earliest Time representable. Handy for
+ // initializing a high watermark.
+ Earliest = Time(math.MinInt64)
+ // Latest is the latest Time representable. Handy for initializing
+ // a low watermark.
+ Latest = Time(math.MaxInt64)
+)
+
+// Time is the number of milliseconds since the epoch
+// (1970-01-01 00:00 UTC) excluding leap seconds.
+type Time int64
+
+// Interval describes and interval between two timestamps.
+type Interval struct {
+ Start, End Time
+}
+
+// Now returns the current time as a Time.
+func Now() Time {
+ return TimeFromUnixNano(time.Now().UnixNano())
+}
+
+// TimeFromUnix returns the Time equivalent to the Unix Time t
+// provided in seconds.
+func TimeFromUnix(t int64) Time {
+ return Time(t * second)
+}
+
+// TimeFromUnixNano returns the Time equivalent to the Unix Time
+// t provided in nanoseconds.
+func TimeFromUnixNano(t int64) Time {
+ return Time(t / nanosPerTick)
+}
+
+// Equal reports whether two Times represent the same instant.
+func (t Time) Equal(o Time) bool {
+ return t == o
+}
+
+// Before reports whether the Time t is before o.
+func (t Time) Before(o Time) bool {
+ return t < o
+}
+
+// After reports whether the Time t is after o.
+func (t Time) After(o Time) bool {
+ return t > o
+}
+
+// Add returns the Time t + d.
+func (t Time) Add(d time.Duration) Time {
+ return t + Time(d/minimumTick)
+}
+
+// Sub returns the Duration t - o.
+func (t Time) Sub(o Time) time.Duration {
+ return time.Duration(t-o) * minimumTick
+}
+
+// Time returns the time.Time representation of t.
+func (t Time) Time() time.Time {
+ return time.Unix(int64(t)/second, (int64(t)%second)*nanosPerTick)
+}
+
+// Unix returns t as a Unix time, the number of seconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) Unix() int64 {
+ return int64(t) / second
+}
+
+// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
+// since January 1, 1970 UTC.
+func (t Time) UnixNano() int64 {
+ return int64(t) * nanosPerTick
+}
+
+// The number of digits after the dot.
+var dotPrecision = int(math.Log10(float64(second)))
+
+// String returns a string representation of the Time.
+func (t Time) String() string {
+ return strconv.FormatFloat(float64(t)/float64(second), 'f', -1, 64)
+}
+
+// MarshalJSON implements the json.Marshaler interface.
+func (t Time) MarshalJSON() ([]byte, error) {
+ return []byte(t.String()), nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaler interface.
+func (t *Time) UnmarshalJSON(b []byte) error {
+ p := strings.Split(string(b), ".")
+ switch len(p) {
+ case 1:
+ v, err := strconv.ParseInt(string(p[0]), 10, 64)
+ if err != nil {
+ return err
+ }
+ *t = Time(v * second)
+
+ case 2:
+ v, err := strconv.ParseInt(string(p[0]), 10, 64)
+ if err != nil {
+ return err
+ }
+ v *= second
+
+ prec := dotPrecision - len(p[1])
+ if prec < 0 {
+ p[1] = p[1][:dotPrecision]
+ } else if prec > 0 {
+ p[1] = p[1] + strings.Repeat("0", prec)
+ }
+
+ va, err := strconv.ParseInt(p[1], 10, 32)
+ if err != nil {
+ return err
+ }
+
+ *t = Time(v + va)
+
+ default:
+ return fmt.Errorf("invalid time %q", string(b))
+ }
+ return nil
+}
+
+// Duration wraps time.Duration. It is used to parse the custom duration format
+// from YAML.
+// This type should not propagate beyond the scope of input/output processing.
+type Duration time.Duration
+
+// StringToDuration parses a string into a time.Duration, assuming that a year
+// a day always has 24h.
+func ParseDuration(durationStr string) (Duration, error) {
+ matches := durationRE.FindStringSubmatch(durationStr)
+ if len(matches) != 3 {
+ return 0, fmt.Errorf("not a valid duration string: %q", durationStr)
+ }
+ durSeconds, _ := strconv.Atoi(matches[1])
+ dur := time.Duration(durSeconds) * time.Second
+ unit := matches[2]
+ switch unit {
+ case "d":
+ dur *= 60 * 60 * 24
+ case "h":
+ dur *= 60 * 60
+ case "m":
+ dur *= 60
+ case "s":
+ dur *= 1
+ default:
+ return 0, fmt.Errorf("invalid time unit in duration string: %q", unit)
+ }
+ return Duration(dur), nil
+}
+
+var durationRE = regexp.MustCompile("^([0-9]+)([ywdhms]+)$")
+
+func (d Duration) String() string {
+ seconds := int64(time.Duration(d) / time.Second)
+ factors := map[string]int64{
+ "d": 60 * 60 * 24,
+ "h": 60 * 60,
+ "m": 60,
+ "s": 1,
+ }
+ unit := "s"
+ switch int64(0) {
+ case seconds % factors["d"]:
+ unit = "d"
+ case seconds % factors["h"]:
+ unit = "h"
+ case seconds % factors["m"]:
+ unit = "m"
+ }
+ return fmt.Sprintf("%v%v", seconds/factors[unit], unit)
+}
+
+// MarshalYAML implements the yaml.Marshaler interface.
+func (d Duration) MarshalYAML() (interface{}, error) {
+ return d.String(), nil
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ var s string
+ if err := unmarshal(&s); err != nil {
+ return err
+ }
+ dur, err := ParseDuration(s)
+ if err != nil {
+ return err
+ }
+ *d = dur
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go
new file mode 100644
index 000000000..9013a6277
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/time_test.go
@@ -0,0 +1,86 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "testing"
+ "time"
+)
+
+func TestComparators(t *testing.T) {
+ t1a := TimeFromUnix(0)
+ t1b := TimeFromUnix(0)
+ t2 := TimeFromUnix(2*second - 1)
+
+ if !t1a.Equal(t1b) {
+ t.Fatalf("Expected %s to be equal to %s", t1a, t1b)
+ }
+ if t1a.Equal(t2) {
+ t.Fatalf("Expected %s to not be equal to %s", t1a, t2)
+ }
+
+ if !t1a.Before(t2) {
+ t.Fatalf("Expected %s to be before %s", t1a, t2)
+ }
+ if t1a.Before(t1b) {
+ t.Fatalf("Expected %s to not be before %s", t1a, t1b)
+ }
+
+ if !t2.After(t1a) {
+ t.Fatalf("Expected %s to be after %s", t2, t1a)
+ }
+ if t1b.After(t1a) {
+ t.Fatalf("Expected %s to not be after %s", t1b, t1a)
+ }
+}
+
+func TestTimeConversions(t *testing.T) {
+ unixSecs := int64(1136239445)
+ unixNsecs := int64(123456789)
+ unixNano := unixSecs*1e9 + unixNsecs
+
+ t1 := time.Unix(unixSecs, unixNsecs-unixNsecs%nanosPerTick)
+ t2 := time.Unix(unixSecs, unixNsecs)
+
+ ts := TimeFromUnixNano(unixNano)
+ if !ts.Time().Equal(t1) {
+ t.Fatalf("Expected %s, got %s", t1, ts.Time())
+ }
+
+ // Test available precision.
+ ts = TimeFromUnixNano(t2.UnixNano())
+ if !ts.Time().Equal(t1) {
+ t.Fatalf("Expected %s, got %s", t1, ts.Time())
+ }
+
+ if ts.UnixNano() != unixNano-unixNano%nanosPerTick {
+ t.Fatalf("Expected %d, got %d", unixNano, ts.UnixNano())
+ }
+}
+
+func TestDuration(t *testing.T) {
+ duration := time.Second + time.Minute + time.Hour
+ goTime := time.Unix(1136239445, 0)
+
+ ts := TimeFromUnix(goTime.Unix())
+ if !goTime.Add(duration).Equal(ts.Add(duration).Time()) {
+ t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration))
+ }
+
+ earlier := ts.Add(-duration)
+ delta := ts.Sub(earlier)
+ if delta != duration {
+ t.Fatalf("Expected %s to be equal to %s", delta, duration)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/value.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/value.go
new file mode 100644
index 000000000..10ffb0bd6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/value.go
@@ -0,0 +1,395 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// A SampleValue is a representation of a value for a given sample at a given
+// time.
+type SampleValue float64
+
+// MarshalJSON implements json.Marshaler.
+func (v SampleValue) MarshalJSON() ([]byte, error) {
+ return json.Marshal(v.String())
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (v *SampleValue) UnmarshalJSON(b []byte) error {
+ if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
+ return fmt.Errorf("sample value must be a quoted string")
+ }
+ f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64)
+ if err != nil {
+ return err
+ }
+ *v = SampleValue(f)
+ return nil
+}
+
+func (v SampleValue) Equal(o SampleValue) bool {
+ return v == o
+}
+
+func (v SampleValue) String() string {
+ return strconv.FormatFloat(float64(v), 'f', -1, 64)
+}
+
+// SamplePair pairs a SampleValue with a Timestamp.
+type SamplePair struct {
+ Timestamp Time
+ Value SampleValue
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s SamplePair) MarshalJSON() ([]byte, error) {
+ t, err := json.Marshal(s.Timestamp)
+ if err != nil {
+ return nil, err
+ }
+ v, err := json.Marshal(s.Value)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(fmt.Sprintf("[%s,%s]", t, v)), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *SamplePair) UnmarshalJSON(b []byte) error {
+ v := [...]json.Unmarshaler{&s.Timestamp, &s.Value}
+ return json.Unmarshal(b, &v)
+}
+
+// Equal returns true if this SamplePair and o have equal Values and equal
+// Timestamps.
+func (s *SamplePair) Equal(o *SamplePair) bool {
+ return s == o || (s.Value == o.Value && s.Timestamp.Equal(o.Timestamp))
+}
+
+func (s SamplePair) String() string {
+ return fmt.Sprintf("%s @[%s]", s.Value, s.Timestamp)
+}
+
+// Sample is a sample pair associated with a metric.
+type Sample struct {
+ Metric Metric `json:"metric"`
+ Value SampleValue `json:"value"`
+ Timestamp Time `json:"timestamp"`
+}
+
+// Equal compares first the metrics, then the timestamp, then the value.
+func (s *Sample) Equal(o *Sample) bool {
+ if s == o {
+ return true
+ }
+
+ if !s.Metric.Equal(o.Metric) {
+ return false
+ }
+ if !s.Timestamp.Equal(o.Timestamp) {
+ return false
+ }
+ if s.Value != o.Value {
+ return false
+ }
+
+ return true
+}
+
+func (s Sample) String() string {
+ return fmt.Sprintf("%s => %s", s.Metric, SamplePair{
+ Timestamp: s.Timestamp,
+ Value: s.Value,
+ })
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s Sample) MarshalJSON() ([]byte, error) {
+ v := struct {
+ Metric Metric `json:"metric"`
+ Value SamplePair `json:"value"`
+ }{
+ Metric: s.Metric,
+ Value: SamplePair{
+ Timestamp: s.Timestamp,
+ Value: s.Value,
+ },
+ }
+
+ return json.Marshal(&v)
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *Sample) UnmarshalJSON(b []byte) error {
+ v := struct {
+ Metric Metric `json:"metric"`
+ Value SamplePair `json:"value"`
+ }{
+ Metric: s.Metric,
+ Value: SamplePair{
+ Timestamp: s.Timestamp,
+ Value: s.Value,
+ },
+ }
+
+ if err := json.Unmarshal(b, &v); err != nil {
+ return err
+ }
+
+ s.Metric = v.Metric
+ s.Timestamp = v.Value.Timestamp
+ s.Value = v.Value.Value
+
+ return nil
+}
+
+// Samples is a sortable Sample slice. It implements sort.Interface.
+type Samples []*Sample
+
+func (s Samples) Len() int {
+ return len(s)
+}
+
+// Less compares first the metrics, then the timestamp.
+func (s Samples) Less(i, j int) bool {
+ switch {
+ case s[i].Metric.Before(s[j].Metric):
+ return true
+ case s[j].Metric.Before(s[i].Metric):
+ return false
+ case s[i].Timestamp.Before(s[j].Timestamp):
+ return true
+ default:
+ return false
+ }
+}
+
+func (s Samples) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+// Equal compares two sets of samples and returns true if they are equal.
+func (s Samples) Equal(o Samples) bool {
+ if len(s) != len(o) {
+ return false
+ }
+
+ for i, sample := range s {
+ if !sample.Equal(o[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// SampleStream is a stream of Values belonging to an attached COWMetric.
+type SampleStream struct {
+ Metric Metric `json:"metric"`
+ Values []SamplePair `json:"values"`
+}
+
+func (ss SampleStream) String() string {
+ vals := make([]string, len(ss.Values))
+ for i, v := range ss.Values {
+ vals[i] = v.String()
+ }
+ return fmt.Sprintf("%s =>\n%s", ss.Metric, strings.Join(vals, "\n"))
+}
+
+// Value is a generic interface for values resulting from a query evaluation.
+type Value interface {
+ Type() ValueType
+ String() string
+}
+
+func (Matrix) Type() ValueType { return ValMatrix }
+func (Vector) Type() ValueType { return ValVector }
+func (*Scalar) Type() ValueType { return ValScalar }
+func (*String) Type() ValueType { return ValString }
+
+type ValueType int
+
+const (
+ ValNone ValueType = iota
+ ValScalar
+ ValVector
+ ValMatrix
+ ValString
+)
+
+// MarshalJSON implements json.Marshaler.
+func (et ValueType) MarshalJSON() ([]byte, error) {
+ return json.Marshal(et.String())
+}
+
+func (et *ValueType) UnmarshalJSON(b []byte) error {
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+ switch s {
+ case "":
+ *et = ValNone
+ case "scalar":
+ *et = ValScalar
+ case "vector":
+ *et = ValVector
+ case "matrix":
+ *et = ValMatrix
+ case "string":
+ *et = ValString
+ default:
+ return fmt.Errorf("unknown value type %q", s)
+ }
+ return nil
+}
+
+func (e ValueType) String() string {
+ switch e {
+ case ValNone:
+ return ""
+ case ValScalar:
+ return "scalar"
+ case ValVector:
+ return "vector"
+ case ValMatrix:
+ return "matrix"
+ case ValString:
+ return "string"
+ }
+ panic("ValueType.String: unhandled value type")
+}
+
+// Scalar is a scalar value evaluated at the set timestamp.
+type Scalar struct {
+ Value SampleValue `json:"value"`
+ Timestamp Time `json:"timestamp"`
+}
+
+func (s Scalar) String() string {
+ return fmt.Sprintf("scalar: %v @[%v]", s.Value, s.Timestamp)
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s Scalar) MarshalJSON() ([]byte, error) {
+ v := strconv.FormatFloat(float64(s.Value), 'f', -1, 64)
+ return json.Marshal([...]interface{}{s.Timestamp, string(v)})
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *Scalar) UnmarshalJSON(b []byte) error {
+ var f string
+ v := [...]interface{}{&s.Timestamp, &f}
+
+ if err := json.Unmarshal(b, &v); err != nil {
+ return err
+ }
+
+ value, err := strconv.ParseFloat(f, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing sample value: %s", err)
+ }
+ s.Value = SampleValue(value)
+ return nil
+}
+
+// String is a string value evaluated at the set timestamp.
+type String struct {
+ Value string `json:"value"`
+ Timestamp Time `json:"timestamp"`
+}
+
+func (s *String) String() string {
+ return s.Value
+}
+
+// MarshalJSON implements json.Marshaler.
+func (s String) MarshalJSON() ([]byte, error) {
+ return json.Marshal([]interface{}{s.Timestamp, s.Value})
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (s *String) UnmarshalJSON(b []byte) error {
+ v := [...]interface{}{&s.Timestamp, &s.Value}
+ return json.Unmarshal(b, &v)
+}
+
+// Vector is basically only an alias for Samples, but the
+// contract is that in a Vector, all Samples have the same timestamp.
+type Vector []*Sample
+
+func (vec Vector) String() string {
+ entries := make([]string, len(vec))
+ for i, s := range vec {
+ entries[i] = s.String()
+ }
+ return strings.Join(entries, "\n")
+}
+
+func (vec Vector) Len() int { return len(vec) }
+func (vec Vector) Swap(i, j int) { vec[i], vec[j] = vec[j], vec[i] }
+
+// Less compares first the metrics, then the timestamp.
+func (vec Vector) Less(i, j int) bool {
+ switch {
+ case vec[i].Metric.Before(vec[j].Metric):
+ return true
+ case vec[j].Metric.Before(vec[i].Metric):
+ return false
+ case vec[i].Timestamp.Before(vec[j].Timestamp):
+ return true
+ default:
+ return false
+ }
+}
+
+// Equal compares two sets of samples and returns true if they are equal.
+func (vec Vector) Equal(o Vector) bool {
+ if len(vec) != len(o) {
+ return false
+ }
+
+ for i, sample := range vec {
+ if !sample.Equal(o[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+// Matrix is a list of time series.
+type Matrix []*SampleStream
+
+func (m Matrix) Len() int { return len(m) }
+func (m Matrix) Less(i, j int) bool { return m[i].Metric.Before(m[j].Metric) }
+func (m Matrix) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
+
+func (mat Matrix) String() string {
+ matCp := make(Matrix, len(mat))
+ copy(matCp, mat)
+ sort.Sort(matCp)
+
+ strs := make([]string, len(matCp))
+
+ for i, ss := range matCp {
+ strs[i] = ss.String()
+ }
+
+ return strings.Join(strs, "\n")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go
new file mode 100644
index 000000000..2e9c7eb09
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/common/model/value_test.go
@@ -0,0 +1,362 @@
+// Copyright 2013 The Prometheus Authors
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package model
+
+import (
+ "encoding/json"
+ "math"
+ "reflect"
+ "sort"
+ "testing"
+)
+
+func TestSamplePairJSON(t *testing.T) {
+ input := []struct {
+ plain string
+ value SamplePair
+ }{
+ {
+ plain: `[1234.567,"123.1"]`,
+ value: SamplePair{
+ Value: 123.1,
+ Timestamp: 1234567,
+ },
+ },
+ }
+
+ for _, test := range input {
+ b, err := json.Marshal(test.value)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if string(b) != test.plain {
+ t.Errorf("encoding error: expected %q, got %q", test.plain, b)
+ continue
+ }
+
+ var sp SamplePair
+ err = json.Unmarshal(b, &sp)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if sp != test.value {
+ t.Errorf("decoding error: expected %v, got %v", test.value, sp)
+ }
+ }
+}
+
+func TestSampleJSON(t *testing.T) {
+ input := []struct {
+ plain string
+ value Sample
+ }{
+ {
+ plain: `{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}`,
+ value: Sample{
+ Metric: Metric{
+ MetricNameLabel: "test_metric",
+ },
+ Value: 123.1,
+ Timestamp: 1234567,
+ },
+ },
+ }
+
+ for _, test := range input {
+ b, err := json.Marshal(test.value)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if string(b) != test.plain {
+ t.Errorf("encoding error: expected %q, got %q", test.plain, b)
+ continue
+ }
+
+ var sv Sample
+ err = json.Unmarshal(b, &sv)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if !reflect.DeepEqual(sv, test.value) {
+ t.Errorf("decoding error: expected %v, got %v", test.value, sv)
+ }
+ }
+}
+
+func TestVectorJSON(t *testing.T) {
+ input := []struct {
+ plain string
+ value Vector
+ }{
+ {
+ plain: `[]`,
+ value: Vector{},
+ },
+ {
+ plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]}]`,
+ value: Vector{&Sample{
+ Metric: Metric{
+ MetricNameLabel: "test_metric",
+ },
+ Value: 123.1,
+ Timestamp: 1234567,
+ }},
+ },
+ {
+ plain: `[{"metric":{"__name__":"test_metric"},"value":[1234.567,"123.1"]},{"metric":{"foo":"bar"},"value":[1.234,"+Inf"]}]`,
+ value: Vector{
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "test_metric",
+ },
+ Value: 123.1,
+ Timestamp: 1234567,
+ },
+ &Sample{
+ Metric: Metric{
+ "foo": "bar",
+ },
+ Value: SampleValue(math.Inf(1)),
+ Timestamp: 1234,
+ },
+ },
+ },
+ }
+
+ for _, test := range input {
+ b, err := json.Marshal(test.value)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if string(b) != test.plain {
+ t.Errorf("encoding error: expected %q, got %q", test.plain, b)
+ continue
+ }
+
+ var vec Vector
+ err = json.Unmarshal(b, &vec)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if !reflect.DeepEqual(vec, test.value) {
+ t.Errorf("decoding error: expected %v, got %v", test.value, vec)
+ }
+ }
+}
+
+func TestScalarJSON(t *testing.T) {
+ input := []struct {
+ plain string
+ value Scalar
+ }{
+ {
+ plain: `[123.456,"456"]`,
+ value: Scalar{
+ Timestamp: 123456,
+ Value: 456,
+ },
+ },
+ {
+ plain: `[123123.456,"+Inf"]`,
+ value: Scalar{
+ Timestamp: 123123456,
+ Value: SampleValue(math.Inf(1)),
+ },
+ },
+ {
+ plain: `[123123.456,"-Inf"]`,
+ value: Scalar{
+ Timestamp: 123123456,
+ Value: SampleValue(math.Inf(-1)),
+ },
+ },
+ }
+
+ for _, test := range input {
+ b, err := json.Marshal(test.value)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if string(b) != test.plain {
+ t.Errorf("encoding error: expected %q, got %q", test.plain, b)
+ continue
+ }
+
+ var sv Scalar
+ err = json.Unmarshal(b, &sv)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if sv != test.value {
+ t.Errorf("decoding error: expected %v, got %v", test.value, sv)
+ }
+ }
+}
+
+func TestStringJSON(t *testing.T) {
+ input := []struct {
+ plain string
+ value String
+ }{
+ {
+ plain: `[123.456,"test"]`,
+ value: String{
+ Timestamp: 123456,
+ Value: "test",
+ },
+ },
+ {
+ plain: `[123123.456,"台北"]`,
+ value: String{
+ Timestamp: 123123456,
+ Value: "台北",
+ },
+ },
+ }
+
+ for _, test := range input {
+ b, err := json.Marshal(test.value)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if string(b) != test.plain {
+ t.Errorf("encoding error: expected %q, got %q", test.plain, b)
+ continue
+ }
+
+ var sv String
+ err = json.Unmarshal(b, &sv)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ if sv != test.value {
+ t.Errorf("decoding error: expected %v, got %v", test.value, sv)
+ }
+ }
+}
+
+func TestVectorSort(t *testing.T) {
+ input := Vector{
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "A",
+ },
+ Timestamp: 1,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "A",
+ },
+ Timestamp: 2,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "C",
+ },
+ Timestamp: 1,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "C",
+ },
+ Timestamp: 2,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "B",
+ },
+ Timestamp: 1,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "B",
+ },
+ Timestamp: 2,
+ },
+ }
+
+ expected := Vector{
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "A",
+ },
+ Timestamp: 1,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "A",
+ },
+ Timestamp: 2,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "B",
+ },
+ Timestamp: 1,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "B",
+ },
+ Timestamp: 2,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "C",
+ },
+ Timestamp: 1,
+ },
+ &Sample{
+ Metric: Metric{
+ MetricNameLabel: "C",
+ },
+ Timestamp: 2,
+ },
+ }
+
+ sort.Sort(input)
+
+ for i, actual := range input {
+ actualFp := actual.Metric.Fingerprint()
+ expectedFp := expected[i].Metric.Fingerprint()
+
+ if actualFp != expectedFp {
+ t.Fatalf("%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String())
+ }
+
+ if actual.Timestamp != expected[i].Timestamp {
+ t.Fatalf("%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/AUTHORS.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/AUTHORS.md
new file mode 100644
index 000000000..f1c27ccb0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/AUTHORS.md
@@ -0,0 +1,20 @@
+The Prometheus project was started by Matt T. Proud (emeritus) and
+Julius Volz in 2012.
+
+Maintainers of this repository:
+
+* Tobias Schmidt
+
+The following individuals have contributed code to this repository
+(listed in alphabetical order):
+
+* Armen Baghumian
+* Bjoern Rabenstein
+* David Cournapeau
+* Ji-Hoon, Seol
+* Jonas Große Sundrup
+* Julius Volz
+* Matthias Rampke
+* Nicky Gerritsen
+* Rémi Audebert
+* Tobias Schmidt
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/CONTRIBUTING.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/CONTRIBUTING.md
new file mode 100644
index 000000000..5705f0fbe
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/CONTRIBUTING.md
@@ -0,0 +1,18 @@
+# Contributing
+
+Prometheus uses GitHub to manage reviews of pull requests.
+
+* If you have a trivial fix or improvement, go ahead and create a pull
+ request, addressing (with `@...`) one or more of the maintainers
+ (see [AUTHORS.md](AUTHORS.md)) in the description of the pull request.
+
+* If you plan to do something more involved, first discuss your ideas
+ on our [mailing list](https://groups.google.com/forum/?fromgroups#!forum/prometheus-developers).
+ This will avoid unnecessary work and surely give you and us a good deal
+ of inspiration.
+
+* Relevant coding style guidelines are the [Go Code Review
+ Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)
+ and the _Formatting and style_ section of Peter Bourgon's [Go: Best
+ Practices for Production
+ Environments](http://peter.bourgon.org/go-in-production/#formatting-and-style).
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/LICENSE
new file mode 100644
index 000000000..261eeb9e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/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/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/Makefile b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/Makefile
new file mode 100644
index 000000000..e8acbbc5e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/Makefile
@@ -0,0 +1,6 @@
+ci:
+ go fmt
+ go vet
+ go test -v ./...
+ go get github.com/golang/lint/golint
+ golint *.go
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/NOTICE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/NOTICE
new file mode 100644
index 000000000..53c5e9aa1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/NOTICE
@@ -0,0 +1,7 @@
+procfs provides functions to retrieve system, kernel and process
+metrics from the pseudo-filesystem proc.
+
+Copyright 2014-2015 The Prometheus Authors
+
+This product includes software developed at
+SoundCloud Ltd. (http://soundcloud.com/).
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/README.md
new file mode 100644
index 000000000..6e7ee6b8b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/README.md
@@ -0,0 +1,10 @@
+# procfs
+
+This procfs package provides functions to retrieve system, kernel and process
+metrics from the pseudo-filesystem proc.
+
+*WARNING*: This package is a work in progress. Its API may still break in
+backwards-incompatible ways without warnings. Use it at your own risk.
+
+[](https://godoc.org/github.com/prometheus/procfs)
+[](https://travis-ci.org/prometheus/procfs)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/doc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/doc.go
new file mode 100644
index 000000000..e2acd6d40
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/doc.go
@@ -0,0 +1,45 @@
+// Copyright 2014 Prometheus Team
+// 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 procfs provides functions to retrieve system, kernel and process
+// metrics from the pseudo-filesystem proc.
+//
+// Example:
+//
+// package main
+//
+// import (
+// "fmt"
+// "log"
+//
+// "github.com/prometheus/procfs"
+// )
+//
+// func main() {
+// p, err := procfs.Self()
+// if err != nil {
+// log.Fatalf("could not get process: %s", err)
+// }
+//
+// stat, err := p.NewStat()
+// if err != nil {
+// log.Fatalf("could not get process stat: %s", err)
+// }
+//
+// fmt.Printf("command: %s\n", stat.Comm)
+// fmt.Printf("cpu time: %fs\n", stat.CPUTime())
+// fmt.Printf("vsize: %dB\n", stat.VirtualMemory())
+// fmt.Printf("rss: %dB\n", stat.ResidentMemory())
+// }
+//
+package procfs
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/cmdline b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/cmdline
new file mode 100644
index 000000000..d2d8ef887
Binary files /dev/null and b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/cmdline differ
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/io b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/io
new file mode 100644
index 000000000..b6210a7a7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/io
@@ -0,0 +1,7 @@
+rchar: 750339
+wchar: 818609
+syscr: 7405
+syscw: 5245
+read_bytes: 1024
+write_bytes: 2048
+cancelled_write_bytes: -1024
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits
new file mode 100644
index 000000000..23c6b6898
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/limits
@@ -0,0 +1,17 @@
+Limit Soft Limit Hard Limit Units
+Max cpu time unlimited unlimited seconds
+Max file size unlimited unlimited bytes
+Max data size unlimited unlimited bytes
+Max stack size 8388608 unlimited bytes
+Max core file size 0 unlimited bytes
+Max resident set unlimited unlimited bytes
+Max processes 62898 62898 processes
+Max open files 2048 4096 files
+Max locked memory 65536 65536 bytes
+Max address space unlimited unlimited bytes
+Max file locks unlimited unlimited locks
+Max pending signals 62898 62898 signals
+Max msgqueue size 819200 819200 bytes
+Max nice priority 0 0
+Max realtime priority 0 0
+Max realtime timeout unlimited unlimited us
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat
new file mode 100644
index 000000000..438aaa9dc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26231/stat
@@ -0,0 +1 @@
+26231 (vim) R 5392 7446 5392 34835 7446 4218880 32533 309516 26 82 1677 44 158 99 20 0 1 0 82375 56274944 1981 18446744073709551615 4194304 6294284 140736914091744 140736914087944 139965136429984 0 0 12288 1870679807 0 0 0 17 0 0 0 31 0 0 8391624 8481048 16420864 140736914093252 140736914093279 140736914093279 140736914096107 0
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/cmdline b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/cmdline
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/limits b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/limits
new file mode 100644
index 000000000..3f9bf16a9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/limits
@@ -0,0 +1,17 @@
+Limit Soft Limit Hard Limit Units
+Max cpu time unlimited unlimited seconds
+Max file size unlimited unlimited bytes
+Max data size unlimited unlimited bytes
+Max stack size 8388608 unlimited bytes
+Max core file size 0 unlimited bytes
+Max resident set unlimited unlimited bytes
+Max processes 29436 29436 processes
+Max open files 1024 4096 files
+Max locked memory 65536 65536 bytes
+Max address space unlimited unlimited bytes
+Max file locks unlimited unlimited locks
+Max pending signals 29436 29436 signals
+Max msgqueue size 819200 819200 bytes
+Max nice priority 0 0
+Max realtime priority 0 0
+Max realtime timeout unlimited unlimited us
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/stat b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/stat
new file mode 100644
index 000000000..321b16073
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/26232/stat
@@ -0,0 +1 @@
+33 (ata_sff) S 2 0 0 0 -1 69238880 0 0 0 0 0 0 0 0 0 -20 1 0 5 0 0 18446744073709551615 0 0 0 0 0 0 0 2147483647 0 18446744073709551615 0 0 17 1 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat
new file mode 100644
index 000000000..65b9369d1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/584/stat
@@ -0,0 +1,2 @@
+1020 ((a b ) ( c d) ) R 28378 1020 28378 34842 1020 4218880 286 0 0 0 0 0 0 0 20 0 1 0 10839175 10395648 155 18446744073709551615 4194304 4238788 140736466511168 140736466511168 140609271124624 0 0 0 0 0 0 0 17 5 0 0 0 0 0 6336016 6337300 25579520 140736466515030 140736466515061 140736466515061 140736466518002 0
+#!/bin/cat /proc/self/stat
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/mdstat b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/mdstat
new file mode 100644
index 000000000..4430bdee2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/mdstat
@@ -0,0 +1,26 @@
+Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4] [raid10]
+md3 : active raid6 sda1[8] sdh1[7] sdg1[6] sdf1[5] sde1[11] sdd1[3] sdc1[10] sdb1[9]
+ 5853468288 blocks super 1.2 level 6, 64k chunk, algorithm 2 [8/8] [UUUUUUUU]
+
+md127 : active raid1 sdi2[0] sdj2[1]
+ 312319552 blocks [2/2] [UU]
+
+md0 : active raid1 sdk[2](S) sdi1[0] sdj1[1]
+ 248896 blocks [2/2] [UU]
+
+md4 : inactive raid1 sda3[0] sdb3[1]
+ 4883648 blocks [2/2] [UU]
+
+md6 : active raid1 sdb2[2] sda2[0]
+ 195310144 blocks [2/1] [U_]
+ [=>...................] recovery = 8.5% (16775552/195310144) finish=17.0min speed=259783K/sec
+
+md8 : active raid1 sdb1[1] sda1[0]
+ 195310144 blocks [2/2] [UU]
+ [=>...................] resync = 8.5% (16775552/195310144) finish=17.0min speed=259783K/sec
+
+md7 : active raid6 sdb1[0] sde1[3] sdd1[2] sdc1[1]
+ 7813735424 blocks super 1.2 level 6, 512k chunk, algorithm 2 [4/3] [U_UU]
+ bitmap: 0/30 pages [0KB], 65536KB chunk
+
+unused devices:
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/net/ip_vs b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/net/ip_vs
new file mode 100644
index 000000000..6a6a97d7d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/net/ip_vs
@@ -0,0 +1,14 @@
+IP Virtual Server version 1.2.1 (size=4096)
+Prot LocalAddress:Port Scheduler Flags
+ -> RemoteAddress:Port Forward Weight ActiveConn InActConn
+TCP C0A80016:0CEA wlc
+ -> C0A85216:0CEA Tunnel 100 248 2
+ -> C0A85318:0CEA Tunnel 100 248 2
+ -> C0A85315:0CEA Tunnel 100 248 1
+TCP C0A80039:0CEA wlc
+ -> C0A85416:0CEA Tunnel 0 0 0
+ -> C0A85215:0CEA Tunnel 100 1499 0
+ -> C0A83215:0CEA Tunnel 100 1498 0
+TCP C0A80037:0CEA wlc
+ -> C0A8321A:0CEA Tunnel 0 0 0
+ -> C0A83120:0CEA Tunnel 100 0 0
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/net/ip_vs_stats b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/net/ip_vs_stats
new file mode 100644
index 000000000..c00724e0f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/net/ip_vs_stats
@@ -0,0 +1,6 @@
+ Total Incoming Outgoing Incoming Outgoing
+ Conns Packets Packets Bytes Bytes
+ 16AA370 E33656E5 0 51D8C8883AB3 0
+
+ Conns/s Pkts/s Pkts/s Bytes/s Bytes/s
+ 4 1FB3C 0 1282A8F 0
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat
new file mode 100644
index 000000000..dabb96f74
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/stat
@@ -0,0 +1,16 @@
+cpu 301854 612 111922 8979004 3552 2 3944 0 0 0
+cpu0 44490 19 21045 1087069 220 1 3410 0 0 0
+cpu1 47869 23 16474 1110787 591 0 46 0 0 0
+cpu2 46504 36 15916 1112321 441 0 326 0 0 0
+cpu3 47054 102 15683 1113230 533 0 60 0 0 0
+cpu4 28413 25 10776 1140321 217 0 8 0 0 0
+cpu5 29271 101 11586 1136270 672 0 30 0 0 0
+cpu6 29152 36 10276 1139721 319 0 29 0 0 0
+cpu7 29098 268 10164 1139282 555 0 31 0 0 0
+intr 8885917 17 0 0 0 0 0 0 0 1 79281 0 0 0 0 0 0 0 231237 0 0 0 0 250586 103 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 223424 190745 13 906 1283803 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ctxt 38014093
+btime 1418183276
+processes 26442
+procs_running 2
+procs_blocked 0
+softirq 5057579 250191 1481983 1647 211099 186066 0 1783454 622196 12499 508444
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/README b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/README
new file mode 100644
index 000000000..5cf184ea0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/README
@@ -0,0 +1,2 @@
+This directory contains some empty files that are the symlinks the files in the "fd" directory point to.
+They are otherwise ignored by the tests
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/abc b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/abc
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/def b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/def
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/ghi b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/ghi
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/uvw b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/uvw
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/xyz b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fixtures/symlinktargets/xyz
new file mode 100644
index 000000000..e69de29bb
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fs.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fs.go
new file mode 100644
index 000000000..6a8d97b11
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fs.go
@@ -0,0 +1,40 @@
+package procfs
+
+import (
+ "fmt"
+ "os"
+ "path"
+)
+
+// FS represents the pseudo-filesystem proc, which provides an interface to
+// kernel data structures.
+type FS string
+
+// DefaultMountPoint is the common mount point of the proc filesystem.
+const DefaultMountPoint = "/proc"
+
+// NewFS returns a new FS mounted under the given mountPoint. It will error
+// if the mount point can't be read.
+func NewFS(mountPoint string) (FS, error) {
+ info, err := os.Stat(mountPoint)
+ if err != nil {
+ return "", fmt.Errorf("could not read %s: %s", mountPoint, err)
+ }
+ if !info.IsDir() {
+ return "", fmt.Errorf("mount point %s is not a directory", mountPoint)
+ }
+
+ return FS(mountPoint), nil
+}
+
+func (fs FS) stat(p string) (os.FileInfo, error) {
+ return os.Stat(path.Join(string(fs), p))
+}
+
+func (fs FS) open(p string) (*os.File, error) {
+ return os.Open(path.Join(string(fs), p))
+}
+
+func (fs FS) readlink(p string) (string, error) {
+ return os.Readlink(path.Join(string(fs), p))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go
new file mode 100644
index 000000000..91f1c6c97
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/fs_test.go
@@ -0,0 +1,13 @@
+package procfs
+
+import "testing"
+
+func TestNewFS(t *testing.T) {
+ if _, err := NewFS("foobar"); err == nil {
+ t.Error("want NewFS to fail for non-existing mount point")
+ }
+
+ if _, err := NewFS("procfs.go"); err == nil {
+ t.Error("want NewFS to fail if mount point is not a directory")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/ipvs.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/ipvs.go
new file mode 100644
index 000000000..26da5000e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/ipvs.go
@@ -0,0 +1,223 @@
+package procfs
+
+import (
+ "bufio"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "strconv"
+ "strings"
+)
+
+// IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
+type IPVSStats struct {
+ // Total count of connections.
+ Connections uint64
+ // Total incoming packages processed.
+ IncomingPackets uint64
+ // Total outgoing packages processed.
+ OutgoingPackets uint64
+ // Total incoming traffic.
+ IncomingBytes uint64
+ // Total outgoing traffic.
+ OutgoingBytes uint64
+}
+
+// IPVSBackendStatus holds current metrics of one virtual / real address pair.
+type IPVSBackendStatus struct {
+ // The local (virtual) IP address.
+ LocalAddress net.IP
+ // The local (virtual) port.
+ LocalPort uint16
+ // The transport protocol (TCP, UDP).
+ Proto string
+ // The remote (real) IP address.
+ RemoteAddress net.IP
+ // The remote (real) port.
+ RemotePort uint16
+ // The current number of active connections for this virtual/real address pair.
+ ActiveConn uint64
+ // The current number of inactive connections for this virtual/real address pair.
+ InactConn uint64
+ // The current weight of this virtual/real address pair.
+ Weight uint64
+}
+
+// NewIPVSStats reads the IPVS statistics.
+func NewIPVSStats() (IPVSStats, error) {
+ fs, err := NewFS(DefaultMountPoint)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+
+ return fs.NewIPVSStats()
+}
+
+// NewIPVSStats reads the IPVS statistics from the specified `proc` filesystem.
+func (fs FS) NewIPVSStats() (IPVSStats, error) {
+ file, err := fs.open("net/ip_vs_stats")
+ if err != nil {
+ return IPVSStats{}, err
+ }
+ defer file.Close()
+
+ return parseIPVSStats(file)
+}
+
+// parseIPVSStats performs the actual parsing of `ip_vs_stats`.
+func parseIPVSStats(file io.Reader) (IPVSStats, error) {
+ var (
+ statContent []byte
+ statLines []string
+ statFields []string
+ stats IPVSStats
+ )
+
+ statContent, err := ioutil.ReadAll(file)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+
+ statLines = strings.SplitN(string(statContent), "\n", 4)
+ if len(statLines) != 4 {
+ return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
+ }
+
+ statFields = strings.Fields(statLines[2])
+ if len(statFields) != 5 {
+ return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
+ }
+
+ stats.Connections, err = strconv.ParseUint(statFields[0], 16, 64)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+ stats.IncomingPackets, err = strconv.ParseUint(statFields[1], 16, 64)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+ stats.OutgoingPackets, err = strconv.ParseUint(statFields[2], 16, 64)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+ stats.IncomingBytes, err = strconv.ParseUint(statFields[3], 16, 64)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+ stats.OutgoingBytes, err = strconv.ParseUint(statFields[4], 16, 64)
+ if err != nil {
+ return IPVSStats{}, err
+ }
+
+ return stats, nil
+}
+
+// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs.
+func NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
+ fs, err := NewFS(DefaultMountPoint)
+ if err != nil {
+ return []IPVSBackendStatus{}, err
+ }
+
+ return fs.NewIPVSBackendStatus()
+}
+
+// NewIPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
+func (fs FS) NewIPVSBackendStatus() ([]IPVSBackendStatus, error) {
+ file, err := fs.open("net/ip_vs")
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ return parseIPVSBackendStatus(file)
+}
+
+func parseIPVSBackendStatus(file io.Reader) ([]IPVSBackendStatus, error) {
+ var (
+ status []IPVSBackendStatus
+ scanner = bufio.NewScanner(file)
+ proto string
+ localAddress net.IP
+ localPort uint16
+ err error
+ )
+
+ for scanner.Scan() {
+ fields := strings.Fields(string(scanner.Text()))
+ if len(fields) == 0 {
+ continue
+ }
+ switch {
+ case fields[0] == "IP" || fields[0] == "Prot" || fields[1] == "RemoteAddress:Port":
+ continue
+ case fields[0] == "TCP" || fields[0] == "UDP":
+ if len(fields) < 2 {
+ continue
+ }
+ proto = fields[0]
+ localAddress, localPort, err = parseIPPort(fields[1])
+ if err != nil {
+ return nil, err
+ }
+ case fields[0] == "->":
+ if len(fields) < 6 {
+ continue
+ }
+ remoteAddress, remotePort, err := parseIPPort(fields[1])
+ if err != nil {
+ return nil, err
+ }
+ weight, err := strconv.ParseUint(fields[3], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ activeConn, err := strconv.ParseUint(fields[4], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ inactConn, err := strconv.ParseUint(fields[5], 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ status = append(status, IPVSBackendStatus{
+ LocalAddress: localAddress,
+ LocalPort: localPort,
+ RemoteAddress: remoteAddress,
+ RemotePort: remotePort,
+ Proto: proto,
+ Weight: weight,
+ ActiveConn: activeConn,
+ InactConn: inactConn,
+ })
+ }
+ }
+ return status, nil
+}
+
+func parseIPPort(s string) (net.IP, uint16, error) {
+ tmp := strings.SplitN(s, ":", 2)
+
+ if len(tmp) != 2 {
+ return nil, 0, fmt.Errorf("invalid IP:Port: %s", s)
+ }
+
+ if len(tmp[0]) != 8 && len(tmp[0]) != 32 {
+ return nil, 0, fmt.Errorf("invalid IP: %s", tmp[0])
+ }
+
+ ip, err := hex.DecodeString(tmp[0])
+ if err != nil {
+ return nil, 0, err
+ }
+
+ port, err := strconv.ParseUint(tmp[1], 16, 16)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return ip, uint16(port), nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/ipvs_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/ipvs_test.go
new file mode 100644
index 000000000..6036cde84
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/ipvs_test.go
@@ -0,0 +1,196 @@
+package procfs
+
+import (
+ "net"
+ "testing"
+)
+
+var (
+ expectedIPVSStats = IPVSStats{
+ Connections: 23765872,
+ IncomingPackets: 3811989221,
+ OutgoingPackets: 0,
+ IncomingBytes: 89991519156915,
+ OutgoingBytes: 0,
+ }
+ expectedIPVSBackendStatuses = []IPVSBackendStatus{
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.22"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.82.22"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 100,
+ ActiveConn: 248,
+ InactConn: 2,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.22"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.83.24"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 100,
+ ActiveConn: 248,
+ InactConn: 2,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.22"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.83.21"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 100,
+ ActiveConn: 248,
+ InactConn: 1,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.57"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.84.22"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 0,
+ ActiveConn: 0,
+ InactConn: 0,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.57"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.82.21"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 100,
+ ActiveConn: 1499,
+ InactConn: 0,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.57"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.50.21"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 100,
+ ActiveConn: 1498,
+ InactConn: 0,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.55"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.50.26"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 0,
+ ActiveConn: 0,
+ InactConn: 0,
+ },
+ IPVSBackendStatus{
+ LocalAddress: net.ParseIP("192.168.0.55"),
+ LocalPort: 3306,
+ RemoteAddress: net.ParseIP("192.168.49.32"),
+ RemotePort: 3306,
+ Proto: "TCP",
+ Weight: 100,
+ ActiveConn: 0,
+ InactConn: 0,
+ },
+ }
+)
+
+func TestIPVSStats(t *testing.T) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ t.Fatal(err)
+ }
+ stats, err := fs.NewIPVSStats()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if stats != expectedIPVSStats {
+ t.Errorf("want %+v, got %+v", expectedIPVSStats, stats)
+ }
+}
+
+func TestParseIPPort(t *testing.T) {
+ ip := net.ParseIP("192.168.0.22")
+ port := uint16(3306)
+
+ gotIP, gotPort, err := parseIPPort("C0A80016:0CEA")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(gotIP.Equal(ip) && port == gotPort) {
+ t.Errorf("want %s:%d, got %s:%d", ip, port, gotIP, gotPort)
+ }
+}
+
+func TestParseIPPortInvalid(t *testing.T) {
+ testcases := []string{
+ "",
+ "C0A80016",
+ "C0A800:1234",
+ "FOOBARBA:1234",
+ "C0A80016:0CEA:1234",
+ }
+
+ for _, s := range testcases {
+ ip, port, err := parseIPPort(s)
+ if ip != nil || port != uint16(0) || err == nil {
+ t.Errorf("Expected error for input %s, got ip = %s, port = %v, err = %v", s, ip, port, err)
+ }
+ }
+}
+
+func TestParseIPPortIPv6(t *testing.T) {
+ ip := net.ParseIP("dead:beef::1")
+ port := uint16(8080)
+
+ gotIP, gotPort, err := parseIPPort("DEADBEEF000000000000000000000001:1F90")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(gotIP.Equal(ip) && port == gotPort) {
+ t.Errorf("want %s:%d, got %s:%d", ip, port, gotIP, gotPort)
+ }
+
+}
+
+func TestIPVSBackendStatus(t *testing.T) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ backendStats, err := fs.NewIPVSBackendStatus()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for idx, expect := range expectedIPVSBackendStatuses {
+ if !backendStats[idx].LocalAddress.Equal(expect.LocalAddress) {
+ t.Errorf("expected LocalAddress %s, got %s", expect.LocalAddress, backendStats[idx].LocalAddress)
+ }
+ if backendStats[idx].LocalPort != expect.LocalPort {
+ t.Errorf("expected LocalPort %d, got %d", expect.LocalPort, backendStats[idx].LocalPort)
+ }
+ if !backendStats[idx].RemoteAddress.Equal(expect.RemoteAddress) {
+ t.Errorf("expected RemoteAddress %s, got %s", expect.RemoteAddress, backendStats[idx].RemoteAddress)
+ }
+ if backendStats[idx].RemotePort != expect.RemotePort {
+ t.Errorf("expected RemotePort %d, got %d", expect.RemotePort, backendStats[idx].RemotePort)
+ }
+ if backendStats[idx].Proto != expect.Proto {
+ t.Errorf("expected Proto %s, got %s", expect.Proto, backendStats[idx].Proto)
+ }
+ if backendStats[idx].Weight != expect.Weight {
+ t.Errorf("expected Weight %d, got %d", expect.Weight, backendStats[idx].Weight)
+ }
+ if backendStats[idx].ActiveConn != expect.ActiveConn {
+ t.Errorf("expected ActiveConn %d, got %d", expect.ActiveConn, backendStats[idx].ActiveConn)
+ }
+ if backendStats[idx].InactConn != expect.InactConn {
+ t.Errorf("expected InactConn %d, got %d", expect.InactConn, backendStats[idx].InactConn)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/mdstat.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/mdstat.go
new file mode 100644
index 000000000..09ed6b5eb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/mdstat.go
@@ -0,0 +1,158 @@
+package procfs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "path"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ statuslineRE = regexp.MustCompile(`(\d+) blocks .*\[(\d+)/(\d+)\] \[[U_]+\]`)
+ buildlineRE = regexp.MustCompile(`\((\d+)/\d+\)`)
+)
+
+// MDStat holds info parsed from /proc/mdstat.
+type MDStat struct {
+ // Name of the device.
+ Name string
+ // activity-state of the device.
+ ActivityState string
+ // Number of active disks.
+ DisksActive int64
+ // Total number of disks the device consists of.
+ DisksTotal int64
+ // Number of blocks the device holds.
+ BlocksTotal int64
+ // Number of blocks on the device that are in sync.
+ BlocksSynced int64
+}
+
+// ParseMDStat parses an mdstat-file and returns a struct with the relevant infos.
+func (fs FS) ParseMDStat() (mdstates []MDStat, err error) {
+ mdStatusFilePath := path.Join(string(fs), "mdstat")
+ content, err := ioutil.ReadFile(mdStatusFilePath)
+ if err != nil {
+ return []MDStat{}, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
+ }
+
+ mdStatusFile := string(content)
+
+ lines := strings.Split(mdStatusFile, "\n")
+ var currentMD string
+
+ // Each md has at least the deviceline, statusline and one empty line afterwards
+ // so we will have probably something of the order len(lines)/3 devices
+ // so we use that for preallocation.
+ estimateMDs := len(lines) / 3
+ mdStates := make([]MDStat, 0, estimateMDs)
+
+ for i, l := range lines {
+ if l == "" {
+ // Skip entirely empty lines.
+ continue
+ }
+
+ if l[0] == ' ' {
+ // Those lines are not the beginning of a md-section.
+ continue
+ }
+
+ if strings.HasPrefix(l, "Personalities") || strings.HasPrefix(l, "unused") {
+ // We aren't interested in lines with general info.
+ continue
+ }
+
+ mainLine := strings.Split(l, " ")
+ if len(mainLine) < 3 {
+ return mdStates, fmt.Errorf("error parsing mdline: %s", l)
+ }
+ currentMD = mainLine[0] // name of md-device
+ activityState := mainLine[2] // activity status of said md-device
+
+ if len(lines) <= i+3 {
+ return mdStates, fmt.Errorf("error parsing %s: entry for %s has fewer lines than expected", mdStatusFilePath, currentMD)
+ }
+
+ active, total, size, err := evalStatusline(lines[i+1]) // parse statusline, always present
+ if err != nil {
+ return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
+ }
+
+ //
+ // Now get the number of synced blocks.
+ //
+
+ // Get the line number of the syncing-line.
+ var j int
+ if strings.Contains(lines[i+2], "bitmap") { // then skip the bitmap line
+ j = i + 3
+ } else {
+ j = i + 2
+ }
+
+ // If device is syncing at the moment, get the number of currently synced bytes,
+ // otherwise that number equals the size of the device.
+ syncedBlocks := size
+ if strings.Contains(lines[j], "recovery") || strings.Contains(lines[j], "resync") {
+ syncedBlocks, err = evalBuildline(lines[j])
+ if err != nil {
+ return mdStates, fmt.Errorf("error parsing %s: %s", mdStatusFilePath, err)
+ }
+ }
+
+ mdStates = append(mdStates, MDStat{currentMD, activityState, active, total, size, syncedBlocks})
+
+ }
+
+ return mdStates, nil
+}
+
+func evalStatusline(statusline string) (active, total, size int64, err error) {
+ matches := statuslineRE.FindStringSubmatch(statusline)
+
+ // +1 to make it more obvious that the whole string containing the info is also returned as matches[0].
+ if len(matches) != 3+1 {
+ return 0, 0, 0, fmt.Errorf("unexpected number matches found in statusline: %s", statusline)
+ }
+
+ size, err = strconv.ParseInt(matches[1], 10, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("%s in statusline: %s", err, statusline)
+ }
+
+ total, err = strconv.ParseInt(matches[2], 10, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("%s in statusline: %s", err, statusline)
+ }
+
+ active, err = strconv.ParseInt(matches[3], 10, 64)
+ if err != nil {
+ return 0, 0, 0, fmt.Errorf("%s in statusline: %s", err, statusline)
+ }
+
+ return active, total, size, nil
+}
+
+// Gets the size that has already been synced out of the sync-line.
+func evalBuildline(buildline string) (int64, error) {
+ matches := buildlineRE.FindStringSubmatch(buildline)
+
+ // +1 to make it more obvious that the whole string containing the info is also returned as matches[0].
+ if len(matches) < 1+1 {
+ return 0, fmt.Errorf("too few matches found in buildline: %s", buildline)
+ }
+
+ if len(matches) > 1+1 {
+ return 0, fmt.Errorf("too many matches found in buildline: %s", buildline)
+ }
+
+ syncedSize, err := strconv.ParseInt(matches[1], 10, 64)
+ if err != nil {
+ return 0, fmt.Errorf("%s in buildline: %s", err, buildline)
+ }
+
+ return syncedSize, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/mdstat_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/mdstat_test.go
new file mode 100644
index 000000000..7b9a3a5f5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/mdstat_test.go
@@ -0,0 +1,33 @@
+package procfs
+
+import (
+ "testing"
+)
+
+func TestMDStat(t *testing.T) {
+ fs := FS("fixtures")
+ mdStates, err := fs.ParseMDStat()
+ if err != nil {
+ t.Fatalf("parsing of reference-file failed entirely: %s", err)
+ }
+
+ refs := map[string]MDStat{
+ "md3": MDStat{"md3", "active", 8, 8, 5853468288, 5853468288},
+ "md127": MDStat{"md127", "active", 2, 2, 312319552, 312319552},
+ "md0": MDStat{"md0", "active", 2, 2, 248896, 248896},
+ "md4": MDStat{"md4", "inactive", 2, 2, 4883648, 4883648},
+ "md6": MDStat{"md6", "active", 1, 2, 195310144, 16775552},
+ "md8": MDStat{"md8", "active", 2, 2, 195310144, 16775552},
+ "md7": MDStat{"md7", "active", 3, 4, 7813735424, 7813735424},
+ }
+
+ for _, md := range mdStates {
+ if md != refs[md.Name] {
+ t.Errorf("failed parsing md-device %s correctly: want %v, got %v", md.Name, refs[md.Name], md)
+ }
+ }
+
+ if want, have := len(refs), len(mdStates); want != have {
+ t.Errorf("want %d parsed md-devices, have %d", want, have)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc.go
new file mode 100644
index 000000000..efc850278
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc.go
@@ -0,0 +1,202 @@
+package procfs
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+)
+
+// Proc provides information about a running process.
+type Proc struct {
+ // The process ID.
+ PID int
+
+ fs FS
+}
+
+// Procs represents a list of Proc structs.
+type Procs []Proc
+
+func (p Procs) Len() int { return len(p) }
+func (p Procs) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p Procs) Less(i, j int) bool { return p[i].PID < p[j].PID }
+
+// Self returns a process for the current process read via /proc/self.
+func Self() (Proc, error) {
+ fs, err := NewFS(DefaultMountPoint)
+ if err != nil {
+ return Proc{}, err
+ }
+ return fs.Self()
+}
+
+// NewProc returns a process for the given pid under /proc.
+func NewProc(pid int) (Proc, error) {
+ fs, err := NewFS(DefaultMountPoint)
+ if err != nil {
+ return Proc{}, err
+ }
+ return fs.NewProc(pid)
+}
+
+// AllProcs returns a list of all currently avaible processes under /proc.
+func AllProcs() (Procs, error) {
+ fs, err := NewFS(DefaultMountPoint)
+ if err != nil {
+ return Procs{}, err
+ }
+ return fs.AllProcs()
+}
+
+// Self returns a process for the current process.
+func (fs FS) Self() (Proc, error) {
+ p, err := fs.readlink("self")
+ if err != nil {
+ return Proc{}, err
+ }
+ pid, err := strconv.Atoi(strings.Replace(p, string(fs), "", -1))
+ if err != nil {
+ return Proc{}, err
+ }
+ return fs.NewProc(pid)
+}
+
+// NewProc returns a process for the given pid.
+func (fs FS) NewProc(pid int) (Proc, error) {
+ if _, err := fs.stat(strconv.Itoa(pid)); err != nil {
+ return Proc{}, err
+ }
+ return Proc{PID: pid, fs: fs}, nil
+}
+
+// AllProcs returns a list of all currently avaible processes.
+func (fs FS) AllProcs() (Procs, error) {
+ d, err := fs.open("")
+ if err != nil {
+ return Procs{}, err
+ }
+ defer d.Close()
+
+ names, err := d.Readdirnames(-1)
+ if err != nil {
+ return Procs{}, fmt.Errorf("could not read %s: %s", d.Name(), err)
+ }
+
+ p := Procs{}
+ for _, n := range names {
+ pid, err := strconv.ParseInt(n, 10, 64)
+ if err != nil {
+ continue
+ }
+ p = append(p, Proc{PID: int(pid), fs: fs})
+ }
+
+ return p, nil
+}
+
+// CmdLine returns the command line of a process.
+func (p Proc) CmdLine() ([]string, error) {
+ f, err := p.open("cmdline")
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return nil, err
+ }
+
+ if len(data) < 1 {
+ return []string{}, nil
+ }
+
+ return strings.Split(string(data[:len(data)-1]), string(byte(0))), nil
+}
+
+// Executable returns the absolute path of the executable command of a process.
+func (p Proc) Executable() (string, error) {
+ exe, err := p.readlink("exe")
+
+ if os.IsNotExist(err) {
+ return "", nil
+ }
+
+ return exe, err
+}
+
+// FileDescriptors returns the currently open file descriptors of a process.
+func (p Proc) FileDescriptors() ([]uintptr, error) {
+ names, err := p.fileDescriptors()
+ if err != nil {
+ return nil, err
+ }
+
+ fds := make([]uintptr, len(names))
+ for i, n := range names {
+ fd, err := strconv.ParseInt(n, 10, 32)
+ if err != nil {
+ return nil, fmt.Errorf("could not parse fd %s: %s", n, err)
+ }
+ fds[i] = uintptr(fd)
+ }
+
+ return fds, nil
+}
+
+// FileDescriptorTargets returns the targets of all file descriptors of a process.
+// If a file descriptor is not a symlink to a file (like a socket), that value will be the empty string.
+func (p Proc) FileDescriptorTargets() ([]string, error) {
+ names, err := p.fileDescriptors()
+ if err != nil {
+ return nil, err
+ }
+
+ targets := make([]string, len(names))
+
+ for i, name := range names {
+ target, err := p.readlink("fd/" + name)
+ if err == nil {
+ targets[i] = target
+ }
+ }
+
+ return targets, nil
+}
+
+// FileDescriptorsLen returns the number of currently open file descriptors of
+// a process.
+func (p Proc) FileDescriptorsLen() (int, error) {
+ fds, err := p.fileDescriptors()
+ if err != nil {
+ return 0, err
+ }
+
+ return len(fds), nil
+}
+
+func (p Proc) fileDescriptors() ([]string, error) {
+ d, err := p.open("fd")
+ if err != nil {
+ return nil, err
+ }
+ defer d.Close()
+
+ names, err := d.Readdirnames(-1)
+ if err != nil {
+ return nil, fmt.Errorf("could not read %s: %s", d.Name(), err)
+ }
+
+ return names, nil
+}
+
+func (p Proc) open(pa string) (*os.File, error) {
+ return p.fs.open(path.Join(strconv.Itoa(p.PID), pa))
+}
+
+func (p Proc) readlink(pa string) (string, error) {
+ return p.fs.readlink(path.Join(strconv.Itoa(p.PID), pa))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_io.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_io.go
new file mode 100644
index 000000000..7c6dc8697
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_io.go
@@ -0,0 +1,54 @@
+package procfs
+
+import (
+ "fmt"
+ "io/ioutil"
+)
+
+// ProcIO models the content of /proc//io.
+type ProcIO struct {
+ // Chars read.
+ RChar uint64
+ // Chars written.
+ WChar uint64
+ // Read syscalls.
+ SyscR uint64
+ // Write syscalls.
+ SyscW uint64
+ // Bytes read.
+ ReadBytes uint64
+ // Bytes written.
+ WriteBytes uint64
+ // Bytes written, but taking into account truncation. See
+ // Documentation/filesystems/proc.txt in the kernel sources for
+ // detailed explanation.
+ CancelledWriteBytes int64
+}
+
+// NewIO creates a new ProcIO instance from a given Proc instance.
+func (p Proc) NewIO() (ProcIO, error) {
+ pio := ProcIO{}
+
+ f, err := p.open("io")
+ if err != nil {
+ return pio, err
+ }
+ defer f.Close()
+
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return pio, err
+ }
+
+ ioFormat := "rchar: %d\nwchar: %d\nsyscr: %d\nsyscw: %d\n" +
+ "read_bytes: %d\nwrite_bytes: %d\n" +
+ "cancelled_write_bytes: %d\n"
+
+ _, err = fmt.Sscanf(string(data), ioFormat, &pio.RChar, &pio.WChar, &pio.SyscR,
+ &pio.SyscW, &pio.ReadBytes, &pio.WriteBytes, &pio.CancelledWriteBytes)
+ if err != nil {
+ return pio, err
+ }
+
+ return pio, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_io_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_io_test.go
new file mode 100644
index 000000000..5ef524d8e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_io_test.go
@@ -0,0 +1,49 @@
+package procfs
+
+import "testing"
+
+func TestProcIO(t *testing.T) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p, err := fs.NewProc(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ s, err := p.NewIO()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, test := range []struct {
+ name string
+ want uint64
+ got uint64
+ }{
+ {name: "RChar", want: 750339, got: s.RChar},
+ {name: "WChar", want: 818609, got: s.WChar},
+ {name: "SyscR", want: 7405, got: s.SyscR},
+ {name: "SyscW", want: 5245, got: s.SyscW},
+ {name: "ReadBytes", want: 1024, got: s.ReadBytes},
+ {name: "WriteBytes", want: 2048, got: s.WriteBytes},
+ } {
+ if test.want != test.got {
+ t.Errorf("want %s %d, got %d", test.name, test.want, test.got)
+ }
+ }
+
+ for _, test := range []struct {
+ name string
+ want int64
+ got int64
+ }{
+ {name: "CancelledWriteBytes", want: -1024, got: s.CancelledWriteBytes},
+ } {
+ if test.want != test.got {
+ t.Errorf("want %s %d, got %d", test.name, test.want, test.got)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits.go
new file mode 100644
index 000000000..9f080b9f6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits.go
@@ -0,0 +1,111 @@
+package procfs
+
+import (
+ "bufio"
+ "fmt"
+ "regexp"
+ "strconv"
+)
+
+// ProcLimits represents the soft limits for each of the process's resource
+// limits.
+type ProcLimits struct {
+ CPUTime int
+ FileSize int
+ DataSize int
+ StackSize int
+ CoreFileSize int
+ ResidentSet int
+ Processes int
+ OpenFiles int
+ LockedMemory int
+ AddressSpace int
+ FileLocks int
+ PendingSignals int
+ MsqqueueSize int
+ NicePriority int
+ RealtimePriority int
+ RealtimeTimeout int
+}
+
+const (
+ limitsFields = 3
+ limitsUnlimited = "unlimited"
+)
+
+var (
+ limitsDelimiter = regexp.MustCompile(" +")
+)
+
+// NewLimits returns the current soft limits of the process.
+func (p Proc) NewLimits() (ProcLimits, error) {
+ f, err := p.open("limits")
+ if err != nil {
+ return ProcLimits{}, err
+ }
+ defer f.Close()
+
+ var (
+ l = ProcLimits{}
+ s = bufio.NewScanner(f)
+ )
+ for s.Scan() {
+ fields := limitsDelimiter.Split(s.Text(), limitsFields)
+ if len(fields) != limitsFields {
+ return ProcLimits{}, fmt.Errorf(
+ "couldn't parse %s line %s", f.Name(), s.Text())
+ }
+
+ switch fields[0] {
+ case "Max cpu time":
+ l.CPUTime, err = parseInt(fields[1])
+ case "Max file size":
+ l.FileLocks, err = parseInt(fields[1])
+ case "Max data size":
+ l.DataSize, err = parseInt(fields[1])
+ case "Max stack size":
+ l.StackSize, err = parseInt(fields[1])
+ case "Max core file size":
+ l.CoreFileSize, err = parseInt(fields[1])
+ case "Max resident set":
+ l.ResidentSet, err = parseInt(fields[1])
+ case "Max processes":
+ l.Processes, err = parseInt(fields[1])
+ case "Max open files":
+ l.OpenFiles, err = parseInt(fields[1])
+ case "Max locked memory":
+ l.LockedMemory, err = parseInt(fields[1])
+ case "Max address space":
+ l.AddressSpace, err = parseInt(fields[1])
+ case "Max file locks":
+ l.FileLocks, err = parseInt(fields[1])
+ case "Max pending signals":
+ l.PendingSignals, err = parseInt(fields[1])
+ case "Max msgqueue size":
+ l.MsqqueueSize, err = parseInt(fields[1])
+ case "Max nice priority":
+ l.NicePriority, err = parseInt(fields[1])
+ case "Max realtime priority":
+ l.RealtimePriority, err = parseInt(fields[1])
+ case "Max realtime timeout":
+ l.RealtimeTimeout, err = parseInt(fields[1])
+ }
+
+ if err != nil {
+ return ProcLimits{}, err
+ }
+ }
+
+ return l, s.Err()
+}
+
+func parseInt(s string) (int, error) {
+ if s == limitsUnlimited {
+ return -1, nil
+ }
+ i, err := strconv.ParseInt(s, 10, 32)
+ if err != nil {
+ return 0, fmt.Errorf("couldn't parse value %s: %s", s, err)
+ }
+ return int(i), nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go
new file mode 100644
index 000000000..ca7a254da
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_limits_test.go
@@ -0,0 +1,36 @@
+package procfs
+
+import "testing"
+
+func TestNewLimits(t *testing.T) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p, err := fs.NewProc(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ l, err := p.NewLimits()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, test := range []struct {
+ name string
+ want int
+ got int
+ }{
+ {name: "cpu time", want: -1, got: l.CPUTime},
+ {name: "open files", want: 2048, got: l.OpenFiles},
+ {name: "msgqueue size", want: 819200, got: l.MsqqueueSize},
+ {name: "nice priority", want: 0, got: l.NicePriority},
+ {name: "address space", want: -1, got: l.AddressSpace},
+ } {
+ if test.want != test.got {
+ t.Errorf("want %s %d, got %d", test.name, test.want, test.got)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat.go
new file mode 100644
index 000000000..30a403b6c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat.go
@@ -0,0 +1,175 @@
+package procfs
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+)
+
+// Originally, this USER_HZ value was dynamically retrieved via a sysconf call which
+// required cgo. However, that caused a lot of problems regarding
+// cross-compilation. Alternatives such as running a binary to determine the
+// value, or trying to derive it in some other way were all problematic.
+// After much research it was determined that USER_HZ is actually hardcoded to
+// 100 on all Go-supported platforms as of the time of this writing. This is
+// why we decided to hardcode it here as well. It is not impossible that there
+// could be systems with exceptions, but they should be very exotic edge cases,
+// and in that case, the worst outcome will be two misreported metrics.
+//
+// See also the following discussions:
+//
+// - https://github.com/prometheus/node_exporter/issues/52
+// - https://github.com/prometheus/procfs/pull/2
+// - http://stackoverflow.com/questions/17410841/how-does-user-hz-solve-the-jiffy-scaling-issue
+const userHZ = 100
+
+// ProcStat provides status information about the process,
+// read from /proc/[pid]/stat.
+type ProcStat struct {
+ // The process ID.
+ PID int
+ // The filename of the executable.
+ Comm string
+ // The process state.
+ State string
+ // The PID of the parent of this process.
+ PPID int
+ // The process group ID of the process.
+ PGRP int
+ // The session ID of the process.
+ Session int
+ // The controlling terminal of the process.
+ TTY int
+ // The ID of the foreground process group of the controlling terminal of
+ // the process.
+ TPGID int
+ // The kernel flags word of the process.
+ Flags uint
+ // The number of minor faults the process has made which have not required
+ // loading a memory page from disk.
+ MinFlt uint
+ // The number of minor faults that the process's waited-for children have
+ // made.
+ CMinFlt uint
+ // The number of major faults the process has made which have required
+ // loading a memory page from disk.
+ MajFlt uint
+ // The number of major faults that the process's waited-for children have
+ // made.
+ CMajFlt uint
+ // Amount of time that this process has been scheduled in user mode,
+ // measured in clock ticks.
+ UTime uint
+ // Amount of time that this process has been scheduled in kernel mode,
+ // measured in clock ticks.
+ STime uint
+ // Amount of time that this process's waited-for children have been
+ // scheduled in user mode, measured in clock ticks.
+ CUTime uint
+ // Amount of time that this process's waited-for children have been
+ // scheduled in kernel mode, measured in clock ticks.
+ CSTime uint
+ // For processes running a real-time scheduling policy, this is the negated
+ // scheduling priority, minus one.
+ Priority int
+ // The nice value, a value in the range 19 (low priority) to -20 (high
+ // priority).
+ Nice int
+ // Number of threads in this process.
+ NumThreads int
+ // The time the process started after system boot, the value is expressed
+ // in clock ticks.
+ Starttime uint64
+ // Virtual memory size in bytes.
+ VSize int
+ // Resident set size in pages.
+ RSS int
+
+ fs FS
+}
+
+// NewStat returns the current status information of the process.
+func (p Proc) NewStat() (ProcStat, error) {
+ f, err := p.open("stat")
+ if err != nil {
+ return ProcStat{}, err
+ }
+ defer f.Close()
+
+ data, err := ioutil.ReadAll(f)
+ if err != nil {
+ return ProcStat{}, err
+ }
+
+ var (
+ ignore int
+
+ s = ProcStat{PID: p.PID, fs: p.fs}
+ l = bytes.Index(data, []byte("("))
+ r = bytes.LastIndex(data, []byte(")"))
+ )
+
+ if l < 0 || r < 0 {
+ return ProcStat{}, fmt.Errorf(
+ "unexpected format, couldn't extract comm: %s",
+ data,
+ )
+ }
+
+ s.Comm = string(data[l+1 : r])
+ _, err = fmt.Fscan(
+ bytes.NewBuffer(data[r+2:]),
+ &s.State,
+ &s.PPID,
+ &s.PGRP,
+ &s.Session,
+ &s.TTY,
+ &s.TPGID,
+ &s.Flags,
+ &s.MinFlt,
+ &s.CMinFlt,
+ &s.MajFlt,
+ &s.CMajFlt,
+ &s.UTime,
+ &s.STime,
+ &s.CUTime,
+ &s.CSTime,
+ &s.Priority,
+ &s.Nice,
+ &s.NumThreads,
+ &ignore,
+ &s.Starttime,
+ &s.VSize,
+ &s.RSS,
+ )
+ if err != nil {
+ return ProcStat{}, err
+ }
+
+ return s, nil
+}
+
+// VirtualMemory returns the virtual memory size in bytes.
+func (s ProcStat) VirtualMemory() int {
+ return s.VSize
+}
+
+// ResidentMemory returns the resident memory size in bytes.
+func (s ProcStat) ResidentMemory() int {
+ return s.RSS * os.Getpagesize()
+}
+
+// StartTime returns the unix timestamp of the process in seconds.
+func (s ProcStat) StartTime() (float64, error) {
+ stat, err := s.fs.NewStat()
+ if err != nil {
+ return 0, err
+ }
+ return float64(stat.BootTime) + (float64(s.Starttime) / userHZ), nil
+}
+
+// CPUTime returns the total CPU user and system time in seconds.
+func (s ProcStat) CPUTime() float64 {
+ return float64(s.UTime+s.STime) / userHZ
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go
new file mode 100644
index 000000000..e4d5cacfa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_stat_test.go
@@ -0,0 +1,112 @@
+package procfs
+
+import "testing"
+
+func TestProcStat(t *testing.T) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p, err := fs.NewProc(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ s, err := p.NewStat()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, test := range []struct {
+ name string
+ want int
+ got int
+ }{
+ {name: "pid", want: 26231, got: s.PID},
+ {name: "user time", want: 1677, got: int(s.UTime)},
+ {name: "system time", want: 44, got: int(s.STime)},
+ {name: "start time", want: 82375, got: int(s.Starttime)},
+ {name: "virtual memory size", want: 56274944, got: s.VSize},
+ {name: "resident set size", want: 1981, got: s.RSS},
+ } {
+ if test.want != test.got {
+ t.Errorf("want %s %d, got %d", test.name, test.want, test.got)
+ }
+ }
+}
+
+func TestProcStatComm(t *testing.T) {
+ s1, err := testProcStat(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want, got := "vim", s1.Comm; want != got {
+ t.Errorf("want comm %s, got %s", want, got)
+ }
+
+ s2, err := testProcStat(584)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want, got := "(a b ) ( c d) ", s2.Comm; want != got {
+ t.Errorf("want comm %s, got %s", want, got)
+ }
+}
+
+func TestProcStatVirtualMemory(t *testing.T) {
+ s, err := testProcStat(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if want, got := 56274944, s.VirtualMemory(); want != got {
+ t.Errorf("want virtual memory %d, got %d", want, got)
+ }
+}
+
+func TestProcStatResidentMemory(t *testing.T) {
+ s, err := testProcStat(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if want, got := 1981*4096, s.ResidentMemory(); want != got {
+ t.Errorf("want resident memory %d, got %d", want, got)
+ }
+}
+
+func TestProcStatStartTime(t *testing.T) {
+ s, err := testProcStat(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ time, err := s.StartTime()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want, got := 1418184099.75, time; want != got {
+ t.Errorf("want start time %f, got %f", want, got)
+ }
+}
+
+func TestProcStatCPUTime(t *testing.T) {
+ s, err := testProcStat(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if want, got := 17.21, s.CPUTime(); want != got {
+ t.Errorf("want cpu time %f, got %f", want, got)
+ }
+}
+
+func testProcStat(pid int) (ProcStat, error) {
+ p, err := testProcess(pid)
+ if err != nil {
+ return ProcStat{}, err
+ }
+
+ return p.NewStat()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go
new file mode 100644
index 000000000..4197282ca
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/proc_test.go
@@ -0,0 +1,146 @@
+package procfs
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+)
+
+func TestSelf(t *testing.T) {
+ fs := FS("fixtures")
+
+ p1, err := fs.NewProc(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+ p2, err := fs.Self()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !reflect.DeepEqual(p1, p2) {
+ t.Errorf("want process %v to equal %v", p1, p2)
+ }
+}
+
+func TestAllProcs(t *testing.T) {
+ procs, err := FS("fixtures").AllProcs()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sort.Sort(procs)
+ for i, p := range []*Proc{{PID: 584}, {PID: 26231}} {
+ if want, got := p.PID, procs[i].PID; want != got {
+ t.Errorf("want processes %d, got %d", want, got)
+ }
+ }
+}
+
+func TestCmdLine(t *testing.T) {
+ for _, tt := range []struct {
+ process int
+ want []string
+ }{
+ {process: 26231, want: []string{"vim", "test.go", "+10"}},
+ {process: 26232, want: []string{}},
+ } {
+ p1, err := testProcess(tt.process)
+ if err != nil {
+ t.Fatal(err)
+ }
+ c1, err := p1.CmdLine()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(tt.want, c1) {
+ t.Errorf("want cmdline %v, got %v", tt.want, c1)
+ }
+ }
+}
+
+func TestExecutable(t *testing.T) {
+ for _, tt := range []struct {
+ process int
+ want string
+ }{
+ {process: 26231, want: "/usr/bin/vim"},
+ {process: 26232, want: ""},
+ } {
+ p, err := testProcess(tt.process)
+ if err != nil {
+ t.Fatal(err)
+ }
+ exe, err := p.Executable()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(tt.want, exe) {
+ t.Errorf("want absolute path to cmdline %v, got %v", tt.want, exe)
+ }
+ }
+}
+
+func TestFileDescriptors(t *testing.T) {
+ p1, err := testProcess(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fds, err := p1.FileDescriptors()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sort.Sort(byUintptr(fds))
+ if want := []uintptr{0, 1, 2, 3, 10}; !reflect.DeepEqual(want, fds) {
+ t.Errorf("want fds %v, got %v", want, fds)
+ }
+}
+
+func TestFileDescriptorTargets(t *testing.T) {
+ p1, err := testProcess(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fds, err := p1.FileDescriptorTargets()
+ if err != nil {
+ t.Fatal(err)
+ }
+ sort.Strings(fds)
+ var want = []string{
+ "../../symlinktargets/abc",
+ "../../symlinktargets/def",
+ "../../symlinktargets/ghi",
+ "../../symlinktargets/uvw",
+ "../../symlinktargets/xyz",
+ }
+ if !reflect.DeepEqual(want, fds) {
+ t.Errorf("want fds %v, got %v", want, fds)
+ }
+}
+
+func TestFileDescriptorsLen(t *testing.T) {
+ p1, err := testProcess(26231)
+ if err != nil {
+ t.Fatal(err)
+ }
+ l, err := p1.FileDescriptorsLen()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want, got := 5, l; want != got {
+ t.Errorf("want fds %d, got %d", want, got)
+ }
+}
+
+func testProcess(pid int) (Proc, error) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ return Proc{}, err
+ }
+ return fs.NewProc(pid)
+}
+
+type byUintptr []uintptr
+
+func (a byUintptr) Len() int { return len(a) }
+func (a byUintptr) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byUintptr) Less(i, j int) bool { return a[i] < a[j] }
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/stat.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/stat.go
new file mode 100644
index 000000000..26fefb0fa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/stat.go
@@ -0,0 +1,55 @@
+package procfs
+
+import (
+ "bufio"
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// Stat represents kernel/system statistics.
+type Stat struct {
+ // Boot time in seconds since the Epoch.
+ BootTime int64
+}
+
+// NewStat returns kernel/system statistics read from /proc/stat.
+func NewStat() (Stat, error) {
+ fs, err := NewFS(DefaultMountPoint)
+ if err != nil {
+ return Stat{}, err
+ }
+
+ return fs.NewStat()
+}
+
+// NewStat returns an information about current kernel/system statistics.
+func (fs FS) NewStat() (Stat, error) {
+ f, err := fs.open("stat")
+ if err != nil {
+ return Stat{}, err
+ }
+ defer f.Close()
+
+ s := bufio.NewScanner(f)
+ for s.Scan() {
+ line := s.Text()
+ if !strings.HasPrefix(line, "btime") {
+ continue
+ }
+ fields := strings.Fields(line)
+ if len(fields) != 2 {
+ return Stat{}, fmt.Errorf("couldn't parse %s line %s", f.Name(), line)
+ }
+ i, err := strconv.ParseInt(fields[1], 10, 32)
+ if err != nil {
+ return Stat{}, fmt.Errorf("couldn't parse %s: %s", fields[1], err)
+ }
+ return Stat{BootTime: i}, nil
+ }
+ if err := s.Err(); err != nil {
+ return Stat{}, fmt.Errorf("couldn't parse %s: %s", f.Name(), err)
+ }
+
+ return Stat{}, fmt.Errorf("couldn't parse %s, missing btime", f.Name())
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go
new file mode 100644
index 000000000..24b5d61f8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/procfs/stat_test.go
@@ -0,0 +1,19 @@
+package procfs
+
+import "testing"
+
+func TestStat(t *testing.T) {
+ fs, err := NewFS("fixtures")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ s, err := fs.NewStat()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if want, got := int64(1418183276), s.BootTime; want != got {
+ t.Errorf("want boot time %d, got %d", want, got)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go
new file mode 100644
index 000000000..dd8b589de
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/0doc.go
@@ -0,0 +1,150 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+/*
+High Performance, Feature-Rich Idiomatic Go codec/encoding library for
+binc, msgpack, cbor, json.
+
+Supported Serialization formats are:
+
+ - msgpack: https://github.com/msgpack/msgpack
+ - binc: http://github.com/ugorji/binc
+ - cbor: http://cbor.io http://tools.ietf.org/html/rfc7049
+ - json: http://json.org http://tools.ietf.org/html/rfc7159
+ - simple:
+
+To install:
+
+ go get github.com/ugorji/go/codec
+
+This package understands the 'unsafe' tag, to allow using unsafe semantics:
+
+ - When decoding into a struct, you need to read the field name as a string
+ so you can find the struct field it is mapped to.
+ Using `unsafe` will bypass the allocation and copying overhead of []byte->string conversion.
+
+To install using unsafe, pass the 'unsafe' tag:
+
+ go get -tags=unsafe github.com/ugorji/go/codec
+
+For detailed usage information, read the primer at http://ugorji.net/blog/go-codec-primer .
+
+The idiomatic Go support is as seen in other encoding packages in
+the standard library (ie json, xml, gob, etc).
+
+Rich Feature Set includes:
+
+ - Simple but extremely powerful and feature-rich API
+ - Very High Performance.
+ Our extensive benchmarks show us outperforming Gob, Json, Bson, etc by 2-4X.
+ - Multiple conversions:
+ Package coerces types where appropriate
+ e.g. decode an int in the stream into a float, etc.
+ - Corner Cases:
+ Overflows, nil maps/slices, nil values in streams are handled correctly
+ - Standard field renaming via tags
+ - Support for omitting empty fields during an encoding
+ - Encoding from any value and decoding into pointer to any value
+ (struct, slice, map, primitives, pointers, interface{}, etc)
+ - Extensions to support efficient encoding/decoding of any named types
+ - Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
+ - Decoding without a schema (into a interface{}).
+ Includes Options to configure what specific map or slice type to use
+ when decoding an encoded list or map into a nil interface{}
+ - Encode a struct as an array, and decode struct from an array in the data stream
+ - Comprehensive support for anonymous fields
+ - Fast (no-reflection) encoding/decoding of common maps and slices
+ - Code-generation for faster performance.
+ - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
+ - Support indefinite-length formats to enable true streaming
+ (for formats which support it e.g. json, cbor)
+ - Support canonical encoding, where a value is ALWAYS encoded as same sequence of bytes.
+ This mostly applies to maps, where iteration order is non-deterministic.
+ - NIL in data stream decoded as zero value
+ - Never silently skip data when decoding.
+ User decides whether to return an error or silently skip data when keys or indexes
+ in the data stream do not map to fields in the struct.
+ - Encode/Decode from/to chan types (for iterative streaming support)
+ - Drop-in replacement for encoding/json. `json:` key in struct tag supported.
+ - Provides a RPC Server and Client Codec for net/rpc communication protocol.
+ - Handle unique idiosynchracies of codecs e.g.
+ - For messagepack, configure how ambiguities in handling raw bytes are resolved
+ - For messagepack, provide rpc server/client codec to support
+ msgpack-rpc protocol defined at:
+ https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
+
+Extension Support
+
+Users can register a function to handle the encoding or decoding of
+their custom types.
+
+There are no restrictions on what the custom type can be. Some examples:
+
+ type BisSet []int
+ type BitSet64 uint64
+ type UUID string
+ type MyStructWithUnexportedFields struct { a int; b bool; c []int; }
+ type GifImage struct { ... }
+
+As an illustration, MyStructWithUnexportedFields would normally be
+encoded as an empty map because it has no exported fields, while UUID
+would be encoded as a string. However, with extension support, you can
+encode any of these however you like.
+
+RPC
+
+RPC Client and Server Codecs are implemented, so the codecs can be used
+with the standard net/rpc package.
+
+Usage
+
+Typical usage model:
+
+ // create and configure Handle
+ var (
+ bh codec.BincHandle
+ mh codec.MsgpackHandle
+ ch codec.CborHandle
+ )
+
+ mh.MapType = reflect.TypeOf(map[string]interface{}(nil))
+
+ // configure extensions
+ // e.g. for msgpack, define functions and enable Time support for tag 1
+ // mh.SetExt(reflect.TypeOf(time.Time{}), 1, myExt)
+
+ // create and use decoder/encoder
+ var (
+ r io.Reader
+ w io.Writer
+ b []byte
+ h = &bh // or mh to use msgpack
+ )
+
+ dec = codec.NewDecoder(r, h)
+ dec = codec.NewDecoderBytes(b, h)
+ err = dec.Decode(&v)
+
+ enc = codec.NewEncoder(w, h)
+ enc = codec.NewEncoderBytes(&b, h)
+ err = enc.Encode(v)
+
+ //RPC Server
+ go func() {
+ for {
+ conn, err := listener.Accept()
+ rpcCodec := codec.GoRpc.ServerCodec(conn, h)
+ //OR rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, h)
+ rpc.ServeCodec(rpcCodec)
+ }
+ }()
+
+ //RPC Communication (client side)
+ conn, err = net.Dial("tcp", "localhost:5555")
+ rpcCodec := codec.GoRpc.ClientCodec(conn, h)
+ //OR rpcCodec := codec.MsgpackSpecRpc.ClientCodec(conn, h)
+ client := rpc.NewClientWithCodec(rpcCodec)
+
+*/
+package codec
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/README.md
new file mode 100644
index 000000000..a790a52bb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/README.md
@@ -0,0 +1,148 @@
+# Codec
+
+High Performance, Feature-Rich Idiomatic Go codec/encoding library for
+binc, msgpack, cbor, json.
+
+Supported Serialization formats are:
+
+ - msgpack: https://github.com/msgpack/msgpack
+ - binc: http://github.com/ugorji/binc
+ - cbor: http://cbor.io http://tools.ietf.org/html/rfc7049
+ - json: http://json.org http://tools.ietf.org/html/rfc7159
+ - simple:
+
+To install:
+
+ go get github.com/ugorji/go/codec
+
+This package understands the `unsafe` tag, to allow using unsafe semantics:
+
+ - When decoding into a struct, you need to read the field name as a string
+ so you can find the struct field it is mapped to.
+ Using `unsafe` will bypass the allocation and copying overhead of `[]byte->string` conversion.
+
+To use it, you must pass the `unsafe` tag during install:
+
+```
+go install -tags=unsafe github.com/ugorji/go/codec
+```
+
+Online documentation: http://godoc.org/github.com/ugorji/go/codec
+Detailed Usage/How-to Primer: http://ugorji.net/blog/go-codec-primer
+
+The idiomatic Go support is as seen in other encoding packages in
+the standard library (ie json, xml, gob, etc).
+
+Rich Feature Set includes:
+
+ - Simple but extremely powerful and feature-rich API
+ - Very High Performance.
+ Our extensive benchmarks show us outperforming Gob, Json, Bson, etc by 2-4X.
+ - Multiple conversions:
+ Package coerces types where appropriate
+ e.g. decode an int in the stream into a float, etc.
+ - Corner Cases:
+ Overflows, nil maps/slices, nil values in streams are handled correctly
+ - Standard field renaming via tags
+ - Support for omitting empty fields during an encoding
+ - Encoding from any value and decoding into pointer to any value
+ (struct, slice, map, primitives, pointers, interface{}, etc)
+ - Extensions to support efficient encoding/decoding of any named types
+ - Support encoding.(Binary|Text)(M|Unm)arshaler interfaces
+ - Decoding without a schema (into a interface{}).
+ Includes Options to configure what specific map or slice type to use
+ when decoding an encoded list or map into a nil interface{}
+ - Encode a struct as an array, and decode struct from an array in the data stream
+ - Comprehensive support for anonymous fields
+ - Fast (no-reflection) encoding/decoding of common maps and slices
+ - Code-generation for faster performance.
+ - Support binary (e.g. messagepack, cbor) and text (e.g. json) formats
+ - Support indefinite-length formats to enable true streaming
+ (for formats which support it e.g. json, cbor)
+ - Support canonical encoding, where a value is ALWAYS encoded as same sequence of bytes.
+ This mostly applies to maps, where iteration order is non-deterministic.
+ - NIL in data stream decoded as zero value
+ - Never silently skip data when decoding.
+ User decides whether to return an error or silently skip data when keys or indexes
+ in the data stream do not map to fields in the struct.
+ - Encode/Decode from/to chan types (for iterative streaming support)
+ - Drop-in replacement for encoding/json. `json:` key in struct tag supported.
+ - Provides a RPC Server and Client Codec for net/rpc communication protocol.
+ - Handle unique idiosynchracies of codecs e.g.
+ - For messagepack, configure how ambiguities in handling raw bytes are resolved
+ - For messagepack, provide rpc server/client codec to support
+ msgpack-rpc protocol defined at:
+ https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
+
+## Extension Support
+
+Users can register a function to handle the encoding or decoding of
+their custom types.
+
+There are no restrictions on what the custom type can be. Some examples:
+
+ type BisSet []int
+ type BitSet64 uint64
+ type UUID string
+ type MyStructWithUnexportedFields struct { a int; b bool; c []int; }
+ type GifImage struct { ... }
+
+As an illustration, MyStructWithUnexportedFields would normally be
+encoded as an empty map because it has no exported fields, while UUID
+would be encoded as a string. However, with extension support, you can
+encode any of these however you like.
+
+## RPC
+
+RPC Client and Server Codecs are implemented, so the codecs can be used
+with the standard net/rpc package.
+
+## Usage
+
+Typical usage model:
+
+ // create and configure Handle
+ var (
+ bh codec.BincHandle
+ mh codec.MsgpackHandle
+ ch codec.CborHandle
+ )
+
+ mh.MapType = reflect.TypeOf(map[string]interface{}(nil))
+
+ // configure extensions
+ // e.g. for msgpack, define functions and enable Time support for tag 1
+ // mh.SetExt(reflect.TypeOf(time.Time{}), 1, myExt)
+
+ // create and use decoder/encoder
+ var (
+ r io.Reader
+ w io.Writer
+ b []byte
+ h = &bh // or mh to use msgpack
+ )
+
+ dec = codec.NewDecoder(r, h)
+ dec = codec.NewDecoderBytes(b, h)
+ err = dec.Decode(&v)
+
+ enc = codec.NewEncoder(w, h)
+ enc = codec.NewEncoderBytes(&b, h)
+ err = enc.Encode(v)
+
+ //RPC Server
+ go func() {
+ for {
+ conn, err := listener.Accept()
+ rpcCodec := codec.GoRpc.ServerCodec(conn, h)
+ //OR rpcCodec := codec.MsgpackSpecRpc.ServerCodec(conn, h)
+ rpc.ServeCodec(rpcCodec)
+ }
+ }()
+
+ //RPC Communication (client side)
+ conn, err = net.Dial("tcp", "localhost:5555")
+ rpcCodec := codec.GoRpc.ClientCodec(conn, h)
+ //OR rpcCodec := codec.MsgpackSpecRpc.ClientCodec(conn, h)
+ client := rpc.NewClientWithCodec(rpcCodec)
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go
new file mode 100644
index 000000000..645376479
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/binc.go
@@ -0,0 +1,914 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "math"
+ "reflect"
+ "time"
+)
+
+const bincDoPrune = true // No longer needed. Needed before as C lib did not support pruning.
+
+// vd as low 4 bits (there are 16 slots)
+const (
+ bincVdSpecial byte = iota
+ bincVdPosInt
+ bincVdNegInt
+ bincVdFloat
+
+ bincVdString
+ bincVdByteArray
+ bincVdArray
+ bincVdMap
+
+ bincVdTimestamp
+ bincVdSmallInt
+ bincVdUnicodeOther
+ bincVdSymbol
+
+ bincVdDecimal
+ _ // open slot
+ _ // open slot
+ bincVdCustomExt = 0x0f
+)
+
+const (
+ bincSpNil byte = iota
+ bincSpFalse
+ bincSpTrue
+ bincSpNan
+ bincSpPosInf
+ bincSpNegInf
+ bincSpZeroFloat
+ bincSpZero
+ bincSpNegOne
+)
+
+const (
+ bincFlBin16 byte = iota
+ bincFlBin32
+ _ // bincFlBin32e
+ bincFlBin64
+ _ // bincFlBin64e
+ // others not currently supported
+)
+
+type bincEncDriver struct {
+ e *Encoder
+ w encWriter
+ m map[string]uint16 // symbols
+ s uint16 // symbols sequencer
+ b [scratchByteArrayLen]byte
+ encNoSeparator
+}
+
+func (e *bincEncDriver) IsBuiltinType(rt uintptr) bool {
+ return rt == timeTypId
+}
+
+func (e *bincEncDriver) EncodeBuiltin(rt uintptr, v interface{}) {
+ if rt == timeTypId {
+ var bs []byte
+ switch x := v.(type) {
+ case time.Time:
+ bs = encodeTime(x)
+ case *time.Time:
+ bs = encodeTime(*x)
+ default:
+ e.e.errorf("binc error encoding builtin: expect time.Time, received %T", v)
+ }
+ e.w.writen1(bincVdTimestamp<<4 | uint8(len(bs)))
+ e.w.writeb(bs)
+ }
+}
+
+func (e *bincEncDriver) EncodeNil() {
+ e.w.writen1(bincVdSpecial<<4 | bincSpNil)
+}
+
+func (e *bincEncDriver) EncodeBool(b bool) {
+ if b {
+ e.w.writen1(bincVdSpecial<<4 | bincSpTrue)
+ } else {
+ e.w.writen1(bincVdSpecial<<4 | bincSpFalse)
+ }
+}
+
+func (e *bincEncDriver) EncodeFloat32(f float32) {
+ if f == 0 {
+ e.w.writen1(bincVdSpecial<<4 | bincSpZeroFloat)
+ return
+ }
+ e.w.writen1(bincVdFloat<<4 | bincFlBin32)
+ bigenHelper{e.b[:4], e.w}.writeUint32(math.Float32bits(f))
+}
+
+func (e *bincEncDriver) EncodeFloat64(f float64) {
+ if f == 0 {
+ e.w.writen1(bincVdSpecial<<4 | bincSpZeroFloat)
+ return
+ }
+ bigen.PutUint64(e.b[:8], math.Float64bits(f))
+ if bincDoPrune {
+ i := 7
+ for ; i >= 0 && (e.b[i] == 0); i-- {
+ }
+ i++
+ if i <= 6 {
+ e.w.writen1(bincVdFloat<<4 | 0x8 | bincFlBin64)
+ e.w.writen1(byte(i))
+ e.w.writeb(e.b[:i])
+ return
+ }
+ }
+ e.w.writen1(bincVdFloat<<4 | bincFlBin64)
+ e.w.writeb(e.b[:8])
+}
+
+func (e *bincEncDriver) encIntegerPrune(bd byte, pos bool, v uint64, lim uint8) {
+ if lim == 4 {
+ bigen.PutUint32(e.b[:lim], uint32(v))
+ } else {
+ bigen.PutUint64(e.b[:lim], v)
+ }
+ if bincDoPrune {
+ i := pruneSignExt(e.b[:lim], pos)
+ e.w.writen1(bd | lim - 1 - byte(i))
+ e.w.writeb(e.b[i:lim])
+ } else {
+ e.w.writen1(bd | lim - 1)
+ e.w.writeb(e.b[:lim])
+ }
+}
+
+func (e *bincEncDriver) EncodeInt(v int64) {
+ const nbd byte = bincVdNegInt << 4
+ if v >= 0 {
+ e.encUint(bincVdPosInt<<4, true, uint64(v))
+ } else if v == -1 {
+ e.w.writen1(bincVdSpecial<<4 | bincSpNegOne)
+ } else {
+ e.encUint(bincVdNegInt<<4, false, uint64(-v))
+ }
+}
+
+func (e *bincEncDriver) EncodeUint(v uint64) {
+ e.encUint(bincVdPosInt<<4, true, v)
+}
+
+func (e *bincEncDriver) encUint(bd byte, pos bool, v uint64) {
+ if v == 0 {
+ e.w.writen1(bincVdSpecial<<4 | bincSpZero)
+ } else if pos && v >= 1 && v <= 16 {
+ e.w.writen1(bincVdSmallInt<<4 | byte(v-1))
+ } else if v <= math.MaxUint8 {
+ e.w.writen2(bd|0x0, byte(v))
+ } else if v <= math.MaxUint16 {
+ e.w.writen1(bd | 0x01)
+ bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
+ } else if v <= math.MaxUint32 {
+ e.encIntegerPrune(bd, pos, v, 4)
+ } else {
+ e.encIntegerPrune(bd, pos, v, 8)
+ }
+}
+
+func (e *bincEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, _ *Encoder) {
+ bs := ext.WriteExt(rv)
+ if bs == nil {
+ e.EncodeNil()
+ return
+ }
+ e.encodeExtPreamble(uint8(xtag), len(bs))
+ e.w.writeb(bs)
+}
+
+func (e *bincEncDriver) EncodeRawExt(re *RawExt, _ *Encoder) {
+ e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
+ e.w.writeb(re.Data)
+}
+
+func (e *bincEncDriver) encodeExtPreamble(xtag byte, length int) {
+ e.encLen(bincVdCustomExt<<4, uint64(length))
+ e.w.writen1(xtag)
+}
+
+func (e *bincEncDriver) EncodeArrayStart(length int) {
+ e.encLen(bincVdArray<<4, uint64(length))
+}
+
+func (e *bincEncDriver) EncodeMapStart(length int) {
+ e.encLen(bincVdMap<<4, uint64(length))
+}
+
+func (e *bincEncDriver) EncodeString(c charEncoding, v string) {
+ l := uint64(len(v))
+ e.encBytesLen(c, l)
+ if l > 0 {
+ e.w.writestr(v)
+ }
+}
+
+func (e *bincEncDriver) EncodeSymbol(v string) {
+ // if WriteSymbolsNoRefs {
+ // e.encodeString(c_UTF8, v)
+ // return
+ // }
+
+ //symbols only offer benefit when string length > 1.
+ //This is because strings with length 1 take only 2 bytes to store
+ //(bd with embedded length, and single byte for string val).
+
+ l := len(v)
+ if l == 0 {
+ e.encBytesLen(c_UTF8, 0)
+ return
+ } else if l == 1 {
+ e.encBytesLen(c_UTF8, 1)
+ e.w.writen1(v[0])
+ return
+ }
+ if e.m == nil {
+ e.m = make(map[string]uint16, 16)
+ }
+ ui, ok := e.m[v]
+ if ok {
+ if ui <= math.MaxUint8 {
+ e.w.writen2(bincVdSymbol<<4, byte(ui))
+ } else {
+ e.w.writen1(bincVdSymbol<<4 | 0x8)
+ bigenHelper{e.b[:2], e.w}.writeUint16(ui)
+ }
+ } else {
+ e.s++
+ ui = e.s
+ //ui = uint16(atomic.AddUint32(&e.s, 1))
+ e.m[v] = ui
+ var lenprec uint8
+ if l <= math.MaxUint8 {
+ // lenprec = 0
+ } else if l <= math.MaxUint16 {
+ lenprec = 1
+ } else if int64(l) <= math.MaxUint32 {
+ lenprec = 2
+ } else {
+ lenprec = 3
+ }
+ if ui <= math.MaxUint8 {
+ e.w.writen2(bincVdSymbol<<4|0x0|0x4|lenprec, byte(ui))
+ } else {
+ e.w.writen1(bincVdSymbol<<4 | 0x8 | 0x4 | lenprec)
+ bigenHelper{e.b[:2], e.w}.writeUint16(ui)
+ }
+ if lenprec == 0 {
+ e.w.writen1(byte(l))
+ } else if lenprec == 1 {
+ bigenHelper{e.b[:2], e.w}.writeUint16(uint16(l))
+ } else if lenprec == 2 {
+ bigenHelper{e.b[:4], e.w}.writeUint32(uint32(l))
+ } else {
+ bigenHelper{e.b[:8], e.w}.writeUint64(uint64(l))
+ }
+ e.w.writestr(v)
+ }
+}
+
+func (e *bincEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
+ l := uint64(len(v))
+ e.encBytesLen(c, l)
+ if l > 0 {
+ e.w.writeb(v)
+ }
+}
+
+func (e *bincEncDriver) encBytesLen(c charEncoding, length uint64) {
+ //TODO: support bincUnicodeOther (for now, just use string or bytearray)
+ if c == c_RAW {
+ e.encLen(bincVdByteArray<<4, length)
+ } else {
+ e.encLen(bincVdString<<4, length)
+ }
+}
+
+func (e *bincEncDriver) encLen(bd byte, l uint64) {
+ if l < 12 {
+ e.w.writen1(bd | uint8(l+4))
+ } else {
+ e.encLenNumber(bd, l)
+ }
+}
+
+func (e *bincEncDriver) encLenNumber(bd byte, v uint64) {
+ if v <= math.MaxUint8 {
+ e.w.writen2(bd, byte(v))
+ } else if v <= math.MaxUint16 {
+ e.w.writen1(bd | 0x01)
+ bigenHelper{e.b[:2], e.w}.writeUint16(uint16(v))
+ } else if v <= math.MaxUint32 {
+ e.w.writen1(bd | 0x02)
+ bigenHelper{e.b[:4], e.w}.writeUint32(uint32(v))
+ } else {
+ e.w.writen1(bd | 0x03)
+ bigenHelper{e.b[:8], e.w}.writeUint64(uint64(v))
+ }
+}
+
+//------------------------------------
+
+type bincDecSymbol struct {
+ i uint16
+ s string
+ b []byte
+}
+
+type bincDecDriver struct {
+ d *Decoder
+ h *BincHandle
+ r decReader
+ br bool // bytes reader
+ bdRead bool
+ bdType valueType
+ bd byte
+ vd byte
+ vs byte
+ noStreamingCodec
+ decNoSeparator
+ b [scratchByteArrayLen]byte
+
+ // linear searching on this slice is ok,
+ // because we typically expect < 32 symbols in each stream.
+ s []bincDecSymbol
+}
+
+func (d *bincDecDriver) readNextBd() {
+ d.bd = d.r.readn1()
+ d.vd = d.bd >> 4
+ d.vs = d.bd & 0x0f
+ d.bdRead = true
+ d.bdType = valueTypeUnset
+}
+
+func (d *bincDecDriver) IsContainerType(vt valueType) (b bool) {
+ switch vt {
+ case valueTypeNil:
+ return d.vd == bincVdSpecial && d.vs == bincSpNil
+ case valueTypeBytes:
+ return d.vd == bincVdByteArray
+ case valueTypeString:
+ return d.vd == bincVdString
+ case valueTypeArray:
+ return d.vd == bincVdArray
+ case valueTypeMap:
+ return d.vd == bincVdMap
+ }
+ d.d.errorf("isContainerType: unsupported parameter: %v", vt)
+ return // "unreachable"
+}
+
+func (d *bincDecDriver) TryDecodeAsNil() bool {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == bincVdSpecial<<4|bincSpNil {
+ d.bdRead = false
+ return true
+ }
+ return false
+}
+
+func (d *bincDecDriver) IsBuiltinType(rt uintptr) bool {
+ return rt == timeTypId
+}
+
+func (d *bincDecDriver) DecodeBuiltin(rt uintptr, v interface{}) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if rt == timeTypId {
+ if d.vd != bincVdTimestamp {
+ d.d.errorf("Invalid d.vd. Expecting 0x%x. Received: 0x%x", bincVdTimestamp, d.vd)
+ return
+ }
+ tt, err := decodeTime(d.r.readx(int(d.vs)))
+ if err != nil {
+ panic(err)
+ }
+ var vt *time.Time = v.(*time.Time)
+ *vt = tt
+ d.bdRead = false
+ }
+}
+
+func (d *bincDecDriver) decFloatPre(vs, defaultLen byte) {
+ if vs&0x8 == 0 {
+ d.r.readb(d.b[0:defaultLen])
+ } else {
+ l := d.r.readn1()
+ if l > 8 {
+ d.d.errorf("At most 8 bytes used to represent float. Received: %v bytes", l)
+ return
+ }
+ for i := l; i < 8; i++ {
+ d.b[i] = 0
+ }
+ d.r.readb(d.b[0:l])
+ }
+}
+
+func (d *bincDecDriver) decFloat() (f float64) {
+ //if true { f = math.Float64frombits(bigen.Uint64(d.r.readx(8))); break; }
+ if x := d.vs & 0x7; x == bincFlBin32 {
+ d.decFloatPre(d.vs, 4)
+ f = float64(math.Float32frombits(bigen.Uint32(d.b[0:4])))
+ } else if x == bincFlBin64 {
+ d.decFloatPre(d.vs, 8)
+ f = math.Float64frombits(bigen.Uint64(d.b[0:8]))
+ } else {
+ d.d.errorf("only float32 and float64 are supported. d.vd: 0x%x, d.vs: 0x%x", d.vd, d.vs)
+ return
+ }
+ return
+}
+
+func (d *bincDecDriver) decUint() (v uint64) {
+ // need to inline the code (interface conversion and type assertion expensive)
+ switch d.vs {
+ case 0:
+ v = uint64(d.r.readn1())
+ case 1:
+ d.r.readb(d.b[6:8])
+ v = uint64(bigen.Uint16(d.b[6:8]))
+ case 2:
+ d.b[4] = 0
+ d.r.readb(d.b[5:8])
+ v = uint64(bigen.Uint32(d.b[4:8]))
+ case 3:
+ d.r.readb(d.b[4:8])
+ v = uint64(bigen.Uint32(d.b[4:8]))
+ case 4, 5, 6:
+ lim := int(7 - d.vs)
+ d.r.readb(d.b[lim:8])
+ for i := 0; i < lim; i++ {
+ d.b[i] = 0
+ }
+ v = uint64(bigen.Uint64(d.b[:8]))
+ case 7:
+ d.r.readb(d.b[:8])
+ v = uint64(bigen.Uint64(d.b[:8]))
+ default:
+ d.d.errorf("unsigned integers with greater than 64 bits of precision not supported")
+ return
+ }
+ return
+}
+
+func (d *bincDecDriver) decCheckInteger() (ui uint64, neg bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ vd, vs := d.vd, d.vs
+ if vd == bincVdPosInt {
+ ui = d.decUint()
+ } else if vd == bincVdNegInt {
+ ui = d.decUint()
+ neg = true
+ } else if vd == bincVdSmallInt {
+ ui = uint64(d.vs) + 1
+ } else if vd == bincVdSpecial {
+ if vs == bincSpZero {
+ //i = 0
+ } else if vs == bincSpNegOne {
+ neg = true
+ ui = 1
+ } else {
+ d.d.errorf("numeric decode fails for special value: d.vs: 0x%x", d.vs)
+ return
+ }
+ } else {
+ d.d.errorf("number can only be decoded from uint or int values. d.bd: 0x%x, d.vd: 0x%x", d.bd, d.vd)
+ return
+ }
+ return
+}
+
+func (d *bincDecDriver) DecodeInt(bitsize uint8) (i int64) {
+ ui, neg := d.decCheckInteger()
+ i, overflow := chkOvf.SignedInt(ui)
+ if overflow {
+ d.d.errorf("simple: overflow converting %v to signed integer", ui)
+ return
+ }
+ if neg {
+ i = -i
+ }
+ if chkOvf.Int(i, bitsize) {
+ d.d.errorf("binc: overflow integer: %v", i)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
+ ui, neg := d.decCheckInteger()
+ if neg {
+ d.d.errorf("Assigning negative signed value to unsigned type")
+ return
+ }
+ if chkOvf.Uint(ui, bitsize) {
+ d.d.errorf("binc: overflow integer: %v", ui)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ vd, vs := d.vd, d.vs
+ if vd == bincVdSpecial {
+ d.bdRead = false
+ if vs == bincSpNan {
+ return math.NaN()
+ } else if vs == bincSpPosInf {
+ return math.Inf(1)
+ } else if vs == bincSpZeroFloat || vs == bincSpZero {
+ return
+ } else if vs == bincSpNegInf {
+ return math.Inf(-1)
+ } else {
+ d.d.errorf("Invalid d.vs decoding float where d.vd=bincVdSpecial: %v", d.vs)
+ return
+ }
+ } else if vd == bincVdFloat {
+ f = d.decFloat()
+ } else {
+ f = float64(d.DecodeInt(64))
+ }
+ if chkOverflow32 && chkOvf.Float32(f) {
+ d.d.errorf("binc: float32 overflow: %v", f)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+// bool can be decoded from bool only (single byte).
+func (d *bincDecDriver) DecodeBool() (b bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if bd := d.bd; bd == (bincVdSpecial | bincSpFalse) {
+ // b = false
+ } else if bd == (bincVdSpecial | bincSpTrue) {
+ b = true
+ } else {
+ d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) ReadMapStart() (length int) {
+ if d.vd != bincVdMap {
+ d.d.errorf("Invalid d.vd for map. Expecting 0x%x. Got: 0x%x", bincVdMap, d.vd)
+ return
+ }
+ length = d.decLen()
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) ReadArrayStart() (length int) {
+ if d.vd != bincVdArray {
+ d.d.errorf("Invalid d.vd for array. Expecting 0x%x. Got: 0x%x", bincVdArray, d.vd)
+ return
+ }
+ length = d.decLen()
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) decLen() int {
+ if d.vs > 3 {
+ return int(d.vs - 4)
+ }
+ return int(d.decLenNumber())
+}
+
+func (d *bincDecDriver) decLenNumber() (v uint64) {
+ if x := d.vs; x == 0 {
+ v = uint64(d.r.readn1())
+ } else if x == 1 {
+ d.r.readb(d.b[6:8])
+ v = uint64(bigen.Uint16(d.b[6:8]))
+ } else if x == 2 {
+ d.r.readb(d.b[4:8])
+ v = uint64(bigen.Uint32(d.b[4:8]))
+ } else {
+ d.r.readb(d.b[:8])
+ v = bigen.Uint64(d.b[:8])
+ }
+ return
+}
+
+func (d *bincDecDriver) decStringAndBytes(bs []byte, withString, zerocopy bool) (bs2 []byte, s string) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == bincVdSpecial<<4|bincSpNil {
+ d.bdRead = false
+ return
+ }
+ var slen int = -1
+ // var ok bool
+ switch d.vd {
+ case bincVdString, bincVdByteArray:
+ slen = d.decLen()
+ if zerocopy {
+ if d.br {
+ bs2 = d.r.readx(slen)
+ } else if len(bs) == 0 {
+ bs2 = decByteSlice(d.r, slen, d.b[:])
+ } else {
+ bs2 = decByteSlice(d.r, slen, bs)
+ }
+ } else {
+ bs2 = decByteSlice(d.r, slen, bs)
+ }
+ if withString {
+ s = string(bs2)
+ }
+ case bincVdSymbol:
+ // zerocopy doesn't apply for symbols,
+ // as the values must be stored in a table for later use.
+ //
+ //from vs: extract numSymbolBytes, containsStringVal, strLenPrecision,
+ //extract symbol
+ //if containsStringVal, read it and put in map
+ //else look in map for string value
+ var symbol uint16
+ vs := d.vs
+ if vs&0x8 == 0 {
+ symbol = uint16(d.r.readn1())
+ } else {
+ symbol = uint16(bigen.Uint16(d.r.readx(2)))
+ }
+ if d.s == nil {
+ d.s = make([]bincDecSymbol, 0, 16)
+ }
+
+ if vs&0x4 == 0 {
+ for i := range d.s {
+ j := &d.s[i]
+ if j.i == symbol {
+ bs2 = j.b
+ if withString {
+ if j.s == "" && bs2 != nil {
+ j.s = string(bs2)
+ }
+ s = j.s
+ }
+ break
+ }
+ }
+ } else {
+ switch vs & 0x3 {
+ case 0:
+ slen = int(d.r.readn1())
+ case 1:
+ slen = int(bigen.Uint16(d.r.readx(2)))
+ case 2:
+ slen = int(bigen.Uint32(d.r.readx(4)))
+ case 3:
+ slen = int(bigen.Uint64(d.r.readx(8)))
+ }
+ // since using symbols, do not store any part of
+ // the parameter bs in the map, as it might be a shared buffer.
+ // bs2 = decByteSlice(d.r, slen, bs)
+ bs2 = decByteSlice(d.r, slen, nil)
+ if withString {
+ s = string(bs2)
+ }
+ d.s = append(d.s, bincDecSymbol{symbol, s, bs2})
+ }
+ default:
+ d.d.errorf("Invalid d.vd. Expecting string:0x%x, bytearray:0x%x or symbol: 0x%x. Got: 0x%x",
+ bincVdString, bincVdByteArray, bincVdSymbol, d.vd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) DecodeString() (s string) {
+ // DecodeBytes does not accomodate symbols, whose impl stores string version in map.
+ // Use decStringAndBytes directly.
+ // return string(d.DecodeBytes(d.b[:], true, true))
+ _, s = d.decStringAndBytes(d.b[:], true, true)
+ return
+}
+
+func (d *bincDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
+ if isstring {
+ bsOut, _ = d.decStringAndBytes(bs, false, zerocopy)
+ return
+ }
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == bincVdSpecial<<4|bincSpNil {
+ d.bdRead = false
+ return nil
+ }
+ var clen int
+ if d.vd == bincVdString || d.vd == bincVdByteArray {
+ clen = d.decLen()
+ } else {
+ d.d.errorf("Invalid d.vd for bytes. Expecting string:0x%x or bytearray:0x%x. Got: 0x%x",
+ bincVdString, bincVdByteArray, d.vd)
+ return
+ }
+ d.bdRead = false
+ if zerocopy {
+ if d.br {
+ return d.r.readx(clen)
+ } else if len(bs) == 0 {
+ bs = d.b[:]
+ }
+ }
+ return decByteSlice(d.r, clen, bs)
+}
+
+func (d *bincDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
+ if xtag > 0xff {
+ d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
+ return
+ }
+ realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
+ realxtag = uint64(realxtag1)
+ if ext == nil {
+ re := rv.(*RawExt)
+ re.Tag = realxtag
+ re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
+ } else {
+ ext.ReadExt(rv, xbs)
+ }
+ return
+}
+
+func (d *bincDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.vd == bincVdCustomExt {
+ l := d.decLen()
+ xtag = d.r.readn1()
+ if verifyTag && xtag != tag {
+ d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
+ return
+ }
+ xbs = d.r.readx(l)
+ } else if d.vd == bincVdByteArray {
+ xbs = d.DecodeBytes(nil, false, true)
+ } else {
+ d.d.errorf("Invalid d.vd for extensions (Expecting extensions or byte array). Got: 0x%x", d.vd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *bincDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+
+ switch d.vd {
+ case bincVdSpecial:
+ switch d.vs {
+ case bincSpNil:
+ vt = valueTypeNil
+ case bincSpFalse:
+ vt = valueTypeBool
+ v = false
+ case bincSpTrue:
+ vt = valueTypeBool
+ v = true
+ case bincSpNan:
+ vt = valueTypeFloat
+ v = math.NaN()
+ case bincSpPosInf:
+ vt = valueTypeFloat
+ v = math.Inf(1)
+ case bincSpNegInf:
+ vt = valueTypeFloat
+ v = math.Inf(-1)
+ case bincSpZeroFloat:
+ vt = valueTypeFloat
+ v = float64(0)
+ case bincSpZero:
+ vt = valueTypeUint
+ v = uint64(0) // int8(0)
+ case bincSpNegOne:
+ vt = valueTypeInt
+ v = int64(-1) // int8(-1)
+ default:
+ d.d.errorf("decodeNaked: Unrecognized special value 0x%x", d.vs)
+ return
+ }
+ case bincVdSmallInt:
+ vt = valueTypeUint
+ v = uint64(int8(d.vs)) + 1 // int8(d.vs) + 1
+ case bincVdPosInt:
+ vt = valueTypeUint
+ v = d.decUint()
+ case bincVdNegInt:
+ vt = valueTypeInt
+ v = -(int64(d.decUint()))
+ case bincVdFloat:
+ vt = valueTypeFloat
+ v = d.decFloat()
+ case bincVdSymbol:
+ vt = valueTypeSymbol
+ v = d.DecodeString()
+ case bincVdString:
+ vt = valueTypeString
+ v = d.DecodeString()
+ case bincVdByteArray:
+ vt = valueTypeBytes
+ v = d.DecodeBytes(nil, false, false)
+ case bincVdTimestamp:
+ vt = valueTypeTimestamp
+ tt, err := decodeTime(d.r.readx(int(d.vs)))
+ if err != nil {
+ panic(err)
+ }
+ v = tt
+ case bincVdCustomExt:
+ vt = valueTypeExt
+ l := d.decLen()
+ var re RawExt
+ re.Tag = uint64(d.r.readn1())
+ re.Data = d.r.readx(l)
+ v = &re
+ vt = valueTypeExt
+ case bincVdArray:
+ vt = valueTypeArray
+ decodeFurther = true
+ case bincVdMap:
+ vt = valueTypeMap
+ decodeFurther = true
+ default:
+ d.d.errorf("decodeNaked: Unrecognized d.vd: 0x%x", d.vd)
+ return
+ }
+
+ if !decodeFurther {
+ d.bdRead = false
+ }
+ if vt == valueTypeUint && d.h.SignedInteger {
+ d.bdType = valueTypeInt
+ v = int64(v.(uint64))
+ }
+ return
+}
+
+//------------------------------------
+
+//BincHandle is a Handle for the Binc Schema-Free Encoding Format
+//defined at https://github.com/ugorji/binc .
+//
+//BincHandle currently supports all Binc features with the following EXCEPTIONS:
+// - only integers up to 64 bits of precision are supported.
+// big integers are unsupported.
+// - Only IEEE 754 binary32 and binary64 floats are supported (ie Go float32 and float64 types).
+// extended precision and decimal IEEE 754 floats are unsupported.
+// - Only UTF-8 strings supported.
+// Unicode_Other Binc types (UTF16, UTF32) are currently unsupported.
+//
+//Note that these EXCEPTIONS are temporary and full support is possible and may happen soon.
+type BincHandle struct {
+ BasicHandle
+ binaryEncodingType
+}
+
+func (h *BincHandle) newEncDriver(e *Encoder) encDriver {
+ return &bincEncDriver{e: e, w: e.w}
+}
+
+func (h *BincHandle) newDecDriver(d *Decoder) decDriver {
+ return &bincDecDriver{d: d, r: d.r, h: h, br: d.bytes}
+}
+
+func (h *BincHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
+ return h.SetExt(rt, tag, &setExtWrapper{b: ext})
+}
+
+var _ decDriver = (*bincDecDriver)(nil)
+var _ encDriver = (*bincEncDriver)(nil)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go
new file mode 100644
index 000000000..8b6e13a89
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor.go
@@ -0,0 +1,577 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "math"
+ "reflect"
+)
+
+const (
+ cborMajorUint byte = iota
+ cborMajorNegInt
+ cborMajorBytes
+ cborMajorText
+ cborMajorArray
+ cborMajorMap
+ cborMajorTag
+ cborMajorOther
+)
+
+const (
+ cborBdFalse byte = 0xf4 + iota
+ cborBdTrue
+ cborBdNil
+ cborBdUndefined
+ cborBdExt
+ cborBdFloat16
+ cborBdFloat32
+ cborBdFloat64
+)
+
+const (
+ cborBdIndefiniteBytes byte = 0x5f
+ cborBdIndefiniteString = 0x7f
+ cborBdIndefiniteArray = 0x9f
+ cborBdIndefiniteMap = 0xbf
+ cborBdBreak = 0xff
+)
+
+const (
+ CborStreamBytes byte = 0x5f
+ CborStreamString = 0x7f
+ CborStreamArray = 0x9f
+ CborStreamMap = 0xbf
+ CborStreamBreak = 0xff
+)
+
+const (
+ cborBaseUint byte = 0x00
+ cborBaseNegInt = 0x20
+ cborBaseBytes = 0x40
+ cborBaseString = 0x60
+ cborBaseArray = 0x80
+ cborBaseMap = 0xa0
+ cborBaseTag = 0xc0
+ cborBaseSimple = 0xe0
+)
+
+// -------------------
+
+type cborEncDriver struct {
+ e *Encoder
+ w encWriter
+ h *CborHandle
+ noBuiltInTypes
+ encNoSeparator
+ x [8]byte
+}
+
+func (e *cborEncDriver) EncodeNil() {
+ e.w.writen1(cborBdNil)
+}
+
+func (e *cborEncDriver) EncodeBool(b bool) {
+ if b {
+ e.w.writen1(cborBdTrue)
+ } else {
+ e.w.writen1(cborBdFalse)
+ }
+}
+
+func (e *cborEncDriver) EncodeFloat32(f float32) {
+ e.w.writen1(cborBdFloat32)
+ bigenHelper{e.x[:4], e.w}.writeUint32(math.Float32bits(f))
+}
+
+func (e *cborEncDriver) EncodeFloat64(f float64) {
+ e.w.writen1(cborBdFloat64)
+ bigenHelper{e.x[:8], e.w}.writeUint64(math.Float64bits(f))
+}
+
+func (e *cborEncDriver) encUint(v uint64, bd byte) {
+ if v <= 0x17 {
+ e.w.writen1(byte(v) + bd)
+ } else if v <= math.MaxUint8 {
+ e.w.writen2(bd+0x18, uint8(v))
+ } else if v <= math.MaxUint16 {
+ e.w.writen1(bd + 0x19)
+ bigenHelper{e.x[:2], e.w}.writeUint16(uint16(v))
+ } else if v <= math.MaxUint32 {
+ e.w.writen1(bd + 0x1a)
+ bigenHelper{e.x[:4], e.w}.writeUint32(uint32(v))
+ } else { // if v <= math.MaxUint64 {
+ e.w.writen1(bd + 0x1b)
+ bigenHelper{e.x[:8], e.w}.writeUint64(v)
+ }
+}
+
+func (e *cborEncDriver) EncodeInt(v int64) {
+ if v < 0 {
+ e.encUint(uint64(-1-v), cborBaseNegInt)
+ } else {
+ e.encUint(uint64(v), cborBaseUint)
+ }
+}
+
+func (e *cborEncDriver) EncodeUint(v uint64) {
+ e.encUint(v, cborBaseUint)
+}
+
+func (e *cborEncDriver) encLen(bd byte, length int) {
+ e.encUint(uint64(length), bd)
+}
+
+func (e *cborEncDriver) EncodeExt(rv interface{}, xtag uint64, ext Ext, en *Encoder) {
+ e.encUint(uint64(xtag), cborBaseTag)
+ if v := ext.ConvertExt(rv); v == nil {
+ e.EncodeNil()
+ } else {
+ en.encode(v)
+ }
+}
+
+func (e *cborEncDriver) EncodeRawExt(re *RawExt, en *Encoder) {
+ e.encUint(uint64(re.Tag), cborBaseTag)
+ if re.Data != nil {
+ en.encode(re.Data)
+ } else if re.Value == nil {
+ e.EncodeNil()
+ } else {
+ en.encode(re.Value)
+ }
+}
+
+func (e *cborEncDriver) EncodeArrayStart(length int) {
+ e.encLen(cborBaseArray, length)
+}
+
+func (e *cborEncDriver) EncodeMapStart(length int) {
+ e.encLen(cborBaseMap, length)
+}
+
+func (e *cborEncDriver) EncodeString(c charEncoding, v string) {
+ e.encLen(cborBaseString, len(v))
+ e.w.writestr(v)
+}
+
+func (e *cborEncDriver) EncodeSymbol(v string) {
+ e.EncodeString(c_UTF8, v)
+}
+
+func (e *cborEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
+ if c == c_RAW {
+ e.encLen(cborBaseBytes, len(v))
+ } else {
+ e.encLen(cborBaseString, len(v))
+ }
+ e.w.writeb(v)
+}
+
+// ----------------------
+
+type cborDecDriver struct {
+ d *Decoder
+ h *CborHandle
+ r decReader
+ br bool // bytes reader
+ bdRead bool
+ bdType valueType
+ bd byte
+ b [scratchByteArrayLen]byte
+ noBuiltInTypes
+ decNoSeparator
+}
+
+func (d *cborDecDriver) readNextBd() {
+ d.bd = d.r.readn1()
+ d.bdRead = true
+ d.bdType = valueTypeUnset
+}
+
+func (d *cborDecDriver) IsContainerType(vt valueType) (bv bool) {
+ switch vt {
+ case valueTypeNil:
+ return d.bd == cborBdNil
+ case valueTypeBytes:
+ return d.bd == cborBdIndefiniteBytes || (d.bd >= cborBaseBytes && d.bd < cborBaseString)
+ case valueTypeString:
+ return d.bd == cborBdIndefiniteString || (d.bd >= cborBaseString && d.bd < cborBaseArray)
+ case valueTypeArray:
+ return d.bd == cborBdIndefiniteArray || (d.bd >= cborBaseArray && d.bd < cborBaseMap)
+ case valueTypeMap:
+ return d.bd == cborBdIndefiniteMap || (d.bd >= cborBaseMap && d.bd < cborBaseTag)
+ }
+ d.d.errorf("isContainerType: unsupported parameter: %v", vt)
+ return // "unreachable"
+}
+
+func (d *cborDecDriver) TryDecodeAsNil() bool {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ // treat Nil and Undefined as nil values
+ if d.bd == cborBdNil || d.bd == cborBdUndefined {
+ d.bdRead = false
+ return true
+ }
+ return false
+}
+
+func (d *cborDecDriver) CheckBreak() bool {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == cborBdBreak {
+ d.bdRead = false
+ return true
+ }
+ return false
+}
+
+func (d *cborDecDriver) decUint() (ui uint64) {
+ v := d.bd & 0x1f
+ if v <= 0x17 {
+ ui = uint64(v)
+ } else {
+ if v == 0x18 {
+ ui = uint64(d.r.readn1())
+ } else if v == 0x19 {
+ ui = uint64(bigen.Uint16(d.r.readx(2)))
+ } else if v == 0x1a {
+ ui = uint64(bigen.Uint32(d.r.readx(4)))
+ } else if v == 0x1b {
+ ui = uint64(bigen.Uint64(d.r.readx(8)))
+ } else {
+ d.d.errorf("decUint: Invalid descriptor: %v", d.bd)
+ return
+ }
+ }
+ return
+}
+
+func (d *cborDecDriver) decCheckInteger() (neg bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ major := d.bd >> 5
+ if major == cborMajorUint {
+ } else if major == cborMajorNegInt {
+ neg = true
+ } else {
+ d.d.errorf("invalid major: %v (bd: %v)", major, d.bd)
+ return
+ }
+ return
+}
+
+func (d *cborDecDriver) DecodeInt(bitsize uint8) (i int64) {
+ neg := d.decCheckInteger()
+ ui := d.decUint()
+ // check if this number can be converted to an int without overflow
+ var overflow bool
+ if neg {
+ if i, overflow = chkOvf.SignedInt(ui + 1); overflow {
+ d.d.errorf("cbor: overflow converting %v to signed integer", ui+1)
+ return
+ }
+ i = -i
+ } else {
+ if i, overflow = chkOvf.SignedInt(ui); overflow {
+ d.d.errorf("cbor: overflow converting %v to signed integer", ui)
+ return
+ }
+ }
+ if chkOvf.Int(i, bitsize) {
+ d.d.errorf("cbor: overflow integer: %v", i)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *cborDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
+ if d.decCheckInteger() {
+ d.d.errorf("Assigning negative signed value to unsigned type")
+ return
+ }
+ ui = d.decUint()
+ if chkOvf.Uint(ui, bitsize) {
+ d.d.errorf("cbor: overflow integer: %v", ui)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *cborDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if bd := d.bd; bd == cborBdFloat16 {
+ f = float64(math.Float32frombits(halfFloatToFloatBits(bigen.Uint16(d.r.readx(2)))))
+ } else if bd == cborBdFloat32 {
+ f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
+ } else if bd == cborBdFloat64 {
+ f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
+ } else if bd >= cborBaseUint && bd < cborBaseBytes {
+ f = float64(d.DecodeInt(64))
+ } else {
+ d.d.errorf("Float only valid from float16/32/64: Invalid descriptor: %v", bd)
+ return
+ }
+ if chkOverflow32 && chkOvf.Float32(f) {
+ d.d.errorf("cbor: float32 overflow: %v", f)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+// bool can be decoded from bool only (single byte).
+func (d *cborDecDriver) DecodeBool() (b bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if bd := d.bd; bd == cborBdTrue {
+ b = true
+ } else if bd == cborBdFalse {
+ } else {
+ d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *cborDecDriver) ReadMapStart() (length int) {
+ d.bdRead = false
+ if d.bd == cborBdIndefiniteMap {
+ return -1
+ }
+ return d.decLen()
+}
+
+func (d *cborDecDriver) ReadArrayStart() (length int) {
+ d.bdRead = false
+ if d.bd == cborBdIndefiniteArray {
+ return -1
+ }
+ return d.decLen()
+}
+
+func (d *cborDecDriver) decLen() int {
+ return int(d.decUint())
+}
+
+func (d *cborDecDriver) decAppendIndefiniteBytes(bs []byte) []byte {
+ d.bdRead = false
+ for {
+ if d.CheckBreak() {
+ break
+ }
+ if major := d.bd >> 5; major != cborMajorBytes && major != cborMajorText {
+ d.d.errorf("cbor: expect bytes or string major type in indefinite string/bytes; got: %v, byte: %v", major, d.bd)
+ return nil
+ }
+ n := d.decLen()
+ oldLen := len(bs)
+ newLen := oldLen + n
+ if newLen > cap(bs) {
+ bs2 := make([]byte, newLen, 2*cap(bs)+n)
+ copy(bs2, bs)
+ bs = bs2
+ } else {
+ bs = bs[:newLen]
+ }
+ d.r.readb(bs[oldLen:newLen])
+ // bs = append(bs, d.r.readn()...)
+ d.bdRead = false
+ }
+ d.bdRead = false
+ return bs
+}
+
+func (d *cborDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == cborBdNil || d.bd == cborBdUndefined {
+ d.bdRead = false
+ return nil
+ }
+ if d.bd == cborBdIndefiniteBytes || d.bd == cborBdIndefiniteString {
+ if bs == nil {
+ return d.decAppendIndefiniteBytes(nil)
+ }
+ return d.decAppendIndefiniteBytes(bs[:0])
+ }
+ clen := d.decLen()
+ d.bdRead = false
+ if zerocopy {
+ if d.br {
+ return d.r.readx(clen)
+ } else if len(bs) == 0 {
+ bs = d.b[:]
+ }
+ }
+ return decByteSlice(d.r, clen, bs)
+}
+
+func (d *cborDecDriver) DecodeString() (s string) {
+ return string(d.DecodeBytes(d.b[:], true, true))
+}
+
+func (d *cborDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ u := d.decUint()
+ d.bdRead = false
+ realxtag = u
+ if ext == nil {
+ re := rv.(*RawExt)
+ re.Tag = realxtag
+ d.d.decode(&re.Value)
+ } else if xtag != realxtag {
+ d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", realxtag, xtag)
+ return
+ } else {
+ var v interface{}
+ d.d.decode(&v)
+ ext.UpdateExt(rv, v)
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *cborDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+
+ switch d.bd {
+ case cborBdNil:
+ vt = valueTypeNil
+ case cborBdFalse:
+ vt = valueTypeBool
+ v = false
+ case cborBdTrue:
+ vt = valueTypeBool
+ v = true
+ case cborBdFloat16, cborBdFloat32:
+ vt = valueTypeFloat
+ v = d.DecodeFloat(true)
+ case cborBdFloat64:
+ vt = valueTypeFloat
+ v = d.DecodeFloat(false)
+ case cborBdIndefiniteBytes:
+ vt = valueTypeBytes
+ v = d.DecodeBytes(nil, false, false)
+ case cborBdIndefiniteString:
+ vt = valueTypeString
+ v = d.DecodeString()
+ case cborBdIndefiniteArray:
+ vt = valueTypeArray
+ decodeFurther = true
+ case cborBdIndefiniteMap:
+ vt = valueTypeMap
+ decodeFurther = true
+ default:
+ switch {
+ case d.bd >= cborBaseUint && d.bd < cborBaseNegInt:
+ if d.h.SignedInteger {
+ vt = valueTypeInt
+ v = d.DecodeInt(64)
+ } else {
+ vt = valueTypeUint
+ v = d.DecodeUint(64)
+ }
+ case d.bd >= cborBaseNegInt && d.bd < cborBaseBytes:
+ vt = valueTypeInt
+ v = d.DecodeInt(64)
+ case d.bd >= cborBaseBytes && d.bd < cborBaseString:
+ vt = valueTypeBytes
+ v = d.DecodeBytes(nil, false, false)
+ case d.bd >= cborBaseString && d.bd < cborBaseArray:
+ vt = valueTypeString
+ v = d.DecodeString()
+ case d.bd >= cborBaseArray && d.bd < cborBaseMap:
+ vt = valueTypeArray
+ decodeFurther = true
+ case d.bd >= cborBaseMap && d.bd < cborBaseTag:
+ vt = valueTypeMap
+ decodeFurther = true
+ case d.bd >= cborBaseTag && d.bd < cborBaseSimple:
+ vt = valueTypeExt
+ var re RawExt
+ ui := d.decUint()
+ d.bdRead = false
+ re.Tag = ui
+ d.d.decode(&re.Value)
+ v = &re
+ // decodeFurther = true
+ default:
+ d.d.errorf("decodeNaked: Unrecognized d.bd: 0x%x", d.bd)
+ return
+ }
+ }
+
+ if !decodeFurther {
+ d.bdRead = false
+ }
+ return
+}
+
+// -------------------------
+
+// CborHandle is a Handle for the CBOR encoding format,
+// defined at http://tools.ietf.org/html/rfc7049 and documented further at http://cbor.io .
+//
+// CBOR is comprehensively supported, including support for:
+// - indefinite-length arrays/maps/bytes/strings
+// - (extension) tags in range 0..0xffff (0 .. 65535)
+// - half, single and double-precision floats
+// - all numbers (1, 2, 4 and 8-byte signed and unsigned integers)
+// - nil, true, false, ...
+// - arrays and maps, bytes and text strings
+//
+// None of the optional extensions (with tags) defined in the spec are supported out-of-the-box.
+// Users can implement them as needed (using SetExt), including spec-documented ones:
+// - timestamp, BigNum, BigFloat, Decimals, Encoded Text (e.g. URL, regexp, base64, MIME Message), etc.
+//
+// To encode with indefinite lengths (streaming), users will use
+// (Must)Encode methods of *Encoder, along with writing CborStreamXXX constants.
+//
+// For example, to encode "one-byte" as an indefinite length string:
+// var buf bytes.Buffer
+// e := NewEncoder(&buf, new(CborHandle))
+// buf.WriteByte(CborStreamString)
+// e.MustEncode("one-")
+// e.MustEncode("byte")
+// buf.WriteByte(CborStreamBreak)
+// encodedBytes := buf.Bytes()
+// var vv interface{}
+// NewDecoderBytes(buf.Bytes(), new(CborHandle)).MustDecode(&vv)
+// // Now, vv contains the same string "one-byte"
+//
+type CborHandle struct {
+ BasicHandle
+ binaryEncodingType
+}
+
+func (h *CborHandle) newEncDriver(e *Encoder) encDriver {
+ return &cborEncDriver{e: e, w: e.w, h: h}
+}
+
+func (h *CborHandle) newDecDriver(d *Decoder) decDriver {
+ return &cborDecDriver{d: d, r: d.r, h: h, br: d.bytes}
+}
+
+func (h *CborHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
+ return h.SetExt(rt, tag, &setExtWrapper{i: ext})
+}
+
+var _ decDriver = (*cborDecDriver)(nil)
+var _ encDriver = (*cborEncDriver)(nil)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go
new file mode 100644
index 000000000..205dffa7d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/cbor_test.go
@@ -0,0 +1,205 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/hex"
+ "math"
+ "os"
+ "regexp"
+ "strings"
+ "testing"
+)
+
+func TestCborIndefiniteLength(t *testing.T) {
+ oldMapType := testCborH.MapType
+ defer func() {
+ testCborH.MapType = oldMapType
+ }()
+ testCborH.MapType = testMapStrIntfTyp
+ // var (
+ // M1 map[string][]byte
+ // M2 map[uint64]bool
+ // L1 []interface{}
+ // S1 []string
+ // B1 []byte
+ // )
+ var v, vv interface{}
+ // define it (v), encode it using indefinite lengths, decode it (vv), compare v to vv
+ v = map[string]interface{}{
+ "one-byte-key": []byte{1, 2, 3, 4, 5, 6},
+ "two-string-key": "two-value",
+ "three-list-key": []interface{}{true, false, uint64(1), int64(-1)},
+ }
+ var buf bytes.Buffer
+ // buf.Reset()
+ e := NewEncoder(&buf, testCborH)
+ buf.WriteByte(cborBdIndefiniteMap)
+ //----
+ buf.WriteByte(cborBdIndefiniteString)
+ e.MustEncode("one-")
+ e.MustEncode("byte-")
+ e.MustEncode("key")
+ buf.WriteByte(cborBdBreak)
+
+ buf.WriteByte(cborBdIndefiniteBytes)
+ e.MustEncode([]byte{1, 2, 3})
+ e.MustEncode([]byte{4, 5, 6})
+ buf.WriteByte(cborBdBreak)
+
+ //----
+ buf.WriteByte(cborBdIndefiniteString)
+ e.MustEncode("two-")
+ e.MustEncode("string-")
+ e.MustEncode("key")
+ buf.WriteByte(cborBdBreak)
+
+ buf.WriteByte(cborBdIndefiniteString)
+ e.MustEncode([]byte("two-")) // encode as bytes, to check robustness of code
+ e.MustEncode([]byte("value"))
+ buf.WriteByte(cborBdBreak)
+
+ //----
+ buf.WriteByte(cborBdIndefiniteString)
+ e.MustEncode("three-")
+ e.MustEncode("list-")
+ e.MustEncode("key")
+ buf.WriteByte(cborBdBreak)
+
+ buf.WriteByte(cborBdIndefiniteArray)
+ e.MustEncode(true)
+ e.MustEncode(false)
+ e.MustEncode(uint64(1))
+ e.MustEncode(int64(-1))
+ buf.WriteByte(cborBdBreak)
+
+ buf.WriteByte(cborBdBreak) // close map
+
+ NewDecoderBytes(buf.Bytes(), testCborH).MustDecode(&vv)
+ if err := deepEqual(v, vv); err != nil {
+ logT(t, "-------- Before and After marshal do not match: Error: %v", err)
+ logT(t, " ....... GOLDEN: (%T) %#v", v, v)
+ logT(t, " ....... DECODED: (%T) %#v", vv, vv)
+ failT(t)
+ }
+}
+
+type testCborGolden struct {
+ Base64 string `codec:"cbor"`
+ Hex string `codec:"hex"`
+ Roundtrip bool `codec:"roundtrip"`
+ Decoded interface{} `codec:"decoded"`
+ Diagnostic string `codec:"diagnostic"`
+ Skip bool `codec:"skip"`
+}
+
+// Some tests are skipped because they include numbers outside the range of int64/uint64
+func doTestCborGoldens(t *testing.T) {
+ oldMapType := testCborH.MapType
+ defer func() {
+ testCborH.MapType = oldMapType
+ }()
+ testCborH.MapType = testMapStrIntfTyp
+ // decode test-cbor-goldens.json into a list of []*testCborGolden
+ // for each one,
+ // - decode hex into []byte bs
+ // - decode bs into interface{} v
+ // - compare both using deepequal
+ // - for any miss, record it
+ var gs []*testCborGolden
+ f, err := os.Open("test-cbor-goldens.json")
+ if err != nil {
+ logT(t, "error opening test-cbor-goldens.json: %v", err)
+ failT(t)
+ }
+ defer f.Close()
+ jh := new(JsonHandle)
+ jh.MapType = testMapStrIntfTyp
+ // d := NewDecoder(f, jh)
+ d := NewDecoder(bufio.NewReader(f), jh)
+ // err = d.Decode(&gs)
+ d.MustDecode(&gs)
+ if err != nil {
+ logT(t, "error json decoding test-cbor-goldens.json: %v", err)
+ failT(t)
+ }
+
+ tagregex := regexp.MustCompile(`[\d]+\(.+?\)`)
+ hexregex := regexp.MustCompile(`h'([0-9a-fA-F]*)'`)
+ for i, g := range gs {
+ // fmt.Printf("%v, skip: %v, isTag: %v, %s\n", i, g.Skip, tagregex.MatchString(g.Diagnostic), g.Diagnostic)
+ // skip tags or simple or those with prefix, as we can't verify them.
+ if g.Skip || strings.HasPrefix(g.Diagnostic, "simple(") || tagregex.MatchString(g.Diagnostic) {
+ // fmt.Printf("%v: skipped\n", i)
+ logT(t, "[%v] skipping because skip=true OR unsupported simple value or Tag Value", i)
+ continue
+ }
+ // println("++++++++++++", i, "g.Diagnostic", g.Diagnostic)
+ if hexregex.MatchString(g.Diagnostic) {
+ // println(i, "g.Diagnostic matched hex")
+ if s2 := g.Diagnostic[2 : len(g.Diagnostic)-1]; s2 == "" {
+ g.Decoded = zeroByteSlice
+ } else if bs2, err2 := hex.DecodeString(s2); err2 == nil {
+ g.Decoded = bs2
+ }
+ // fmt.Printf("%v: hex: %v\n", i, g.Decoded)
+ }
+ bs, err := hex.DecodeString(g.Hex)
+ if err != nil {
+ logT(t, "[%v] error hex decoding %s [%v]: %v", i, g.Hex, err)
+ failT(t)
+ }
+ var v interface{}
+ NewDecoderBytes(bs, testCborH).MustDecode(&v)
+ if _, ok := v.(RawExt); ok {
+ continue
+ }
+ // check the diagnostics to compare
+ switch g.Diagnostic {
+ case "Infinity":
+ b := math.IsInf(v.(float64), 1)
+ testCborError(t, i, math.Inf(1), v, nil, &b)
+ case "-Infinity":
+ b := math.IsInf(v.(float64), -1)
+ testCborError(t, i, math.Inf(-1), v, nil, &b)
+ case "NaN":
+ // println(i, "checking NaN")
+ b := math.IsNaN(v.(float64))
+ testCborError(t, i, math.NaN(), v, nil, &b)
+ case "undefined":
+ b := v == nil
+ testCborError(t, i, nil, v, nil, &b)
+ default:
+ v0 := g.Decoded
+ // testCborCoerceJsonNumber(reflect.ValueOf(&v0))
+ testCborError(t, i, v0, v, deepEqual(v0, v), nil)
+ }
+ }
+}
+
+func testCborError(t *testing.T, i int, v0, v1 interface{}, err error, equal *bool) {
+ if err == nil && equal == nil {
+ // fmt.Printf("%v testCborError passed (err and equal nil)\n", i)
+ return
+ }
+ if err != nil {
+ logT(t, "[%v] deepEqual error: %v", i, err)
+ logT(t, " ....... GOLDEN: (%T) %#v", v0, v0)
+ logT(t, " ....... DECODED: (%T) %#v", v1, v1)
+ failT(t)
+ }
+ if equal != nil && !*equal {
+ logT(t, "[%v] values not equal", i)
+ logT(t, " ....... GOLDEN: (%T) %#v", v0, v0)
+ logT(t, " ....... DECODED: (%T) %#v", v1, v1)
+ failT(t)
+ }
+ // fmt.Printf("%v testCborError passed (checks passed)\n", i)
+}
+
+func TestCborGoldens(t *testing.T) {
+ doTestCborGoldens(t)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go
new file mode 100644
index 000000000..d9583a9b1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codec_test.go
@@ -0,0 +1,1175 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// Test works by using a slice of interfaces.
+// It can test for encoding/decoding into/from a nil interface{}
+// or passing the object to encode/decode into.
+//
+// There are basically 2 main tests here.
+// First test internally encodes and decodes things and verifies that
+// the artifact was as expected.
+// Second test will use python msgpack to create a bunch of golden files,
+// read those files, and compare them to what it should be. It then
+// writes those files back out and compares the byte streams.
+//
+// Taken together, the tests are pretty extensive.
+//
+// The following manual tests must be done:
+// - TestCodecUnderlyingType
+// - Set fastpathEnabled to false and run tests (to ensure that regular reflection works).
+// We don't want to use a variable there so that code is ellided.
+
+import (
+ "bytes"
+ "encoding/gob"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "math"
+ "net"
+ "net/rpc"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "strconv"
+ "sync/atomic"
+ "testing"
+ "time"
+)
+
+func init() {
+ testInitFlags()
+ testPreInitFns = append(testPreInitFns, testInit)
+}
+
+type testVerifyArg int
+
+const (
+ testVerifyMapTypeSame testVerifyArg = iota
+ testVerifyMapTypeStrIntf
+ testVerifyMapTypeIntfIntf
+ // testVerifySliceIntf
+ testVerifyForPython
+)
+
+const testSkipRPCTests = false
+
+var (
+ testVerbose bool
+ testInitDebug bool
+ testUseIoEncDec bool
+ testStructToArray bool
+ testCanonical bool
+ testWriteNoSymbols bool
+ testSkipIntf bool
+
+ skipVerifyVal interface{} = &(struct{}{})
+
+ testMapStrIntfTyp = reflect.TypeOf(map[string]interface{}(nil))
+
+ // For Go Time, do not use a descriptive timezone.
+ // It's unnecessary, and makes it harder to do a reflect.DeepEqual.
+ // The Offset already tells what the offset should be, if not on UTC and unknown zone name.
+ timeLoc = time.FixedZone("", -8*60*60) // UTC-08:00 //time.UTC-8
+ timeToCompare1 = time.Date(2012, 2, 2, 2, 2, 2, 2000, timeLoc).UTC()
+ timeToCompare2 = time.Date(1900, 2, 2, 2, 2, 2, 2000, timeLoc).UTC()
+ timeToCompare3 = time.Unix(0, 270).UTC() // use value that must be encoded as uint64 for nanoseconds (for cbor/msgpack comparison)
+ //timeToCompare4 = time.Time{}.UTC() // does not work well with simple cbor time encoding (overflow)
+ timeToCompare4 = time.Unix(-2013855848, 4223).UTC()
+
+ table []interface{} // main items we encode
+ tableVerify []interface{} // we verify encoded things against this after decode
+ tableTestNilVerify []interface{} // for nil interface, use this to verify (rules are different)
+ tablePythonVerify []interface{} // for verifying for python, since Python sometimes
+ // will encode a float32 as float64, or large int as uint
+ testRpcInt = new(TestRpcInt)
+)
+
+func testInitFlags() {
+ // delete(testDecOpts.ExtFuncs, timeTyp)
+ flag.BoolVar(&testVerbose, "tv", false, "Test Verbose")
+ flag.BoolVar(&testInitDebug, "tg", false, "Test Init Debug")
+ flag.BoolVar(&testUseIoEncDec, "ti", false, "Use IO Reader/Writer for Marshal/Unmarshal")
+ flag.BoolVar(&testStructToArray, "ts", false, "Set StructToArray option")
+ flag.BoolVar(&testWriteNoSymbols, "tn", false, "Set NoSymbols option")
+ flag.BoolVar(&testCanonical, "tc", false, "Set Canonical option")
+ flag.BoolVar(&testSkipIntf, "tf", false, "Skip Interfaces")
+}
+
+type TestABC struct {
+ A, B, C string
+}
+
+type TestRpcInt struct {
+ i int
+}
+
+func (r *TestRpcInt) Update(n int, res *int) error { r.i = n; *res = r.i; return nil }
+func (r *TestRpcInt) Square(ignore int, res *int) error { *res = r.i * r.i; return nil }
+func (r *TestRpcInt) Mult(n int, res *int) error { *res = r.i * n; return nil }
+func (r *TestRpcInt) EchoStruct(arg TestABC, res *string) error {
+ *res = fmt.Sprintf("%#v", arg)
+ return nil
+}
+func (r *TestRpcInt) Echo123(args []string, res *string) error {
+ *res = fmt.Sprintf("%#v", args)
+ return nil
+}
+
+type testUnixNanoTimeExt struct{}
+
+func (x testUnixNanoTimeExt) WriteExt(interface{}) []byte { panic("unsupported") }
+func (x testUnixNanoTimeExt) ReadExt(interface{}, []byte) { panic("unsupported") }
+func (x testUnixNanoTimeExt) ConvertExt(v interface{}) interface{} {
+ switch v2 := v.(type) {
+ case time.Time:
+ return v2.UTC().UnixNano()
+ case *time.Time:
+ return v2.UTC().UnixNano()
+ default:
+ panic(fmt.Sprintf("unsupported format for time conversion: expecting time.Time; got %T", v))
+ }
+}
+func (x testUnixNanoTimeExt) UpdateExt(dest interface{}, v interface{}) {
+ // fmt.Printf("testUnixNanoTimeExt.UpdateExt: v: %v\n", v)
+ tt := dest.(*time.Time)
+ switch v2 := v.(type) {
+ case int64:
+ *tt = time.Unix(0, v2).UTC()
+ case uint64:
+ *tt = time.Unix(0, int64(v2)).UTC()
+ //case float64:
+ //case string:
+ default:
+ panic(fmt.Sprintf("unsupported format for time conversion: expecting int64/uint64; got %T", v))
+ }
+ // fmt.Printf("testUnixNanoTimeExt.UpdateExt: v: %v, tt: %#v\n", v, tt)
+}
+
+func testVerifyVal(v interface{}, arg testVerifyArg) (v2 interface{}) {
+ //for python msgpack,
+ // - all positive integers are unsigned 64-bit ints
+ // - all floats are float64
+ switch iv := v.(type) {
+ case int8:
+ if iv >= 0 {
+ v2 = uint64(iv)
+ } else {
+ v2 = int64(iv)
+ }
+ case int16:
+ if iv >= 0 {
+ v2 = uint64(iv)
+ } else {
+ v2 = int64(iv)
+ }
+ case int32:
+ if iv >= 0 {
+ v2 = uint64(iv)
+ } else {
+ v2 = int64(iv)
+ }
+ case int64:
+ if iv >= 0 {
+ v2 = uint64(iv)
+ } else {
+ v2 = int64(iv)
+ }
+ case uint8:
+ v2 = uint64(iv)
+ case uint16:
+ v2 = uint64(iv)
+ case uint32:
+ v2 = uint64(iv)
+ case uint64:
+ v2 = uint64(iv)
+ case float32:
+ v2 = float64(iv)
+ case float64:
+ v2 = float64(iv)
+ case []interface{}:
+ m2 := make([]interface{}, len(iv))
+ for j, vj := range iv {
+ m2[j] = testVerifyVal(vj, arg)
+ }
+ v2 = m2
+ case map[string]bool:
+ switch arg {
+ case testVerifyMapTypeSame:
+ m2 := make(map[string]bool)
+ for kj, kv := range iv {
+ m2[kj] = kv
+ }
+ v2 = m2
+ case testVerifyMapTypeStrIntf, testVerifyForPython:
+ m2 := make(map[string]interface{})
+ for kj, kv := range iv {
+ m2[kj] = kv
+ }
+ v2 = m2
+ case testVerifyMapTypeIntfIntf:
+ m2 := make(map[interface{}]interface{})
+ for kj, kv := range iv {
+ m2[kj] = kv
+ }
+ v2 = m2
+ }
+ case map[string]interface{}:
+ switch arg {
+ case testVerifyMapTypeSame:
+ m2 := make(map[string]interface{})
+ for kj, kv := range iv {
+ m2[kj] = testVerifyVal(kv, arg)
+ }
+ v2 = m2
+ case testVerifyMapTypeStrIntf, testVerifyForPython:
+ m2 := make(map[string]interface{})
+ for kj, kv := range iv {
+ m2[kj] = testVerifyVal(kv, arg)
+ }
+ v2 = m2
+ case testVerifyMapTypeIntfIntf:
+ m2 := make(map[interface{}]interface{})
+ for kj, kv := range iv {
+ m2[kj] = testVerifyVal(kv, arg)
+ }
+ v2 = m2
+ }
+ case map[interface{}]interface{}:
+ m2 := make(map[interface{}]interface{})
+ for kj, kv := range iv {
+ m2[testVerifyVal(kj, arg)] = testVerifyVal(kv, arg)
+ }
+ v2 = m2
+ case time.Time:
+ switch arg {
+ case testVerifyForPython:
+ if iv2 := iv.UnixNano(); iv2 >= 0 {
+ v2 = uint64(iv2)
+ } else {
+ v2 = int64(iv2)
+ }
+ default:
+ v2 = v
+ }
+ default:
+ v2 = v
+ }
+ return
+}
+
+func testInit() {
+ gob.Register(new(TestStruc))
+ if testInitDebug {
+ ts0 := newTestStruc(2, false, !testSkipIntf, false)
+ fmt.Printf("====> depth: %v, ts: %#v\n", 2, ts0)
+ }
+
+ testJsonH.Canonical = testCanonical
+ testCborH.Canonical = testCanonical
+ testSimpleH.Canonical = testCanonical
+ testBincH.Canonical = testCanonical
+ testMsgpackH.Canonical = testCanonical
+
+ testJsonH.StructToArray = testStructToArray
+ testCborH.StructToArray = testStructToArray
+ testSimpleH.StructToArray = testStructToArray
+ testBincH.StructToArray = testStructToArray
+ testMsgpackH.StructToArray = testStructToArray
+
+ testMsgpackH.RawToString = true
+
+ if testWriteNoSymbols {
+ testBincH.AsSymbols = AsSymbolNone
+ } else {
+ testBincH.AsSymbols = AsSymbolAll
+ }
+
+ // testMsgpackH.AddExt(byteSliceTyp, 0, testMsgpackH.BinaryEncodeExt, testMsgpackH.BinaryDecodeExt)
+ // testMsgpackH.AddExt(timeTyp, 1, testMsgpackH.TimeEncodeExt, testMsgpackH.TimeDecodeExt)
+ timeEncExt := func(rv reflect.Value) (bs []byte, err error) {
+ switch v2 := rv.Interface().(type) {
+ case time.Time:
+ bs = encodeTime(v2)
+ case *time.Time:
+ bs = encodeTime(*v2)
+ default:
+ err = fmt.Errorf("unsupported format for time conversion: expecting time.Time; got %T", v2)
+ }
+ return
+ }
+ timeDecExt := func(rv reflect.Value, bs []byte) (err error) {
+ tt, err := decodeTime(bs)
+ if err == nil {
+ *(rv.Interface().(*time.Time)) = tt
+ }
+ return
+ }
+
+ // add extensions for msgpack, simple for time.Time, so we can encode/decode same way.
+ testMsgpackH.AddExt(timeTyp, 1, timeEncExt, timeDecExt)
+ testSimpleH.AddExt(timeTyp, 1, timeEncExt, timeDecExt)
+ testCborH.SetExt(timeTyp, 1, &testUnixNanoTimeExt{})
+ testJsonH.SetExt(timeTyp, 1, &testUnixNanoTimeExt{})
+
+ primitives := []interface{}{
+ int8(-8),
+ int16(-1616),
+ int32(-32323232),
+ int64(-6464646464646464),
+ uint8(192),
+ uint16(1616),
+ uint32(32323232),
+ uint64(6464646464646464),
+ byte(192),
+ float32(-3232.0),
+ float64(-6464646464.0),
+ float32(3232.0),
+ float64(6464646464.0),
+ false,
+ true,
+ nil,
+ "someday",
+ "",
+ "bytestring",
+ timeToCompare1,
+ timeToCompare2,
+ timeToCompare3,
+ timeToCompare4,
+ }
+ mapsAndStrucs := []interface{}{
+ map[string]bool{
+ "true": true,
+ "false": false,
+ },
+ map[string]interface{}{
+ "true": "True",
+ "false": false,
+ "uint16(1616)": uint16(1616),
+ },
+ //add a complex combo map in here. (map has list which has map)
+ //note that after the first thing, everything else should be generic.
+ map[string]interface{}{
+ "list": []interface{}{
+ int16(1616),
+ int32(32323232),
+ true,
+ float32(-3232.0),
+ map[string]interface{}{
+ "TRUE": true,
+ "FALSE": false,
+ },
+ []interface{}{true, false},
+ },
+ "int32": int32(32323232),
+ "bool": true,
+ "LONG STRING": "123456789012345678901234567890123456789012345678901234567890",
+ "SHORT STRING": "1234567890",
+ },
+ map[interface{}]interface{}{
+ true: "true",
+ uint8(138): false,
+ "false": uint8(200),
+ },
+ newTestStruc(0, false, !testSkipIntf, false),
+ }
+
+ table = []interface{}{}
+ table = append(table, primitives...) //0-19 are primitives
+ table = append(table, primitives) //20 is a list of primitives
+ table = append(table, mapsAndStrucs...) //21-24 are maps. 25 is a *struct
+
+ tableVerify = make([]interface{}, len(table))
+ tableTestNilVerify = make([]interface{}, len(table))
+ tablePythonVerify = make([]interface{}, len(table))
+
+ lp := len(primitives)
+ av := tableVerify
+ for i, v := range table {
+ if i == lp+3 {
+ av[i] = skipVerifyVal
+ continue
+ }
+ //av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+ switch v.(type) {
+ case []interface{}:
+ av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+ case map[string]interface{}:
+ av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+ case map[interface{}]interface{}:
+ av[i] = testVerifyVal(v, testVerifyMapTypeSame)
+ default:
+ av[i] = v
+ }
+ }
+
+ av = tableTestNilVerify
+ for i, v := range table {
+ if i > lp+3 {
+ av[i] = skipVerifyVal
+ continue
+ }
+ av[i] = testVerifyVal(v, testVerifyMapTypeStrIntf)
+ }
+
+ av = tablePythonVerify
+ for i, v := range table {
+ if i > lp+3 {
+ av[i] = skipVerifyVal
+ continue
+ }
+ av[i] = testVerifyVal(v, testVerifyForPython)
+ }
+
+ tablePythonVerify = tablePythonVerify[:24]
+}
+
+func testUnmarshal(v interface{}, data []byte, h Handle) (err error) {
+ if testUseIoEncDec {
+ NewDecoder(bytes.NewBuffer(data), h).MustDecode(v)
+ } else {
+ NewDecoderBytes(data, h).MustDecode(v)
+ }
+ return
+}
+
+func testMarshal(v interface{}, h Handle) (bs []byte, err error) {
+ if testUseIoEncDec {
+ var buf bytes.Buffer
+ NewEncoder(&buf, h).MustEncode(v)
+ bs = buf.Bytes()
+ return
+ }
+ NewEncoderBytes(&bs, h).MustEncode(v)
+ return
+}
+
+func testMarshalErr(v interface{}, h Handle, t *testing.T, name string) (bs []byte, err error) {
+ if bs, err = testMarshal(v, h); err != nil {
+ logT(t, "Error encoding %s: %v, Err: %v", name, v, err)
+ t.FailNow()
+ }
+ return
+}
+
+func testUnmarshalErr(v interface{}, data []byte, h Handle, t *testing.T, name string) (err error) {
+ if err = testUnmarshal(v, data, h); err != nil {
+ logT(t, "Error Decoding into %s: %v, Err: %v", name, v, err)
+ t.FailNow()
+ }
+ return
+}
+
+// doTestCodecTableOne allows us test for different variations based on arguments passed.
+func doTestCodecTableOne(t *testing.T, testNil bool, h Handle,
+ vs []interface{}, vsVerify []interface{}) {
+ //if testNil, then just test for when a pointer to a nil interface{} is passed. It should work.
+ //Current setup allows us test (at least manually) the nil interface or typed interface.
+ logT(t, "================ TestNil: %v ================\n", testNil)
+ for i, v0 := range vs {
+ logT(t, "..............................................")
+ logT(t, " Testing: #%d:, %T, %#v\n", i, v0, v0)
+ b0, err := testMarshalErr(v0, h, t, "v0")
+ if err != nil {
+ continue
+ }
+ if h.isBinary() {
+ logT(t, " Encoded bytes: len: %v, %v\n", len(b0), b0)
+ } else {
+ logT(t, " Encoded string: len: %v, %v\n", len(string(b0)), string(b0))
+ // println("########### encoded string: " + string(b0))
+ }
+ var v1 interface{}
+
+ if testNil {
+ err = testUnmarshal(&v1, b0, h)
+ } else {
+ if v0 != nil {
+ v0rt := reflect.TypeOf(v0) // ptr
+ rv1 := reflect.New(v0rt)
+ err = testUnmarshal(rv1.Interface(), b0, h)
+ v1 = rv1.Elem().Interface()
+ // v1 = reflect.Indirect(reflect.ValueOf(v1)).Interface()
+ }
+ }
+
+ logT(t, " v1 returned: %T, %#v", v1, v1)
+ // if v1 != nil {
+ // logT(t, " v1 returned: %T, %#v", v1, v1)
+ // //we always indirect, because ptr to typed value may be passed (if not testNil)
+ // v1 = reflect.Indirect(reflect.ValueOf(v1)).Interface()
+ // }
+ if err != nil {
+ logT(t, "-------- Error: %v. Partial return: %v", err, v1)
+ failT(t)
+ continue
+ }
+ v0check := vsVerify[i]
+ if v0check == skipVerifyVal {
+ logT(t, " Nil Check skipped: Decoded: %T, %#v\n", v1, v1)
+ continue
+ }
+
+ if err = deepEqual(v0check, v1); err == nil {
+ logT(t, "++++++++ Before and After marshal matched\n")
+ } else {
+ // logT(t, "-------- Before and After marshal do not match: Error: %v"+
+ // " ====> GOLDEN: (%T) %#v, DECODED: (%T) %#v\n", err, v0check, v0check, v1, v1)
+ logT(t, "-------- Before and After marshal do not match: Error: %v", err)
+ logT(t, " ....... GOLDEN: (%T) %#v", v0check, v0check)
+ logT(t, " ....... DECODED: (%T) %#v", v1, v1)
+ failT(t)
+ }
+ }
+}
+
+func testCodecTableOne(t *testing.T, h Handle) {
+ testOnce.Do(testInitAll)
+ // func TestMsgpackAllExperimental(t *testing.T) {
+ // dopts := testDecOpts(nil, nil, false, true, true),
+
+ idxTime, numPrim, numMap := 19, 23, 4
+ //println("#################")
+ switch v := h.(type) {
+ case *MsgpackHandle:
+ var oldWriteExt, oldRawToString bool
+ oldWriteExt, v.WriteExt = v.WriteExt, true
+ oldRawToString, v.RawToString = v.RawToString, true
+ doTestCodecTableOne(t, false, h, table, tableVerify)
+ v.WriteExt, v.RawToString = oldWriteExt, oldRawToString
+ case *JsonHandle:
+ //skip []interface{} containing time.Time, as it encodes as a number, but cannot decode back to time.Time.
+ //As there is no real support for extension tags in json, this must be skipped.
+ doTestCodecTableOne(t, false, h, table[:numPrim], tableVerify[:numPrim])
+ doTestCodecTableOne(t, false, h, table[numPrim+1:], tableVerify[numPrim+1:])
+ default:
+ doTestCodecTableOne(t, false, h, table, tableVerify)
+ }
+ // func TestMsgpackAll(t *testing.T) {
+
+ // //skip []interface{} containing time.Time
+ // doTestCodecTableOne(t, false, h, table[:numPrim], tableVerify[:numPrim])
+ // doTestCodecTableOne(t, false, h, table[numPrim+1:], tableVerify[numPrim+1:])
+ // func TestMsgpackNilStringMap(t *testing.T) {
+ var oldMapType reflect.Type
+ v := h.getBasicHandle()
+
+ oldMapType, v.MapType = v.MapType, testMapStrIntfTyp
+
+ //skip time.Time, []interface{} containing time.Time, last map, and newStruc
+ doTestCodecTableOne(t, true, h, table[:idxTime], tableTestNilVerify[:idxTime])
+ doTestCodecTableOne(t, true, h, table[numPrim+1:numPrim+numMap], tableTestNilVerify[numPrim+1:numPrim+numMap])
+
+ v.MapType = oldMapType
+
+ // func TestMsgpackNilIntf(t *testing.T) {
+
+ //do newTestStruc and last element of map
+ doTestCodecTableOne(t, true, h, table[numPrim+numMap:], tableTestNilVerify[numPrim+numMap:])
+ //TODO? What is this one?
+ //doTestCodecTableOne(t, true, h, table[17:18], tableTestNilVerify[17:18])
+}
+
+func testCodecMiscOne(t *testing.T, h Handle) {
+ testOnce.Do(testInitAll)
+ b, err := testMarshalErr(32, h, t, "32")
+ // Cannot do this nil one, because faster type assertion decoding will panic
+ // var i *int32
+ // if err = testUnmarshal(b, i, nil); err == nil {
+ // logT(t, "------- Expecting error because we cannot unmarshal to int32 nil ptr")
+ // t.FailNow()
+ // }
+ var i2 int32 = 0
+ err = testUnmarshalErr(&i2, b, h, t, "int32-ptr")
+ if i2 != int32(32) {
+ logT(t, "------- didn't unmarshal to 32: Received: %d", i2)
+ t.FailNow()
+ }
+
+ // func TestMsgpackDecodePtr(t *testing.T) {
+ ts := newTestStruc(0, false, !testSkipIntf, false)
+ b, err = testMarshalErr(ts, h, t, "pointer-to-struct")
+ if len(b) < 40 {
+ logT(t, "------- Size must be > 40. Size: %d", len(b))
+ t.FailNow()
+ }
+ if h.isBinary() {
+ logT(t, "------- b: %v", b)
+ } else {
+ logT(t, "------- b: %s", b)
+ }
+ ts2 := new(TestStruc)
+ err = testUnmarshalErr(ts2, b, h, t, "pointer-to-struct")
+ if ts2.I64 != math.MaxInt64*2/3 {
+ logT(t, "------- Unmarshal wrong. Expect I64 = 64. Got: %v", ts2.I64)
+ t.FailNow()
+ }
+
+ // func TestMsgpackIntfDecode(t *testing.T) {
+ m := map[string]int{"A": 2, "B": 3}
+ p := []interface{}{m}
+ bs, err := testMarshalErr(p, h, t, "p")
+
+ m2 := map[string]int{}
+ p2 := []interface{}{m2}
+ err = testUnmarshalErr(&p2, bs, h, t, "&p2")
+
+ if m2["A"] != 2 || m2["B"] != 3 {
+ logT(t, "m2 not as expected: expecting: %v, got: %v", m, m2)
+ t.FailNow()
+ }
+ // log("m: %v, m2: %v, p: %v, p2: %v", m, m2, p, p2)
+ checkEqualT(t, p, p2, "p=p2")
+ checkEqualT(t, m, m2, "m=m2")
+ if err = deepEqual(p, p2); err == nil {
+ logT(t, "p and p2 match")
+ } else {
+ logT(t, "Not Equal: %v. p: %v, p2: %v", err, p, p2)
+ t.FailNow()
+ }
+ if err = deepEqual(m, m2); err == nil {
+ logT(t, "m and m2 match")
+ } else {
+ logT(t, "Not Equal: %v. m: %v, m2: %v", err, m, m2)
+ t.FailNow()
+ }
+
+ // func TestMsgpackDecodeStructSubset(t *testing.T) {
+ // test that we can decode a subset of the stream
+ mm := map[string]interface{}{"A": 5, "B": 99, "C": 333}
+ bs, err = testMarshalErr(mm, h, t, "mm")
+ type ttt struct {
+ A uint8
+ C int32
+ }
+ var t2 ttt
+ testUnmarshalErr(&t2, bs, h, t, "t2")
+ t3 := ttt{5, 333}
+ checkEqualT(t, t2, t3, "t2=t3")
+
+ // println(">>>>>")
+ // test simple arrays, non-addressable arrays, slices
+ type tarr struct {
+ A int64
+ B [3]int64
+ C []byte
+ D [3]byte
+ }
+ var tarr0 = tarr{1, [3]int64{2, 3, 4}, []byte{4, 5, 6}, [3]byte{7, 8, 9}}
+ // test both pointer and non-pointer (value)
+ for _, tarr1 := range []interface{}{tarr0, &tarr0} {
+ bs, err = testMarshalErr(tarr1, h, t, "tarr1")
+ var tarr2 tarr
+ testUnmarshalErr(&tarr2, bs, h, t, "tarr2")
+ checkEqualT(t, tarr0, tarr2, "tarr0=tarr2")
+ // fmt.Printf(">>>> err: %v. tarr1: %v, tarr2: %v\n", err, tarr0, tarr2)
+ }
+
+ // test byte array, even if empty (msgpack only)
+ if h == testMsgpackH {
+ type ystruct struct {
+ Anarray []byte
+ }
+ var ya = ystruct{}
+ testUnmarshalErr(&ya, []byte{0x91, 0x90}, h, t, "ya")
+ }
+}
+
+func testCodecEmbeddedPointer(t *testing.T, h Handle) {
+ testOnce.Do(testInitAll)
+ type Z int
+ type A struct {
+ AnInt int
+ }
+ type B struct {
+ *Z
+ *A
+ MoreInt int
+ }
+ var z Z = 4
+ x1 := &B{&z, &A{5}, 6}
+ bs, err := testMarshalErr(x1, h, t, "x1")
+ // fmt.Printf("buf: len(%v): %x\n", buf.Len(), buf.Bytes())
+ var x2 = new(B)
+ err = testUnmarshalErr(x2, bs, h, t, "x2")
+ err = checkEqualT(t, x1, x2, "x1=x2")
+ _ = err
+}
+
+func testCodecUnderlyingType(t *testing.T, h Handle) {
+ testOnce.Do(testInitAll)
+ // Manual Test.
+ // Run by hand, with accompanying print statements in fast-path.go
+ // to ensure that the fast functions are called.
+ type T1 map[string]string
+ v := T1{"1": "1s", "2": "2s"}
+ var bs []byte
+ var err error
+ NewEncoderBytes(&bs, h).MustEncode(v)
+ if err != nil {
+ logT(t, "Error during encode: %v", err)
+ failT(t)
+ }
+ var v2 T1
+ NewDecoderBytes(bs, h).MustDecode(&v2)
+ if err != nil {
+ logT(t, "Error during decode: %v", err)
+ failT(t)
+ }
+}
+
+func testCodecChan(t *testing.T, h Handle) {
+ // - send a slice []*int64 (sl1) into an chan (ch1) with cap > len(s1)
+ // - encode ch1 as a stream array
+ // - decode a chan (ch2), with cap > len(s1) from the stream array
+ // - receive from ch2 into slice sl2
+ // - compare sl1 and sl2
+ // - do this for codecs: json, cbor (covers all types)
+ sl1 := make([]*int64, 4)
+ for i := range sl1 {
+ var j int64 = int64(i)
+ sl1[i] = &j
+ }
+ ch1 := make(chan *int64, 4)
+ for _, j := range sl1 {
+ ch1 <- j
+ }
+ var bs []byte
+ NewEncoderBytes(&bs, h).MustEncode(ch1)
+ // if !h.isBinary() {
+ // fmt.Printf("before: len(ch1): %v, bs: %s\n", len(ch1), bs)
+ // }
+ // var ch2 chan *int64 // this will block if json, etc.
+ ch2 := make(chan *int64, 8)
+ NewDecoderBytes(bs, h).MustDecode(&ch2)
+ // logT(t, "Len(ch2): %v", len(ch2))
+ // fmt.Printf("after: len(ch2): %v, ch2: %v\n", len(ch2), ch2)
+ close(ch2)
+ var sl2 []*int64
+ for j := range ch2 {
+ sl2 = append(sl2, j)
+ }
+ if err := deepEqual(sl1, sl2); err != nil {
+ logT(t, "Not Match: %v; len: %v, %v", err, len(sl1), len(sl2))
+ failT(t)
+ }
+}
+
+func testCodecRpcOne(t *testing.T, rr Rpc, h Handle, doRequest bool, exitSleepMs time.Duration,
+) (port int) {
+ testOnce.Do(testInitAll)
+ if testSkipRPCTests {
+ return
+ }
+ // rpc needs EOF, which is sent via a panic, and so must be recovered.
+ if !recoverPanicToErr {
+ logT(t, "EXPECTED. set recoverPanicToErr=true, since rpc needs EOF")
+ t.FailNow()
+ }
+ srv := rpc.NewServer()
+ srv.Register(testRpcInt)
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ // log("listener: %v", ln.Addr())
+ checkErrT(t, err)
+ port = (ln.Addr().(*net.TCPAddr)).Port
+ // var opts *DecoderOptions
+ // opts := testDecOpts
+ // opts.MapType = mapStrIntfTyp
+ // opts.RawToString = false
+ serverExitChan := make(chan bool, 1)
+ var serverExitFlag uint64 = 0
+ serverFn := func() {
+ for {
+ conn1, err1 := ln.Accept()
+ // if err1 != nil {
+ // //fmt.Printf("accept err1: %v\n", err1)
+ // continue
+ // }
+ if atomic.LoadUint64(&serverExitFlag) == 1 {
+ serverExitChan <- true
+ conn1.Close()
+ return // exit serverFn goroutine
+ }
+ if err1 == nil {
+ var sc rpc.ServerCodec = rr.ServerCodec(conn1, h)
+ srv.ServeCodec(sc)
+ }
+ }
+ }
+
+ clientFn := func(cc rpc.ClientCodec) {
+ cl := rpc.NewClientWithCodec(cc)
+ defer cl.Close()
+ // defer func() { println("##### client closing"); cl.Close() }()
+ var up, sq, mult int
+ var rstr string
+ // log("Calling client")
+ checkErrT(t, cl.Call("TestRpcInt.Update", 5, &up))
+ // log("Called TestRpcInt.Update")
+ checkEqualT(t, testRpcInt.i, 5, "testRpcInt.i=5")
+ checkEqualT(t, up, 5, "up=5")
+ checkErrT(t, cl.Call("TestRpcInt.Square", 1, &sq))
+ checkEqualT(t, sq, 25, "sq=25")
+ checkErrT(t, cl.Call("TestRpcInt.Mult", 20, &mult))
+ checkEqualT(t, mult, 100, "mult=100")
+ checkErrT(t, cl.Call("TestRpcInt.EchoStruct", TestABC{"Aa", "Bb", "Cc"}, &rstr))
+ checkEqualT(t, rstr, fmt.Sprintf("%#v", TestABC{"Aa", "Bb", "Cc"}), "rstr=")
+ checkErrT(t, cl.Call("TestRpcInt.Echo123", []string{"A1", "B2", "C3"}, &rstr))
+ checkEqualT(t, rstr, fmt.Sprintf("%#v", []string{"A1", "B2", "C3"}), "rstr=")
+ }
+
+ connFn := func() (bs net.Conn) {
+ // log("calling f1")
+ bs, err2 := net.Dial(ln.Addr().Network(), ln.Addr().String())
+ //fmt.Printf("f1. bs: %v, err2: %v\n", bs, err2)
+ checkErrT(t, err2)
+ return
+ }
+
+ exitFn := func() {
+ atomic.StoreUint64(&serverExitFlag, 1)
+ bs := connFn()
+ <-serverExitChan
+ bs.Close()
+ // serverExitChan <- true
+ }
+
+ go serverFn()
+ runtime.Gosched()
+ //time.Sleep(100 * time.Millisecond)
+ if exitSleepMs == 0 {
+ defer ln.Close()
+ defer exitFn()
+ }
+ if doRequest {
+ bs := connFn()
+ cc := rr.ClientCodec(bs, h)
+ clientFn(cc)
+ }
+ if exitSleepMs != 0 {
+ go func() {
+ defer ln.Close()
+ time.Sleep(exitSleepMs)
+ exitFn()
+ }()
+ }
+ return
+}
+
+func doTestMapEncodeForCanonical(t *testing.T, name string, h Handle) {
+ v1 := map[string]interface{}{
+ "a": 1,
+ "b": "hello",
+ "c": map[string]interface{}{
+ "c/a": 1,
+ "c/b": "world",
+ "c/c": []int{1, 2, 3, 4},
+ "c/d": map[string]interface{}{
+ "c/d/a": "fdisajfoidsajfopdjsaopfjdsapofda",
+ "c/d/b": "fdsafjdposakfodpsakfopdsakfpodsakfpodksaopfkdsopafkdopsa",
+ "c/d/c": "poir02 ir30qif4p03qir0pogjfpoaerfgjp ofke[padfk[ewapf kdp[afep[aw",
+ "c/d/d": "fdsopafkd[sa f-32qor-=4qeof -afo-erfo r-eafo 4e- o r4-qwo ag",
+ "c/d/e": "kfep[a sfkr0[paf[a foe-[wq ewpfao-q ro3-q ro-4qof4-qor 3-e orfkropzjbvoisdb",
+ "c/d/f": "",
+ },
+ "c/e": map[int]string{
+ 1: "1",
+ 22: "22",
+ 333: "333",
+ 4444: "4444",
+ 55555: "55555",
+ },
+ "c/f": map[string]int{
+ "1": 1,
+ "22": 22,
+ "333": 333,
+ "4444": 4444,
+ "55555": 55555,
+ },
+ },
+ }
+ var v2 map[string]interface{}
+ var b1, b2 []byte
+
+ // encode v1 into b1, decode b1 into v2, encode v2 into b2, compare b1 and b2
+
+ bh := h.getBasicHandle()
+ canonical0 := bh.Canonical
+ bh.Canonical = true
+ defer func() { bh.Canonical = canonical0 }()
+
+ e1 := NewEncoderBytes(&b1, h)
+ e1.MustEncode(v1)
+ d1 := NewDecoderBytes(b1, h)
+ d1.MustDecode(&v2)
+ e2 := NewEncoderBytes(&b2, h)
+ e2.MustEncode(v2)
+ if !bytes.Equal(b1, b2) {
+ logT(t, "Unequal bytes: %v VS %v", b1, b2)
+ t.FailNow()
+ }
+}
+
+// Comprehensive testing that generates data encoded from python handle (cbor, msgpack),
+// and validates that our code can read and write it out accordingly.
+// We keep this unexported here, and put actual test in ext_dep_test.go.
+// This way, it can be excluded by excluding file completely.
+func doTestPythonGenStreams(t *testing.T, name string, h Handle) {
+ logT(t, "TestPythonGenStreams-%v", name)
+ tmpdir, err := ioutil.TempDir("", "golang-"+name+"-test")
+ if err != nil {
+ logT(t, "-------- Unable to create temp directory\n")
+ t.FailNow()
+ }
+ defer os.RemoveAll(tmpdir)
+ logT(t, "tmpdir: %v", tmpdir)
+ cmd := exec.Command("python", "test.py", "testdata", tmpdir)
+ //cmd.Stdin = strings.NewReader("some input")
+ //cmd.Stdout = &out
+ var cmdout []byte
+ if cmdout, err = cmd.CombinedOutput(); err != nil {
+ logT(t, "-------- Error running test.py testdata. Err: %v", err)
+ logT(t, " %v", string(cmdout))
+ t.FailNow()
+ }
+
+ bh := h.getBasicHandle()
+
+ oldMapType := bh.MapType
+ for i, v := range tablePythonVerify {
+ // if v == uint64(0) && h == testMsgpackH {
+ // v = int64(0)
+ // }
+ bh.MapType = oldMapType
+ //load up the golden file based on number
+ //decode it
+ //compare to in-mem object
+ //encode it again
+ //compare to output stream
+ logT(t, "..............................................")
+ logT(t, " Testing: #%d: %T, %#v\n", i, v, v)
+ var bss []byte
+ bss, err = ioutil.ReadFile(filepath.Join(tmpdir, strconv.Itoa(i)+"."+name+".golden"))
+ if err != nil {
+ logT(t, "-------- Error reading golden file: %d. Err: %v", i, err)
+ failT(t)
+ continue
+ }
+ bh.MapType = testMapStrIntfTyp
+
+ var v1 interface{}
+ if err = testUnmarshal(&v1, bss, h); err != nil {
+ logT(t, "-------- Error decoding stream: %d: Err: %v", i, err)
+ failT(t)
+ continue
+ }
+ if v == skipVerifyVal {
+ continue
+ }
+ //no need to indirect, because we pass a nil ptr, so we already have the value
+ //if v1 != nil { v1 = reflect.Indirect(reflect.ValueOf(v1)).Interface() }
+ if err = deepEqual(v, v1); err == nil {
+ logT(t, "++++++++ Objects match: %T, %v", v, v)
+ } else {
+ logT(t, "-------- Objects do not match: %v. Source: %T. Decoded: %T", err, v, v1)
+ logT(t, "-------- GOLDEN: %#v", v)
+ // logT(t, "-------- DECODED: %#v <====> %#v", v1, reflect.Indirect(reflect.ValueOf(v1)).Interface())
+ logT(t, "-------- DECODED: %#v <====> %#v", v1, reflect.Indirect(reflect.ValueOf(v1)).Interface())
+ failT(t)
+ }
+ bsb, err := testMarshal(v1, h)
+ if err != nil {
+ logT(t, "Error encoding to stream: %d: Err: %v", i, err)
+ failT(t)
+ continue
+ }
+ if err = deepEqual(bsb, bss); err == nil {
+ logT(t, "++++++++ Bytes match")
+ } else {
+ logT(t, "???????? Bytes do not match. %v.", err)
+ xs := "--------"
+ if reflect.ValueOf(v).Kind() == reflect.Map {
+ xs = " "
+ logT(t, "%s It's a map. Ok that they don't match (dependent on ordering).", xs)
+ } else {
+ logT(t, "%s It's not a map. They should match.", xs)
+ failT(t)
+ }
+ logT(t, "%s FROM_FILE: %4d] %v", xs, len(bss), bss)
+ logT(t, "%s ENCODED: %4d] %v", xs, len(bsb), bsb)
+ }
+ }
+ bh.MapType = oldMapType
+}
+
+// To test MsgpackSpecRpc, we test 3 scenarios:
+// - Go Client to Go RPC Service (contained within TestMsgpackRpcSpec)
+// - Go client to Python RPC Service (contained within doTestMsgpackRpcSpecGoClientToPythonSvc)
+// - Python Client to Go RPC Service (contained within doTestMsgpackRpcSpecPythonClientToGoSvc)
+//
+// This allows us test the different calling conventions
+// - Go Service requires only one argument
+// - Python Service allows multiple arguments
+
+func doTestMsgpackRpcSpecGoClientToPythonSvc(t *testing.T) {
+ if testSkipRPCTests {
+ return
+ }
+ openPort := "6789"
+ cmd := exec.Command("python", "test.py", "rpc-server", openPort, "2")
+ checkErrT(t, cmd.Start())
+ time.Sleep(100 * time.Millisecond) // time for python rpc server to start
+ bs, err2 := net.Dial("tcp", ":"+openPort)
+ checkErrT(t, err2)
+ cc := MsgpackSpecRpc.ClientCodec(bs, testMsgpackH)
+ cl := rpc.NewClientWithCodec(cc)
+ defer cl.Close()
+ var rstr string
+ checkErrT(t, cl.Call("EchoStruct", TestABC{"Aa", "Bb", "Cc"}, &rstr))
+ //checkEqualT(t, rstr, "{'A': 'Aa', 'B': 'Bb', 'C': 'Cc'}")
+ var mArgs MsgpackSpecRpcMultiArgs = []interface{}{"A1", "B2", "C3"}
+ checkErrT(t, cl.Call("Echo123", mArgs, &rstr))
+ checkEqualT(t, rstr, "1:A1 2:B2 3:C3", "rstr=")
+}
+
+func doTestMsgpackRpcSpecPythonClientToGoSvc(t *testing.T) {
+ if testSkipRPCTests {
+ return
+ }
+ port := testCodecRpcOne(t, MsgpackSpecRpc, testMsgpackH, false, 1*time.Second)
+ //time.Sleep(1000 * time.Millisecond)
+ cmd := exec.Command("python", "test.py", "rpc-client-go-service", strconv.Itoa(port))
+ var cmdout []byte
+ var err error
+ if cmdout, err = cmd.CombinedOutput(); err != nil {
+ logT(t, "-------- Error running test.py rpc-client-go-service. Err: %v", err)
+ logT(t, " %v", string(cmdout))
+ t.FailNow()
+ }
+ checkEqualT(t, string(cmdout),
+ fmt.Sprintf("%#v\n%#v\n", []string{"A1", "B2", "C3"}, TestABC{"Aa", "Bb", "Cc"}), "cmdout=")
+}
+
+func TestBincCodecsTable(t *testing.T) {
+ testCodecTableOne(t, testBincH)
+}
+
+func TestBincCodecsMisc(t *testing.T) {
+ testCodecMiscOne(t, testBincH)
+}
+
+func TestBincCodecsEmbeddedPointer(t *testing.T) {
+ testCodecEmbeddedPointer(t, testBincH)
+}
+
+func TestSimpleCodecsTable(t *testing.T) {
+ testCodecTableOne(t, testSimpleH)
+}
+
+func TestSimpleCodecsMisc(t *testing.T) {
+ testCodecMiscOne(t, testSimpleH)
+}
+
+func TestSimpleCodecsEmbeddedPointer(t *testing.T) {
+ testCodecEmbeddedPointer(t, testSimpleH)
+}
+
+func TestMsgpackCodecsTable(t *testing.T) {
+ testCodecTableOne(t, testMsgpackH)
+}
+
+func TestMsgpackCodecsMisc(t *testing.T) {
+ testCodecMiscOne(t, testMsgpackH)
+}
+
+func TestMsgpackCodecsEmbeddedPointer(t *testing.T) {
+ testCodecEmbeddedPointer(t, testMsgpackH)
+}
+
+func TestCborCodecsTable(t *testing.T) {
+ testCodecTableOne(t, testCborH)
+}
+
+func TestCborCodecsMisc(t *testing.T) {
+ testCodecMiscOne(t, testCborH)
+}
+
+func TestCborCodecsEmbeddedPointer(t *testing.T) {
+ testCodecEmbeddedPointer(t, testCborH)
+}
+
+func TestCborMapEncodeForCanonical(t *testing.T) {
+ doTestMapEncodeForCanonical(t, "cbor", testCborH)
+}
+
+func TestJsonCodecsTable(t *testing.T) {
+ testCodecTableOne(t, testJsonH)
+}
+
+func TestJsonCodecsMisc(t *testing.T) {
+ testCodecMiscOne(t, testJsonH)
+}
+
+func TestJsonCodecsEmbeddedPointer(t *testing.T) {
+ testCodecEmbeddedPointer(t, testJsonH)
+}
+
+func TestJsonCodecChan(t *testing.T) {
+ testCodecChan(t, testJsonH)
+}
+
+func TestCborCodecChan(t *testing.T) {
+ testCodecChan(t, testCborH)
+}
+
+// ----- RPC -----
+
+func TestBincRpcGo(t *testing.T) {
+ testCodecRpcOne(t, GoRpc, testBincH, true, 0)
+}
+
+func TestSimpleRpcGo(t *testing.T) {
+ testCodecRpcOne(t, GoRpc, testSimpleH, true, 0)
+}
+
+func TestMsgpackRpcGo(t *testing.T) {
+ testCodecRpcOne(t, GoRpc, testMsgpackH, true, 0)
+}
+
+func TestCborRpcGo(t *testing.T) {
+ testCodecRpcOne(t, GoRpc, testCborH, true, 0)
+}
+
+func TestJsonRpcGo(t *testing.T) {
+ testCodecRpcOne(t, GoRpc, testJsonH, true, 0)
+}
+
+func TestMsgpackRpcSpec(t *testing.T) {
+ testCodecRpcOne(t, MsgpackSpecRpc, testMsgpackH, true, 0)
+}
+
+func TestBincUnderlyingType(t *testing.T) {
+ testCodecUnderlyingType(t, testBincH)
+}
+
+// TODO:
+// Add Tests for:
+// - decoding empty list/map in stream into a nil slice/map
+// - binary(M|Unm)arsher support for time.Time (e.g. cbor encoding)
+// - text(M|Unm)arshaler support for time.Time (e.g. json encoding)
+// - non fast-path scenarios e.g. map[string]uint16, []customStruct.
+// Expand cbor to include indefinite length stuff for this non-fast-path types.
+// This may not be necessary, since we have the manual tests (fastpathEnabled=false) to test/validate with.
+// - CodecSelfer
+// Ensure it is called when (en|de)coding interface{} or reflect.Value (2 different codepaths).
+// - interfaces: textMarshaler, binaryMarshaler, codecSelfer
+// - struct tags:
+// on anonymous fields, _struct (all fields), etc
+// - codecgen of struct containing channels.
+//
+// Cleanup tests:
+// - The are brittle in their handling of validation and skipping
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md
new file mode 100644
index 000000000..3ae8a056f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/README.md
@@ -0,0 +1,36 @@
+# codecgen tool
+
+Generate is given a list of *.go files to parse, and an output file (fout),
+codecgen will create an output file __file.go__ which
+contains `codec.Selfer` implementations for the named types found
+in the files parsed.
+
+Using codecgen is very straightforward.
+
+**Download and install the tool**
+
+`go get -u github.com/ugorji/go/codec/codecgen`
+
+**Run the tool on your files**
+
+The command line format is:
+
+`codecgen [options] (-o outfile) (infile ...)`
+
+```sh
+% codecgen -?
+Usage of codecgen:
+ -c="github.com/ugorji/go/codec": codec path
+ -o="": out file
+ -r=".*": regex for type name to match
+ -rt="": tags for go run
+ -t="": build tag to put in file
+ -u=false: Use unsafe, e.g. to avoid unnecessary allocation on []byte->string
+ -x=false: keep temp file
+
+% codecgen -o values_codecgen.go values.go values2.go moretypedefs.go
+```
+
+Please see the [blog article](http://ugorji.net/blog/go-codecgen)
+for more information on how to use the tool.
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go
new file mode 100644
index 000000000..8f4264356
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/gen.go
@@ -0,0 +1,281 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+// codecgen generates codec.Selfer implementations for a set of types.
+package main
+
+import (
+ "bufio"
+ "bytes"
+ "errors"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/parser"
+ "go/token"
+ "math/rand"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "text/template"
+ "time"
+)
+
+const genCodecPkg = "codec1978" // keep this in sync with codec.genCodecPkg
+
+const genFrunMainTmpl = `//+build ignore
+
+package main
+{{ if .Types }}import "{{ .ImportPath }}"{{ end }}
+func main() {
+ {{ $.PackageName }}.CodecGenTempWrite{{ .RandString }}()
+}
+`
+
+// const genFrunPkgTmpl = `//+build codecgen
+const genFrunPkgTmpl = `
+package {{ $.PackageName }}
+
+import (
+ {{ if not .CodecPkgFiles }}{{ .CodecPkgName }} "{{ .CodecImportPath }}"{{ end }}
+{{/*
+ {{ if .Types }}"{{ .ImportPath }}"{{ end }}
+ "io"
+*/}}
+ "os"
+ "reflect"
+ "bytes"
+ "go/format"
+)
+
+{{/* This is not used anymore. Remove it.
+func write(w io.Writer, s string) {
+ if _, err := io.WriteString(w, s); err != nil {
+ panic(err)
+ }
+}
+*/}}
+
+func CodecGenTempWrite{{ .RandString }}() {
+ fout, err := os.Create("{{ .OutFile }}")
+ if err != nil {
+ panic(err)
+ }
+ defer fout.Close()
+ var out bytes.Buffer
+
+ var typs []reflect.Type
+{{ range $index, $element := .Types }}
+ var t{{ $index }} {{ . }}
+ typs = append(typs, reflect.TypeOf(t{{ $index }}))
+{{ end }}
+ {{ if not .CodecPkgFiles }}{{ .CodecPkgName }}.{{ end }}Gen(&out, "{{ .BuildTag }}", "{{ .PackageName }}", "{{ .RandString }}", {{ .UseUnsafe }}, typs...)
+ bout, err := format.Source(out.Bytes())
+ if err != nil {
+ fout.Write(out.Bytes())
+ panic(err)
+ }
+ fout.Write(bout)
+}
+
+`
+
+// Generate is given a list of *.go files to parse, and an output file (fout).
+//
+// It finds all types T in the files, and it creates 2 tmp files (frun).
+// - main package file passed to 'go run'
+// - package level file which calls *genRunner.Selfer to write Selfer impls for each T.
+// We use a package level file so that it can reference unexported types in the package being worked on.
+// Tool then executes: "go run __frun__" which creates fout.
+// fout contains Codec(En|De)codeSelf implementations for every type T.
+//
+func Generate(outfile, buildTag, codecPkgPath string, uid int64, useUnsafe bool, goRunTag string,
+ regexName *regexp.Regexp, deleteTempFile bool, infiles ...string) (err error) {
+ // For each file, grab AST, find each type, and write a call to it.
+ if len(infiles) == 0 {
+ return
+ }
+ if outfile == "" || codecPkgPath == "" {
+ err = errors.New("outfile and codec package path cannot be blank")
+ return
+ }
+ if uid < 0 {
+ uid = -uid
+ }
+ if uid == 0 {
+ rr := rand.New(rand.NewSource(time.Now().UnixNano()))
+ uid = 101 + rr.Int63n(9777)
+ }
+ // We have to parse dir for package, before opening the temp file for writing (else ImportDir fails).
+ // Also, ImportDir(...) must take an absolute path.
+ lastdir := filepath.Dir(outfile)
+ absdir, err := filepath.Abs(lastdir)
+ if err != nil {
+ return
+ }
+ pkg, err := build.Default.ImportDir(absdir, build.AllowBinary)
+ if err != nil {
+ return
+ }
+ type tmplT struct {
+ CodecPkgName string
+ CodecImportPath string
+ ImportPath string
+ OutFile string
+ PackageName string
+ RandString string
+ BuildTag string
+ Types []string
+ CodecPkgFiles bool
+ UseUnsafe bool
+ }
+ tv := tmplT{
+ CodecPkgName: genCodecPkg,
+ OutFile: outfile,
+ CodecImportPath: codecPkgPath,
+ BuildTag: buildTag,
+ UseUnsafe: useUnsafe,
+ RandString: strconv.FormatInt(uid, 10),
+ }
+ tv.ImportPath = pkg.ImportPath
+ if tv.ImportPath == tv.CodecImportPath {
+ tv.CodecPkgFiles = true
+ tv.CodecPkgName = "codec"
+ }
+ astfiles := make([]*ast.File, len(infiles))
+ for i, infile := range infiles {
+ if filepath.Dir(infile) != lastdir {
+ err = errors.New("in files must all be in same directory as outfile")
+ return
+ }
+ fset := token.NewFileSet()
+ astfiles[i], err = parser.ParseFile(fset, infile, nil, 0)
+ if err != nil {
+ return
+ }
+ if i == 0 {
+ tv.PackageName = astfiles[i].Name.Name
+ if tv.PackageName == "main" {
+ // codecgen cannot be run on types in the 'main' package.
+ // A temporary 'main' package must be created, and should reference the fully built
+ // package containing the types.
+ // Also, the temporary main package will conflict with the main package which already has a main method.
+ err = errors.New("codecgen cannot be run on types in the 'main' package")
+ return
+ }
+ }
+ }
+
+ for _, f := range astfiles {
+ for _, d := range f.Decls {
+ if gd, ok := d.(*ast.GenDecl); ok {
+ for _, dd := range gd.Specs {
+ if td, ok := dd.(*ast.TypeSpec); ok {
+ // if len(td.Name.Name) == 0 || td.Name.Name[0] > 'Z' || td.Name.Name[0] < 'A' {
+ if len(td.Name.Name) == 0 {
+ continue
+ }
+
+ // only generate for:
+ // struct: StructType
+ // primitives (numbers, bool, string): Ident
+ // map: MapType
+ // slice, array: ArrayType
+ // chan: ChanType
+ // do not generate:
+ // FuncType, InterfaceType, StarExpr (ptr), etc
+ switch td.Type.(type) {
+ case *ast.StructType, *ast.Ident, *ast.MapType, *ast.ArrayType, *ast.ChanType:
+ if regexName.FindStringIndex(td.Name.Name) != nil {
+ tv.Types = append(tv.Types, td.Name.Name)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if len(tv.Types) == 0 {
+ return
+ }
+
+ // we cannot use ioutil.TempFile, because we cannot guarantee the file suffix (.go).
+ // Also, we cannot create file in temp directory,
+ // because go run will not work (as it needs to see the types here).
+ // Consequently, create the temp file in the current directory, and remove when done.
+
+ // frun, err = ioutil.TempFile("", "codecgen-")
+ // frunName := filepath.Join(os.TempDir(), "codecgen-"+strconv.FormatInt(time.Now().UnixNano(), 10)+".go")
+
+ frunMainName := "codecgen-main-" + tv.RandString + ".generated.go"
+ frunPkgName := "codecgen-pkg-" + tv.RandString + ".generated.go"
+ if deleteTempFile {
+ defer os.Remove(frunMainName)
+ defer os.Remove(frunPkgName)
+ }
+ // var frunMain, frunPkg *os.File
+ if _, err = gen1(frunMainName, genFrunMainTmpl, &tv); err != nil {
+ return
+ }
+ if _, err = gen1(frunPkgName, genFrunPkgTmpl, &tv); err != nil {
+ return
+ }
+
+ // remove outfile, so "go run ..." will not think that types in outfile already exist.
+ os.Remove(outfile)
+
+ // execute go run frun
+ cmd := exec.Command("go", "run", "-tags="+goRunTag, frunMainName) //, frunPkg.Name())
+ var buf bytes.Buffer
+ cmd.Stdout = &buf
+ cmd.Stderr = &buf
+ if err = cmd.Run(); err != nil {
+ err = fmt.Errorf("error running 'go run %s': %v, console: %s",
+ frunMainName, err, buf.Bytes())
+ return
+ }
+ os.Stdout.Write(buf.Bytes())
+ return
+}
+
+func gen1(frunName, tmplStr string, tv interface{}) (frun *os.File, err error) {
+ os.Remove(frunName)
+ if frun, err = os.Create(frunName); err != nil {
+ return
+ }
+ defer frun.Close()
+
+ t := template.New("")
+ if t, err = t.Parse(tmplStr); err != nil {
+ return
+ }
+ bw := bufio.NewWriter(frun)
+ if err = t.Execute(bw, tv); err != nil {
+ return
+ }
+ if err = bw.Flush(); err != nil {
+ return
+ }
+ return
+}
+
+func main() {
+ o := flag.String("o", "", "out file")
+ c := flag.String("c", genCodecPath, "codec path")
+ t := flag.String("t", "", "build tag to put in file")
+ r := flag.String("r", ".*", "regex for type name to match")
+ rt := flag.String("rt", "", "tags for go run")
+ x := flag.Bool("x", false, "keep temp file")
+ u := flag.Bool("u", false, "Use unsafe, e.g. to avoid unnecessary allocation on []byte->string")
+ d := flag.Int64("d", 0, "random identifier for use in generated code")
+ flag.Parse()
+ if err := Generate(*o, *t, *c, *d, *u, *rt,
+ regexp.MustCompile(*r), !*x, flag.Args()...); err != nil {
+ fmt.Fprintf(os.Stderr, "codecgen error: %v\n", err)
+ os.Exit(1)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go
new file mode 100644
index 000000000..e120a4eb9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen/z.go
@@ -0,0 +1,3 @@
+package main
+
+const genCodecPath = "github.com/ugorji/go/codec"
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go
new file mode 100644
index 000000000..2fdfd161d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/codecgen_test.go
@@ -0,0 +1,22 @@
+//+build x,codecgen
+
+package codec
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestCodecgenJson1(t *testing.T) {
+ const callCodecgenDirect bool = true
+ v := newTestStruc(2, false, !testSkipIntf, false)
+ var bs []byte
+ e := NewEncoderBytes(&bs, testJsonH)
+ if callCodecgenDirect {
+ v.CodecEncodeSelf(e)
+ e.w.atEndOfEncode()
+ } else {
+ e.MustEncode(v)
+ }
+ fmt.Printf("%s\n", bs)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go
new file mode 100644
index 000000000..1810e40b4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/decode.go
@@ -0,0 +1,1591 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "encoding"
+ "errors"
+ "fmt"
+ "io"
+ "reflect"
+)
+
+// Some tagging information for error messages.
+const (
+ msgBadDesc = "Unrecognized descriptor byte"
+ msgDecCannotExpandArr = "cannot expand go array from %v to stream length: %v"
+)
+
+var (
+ onlyMapOrArrayCanDecodeIntoStructErr = errors.New("only encoded map or array can be decoded into a struct")
+ cannotDecodeIntoNilErr = errors.New("cannot decode into nil")
+)
+
+// decReader abstracts the reading source, allowing implementations that can
+// read from an io.Reader or directly off a byte slice with zero-copying.
+type decReader interface {
+ unreadn1()
+
+ // readx will use the implementation scratch buffer if possible i.e. n < len(scratchbuf), OR
+ // just return a view of the []byte being decoded from.
+ // Ensure you call detachZeroCopyBytes later if this needs to be sent outside codec control.
+ readx(n int) []byte
+ readb([]byte)
+ readn1() uint8
+ readn1eof() (v uint8, eof bool)
+ numread() int // number of bytes read
+ track()
+ stopTrack() []byte
+}
+
+type decReaderByteScanner interface {
+ io.Reader
+ io.ByteScanner
+}
+
+type decDriver interface {
+ // this will check if the next token is a break.
+ CheckBreak() bool
+ TryDecodeAsNil() bool
+ // check if a container type: vt is one of: Bytes, String, Nil, Slice or Map.
+ // if vt param == valueTypeNil, and nil is seen in stream, consume the nil.
+ IsContainerType(vt valueType) bool
+ IsBuiltinType(rt uintptr) bool
+ DecodeBuiltin(rt uintptr, v interface{})
+ //decodeNaked: Numbers are decoded as int64, uint64, float64 only (no smaller sized number types).
+ //for extensions, decodeNaked must completely decode them as a *RawExt.
+ //extensions should also use readx to decode them, for efficiency.
+ //kInterface will extract the detached byte slice if it has to pass it outside its realm.
+ DecodeNaked() (v interface{}, vt valueType, decodeFurther bool)
+ DecodeInt(bitsize uint8) (i int64)
+ DecodeUint(bitsize uint8) (ui uint64)
+ DecodeFloat(chkOverflow32 bool) (f float64)
+ DecodeBool() (b bool)
+ // DecodeString can also decode symbols.
+ // It looks redundant as DecodeBytes is available.
+ // However, some codecs (e.g. binc) support symbols and can
+ // return a pre-stored string value, meaning that it can bypass
+ // the cost of []byte->string conversion.
+ DecodeString() (s string)
+
+ // DecodeBytes may be called directly, without going through reflection.
+ // Consequently, it must be designed to handle possible nil.
+ DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte)
+
+ // decodeExt will decode into a *RawExt or into an extension.
+ DecodeExt(v interface{}, xtag uint64, ext Ext) (realxtag uint64)
+ // decodeExt(verifyTag bool, tag byte) (xtag byte, xbs []byte)
+ ReadMapStart() int
+ ReadArrayStart() int
+ // ReadEnd registers the end of a map or array.
+ ReadEnd()
+}
+
+type decNoSeparator struct{}
+
+func (_ decNoSeparator) ReadEnd() {}
+
+type DecodeOptions struct {
+ // MapType specifies type to use during schema-less decoding of a map in the stream.
+ // If nil, we use map[interface{}]interface{}
+ MapType reflect.Type
+
+ // SliceType specifies type to use during schema-less decoding of an array in the stream.
+ // If nil, we use []interface{}
+ SliceType reflect.Type
+
+ // If ErrorIfNoField, return an error when decoding a map
+ // from a codec stream into a struct, and no matching struct field is found.
+ ErrorIfNoField bool
+
+ // If ErrorIfNoArrayExpand, return an error when decoding a slice/array that cannot be expanded.
+ // For example, the stream contains an array of 8 items, but you are decoding into a [4]T array,
+ // or you are decoding into a slice of length 4 which is non-addressable (and so cannot be set).
+ ErrorIfNoArrayExpand bool
+
+ // If SignedInteger, use the int64 during schema-less decoding of unsigned values (not uint64).
+ SignedInteger bool
+
+ // MaxInitLen defines the initial length that we "make" a collection (slice, chan or map) with.
+ // If 0 or negative, we default to a sensible value based on the size of an element in the collection.
+ //
+ // For example, when decoding, a stream may say that it has MAX_UINT elements.
+ // We should not auto-matically provision a slice of that length, to prevent Out-Of-Memory crash.
+ // Instead, we provision up to MaxInitLen, fill that up, and start appending after that.
+ MaxInitLen int
+}
+
+// ------------------------------------
+
+// ioDecByteScanner implements Read(), ReadByte(...), UnreadByte(...) methods
+// of io.Reader, io.ByteScanner.
+type ioDecByteScanner struct {
+ r io.Reader
+ l byte // last byte
+ ls byte // last byte status. 0: init-canDoNothing, 1: canRead, 2: canUnread
+ b [1]byte // tiny buffer for reading single bytes
+}
+
+func (z *ioDecByteScanner) Read(p []byte) (n int, err error) {
+ var firstByte bool
+ if z.ls == 1 {
+ z.ls = 2
+ p[0] = z.l
+ if len(p) == 1 {
+ n = 1
+ return
+ }
+ firstByte = true
+ p = p[1:]
+ }
+ n, err = z.r.Read(p)
+ if n > 0 {
+ if err == io.EOF && n == len(p) {
+ err = nil // read was successful, so postpone EOF (till next time)
+ }
+ z.l = p[n-1]
+ z.ls = 2
+ }
+ if firstByte {
+ n++
+ }
+ return
+}
+
+func (z *ioDecByteScanner) ReadByte() (c byte, err error) {
+ n, err := z.Read(z.b[:])
+ if n == 1 {
+ c = z.b[0]
+ if err == io.EOF {
+ err = nil // read was successful, so postpone EOF (till next time)
+ }
+ }
+ return
+}
+
+func (z *ioDecByteScanner) UnreadByte() (err error) {
+ x := z.ls
+ if x == 0 {
+ err = errors.New("cannot unread - nothing has been read")
+ } else if x == 1 {
+ err = errors.New("cannot unread - last byte has not been read")
+ } else if x == 2 {
+ z.ls = 1
+ }
+ return
+}
+
+// ioDecReader is a decReader that reads off an io.Reader
+type ioDecReader struct {
+ br decReaderByteScanner
+ // temp byte array re-used internally for efficiency during read.
+ // shares buffer with Decoder, so we keep size of struct within 8 words.
+ x *[scratchByteArrayLen]byte
+ bs ioDecByteScanner
+ n int // num read
+ tr []byte // tracking bytes read
+ trb bool
+}
+
+func (z *ioDecReader) numread() int {
+ return z.n
+}
+
+func (z *ioDecReader) readx(n int) (bs []byte) {
+ if n <= 0 {
+ return
+ }
+ if n < len(z.x) {
+ bs = z.x[:n]
+ } else {
+ bs = make([]byte, n)
+ }
+ if _, err := io.ReadAtLeast(z.br, bs, n); err != nil {
+ panic(err)
+ }
+ z.n += len(bs)
+ if z.trb {
+ z.tr = append(z.tr, bs...)
+ }
+ return
+}
+
+func (z *ioDecReader) readb(bs []byte) {
+ if len(bs) == 0 {
+ return
+ }
+ n, err := io.ReadAtLeast(z.br, bs, len(bs))
+ z.n += n
+ if err != nil {
+ panic(err)
+ }
+ if z.trb {
+ z.tr = append(z.tr, bs...)
+ }
+}
+
+func (z *ioDecReader) readn1() (b uint8) {
+ b, err := z.br.ReadByte()
+ if err != nil {
+ panic(err)
+ }
+ z.n++
+ if z.trb {
+ z.tr = append(z.tr, b)
+ }
+ return b
+}
+
+func (z *ioDecReader) readn1eof() (b uint8, eof bool) {
+ b, err := z.br.ReadByte()
+ if err == nil {
+ z.n++
+ if z.trb {
+ z.tr = append(z.tr, b)
+ }
+ } else if err == io.EOF {
+ eof = true
+ } else {
+ panic(err)
+ }
+ return
+}
+
+func (z *ioDecReader) unreadn1() {
+ err := z.br.UnreadByte()
+ if err != nil {
+ panic(err)
+ }
+ z.n--
+ if z.trb {
+ if l := len(z.tr) - 1; l >= 0 {
+ z.tr = z.tr[:l]
+ }
+ }
+}
+
+func (z *ioDecReader) track() {
+ if z.tr != nil {
+ z.tr = z.tr[:0]
+ }
+ z.trb = true
+}
+
+func (z *ioDecReader) stopTrack() (bs []byte) {
+ z.trb = false
+ return z.tr
+}
+
+// ------------------------------------
+
+var bytesDecReaderCannotUnreadErr = errors.New("cannot unread last byte read")
+
+// bytesDecReader is a decReader that reads off a byte slice with zero copying
+type bytesDecReader struct {
+ b []byte // data
+ c int // cursor
+ a int // available
+ t int // track start
+}
+
+func (z *bytesDecReader) numread() int {
+ return z.c
+}
+
+func (z *bytesDecReader) unreadn1() {
+ if z.c == 0 || len(z.b) == 0 {
+ panic(bytesDecReaderCannotUnreadErr)
+ }
+ z.c--
+ z.a++
+ return
+}
+
+func (z *bytesDecReader) readx(n int) (bs []byte) {
+ // slicing from a non-constant start position is more expensive,
+ // as more computation is required to decipher the pointer start position.
+ // However, we do it only once, and it's better than reslicing both z.b and return value.
+
+ if n <= 0 {
+ } else if z.a == 0 {
+ panic(io.EOF)
+ } else if n > z.a {
+ panic(io.ErrUnexpectedEOF)
+ } else {
+ c0 := z.c
+ z.c = c0 + n
+ z.a = z.a - n
+ bs = z.b[c0:z.c]
+ }
+ return
+}
+
+func (z *bytesDecReader) readn1() (v uint8) {
+ if z.a == 0 {
+ panic(io.EOF)
+ }
+ v = z.b[z.c]
+ z.c++
+ z.a--
+ return
+}
+
+func (z *bytesDecReader) readn1eof() (v uint8, eof bool) {
+ if z.a == 0 {
+ eof = true
+ return
+ }
+ v = z.b[z.c]
+ z.c++
+ z.a--
+ return
+}
+
+func (z *bytesDecReader) readb(bs []byte) {
+ copy(bs, z.readx(len(bs)))
+}
+
+func (z *bytesDecReader) track() {
+ z.t = z.c
+}
+
+func (z *bytesDecReader) stopTrack() (bs []byte) {
+ return z.b[z.t:z.c]
+}
+
+// ------------------------------------
+
+type decFnInfo struct {
+ d *Decoder
+ ti *typeInfo
+ xfFn Ext
+ xfTag uint64
+ seq seqType
+}
+
+// ----------------------------------------
+
+type decFn struct {
+ i decFnInfo
+ f func(*decFnInfo, reflect.Value)
+}
+
+func (f *decFnInfo) builtin(rv reflect.Value) {
+ f.d.d.DecodeBuiltin(f.ti.rtid, rv.Addr().Interface())
+}
+
+func (f *decFnInfo) rawExt(rv reflect.Value) {
+ f.d.d.DecodeExt(rv.Addr().Interface(), 0, nil)
+}
+
+func (f *decFnInfo) ext(rv reflect.Value) {
+ f.d.d.DecodeExt(rv.Addr().Interface(), f.xfTag, f.xfFn)
+}
+
+func (f *decFnInfo) getValueForUnmarshalInterface(rv reflect.Value, indir int8) (v interface{}) {
+ if indir == -1 {
+ v = rv.Addr().Interface()
+ } else if indir == 0 {
+ v = rv.Interface()
+ } else {
+ for j := int8(0); j < indir; j++ {
+ if rv.IsNil() {
+ rv.Set(reflect.New(rv.Type().Elem()))
+ }
+ rv = rv.Elem()
+ }
+ v = rv.Interface()
+ }
+ return
+}
+
+func (f *decFnInfo) selferUnmarshal(rv reflect.Value) {
+ f.getValueForUnmarshalInterface(rv, f.ti.csIndir).(Selfer).CodecDecodeSelf(f.d)
+}
+
+func (f *decFnInfo) binaryUnmarshal(rv reflect.Value) {
+ bm := f.getValueForUnmarshalInterface(rv, f.ti.bunmIndir).(encoding.BinaryUnmarshaler)
+ xbs := f.d.d.DecodeBytes(nil, false, true)
+ if fnerr := bm.UnmarshalBinary(xbs); fnerr != nil {
+ panic(fnerr)
+ }
+}
+
+func (f *decFnInfo) textUnmarshal(rv reflect.Value) {
+ tm := f.getValueForUnmarshalInterface(rv, f.ti.tunmIndir).(encoding.TextUnmarshaler)
+ fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+
+func (f *decFnInfo) jsonUnmarshal(rv reflect.Value) {
+ tm := f.getValueForUnmarshalInterface(rv, f.ti.junmIndir).(jsonUnmarshaler)
+ // bs := f.d.d.DecodeBytes(f.d.b[:], true, true)
+ // grab the bytes to be read, as UnmarshalJSON wants the full JSON to unmarshal it itself.
+ f.d.r.track()
+ f.d.swallow()
+ bs := f.d.r.stopTrack()
+ // fmt.Printf(">>>>>> REFLECTION JSON: %s\n", bs)
+ fnerr := tm.UnmarshalJSON(bs)
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+
+func (f *decFnInfo) kErr(rv reflect.Value) {
+ f.d.errorf("no decoding function defined for kind %v", rv.Kind())
+}
+
+func (f *decFnInfo) kString(rv reflect.Value) {
+ rv.SetString(f.d.d.DecodeString())
+}
+
+func (f *decFnInfo) kBool(rv reflect.Value) {
+ rv.SetBool(f.d.d.DecodeBool())
+}
+
+func (f *decFnInfo) kInt(rv reflect.Value) {
+ rv.SetInt(f.d.d.DecodeInt(intBitsize))
+}
+
+func (f *decFnInfo) kInt64(rv reflect.Value) {
+ rv.SetInt(f.d.d.DecodeInt(64))
+}
+
+func (f *decFnInfo) kInt32(rv reflect.Value) {
+ rv.SetInt(f.d.d.DecodeInt(32))
+}
+
+func (f *decFnInfo) kInt8(rv reflect.Value) {
+ rv.SetInt(f.d.d.DecodeInt(8))
+}
+
+func (f *decFnInfo) kInt16(rv reflect.Value) {
+ rv.SetInt(f.d.d.DecodeInt(16))
+}
+
+func (f *decFnInfo) kFloat32(rv reflect.Value) {
+ rv.SetFloat(f.d.d.DecodeFloat(true))
+}
+
+func (f *decFnInfo) kFloat64(rv reflect.Value) {
+ rv.SetFloat(f.d.d.DecodeFloat(false))
+}
+
+func (f *decFnInfo) kUint8(rv reflect.Value) {
+ rv.SetUint(f.d.d.DecodeUint(8))
+}
+
+func (f *decFnInfo) kUint64(rv reflect.Value) {
+ rv.SetUint(f.d.d.DecodeUint(64))
+}
+
+func (f *decFnInfo) kUint(rv reflect.Value) {
+ rv.SetUint(f.d.d.DecodeUint(uintBitsize))
+}
+
+func (f *decFnInfo) kUintptr(rv reflect.Value) {
+ rv.SetUint(f.d.d.DecodeUint(uintBitsize))
+}
+
+func (f *decFnInfo) kUint32(rv reflect.Value) {
+ rv.SetUint(f.d.d.DecodeUint(32))
+}
+
+func (f *decFnInfo) kUint16(rv reflect.Value) {
+ rv.SetUint(f.d.d.DecodeUint(16))
+}
+
+// func (f *decFnInfo) kPtr(rv reflect.Value) {
+// debugf(">>>>>>> ??? decode kPtr called - shouldn't get called")
+// if rv.IsNil() {
+// rv.Set(reflect.New(rv.Type().Elem()))
+// }
+// f.d.decodeValue(rv.Elem())
+// }
+
+// var kIntfCtr uint64
+
+func (f *decFnInfo) kInterfaceNaked() (rvn reflect.Value) {
+ // nil interface:
+ // use some hieristics to decode it appropriately
+ // based on the detected next value in the stream.
+ v, vt, decodeFurther := f.d.d.DecodeNaked()
+ if vt == valueTypeNil {
+ return
+ }
+ // We cannot decode non-nil stream value into nil interface with methods (e.g. io.Reader).
+ if num := f.ti.rt.NumMethod(); num > 0 {
+ f.d.errorf("cannot decode non-nil codec value into nil %v (%v methods)", f.ti.rt, num)
+ return
+ }
+ var useRvn bool
+ switch vt {
+ case valueTypeMap:
+ if f.d.h.MapType == nil {
+ var m2 map[interface{}]interface{}
+ v = &m2
+ } else {
+ rvn = reflect.New(f.d.h.MapType).Elem()
+ useRvn = true
+ }
+ case valueTypeArray:
+ if f.d.h.SliceType == nil {
+ var m2 []interface{}
+ v = &m2
+ } else {
+ rvn = reflect.New(f.d.h.SliceType).Elem()
+ useRvn = true
+ }
+ case valueTypeExt:
+ re := v.(*RawExt)
+ bfn := f.d.h.getExtForTag(re.Tag)
+ if bfn == nil {
+ re.Data = detachZeroCopyBytes(f.d.bytes, nil, re.Data)
+ rvn = reflect.ValueOf(*re)
+ } else {
+ rvnA := reflect.New(bfn.rt)
+ rvn = rvnA.Elem()
+ if re.Data != nil {
+ bfn.ext.ReadExt(rvnA.Interface(), re.Data)
+ } else {
+ bfn.ext.UpdateExt(rvnA.Interface(), re.Value)
+ }
+ }
+ return
+ }
+ if decodeFurther {
+ if useRvn {
+ f.d.decodeValue(rvn, nil)
+ } else if v != nil {
+ // this v is a pointer, so we need to dereference it when done
+ f.d.decode(v)
+ rvn = reflect.ValueOf(v).Elem()
+ useRvn = true
+ }
+ }
+
+ if !useRvn && v != nil {
+ rvn = reflect.ValueOf(v)
+ }
+ return
+}
+
+func (f *decFnInfo) kInterface(rv reflect.Value) {
+ // debugf("\t===> kInterface")
+
+ // Note:
+ // A consequence of how kInterface works, is that
+ // if an interface already contains something, we try
+ // to decode into what was there before.
+ // We do not replace with a generic value (as got from decodeNaked).
+
+ if rv.IsNil() {
+ rvn := f.kInterfaceNaked()
+ if rvn.IsValid() {
+ rv.Set(rvn)
+ }
+ } else {
+ rve := rv.Elem()
+ // Note: interface{} is settable, but underlying type may not be.
+ // Consequently, we have to set the reflect.Value directly.
+ // if underlying type is settable (e.g. ptr or interface),
+ // we just decode into it.
+ // Else we create a settable value, decode into it, and set on the interface.
+ if rve.CanSet() {
+ f.d.decodeValue(rve, nil)
+ } else {
+ rve2 := reflect.New(rve.Type()).Elem()
+ rve2.Set(rve)
+ f.d.decodeValue(rve2, nil)
+ rv.Set(rve2)
+ }
+ }
+}
+
+func (f *decFnInfo) kStruct(rv reflect.Value) {
+ fti := f.ti
+ d := f.d
+ dd := d.d
+ if dd.IsContainerType(valueTypeMap) {
+ containerLen := dd.ReadMapStart()
+ if containerLen == 0 {
+ dd.ReadEnd()
+ return
+ }
+ tisfi := fti.sfi
+ hasLen := containerLen >= 0
+ if hasLen {
+ for j := 0; j < containerLen; j++ {
+ // rvkencname := dd.DecodeString()
+ rvkencname := stringView(dd.DecodeBytes(f.d.b[:], true, true))
+ // rvksi := ti.getForEncName(rvkencname)
+ if k := fti.indexForEncName(rvkencname); k > -1 {
+ si := tisfi[k]
+ if dd.TryDecodeAsNil() {
+ si.setToZeroValue(rv)
+ } else {
+ d.decodeValue(si.field(rv, true), nil)
+ }
+ } else {
+ d.structFieldNotFound(-1, rvkencname)
+ }
+ }
+ } else {
+ for j := 0; !dd.CheckBreak(); j++ {
+ // rvkencname := dd.DecodeString()
+ rvkencname := stringView(dd.DecodeBytes(f.d.b[:], true, true))
+ // rvksi := ti.getForEncName(rvkencname)
+ if k := fti.indexForEncName(rvkencname); k > -1 {
+ si := tisfi[k]
+ if dd.TryDecodeAsNil() {
+ si.setToZeroValue(rv)
+ } else {
+ d.decodeValue(si.field(rv, true), nil)
+ }
+ } else {
+ d.structFieldNotFound(-1, rvkencname)
+ }
+ }
+ dd.ReadEnd()
+ }
+ } else if dd.IsContainerType(valueTypeArray) {
+ containerLen := dd.ReadArrayStart()
+ if containerLen == 0 {
+ dd.ReadEnd()
+ return
+ }
+ // Not much gain from doing it two ways for array.
+ // Arrays are not used as much for structs.
+ hasLen := containerLen >= 0
+ for j, si := range fti.sfip {
+ if hasLen {
+ if j == containerLen {
+ break
+ }
+ } else if dd.CheckBreak() {
+ break
+ }
+ if dd.TryDecodeAsNil() {
+ si.setToZeroValue(rv)
+ } else {
+ d.decodeValue(si.field(rv, true), nil)
+ }
+ }
+ if containerLen > len(fti.sfip) {
+ // read remaining values and throw away
+ for j := len(fti.sfip); j < containerLen; j++ {
+ d.structFieldNotFound(j, "")
+ }
+ }
+ dd.ReadEnd()
+ } else {
+ f.d.error(onlyMapOrArrayCanDecodeIntoStructErr)
+ return
+ }
+}
+
+func (f *decFnInfo) kSlice(rv reflect.Value) {
+ // A slice can be set from a map or array in stream.
+ // This way, the order can be kept (as order is lost with map).
+ ti := f.ti
+ d := f.d
+ dd := d.d
+ rtelem0 := ti.rt.Elem()
+
+ if dd.IsContainerType(valueTypeBytes) || dd.IsContainerType(valueTypeString) {
+ if ti.rtid == uint8SliceTypId || rtelem0.Kind() == reflect.Uint8 {
+ if f.seq == seqTypeChan {
+ bs2 := dd.DecodeBytes(nil, false, true)
+ ch := rv.Interface().(chan<- byte)
+ for _, b := range bs2 {
+ ch <- b
+ }
+ } else {
+ rvbs := rv.Bytes()
+ bs2 := dd.DecodeBytes(rvbs, false, false)
+ if rvbs == nil && bs2 != nil || rvbs != nil && bs2 == nil || len(bs2) != len(rvbs) {
+ if rv.CanSet() {
+ rv.SetBytes(bs2)
+ } else {
+ copy(rvbs, bs2)
+ }
+ }
+ }
+ return
+ }
+ }
+
+ // array := f.seq == seqTypeChan
+
+ slh, containerLenS := d.decSliceHelperStart()
+
+ var rvlen, numToRead int
+ var truncated bool // says that the len of the sequence is not same as the expected number of elements.
+
+ numToRead = containerLenS // if truncated, reset numToRead
+
+ // an array can never return a nil slice. so no need to check f.array here.
+ if rv.IsNil() {
+ // either chan or slice
+ if rvlen, truncated = decInferLen(containerLenS, f.d.h.MaxInitLen, int(rtelem0.Size())); truncated {
+ numToRead = rvlen
+ }
+ if f.seq == seqTypeSlice {
+ rv.Set(reflect.MakeSlice(ti.rt, rvlen, rvlen))
+ } else if f.seq == seqTypeChan {
+ rv.Set(reflect.MakeChan(ti.rt, rvlen))
+ }
+ } else {
+ rvlen = rv.Len()
+ }
+
+ if containerLenS == 0 {
+ if f.seq == seqTypeSlice && rvlen != 0 {
+ rv.SetLen(0)
+ }
+ // dd.ReadEnd()
+ return
+ }
+
+ rtelem := rtelem0
+ for rtelem.Kind() == reflect.Ptr {
+ rtelem = rtelem.Elem()
+ }
+ fn := d.getDecFn(rtelem, true, true)
+
+ var rv0, rv9 reflect.Value
+ rv0 = rv
+ rvChanged := false
+
+ rvcap := rv.Cap()
+
+ // for j := 0; j < containerLenS; j++ {
+
+ if containerLenS >= 0 { // hasLen
+ if f.seq == seqTypeChan {
+ // handle chan specially:
+ for j := 0; j < containerLenS; j++ {
+ rv9 = reflect.New(rtelem0).Elem()
+ d.decodeValue(rv9, fn)
+ rv.Send(rv9)
+ }
+ } else { // slice or array
+ if containerLenS > rvcap {
+ if f.seq == seqTypeArray {
+ d.arrayCannotExpand(rvlen, containerLenS)
+ } else {
+ oldRvlenGtZero := rvlen > 0
+ rvlen, truncated = decInferLen(containerLenS, f.d.h.MaxInitLen, int(rtelem0.Size()))
+ rv = reflect.MakeSlice(ti.rt, rvlen, rvlen)
+ if oldRvlenGtZero && !isImmutableKind(rtelem0.Kind()) {
+ reflect.Copy(rv, rv0) // only copy up to length NOT cap i.e. rv0.Slice(0, rvcap)
+ }
+ rvChanged = true
+ }
+ numToRead = rvlen
+ } else if containerLenS != rvlen {
+ if f.seq == seqTypeSlice {
+ rv.SetLen(containerLenS)
+ rvlen = containerLenS
+ }
+ }
+ j := 0
+ // we read up to the numToRead
+ for ; j < numToRead; j++ {
+ d.decodeValue(rv.Index(j), fn)
+ }
+
+ // if slice, expand and read up to containerLenS (or EOF) iff truncated
+ // if array, swallow all the rest.
+
+ if f.seq == seqTypeArray {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ } else if truncated { // slice was truncated, as chan NOT in this block
+ for ; j < containerLenS; j++ {
+ rv = expandSliceValue(rv, 1)
+ rv9 = rv.Index(j)
+ if resetSliceElemToZeroValue {
+ rv9.Set(reflect.Zero(rtelem0))
+ }
+ d.decodeValue(rv9, fn)
+ }
+ }
+ }
+ } else {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var decodeIntoBlank bool
+ // if indefinite, etc, then expand the slice if necessary
+ if j >= rvlen {
+ if f.seq == seqTypeArray {
+ d.arrayCannotExpand(rvlen, j+1)
+ decodeIntoBlank = true
+ } else if f.seq == seqTypeSlice {
+ // rv = reflect.Append(rv, reflect.Zero(rtelem0)) // uses append logic, plus varargs
+ rv = expandSliceValue(rv, 1)
+ rv9 = rv.Index(j)
+ // rv.Index(rv.Len() - 1).Set(reflect.Zero(rtelem0))
+ if resetSliceElemToZeroValue {
+ rv9.Set(reflect.Zero(rtelem0))
+ }
+ rvlen++
+ rvChanged = true
+ }
+ } else if f.seq != seqTypeChan { // slice or array
+ rv9 = rv.Index(j)
+ }
+ if f.seq == seqTypeChan {
+ rv9 = reflect.New(rtelem0).Elem()
+ d.decodeValue(rv9, fn)
+ rv.Send(rv9)
+ } else if decodeIntoBlank {
+ d.swallow()
+ } else { // seqTypeSlice
+ d.decodeValue(rv9, fn)
+ }
+ }
+ slh.End()
+ }
+
+ if rvChanged {
+ rv0.Set(rv)
+ }
+}
+
+func (f *decFnInfo) kArray(rv reflect.Value) {
+ // f.d.decodeValue(rv.Slice(0, rv.Len()))
+ f.kSlice(rv.Slice(0, rv.Len()))
+}
+
+func (f *decFnInfo) kMap(rv reflect.Value) {
+ d := f.d
+ dd := d.d
+ containerLen := dd.ReadMapStart()
+
+ ti := f.ti
+ if rv.IsNil() {
+ rv.Set(reflect.MakeMap(ti.rt))
+ }
+
+ if containerLen == 0 {
+ // It is not length-prefix style container. They have no End marker.
+ // dd.ReadMapEnd()
+ return
+ }
+
+ ktype, vtype := ti.rt.Key(), ti.rt.Elem()
+ ktypeId := reflect.ValueOf(ktype).Pointer()
+ var keyFn, valFn *decFn
+ var xtyp reflect.Type
+ for xtyp = ktype; xtyp.Kind() == reflect.Ptr; xtyp = xtyp.Elem() {
+ }
+ keyFn = d.getDecFn(xtyp, true, true)
+ for xtyp = vtype; xtyp.Kind() == reflect.Ptr; xtyp = xtyp.Elem() {
+ }
+ valFn = d.getDecFn(xtyp, true, true)
+ // for j := 0; j < containerLen; j++ {
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ rvk := reflect.New(ktype).Elem()
+ d.decodeValue(rvk, keyFn)
+
+ // special case if a byte array.
+ if ktypeId == intfTypId {
+ rvk = rvk.Elem()
+ if rvk.Type() == uint8SliceTyp {
+ rvk = reflect.ValueOf(string(rvk.Bytes()))
+ }
+ }
+ rvv := rv.MapIndex(rvk)
+ // TODO: is !IsValid check required?
+ if !rvv.IsValid() {
+ rvv = reflect.New(vtype).Elem()
+ }
+ d.decodeValue(rvv, valFn)
+ rv.SetMapIndex(rvk, rvv)
+ }
+ } else {
+ for j := 0; !dd.CheckBreak(); j++ {
+ rvk := reflect.New(ktype).Elem()
+ d.decodeValue(rvk, keyFn)
+
+ // special case if a byte array.
+ if ktypeId == intfTypId {
+ rvk = rvk.Elem()
+ if rvk.Type() == uint8SliceTyp {
+ rvk = reflect.ValueOf(string(rvk.Bytes()))
+ }
+ }
+ rvv := rv.MapIndex(rvk)
+ if !rvv.IsValid() {
+ rvv = reflect.New(vtype).Elem()
+ }
+ d.decodeValue(rvv, valFn)
+ rv.SetMapIndex(rvk, rvv)
+ }
+ dd.ReadEnd()
+ }
+}
+
+type decRtidFn struct {
+ rtid uintptr
+ fn decFn
+}
+
+// A Decoder reads and decodes an object from an input stream in the codec format.
+type Decoder struct {
+ // hopefully, reduce derefencing cost by laying the decReader inside the Decoder.
+ // Try to put things that go together to fit within a cache line (8 words).
+
+ d decDriver
+ // NOTE: Decoder shouldn't call it's read methods,
+ // as the handler MAY need to do some coordination.
+ r decReader
+ // sa [initCollectionCap]decRtidFn
+ s []decRtidFn
+ h *BasicHandle
+
+ rb bytesDecReader
+ hh Handle
+ be bool // is binary encoding
+ bytes bool // is bytes reader
+ js bool // is json handle
+
+ ri ioDecReader
+ f map[uintptr]*decFn
+ // _ uintptr // for alignment purposes, so next one starts from a cache line
+
+ b [scratchByteArrayLen]byte
+}
+
+// NewDecoder returns a Decoder for decoding a stream of bytes from an io.Reader.
+//
+// For efficiency, Users are encouraged to pass in a memory buffered reader
+// (eg bufio.Reader, bytes.Buffer).
+func NewDecoder(r io.Reader, h Handle) (d *Decoder) {
+ d = &Decoder{hh: h, h: h.getBasicHandle(), be: h.isBinary()}
+ // d.s = d.sa[:0]
+ d.ri.x = &d.b
+ d.ri.bs.r = r
+ var ok bool
+ d.ri.br, ok = r.(decReaderByteScanner)
+ if !ok {
+ d.ri.br = &d.ri.bs
+ }
+ d.r = &d.ri
+ _, d.js = h.(*JsonHandle)
+ d.d = h.newDecDriver(d)
+ return
+}
+
+// NewDecoderBytes returns a Decoder which efficiently decodes directly
+// from a byte slice with zero copying.
+func NewDecoderBytes(in []byte, h Handle) (d *Decoder) {
+ d = &Decoder{hh: h, h: h.getBasicHandle(), be: h.isBinary(), bytes: true}
+ // d.s = d.sa[:0]
+ d.rb.b = in
+ d.rb.a = len(in)
+ d.r = &d.rb
+ _, d.js = h.(*JsonHandle)
+ d.d = h.newDecDriver(d)
+ // d.d = h.newDecDriver(decReaderT{true, &d.rb, &d.ri})
+ return
+}
+
+// Decode decodes the stream from reader and stores the result in the
+// value pointed to by v. v cannot be a nil pointer. v can also be
+// a reflect.Value of a pointer.
+//
+// Note that a pointer to a nil interface is not a nil pointer.
+// If you do not know what type of stream it is, pass in a pointer to a nil interface.
+// We will decode and store a value in that nil interface.
+//
+// Sample usages:
+// // Decoding into a non-nil typed value
+// var f float32
+// err = codec.NewDecoder(r, handle).Decode(&f)
+//
+// // Decoding into nil interface
+// var v interface{}
+// dec := codec.NewDecoder(r, handle)
+// err = dec.Decode(&v)
+//
+// When decoding into a nil interface{}, we will decode into an appropriate value based
+// on the contents of the stream:
+// - Numbers are decoded as float64, int64 or uint64.
+// - Other values are decoded appropriately depending on the type:
+// bool, string, []byte, time.Time, etc
+// - Extensions are decoded as RawExt (if no ext function registered for the tag)
+// Configurations exist on the Handle to override defaults
+// (e.g. for MapType, SliceType and how to decode raw bytes).
+//
+// When decoding into a non-nil interface{} value, the mode of encoding is based on the
+// type of the value. When a value is seen:
+// - If an extension is registered for it, call that extension function
+// - If it implements BinaryUnmarshaler, call its UnmarshalBinary(data []byte) error
+// - Else decode it based on its reflect.Kind
+//
+// There are some special rules when decoding into containers (slice/array/map/struct).
+// Decode will typically use the stream contents to UPDATE the container.
+// - A map can be decoded from a stream map, by updating matching keys.
+// - A slice can be decoded from a stream array,
+// by updating the first n elements, where n is length of the stream.
+// - A slice can be decoded from a stream map, by decoding as if
+// it contains a sequence of key-value pairs.
+// - A struct can be decoded from a stream map, by updating matching fields.
+// - A struct can be decoded from a stream array,
+// by updating fields as they occur in the struct (by index).
+//
+// When decoding a stream map or array with length of 0 into a nil map or slice,
+// we reset the destination map or slice to a zero-length value.
+//
+// However, when decoding a stream nil, we reset the destination container
+// to its "zero" value (e.g. nil for slice/map, etc).
+//
+func (d *Decoder) Decode(v interface{}) (err error) {
+ defer panicToErr(&err)
+ d.decode(v)
+ return
+}
+
+// this is not a smart swallow, as it allocates objects and does unnecessary work.
+func (d *Decoder) swallowViaHammer() {
+ var blank interface{}
+ d.decodeValue(reflect.ValueOf(&blank).Elem(), nil)
+}
+
+func (d *Decoder) swallow() {
+ // smarter decode that just swallows the content
+ dd := d.d
+ switch {
+ case dd.TryDecodeAsNil():
+ case dd.IsContainerType(valueTypeMap):
+ containerLen := dd.ReadMapStart()
+ clenGtEqualZero := containerLen >= 0
+ for j := 0; ; j++ {
+ if clenGtEqualZero {
+ if j >= containerLen {
+ break
+ }
+ } else if dd.CheckBreak() {
+ break
+ }
+ d.swallow()
+ d.swallow()
+ }
+ dd.ReadEnd()
+ case dd.IsContainerType(valueTypeArray):
+ containerLenS := dd.ReadArrayStart()
+ clenGtEqualZero := containerLenS >= 0
+ for j := 0; ; j++ {
+ if clenGtEqualZero {
+ if j >= containerLenS {
+ break
+ }
+ } else if dd.CheckBreak() {
+ break
+ }
+ d.swallow()
+ }
+ dd.ReadEnd()
+ case dd.IsContainerType(valueTypeBytes):
+ dd.DecodeBytes(d.b[:], false, true)
+ case dd.IsContainerType(valueTypeString):
+ dd.DecodeBytes(d.b[:], true, true)
+ // dd.DecodeStringAsBytes(d.b[:])
+ default:
+ // these are all primitives, which we can get from decodeNaked
+ dd.DecodeNaked()
+ }
+}
+
+// MustDecode is like Decode, but panics if unable to Decode.
+// This provides insight to the code location that triggered the error.
+func (d *Decoder) MustDecode(v interface{}) {
+ d.decode(v)
+}
+
+func (d *Decoder) decode(iv interface{}) {
+ // if ics, ok := iv.(Selfer); ok {
+ // ics.CodecDecodeSelf(d)
+ // return
+ // }
+
+ if d.d.TryDecodeAsNil() {
+ switch v := iv.(type) {
+ case nil:
+ case *string:
+ *v = ""
+ case *bool:
+ *v = false
+ case *int:
+ *v = 0
+ case *int8:
+ *v = 0
+ case *int16:
+ *v = 0
+ case *int32:
+ *v = 0
+ case *int64:
+ *v = 0
+ case *uint:
+ *v = 0
+ case *uint8:
+ *v = 0
+ case *uint16:
+ *v = 0
+ case *uint32:
+ *v = 0
+ case *uint64:
+ *v = 0
+ case *float32:
+ *v = 0
+ case *float64:
+ *v = 0
+ case *[]uint8:
+ *v = nil
+ case reflect.Value:
+ d.chkPtrValue(v)
+ v = v.Elem()
+ if v.IsValid() {
+ v.Set(reflect.Zero(v.Type()))
+ }
+ default:
+ rv := reflect.ValueOf(iv)
+ d.chkPtrValue(rv)
+ rv = rv.Elem()
+ if rv.IsValid() {
+ rv.Set(reflect.Zero(rv.Type()))
+ }
+ }
+ return
+ }
+
+ switch v := iv.(type) {
+ case nil:
+ d.error(cannotDecodeIntoNilErr)
+ return
+
+ case Selfer:
+ v.CodecDecodeSelf(d)
+
+ case reflect.Value:
+ d.chkPtrValue(v)
+ d.decodeValueNotNil(v.Elem(), nil)
+
+ case *string:
+
+ *v = d.d.DecodeString()
+ case *bool:
+ *v = d.d.DecodeBool()
+ case *int:
+ *v = int(d.d.DecodeInt(intBitsize))
+ case *int8:
+ *v = int8(d.d.DecodeInt(8))
+ case *int16:
+ *v = int16(d.d.DecodeInt(16))
+ case *int32:
+ *v = int32(d.d.DecodeInt(32))
+ case *int64:
+ *v = d.d.DecodeInt(64)
+ case *uint:
+ *v = uint(d.d.DecodeUint(uintBitsize))
+ case *uint8:
+ *v = uint8(d.d.DecodeUint(8))
+ case *uint16:
+ *v = uint16(d.d.DecodeUint(16))
+ case *uint32:
+ *v = uint32(d.d.DecodeUint(32))
+ case *uint64:
+ *v = d.d.DecodeUint(64)
+ case *float32:
+ *v = float32(d.d.DecodeFloat(true))
+ case *float64:
+ *v = d.d.DecodeFloat(false)
+ case *[]uint8:
+ *v = d.d.DecodeBytes(*v, false, false)
+
+ case *interface{}:
+ d.decodeValueNotNil(reflect.ValueOf(iv).Elem(), nil)
+
+ default:
+ if !fastpathDecodeTypeSwitch(iv, d) {
+ d.decodeI(iv, true, false, false, false)
+ }
+ }
+}
+
+func (d *Decoder) preDecodeValue(rv reflect.Value, tryNil bool) (rv2 reflect.Value, proceed bool) {
+ if tryNil && d.d.TryDecodeAsNil() {
+ // No need to check if a ptr, recursively, to determine
+ // whether to set value to nil.
+ // Just always set value to its zero type.
+ if rv.IsValid() { // rv.CanSet() // always settable, except it's invalid
+ rv.Set(reflect.Zero(rv.Type()))
+ }
+ return
+ }
+
+ // If stream is not containing a nil value, then we can deref to the base
+ // non-pointer value, and decode into that.
+ for rv.Kind() == reflect.Ptr {
+ if rv.IsNil() {
+ rv.Set(reflect.New(rv.Type().Elem()))
+ }
+ rv = rv.Elem()
+ }
+ return rv, true
+}
+
+func (d *Decoder) decodeI(iv interface{}, checkPtr, tryNil, checkFastpath, checkCodecSelfer bool) {
+ rv := reflect.ValueOf(iv)
+ if checkPtr {
+ d.chkPtrValue(rv)
+ }
+ rv, proceed := d.preDecodeValue(rv, tryNil)
+ if proceed {
+ fn := d.getDecFn(rv.Type(), checkFastpath, checkCodecSelfer)
+ fn.f(&fn.i, rv)
+ }
+}
+
+func (d *Decoder) decodeValue(rv reflect.Value, fn *decFn) {
+ if rv, proceed := d.preDecodeValue(rv, true); proceed {
+ if fn == nil {
+ fn = d.getDecFn(rv.Type(), true, true)
+ }
+ fn.f(&fn.i, rv)
+ }
+}
+
+func (d *Decoder) decodeValueNotNil(rv reflect.Value, fn *decFn) {
+ if rv, proceed := d.preDecodeValue(rv, false); proceed {
+ if fn == nil {
+ fn = d.getDecFn(rv.Type(), true, true)
+ }
+ fn.f(&fn.i, rv)
+ }
+}
+
+func (d *Decoder) getDecFn(rt reflect.Type, checkFastpath, checkCodecSelfer bool) (fn *decFn) {
+ rtid := reflect.ValueOf(rt).Pointer()
+
+ // retrieve or register a focus'ed function for this type
+ // to eliminate need to do the retrieval multiple times
+
+ // if d.f == nil && d.s == nil { debugf("---->Creating new dec f map for type: %v\n", rt) }
+ var ok bool
+ if useMapForCodecCache {
+ fn, ok = d.f[rtid]
+ } else {
+ for i := range d.s {
+ v := &(d.s[i])
+ if v.rtid == rtid {
+ fn, ok = &(v.fn), true
+ break
+ }
+ }
+ }
+ if ok {
+ return
+ }
+
+ if useMapForCodecCache {
+ if d.f == nil {
+ d.f = make(map[uintptr]*decFn, initCollectionCap)
+ }
+ fn = new(decFn)
+ d.f[rtid] = fn
+ } else {
+ if d.s == nil {
+ d.s = make([]decRtidFn, 0, initCollectionCap)
+ }
+ d.s = append(d.s, decRtidFn{rtid: rtid})
+ fn = &(d.s[len(d.s)-1]).fn
+ }
+
+ // debugf("\tCreating new dec fn for type: %v\n", rt)
+ ti := getTypeInfo(rtid, rt)
+ fi := &(fn.i)
+ fi.d = d
+ fi.ti = ti
+
+ // An extension can be registered for any type, regardless of the Kind
+ // (e.g. type BitSet int64, type MyStruct { / * unexported fields * / }, type X []int, etc.
+ //
+ // We can't check if it's an extension byte here first, because the user may have
+ // registered a pointer or non-pointer type, meaning we may have to recurse first
+ // before matching a mapped type, even though the extension byte is already detected.
+ //
+ // NOTE: if decoding into a nil interface{}, we return a non-nil
+ // value except even if the container registers a length of 0.
+ if checkCodecSelfer && ti.cs {
+ fn.f = (*decFnInfo).selferUnmarshal
+ } else if rtid == rawExtTypId {
+ fn.f = (*decFnInfo).rawExt
+ } else if d.d.IsBuiltinType(rtid) {
+ fn.f = (*decFnInfo).builtin
+ } else if xfFn := d.h.getExt(rtid); xfFn != nil {
+ fi.xfTag, fi.xfFn = xfFn.tag, xfFn.ext
+ fn.f = (*decFnInfo).ext
+ } else if supportMarshalInterfaces && d.be && ti.bunm {
+ fn.f = (*decFnInfo).binaryUnmarshal
+ } else if supportMarshalInterfaces && !d.be && d.js && ti.junm {
+ //If JSON, we should check JSONUnmarshal before textUnmarshal
+ fn.f = (*decFnInfo).jsonUnmarshal
+ } else if supportMarshalInterfaces && !d.be && ti.tunm {
+ fn.f = (*decFnInfo).textUnmarshal
+ } else {
+ rk := rt.Kind()
+ if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
+ if rt.PkgPath() == "" {
+ if idx := fastpathAV.index(rtid); idx != -1 {
+ fn.f = fastpathAV[idx].decfn
+ }
+ } else {
+ // use mapping for underlying type if there
+ ok = false
+ var rtu reflect.Type
+ if rk == reflect.Map {
+ rtu = reflect.MapOf(rt.Key(), rt.Elem())
+ } else {
+ rtu = reflect.SliceOf(rt.Elem())
+ }
+ rtuid := reflect.ValueOf(rtu).Pointer()
+ if idx := fastpathAV.index(rtuid); idx != -1 {
+ xfnf := fastpathAV[idx].decfn
+ xrt := fastpathAV[idx].rt
+ fn.f = func(xf *decFnInfo, xrv reflect.Value) {
+ // xfnf(xf, xrv.Convert(xrt))
+ xfnf(xf, xrv.Addr().Convert(reflect.PtrTo(xrt)).Elem())
+ }
+ }
+ }
+ }
+ if fn.f == nil {
+ switch rk {
+ case reflect.String:
+ fn.f = (*decFnInfo).kString
+ case reflect.Bool:
+ fn.f = (*decFnInfo).kBool
+ case reflect.Int:
+ fn.f = (*decFnInfo).kInt
+ case reflect.Int64:
+ fn.f = (*decFnInfo).kInt64
+ case reflect.Int32:
+ fn.f = (*decFnInfo).kInt32
+ case reflect.Int8:
+ fn.f = (*decFnInfo).kInt8
+ case reflect.Int16:
+ fn.f = (*decFnInfo).kInt16
+ case reflect.Float32:
+ fn.f = (*decFnInfo).kFloat32
+ case reflect.Float64:
+ fn.f = (*decFnInfo).kFloat64
+ case reflect.Uint8:
+ fn.f = (*decFnInfo).kUint8
+ case reflect.Uint64:
+ fn.f = (*decFnInfo).kUint64
+ case reflect.Uint:
+ fn.f = (*decFnInfo).kUint
+ case reflect.Uint32:
+ fn.f = (*decFnInfo).kUint32
+ case reflect.Uint16:
+ fn.f = (*decFnInfo).kUint16
+ // case reflect.Ptr:
+ // fn.f = (*decFnInfo).kPtr
+ case reflect.Uintptr:
+ fn.f = (*decFnInfo).kUintptr
+ case reflect.Interface:
+ fn.f = (*decFnInfo).kInterface
+ case reflect.Struct:
+ fn.f = (*decFnInfo).kStruct
+ case reflect.Chan:
+ fi.seq = seqTypeChan
+ fn.f = (*decFnInfo).kSlice
+ case reflect.Slice:
+ fi.seq = seqTypeSlice
+ fn.f = (*decFnInfo).kSlice
+ case reflect.Array:
+ fi.seq = seqTypeArray
+ fn.f = (*decFnInfo).kArray
+ case reflect.Map:
+ fn.f = (*decFnInfo).kMap
+ default:
+ fn.f = (*decFnInfo).kErr
+ }
+ }
+ }
+
+ return
+}
+
+func (d *Decoder) structFieldNotFound(index int, rvkencname string) {
+ if d.h.ErrorIfNoField {
+ if index >= 0 {
+ d.errorf("no matching struct field found when decoding stream array at index %v", index)
+ return
+ } else if rvkencname != "" {
+ d.errorf("no matching struct field found when decoding stream map with key %s", rvkencname)
+ return
+ }
+ }
+ d.swallow()
+}
+
+func (d *Decoder) arrayCannotExpand(sliceLen, streamLen int) {
+ if d.h.ErrorIfNoArrayExpand {
+ d.errorf("cannot expand array len during decode from %v to %v", sliceLen, streamLen)
+ }
+}
+
+func (d *Decoder) chkPtrValue(rv reflect.Value) {
+ // We can only decode into a non-nil pointer
+ if rv.Kind() == reflect.Ptr && !rv.IsNil() {
+ return
+ }
+ if !rv.IsValid() {
+ d.error(cannotDecodeIntoNilErr)
+ return
+ }
+ if !rv.CanInterface() {
+ d.errorf("cannot decode into a value without an interface: %v", rv)
+ return
+ }
+ rvi := rv.Interface()
+ d.errorf("cannot decode into non-pointer or nil pointer. Got: %v, %T, %v", rv.Kind(), rvi, rvi)
+}
+
+func (d *Decoder) error(err error) {
+ panic(err)
+}
+
+func (d *Decoder) errorf(format string, params ...interface{}) {
+ params2 := make([]interface{}, len(params)+1)
+ params2[0] = d.r.numread()
+ copy(params2[1:], params)
+ err := fmt.Errorf("[pos %d]: "+format, params2...)
+ panic(err)
+}
+
+// --------------------------------------------------
+
+// decSliceHelper assists when decoding into a slice, from a map or an array in the stream.
+// A slice can be set from a map or array in stream. This supports the MapBySlice interface.
+type decSliceHelper struct {
+ dd decDriver
+ ct valueType
+}
+
+func (d *Decoder) decSliceHelperStart() (x decSliceHelper, clen int) {
+ x.dd = d.d
+ if x.dd.IsContainerType(valueTypeArray) {
+ x.ct = valueTypeArray
+ clen = x.dd.ReadArrayStart()
+ } else if x.dd.IsContainerType(valueTypeMap) {
+ x.ct = valueTypeMap
+ clen = x.dd.ReadMapStart() * 2
+ } else {
+ d.errorf("only encoded map or array can be decoded into a slice")
+ }
+ return
+}
+
+func (x decSliceHelper) End() {
+ x.dd.ReadEnd()
+}
+
+func decByteSlice(r decReader, clen int, bs []byte) (bsOut []byte) {
+ if clen == 0 {
+ return zeroByteSlice
+ }
+ if len(bs) == clen {
+ bsOut = bs
+ } else if cap(bs) >= clen {
+ bsOut = bs[:clen]
+ } else {
+ bsOut = make([]byte, clen)
+ }
+ r.readb(bsOut)
+ return
+}
+
+func detachZeroCopyBytes(isBytesReader bool, dest []byte, in []byte) (out []byte) {
+ if xlen := len(in); xlen > 0 {
+ if isBytesReader || xlen <= scratchByteArrayLen {
+ if cap(dest) >= xlen {
+ out = dest[:xlen]
+ } else {
+ out = make([]byte, xlen)
+ }
+ copy(out, in)
+ return
+ }
+ }
+ return in
+}
+
+// decInferLen will infer a sensible length, given the following:
+// - clen: length wanted.
+// - maxlen: max length to be returned.
+// if <= 0, it is unset, and we infer it based on the unit size
+// - unit: number of bytes for each element of the collection
+func decInferLen(clen, maxlen, unit int) (rvlen int, truncated bool) {
+ // handle when maxlen is not set i.e. <= 0
+ if clen <= 0 {
+ return
+ }
+ if maxlen <= 0 {
+ // no maxlen defined. Use maximum of 256K memory, with a floor of 4K items.
+ // maxlen = 256 * 1024 / unit
+ // if maxlen < (4 * 1024) {
+ // maxlen = 4 * 1024
+ // }
+ if unit < (256 / 4) {
+ maxlen = 256 * 1024 / unit
+ } else {
+ maxlen = 4 * 1024
+ }
+ }
+ if clen > maxlen {
+ rvlen = maxlen
+ truncated = true
+ } else {
+ rvlen = clen
+ }
+ return
+ // if clen <= 0 {
+ // rvlen = 0
+ // } else if maxlen > 0 && clen > maxlen {
+ // rvlen = maxlen
+ // truncated = true
+ // } else {
+ // rvlen = clen
+ // }
+ // return
+}
+
+// // implement overall decReader wrapping both, for possible use inline:
+// type decReaderT struct {
+// bytes bool
+// rb *bytesDecReader
+// ri *ioDecReader
+// }
+//
+// // implement *Decoder as a decReader.
+// // Using decReaderT (defined just above) caused performance degradation
+// // possibly because of constant copying the value,
+// // and some value->interface conversion causing allocation.
+// func (d *Decoder) unreadn1() {
+// if d.bytes {
+// d.rb.unreadn1()
+// } else {
+// d.ri.unreadn1()
+// }
+// }
+// ... for other methods of decReader.
+// Testing showed that performance improvement was negligible.
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go
new file mode 100644
index 000000000..c5260f600
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/encode.go
@@ -0,0 +1,1197 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "bytes"
+ "encoding"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "sync"
+)
+
+const (
+ defEncByteBufSize = 1 << 6 // 4:16, 6:64, 8:256, 10:1024
+)
+
+// AsSymbolFlag defines what should be encoded as symbols.
+type AsSymbolFlag uint8
+
+const (
+ // AsSymbolDefault is default.
+ // Currently, this means only encode struct field names as symbols.
+ // The default is subject to change.
+ AsSymbolDefault AsSymbolFlag = iota
+
+ // AsSymbolAll means encode anything which could be a symbol as a symbol.
+ AsSymbolAll = 0xfe
+
+ // AsSymbolNone means do not encode anything as a symbol.
+ AsSymbolNone = 1 << iota
+
+ // AsSymbolMapStringKeys means encode keys in map[string]XXX as symbols.
+ AsSymbolMapStringKeysFlag
+
+ // AsSymbolStructFieldName means encode struct field names as symbols.
+ AsSymbolStructFieldNameFlag
+)
+
+// encWriter abstracts writing to a byte array or to an io.Writer.
+type encWriter interface {
+ writeb([]byte)
+ writestr(string)
+ writen1(byte)
+ writen2(byte, byte)
+ atEndOfEncode()
+}
+
+// encDriver abstracts the actual codec (binc vs msgpack, etc)
+type encDriver interface {
+ IsBuiltinType(rt uintptr) bool
+ EncodeBuiltin(rt uintptr, v interface{})
+ EncodeNil()
+ EncodeInt(i int64)
+ EncodeUint(i uint64)
+ EncodeBool(b bool)
+ EncodeFloat32(f float32)
+ EncodeFloat64(f float64)
+ // encodeExtPreamble(xtag byte, length int)
+ EncodeRawExt(re *RawExt, e *Encoder)
+ EncodeExt(v interface{}, xtag uint64, ext Ext, e *Encoder)
+ EncodeArrayStart(length int)
+ EncodeMapStart(length int)
+ EncodeEnd()
+ EncodeString(c charEncoding, v string)
+ EncodeSymbol(v string)
+ EncodeStringBytes(c charEncoding, v []byte)
+ //TODO
+ //encBignum(f *big.Int)
+ //encStringRunes(c charEncoding, v []rune)
+}
+
+type encDriverAsis interface {
+ EncodeAsis(v []byte)
+}
+
+type encNoSeparator struct{}
+
+func (_ encNoSeparator) EncodeEnd() {}
+
+type encStructFieldBytesV struct {
+ b []byte
+ v reflect.Value
+}
+
+type encStructFieldBytesVslice []encStructFieldBytesV
+
+func (p encStructFieldBytesVslice) Len() int { return len(p) }
+func (p encStructFieldBytesVslice) Less(i, j int) bool { return bytes.Compare(p[i].b, p[j].b) == -1 }
+func (p encStructFieldBytesVslice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+type ioEncWriterWriter interface {
+ WriteByte(c byte) error
+ WriteString(s string) (n int, err error)
+ Write(p []byte) (n int, err error)
+}
+
+type ioEncStringWriter interface {
+ WriteString(s string) (n int, err error)
+}
+
+type EncodeOptions struct {
+ // Encode a struct as an array, and not as a map
+ StructToArray bool
+
+ // Canonical representation means that encoding a value will always result in the same
+ // sequence of bytes.
+ //
+ // This only affects maps, as the iteration order for maps is random.
+ // In this case, the map keys will first be encoded into []byte, and then sorted,
+ // before writing the sorted keys and the corresponding map values to the stream.
+ Canonical bool
+
+ // AsSymbols defines what should be encoded as symbols.
+ //
+ // Encoding as symbols can reduce the encoded size significantly.
+ //
+ // However, during decoding, each string to be encoded as a symbol must
+ // be checked to see if it has been seen before. Consequently, encoding time
+ // will increase if using symbols, because string comparisons has a clear cost.
+ //
+ // Sample values:
+ // AsSymbolNone
+ // AsSymbolAll
+ // AsSymbolMapStringKeys
+ // AsSymbolMapStringKeysFlag | AsSymbolStructFieldNameFlag
+ AsSymbols AsSymbolFlag
+}
+
+// ---------------------------------------------
+
+type simpleIoEncWriterWriter struct {
+ w io.Writer
+ bw io.ByteWriter
+ sw ioEncStringWriter
+}
+
+func (o *simpleIoEncWriterWriter) WriteByte(c byte) (err error) {
+ if o.bw != nil {
+ return o.bw.WriteByte(c)
+ }
+ _, err = o.w.Write([]byte{c})
+ return
+}
+
+func (o *simpleIoEncWriterWriter) WriteString(s string) (n int, err error) {
+ if o.sw != nil {
+ return o.sw.WriteString(s)
+ }
+ // return o.w.Write([]byte(s))
+ return o.w.Write(bytesView(s))
+}
+
+func (o *simpleIoEncWriterWriter) Write(p []byte) (n int, err error) {
+ return o.w.Write(p)
+}
+
+// ----------------------------------------
+
+// ioEncWriter implements encWriter and can write to an io.Writer implementation
+type ioEncWriter struct {
+ w ioEncWriterWriter
+ // x [8]byte // temp byte array re-used internally for efficiency
+}
+
+func (z *ioEncWriter) writeb(bs []byte) {
+ if len(bs) == 0 {
+ return
+ }
+ n, err := z.w.Write(bs)
+ if err != nil {
+ panic(err)
+ }
+ if n != len(bs) {
+ panic(fmt.Errorf("incorrect num bytes written. Expecting: %v, Wrote: %v", len(bs), n))
+ }
+}
+
+func (z *ioEncWriter) writestr(s string) {
+ n, err := z.w.WriteString(s)
+ if err != nil {
+ panic(err)
+ }
+ if n != len(s) {
+ panic(fmt.Errorf("incorrect num bytes written. Expecting: %v, Wrote: %v", len(s), n))
+ }
+}
+
+func (z *ioEncWriter) writen1(b byte) {
+ if err := z.w.WriteByte(b); err != nil {
+ panic(err)
+ }
+}
+
+func (z *ioEncWriter) writen2(b1 byte, b2 byte) {
+ z.writen1(b1)
+ z.writen1(b2)
+}
+
+func (z *ioEncWriter) atEndOfEncode() {}
+
+// ----------------------------------------
+
+// bytesEncWriter implements encWriter and can write to an byte slice.
+// It is used by Marshal function.
+type bytesEncWriter struct {
+ b []byte
+ c int // cursor
+ out *[]byte // write out on atEndOfEncode
+}
+
+func (z *bytesEncWriter) writeb(s []byte) {
+ if len(s) > 0 {
+ c := z.grow(len(s))
+ copy(z.b[c:], s)
+ }
+}
+
+func (z *bytesEncWriter) writestr(s string) {
+ if len(s) > 0 {
+ c := z.grow(len(s))
+ copy(z.b[c:], s)
+ }
+}
+
+func (z *bytesEncWriter) writen1(b1 byte) {
+ c := z.grow(1)
+ z.b[c] = b1
+}
+
+func (z *bytesEncWriter) writen2(b1 byte, b2 byte) {
+ c := z.grow(2)
+ z.b[c] = b1
+ z.b[c+1] = b2
+}
+
+func (z *bytesEncWriter) atEndOfEncode() {
+ *(z.out) = z.b[:z.c]
+}
+
+func (z *bytesEncWriter) grow(n int) (oldcursor int) {
+ oldcursor = z.c
+ z.c = oldcursor + n
+ if z.c > len(z.b) {
+ if z.c > cap(z.b) {
+ // appendslice logic (if cap < 1024, *2, else *1.25): more expensive. many copy calls.
+ // bytes.Buffer model (2*cap + n): much better
+ // bs := make([]byte, 2*cap(z.b)+n)
+ bs := make([]byte, growCap(cap(z.b), 1, n))
+ copy(bs, z.b[:oldcursor])
+ z.b = bs
+ } else {
+ z.b = z.b[:cap(z.b)]
+ }
+ }
+ return
+}
+
+// ---------------------------------------------
+
+type encFnInfo struct {
+ e *Encoder
+ ti *typeInfo
+ xfFn Ext
+ xfTag uint64
+ seq seqType
+}
+
+func (f *encFnInfo) builtin(rv reflect.Value) {
+ f.e.e.EncodeBuiltin(f.ti.rtid, rv.Interface())
+}
+
+func (f *encFnInfo) rawExt(rv reflect.Value) {
+ // rev := rv.Interface().(RawExt)
+ // f.e.e.EncodeRawExt(&rev, f.e)
+ var re *RawExt
+ if rv.CanAddr() {
+ re = rv.Addr().Interface().(*RawExt)
+ } else {
+ rev := rv.Interface().(RawExt)
+ re = &rev
+ }
+ f.e.e.EncodeRawExt(re, f.e)
+}
+
+func (f *encFnInfo) ext(rv reflect.Value) {
+ // if this is a struct|array and it was addressable, then pass the address directly (not the value)
+ if k := rv.Kind(); (k == reflect.Struct || k == reflect.Array) && rv.CanAddr() {
+ rv = rv.Addr()
+ }
+ f.e.e.EncodeExt(rv.Interface(), f.xfTag, f.xfFn, f.e)
+}
+
+func (f *encFnInfo) getValueForMarshalInterface(rv reflect.Value, indir int8) (v interface{}, proceed bool) {
+ if indir == 0 {
+ v = rv.Interface()
+ } else if indir == -1 {
+ // If a non-pointer was passed to Encode(), then that value is not addressable.
+ // Take addr if addresable, else copy value to an addressable value.
+ if rv.CanAddr() {
+ v = rv.Addr().Interface()
+ } else {
+ rv2 := reflect.New(rv.Type())
+ rv2.Elem().Set(rv)
+ v = rv2.Interface()
+ // fmt.Printf("rv.Type: %v, rv2.Type: %v, v: %v\n", rv.Type(), rv2.Type(), v)
+ }
+ } else {
+ for j := int8(0); j < indir; j++ {
+ if rv.IsNil() {
+ f.e.e.EncodeNil()
+ return
+ }
+ rv = rv.Elem()
+ }
+ v = rv.Interface()
+ }
+ return v, true
+}
+
+func (f *encFnInfo) selferMarshal(rv reflect.Value) {
+ if v, proceed := f.getValueForMarshalInterface(rv, f.ti.csIndir); proceed {
+ v.(Selfer).CodecEncodeSelf(f.e)
+ }
+}
+
+func (f *encFnInfo) binaryMarshal(rv reflect.Value) {
+ if v, proceed := f.getValueForMarshalInterface(rv, f.ti.bmIndir); proceed {
+ bs, fnerr := v.(encoding.BinaryMarshaler).MarshalBinary()
+ f.e.marshal(bs, fnerr, false, c_RAW)
+ }
+}
+
+func (f *encFnInfo) textMarshal(rv reflect.Value) {
+ if v, proceed := f.getValueForMarshalInterface(rv, f.ti.tmIndir); proceed {
+ // debugf(">>>> encoding.TextMarshaler: %T", rv.Interface())
+ bs, fnerr := v.(encoding.TextMarshaler).MarshalText()
+ f.e.marshal(bs, fnerr, false, c_UTF8)
+ }
+}
+
+func (f *encFnInfo) jsonMarshal(rv reflect.Value) {
+ if v, proceed := f.getValueForMarshalInterface(rv, f.ti.jmIndir); proceed {
+ bs, fnerr := v.(jsonMarshaler).MarshalJSON()
+ f.e.marshal(bs, fnerr, true, c_UTF8)
+ }
+}
+
+func (f *encFnInfo) kBool(rv reflect.Value) {
+ f.e.e.EncodeBool(rv.Bool())
+}
+
+func (f *encFnInfo) kString(rv reflect.Value) {
+ f.e.e.EncodeString(c_UTF8, rv.String())
+}
+
+func (f *encFnInfo) kFloat64(rv reflect.Value) {
+ f.e.e.EncodeFloat64(rv.Float())
+}
+
+func (f *encFnInfo) kFloat32(rv reflect.Value) {
+ f.e.e.EncodeFloat32(float32(rv.Float()))
+}
+
+func (f *encFnInfo) kInt(rv reflect.Value) {
+ f.e.e.EncodeInt(rv.Int())
+}
+
+func (f *encFnInfo) kUint(rv reflect.Value) {
+ f.e.e.EncodeUint(rv.Uint())
+}
+
+func (f *encFnInfo) kInvalid(rv reflect.Value) {
+ f.e.e.EncodeNil()
+}
+
+func (f *encFnInfo) kErr(rv reflect.Value) {
+ f.e.errorf("unsupported kind %s, for %#v", rv.Kind(), rv)
+}
+
+func (f *encFnInfo) kSlice(rv reflect.Value) {
+ ti := f.ti
+ // array may be non-addressable, so we have to manage with care
+ // (don't call rv.Bytes, rv.Slice, etc).
+ // E.g. type struct S{B [2]byte};
+ // Encode(S{}) will bomb on "panic: slice of unaddressable array".
+ if f.seq != seqTypeArray {
+ if rv.IsNil() {
+ f.e.e.EncodeNil()
+ return
+ }
+ // If in this method, then there was no extension function defined.
+ // So it's okay to treat as []byte.
+ if ti.rtid == uint8SliceTypId {
+ f.e.e.EncodeStringBytes(c_RAW, rv.Bytes())
+ return
+ }
+ }
+ rtelem := ti.rt.Elem()
+ l := rv.Len()
+ if rtelem.Kind() == reflect.Uint8 {
+ switch f.seq {
+ case seqTypeArray:
+ // if l == 0 { f.e.e.encodeStringBytes(c_RAW, nil) } else
+ if rv.CanAddr() {
+ f.e.e.EncodeStringBytes(c_RAW, rv.Slice(0, l).Bytes())
+ } else {
+ var bs []byte
+ if l <= cap(f.e.b) {
+ bs = f.e.b[:l]
+ } else {
+ bs = make([]byte, l)
+ }
+ reflect.Copy(reflect.ValueOf(bs), rv)
+ // TODO: Test that reflect.Copy works instead of manual one-by-one
+ // for i := 0; i < l; i++ {
+ // bs[i] = byte(rv.Index(i).Uint())
+ // }
+ f.e.e.EncodeStringBytes(c_RAW, bs)
+ }
+ case seqTypeSlice:
+ f.e.e.EncodeStringBytes(c_RAW, rv.Bytes())
+ case seqTypeChan:
+ bs := f.e.b[:0]
+ // do not use range, so that the number of elements encoded
+ // does not change, and encoding does not hang waiting on someone to close chan.
+ // for b := range rv.Interface().(<-chan byte) {
+ // bs = append(bs, b)
+ // }
+ ch := rv.Interface().(<-chan byte)
+ for i := 0; i < l; i++ {
+ bs = append(bs, <-ch)
+ }
+ f.e.e.EncodeStringBytes(c_RAW, bs)
+ }
+ return
+ }
+
+ if ti.mbs {
+ if l%2 == 1 {
+ f.e.errorf("mapBySlice requires even slice length, but got %v", l)
+ return
+ }
+ f.e.e.EncodeMapStart(l / 2)
+ } else {
+ f.e.e.EncodeArrayStart(l)
+ }
+
+ e := f.e
+ if l > 0 {
+ for rtelem.Kind() == reflect.Ptr {
+ rtelem = rtelem.Elem()
+ }
+ // if kind is reflect.Interface, do not pre-determine the
+ // encoding type, because preEncodeValue may break it down to
+ // a concrete type and kInterface will bomb.
+ var fn *encFn
+ if rtelem.Kind() != reflect.Interface {
+ rtelemid := reflect.ValueOf(rtelem).Pointer()
+ fn = e.getEncFn(rtelemid, rtelem, true, true)
+ }
+ // TODO: Consider perf implication of encoding odd index values as symbols if type is string
+ for j := 0; j < l; j++ {
+ if f.seq == seqTypeChan {
+ if rv2, ok2 := rv.Recv(); ok2 {
+ e.encodeValue(rv2, fn)
+ }
+ } else {
+ e.encodeValue(rv.Index(j), fn)
+ }
+ }
+
+ }
+
+ f.e.e.EncodeEnd()
+}
+
+func (f *encFnInfo) kStruct(rv reflect.Value) {
+ fti := f.ti
+ e := f.e
+ tisfi := fti.sfip
+ toMap := !(fti.toArray || e.h.StructToArray)
+ newlen := len(fti.sfi)
+
+ // Use sync.Pool to reduce allocating slices unnecessarily.
+ // The cost of the occasional locking is less than the cost of locking.
+ pool, poolv, fkvs := encStructPoolGet(newlen)
+
+ // if toMap, use the sorted array. If toArray, use unsorted array (to match sequence in struct)
+ if toMap {
+ tisfi = fti.sfi
+ }
+ newlen = 0
+ var kv encStructFieldKV
+ for _, si := range tisfi {
+ kv.v = si.field(rv, false)
+ // if si.i != -1 {
+ // rvals[newlen] = rv.Field(int(si.i))
+ // } else {
+ // rvals[newlen] = rv.FieldByIndex(si.is)
+ // }
+ if toMap {
+ if si.omitEmpty && isEmptyValue(kv.v) {
+ continue
+ }
+ kv.k = si.encName
+ } else {
+ // use the zero value.
+ // if a reference or struct, set to nil (so you do not output too much)
+ if si.omitEmpty && isEmptyValue(kv.v) {
+ switch kv.v.Kind() {
+ case reflect.Struct, reflect.Interface, reflect.Ptr, reflect.Array,
+ reflect.Map, reflect.Slice:
+ kv.v = reflect.Value{} //encode as nil
+ }
+ }
+ }
+ fkvs[newlen] = kv
+ newlen++
+ }
+
+ // debugf(">>>> kStruct: newlen: %v", newlen)
+ // sep := !e.be
+ ee := f.e.e //don't dereference everytime
+
+ if toMap {
+ ee.EncodeMapStart(newlen)
+ // asSymbols := e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
+ asSymbols := e.h.AsSymbols == AsSymbolDefault || e.h.AsSymbols&AsSymbolStructFieldNameFlag != 0
+ for j := 0; j < newlen; j++ {
+ kv = fkvs[j]
+ if asSymbols {
+ ee.EncodeSymbol(kv.k)
+ } else {
+ ee.EncodeString(c_UTF8, kv.k)
+ }
+ e.encodeValue(kv.v, nil)
+ }
+ } else {
+ ee.EncodeArrayStart(newlen)
+ for j := 0; j < newlen; j++ {
+ kv = fkvs[j]
+ e.encodeValue(kv.v, nil)
+ }
+ }
+ ee.EncodeEnd()
+
+ // do not use defer. Instead, use explicit pool return at end of function.
+ // defer has a cost we are trying to avoid.
+ // If there is a panic and these slices are not returned, it is ok.
+ if pool != nil {
+ pool.Put(poolv)
+ }
+}
+
+// func (f *encFnInfo) kPtr(rv reflect.Value) {
+// debugf(">>>>>>> ??? encode kPtr called - shouldn't get called")
+// if rv.IsNil() {
+// f.e.e.encodeNil()
+// return
+// }
+// f.e.encodeValue(rv.Elem())
+// }
+
+func (f *encFnInfo) kInterface(rv reflect.Value) {
+ if rv.IsNil() {
+ f.e.e.EncodeNil()
+ return
+ }
+ f.e.encodeValue(rv.Elem(), nil)
+}
+
+func (f *encFnInfo) kMap(rv reflect.Value) {
+ ee := f.e.e
+ if rv.IsNil() {
+ ee.EncodeNil()
+ return
+ }
+
+ l := rv.Len()
+ ee.EncodeMapStart(l)
+ e := f.e
+ if l == 0 {
+ ee.EncodeEnd()
+ return
+ }
+ var asSymbols bool
+ // determine the underlying key and val encFn's for the map.
+ // This eliminates some work which is done for each loop iteration i.e.
+ // rv.Type(), ref.ValueOf(rt).Pointer(), then check map/list for fn.
+ //
+ // However, if kind is reflect.Interface, do not pre-determine the
+ // encoding type, because preEncodeValue may break it down to
+ // a concrete type and kInterface will bomb.
+ var keyFn, valFn *encFn
+ ti := f.ti
+ rtkey := ti.rt.Key()
+ rtval := ti.rt.Elem()
+ rtkeyid := reflect.ValueOf(rtkey).Pointer()
+ // keyTypeIsString := f.ti.rt.Key().Kind() == reflect.String
+ var keyTypeIsString = rtkeyid == stringTypId
+ if keyTypeIsString {
+ asSymbols = e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ } else {
+ for rtkey.Kind() == reflect.Ptr {
+ rtkey = rtkey.Elem()
+ }
+ if rtkey.Kind() != reflect.Interface {
+ rtkeyid = reflect.ValueOf(rtkey).Pointer()
+ keyFn = e.getEncFn(rtkeyid, rtkey, true, true)
+ }
+ }
+ for rtval.Kind() == reflect.Ptr {
+ rtval = rtval.Elem()
+ }
+ if rtval.Kind() != reflect.Interface {
+ rtvalid := reflect.ValueOf(rtval).Pointer()
+ valFn = e.getEncFn(rtvalid, rtval, true, true)
+ }
+ mks := rv.MapKeys()
+ // for j, lmks := 0, len(mks); j < lmks; j++ {
+ if e.h.Canonical {
+ // first encode each key to a []byte first, then sort them, then record
+ // println(">>>>>>>> CANONICAL <<<<<<<<")
+ var mksv []byte = make([]byte, 0, len(mks)*16) // temporary byte slice for the encoding
+ e2 := NewEncoderBytes(&mksv, e.hh)
+ mksbv := make([]encStructFieldBytesV, len(mks))
+ for i, k := range mks {
+ l := len(mksv)
+ e2.MustEncode(k)
+ mksbv[i].v = k
+ mksbv[i].b = mksv[l:]
+ // fmt.Printf(">>>>> %s\n", mksv[l:])
+ }
+ sort.Sort(encStructFieldBytesVslice(mksbv))
+ for j := range mksbv {
+ e.asis(mksbv[j].b)
+ e.encodeValue(rv.MapIndex(mksbv[j].v), valFn)
+ }
+ } else {
+ for j := range mks {
+ if keyTypeIsString {
+ if asSymbols {
+ ee.EncodeSymbol(mks[j].String())
+ } else {
+ ee.EncodeString(c_UTF8, mks[j].String())
+ }
+ } else {
+ e.encodeValue(mks[j], keyFn)
+ }
+ e.encodeValue(rv.MapIndex(mks[j]), valFn)
+ }
+ }
+ ee.EncodeEnd()
+}
+
+// --------------------------------------------------
+
+// encFn encapsulates the captured variables and the encode function.
+// This way, we only do some calculations one times, and pass to the
+// code block that should be called (encapsulated in a function)
+// instead of executing the checks every time.
+type encFn struct {
+ i encFnInfo
+ f func(*encFnInfo, reflect.Value)
+}
+
+// --------------------------------------------------
+
+type encRtidFn struct {
+ rtid uintptr
+ fn encFn
+}
+
+// An Encoder writes an object to an output stream in the codec format.
+type Encoder struct {
+ // hopefully, reduce derefencing cost by laying the encWriter inside the Encoder
+ e encDriver
+ // NOTE: Encoder shouldn't call it's write methods,
+ // as the handler MAY need to do some coordination.
+ w encWriter
+ s []encRtidFn
+ be bool // is binary encoding
+ js bool // is json handle
+
+ wi ioEncWriter
+ wb bytesEncWriter
+ h *BasicHandle
+
+ as encDriverAsis
+ hh Handle
+ f map[uintptr]*encFn
+ b [scratchByteArrayLen]byte
+}
+
+// NewEncoder returns an Encoder for encoding into an io.Writer.
+//
+// For efficiency, Users are encouraged to pass in a memory buffered writer
+// (eg bufio.Writer, bytes.Buffer).
+func NewEncoder(w io.Writer, h Handle) *Encoder {
+ e := &Encoder{hh: h, h: h.getBasicHandle(), be: h.isBinary()}
+ ww, ok := w.(ioEncWriterWriter)
+ if !ok {
+ sww := simpleIoEncWriterWriter{w: w}
+ sww.bw, _ = w.(io.ByteWriter)
+ sww.sw, _ = w.(ioEncStringWriter)
+ ww = &sww
+ //ww = bufio.NewWriterSize(w, defEncByteBufSize)
+ }
+ e.wi.w = ww
+ e.w = &e.wi
+ _, e.js = h.(*JsonHandle)
+ e.e = h.newEncDriver(e)
+ e.as, _ = e.e.(encDriverAsis)
+ return e
+}
+
+// NewEncoderBytes returns an encoder for encoding directly and efficiently
+// into a byte slice, using zero-copying to temporary slices.
+//
+// It will potentially replace the output byte slice pointed to.
+// After encoding, the out parameter contains the encoded contents.
+func NewEncoderBytes(out *[]byte, h Handle) *Encoder {
+ e := &Encoder{hh: h, h: h.getBasicHandle(), be: h.isBinary()}
+ in := *out
+ if in == nil {
+ in = make([]byte, defEncByteBufSize)
+ }
+ e.wb.b, e.wb.out = in, out
+ e.w = &e.wb
+ _, e.js = h.(*JsonHandle)
+ e.e = h.newEncDriver(e)
+ e.as, _ = e.e.(encDriverAsis)
+ return e
+}
+
+// Encode writes an object into a stream.
+//
+// Encoding can be configured via the struct tag for the fields.
+// The "codec" key in struct field's tag value is the key name,
+// followed by an optional comma and options.
+// Note that the "json" key is used in the absence of the "codec" key.
+//
+// To set an option on all fields (e.g. omitempty on all fields), you
+// can create a field called _struct, and set flags on it.
+//
+// Struct values "usually" encode as maps. Each exported struct field is encoded unless:
+// - the field's tag is "-", OR
+// - the field is empty (empty or the zero value) and its tag specifies the "omitempty" option.
+//
+// When encoding as a map, the first string in the tag (before the comma)
+// is the map key string to use when encoding.
+//
+// However, struct values may encode as arrays. This happens when:
+// - StructToArray Encode option is set, OR
+// - the tag on the _struct field sets the "toarray" option
+//
+// Values with types that implement MapBySlice are encoded as stream maps.
+//
+// The empty values (for omitempty option) are false, 0, any nil pointer
+// or interface value, and any array, slice, map, or string of length zero.
+//
+// Anonymous fields are encoded inline except:
+// - the struct tag specifies a replacement name (first value)
+// - the field is of an interface type
+//
+// Examples:
+//
+// // NOTE: 'json:' can be used as struct tag key, in place 'codec:' below.
+// type MyStruct struct {
+// _struct bool `codec:",omitempty"` //set omitempty for every field
+// Field1 string `codec:"-"` //skip this field
+// Field2 int `codec:"myName"` //Use key "myName" in encode stream
+// Field3 int32 `codec:",omitempty"` //use key "Field3". Omit if empty.
+// Field4 bool `codec:"f4,omitempty"` //use key "f4". Omit if empty.
+// io.Reader //use key "Reader".
+// MyStruct `codec:"my1" //use key "my1".
+// MyStruct //inline it
+// ...
+// }
+//
+// type MyStruct struct {
+// _struct bool `codec:",omitempty,toarray"` //set omitempty for every field
+// //and encode struct as an array
+// }
+//
+// The mode of encoding is based on the type of the value. When a value is seen:
+// - If a Selfer, call its CodecEncodeSelf method
+// - If an extension is registered for it, call that extension function
+// - If it implements encoding.(Binary|Text|JSON)Marshaler, call its Marshal(Binary|Text|JSON) method
+// - Else encode it based on its reflect.Kind
+//
+// Note that struct field names and keys in map[string]XXX will be treated as symbols.
+// Some formats support symbols (e.g. binc) and will properly encode the string
+// only once in the stream, and use a tag to refer to it thereafter.
+func (e *Encoder) Encode(v interface{}) (err error) {
+ defer panicToErr(&err)
+ e.encode(v)
+ e.w.atEndOfEncode()
+ return
+}
+
+// MustEncode is like Encode, but panics if unable to Encode.
+// This provides insight to the code location that triggered the error.
+func (e *Encoder) MustEncode(v interface{}) {
+ e.encode(v)
+ e.w.atEndOfEncode()
+}
+
+// comment out these (Must)Write methods. They were only put there to support cbor.
+// However, users already have access to the streams, and can write directly.
+//
+// // Write allows users write to the Encoder stream directly.
+// func (e *Encoder) Write(bs []byte) (err error) {
+// defer panicToErr(&err)
+// e.w.writeb(bs)
+// return
+// }
+// // MustWrite is like write, but panics if unable to Write.
+// func (e *Encoder) MustWrite(bs []byte) {
+// e.w.writeb(bs)
+// }
+
+func (e *Encoder) encode(iv interface{}) {
+ // if ics, ok := iv.(Selfer); ok {
+ // ics.CodecEncodeSelf(e)
+ // return
+ // }
+
+ switch v := iv.(type) {
+ case nil:
+ e.e.EncodeNil()
+ case Selfer:
+ v.CodecEncodeSelf(e)
+
+ case reflect.Value:
+ e.encodeValue(v, nil)
+
+ case string:
+ e.e.EncodeString(c_UTF8, v)
+ case bool:
+ e.e.EncodeBool(v)
+ case int:
+ e.e.EncodeInt(int64(v))
+ case int8:
+ e.e.EncodeInt(int64(v))
+ case int16:
+ e.e.EncodeInt(int64(v))
+ case int32:
+ e.e.EncodeInt(int64(v))
+ case int64:
+ e.e.EncodeInt(v)
+ case uint:
+ e.e.EncodeUint(uint64(v))
+ case uint8:
+ e.e.EncodeUint(uint64(v))
+ case uint16:
+ e.e.EncodeUint(uint64(v))
+ case uint32:
+ e.e.EncodeUint(uint64(v))
+ case uint64:
+ e.e.EncodeUint(v)
+ case float32:
+ e.e.EncodeFloat32(v)
+ case float64:
+ e.e.EncodeFloat64(v)
+
+ case []uint8:
+ e.e.EncodeStringBytes(c_RAW, v)
+
+ case *string:
+ e.e.EncodeString(c_UTF8, *v)
+ case *bool:
+ e.e.EncodeBool(*v)
+ case *int:
+ e.e.EncodeInt(int64(*v))
+ case *int8:
+ e.e.EncodeInt(int64(*v))
+ case *int16:
+ e.e.EncodeInt(int64(*v))
+ case *int32:
+ e.e.EncodeInt(int64(*v))
+ case *int64:
+ e.e.EncodeInt(*v)
+ case *uint:
+ e.e.EncodeUint(uint64(*v))
+ case *uint8:
+ e.e.EncodeUint(uint64(*v))
+ case *uint16:
+ e.e.EncodeUint(uint64(*v))
+ case *uint32:
+ e.e.EncodeUint(uint64(*v))
+ case *uint64:
+ e.e.EncodeUint(*v)
+ case *float32:
+ e.e.EncodeFloat32(*v)
+ case *float64:
+ e.e.EncodeFloat64(*v)
+
+ case *[]uint8:
+ e.e.EncodeStringBytes(c_RAW, *v)
+
+ default:
+ // canonical mode is not supported for fastpath of maps (but is fine for slices)
+ const checkCodecSelfer1 = true // in case T is passed, where *T is a Selfer, still checkCodecSelfer
+ if e.h.Canonical {
+ if !fastpathEncodeTypeSwitchSlice(iv, e) {
+ e.encodeI(iv, false, checkCodecSelfer1)
+ }
+ } else {
+ if !fastpathEncodeTypeSwitch(iv, e) {
+ e.encodeI(iv, false, checkCodecSelfer1)
+ }
+ }
+ }
+}
+
+func (e *Encoder) encodeI(iv interface{}, checkFastpath, checkCodecSelfer bool) {
+ if rv, proceed := e.preEncodeValue(reflect.ValueOf(iv)); proceed {
+ rt := rv.Type()
+ rtid := reflect.ValueOf(rt).Pointer()
+ fn := e.getEncFn(rtid, rt, checkFastpath, checkCodecSelfer)
+ fn.f(&fn.i, rv)
+ }
+}
+
+func (e *Encoder) preEncodeValue(rv reflect.Value) (rv2 reflect.Value, proceed bool) {
+LOOP:
+ for {
+ switch rv.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ if rv.IsNil() {
+ e.e.EncodeNil()
+ return
+ }
+ rv = rv.Elem()
+ continue LOOP
+ case reflect.Slice, reflect.Map:
+ if rv.IsNil() {
+ e.e.EncodeNil()
+ return
+ }
+ case reflect.Invalid, reflect.Func:
+ e.e.EncodeNil()
+ return
+ }
+ break
+ }
+
+ return rv, true
+}
+
+func (e *Encoder) encodeValue(rv reflect.Value, fn *encFn) {
+ // if a valid fn is passed, it MUST BE for the dereferenced type of rv
+ if rv, proceed := e.preEncodeValue(rv); proceed {
+ if fn == nil {
+ rt := rv.Type()
+ rtid := reflect.ValueOf(rt).Pointer()
+ fn = e.getEncFn(rtid, rt, true, true)
+ }
+ fn.f(&fn.i, rv)
+ }
+}
+
+func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCodecSelfer bool) (fn *encFn) {
+ // rtid := reflect.ValueOf(rt).Pointer()
+ var ok bool
+ if useMapForCodecCache {
+ fn, ok = e.f[rtid]
+ } else {
+ for i := range e.s {
+ v := &(e.s[i])
+ if v.rtid == rtid {
+ fn, ok = &(v.fn), true
+ break
+ }
+ }
+ }
+ if ok {
+ return
+ }
+
+ if useMapForCodecCache {
+ if e.f == nil {
+ e.f = make(map[uintptr]*encFn, initCollectionCap)
+ }
+ fn = new(encFn)
+ e.f[rtid] = fn
+ } else {
+ if e.s == nil {
+ e.s = make([]encRtidFn, 0, initCollectionCap)
+ }
+ e.s = append(e.s, encRtidFn{rtid: rtid})
+ fn = &(e.s[len(e.s)-1]).fn
+ }
+
+ ti := getTypeInfo(rtid, rt)
+ fi := &(fn.i)
+ fi.e = e
+ fi.ti = ti
+
+ if checkCodecSelfer && ti.cs {
+ fn.f = (*encFnInfo).selferMarshal
+ } else if rtid == rawExtTypId {
+ fn.f = (*encFnInfo).rawExt
+ } else if e.e.IsBuiltinType(rtid) {
+ fn.f = (*encFnInfo).builtin
+ } else if xfFn := e.h.getExt(rtid); xfFn != nil {
+ fi.xfTag, fi.xfFn = xfFn.tag, xfFn.ext
+ fn.f = (*encFnInfo).ext
+ } else if supportMarshalInterfaces && e.be && ti.bm {
+ fn.f = (*encFnInfo).binaryMarshal
+ } else if supportMarshalInterfaces && !e.be && e.js && ti.jm {
+ //If JSON, we should check JSONMarshal before textMarshal
+ fn.f = (*encFnInfo).jsonMarshal
+ } else if supportMarshalInterfaces && !e.be && ti.tm {
+ fn.f = (*encFnInfo).textMarshal
+ } else {
+ rk := rt.Kind()
+ // if fastpathEnabled && checkFastpath && (rk == reflect.Map || rk == reflect.Slice) {
+ if fastpathEnabled && checkFastpath && (rk == reflect.Slice || (rk == reflect.Map && !e.h.Canonical)) {
+ if rt.PkgPath() == "" {
+ if idx := fastpathAV.index(rtid); idx != -1 {
+ fn.f = fastpathAV[idx].encfn
+ }
+ } else {
+ ok = false
+ // use mapping for underlying type if there
+ var rtu reflect.Type
+ if rk == reflect.Map {
+ rtu = reflect.MapOf(rt.Key(), rt.Elem())
+ } else {
+ rtu = reflect.SliceOf(rt.Elem())
+ }
+ rtuid := reflect.ValueOf(rtu).Pointer()
+ if idx := fastpathAV.index(rtuid); idx != -1 {
+ xfnf := fastpathAV[idx].encfn
+ xrt := fastpathAV[idx].rt
+ fn.f = func(xf *encFnInfo, xrv reflect.Value) {
+ xfnf(xf, xrv.Convert(xrt))
+ }
+ }
+ }
+ }
+ if fn.f == nil {
+ switch rk {
+ case reflect.Bool:
+ fn.f = (*encFnInfo).kBool
+ case reflect.String:
+ fn.f = (*encFnInfo).kString
+ case reflect.Float64:
+ fn.f = (*encFnInfo).kFloat64
+ case reflect.Float32:
+ fn.f = (*encFnInfo).kFloat32
+ case reflect.Int, reflect.Int8, reflect.Int64, reflect.Int32, reflect.Int16:
+ fn.f = (*encFnInfo).kInt
+ case reflect.Uint8, reflect.Uint64, reflect.Uint, reflect.Uint32, reflect.Uint16, reflect.Uintptr:
+ fn.f = (*encFnInfo).kUint
+ case reflect.Invalid:
+ fn.f = (*encFnInfo).kInvalid
+ case reflect.Chan:
+ fi.seq = seqTypeChan
+ fn.f = (*encFnInfo).kSlice
+ case reflect.Slice:
+ fi.seq = seqTypeSlice
+ fn.f = (*encFnInfo).kSlice
+ case reflect.Array:
+ fi.seq = seqTypeArray
+ fn.f = (*encFnInfo).kSlice
+ case reflect.Struct:
+ fn.f = (*encFnInfo).kStruct
+ // case reflect.Ptr:
+ // fn.f = (*encFnInfo).kPtr
+ case reflect.Interface:
+ fn.f = (*encFnInfo).kInterface
+ case reflect.Map:
+ fn.f = (*encFnInfo).kMap
+ default:
+ fn.f = (*encFnInfo).kErr
+ }
+ }
+ }
+
+ return
+}
+
+func (e *Encoder) marshal(bs []byte, fnerr error, asis bool, c charEncoding) {
+ if fnerr != nil {
+ panic(fnerr)
+ }
+ if bs == nil {
+ e.e.EncodeNil()
+ } else if asis {
+ e.asis(bs)
+ } else {
+ e.e.EncodeStringBytes(c, bs)
+ }
+}
+
+func (e *Encoder) asis(v []byte) {
+ if e.as == nil {
+ e.w.writeb(v)
+ } else {
+ e.as.EncodeAsis(v)
+ }
+}
+
+func (e *Encoder) errorf(format string, params ...interface{}) {
+ err := fmt.Errorf(format, params...)
+ panic(err)
+}
+
+// ----------------------------------------
+
+type encStructFieldKV struct {
+ k string
+ v reflect.Value
+}
+
+const encStructPoolLen = 5
+
+// encStructPool is an array of sync.Pool.
+// Each element of the array pools one of encStructPool(8|16|32|64).
+// It allows the re-use of slices up to 64 in length.
+// A performance cost of encoding structs was collecting
+// which values were empty and should be omitted.
+// We needed slices of reflect.Value and string to collect them.
+// This shared pool reduces the amount of unnecessary creation we do.
+// The cost is that of locking sometimes, but sync.Pool is efficient
+// enough to reduce thread contention.
+var encStructPool [encStructPoolLen]sync.Pool
+
+func init() {
+ encStructPool[0].New = func() interface{} { return new([8]encStructFieldKV) }
+ encStructPool[1].New = func() interface{} { return new([16]encStructFieldKV) }
+ encStructPool[2].New = func() interface{} { return new([32]encStructFieldKV) }
+ encStructPool[3].New = func() interface{} { return new([64]encStructFieldKV) }
+ encStructPool[4].New = func() interface{} { return new([128]encStructFieldKV) }
+}
+
+func encStructPoolGet(newlen int) (p *sync.Pool, v interface{}, s []encStructFieldKV) {
+ // if encStructPoolLen != 5 { // constant chec, so removed at build time.
+ // panic(errors.New("encStructPoolLen must be equal to 4")) // defensive, in case it is changed
+ // }
+ // idxpool := newlen / 8
+
+ // if pool == nil {
+ // fkvs = make([]encStructFieldKV, newlen)
+ // } else {
+ // poolv = pool.Get()
+ // switch vv := poolv.(type) {
+ // case *[8]encStructFieldKV:
+ // fkvs = vv[:newlen]
+ // case *[16]encStructFieldKV:
+ // fkvs = vv[:newlen]
+ // case *[32]encStructFieldKV:
+ // fkvs = vv[:newlen]
+ // case *[64]encStructFieldKV:
+ // fkvs = vv[:newlen]
+ // case *[128]encStructFieldKV:
+ // fkvs = vv[:newlen]
+ // }
+ // }
+
+ if newlen <= 8 {
+ p = &encStructPool[0]
+ v = p.Get()
+ s = v.(*[8]encStructFieldKV)[:newlen]
+ } else if newlen <= 16 {
+ p = &encStructPool[1]
+ v = p.Get()
+ s = v.(*[16]encStructFieldKV)[:newlen]
+ } else if newlen <= 32 {
+ p = &encStructPool[2]
+ v = p.Get()
+ s = v.(*[32]encStructFieldKV)[:newlen]
+ } else if newlen <= 64 {
+ p = &encStructPool[3]
+ v = p.Get()
+ s = v.(*[64]encStructFieldKV)[:newlen]
+ } else if newlen <= 128 {
+ p = &encStructPool[4]
+ v = p.Get()
+ s = v.(*[128]encStructFieldKV)[:newlen]
+ } else {
+ s = make([]encStructFieldKV, newlen)
+ }
+ return
+}
+
+// ----------------------------------------
+
+// func encErr(format string, params ...interface{}) {
+// doPanic(msgTagEnc, format, params...)
+// }
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go
new file mode 100644
index 000000000..554e80b61
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.generated.go
@@ -0,0 +1,23677 @@
+// //+build ignore
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED from fast-path.go.tmpl
+// ************************************************************
+
+package codec
+
+// Fast path functions try to create a fast path encode or decode implementation
+// for common maps and slices.
+//
+// We define the functions and register then in this single file
+// so as not to pollute the encode.go and decode.go, and create a dependency in there.
+// This file can be omitted without causing a build failure.
+//
+// The advantage of fast paths is:
+// - Many calls bypass reflection altogether
+//
+// Currently support
+// - slice of all builtin types,
+// - map of all builtin types to string or interface value
+// - symetrical maps of all builtin types (e.g. str-str, uint8-uint8)
+// This should provide adequate "typical" implementations.
+//
+// Note that fast track decode functions must handle values for which an address cannot be obtained.
+// For example:
+// m2 := map[string]int{}
+// p2 := []interface{}{m2}
+// // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
+//
+
+import (
+ "reflect"
+ "sort"
+)
+
+const fastpathCheckNilFalse = false // for reflect
+const fastpathCheckNilTrue = true // for type switch
+
+type fastpathT struct{}
+
+var fastpathTV fastpathT
+
+type fastpathE struct {
+ rtid uintptr
+ rt reflect.Type
+ encfn func(*encFnInfo, reflect.Value)
+ decfn func(*decFnInfo, reflect.Value)
+}
+
+type fastpathA [239]fastpathE
+
+func (x *fastpathA) index(rtid uintptr) int {
+ // use binary search to grab the index (adapted from sort/search.go)
+ h, i, j := 0, 0, 239 // len(x)
+ for i < j {
+ h = i + (j-i)/2
+ if x[h].rtid < rtid {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ if i < 239 && x[i].rtid == rtid {
+ return i
+ }
+ return -1
+}
+
+type fastpathAslice []fastpathE
+
+func (x fastpathAslice) Len() int { return len(x) }
+func (x fastpathAslice) Less(i, j int) bool { return x[i].rtid < x[j].rtid }
+func (x fastpathAslice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+var fastpathAV fastpathA
+
+// due to possible initialization loop error, make fastpath in an init()
+func init() {
+ if !fastpathEnabled {
+ return
+ }
+ i := 0
+ fn := func(v interface{}, fe func(*encFnInfo, reflect.Value), fd func(*decFnInfo, reflect.Value)) (f fastpathE) {
+ xrt := reflect.TypeOf(v)
+ xptr := reflect.ValueOf(xrt).Pointer()
+ fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
+ i++
+ return
+ }
+
+ fn([]interface{}(nil), (*encFnInfo).fastpathEncSliceIntfR, (*decFnInfo).fastpathDecSliceIntfR)
+ fn([]string(nil), (*encFnInfo).fastpathEncSliceStringR, (*decFnInfo).fastpathDecSliceStringR)
+ fn([]float32(nil), (*encFnInfo).fastpathEncSliceFloat32R, (*decFnInfo).fastpathDecSliceFloat32R)
+ fn([]float64(nil), (*encFnInfo).fastpathEncSliceFloat64R, (*decFnInfo).fastpathDecSliceFloat64R)
+ fn([]uint(nil), (*encFnInfo).fastpathEncSliceUintR, (*decFnInfo).fastpathDecSliceUintR)
+ fn([]uint16(nil), (*encFnInfo).fastpathEncSliceUint16R, (*decFnInfo).fastpathDecSliceUint16R)
+ fn([]uint32(nil), (*encFnInfo).fastpathEncSliceUint32R, (*decFnInfo).fastpathDecSliceUint32R)
+ fn([]uint64(nil), (*encFnInfo).fastpathEncSliceUint64R, (*decFnInfo).fastpathDecSliceUint64R)
+ fn([]int(nil), (*encFnInfo).fastpathEncSliceIntR, (*decFnInfo).fastpathDecSliceIntR)
+ fn([]int8(nil), (*encFnInfo).fastpathEncSliceInt8R, (*decFnInfo).fastpathDecSliceInt8R)
+ fn([]int16(nil), (*encFnInfo).fastpathEncSliceInt16R, (*decFnInfo).fastpathDecSliceInt16R)
+ fn([]int32(nil), (*encFnInfo).fastpathEncSliceInt32R, (*decFnInfo).fastpathDecSliceInt32R)
+ fn([]int64(nil), (*encFnInfo).fastpathEncSliceInt64R, (*decFnInfo).fastpathDecSliceInt64R)
+ fn([]bool(nil), (*encFnInfo).fastpathEncSliceBoolR, (*decFnInfo).fastpathDecSliceBoolR)
+
+ fn(map[interface{}]interface{}(nil), (*encFnInfo).fastpathEncMapIntfIntfR, (*decFnInfo).fastpathDecMapIntfIntfR)
+ fn(map[interface{}]string(nil), (*encFnInfo).fastpathEncMapIntfStringR, (*decFnInfo).fastpathDecMapIntfStringR)
+ fn(map[interface{}]uint(nil), (*encFnInfo).fastpathEncMapIntfUintR, (*decFnInfo).fastpathDecMapIntfUintR)
+ fn(map[interface{}]uint8(nil), (*encFnInfo).fastpathEncMapIntfUint8R, (*decFnInfo).fastpathDecMapIntfUint8R)
+ fn(map[interface{}]uint16(nil), (*encFnInfo).fastpathEncMapIntfUint16R, (*decFnInfo).fastpathDecMapIntfUint16R)
+ fn(map[interface{}]uint32(nil), (*encFnInfo).fastpathEncMapIntfUint32R, (*decFnInfo).fastpathDecMapIntfUint32R)
+ fn(map[interface{}]uint64(nil), (*encFnInfo).fastpathEncMapIntfUint64R, (*decFnInfo).fastpathDecMapIntfUint64R)
+ fn(map[interface{}]int(nil), (*encFnInfo).fastpathEncMapIntfIntR, (*decFnInfo).fastpathDecMapIntfIntR)
+ fn(map[interface{}]int8(nil), (*encFnInfo).fastpathEncMapIntfInt8R, (*decFnInfo).fastpathDecMapIntfInt8R)
+ fn(map[interface{}]int16(nil), (*encFnInfo).fastpathEncMapIntfInt16R, (*decFnInfo).fastpathDecMapIntfInt16R)
+ fn(map[interface{}]int32(nil), (*encFnInfo).fastpathEncMapIntfInt32R, (*decFnInfo).fastpathDecMapIntfInt32R)
+ fn(map[interface{}]int64(nil), (*encFnInfo).fastpathEncMapIntfInt64R, (*decFnInfo).fastpathDecMapIntfInt64R)
+ fn(map[interface{}]float32(nil), (*encFnInfo).fastpathEncMapIntfFloat32R, (*decFnInfo).fastpathDecMapIntfFloat32R)
+ fn(map[interface{}]float64(nil), (*encFnInfo).fastpathEncMapIntfFloat64R, (*decFnInfo).fastpathDecMapIntfFloat64R)
+ fn(map[interface{}]bool(nil), (*encFnInfo).fastpathEncMapIntfBoolR, (*decFnInfo).fastpathDecMapIntfBoolR)
+ fn(map[string]interface{}(nil), (*encFnInfo).fastpathEncMapStringIntfR, (*decFnInfo).fastpathDecMapStringIntfR)
+ fn(map[string]string(nil), (*encFnInfo).fastpathEncMapStringStringR, (*decFnInfo).fastpathDecMapStringStringR)
+ fn(map[string]uint(nil), (*encFnInfo).fastpathEncMapStringUintR, (*decFnInfo).fastpathDecMapStringUintR)
+ fn(map[string]uint8(nil), (*encFnInfo).fastpathEncMapStringUint8R, (*decFnInfo).fastpathDecMapStringUint8R)
+ fn(map[string]uint16(nil), (*encFnInfo).fastpathEncMapStringUint16R, (*decFnInfo).fastpathDecMapStringUint16R)
+ fn(map[string]uint32(nil), (*encFnInfo).fastpathEncMapStringUint32R, (*decFnInfo).fastpathDecMapStringUint32R)
+ fn(map[string]uint64(nil), (*encFnInfo).fastpathEncMapStringUint64R, (*decFnInfo).fastpathDecMapStringUint64R)
+ fn(map[string]int(nil), (*encFnInfo).fastpathEncMapStringIntR, (*decFnInfo).fastpathDecMapStringIntR)
+ fn(map[string]int8(nil), (*encFnInfo).fastpathEncMapStringInt8R, (*decFnInfo).fastpathDecMapStringInt8R)
+ fn(map[string]int16(nil), (*encFnInfo).fastpathEncMapStringInt16R, (*decFnInfo).fastpathDecMapStringInt16R)
+ fn(map[string]int32(nil), (*encFnInfo).fastpathEncMapStringInt32R, (*decFnInfo).fastpathDecMapStringInt32R)
+ fn(map[string]int64(nil), (*encFnInfo).fastpathEncMapStringInt64R, (*decFnInfo).fastpathDecMapStringInt64R)
+ fn(map[string]float32(nil), (*encFnInfo).fastpathEncMapStringFloat32R, (*decFnInfo).fastpathDecMapStringFloat32R)
+ fn(map[string]float64(nil), (*encFnInfo).fastpathEncMapStringFloat64R, (*decFnInfo).fastpathDecMapStringFloat64R)
+ fn(map[string]bool(nil), (*encFnInfo).fastpathEncMapStringBoolR, (*decFnInfo).fastpathDecMapStringBoolR)
+ fn(map[float32]interface{}(nil), (*encFnInfo).fastpathEncMapFloat32IntfR, (*decFnInfo).fastpathDecMapFloat32IntfR)
+ fn(map[float32]string(nil), (*encFnInfo).fastpathEncMapFloat32StringR, (*decFnInfo).fastpathDecMapFloat32StringR)
+ fn(map[float32]uint(nil), (*encFnInfo).fastpathEncMapFloat32UintR, (*decFnInfo).fastpathDecMapFloat32UintR)
+ fn(map[float32]uint8(nil), (*encFnInfo).fastpathEncMapFloat32Uint8R, (*decFnInfo).fastpathDecMapFloat32Uint8R)
+ fn(map[float32]uint16(nil), (*encFnInfo).fastpathEncMapFloat32Uint16R, (*decFnInfo).fastpathDecMapFloat32Uint16R)
+ fn(map[float32]uint32(nil), (*encFnInfo).fastpathEncMapFloat32Uint32R, (*decFnInfo).fastpathDecMapFloat32Uint32R)
+ fn(map[float32]uint64(nil), (*encFnInfo).fastpathEncMapFloat32Uint64R, (*decFnInfo).fastpathDecMapFloat32Uint64R)
+ fn(map[float32]int(nil), (*encFnInfo).fastpathEncMapFloat32IntR, (*decFnInfo).fastpathDecMapFloat32IntR)
+ fn(map[float32]int8(nil), (*encFnInfo).fastpathEncMapFloat32Int8R, (*decFnInfo).fastpathDecMapFloat32Int8R)
+ fn(map[float32]int16(nil), (*encFnInfo).fastpathEncMapFloat32Int16R, (*decFnInfo).fastpathDecMapFloat32Int16R)
+ fn(map[float32]int32(nil), (*encFnInfo).fastpathEncMapFloat32Int32R, (*decFnInfo).fastpathDecMapFloat32Int32R)
+ fn(map[float32]int64(nil), (*encFnInfo).fastpathEncMapFloat32Int64R, (*decFnInfo).fastpathDecMapFloat32Int64R)
+ fn(map[float32]float32(nil), (*encFnInfo).fastpathEncMapFloat32Float32R, (*decFnInfo).fastpathDecMapFloat32Float32R)
+ fn(map[float32]float64(nil), (*encFnInfo).fastpathEncMapFloat32Float64R, (*decFnInfo).fastpathDecMapFloat32Float64R)
+ fn(map[float32]bool(nil), (*encFnInfo).fastpathEncMapFloat32BoolR, (*decFnInfo).fastpathDecMapFloat32BoolR)
+ fn(map[float64]interface{}(nil), (*encFnInfo).fastpathEncMapFloat64IntfR, (*decFnInfo).fastpathDecMapFloat64IntfR)
+ fn(map[float64]string(nil), (*encFnInfo).fastpathEncMapFloat64StringR, (*decFnInfo).fastpathDecMapFloat64StringR)
+ fn(map[float64]uint(nil), (*encFnInfo).fastpathEncMapFloat64UintR, (*decFnInfo).fastpathDecMapFloat64UintR)
+ fn(map[float64]uint8(nil), (*encFnInfo).fastpathEncMapFloat64Uint8R, (*decFnInfo).fastpathDecMapFloat64Uint8R)
+ fn(map[float64]uint16(nil), (*encFnInfo).fastpathEncMapFloat64Uint16R, (*decFnInfo).fastpathDecMapFloat64Uint16R)
+ fn(map[float64]uint32(nil), (*encFnInfo).fastpathEncMapFloat64Uint32R, (*decFnInfo).fastpathDecMapFloat64Uint32R)
+ fn(map[float64]uint64(nil), (*encFnInfo).fastpathEncMapFloat64Uint64R, (*decFnInfo).fastpathDecMapFloat64Uint64R)
+ fn(map[float64]int(nil), (*encFnInfo).fastpathEncMapFloat64IntR, (*decFnInfo).fastpathDecMapFloat64IntR)
+ fn(map[float64]int8(nil), (*encFnInfo).fastpathEncMapFloat64Int8R, (*decFnInfo).fastpathDecMapFloat64Int8R)
+ fn(map[float64]int16(nil), (*encFnInfo).fastpathEncMapFloat64Int16R, (*decFnInfo).fastpathDecMapFloat64Int16R)
+ fn(map[float64]int32(nil), (*encFnInfo).fastpathEncMapFloat64Int32R, (*decFnInfo).fastpathDecMapFloat64Int32R)
+ fn(map[float64]int64(nil), (*encFnInfo).fastpathEncMapFloat64Int64R, (*decFnInfo).fastpathDecMapFloat64Int64R)
+ fn(map[float64]float32(nil), (*encFnInfo).fastpathEncMapFloat64Float32R, (*decFnInfo).fastpathDecMapFloat64Float32R)
+ fn(map[float64]float64(nil), (*encFnInfo).fastpathEncMapFloat64Float64R, (*decFnInfo).fastpathDecMapFloat64Float64R)
+ fn(map[float64]bool(nil), (*encFnInfo).fastpathEncMapFloat64BoolR, (*decFnInfo).fastpathDecMapFloat64BoolR)
+ fn(map[uint]interface{}(nil), (*encFnInfo).fastpathEncMapUintIntfR, (*decFnInfo).fastpathDecMapUintIntfR)
+ fn(map[uint]string(nil), (*encFnInfo).fastpathEncMapUintStringR, (*decFnInfo).fastpathDecMapUintStringR)
+ fn(map[uint]uint(nil), (*encFnInfo).fastpathEncMapUintUintR, (*decFnInfo).fastpathDecMapUintUintR)
+ fn(map[uint]uint8(nil), (*encFnInfo).fastpathEncMapUintUint8R, (*decFnInfo).fastpathDecMapUintUint8R)
+ fn(map[uint]uint16(nil), (*encFnInfo).fastpathEncMapUintUint16R, (*decFnInfo).fastpathDecMapUintUint16R)
+ fn(map[uint]uint32(nil), (*encFnInfo).fastpathEncMapUintUint32R, (*decFnInfo).fastpathDecMapUintUint32R)
+ fn(map[uint]uint64(nil), (*encFnInfo).fastpathEncMapUintUint64R, (*decFnInfo).fastpathDecMapUintUint64R)
+ fn(map[uint]int(nil), (*encFnInfo).fastpathEncMapUintIntR, (*decFnInfo).fastpathDecMapUintIntR)
+ fn(map[uint]int8(nil), (*encFnInfo).fastpathEncMapUintInt8R, (*decFnInfo).fastpathDecMapUintInt8R)
+ fn(map[uint]int16(nil), (*encFnInfo).fastpathEncMapUintInt16R, (*decFnInfo).fastpathDecMapUintInt16R)
+ fn(map[uint]int32(nil), (*encFnInfo).fastpathEncMapUintInt32R, (*decFnInfo).fastpathDecMapUintInt32R)
+ fn(map[uint]int64(nil), (*encFnInfo).fastpathEncMapUintInt64R, (*decFnInfo).fastpathDecMapUintInt64R)
+ fn(map[uint]float32(nil), (*encFnInfo).fastpathEncMapUintFloat32R, (*decFnInfo).fastpathDecMapUintFloat32R)
+ fn(map[uint]float64(nil), (*encFnInfo).fastpathEncMapUintFloat64R, (*decFnInfo).fastpathDecMapUintFloat64R)
+ fn(map[uint]bool(nil), (*encFnInfo).fastpathEncMapUintBoolR, (*decFnInfo).fastpathDecMapUintBoolR)
+ fn(map[uint8]interface{}(nil), (*encFnInfo).fastpathEncMapUint8IntfR, (*decFnInfo).fastpathDecMapUint8IntfR)
+ fn(map[uint8]string(nil), (*encFnInfo).fastpathEncMapUint8StringR, (*decFnInfo).fastpathDecMapUint8StringR)
+ fn(map[uint8]uint(nil), (*encFnInfo).fastpathEncMapUint8UintR, (*decFnInfo).fastpathDecMapUint8UintR)
+ fn(map[uint8]uint8(nil), (*encFnInfo).fastpathEncMapUint8Uint8R, (*decFnInfo).fastpathDecMapUint8Uint8R)
+ fn(map[uint8]uint16(nil), (*encFnInfo).fastpathEncMapUint8Uint16R, (*decFnInfo).fastpathDecMapUint8Uint16R)
+ fn(map[uint8]uint32(nil), (*encFnInfo).fastpathEncMapUint8Uint32R, (*decFnInfo).fastpathDecMapUint8Uint32R)
+ fn(map[uint8]uint64(nil), (*encFnInfo).fastpathEncMapUint8Uint64R, (*decFnInfo).fastpathDecMapUint8Uint64R)
+ fn(map[uint8]int(nil), (*encFnInfo).fastpathEncMapUint8IntR, (*decFnInfo).fastpathDecMapUint8IntR)
+ fn(map[uint8]int8(nil), (*encFnInfo).fastpathEncMapUint8Int8R, (*decFnInfo).fastpathDecMapUint8Int8R)
+ fn(map[uint8]int16(nil), (*encFnInfo).fastpathEncMapUint8Int16R, (*decFnInfo).fastpathDecMapUint8Int16R)
+ fn(map[uint8]int32(nil), (*encFnInfo).fastpathEncMapUint8Int32R, (*decFnInfo).fastpathDecMapUint8Int32R)
+ fn(map[uint8]int64(nil), (*encFnInfo).fastpathEncMapUint8Int64R, (*decFnInfo).fastpathDecMapUint8Int64R)
+ fn(map[uint8]float32(nil), (*encFnInfo).fastpathEncMapUint8Float32R, (*decFnInfo).fastpathDecMapUint8Float32R)
+ fn(map[uint8]float64(nil), (*encFnInfo).fastpathEncMapUint8Float64R, (*decFnInfo).fastpathDecMapUint8Float64R)
+ fn(map[uint8]bool(nil), (*encFnInfo).fastpathEncMapUint8BoolR, (*decFnInfo).fastpathDecMapUint8BoolR)
+ fn(map[uint16]interface{}(nil), (*encFnInfo).fastpathEncMapUint16IntfR, (*decFnInfo).fastpathDecMapUint16IntfR)
+ fn(map[uint16]string(nil), (*encFnInfo).fastpathEncMapUint16StringR, (*decFnInfo).fastpathDecMapUint16StringR)
+ fn(map[uint16]uint(nil), (*encFnInfo).fastpathEncMapUint16UintR, (*decFnInfo).fastpathDecMapUint16UintR)
+ fn(map[uint16]uint8(nil), (*encFnInfo).fastpathEncMapUint16Uint8R, (*decFnInfo).fastpathDecMapUint16Uint8R)
+ fn(map[uint16]uint16(nil), (*encFnInfo).fastpathEncMapUint16Uint16R, (*decFnInfo).fastpathDecMapUint16Uint16R)
+ fn(map[uint16]uint32(nil), (*encFnInfo).fastpathEncMapUint16Uint32R, (*decFnInfo).fastpathDecMapUint16Uint32R)
+ fn(map[uint16]uint64(nil), (*encFnInfo).fastpathEncMapUint16Uint64R, (*decFnInfo).fastpathDecMapUint16Uint64R)
+ fn(map[uint16]int(nil), (*encFnInfo).fastpathEncMapUint16IntR, (*decFnInfo).fastpathDecMapUint16IntR)
+ fn(map[uint16]int8(nil), (*encFnInfo).fastpathEncMapUint16Int8R, (*decFnInfo).fastpathDecMapUint16Int8R)
+ fn(map[uint16]int16(nil), (*encFnInfo).fastpathEncMapUint16Int16R, (*decFnInfo).fastpathDecMapUint16Int16R)
+ fn(map[uint16]int32(nil), (*encFnInfo).fastpathEncMapUint16Int32R, (*decFnInfo).fastpathDecMapUint16Int32R)
+ fn(map[uint16]int64(nil), (*encFnInfo).fastpathEncMapUint16Int64R, (*decFnInfo).fastpathDecMapUint16Int64R)
+ fn(map[uint16]float32(nil), (*encFnInfo).fastpathEncMapUint16Float32R, (*decFnInfo).fastpathDecMapUint16Float32R)
+ fn(map[uint16]float64(nil), (*encFnInfo).fastpathEncMapUint16Float64R, (*decFnInfo).fastpathDecMapUint16Float64R)
+ fn(map[uint16]bool(nil), (*encFnInfo).fastpathEncMapUint16BoolR, (*decFnInfo).fastpathDecMapUint16BoolR)
+ fn(map[uint32]interface{}(nil), (*encFnInfo).fastpathEncMapUint32IntfR, (*decFnInfo).fastpathDecMapUint32IntfR)
+ fn(map[uint32]string(nil), (*encFnInfo).fastpathEncMapUint32StringR, (*decFnInfo).fastpathDecMapUint32StringR)
+ fn(map[uint32]uint(nil), (*encFnInfo).fastpathEncMapUint32UintR, (*decFnInfo).fastpathDecMapUint32UintR)
+ fn(map[uint32]uint8(nil), (*encFnInfo).fastpathEncMapUint32Uint8R, (*decFnInfo).fastpathDecMapUint32Uint8R)
+ fn(map[uint32]uint16(nil), (*encFnInfo).fastpathEncMapUint32Uint16R, (*decFnInfo).fastpathDecMapUint32Uint16R)
+ fn(map[uint32]uint32(nil), (*encFnInfo).fastpathEncMapUint32Uint32R, (*decFnInfo).fastpathDecMapUint32Uint32R)
+ fn(map[uint32]uint64(nil), (*encFnInfo).fastpathEncMapUint32Uint64R, (*decFnInfo).fastpathDecMapUint32Uint64R)
+ fn(map[uint32]int(nil), (*encFnInfo).fastpathEncMapUint32IntR, (*decFnInfo).fastpathDecMapUint32IntR)
+ fn(map[uint32]int8(nil), (*encFnInfo).fastpathEncMapUint32Int8R, (*decFnInfo).fastpathDecMapUint32Int8R)
+ fn(map[uint32]int16(nil), (*encFnInfo).fastpathEncMapUint32Int16R, (*decFnInfo).fastpathDecMapUint32Int16R)
+ fn(map[uint32]int32(nil), (*encFnInfo).fastpathEncMapUint32Int32R, (*decFnInfo).fastpathDecMapUint32Int32R)
+ fn(map[uint32]int64(nil), (*encFnInfo).fastpathEncMapUint32Int64R, (*decFnInfo).fastpathDecMapUint32Int64R)
+ fn(map[uint32]float32(nil), (*encFnInfo).fastpathEncMapUint32Float32R, (*decFnInfo).fastpathDecMapUint32Float32R)
+ fn(map[uint32]float64(nil), (*encFnInfo).fastpathEncMapUint32Float64R, (*decFnInfo).fastpathDecMapUint32Float64R)
+ fn(map[uint32]bool(nil), (*encFnInfo).fastpathEncMapUint32BoolR, (*decFnInfo).fastpathDecMapUint32BoolR)
+ fn(map[uint64]interface{}(nil), (*encFnInfo).fastpathEncMapUint64IntfR, (*decFnInfo).fastpathDecMapUint64IntfR)
+ fn(map[uint64]string(nil), (*encFnInfo).fastpathEncMapUint64StringR, (*decFnInfo).fastpathDecMapUint64StringR)
+ fn(map[uint64]uint(nil), (*encFnInfo).fastpathEncMapUint64UintR, (*decFnInfo).fastpathDecMapUint64UintR)
+ fn(map[uint64]uint8(nil), (*encFnInfo).fastpathEncMapUint64Uint8R, (*decFnInfo).fastpathDecMapUint64Uint8R)
+ fn(map[uint64]uint16(nil), (*encFnInfo).fastpathEncMapUint64Uint16R, (*decFnInfo).fastpathDecMapUint64Uint16R)
+ fn(map[uint64]uint32(nil), (*encFnInfo).fastpathEncMapUint64Uint32R, (*decFnInfo).fastpathDecMapUint64Uint32R)
+ fn(map[uint64]uint64(nil), (*encFnInfo).fastpathEncMapUint64Uint64R, (*decFnInfo).fastpathDecMapUint64Uint64R)
+ fn(map[uint64]int(nil), (*encFnInfo).fastpathEncMapUint64IntR, (*decFnInfo).fastpathDecMapUint64IntR)
+ fn(map[uint64]int8(nil), (*encFnInfo).fastpathEncMapUint64Int8R, (*decFnInfo).fastpathDecMapUint64Int8R)
+ fn(map[uint64]int16(nil), (*encFnInfo).fastpathEncMapUint64Int16R, (*decFnInfo).fastpathDecMapUint64Int16R)
+ fn(map[uint64]int32(nil), (*encFnInfo).fastpathEncMapUint64Int32R, (*decFnInfo).fastpathDecMapUint64Int32R)
+ fn(map[uint64]int64(nil), (*encFnInfo).fastpathEncMapUint64Int64R, (*decFnInfo).fastpathDecMapUint64Int64R)
+ fn(map[uint64]float32(nil), (*encFnInfo).fastpathEncMapUint64Float32R, (*decFnInfo).fastpathDecMapUint64Float32R)
+ fn(map[uint64]float64(nil), (*encFnInfo).fastpathEncMapUint64Float64R, (*decFnInfo).fastpathDecMapUint64Float64R)
+ fn(map[uint64]bool(nil), (*encFnInfo).fastpathEncMapUint64BoolR, (*decFnInfo).fastpathDecMapUint64BoolR)
+ fn(map[int]interface{}(nil), (*encFnInfo).fastpathEncMapIntIntfR, (*decFnInfo).fastpathDecMapIntIntfR)
+ fn(map[int]string(nil), (*encFnInfo).fastpathEncMapIntStringR, (*decFnInfo).fastpathDecMapIntStringR)
+ fn(map[int]uint(nil), (*encFnInfo).fastpathEncMapIntUintR, (*decFnInfo).fastpathDecMapIntUintR)
+ fn(map[int]uint8(nil), (*encFnInfo).fastpathEncMapIntUint8R, (*decFnInfo).fastpathDecMapIntUint8R)
+ fn(map[int]uint16(nil), (*encFnInfo).fastpathEncMapIntUint16R, (*decFnInfo).fastpathDecMapIntUint16R)
+ fn(map[int]uint32(nil), (*encFnInfo).fastpathEncMapIntUint32R, (*decFnInfo).fastpathDecMapIntUint32R)
+ fn(map[int]uint64(nil), (*encFnInfo).fastpathEncMapIntUint64R, (*decFnInfo).fastpathDecMapIntUint64R)
+ fn(map[int]int(nil), (*encFnInfo).fastpathEncMapIntIntR, (*decFnInfo).fastpathDecMapIntIntR)
+ fn(map[int]int8(nil), (*encFnInfo).fastpathEncMapIntInt8R, (*decFnInfo).fastpathDecMapIntInt8R)
+ fn(map[int]int16(nil), (*encFnInfo).fastpathEncMapIntInt16R, (*decFnInfo).fastpathDecMapIntInt16R)
+ fn(map[int]int32(nil), (*encFnInfo).fastpathEncMapIntInt32R, (*decFnInfo).fastpathDecMapIntInt32R)
+ fn(map[int]int64(nil), (*encFnInfo).fastpathEncMapIntInt64R, (*decFnInfo).fastpathDecMapIntInt64R)
+ fn(map[int]float32(nil), (*encFnInfo).fastpathEncMapIntFloat32R, (*decFnInfo).fastpathDecMapIntFloat32R)
+ fn(map[int]float64(nil), (*encFnInfo).fastpathEncMapIntFloat64R, (*decFnInfo).fastpathDecMapIntFloat64R)
+ fn(map[int]bool(nil), (*encFnInfo).fastpathEncMapIntBoolR, (*decFnInfo).fastpathDecMapIntBoolR)
+ fn(map[int8]interface{}(nil), (*encFnInfo).fastpathEncMapInt8IntfR, (*decFnInfo).fastpathDecMapInt8IntfR)
+ fn(map[int8]string(nil), (*encFnInfo).fastpathEncMapInt8StringR, (*decFnInfo).fastpathDecMapInt8StringR)
+ fn(map[int8]uint(nil), (*encFnInfo).fastpathEncMapInt8UintR, (*decFnInfo).fastpathDecMapInt8UintR)
+ fn(map[int8]uint8(nil), (*encFnInfo).fastpathEncMapInt8Uint8R, (*decFnInfo).fastpathDecMapInt8Uint8R)
+ fn(map[int8]uint16(nil), (*encFnInfo).fastpathEncMapInt8Uint16R, (*decFnInfo).fastpathDecMapInt8Uint16R)
+ fn(map[int8]uint32(nil), (*encFnInfo).fastpathEncMapInt8Uint32R, (*decFnInfo).fastpathDecMapInt8Uint32R)
+ fn(map[int8]uint64(nil), (*encFnInfo).fastpathEncMapInt8Uint64R, (*decFnInfo).fastpathDecMapInt8Uint64R)
+ fn(map[int8]int(nil), (*encFnInfo).fastpathEncMapInt8IntR, (*decFnInfo).fastpathDecMapInt8IntR)
+ fn(map[int8]int8(nil), (*encFnInfo).fastpathEncMapInt8Int8R, (*decFnInfo).fastpathDecMapInt8Int8R)
+ fn(map[int8]int16(nil), (*encFnInfo).fastpathEncMapInt8Int16R, (*decFnInfo).fastpathDecMapInt8Int16R)
+ fn(map[int8]int32(nil), (*encFnInfo).fastpathEncMapInt8Int32R, (*decFnInfo).fastpathDecMapInt8Int32R)
+ fn(map[int8]int64(nil), (*encFnInfo).fastpathEncMapInt8Int64R, (*decFnInfo).fastpathDecMapInt8Int64R)
+ fn(map[int8]float32(nil), (*encFnInfo).fastpathEncMapInt8Float32R, (*decFnInfo).fastpathDecMapInt8Float32R)
+ fn(map[int8]float64(nil), (*encFnInfo).fastpathEncMapInt8Float64R, (*decFnInfo).fastpathDecMapInt8Float64R)
+ fn(map[int8]bool(nil), (*encFnInfo).fastpathEncMapInt8BoolR, (*decFnInfo).fastpathDecMapInt8BoolR)
+ fn(map[int16]interface{}(nil), (*encFnInfo).fastpathEncMapInt16IntfR, (*decFnInfo).fastpathDecMapInt16IntfR)
+ fn(map[int16]string(nil), (*encFnInfo).fastpathEncMapInt16StringR, (*decFnInfo).fastpathDecMapInt16StringR)
+ fn(map[int16]uint(nil), (*encFnInfo).fastpathEncMapInt16UintR, (*decFnInfo).fastpathDecMapInt16UintR)
+ fn(map[int16]uint8(nil), (*encFnInfo).fastpathEncMapInt16Uint8R, (*decFnInfo).fastpathDecMapInt16Uint8R)
+ fn(map[int16]uint16(nil), (*encFnInfo).fastpathEncMapInt16Uint16R, (*decFnInfo).fastpathDecMapInt16Uint16R)
+ fn(map[int16]uint32(nil), (*encFnInfo).fastpathEncMapInt16Uint32R, (*decFnInfo).fastpathDecMapInt16Uint32R)
+ fn(map[int16]uint64(nil), (*encFnInfo).fastpathEncMapInt16Uint64R, (*decFnInfo).fastpathDecMapInt16Uint64R)
+ fn(map[int16]int(nil), (*encFnInfo).fastpathEncMapInt16IntR, (*decFnInfo).fastpathDecMapInt16IntR)
+ fn(map[int16]int8(nil), (*encFnInfo).fastpathEncMapInt16Int8R, (*decFnInfo).fastpathDecMapInt16Int8R)
+ fn(map[int16]int16(nil), (*encFnInfo).fastpathEncMapInt16Int16R, (*decFnInfo).fastpathDecMapInt16Int16R)
+ fn(map[int16]int32(nil), (*encFnInfo).fastpathEncMapInt16Int32R, (*decFnInfo).fastpathDecMapInt16Int32R)
+ fn(map[int16]int64(nil), (*encFnInfo).fastpathEncMapInt16Int64R, (*decFnInfo).fastpathDecMapInt16Int64R)
+ fn(map[int16]float32(nil), (*encFnInfo).fastpathEncMapInt16Float32R, (*decFnInfo).fastpathDecMapInt16Float32R)
+ fn(map[int16]float64(nil), (*encFnInfo).fastpathEncMapInt16Float64R, (*decFnInfo).fastpathDecMapInt16Float64R)
+ fn(map[int16]bool(nil), (*encFnInfo).fastpathEncMapInt16BoolR, (*decFnInfo).fastpathDecMapInt16BoolR)
+ fn(map[int32]interface{}(nil), (*encFnInfo).fastpathEncMapInt32IntfR, (*decFnInfo).fastpathDecMapInt32IntfR)
+ fn(map[int32]string(nil), (*encFnInfo).fastpathEncMapInt32StringR, (*decFnInfo).fastpathDecMapInt32StringR)
+ fn(map[int32]uint(nil), (*encFnInfo).fastpathEncMapInt32UintR, (*decFnInfo).fastpathDecMapInt32UintR)
+ fn(map[int32]uint8(nil), (*encFnInfo).fastpathEncMapInt32Uint8R, (*decFnInfo).fastpathDecMapInt32Uint8R)
+ fn(map[int32]uint16(nil), (*encFnInfo).fastpathEncMapInt32Uint16R, (*decFnInfo).fastpathDecMapInt32Uint16R)
+ fn(map[int32]uint32(nil), (*encFnInfo).fastpathEncMapInt32Uint32R, (*decFnInfo).fastpathDecMapInt32Uint32R)
+ fn(map[int32]uint64(nil), (*encFnInfo).fastpathEncMapInt32Uint64R, (*decFnInfo).fastpathDecMapInt32Uint64R)
+ fn(map[int32]int(nil), (*encFnInfo).fastpathEncMapInt32IntR, (*decFnInfo).fastpathDecMapInt32IntR)
+ fn(map[int32]int8(nil), (*encFnInfo).fastpathEncMapInt32Int8R, (*decFnInfo).fastpathDecMapInt32Int8R)
+ fn(map[int32]int16(nil), (*encFnInfo).fastpathEncMapInt32Int16R, (*decFnInfo).fastpathDecMapInt32Int16R)
+ fn(map[int32]int32(nil), (*encFnInfo).fastpathEncMapInt32Int32R, (*decFnInfo).fastpathDecMapInt32Int32R)
+ fn(map[int32]int64(nil), (*encFnInfo).fastpathEncMapInt32Int64R, (*decFnInfo).fastpathDecMapInt32Int64R)
+ fn(map[int32]float32(nil), (*encFnInfo).fastpathEncMapInt32Float32R, (*decFnInfo).fastpathDecMapInt32Float32R)
+ fn(map[int32]float64(nil), (*encFnInfo).fastpathEncMapInt32Float64R, (*decFnInfo).fastpathDecMapInt32Float64R)
+ fn(map[int32]bool(nil), (*encFnInfo).fastpathEncMapInt32BoolR, (*decFnInfo).fastpathDecMapInt32BoolR)
+ fn(map[int64]interface{}(nil), (*encFnInfo).fastpathEncMapInt64IntfR, (*decFnInfo).fastpathDecMapInt64IntfR)
+ fn(map[int64]string(nil), (*encFnInfo).fastpathEncMapInt64StringR, (*decFnInfo).fastpathDecMapInt64StringR)
+ fn(map[int64]uint(nil), (*encFnInfo).fastpathEncMapInt64UintR, (*decFnInfo).fastpathDecMapInt64UintR)
+ fn(map[int64]uint8(nil), (*encFnInfo).fastpathEncMapInt64Uint8R, (*decFnInfo).fastpathDecMapInt64Uint8R)
+ fn(map[int64]uint16(nil), (*encFnInfo).fastpathEncMapInt64Uint16R, (*decFnInfo).fastpathDecMapInt64Uint16R)
+ fn(map[int64]uint32(nil), (*encFnInfo).fastpathEncMapInt64Uint32R, (*decFnInfo).fastpathDecMapInt64Uint32R)
+ fn(map[int64]uint64(nil), (*encFnInfo).fastpathEncMapInt64Uint64R, (*decFnInfo).fastpathDecMapInt64Uint64R)
+ fn(map[int64]int(nil), (*encFnInfo).fastpathEncMapInt64IntR, (*decFnInfo).fastpathDecMapInt64IntR)
+ fn(map[int64]int8(nil), (*encFnInfo).fastpathEncMapInt64Int8R, (*decFnInfo).fastpathDecMapInt64Int8R)
+ fn(map[int64]int16(nil), (*encFnInfo).fastpathEncMapInt64Int16R, (*decFnInfo).fastpathDecMapInt64Int16R)
+ fn(map[int64]int32(nil), (*encFnInfo).fastpathEncMapInt64Int32R, (*decFnInfo).fastpathDecMapInt64Int32R)
+ fn(map[int64]int64(nil), (*encFnInfo).fastpathEncMapInt64Int64R, (*decFnInfo).fastpathDecMapInt64Int64R)
+ fn(map[int64]float32(nil), (*encFnInfo).fastpathEncMapInt64Float32R, (*decFnInfo).fastpathDecMapInt64Float32R)
+ fn(map[int64]float64(nil), (*encFnInfo).fastpathEncMapInt64Float64R, (*decFnInfo).fastpathDecMapInt64Float64R)
+ fn(map[int64]bool(nil), (*encFnInfo).fastpathEncMapInt64BoolR, (*decFnInfo).fastpathDecMapInt64BoolR)
+ fn(map[bool]interface{}(nil), (*encFnInfo).fastpathEncMapBoolIntfR, (*decFnInfo).fastpathDecMapBoolIntfR)
+ fn(map[bool]string(nil), (*encFnInfo).fastpathEncMapBoolStringR, (*decFnInfo).fastpathDecMapBoolStringR)
+ fn(map[bool]uint(nil), (*encFnInfo).fastpathEncMapBoolUintR, (*decFnInfo).fastpathDecMapBoolUintR)
+ fn(map[bool]uint8(nil), (*encFnInfo).fastpathEncMapBoolUint8R, (*decFnInfo).fastpathDecMapBoolUint8R)
+ fn(map[bool]uint16(nil), (*encFnInfo).fastpathEncMapBoolUint16R, (*decFnInfo).fastpathDecMapBoolUint16R)
+ fn(map[bool]uint32(nil), (*encFnInfo).fastpathEncMapBoolUint32R, (*decFnInfo).fastpathDecMapBoolUint32R)
+ fn(map[bool]uint64(nil), (*encFnInfo).fastpathEncMapBoolUint64R, (*decFnInfo).fastpathDecMapBoolUint64R)
+ fn(map[bool]int(nil), (*encFnInfo).fastpathEncMapBoolIntR, (*decFnInfo).fastpathDecMapBoolIntR)
+ fn(map[bool]int8(nil), (*encFnInfo).fastpathEncMapBoolInt8R, (*decFnInfo).fastpathDecMapBoolInt8R)
+ fn(map[bool]int16(nil), (*encFnInfo).fastpathEncMapBoolInt16R, (*decFnInfo).fastpathDecMapBoolInt16R)
+ fn(map[bool]int32(nil), (*encFnInfo).fastpathEncMapBoolInt32R, (*decFnInfo).fastpathDecMapBoolInt32R)
+ fn(map[bool]int64(nil), (*encFnInfo).fastpathEncMapBoolInt64R, (*decFnInfo).fastpathDecMapBoolInt64R)
+ fn(map[bool]float32(nil), (*encFnInfo).fastpathEncMapBoolFloat32R, (*decFnInfo).fastpathDecMapBoolFloat32R)
+ fn(map[bool]float64(nil), (*encFnInfo).fastpathEncMapBoolFloat64R, (*decFnInfo).fastpathDecMapBoolFloat64R)
+ fn(map[bool]bool(nil), (*encFnInfo).fastpathEncMapBoolBoolR, (*decFnInfo).fastpathDecMapBoolBoolR)
+
+ sort.Sort(fastpathAslice(fastpathAV[:]))
+}
+
+// -- encode
+
+// -- -- fast path type switch
+func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
+ switch v := iv.(type) {
+
+ case []interface{}:
+ fastpathTV.EncSliceIntfV(v, fastpathCheckNilTrue, e)
+ case *[]interface{}:
+ fastpathTV.EncSliceIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]interface{}:
+ fastpathTV.EncMapIntfIntfV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]interface{}:
+ fastpathTV.EncMapIntfIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]string:
+ fastpathTV.EncMapIntfStringV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]string:
+ fastpathTV.EncMapIntfStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint:
+ fastpathTV.EncMapIntfUintV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint:
+ fastpathTV.EncMapIntfUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint8:
+ fastpathTV.EncMapIntfUint8V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint8:
+ fastpathTV.EncMapIntfUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint16:
+ fastpathTV.EncMapIntfUint16V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint16:
+ fastpathTV.EncMapIntfUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint32:
+ fastpathTV.EncMapIntfUint32V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint32:
+ fastpathTV.EncMapIntfUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint64:
+ fastpathTV.EncMapIntfUint64V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint64:
+ fastpathTV.EncMapIntfUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int:
+ fastpathTV.EncMapIntfIntV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int:
+ fastpathTV.EncMapIntfIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int8:
+ fastpathTV.EncMapIntfInt8V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int8:
+ fastpathTV.EncMapIntfInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int16:
+ fastpathTV.EncMapIntfInt16V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int16:
+ fastpathTV.EncMapIntfInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int32:
+ fastpathTV.EncMapIntfInt32V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int32:
+ fastpathTV.EncMapIntfInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int64:
+ fastpathTV.EncMapIntfInt64V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int64:
+ fastpathTV.EncMapIntfInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]float32:
+ fastpathTV.EncMapIntfFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]float32:
+ fastpathTV.EncMapIntfFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]float64:
+ fastpathTV.EncMapIntfFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]float64:
+ fastpathTV.EncMapIntfFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]bool:
+ fastpathTV.EncMapIntfBoolV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]bool:
+ fastpathTV.EncMapIntfBoolV(*v, fastpathCheckNilTrue, e)
+
+ case []string:
+ fastpathTV.EncSliceStringV(v, fastpathCheckNilTrue, e)
+ case *[]string:
+ fastpathTV.EncSliceStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]interface{}:
+ fastpathTV.EncMapStringIntfV(v, fastpathCheckNilTrue, e)
+ case *map[string]interface{}:
+ fastpathTV.EncMapStringIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]string:
+ fastpathTV.EncMapStringStringV(v, fastpathCheckNilTrue, e)
+ case *map[string]string:
+ fastpathTV.EncMapStringStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint:
+ fastpathTV.EncMapStringUintV(v, fastpathCheckNilTrue, e)
+ case *map[string]uint:
+ fastpathTV.EncMapStringUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint8:
+ fastpathTV.EncMapStringUint8V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint8:
+ fastpathTV.EncMapStringUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint16:
+ fastpathTV.EncMapStringUint16V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint16:
+ fastpathTV.EncMapStringUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint32:
+ fastpathTV.EncMapStringUint32V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint32:
+ fastpathTV.EncMapStringUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint64:
+ fastpathTV.EncMapStringUint64V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint64:
+ fastpathTV.EncMapStringUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int:
+ fastpathTV.EncMapStringIntV(v, fastpathCheckNilTrue, e)
+ case *map[string]int:
+ fastpathTV.EncMapStringIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int8:
+ fastpathTV.EncMapStringInt8V(v, fastpathCheckNilTrue, e)
+ case *map[string]int8:
+ fastpathTV.EncMapStringInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int16:
+ fastpathTV.EncMapStringInt16V(v, fastpathCheckNilTrue, e)
+ case *map[string]int16:
+ fastpathTV.EncMapStringInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int32:
+ fastpathTV.EncMapStringInt32V(v, fastpathCheckNilTrue, e)
+ case *map[string]int32:
+ fastpathTV.EncMapStringInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int64:
+ fastpathTV.EncMapStringInt64V(v, fastpathCheckNilTrue, e)
+ case *map[string]int64:
+ fastpathTV.EncMapStringInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]float32:
+ fastpathTV.EncMapStringFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[string]float32:
+ fastpathTV.EncMapStringFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]float64:
+ fastpathTV.EncMapStringFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[string]float64:
+ fastpathTV.EncMapStringFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]bool:
+ fastpathTV.EncMapStringBoolV(v, fastpathCheckNilTrue, e)
+ case *map[string]bool:
+ fastpathTV.EncMapStringBoolV(*v, fastpathCheckNilTrue, e)
+
+ case []float32:
+ fastpathTV.EncSliceFloat32V(v, fastpathCheckNilTrue, e)
+ case *[]float32:
+ fastpathTV.EncSliceFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]interface{}:
+ fastpathTV.EncMapFloat32IntfV(v, fastpathCheckNilTrue, e)
+ case *map[float32]interface{}:
+ fastpathTV.EncMapFloat32IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]string:
+ fastpathTV.EncMapFloat32StringV(v, fastpathCheckNilTrue, e)
+ case *map[float32]string:
+ fastpathTV.EncMapFloat32StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint:
+ fastpathTV.EncMapFloat32UintV(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint:
+ fastpathTV.EncMapFloat32UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint8:
+ fastpathTV.EncMapFloat32Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint8:
+ fastpathTV.EncMapFloat32Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint16:
+ fastpathTV.EncMapFloat32Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint16:
+ fastpathTV.EncMapFloat32Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint32:
+ fastpathTV.EncMapFloat32Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint32:
+ fastpathTV.EncMapFloat32Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint64:
+ fastpathTV.EncMapFloat32Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint64:
+ fastpathTV.EncMapFloat32Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int:
+ fastpathTV.EncMapFloat32IntV(v, fastpathCheckNilTrue, e)
+ case *map[float32]int:
+ fastpathTV.EncMapFloat32IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int8:
+ fastpathTV.EncMapFloat32Int8V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int8:
+ fastpathTV.EncMapFloat32Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int16:
+ fastpathTV.EncMapFloat32Int16V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int16:
+ fastpathTV.EncMapFloat32Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int32:
+ fastpathTV.EncMapFloat32Int32V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int32:
+ fastpathTV.EncMapFloat32Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int64:
+ fastpathTV.EncMapFloat32Int64V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int64:
+ fastpathTV.EncMapFloat32Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]float32:
+ fastpathTV.EncMapFloat32Float32V(v, fastpathCheckNilTrue, e)
+ case *map[float32]float32:
+ fastpathTV.EncMapFloat32Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]float64:
+ fastpathTV.EncMapFloat32Float64V(v, fastpathCheckNilTrue, e)
+ case *map[float32]float64:
+ fastpathTV.EncMapFloat32Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]bool:
+ fastpathTV.EncMapFloat32BoolV(v, fastpathCheckNilTrue, e)
+ case *map[float32]bool:
+ fastpathTV.EncMapFloat32BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []float64:
+ fastpathTV.EncSliceFloat64V(v, fastpathCheckNilTrue, e)
+ case *[]float64:
+ fastpathTV.EncSliceFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]interface{}:
+ fastpathTV.EncMapFloat64IntfV(v, fastpathCheckNilTrue, e)
+ case *map[float64]interface{}:
+ fastpathTV.EncMapFloat64IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]string:
+ fastpathTV.EncMapFloat64StringV(v, fastpathCheckNilTrue, e)
+ case *map[float64]string:
+ fastpathTV.EncMapFloat64StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint:
+ fastpathTV.EncMapFloat64UintV(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint:
+ fastpathTV.EncMapFloat64UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint8:
+ fastpathTV.EncMapFloat64Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint8:
+ fastpathTV.EncMapFloat64Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint16:
+ fastpathTV.EncMapFloat64Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint16:
+ fastpathTV.EncMapFloat64Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint32:
+ fastpathTV.EncMapFloat64Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint32:
+ fastpathTV.EncMapFloat64Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint64:
+ fastpathTV.EncMapFloat64Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint64:
+ fastpathTV.EncMapFloat64Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int:
+ fastpathTV.EncMapFloat64IntV(v, fastpathCheckNilTrue, e)
+ case *map[float64]int:
+ fastpathTV.EncMapFloat64IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int8:
+ fastpathTV.EncMapFloat64Int8V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int8:
+ fastpathTV.EncMapFloat64Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int16:
+ fastpathTV.EncMapFloat64Int16V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int16:
+ fastpathTV.EncMapFloat64Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int32:
+ fastpathTV.EncMapFloat64Int32V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int32:
+ fastpathTV.EncMapFloat64Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int64:
+ fastpathTV.EncMapFloat64Int64V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int64:
+ fastpathTV.EncMapFloat64Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]float32:
+ fastpathTV.EncMapFloat64Float32V(v, fastpathCheckNilTrue, e)
+ case *map[float64]float32:
+ fastpathTV.EncMapFloat64Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]float64:
+ fastpathTV.EncMapFloat64Float64V(v, fastpathCheckNilTrue, e)
+ case *map[float64]float64:
+ fastpathTV.EncMapFloat64Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]bool:
+ fastpathTV.EncMapFloat64BoolV(v, fastpathCheckNilTrue, e)
+ case *map[float64]bool:
+ fastpathTV.EncMapFloat64BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []uint:
+ fastpathTV.EncSliceUintV(v, fastpathCheckNilTrue, e)
+ case *[]uint:
+ fastpathTV.EncSliceUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]interface{}:
+ fastpathTV.EncMapUintIntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint]interface{}:
+ fastpathTV.EncMapUintIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]string:
+ fastpathTV.EncMapUintStringV(v, fastpathCheckNilTrue, e)
+ case *map[uint]string:
+ fastpathTV.EncMapUintStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint:
+ fastpathTV.EncMapUintUintV(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint:
+ fastpathTV.EncMapUintUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint8:
+ fastpathTV.EncMapUintUint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint8:
+ fastpathTV.EncMapUintUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint16:
+ fastpathTV.EncMapUintUint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint16:
+ fastpathTV.EncMapUintUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint32:
+ fastpathTV.EncMapUintUint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint32:
+ fastpathTV.EncMapUintUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint64:
+ fastpathTV.EncMapUintUint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint64:
+ fastpathTV.EncMapUintUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int:
+ fastpathTV.EncMapUintIntV(v, fastpathCheckNilTrue, e)
+ case *map[uint]int:
+ fastpathTV.EncMapUintIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int8:
+ fastpathTV.EncMapUintInt8V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int8:
+ fastpathTV.EncMapUintInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int16:
+ fastpathTV.EncMapUintInt16V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int16:
+ fastpathTV.EncMapUintInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int32:
+ fastpathTV.EncMapUintInt32V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int32:
+ fastpathTV.EncMapUintInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int64:
+ fastpathTV.EncMapUintInt64V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int64:
+ fastpathTV.EncMapUintInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]float32:
+ fastpathTV.EncMapUintFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[uint]float32:
+ fastpathTV.EncMapUintFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]float64:
+ fastpathTV.EncMapUintFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[uint]float64:
+ fastpathTV.EncMapUintFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]bool:
+ fastpathTV.EncMapUintBoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint]bool:
+ fastpathTV.EncMapUintBoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]interface{}:
+ fastpathTV.EncMapUint8IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]interface{}:
+ fastpathTV.EncMapUint8IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]string:
+ fastpathTV.EncMapUint8StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]string:
+ fastpathTV.EncMapUint8StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint:
+ fastpathTV.EncMapUint8UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint:
+ fastpathTV.EncMapUint8UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint8:
+ fastpathTV.EncMapUint8Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint8:
+ fastpathTV.EncMapUint8Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint16:
+ fastpathTV.EncMapUint8Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint16:
+ fastpathTV.EncMapUint8Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint32:
+ fastpathTV.EncMapUint8Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint32:
+ fastpathTV.EncMapUint8Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint64:
+ fastpathTV.EncMapUint8Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint64:
+ fastpathTV.EncMapUint8Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int:
+ fastpathTV.EncMapUint8IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int:
+ fastpathTV.EncMapUint8IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int8:
+ fastpathTV.EncMapUint8Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int8:
+ fastpathTV.EncMapUint8Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int16:
+ fastpathTV.EncMapUint8Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int16:
+ fastpathTV.EncMapUint8Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int32:
+ fastpathTV.EncMapUint8Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int32:
+ fastpathTV.EncMapUint8Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int64:
+ fastpathTV.EncMapUint8Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int64:
+ fastpathTV.EncMapUint8Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]float32:
+ fastpathTV.EncMapUint8Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]float32:
+ fastpathTV.EncMapUint8Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]float64:
+ fastpathTV.EncMapUint8Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]float64:
+ fastpathTV.EncMapUint8Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]bool:
+ fastpathTV.EncMapUint8BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]bool:
+ fastpathTV.EncMapUint8BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []uint16:
+ fastpathTV.EncSliceUint16V(v, fastpathCheckNilTrue, e)
+ case *[]uint16:
+ fastpathTV.EncSliceUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]interface{}:
+ fastpathTV.EncMapUint16IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]interface{}:
+ fastpathTV.EncMapUint16IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]string:
+ fastpathTV.EncMapUint16StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]string:
+ fastpathTV.EncMapUint16StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint:
+ fastpathTV.EncMapUint16UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint:
+ fastpathTV.EncMapUint16UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint8:
+ fastpathTV.EncMapUint16Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint8:
+ fastpathTV.EncMapUint16Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint16:
+ fastpathTV.EncMapUint16Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint16:
+ fastpathTV.EncMapUint16Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint32:
+ fastpathTV.EncMapUint16Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint32:
+ fastpathTV.EncMapUint16Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint64:
+ fastpathTV.EncMapUint16Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint64:
+ fastpathTV.EncMapUint16Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int:
+ fastpathTV.EncMapUint16IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int:
+ fastpathTV.EncMapUint16IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int8:
+ fastpathTV.EncMapUint16Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int8:
+ fastpathTV.EncMapUint16Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int16:
+ fastpathTV.EncMapUint16Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int16:
+ fastpathTV.EncMapUint16Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int32:
+ fastpathTV.EncMapUint16Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int32:
+ fastpathTV.EncMapUint16Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int64:
+ fastpathTV.EncMapUint16Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int64:
+ fastpathTV.EncMapUint16Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]float32:
+ fastpathTV.EncMapUint16Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]float32:
+ fastpathTV.EncMapUint16Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]float64:
+ fastpathTV.EncMapUint16Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]float64:
+ fastpathTV.EncMapUint16Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]bool:
+ fastpathTV.EncMapUint16BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]bool:
+ fastpathTV.EncMapUint16BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []uint32:
+ fastpathTV.EncSliceUint32V(v, fastpathCheckNilTrue, e)
+ case *[]uint32:
+ fastpathTV.EncSliceUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]interface{}:
+ fastpathTV.EncMapUint32IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]interface{}:
+ fastpathTV.EncMapUint32IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]string:
+ fastpathTV.EncMapUint32StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]string:
+ fastpathTV.EncMapUint32StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint:
+ fastpathTV.EncMapUint32UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint:
+ fastpathTV.EncMapUint32UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint8:
+ fastpathTV.EncMapUint32Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint8:
+ fastpathTV.EncMapUint32Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint16:
+ fastpathTV.EncMapUint32Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint16:
+ fastpathTV.EncMapUint32Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint32:
+ fastpathTV.EncMapUint32Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint32:
+ fastpathTV.EncMapUint32Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint64:
+ fastpathTV.EncMapUint32Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint64:
+ fastpathTV.EncMapUint32Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int:
+ fastpathTV.EncMapUint32IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int:
+ fastpathTV.EncMapUint32IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int8:
+ fastpathTV.EncMapUint32Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int8:
+ fastpathTV.EncMapUint32Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int16:
+ fastpathTV.EncMapUint32Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int16:
+ fastpathTV.EncMapUint32Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int32:
+ fastpathTV.EncMapUint32Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int32:
+ fastpathTV.EncMapUint32Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int64:
+ fastpathTV.EncMapUint32Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int64:
+ fastpathTV.EncMapUint32Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]float32:
+ fastpathTV.EncMapUint32Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]float32:
+ fastpathTV.EncMapUint32Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]float64:
+ fastpathTV.EncMapUint32Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]float64:
+ fastpathTV.EncMapUint32Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]bool:
+ fastpathTV.EncMapUint32BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]bool:
+ fastpathTV.EncMapUint32BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []uint64:
+ fastpathTV.EncSliceUint64V(v, fastpathCheckNilTrue, e)
+ case *[]uint64:
+ fastpathTV.EncSliceUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]interface{}:
+ fastpathTV.EncMapUint64IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]interface{}:
+ fastpathTV.EncMapUint64IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]string:
+ fastpathTV.EncMapUint64StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]string:
+ fastpathTV.EncMapUint64StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint:
+ fastpathTV.EncMapUint64UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint:
+ fastpathTV.EncMapUint64UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint8:
+ fastpathTV.EncMapUint64Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint8:
+ fastpathTV.EncMapUint64Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint16:
+ fastpathTV.EncMapUint64Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint16:
+ fastpathTV.EncMapUint64Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint32:
+ fastpathTV.EncMapUint64Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint32:
+ fastpathTV.EncMapUint64Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint64:
+ fastpathTV.EncMapUint64Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint64:
+ fastpathTV.EncMapUint64Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int:
+ fastpathTV.EncMapUint64IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int:
+ fastpathTV.EncMapUint64IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int8:
+ fastpathTV.EncMapUint64Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int8:
+ fastpathTV.EncMapUint64Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int16:
+ fastpathTV.EncMapUint64Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int16:
+ fastpathTV.EncMapUint64Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int32:
+ fastpathTV.EncMapUint64Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int32:
+ fastpathTV.EncMapUint64Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int64:
+ fastpathTV.EncMapUint64Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int64:
+ fastpathTV.EncMapUint64Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]float32:
+ fastpathTV.EncMapUint64Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]float32:
+ fastpathTV.EncMapUint64Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]float64:
+ fastpathTV.EncMapUint64Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]float64:
+ fastpathTV.EncMapUint64Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]bool:
+ fastpathTV.EncMapUint64BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]bool:
+ fastpathTV.EncMapUint64BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []int:
+ fastpathTV.EncSliceIntV(v, fastpathCheckNilTrue, e)
+ case *[]int:
+ fastpathTV.EncSliceIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]interface{}:
+ fastpathTV.EncMapIntIntfV(v, fastpathCheckNilTrue, e)
+ case *map[int]interface{}:
+ fastpathTV.EncMapIntIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]string:
+ fastpathTV.EncMapIntStringV(v, fastpathCheckNilTrue, e)
+ case *map[int]string:
+ fastpathTV.EncMapIntStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint:
+ fastpathTV.EncMapIntUintV(v, fastpathCheckNilTrue, e)
+ case *map[int]uint:
+ fastpathTV.EncMapIntUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint8:
+ fastpathTV.EncMapIntUint8V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint8:
+ fastpathTV.EncMapIntUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint16:
+ fastpathTV.EncMapIntUint16V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint16:
+ fastpathTV.EncMapIntUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint32:
+ fastpathTV.EncMapIntUint32V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint32:
+ fastpathTV.EncMapIntUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint64:
+ fastpathTV.EncMapIntUint64V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint64:
+ fastpathTV.EncMapIntUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int:
+ fastpathTV.EncMapIntIntV(v, fastpathCheckNilTrue, e)
+ case *map[int]int:
+ fastpathTV.EncMapIntIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int8:
+ fastpathTV.EncMapIntInt8V(v, fastpathCheckNilTrue, e)
+ case *map[int]int8:
+ fastpathTV.EncMapIntInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int16:
+ fastpathTV.EncMapIntInt16V(v, fastpathCheckNilTrue, e)
+ case *map[int]int16:
+ fastpathTV.EncMapIntInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int32:
+ fastpathTV.EncMapIntInt32V(v, fastpathCheckNilTrue, e)
+ case *map[int]int32:
+ fastpathTV.EncMapIntInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int64:
+ fastpathTV.EncMapIntInt64V(v, fastpathCheckNilTrue, e)
+ case *map[int]int64:
+ fastpathTV.EncMapIntInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]float32:
+ fastpathTV.EncMapIntFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[int]float32:
+ fastpathTV.EncMapIntFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]float64:
+ fastpathTV.EncMapIntFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[int]float64:
+ fastpathTV.EncMapIntFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]bool:
+ fastpathTV.EncMapIntBoolV(v, fastpathCheckNilTrue, e)
+ case *map[int]bool:
+ fastpathTV.EncMapIntBoolV(*v, fastpathCheckNilTrue, e)
+
+ case []int8:
+ fastpathTV.EncSliceInt8V(v, fastpathCheckNilTrue, e)
+ case *[]int8:
+ fastpathTV.EncSliceInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]interface{}:
+ fastpathTV.EncMapInt8IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int8]interface{}:
+ fastpathTV.EncMapInt8IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]string:
+ fastpathTV.EncMapInt8StringV(v, fastpathCheckNilTrue, e)
+ case *map[int8]string:
+ fastpathTV.EncMapInt8StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint:
+ fastpathTV.EncMapInt8UintV(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint:
+ fastpathTV.EncMapInt8UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint8:
+ fastpathTV.EncMapInt8Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint8:
+ fastpathTV.EncMapInt8Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint16:
+ fastpathTV.EncMapInt8Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint16:
+ fastpathTV.EncMapInt8Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint32:
+ fastpathTV.EncMapInt8Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint32:
+ fastpathTV.EncMapInt8Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint64:
+ fastpathTV.EncMapInt8Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint64:
+ fastpathTV.EncMapInt8Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int:
+ fastpathTV.EncMapInt8IntV(v, fastpathCheckNilTrue, e)
+ case *map[int8]int:
+ fastpathTV.EncMapInt8IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int8:
+ fastpathTV.EncMapInt8Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int8:
+ fastpathTV.EncMapInt8Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int16:
+ fastpathTV.EncMapInt8Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int16:
+ fastpathTV.EncMapInt8Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int32:
+ fastpathTV.EncMapInt8Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int32:
+ fastpathTV.EncMapInt8Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int64:
+ fastpathTV.EncMapInt8Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int64:
+ fastpathTV.EncMapInt8Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]float32:
+ fastpathTV.EncMapInt8Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int8]float32:
+ fastpathTV.EncMapInt8Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]float64:
+ fastpathTV.EncMapInt8Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int8]float64:
+ fastpathTV.EncMapInt8Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]bool:
+ fastpathTV.EncMapInt8BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int8]bool:
+ fastpathTV.EncMapInt8BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []int16:
+ fastpathTV.EncSliceInt16V(v, fastpathCheckNilTrue, e)
+ case *[]int16:
+ fastpathTV.EncSliceInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]interface{}:
+ fastpathTV.EncMapInt16IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int16]interface{}:
+ fastpathTV.EncMapInt16IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]string:
+ fastpathTV.EncMapInt16StringV(v, fastpathCheckNilTrue, e)
+ case *map[int16]string:
+ fastpathTV.EncMapInt16StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint:
+ fastpathTV.EncMapInt16UintV(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint:
+ fastpathTV.EncMapInt16UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint8:
+ fastpathTV.EncMapInt16Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint8:
+ fastpathTV.EncMapInt16Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint16:
+ fastpathTV.EncMapInt16Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint16:
+ fastpathTV.EncMapInt16Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint32:
+ fastpathTV.EncMapInt16Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint32:
+ fastpathTV.EncMapInt16Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint64:
+ fastpathTV.EncMapInt16Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint64:
+ fastpathTV.EncMapInt16Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int:
+ fastpathTV.EncMapInt16IntV(v, fastpathCheckNilTrue, e)
+ case *map[int16]int:
+ fastpathTV.EncMapInt16IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int8:
+ fastpathTV.EncMapInt16Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int8:
+ fastpathTV.EncMapInt16Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int16:
+ fastpathTV.EncMapInt16Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int16:
+ fastpathTV.EncMapInt16Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int32:
+ fastpathTV.EncMapInt16Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int32:
+ fastpathTV.EncMapInt16Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int64:
+ fastpathTV.EncMapInt16Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int64:
+ fastpathTV.EncMapInt16Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]float32:
+ fastpathTV.EncMapInt16Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int16]float32:
+ fastpathTV.EncMapInt16Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]float64:
+ fastpathTV.EncMapInt16Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int16]float64:
+ fastpathTV.EncMapInt16Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]bool:
+ fastpathTV.EncMapInt16BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int16]bool:
+ fastpathTV.EncMapInt16BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []int32:
+ fastpathTV.EncSliceInt32V(v, fastpathCheckNilTrue, e)
+ case *[]int32:
+ fastpathTV.EncSliceInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]interface{}:
+ fastpathTV.EncMapInt32IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int32]interface{}:
+ fastpathTV.EncMapInt32IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]string:
+ fastpathTV.EncMapInt32StringV(v, fastpathCheckNilTrue, e)
+ case *map[int32]string:
+ fastpathTV.EncMapInt32StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint:
+ fastpathTV.EncMapInt32UintV(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint:
+ fastpathTV.EncMapInt32UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint8:
+ fastpathTV.EncMapInt32Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint8:
+ fastpathTV.EncMapInt32Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint16:
+ fastpathTV.EncMapInt32Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint16:
+ fastpathTV.EncMapInt32Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint32:
+ fastpathTV.EncMapInt32Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint32:
+ fastpathTV.EncMapInt32Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint64:
+ fastpathTV.EncMapInt32Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint64:
+ fastpathTV.EncMapInt32Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int:
+ fastpathTV.EncMapInt32IntV(v, fastpathCheckNilTrue, e)
+ case *map[int32]int:
+ fastpathTV.EncMapInt32IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int8:
+ fastpathTV.EncMapInt32Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int8:
+ fastpathTV.EncMapInt32Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int16:
+ fastpathTV.EncMapInt32Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int16:
+ fastpathTV.EncMapInt32Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int32:
+ fastpathTV.EncMapInt32Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int32:
+ fastpathTV.EncMapInt32Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int64:
+ fastpathTV.EncMapInt32Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int64:
+ fastpathTV.EncMapInt32Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]float32:
+ fastpathTV.EncMapInt32Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int32]float32:
+ fastpathTV.EncMapInt32Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]float64:
+ fastpathTV.EncMapInt32Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int32]float64:
+ fastpathTV.EncMapInt32Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]bool:
+ fastpathTV.EncMapInt32BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int32]bool:
+ fastpathTV.EncMapInt32BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []int64:
+ fastpathTV.EncSliceInt64V(v, fastpathCheckNilTrue, e)
+ case *[]int64:
+ fastpathTV.EncSliceInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]interface{}:
+ fastpathTV.EncMapInt64IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int64]interface{}:
+ fastpathTV.EncMapInt64IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]string:
+ fastpathTV.EncMapInt64StringV(v, fastpathCheckNilTrue, e)
+ case *map[int64]string:
+ fastpathTV.EncMapInt64StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint:
+ fastpathTV.EncMapInt64UintV(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint:
+ fastpathTV.EncMapInt64UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint8:
+ fastpathTV.EncMapInt64Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint8:
+ fastpathTV.EncMapInt64Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint16:
+ fastpathTV.EncMapInt64Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint16:
+ fastpathTV.EncMapInt64Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint32:
+ fastpathTV.EncMapInt64Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint32:
+ fastpathTV.EncMapInt64Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint64:
+ fastpathTV.EncMapInt64Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint64:
+ fastpathTV.EncMapInt64Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int:
+ fastpathTV.EncMapInt64IntV(v, fastpathCheckNilTrue, e)
+ case *map[int64]int:
+ fastpathTV.EncMapInt64IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int8:
+ fastpathTV.EncMapInt64Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int8:
+ fastpathTV.EncMapInt64Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int16:
+ fastpathTV.EncMapInt64Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int16:
+ fastpathTV.EncMapInt64Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int32:
+ fastpathTV.EncMapInt64Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int32:
+ fastpathTV.EncMapInt64Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int64:
+ fastpathTV.EncMapInt64Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int64:
+ fastpathTV.EncMapInt64Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]float32:
+ fastpathTV.EncMapInt64Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int64]float32:
+ fastpathTV.EncMapInt64Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]float64:
+ fastpathTV.EncMapInt64Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int64]float64:
+ fastpathTV.EncMapInt64Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]bool:
+ fastpathTV.EncMapInt64BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int64]bool:
+ fastpathTV.EncMapInt64BoolV(*v, fastpathCheckNilTrue, e)
+
+ case []bool:
+ fastpathTV.EncSliceBoolV(v, fastpathCheckNilTrue, e)
+ case *[]bool:
+ fastpathTV.EncSliceBoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]interface{}:
+ fastpathTV.EncMapBoolIntfV(v, fastpathCheckNilTrue, e)
+ case *map[bool]interface{}:
+ fastpathTV.EncMapBoolIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]string:
+ fastpathTV.EncMapBoolStringV(v, fastpathCheckNilTrue, e)
+ case *map[bool]string:
+ fastpathTV.EncMapBoolStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint:
+ fastpathTV.EncMapBoolUintV(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint:
+ fastpathTV.EncMapBoolUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint8:
+ fastpathTV.EncMapBoolUint8V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint8:
+ fastpathTV.EncMapBoolUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint16:
+ fastpathTV.EncMapBoolUint16V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint16:
+ fastpathTV.EncMapBoolUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint32:
+ fastpathTV.EncMapBoolUint32V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint32:
+ fastpathTV.EncMapBoolUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint64:
+ fastpathTV.EncMapBoolUint64V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint64:
+ fastpathTV.EncMapBoolUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int:
+ fastpathTV.EncMapBoolIntV(v, fastpathCheckNilTrue, e)
+ case *map[bool]int:
+ fastpathTV.EncMapBoolIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int8:
+ fastpathTV.EncMapBoolInt8V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int8:
+ fastpathTV.EncMapBoolInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int16:
+ fastpathTV.EncMapBoolInt16V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int16:
+ fastpathTV.EncMapBoolInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int32:
+ fastpathTV.EncMapBoolInt32V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int32:
+ fastpathTV.EncMapBoolInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int64:
+ fastpathTV.EncMapBoolInt64V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int64:
+ fastpathTV.EncMapBoolInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]float32:
+ fastpathTV.EncMapBoolFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[bool]float32:
+ fastpathTV.EncMapBoolFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]float64:
+ fastpathTV.EncMapBoolFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[bool]float64:
+ fastpathTV.EncMapBoolFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]bool:
+ fastpathTV.EncMapBoolBoolV(v, fastpathCheckNilTrue, e)
+ case *map[bool]bool:
+ fastpathTV.EncMapBoolBoolV(*v, fastpathCheckNilTrue, e)
+
+ default:
+ return false
+ }
+ return true
+}
+
+func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
+ switch v := iv.(type) {
+
+ case []interface{}:
+ fastpathTV.EncSliceIntfV(v, fastpathCheckNilTrue, e)
+ case *[]interface{}:
+ fastpathTV.EncSliceIntfV(*v, fastpathCheckNilTrue, e)
+
+ case []string:
+ fastpathTV.EncSliceStringV(v, fastpathCheckNilTrue, e)
+ case *[]string:
+ fastpathTV.EncSliceStringV(*v, fastpathCheckNilTrue, e)
+
+ case []float32:
+ fastpathTV.EncSliceFloat32V(v, fastpathCheckNilTrue, e)
+ case *[]float32:
+ fastpathTV.EncSliceFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case []float64:
+ fastpathTV.EncSliceFloat64V(v, fastpathCheckNilTrue, e)
+ case *[]float64:
+ fastpathTV.EncSliceFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case []uint:
+ fastpathTV.EncSliceUintV(v, fastpathCheckNilTrue, e)
+ case *[]uint:
+ fastpathTV.EncSliceUintV(*v, fastpathCheckNilTrue, e)
+
+ case []uint16:
+ fastpathTV.EncSliceUint16V(v, fastpathCheckNilTrue, e)
+ case *[]uint16:
+ fastpathTV.EncSliceUint16V(*v, fastpathCheckNilTrue, e)
+
+ case []uint32:
+ fastpathTV.EncSliceUint32V(v, fastpathCheckNilTrue, e)
+ case *[]uint32:
+ fastpathTV.EncSliceUint32V(*v, fastpathCheckNilTrue, e)
+
+ case []uint64:
+ fastpathTV.EncSliceUint64V(v, fastpathCheckNilTrue, e)
+ case *[]uint64:
+ fastpathTV.EncSliceUint64V(*v, fastpathCheckNilTrue, e)
+
+ case []int:
+ fastpathTV.EncSliceIntV(v, fastpathCheckNilTrue, e)
+ case *[]int:
+ fastpathTV.EncSliceIntV(*v, fastpathCheckNilTrue, e)
+
+ case []int8:
+ fastpathTV.EncSliceInt8V(v, fastpathCheckNilTrue, e)
+ case *[]int8:
+ fastpathTV.EncSliceInt8V(*v, fastpathCheckNilTrue, e)
+
+ case []int16:
+ fastpathTV.EncSliceInt16V(v, fastpathCheckNilTrue, e)
+ case *[]int16:
+ fastpathTV.EncSliceInt16V(*v, fastpathCheckNilTrue, e)
+
+ case []int32:
+ fastpathTV.EncSliceInt32V(v, fastpathCheckNilTrue, e)
+ case *[]int32:
+ fastpathTV.EncSliceInt32V(*v, fastpathCheckNilTrue, e)
+
+ case []int64:
+ fastpathTV.EncSliceInt64V(v, fastpathCheckNilTrue, e)
+ case *[]int64:
+ fastpathTV.EncSliceInt64V(*v, fastpathCheckNilTrue, e)
+
+ case []bool:
+ fastpathTV.EncSliceBoolV(v, fastpathCheckNilTrue, e)
+ case *[]bool:
+ fastpathTV.EncSliceBoolV(*v, fastpathCheckNilTrue, e)
+
+ default:
+ return false
+ }
+ return true
+}
+
+func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
+ switch v := iv.(type) {
+
+ case map[interface{}]interface{}:
+ fastpathTV.EncMapIntfIntfV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]interface{}:
+ fastpathTV.EncMapIntfIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]string:
+ fastpathTV.EncMapIntfStringV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]string:
+ fastpathTV.EncMapIntfStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint:
+ fastpathTV.EncMapIntfUintV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint:
+ fastpathTV.EncMapIntfUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint8:
+ fastpathTV.EncMapIntfUint8V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint8:
+ fastpathTV.EncMapIntfUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint16:
+ fastpathTV.EncMapIntfUint16V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint16:
+ fastpathTV.EncMapIntfUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint32:
+ fastpathTV.EncMapIntfUint32V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint32:
+ fastpathTV.EncMapIntfUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]uint64:
+ fastpathTV.EncMapIntfUint64V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]uint64:
+ fastpathTV.EncMapIntfUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int:
+ fastpathTV.EncMapIntfIntV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int:
+ fastpathTV.EncMapIntfIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int8:
+ fastpathTV.EncMapIntfInt8V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int8:
+ fastpathTV.EncMapIntfInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int16:
+ fastpathTV.EncMapIntfInt16V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int16:
+ fastpathTV.EncMapIntfInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int32:
+ fastpathTV.EncMapIntfInt32V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int32:
+ fastpathTV.EncMapIntfInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]int64:
+ fastpathTV.EncMapIntfInt64V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]int64:
+ fastpathTV.EncMapIntfInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]float32:
+ fastpathTV.EncMapIntfFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]float32:
+ fastpathTV.EncMapIntfFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]float64:
+ fastpathTV.EncMapIntfFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]float64:
+ fastpathTV.EncMapIntfFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[interface{}]bool:
+ fastpathTV.EncMapIntfBoolV(v, fastpathCheckNilTrue, e)
+ case *map[interface{}]bool:
+ fastpathTV.EncMapIntfBoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]interface{}:
+ fastpathTV.EncMapStringIntfV(v, fastpathCheckNilTrue, e)
+ case *map[string]interface{}:
+ fastpathTV.EncMapStringIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]string:
+ fastpathTV.EncMapStringStringV(v, fastpathCheckNilTrue, e)
+ case *map[string]string:
+ fastpathTV.EncMapStringStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint:
+ fastpathTV.EncMapStringUintV(v, fastpathCheckNilTrue, e)
+ case *map[string]uint:
+ fastpathTV.EncMapStringUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint8:
+ fastpathTV.EncMapStringUint8V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint8:
+ fastpathTV.EncMapStringUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint16:
+ fastpathTV.EncMapStringUint16V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint16:
+ fastpathTV.EncMapStringUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint32:
+ fastpathTV.EncMapStringUint32V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint32:
+ fastpathTV.EncMapStringUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]uint64:
+ fastpathTV.EncMapStringUint64V(v, fastpathCheckNilTrue, e)
+ case *map[string]uint64:
+ fastpathTV.EncMapStringUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int:
+ fastpathTV.EncMapStringIntV(v, fastpathCheckNilTrue, e)
+ case *map[string]int:
+ fastpathTV.EncMapStringIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int8:
+ fastpathTV.EncMapStringInt8V(v, fastpathCheckNilTrue, e)
+ case *map[string]int8:
+ fastpathTV.EncMapStringInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int16:
+ fastpathTV.EncMapStringInt16V(v, fastpathCheckNilTrue, e)
+ case *map[string]int16:
+ fastpathTV.EncMapStringInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int32:
+ fastpathTV.EncMapStringInt32V(v, fastpathCheckNilTrue, e)
+ case *map[string]int32:
+ fastpathTV.EncMapStringInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]int64:
+ fastpathTV.EncMapStringInt64V(v, fastpathCheckNilTrue, e)
+ case *map[string]int64:
+ fastpathTV.EncMapStringInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]float32:
+ fastpathTV.EncMapStringFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[string]float32:
+ fastpathTV.EncMapStringFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]float64:
+ fastpathTV.EncMapStringFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[string]float64:
+ fastpathTV.EncMapStringFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[string]bool:
+ fastpathTV.EncMapStringBoolV(v, fastpathCheckNilTrue, e)
+ case *map[string]bool:
+ fastpathTV.EncMapStringBoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]interface{}:
+ fastpathTV.EncMapFloat32IntfV(v, fastpathCheckNilTrue, e)
+ case *map[float32]interface{}:
+ fastpathTV.EncMapFloat32IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]string:
+ fastpathTV.EncMapFloat32StringV(v, fastpathCheckNilTrue, e)
+ case *map[float32]string:
+ fastpathTV.EncMapFloat32StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint:
+ fastpathTV.EncMapFloat32UintV(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint:
+ fastpathTV.EncMapFloat32UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint8:
+ fastpathTV.EncMapFloat32Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint8:
+ fastpathTV.EncMapFloat32Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint16:
+ fastpathTV.EncMapFloat32Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint16:
+ fastpathTV.EncMapFloat32Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint32:
+ fastpathTV.EncMapFloat32Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint32:
+ fastpathTV.EncMapFloat32Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]uint64:
+ fastpathTV.EncMapFloat32Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[float32]uint64:
+ fastpathTV.EncMapFloat32Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int:
+ fastpathTV.EncMapFloat32IntV(v, fastpathCheckNilTrue, e)
+ case *map[float32]int:
+ fastpathTV.EncMapFloat32IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int8:
+ fastpathTV.EncMapFloat32Int8V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int8:
+ fastpathTV.EncMapFloat32Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int16:
+ fastpathTV.EncMapFloat32Int16V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int16:
+ fastpathTV.EncMapFloat32Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int32:
+ fastpathTV.EncMapFloat32Int32V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int32:
+ fastpathTV.EncMapFloat32Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]int64:
+ fastpathTV.EncMapFloat32Int64V(v, fastpathCheckNilTrue, e)
+ case *map[float32]int64:
+ fastpathTV.EncMapFloat32Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]float32:
+ fastpathTV.EncMapFloat32Float32V(v, fastpathCheckNilTrue, e)
+ case *map[float32]float32:
+ fastpathTV.EncMapFloat32Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]float64:
+ fastpathTV.EncMapFloat32Float64V(v, fastpathCheckNilTrue, e)
+ case *map[float32]float64:
+ fastpathTV.EncMapFloat32Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float32]bool:
+ fastpathTV.EncMapFloat32BoolV(v, fastpathCheckNilTrue, e)
+ case *map[float32]bool:
+ fastpathTV.EncMapFloat32BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]interface{}:
+ fastpathTV.EncMapFloat64IntfV(v, fastpathCheckNilTrue, e)
+ case *map[float64]interface{}:
+ fastpathTV.EncMapFloat64IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]string:
+ fastpathTV.EncMapFloat64StringV(v, fastpathCheckNilTrue, e)
+ case *map[float64]string:
+ fastpathTV.EncMapFloat64StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint:
+ fastpathTV.EncMapFloat64UintV(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint:
+ fastpathTV.EncMapFloat64UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint8:
+ fastpathTV.EncMapFloat64Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint8:
+ fastpathTV.EncMapFloat64Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint16:
+ fastpathTV.EncMapFloat64Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint16:
+ fastpathTV.EncMapFloat64Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint32:
+ fastpathTV.EncMapFloat64Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint32:
+ fastpathTV.EncMapFloat64Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]uint64:
+ fastpathTV.EncMapFloat64Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[float64]uint64:
+ fastpathTV.EncMapFloat64Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int:
+ fastpathTV.EncMapFloat64IntV(v, fastpathCheckNilTrue, e)
+ case *map[float64]int:
+ fastpathTV.EncMapFloat64IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int8:
+ fastpathTV.EncMapFloat64Int8V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int8:
+ fastpathTV.EncMapFloat64Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int16:
+ fastpathTV.EncMapFloat64Int16V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int16:
+ fastpathTV.EncMapFloat64Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int32:
+ fastpathTV.EncMapFloat64Int32V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int32:
+ fastpathTV.EncMapFloat64Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]int64:
+ fastpathTV.EncMapFloat64Int64V(v, fastpathCheckNilTrue, e)
+ case *map[float64]int64:
+ fastpathTV.EncMapFloat64Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]float32:
+ fastpathTV.EncMapFloat64Float32V(v, fastpathCheckNilTrue, e)
+ case *map[float64]float32:
+ fastpathTV.EncMapFloat64Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]float64:
+ fastpathTV.EncMapFloat64Float64V(v, fastpathCheckNilTrue, e)
+ case *map[float64]float64:
+ fastpathTV.EncMapFloat64Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[float64]bool:
+ fastpathTV.EncMapFloat64BoolV(v, fastpathCheckNilTrue, e)
+ case *map[float64]bool:
+ fastpathTV.EncMapFloat64BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]interface{}:
+ fastpathTV.EncMapUintIntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint]interface{}:
+ fastpathTV.EncMapUintIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]string:
+ fastpathTV.EncMapUintStringV(v, fastpathCheckNilTrue, e)
+ case *map[uint]string:
+ fastpathTV.EncMapUintStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint:
+ fastpathTV.EncMapUintUintV(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint:
+ fastpathTV.EncMapUintUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint8:
+ fastpathTV.EncMapUintUint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint8:
+ fastpathTV.EncMapUintUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint16:
+ fastpathTV.EncMapUintUint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint16:
+ fastpathTV.EncMapUintUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint32:
+ fastpathTV.EncMapUintUint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint32:
+ fastpathTV.EncMapUintUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]uint64:
+ fastpathTV.EncMapUintUint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint]uint64:
+ fastpathTV.EncMapUintUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int:
+ fastpathTV.EncMapUintIntV(v, fastpathCheckNilTrue, e)
+ case *map[uint]int:
+ fastpathTV.EncMapUintIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int8:
+ fastpathTV.EncMapUintInt8V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int8:
+ fastpathTV.EncMapUintInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int16:
+ fastpathTV.EncMapUintInt16V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int16:
+ fastpathTV.EncMapUintInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int32:
+ fastpathTV.EncMapUintInt32V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int32:
+ fastpathTV.EncMapUintInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]int64:
+ fastpathTV.EncMapUintInt64V(v, fastpathCheckNilTrue, e)
+ case *map[uint]int64:
+ fastpathTV.EncMapUintInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]float32:
+ fastpathTV.EncMapUintFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[uint]float32:
+ fastpathTV.EncMapUintFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]float64:
+ fastpathTV.EncMapUintFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[uint]float64:
+ fastpathTV.EncMapUintFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint]bool:
+ fastpathTV.EncMapUintBoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint]bool:
+ fastpathTV.EncMapUintBoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]interface{}:
+ fastpathTV.EncMapUint8IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]interface{}:
+ fastpathTV.EncMapUint8IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]string:
+ fastpathTV.EncMapUint8StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]string:
+ fastpathTV.EncMapUint8StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint:
+ fastpathTV.EncMapUint8UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint:
+ fastpathTV.EncMapUint8UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint8:
+ fastpathTV.EncMapUint8Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint8:
+ fastpathTV.EncMapUint8Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint16:
+ fastpathTV.EncMapUint8Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint16:
+ fastpathTV.EncMapUint8Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint32:
+ fastpathTV.EncMapUint8Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint32:
+ fastpathTV.EncMapUint8Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]uint64:
+ fastpathTV.EncMapUint8Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]uint64:
+ fastpathTV.EncMapUint8Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int:
+ fastpathTV.EncMapUint8IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int:
+ fastpathTV.EncMapUint8IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int8:
+ fastpathTV.EncMapUint8Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int8:
+ fastpathTV.EncMapUint8Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int16:
+ fastpathTV.EncMapUint8Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int16:
+ fastpathTV.EncMapUint8Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int32:
+ fastpathTV.EncMapUint8Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int32:
+ fastpathTV.EncMapUint8Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]int64:
+ fastpathTV.EncMapUint8Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]int64:
+ fastpathTV.EncMapUint8Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]float32:
+ fastpathTV.EncMapUint8Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]float32:
+ fastpathTV.EncMapUint8Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]float64:
+ fastpathTV.EncMapUint8Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint8]float64:
+ fastpathTV.EncMapUint8Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint8]bool:
+ fastpathTV.EncMapUint8BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint8]bool:
+ fastpathTV.EncMapUint8BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]interface{}:
+ fastpathTV.EncMapUint16IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]interface{}:
+ fastpathTV.EncMapUint16IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]string:
+ fastpathTV.EncMapUint16StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]string:
+ fastpathTV.EncMapUint16StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint:
+ fastpathTV.EncMapUint16UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint:
+ fastpathTV.EncMapUint16UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint8:
+ fastpathTV.EncMapUint16Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint8:
+ fastpathTV.EncMapUint16Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint16:
+ fastpathTV.EncMapUint16Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint16:
+ fastpathTV.EncMapUint16Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint32:
+ fastpathTV.EncMapUint16Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint32:
+ fastpathTV.EncMapUint16Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]uint64:
+ fastpathTV.EncMapUint16Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]uint64:
+ fastpathTV.EncMapUint16Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int:
+ fastpathTV.EncMapUint16IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int:
+ fastpathTV.EncMapUint16IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int8:
+ fastpathTV.EncMapUint16Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int8:
+ fastpathTV.EncMapUint16Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int16:
+ fastpathTV.EncMapUint16Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int16:
+ fastpathTV.EncMapUint16Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int32:
+ fastpathTV.EncMapUint16Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int32:
+ fastpathTV.EncMapUint16Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]int64:
+ fastpathTV.EncMapUint16Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]int64:
+ fastpathTV.EncMapUint16Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]float32:
+ fastpathTV.EncMapUint16Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]float32:
+ fastpathTV.EncMapUint16Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]float64:
+ fastpathTV.EncMapUint16Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint16]float64:
+ fastpathTV.EncMapUint16Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint16]bool:
+ fastpathTV.EncMapUint16BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint16]bool:
+ fastpathTV.EncMapUint16BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]interface{}:
+ fastpathTV.EncMapUint32IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]interface{}:
+ fastpathTV.EncMapUint32IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]string:
+ fastpathTV.EncMapUint32StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]string:
+ fastpathTV.EncMapUint32StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint:
+ fastpathTV.EncMapUint32UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint:
+ fastpathTV.EncMapUint32UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint8:
+ fastpathTV.EncMapUint32Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint8:
+ fastpathTV.EncMapUint32Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint16:
+ fastpathTV.EncMapUint32Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint16:
+ fastpathTV.EncMapUint32Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint32:
+ fastpathTV.EncMapUint32Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint32:
+ fastpathTV.EncMapUint32Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]uint64:
+ fastpathTV.EncMapUint32Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]uint64:
+ fastpathTV.EncMapUint32Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int:
+ fastpathTV.EncMapUint32IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int:
+ fastpathTV.EncMapUint32IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int8:
+ fastpathTV.EncMapUint32Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int8:
+ fastpathTV.EncMapUint32Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int16:
+ fastpathTV.EncMapUint32Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int16:
+ fastpathTV.EncMapUint32Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int32:
+ fastpathTV.EncMapUint32Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int32:
+ fastpathTV.EncMapUint32Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]int64:
+ fastpathTV.EncMapUint32Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]int64:
+ fastpathTV.EncMapUint32Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]float32:
+ fastpathTV.EncMapUint32Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]float32:
+ fastpathTV.EncMapUint32Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]float64:
+ fastpathTV.EncMapUint32Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint32]float64:
+ fastpathTV.EncMapUint32Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint32]bool:
+ fastpathTV.EncMapUint32BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint32]bool:
+ fastpathTV.EncMapUint32BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]interface{}:
+ fastpathTV.EncMapUint64IntfV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]interface{}:
+ fastpathTV.EncMapUint64IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]string:
+ fastpathTV.EncMapUint64StringV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]string:
+ fastpathTV.EncMapUint64StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint:
+ fastpathTV.EncMapUint64UintV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint:
+ fastpathTV.EncMapUint64UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint8:
+ fastpathTV.EncMapUint64Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint8:
+ fastpathTV.EncMapUint64Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint16:
+ fastpathTV.EncMapUint64Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint16:
+ fastpathTV.EncMapUint64Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint32:
+ fastpathTV.EncMapUint64Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint32:
+ fastpathTV.EncMapUint64Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]uint64:
+ fastpathTV.EncMapUint64Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]uint64:
+ fastpathTV.EncMapUint64Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int:
+ fastpathTV.EncMapUint64IntV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int:
+ fastpathTV.EncMapUint64IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int8:
+ fastpathTV.EncMapUint64Int8V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int8:
+ fastpathTV.EncMapUint64Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int16:
+ fastpathTV.EncMapUint64Int16V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int16:
+ fastpathTV.EncMapUint64Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int32:
+ fastpathTV.EncMapUint64Int32V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int32:
+ fastpathTV.EncMapUint64Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]int64:
+ fastpathTV.EncMapUint64Int64V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]int64:
+ fastpathTV.EncMapUint64Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]float32:
+ fastpathTV.EncMapUint64Float32V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]float32:
+ fastpathTV.EncMapUint64Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]float64:
+ fastpathTV.EncMapUint64Float64V(v, fastpathCheckNilTrue, e)
+ case *map[uint64]float64:
+ fastpathTV.EncMapUint64Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[uint64]bool:
+ fastpathTV.EncMapUint64BoolV(v, fastpathCheckNilTrue, e)
+ case *map[uint64]bool:
+ fastpathTV.EncMapUint64BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]interface{}:
+ fastpathTV.EncMapIntIntfV(v, fastpathCheckNilTrue, e)
+ case *map[int]interface{}:
+ fastpathTV.EncMapIntIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]string:
+ fastpathTV.EncMapIntStringV(v, fastpathCheckNilTrue, e)
+ case *map[int]string:
+ fastpathTV.EncMapIntStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint:
+ fastpathTV.EncMapIntUintV(v, fastpathCheckNilTrue, e)
+ case *map[int]uint:
+ fastpathTV.EncMapIntUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint8:
+ fastpathTV.EncMapIntUint8V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint8:
+ fastpathTV.EncMapIntUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint16:
+ fastpathTV.EncMapIntUint16V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint16:
+ fastpathTV.EncMapIntUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint32:
+ fastpathTV.EncMapIntUint32V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint32:
+ fastpathTV.EncMapIntUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]uint64:
+ fastpathTV.EncMapIntUint64V(v, fastpathCheckNilTrue, e)
+ case *map[int]uint64:
+ fastpathTV.EncMapIntUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int:
+ fastpathTV.EncMapIntIntV(v, fastpathCheckNilTrue, e)
+ case *map[int]int:
+ fastpathTV.EncMapIntIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int8:
+ fastpathTV.EncMapIntInt8V(v, fastpathCheckNilTrue, e)
+ case *map[int]int8:
+ fastpathTV.EncMapIntInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int16:
+ fastpathTV.EncMapIntInt16V(v, fastpathCheckNilTrue, e)
+ case *map[int]int16:
+ fastpathTV.EncMapIntInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int32:
+ fastpathTV.EncMapIntInt32V(v, fastpathCheckNilTrue, e)
+ case *map[int]int32:
+ fastpathTV.EncMapIntInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]int64:
+ fastpathTV.EncMapIntInt64V(v, fastpathCheckNilTrue, e)
+ case *map[int]int64:
+ fastpathTV.EncMapIntInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]float32:
+ fastpathTV.EncMapIntFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[int]float32:
+ fastpathTV.EncMapIntFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]float64:
+ fastpathTV.EncMapIntFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[int]float64:
+ fastpathTV.EncMapIntFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int]bool:
+ fastpathTV.EncMapIntBoolV(v, fastpathCheckNilTrue, e)
+ case *map[int]bool:
+ fastpathTV.EncMapIntBoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]interface{}:
+ fastpathTV.EncMapInt8IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int8]interface{}:
+ fastpathTV.EncMapInt8IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]string:
+ fastpathTV.EncMapInt8StringV(v, fastpathCheckNilTrue, e)
+ case *map[int8]string:
+ fastpathTV.EncMapInt8StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint:
+ fastpathTV.EncMapInt8UintV(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint:
+ fastpathTV.EncMapInt8UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint8:
+ fastpathTV.EncMapInt8Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint8:
+ fastpathTV.EncMapInt8Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint16:
+ fastpathTV.EncMapInt8Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint16:
+ fastpathTV.EncMapInt8Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint32:
+ fastpathTV.EncMapInt8Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint32:
+ fastpathTV.EncMapInt8Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]uint64:
+ fastpathTV.EncMapInt8Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int8]uint64:
+ fastpathTV.EncMapInt8Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int:
+ fastpathTV.EncMapInt8IntV(v, fastpathCheckNilTrue, e)
+ case *map[int8]int:
+ fastpathTV.EncMapInt8IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int8:
+ fastpathTV.EncMapInt8Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int8:
+ fastpathTV.EncMapInt8Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int16:
+ fastpathTV.EncMapInt8Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int16:
+ fastpathTV.EncMapInt8Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int32:
+ fastpathTV.EncMapInt8Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int32:
+ fastpathTV.EncMapInt8Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]int64:
+ fastpathTV.EncMapInt8Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int8]int64:
+ fastpathTV.EncMapInt8Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]float32:
+ fastpathTV.EncMapInt8Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int8]float32:
+ fastpathTV.EncMapInt8Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]float64:
+ fastpathTV.EncMapInt8Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int8]float64:
+ fastpathTV.EncMapInt8Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int8]bool:
+ fastpathTV.EncMapInt8BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int8]bool:
+ fastpathTV.EncMapInt8BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]interface{}:
+ fastpathTV.EncMapInt16IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int16]interface{}:
+ fastpathTV.EncMapInt16IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]string:
+ fastpathTV.EncMapInt16StringV(v, fastpathCheckNilTrue, e)
+ case *map[int16]string:
+ fastpathTV.EncMapInt16StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint:
+ fastpathTV.EncMapInt16UintV(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint:
+ fastpathTV.EncMapInt16UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint8:
+ fastpathTV.EncMapInt16Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint8:
+ fastpathTV.EncMapInt16Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint16:
+ fastpathTV.EncMapInt16Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint16:
+ fastpathTV.EncMapInt16Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint32:
+ fastpathTV.EncMapInt16Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint32:
+ fastpathTV.EncMapInt16Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]uint64:
+ fastpathTV.EncMapInt16Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int16]uint64:
+ fastpathTV.EncMapInt16Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int:
+ fastpathTV.EncMapInt16IntV(v, fastpathCheckNilTrue, e)
+ case *map[int16]int:
+ fastpathTV.EncMapInt16IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int8:
+ fastpathTV.EncMapInt16Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int8:
+ fastpathTV.EncMapInt16Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int16:
+ fastpathTV.EncMapInt16Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int16:
+ fastpathTV.EncMapInt16Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int32:
+ fastpathTV.EncMapInt16Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int32:
+ fastpathTV.EncMapInt16Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]int64:
+ fastpathTV.EncMapInt16Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int16]int64:
+ fastpathTV.EncMapInt16Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]float32:
+ fastpathTV.EncMapInt16Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int16]float32:
+ fastpathTV.EncMapInt16Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]float64:
+ fastpathTV.EncMapInt16Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int16]float64:
+ fastpathTV.EncMapInt16Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int16]bool:
+ fastpathTV.EncMapInt16BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int16]bool:
+ fastpathTV.EncMapInt16BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]interface{}:
+ fastpathTV.EncMapInt32IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int32]interface{}:
+ fastpathTV.EncMapInt32IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]string:
+ fastpathTV.EncMapInt32StringV(v, fastpathCheckNilTrue, e)
+ case *map[int32]string:
+ fastpathTV.EncMapInt32StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint:
+ fastpathTV.EncMapInt32UintV(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint:
+ fastpathTV.EncMapInt32UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint8:
+ fastpathTV.EncMapInt32Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint8:
+ fastpathTV.EncMapInt32Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint16:
+ fastpathTV.EncMapInt32Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint16:
+ fastpathTV.EncMapInt32Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint32:
+ fastpathTV.EncMapInt32Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint32:
+ fastpathTV.EncMapInt32Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]uint64:
+ fastpathTV.EncMapInt32Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int32]uint64:
+ fastpathTV.EncMapInt32Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int:
+ fastpathTV.EncMapInt32IntV(v, fastpathCheckNilTrue, e)
+ case *map[int32]int:
+ fastpathTV.EncMapInt32IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int8:
+ fastpathTV.EncMapInt32Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int8:
+ fastpathTV.EncMapInt32Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int16:
+ fastpathTV.EncMapInt32Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int16:
+ fastpathTV.EncMapInt32Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int32:
+ fastpathTV.EncMapInt32Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int32:
+ fastpathTV.EncMapInt32Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]int64:
+ fastpathTV.EncMapInt32Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int32]int64:
+ fastpathTV.EncMapInt32Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]float32:
+ fastpathTV.EncMapInt32Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int32]float32:
+ fastpathTV.EncMapInt32Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]float64:
+ fastpathTV.EncMapInt32Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int32]float64:
+ fastpathTV.EncMapInt32Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int32]bool:
+ fastpathTV.EncMapInt32BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int32]bool:
+ fastpathTV.EncMapInt32BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]interface{}:
+ fastpathTV.EncMapInt64IntfV(v, fastpathCheckNilTrue, e)
+ case *map[int64]interface{}:
+ fastpathTV.EncMapInt64IntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]string:
+ fastpathTV.EncMapInt64StringV(v, fastpathCheckNilTrue, e)
+ case *map[int64]string:
+ fastpathTV.EncMapInt64StringV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint:
+ fastpathTV.EncMapInt64UintV(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint:
+ fastpathTV.EncMapInt64UintV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint8:
+ fastpathTV.EncMapInt64Uint8V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint8:
+ fastpathTV.EncMapInt64Uint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint16:
+ fastpathTV.EncMapInt64Uint16V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint16:
+ fastpathTV.EncMapInt64Uint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint32:
+ fastpathTV.EncMapInt64Uint32V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint32:
+ fastpathTV.EncMapInt64Uint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]uint64:
+ fastpathTV.EncMapInt64Uint64V(v, fastpathCheckNilTrue, e)
+ case *map[int64]uint64:
+ fastpathTV.EncMapInt64Uint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int:
+ fastpathTV.EncMapInt64IntV(v, fastpathCheckNilTrue, e)
+ case *map[int64]int:
+ fastpathTV.EncMapInt64IntV(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int8:
+ fastpathTV.EncMapInt64Int8V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int8:
+ fastpathTV.EncMapInt64Int8V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int16:
+ fastpathTV.EncMapInt64Int16V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int16:
+ fastpathTV.EncMapInt64Int16V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int32:
+ fastpathTV.EncMapInt64Int32V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int32:
+ fastpathTV.EncMapInt64Int32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]int64:
+ fastpathTV.EncMapInt64Int64V(v, fastpathCheckNilTrue, e)
+ case *map[int64]int64:
+ fastpathTV.EncMapInt64Int64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]float32:
+ fastpathTV.EncMapInt64Float32V(v, fastpathCheckNilTrue, e)
+ case *map[int64]float32:
+ fastpathTV.EncMapInt64Float32V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]float64:
+ fastpathTV.EncMapInt64Float64V(v, fastpathCheckNilTrue, e)
+ case *map[int64]float64:
+ fastpathTV.EncMapInt64Float64V(*v, fastpathCheckNilTrue, e)
+
+ case map[int64]bool:
+ fastpathTV.EncMapInt64BoolV(v, fastpathCheckNilTrue, e)
+ case *map[int64]bool:
+ fastpathTV.EncMapInt64BoolV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]interface{}:
+ fastpathTV.EncMapBoolIntfV(v, fastpathCheckNilTrue, e)
+ case *map[bool]interface{}:
+ fastpathTV.EncMapBoolIntfV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]string:
+ fastpathTV.EncMapBoolStringV(v, fastpathCheckNilTrue, e)
+ case *map[bool]string:
+ fastpathTV.EncMapBoolStringV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint:
+ fastpathTV.EncMapBoolUintV(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint:
+ fastpathTV.EncMapBoolUintV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint8:
+ fastpathTV.EncMapBoolUint8V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint8:
+ fastpathTV.EncMapBoolUint8V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint16:
+ fastpathTV.EncMapBoolUint16V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint16:
+ fastpathTV.EncMapBoolUint16V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint32:
+ fastpathTV.EncMapBoolUint32V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint32:
+ fastpathTV.EncMapBoolUint32V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]uint64:
+ fastpathTV.EncMapBoolUint64V(v, fastpathCheckNilTrue, e)
+ case *map[bool]uint64:
+ fastpathTV.EncMapBoolUint64V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int:
+ fastpathTV.EncMapBoolIntV(v, fastpathCheckNilTrue, e)
+ case *map[bool]int:
+ fastpathTV.EncMapBoolIntV(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int8:
+ fastpathTV.EncMapBoolInt8V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int8:
+ fastpathTV.EncMapBoolInt8V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int16:
+ fastpathTV.EncMapBoolInt16V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int16:
+ fastpathTV.EncMapBoolInt16V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int32:
+ fastpathTV.EncMapBoolInt32V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int32:
+ fastpathTV.EncMapBoolInt32V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]int64:
+ fastpathTV.EncMapBoolInt64V(v, fastpathCheckNilTrue, e)
+ case *map[bool]int64:
+ fastpathTV.EncMapBoolInt64V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]float32:
+ fastpathTV.EncMapBoolFloat32V(v, fastpathCheckNilTrue, e)
+ case *map[bool]float32:
+ fastpathTV.EncMapBoolFloat32V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]float64:
+ fastpathTV.EncMapBoolFloat64V(v, fastpathCheckNilTrue, e)
+ case *map[bool]float64:
+ fastpathTV.EncMapBoolFloat64V(*v, fastpathCheckNilTrue, e)
+
+ case map[bool]bool:
+ fastpathTV.EncMapBoolBoolV(v, fastpathCheckNilTrue, e)
+ case *map[bool]bool:
+ fastpathTV.EncMapBoolBoolV(*v, fastpathCheckNilTrue, e)
+
+ default:
+ return false
+ }
+ return true
+}
+
+// -- -- fast path functions
+
+func (f *encFnInfo) fastpathEncSliceIntfR(rv reflect.Value) {
+ fastpathTV.EncSliceIntfV(rv.Interface().([]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceIntfV(v []interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceStringR(rv reflect.Value) {
+ fastpathTV.EncSliceStringV(rv.Interface().([]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceStringV(v []string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceFloat32R(rv reflect.Value) {
+ fastpathTV.EncSliceFloat32V(rv.Interface().([]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceFloat32V(v []float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceFloat64R(rv reflect.Value) {
+ fastpathTV.EncSliceFloat64V(rv.Interface().([]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceFloat64V(v []float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceUintR(rv reflect.Value) {
+ fastpathTV.EncSliceUintV(rv.Interface().([]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceUintV(v []uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceUint16R(rv reflect.Value) {
+ fastpathTV.EncSliceUint16V(rv.Interface().([]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceUint16V(v []uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceUint32R(rv reflect.Value) {
+ fastpathTV.EncSliceUint32V(rv.Interface().([]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceUint32V(v []uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceUint64R(rv reflect.Value) {
+ fastpathTV.EncSliceUint64V(rv.Interface().([]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceUint64V(v []uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceIntR(rv reflect.Value) {
+ fastpathTV.EncSliceIntV(rv.Interface().([]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceIntV(v []int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceInt8R(rv reflect.Value) {
+ fastpathTV.EncSliceInt8V(rv.Interface().([]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceInt8V(v []int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceInt16R(rv reflect.Value) {
+ fastpathTV.EncSliceInt16V(rv.Interface().([]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceInt16V(v []int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceInt32R(rv reflect.Value) {
+ fastpathTV.EncSliceInt32V(rv.Interface().([]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceInt32V(v []int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceInt64R(rv reflect.Value) {
+ fastpathTV.EncSliceInt64V(rv.Interface().([]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceInt64V(v []int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncSliceBoolR(rv reflect.Value) {
+ fastpathTV.EncSliceBoolV(rv.Interface().([]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncSliceBoolV(v []bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfIntfR(rv reflect.Value) {
+ fastpathTV.EncMapIntfIntfV(rv.Interface().(map[interface{}]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfIntfV(v map[interface{}]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfStringR(rv reflect.Value) {
+ fastpathTV.EncMapIntfStringV(rv.Interface().(map[interface{}]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfStringV(v map[interface{}]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfUintR(rv reflect.Value) {
+ fastpathTV.EncMapIntfUintV(rv.Interface().(map[interface{}]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfUintV(v map[interface{}]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfUint8R(rv reflect.Value) {
+ fastpathTV.EncMapIntfUint8V(rv.Interface().(map[interface{}]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfUint8V(v map[interface{}]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfUint16R(rv reflect.Value) {
+ fastpathTV.EncMapIntfUint16V(rv.Interface().(map[interface{}]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfUint16V(v map[interface{}]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfUint32R(rv reflect.Value) {
+ fastpathTV.EncMapIntfUint32V(rv.Interface().(map[interface{}]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfUint32V(v map[interface{}]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfUint64R(rv reflect.Value) {
+ fastpathTV.EncMapIntfUint64V(rv.Interface().(map[interface{}]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfUint64V(v map[interface{}]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfIntR(rv reflect.Value) {
+ fastpathTV.EncMapIntfIntV(rv.Interface().(map[interface{}]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfIntV(v map[interface{}]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfInt8R(rv reflect.Value) {
+ fastpathTV.EncMapIntfInt8V(rv.Interface().(map[interface{}]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfInt8V(v map[interface{}]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfInt16R(rv reflect.Value) {
+ fastpathTV.EncMapIntfInt16V(rv.Interface().(map[interface{}]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfInt16V(v map[interface{}]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfInt32R(rv reflect.Value) {
+ fastpathTV.EncMapIntfInt32V(rv.Interface().(map[interface{}]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfInt32V(v map[interface{}]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfInt64R(rv reflect.Value) {
+ fastpathTV.EncMapIntfInt64V(rv.Interface().(map[interface{}]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfInt64V(v map[interface{}]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfFloat32R(rv reflect.Value) {
+ fastpathTV.EncMapIntfFloat32V(rv.Interface().(map[interface{}]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfFloat32V(v map[interface{}]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfFloat64R(rv reflect.Value) {
+ fastpathTV.EncMapIntfFloat64V(rv.Interface().(map[interface{}]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfFloat64V(v map[interface{}]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntfBoolR(rv reflect.Value) {
+ fastpathTV.EncMapIntfBoolV(rv.Interface().(map[interface{}]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntfBoolV(v map[interface{}]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ e.encode(k2)
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringIntfR(rv reflect.Value) {
+ fastpathTV.EncMapStringIntfV(rv.Interface().(map[string]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringIntfV(v map[string]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringStringR(rv reflect.Value) {
+ fastpathTV.EncMapStringStringV(rv.Interface().(map[string]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringStringV(v map[string]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringUintR(rv reflect.Value) {
+ fastpathTV.EncMapStringUintV(rv.Interface().(map[string]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringUintV(v map[string]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringUint8R(rv reflect.Value) {
+ fastpathTV.EncMapStringUint8V(rv.Interface().(map[string]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringUint8V(v map[string]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringUint16R(rv reflect.Value) {
+ fastpathTV.EncMapStringUint16V(rv.Interface().(map[string]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringUint16V(v map[string]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringUint32R(rv reflect.Value) {
+ fastpathTV.EncMapStringUint32V(rv.Interface().(map[string]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringUint32V(v map[string]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringUint64R(rv reflect.Value) {
+ fastpathTV.EncMapStringUint64V(rv.Interface().(map[string]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringUint64V(v map[string]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringIntR(rv reflect.Value) {
+ fastpathTV.EncMapStringIntV(rv.Interface().(map[string]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringIntV(v map[string]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringInt8R(rv reflect.Value) {
+ fastpathTV.EncMapStringInt8V(rv.Interface().(map[string]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringInt8V(v map[string]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringInt16R(rv reflect.Value) {
+ fastpathTV.EncMapStringInt16V(rv.Interface().(map[string]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringInt16V(v map[string]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringInt32R(rv reflect.Value) {
+ fastpathTV.EncMapStringInt32V(rv.Interface().(map[string]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringInt32V(v map[string]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringInt64R(rv reflect.Value) {
+ fastpathTV.EncMapStringInt64V(rv.Interface().(map[string]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringInt64V(v map[string]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringFloat32R(rv reflect.Value) {
+ fastpathTV.EncMapStringFloat32V(rv.Interface().(map[string]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringFloat32V(v map[string]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringFloat64R(rv reflect.Value) {
+ fastpathTV.EncMapStringFloat64V(rv.Interface().(map[string]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringFloat64V(v map[string]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapStringBoolR(rv reflect.Value) {
+ fastpathTV.EncMapStringBoolV(rv.Interface().(map[string]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapStringBoolV(v map[string]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0
+ for k2, v2 := range v {
+ if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32IntfR(rv reflect.Value) {
+ fastpathTV.EncMapFloat32IntfV(rv.Interface().(map[float32]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32IntfV(v map[float32]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32StringR(rv reflect.Value) {
+ fastpathTV.EncMapFloat32StringV(rv.Interface().(map[float32]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32StringV(v map[float32]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32UintR(rv reflect.Value) {
+ fastpathTV.EncMapFloat32UintV(rv.Interface().(map[float32]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32UintV(v map[float32]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Uint8V(rv.Interface().(map[float32]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Uint8V(v map[float32]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Uint16V(rv.Interface().(map[float32]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Uint16V(v map[float32]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Uint32V(rv.Interface().(map[float32]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Uint32V(v map[float32]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Uint64V(rv.Interface().(map[float32]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Uint64V(v map[float32]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32IntR(rv reflect.Value) {
+ fastpathTV.EncMapFloat32IntV(rv.Interface().(map[float32]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32IntV(v map[float32]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Int8R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Int8V(rv.Interface().(map[float32]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Int8V(v map[float32]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Int16R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Int16V(rv.Interface().(map[float32]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Int16V(v map[float32]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Int32R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Int32V(rv.Interface().(map[float32]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Int32V(v map[float32]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Int64R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Int64V(rv.Interface().(map[float32]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Int64V(v map[float32]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Float32R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Float32V(rv.Interface().(map[float32]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Float32V(v map[float32]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32Float64R(rv reflect.Value) {
+ fastpathTV.EncMapFloat32Float64V(rv.Interface().(map[float32]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32Float64V(v map[float32]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat32BoolR(rv reflect.Value) {
+ fastpathTV.EncMapFloat32BoolV(rv.Interface().(map[float32]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat32BoolV(v map[float32]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat32(k2)
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64IntfR(rv reflect.Value) {
+ fastpathTV.EncMapFloat64IntfV(rv.Interface().(map[float64]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64IntfV(v map[float64]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64StringR(rv reflect.Value) {
+ fastpathTV.EncMapFloat64StringV(rv.Interface().(map[float64]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64StringV(v map[float64]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64UintR(rv reflect.Value) {
+ fastpathTV.EncMapFloat64UintV(rv.Interface().(map[float64]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64UintV(v map[float64]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Uint8V(rv.Interface().(map[float64]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Uint8V(v map[float64]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Uint16V(rv.Interface().(map[float64]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Uint16V(v map[float64]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Uint32V(rv.Interface().(map[float64]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Uint32V(v map[float64]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Uint64V(rv.Interface().(map[float64]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Uint64V(v map[float64]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64IntR(rv reflect.Value) {
+ fastpathTV.EncMapFloat64IntV(rv.Interface().(map[float64]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64IntV(v map[float64]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Int8R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Int8V(rv.Interface().(map[float64]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Int8V(v map[float64]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Int16R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Int16V(rv.Interface().(map[float64]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Int16V(v map[float64]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Int32R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Int32V(rv.Interface().(map[float64]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Int32V(v map[float64]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Int64R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Int64V(rv.Interface().(map[float64]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Int64V(v map[float64]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Float32R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Float32V(rv.Interface().(map[float64]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Float32V(v map[float64]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64Float64R(rv reflect.Value) {
+ fastpathTV.EncMapFloat64Float64V(rv.Interface().(map[float64]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64Float64V(v map[float64]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapFloat64BoolR(rv reflect.Value) {
+ fastpathTV.EncMapFloat64BoolV(rv.Interface().(map[float64]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapFloat64BoolV(v map[float64]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeFloat64(k2)
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintIntfR(rv reflect.Value) {
+ fastpathTV.EncMapUintIntfV(rv.Interface().(map[uint]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintIntfV(v map[uint]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintStringR(rv reflect.Value) {
+ fastpathTV.EncMapUintStringV(rv.Interface().(map[uint]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintStringV(v map[uint]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintUintR(rv reflect.Value) {
+ fastpathTV.EncMapUintUintV(rv.Interface().(map[uint]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintUintV(v map[uint]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintUint8R(rv reflect.Value) {
+ fastpathTV.EncMapUintUint8V(rv.Interface().(map[uint]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintUint8V(v map[uint]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintUint16R(rv reflect.Value) {
+ fastpathTV.EncMapUintUint16V(rv.Interface().(map[uint]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintUint16V(v map[uint]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintUint32R(rv reflect.Value) {
+ fastpathTV.EncMapUintUint32V(rv.Interface().(map[uint]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintUint32V(v map[uint]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintUint64R(rv reflect.Value) {
+ fastpathTV.EncMapUintUint64V(rv.Interface().(map[uint]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintUint64V(v map[uint]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintIntR(rv reflect.Value) {
+ fastpathTV.EncMapUintIntV(rv.Interface().(map[uint]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintIntV(v map[uint]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintInt8R(rv reflect.Value) {
+ fastpathTV.EncMapUintInt8V(rv.Interface().(map[uint]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintInt8V(v map[uint]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintInt16R(rv reflect.Value) {
+ fastpathTV.EncMapUintInt16V(rv.Interface().(map[uint]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintInt16V(v map[uint]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintInt32R(rv reflect.Value) {
+ fastpathTV.EncMapUintInt32V(rv.Interface().(map[uint]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintInt32V(v map[uint]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintInt64R(rv reflect.Value) {
+ fastpathTV.EncMapUintInt64V(rv.Interface().(map[uint]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintInt64V(v map[uint]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintFloat32R(rv reflect.Value) {
+ fastpathTV.EncMapUintFloat32V(rv.Interface().(map[uint]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintFloat32V(v map[uint]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintFloat64R(rv reflect.Value) {
+ fastpathTV.EncMapUintFloat64V(rv.Interface().(map[uint]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintFloat64V(v map[uint]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUintBoolR(rv reflect.Value) {
+ fastpathTV.EncMapUintBoolV(rv.Interface().(map[uint]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUintBoolV(v map[uint]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8IntfR(rv reflect.Value) {
+ fastpathTV.EncMapUint8IntfV(rv.Interface().(map[uint8]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8IntfV(v map[uint8]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8StringR(rv reflect.Value) {
+ fastpathTV.EncMapUint8StringV(rv.Interface().(map[uint8]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8StringV(v map[uint8]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8UintR(rv reflect.Value) {
+ fastpathTV.EncMapUint8UintV(rv.Interface().(map[uint8]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8UintV(v map[uint8]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Uint8V(rv.Interface().(map[uint8]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Uint8V(v map[uint8]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Uint16V(rv.Interface().(map[uint8]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Uint16V(v map[uint8]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Uint32V(rv.Interface().(map[uint8]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Uint32V(v map[uint8]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Uint64V(rv.Interface().(map[uint8]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Uint64V(v map[uint8]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8IntR(rv reflect.Value) {
+ fastpathTV.EncMapUint8IntV(rv.Interface().(map[uint8]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8IntV(v map[uint8]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Int8R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Int8V(rv.Interface().(map[uint8]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Int8V(v map[uint8]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Int16R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Int16V(rv.Interface().(map[uint8]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Int16V(v map[uint8]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Int32R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Int32V(rv.Interface().(map[uint8]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Int32V(v map[uint8]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Int64R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Int64V(rv.Interface().(map[uint8]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Int64V(v map[uint8]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Float32R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Float32V(rv.Interface().(map[uint8]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Float32V(v map[uint8]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8Float64R(rv reflect.Value) {
+ fastpathTV.EncMapUint8Float64V(rv.Interface().(map[uint8]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8Float64V(v map[uint8]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint8BoolR(rv reflect.Value) {
+ fastpathTV.EncMapUint8BoolV(rv.Interface().(map[uint8]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint8BoolV(v map[uint8]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16IntfR(rv reflect.Value) {
+ fastpathTV.EncMapUint16IntfV(rv.Interface().(map[uint16]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16IntfV(v map[uint16]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16StringR(rv reflect.Value) {
+ fastpathTV.EncMapUint16StringV(rv.Interface().(map[uint16]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16StringV(v map[uint16]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16UintR(rv reflect.Value) {
+ fastpathTV.EncMapUint16UintV(rv.Interface().(map[uint16]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16UintV(v map[uint16]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Uint8V(rv.Interface().(map[uint16]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Uint8V(v map[uint16]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Uint16V(rv.Interface().(map[uint16]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Uint16V(v map[uint16]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Uint32V(rv.Interface().(map[uint16]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Uint32V(v map[uint16]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Uint64V(rv.Interface().(map[uint16]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Uint64V(v map[uint16]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16IntR(rv reflect.Value) {
+ fastpathTV.EncMapUint16IntV(rv.Interface().(map[uint16]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16IntV(v map[uint16]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Int8R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Int8V(rv.Interface().(map[uint16]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Int8V(v map[uint16]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Int16R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Int16V(rv.Interface().(map[uint16]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Int16V(v map[uint16]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Int32R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Int32V(rv.Interface().(map[uint16]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Int32V(v map[uint16]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Int64R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Int64V(rv.Interface().(map[uint16]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Int64V(v map[uint16]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Float32R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Float32V(rv.Interface().(map[uint16]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Float32V(v map[uint16]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16Float64R(rv reflect.Value) {
+ fastpathTV.EncMapUint16Float64V(rv.Interface().(map[uint16]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16Float64V(v map[uint16]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint16BoolR(rv reflect.Value) {
+ fastpathTV.EncMapUint16BoolV(rv.Interface().(map[uint16]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint16BoolV(v map[uint16]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32IntfR(rv reflect.Value) {
+ fastpathTV.EncMapUint32IntfV(rv.Interface().(map[uint32]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32IntfV(v map[uint32]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32StringR(rv reflect.Value) {
+ fastpathTV.EncMapUint32StringV(rv.Interface().(map[uint32]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32StringV(v map[uint32]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32UintR(rv reflect.Value) {
+ fastpathTV.EncMapUint32UintV(rv.Interface().(map[uint32]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32UintV(v map[uint32]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Uint8V(rv.Interface().(map[uint32]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Uint8V(v map[uint32]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Uint16V(rv.Interface().(map[uint32]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Uint16V(v map[uint32]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Uint32V(rv.Interface().(map[uint32]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Uint32V(v map[uint32]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Uint64V(rv.Interface().(map[uint32]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Uint64V(v map[uint32]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32IntR(rv reflect.Value) {
+ fastpathTV.EncMapUint32IntV(rv.Interface().(map[uint32]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32IntV(v map[uint32]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Int8R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Int8V(rv.Interface().(map[uint32]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Int8V(v map[uint32]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Int16R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Int16V(rv.Interface().(map[uint32]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Int16V(v map[uint32]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Int32R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Int32V(rv.Interface().(map[uint32]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Int32V(v map[uint32]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Int64R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Int64V(rv.Interface().(map[uint32]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Int64V(v map[uint32]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Float32R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Float32V(rv.Interface().(map[uint32]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Float32V(v map[uint32]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32Float64R(rv reflect.Value) {
+ fastpathTV.EncMapUint32Float64V(rv.Interface().(map[uint32]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32Float64V(v map[uint32]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint32BoolR(rv reflect.Value) {
+ fastpathTV.EncMapUint32BoolV(rv.Interface().(map[uint32]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint32BoolV(v map[uint32]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64IntfR(rv reflect.Value) {
+ fastpathTV.EncMapUint64IntfV(rv.Interface().(map[uint64]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64IntfV(v map[uint64]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64StringR(rv reflect.Value) {
+ fastpathTV.EncMapUint64StringV(rv.Interface().(map[uint64]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64StringV(v map[uint64]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64UintR(rv reflect.Value) {
+ fastpathTV.EncMapUint64UintV(rv.Interface().(map[uint64]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64UintV(v map[uint64]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Uint8V(rv.Interface().(map[uint64]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Uint8V(v map[uint64]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Uint16V(rv.Interface().(map[uint64]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Uint16V(v map[uint64]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Uint32V(rv.Interface().(map[uint64]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Uint32V(v map[uint64]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Uint64V(rv.Interface().(map[uint64]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Uint64V(v map[uint64]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64IntR(rv reflect.Value) {
+ fastpathTV.EncMapUint64IntV(rv.Interface().(map[uint64]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64IntV(v map[uint64]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Int8R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Int8V(rv.Interface().(map[uint64]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Int8V(v map[uint64]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Int16R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Int16V(rv.Interface().(map[uint64]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Int16V(v map[uint64]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Int32R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Int32V(rv.Interface().(map[uint64]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Int32V(v map[uint64]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Int64R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Int64V(rv.Interface().(map[uint64]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Int64V(v map[uint64]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Float32R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Float32V(rv.Interface().(map[uint64]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Float32V(v map[uint64]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64Float64R(rv reflect.Value) {
+ fastpathTV.EncMapUint64Float64V(rv.Interface().(map[uint64]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64Float64V(v map[uint64]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapUint64BoolR(rv reflect.Value) {
+ fastpathTV.EncMapUint64BoolV(rv.Interface().(map[uint64]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapUint64BoolV(v map[uint64]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeUint(uint64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntIntfR(rv reflect.Value) {
+ fastpathTV.EncMapIntIntfV(rv.Interface().(map[int]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntIntfV(v map[int]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntStringR(rv reflect.Value) {
+ fastpathTV.EncMapIntStringV(rv.Interface().(map[int]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntStringV(v map[int]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntUintR(rv reflect.Value) {
+ fastpathTV.EncMapIntUintV(rv.Interface().(map[int]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntUintV(v map[int]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntUint8R(rv reflect.Value) {
+ fastpathTV.EncMapIntUint8V(rv.Interface().(map[int]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntUint8V(v map[int]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntUint16R(rv reflect.Value) {
+ fastpathTV.EncMapIntUint16V(rv.Interface().(map[int]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntUint16V(v map[int]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntUint32R(rv reflect.Value) {
+ fastpathTV.EncMapIntUint32V(rv.Interface().(map[int]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntUint32V(v map[int]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntUint64R(rv reflect.Value) {
+ fastpathTV.EncMapIntUint64V(rv.Interface().(map[int]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntUint64V(v map[int]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntIntR(rv reflect.Value) {
+ fastpathTV.EncMapIntIntV(rv.Interface().(map[int]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntIntV(v map[int]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntInt8R(rv reflect.Value) {
+ fastpathTV.EncMapIntInt8V(rv.Interface().(map[int]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntInt8V(v map[int]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntInt16R(rv reflect.Value) {
+ fastpathTV.EncMapIntInt16V(rv.Interface().(map[int]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntInt16V(v map[int]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntInt32R(rv reflect.Value) {
+ fastpathTV.EncMapIntInt32V(rv.Interface().(map[int]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntInt32V(v map[int]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntInt64R(rv reflect.Value) {
+ fastpathTV.EncMapIntInt64V(rv.Interface().(map[int]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntInt64V(v map[int]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntFloat32R(rv reflect.Value) {
+ fastpathTV.EncMapIntFloat32V(rv.Interface().(map[int]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntFloat32V(v map[int]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntFloat64R(rv reflect.Value) {
+ fastpathTV.EncMapIntFloat64V(rv.Interface().(map[int]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntFloat64V(v map[int]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapIntBoolR(rv reflect.Value) {
+ fastpathTV.EncMapIntBoolV(rv.Interface().(map[int]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapIntBoolV(v map[int]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8IntfR(rv reflect.Value) {
+ fastpathTV.EncMapInt8IntfV(rv.Interface().(map[int8]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8IntfV(v map[int8]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8StringR(rv reflect.Value) {
+ fastpathTV.EncMapInt8StringV(rv.Interface().(map[int8]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8StringV(v map[int8]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8UintR(rv reflect.Value) {
+ fastpathTV.EncMapInt8UintV(rv.Interface().(map[int8]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8UintV(v map[int8]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Uint8V(rv.Interface().(map[int8]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Uint8V(v map[int8]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Uint16V(rv.Interface().(map[int8]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Uint16V(v map[int8]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Uint32V(rv.Interface().(map[int8]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Uint32V(v map[int8]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Uint64V(rv.Interface().(map[int8]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Uint64V(v map[int8]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8IntR(rv reflect.Value) {
+ fastpathTV.EncMapInt8IntV(rv.Interface().(map[int8]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8IntV(v map[int8]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Int8R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Int8V(rv.Interface().(map[int8]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Int8V(v map[int8]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Int16R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Int16V(rv.Interface().(map[int8]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Int16V(v map[int8]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Int32R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Int32V(rv.Interface().(map[int8]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Int32V(v map[int8]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Int64R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Int64V(rv.Interface().(map[int8]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Int64V(v map[int8]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Float32R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Float32V(rv.Interface().(map[int8]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Float32V(v map[int8]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8Float64R(rv reflect.Value) {
+ fastpathTV.EncMapInt8Float64V(rv.Interface().(map[int8]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8Float64V(v map[int8]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt8BoolR(rv reflect.Value) {
+ fastpathTV.EncMapInt8BoolV(rv.Interface().(map[int8]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt8BoolV(v map[int8]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16IntfR(rv reflect.Value) {
+ fastpathTV.EncMapInt16IntfV(rv.Interface().(map[int16]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16IntfV(v map[int16]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16StringR(rv reflect.Value) {
+ fastpathTV.EncMapInt16StringV(rv.Interface().(map[int16]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16StringV(v map[int16]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16UintR(rv reflect.Value) {
+ fastpathTV.EncMapInt16UintV(rv.Interface().(map[int16]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16UintV(v map[int16]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Uint8V(rv.Interface().(map[int16]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Uint8V(v map[int16]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Uint16V(rv.Interface().(map[int16]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Uint16V(v map[int16]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Uint32V(rv.Interface().(map[int16]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Uint32V(v map[int16]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Uint64V(rv.Interface().(map[int16]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Uint64V(v map[int16]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16IntR(rv reflect.Value) {
+ fastpathTV.EncMapInt16IntV(rv.Interface().(map[int16]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16IntV(v map[int16]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Int8R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Int8V(rv.Interface().(map[int16]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Int8V(v map[int16]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Int16R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Int16V(rv.Interface().(map[int16]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Int16V(v map[int16]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Int32R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Int32V(rv.Interface().(map[int16]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Int32V(v map[int16]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Int64R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Int64V(rv.Interface().(map[int16]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Int64V(v map[int16]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Float32R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Float32V(rv.Interface().(map[int16]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Float32V(v map[int16]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16Float64R(rv reflect.Value) {
+ fastpathTV.EncMapInt16Float64V(rv.Interface().(map[int16]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16Float64V(v map[int16]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt16BoolR(rv reflect.Value) {
+ fastpathTV.EncMapInt16BoolV(rv.Interface().(map[int16]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt16BoolV(v map[int16]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32IntfR(rv reflect.Value) {
+ fastpathTV.EncMapInt32IntfV(rv.Interface().(map[int32]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32IntfV(v map[int32]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32StringR(rv reflect.Value) {
+ fastpathTV.EncMapInt32StringV(rv.Interface().(map[int32]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32StringV(v map[int32]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32UintR(rv reflect.Value) {
+ fastpathTV.EncMapInt32UintV(rv.Interface().(map[int32]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32UintV(v map[int32]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Uint8V(rv.Interface().(map[int32]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Uint8V(v map[int32]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Uint16V(rv.Interface().(map[int32]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Uint16V(v map[int32]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Uint32V(rv.Interface().(map[int32]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Uint32V(v map[int32]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Uint64V(rv.Interface().(map[int32]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Uint64V(v map[int32]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32IntR(rv reflect.Value) {
+ fastpathTV.EncMapInt32IntV(rv.Interface().(map[int32]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32IntV(v map[int32]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Int8R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Int8V(rv.Interface().(map[int32]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Int8V(v map[int32]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Int16R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Int16V(rv.Interface().(map[int32]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Int16V(v map[int32]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Int32R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Int32V(rv.Interface().(map[int32]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Int32V(v map[int32]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Int64R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Int64V(rv.Interface().(map[int32]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Int64V(v map[int32]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Float32R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Float32V(rv.Interface().(map[int32]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Float32V(v map[int32]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32Float64R(rv reflect.Value) {
+ fastpathTV.EncMapInt32Float64V(rv.Interface().(map[int32]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32Float64V(v map[int32]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt32BoolR(rv reflect.Value) {
+ fastpathTV.EncMapInt32BoolV(rv.Interface().(map[int32]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt32BoolV(v map[int32]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64IntfR(rv reflect.Value) {
+ fastpathTV.EncMapInt64IntfV(rv.Interface().(map[int64]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64IntfV(v map[int64]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64StringR(rv reflect.Value) {
+ fastpathTV.EncMapInt64StringV(rv.Interface().(map[int64]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64StringV(v map[int64]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64UintR(rv reflect.Value) {
+ fastpathTV.EncMapInt64UintV(rv.Interface().(map[int64]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64UintV(v map[int64]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Uint8R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Uint8V(rv.Interface().(map[int64]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Uint8V(v map[int64]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Uint16R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Uint16V(rv.Interface().(map[int64]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Uint16V(v map[int64]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Uint32R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Uint32V(rv.Interface().(map[int64]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Uint32V(v map[int64]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Uint64R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Uint64V(rv.Interface().(map[int64]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Uint64V(v map[int64]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64IntR(rv reflect.Value) {
+ fastpathTV.EncMapInt64IntV(rv.Interface().(map[int64]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64IntV(v map[int64]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Int8R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Int8V(rv.Interface().(map[int64]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Int8V(v map[int64]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Int16R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Int16V(rv.Interface().(map[int64]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Int16V(v map[int64]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Int32R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Int32V(rv.Interface().(map[int64]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Int32V(v map[int64]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Int64R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Int64V(rv.Interface().(map[int64]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Int64V(v map[int64]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Float32R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Float32V(rv.Interface().(map[int64]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Float32V(v map[int64]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64Float64R(rv reflect.Value) {
+ fastpathTV.EncMapInt64Float64V(rv.Interface().(map[int64]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64Float64V(v map[int64]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapInt64BoolR(rv reflect.Value) {
+ fastpathTV.EncMapInt64BoolV(rv.Interface().(map[int64]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapInt64BoolV(v map[int64]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeInt(int64(k2))
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolIntfR(rv reflect.Value) {
+ fastpathTV.EncMapBoolIntfV(rv.Interface().(map[bool]interface{}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolIntfV(v map[bool]interface{}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ e.encode(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolStringR(rv reflect.Value) {
+ fastpathTV.EncMapBoolStringV(rv.Interface().(map[bool]string), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolStringV(v map[bool]string, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeString(c_UTF8, v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolUintR(rv reflect.Value) {
+ fastpathTV.EncMapBoolUintV(rv.Interface().(map[bool]uint), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolUintV(v map[bool]uint, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolUint8R(rv reflect.Value) {
+ fastpathTV.EncMapBoolUint8V(rv.Interface().(map[bool]uint8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolUint8V(v map[bool]uint8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolUint16R(rv reflect.Value) {
+ fastpathTV.EncMapBoolUint16V(rv.Interface().(map[bool]uint16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolUint16V(v map[bool]uint16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolUint32R(rv reflect.Value) {
+ fastpathTV.EncMapBoolUint32V(rv.Interface().(map[bool]uint32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolUint32V(v map[bool]uint32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolUint64R(rv reflect.Value) {
+ fastpathTV.EncMapBoolUint64V(rv.Interface().(map[bool]uint64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolUint64V(v map[bool]uint64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeUint(uint64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolIntR(rv reflect.Value) {
+ fastpathTV.EncMapBoolIntV(rv.Interface().(map[bool]int), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolIntV(v map[bool]int, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolInt8R(rv reflect.Value) {
+ fastpathTV.EncMapBoolInt8V(rv.Interface().(map[bool]int8), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolInt8V(v map[bool]int8, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolInt16R(rv reflect.Value) {
+ fastpathTV.EncMapBoolInt16V(rv.Interface().(map[bool]int16), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolInt16V(v map[bool]int16, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolInt32R(rv reflect.Value) {
+ fastpathTV.EncMapBoolInt32V(rv.Interface().(map[bool]int32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolInt32V(v map[bool]int32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolInt64R(rv reflect.Value) {
+ fastpathTV.EncMapBoolInt64V(rv.Interface().(map[bool]int64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolInt64V(v map[bool]int64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeInt(int64(v2))
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolFloat32R(rv reflect.Value) {
+ fastpathTV.EncMapBoolFloat32V(rv.Interface().(map[bool]float32), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolFloat32V(v map[bool]float32, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeFloat32(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolFloat64R(rv reflect.Value) {
+ fastpathTV.EncMapBoolFloat64V(rv.Interface().(map[bool]float64), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolFloat64V(v map[bool]float64, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeFloat64(v2)
+ }
+ ee.EncodeEnd()
+}
+
+func (f *encFnInfo) fastpathEncMapBoolBoolR(rv reflect.Value) {
+ fastpathTV.EncMapBoolBoolV(rv.Interface().(map[bool]bool), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) EncMapBoolBoolV(v map[bool]bool, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+
+ for k2, v2 := range v {
+ ee.EncodeBool(k2)
+ ee.EncodeBool(v2)
+ }
+ ee.EncodeEnd()
+}
+
+// -- decode
+
+// -- -- fast path type switch
+func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
+ switch v := iv.(type) {
+
+ case []interface{}:
+ fastpathTV.DecSliceIntfV(v, fastpathCheckNilFalse, false, d)
+ case *[]interface{}:
+ v2, changed2 := fastpathTV.DecSliceIntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]interface{}:
+ fastpathTV.DecMapIntfIntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]interface{}:
+ v2, changed2 := fastpathTV.DecMapIntfIntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]string:
+ fastpathTV.DecMapIntfStringV(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]string:
+ v2, changed2 := fastpathTV.DecMapIntfStringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]uint:
+ fastpathTV.DecMapIntfUintV(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]uint:
+ v2, changed2 := fastpathTV.DecMapIntfUintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]uint8:
+ fastpathTV.DecMapIntfUint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]uint8:
+ v2, changed2 := fastpathTV.DecMapIntfUint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]uint16:
+ fastpathTV.DecMapIntfUint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]uint16:
+ v2, changed2 := fastpathTV.DecMapIntfUint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]uint32:
+ fastpathTV.DecMapIntfUint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]uint32:
+ v2, changed2 := fastpathTV.DecMapIntfUint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]uint64:
+ fastpathTV.DecMapIntfUint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]uint64:
+ v2, changed2 := fastpathTV.DecMapIntfUint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]int:
+ fastpathTV.DecMapIntfIntV(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]int:
+ v2, changed2 := fastpathTV.DecMapIntfIntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]int8:
+ fastpathTV.DecMapIntfInt8V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]int8:
+ v2, changed2 := fastpathTV.DecMapIntfInt8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]int16:
+ fastpathTV.DecMapIntfInt16V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]int16:
+ v2, changed2 := fastpathTV.DecMapIntfInt16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]int32:
+ fastpathTV.DecMapIntfInt32V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]int32:
+ v2, changed2 := fastpathTV.DecMapIntfInt32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]int64:
+ fastpathTV.DecMapIntfInt64V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]int64:
+ v2, changed2 := fastpathTV.DecMapIntfInt64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]float32:
+ fastpathTV.DecMapIntfFloat32V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]float32:
+ v2, changed2 := fastpathTV.DecMapIntfFloat32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]float64:
+ fastpathTV.DecMapIntfFloat64V(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]float64:
+ v2, changed2 := fastpathTV.DecMapIntfFloat64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[interface{}]bool:
+ fastpathTV.DecMapIntfBoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[interface{}]bool:
+ v2, changed2 := fastpathTV.DecMapIntfBoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []string:
+ fastpathTV.DecSliceStringV(v, fastpathCheckNilFalse, false, d)
+ case *[]string:
+ v2, changed2 := fastpathTV.DecSliceStringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]interface{}:
+ fastpathTV.DecMapStringIntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[string]interface{}:
+ v2, changed2 := fastpathTV.DecMapStringIntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]string:
+ fastpathTV.DecMapStringStringV(v, fastpathCheckNilFalse, false, d)
+ case *map[string]string:
+ v2, changed2 := fastpathTV.DecMapStringStringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]uint:
+ fastpathTV.DecMapStringUintV(v, fastpathCheckNilFalse, false, d)
+ case *map[string]uint:
+ v2, changed2 := fastpathTV.DecMapStringUintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]uint8:
+ fastpathTV.DecMapStringUint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]uint8:
+ v2, changed2 := fastpathTV.DecMapStringUint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]uint16:
+ fastpathTV.DecMapStringUint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]uint16:
+ v2, changed2 := fastpathTV.DecMapStringUint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]uint32:
+ fastpathTV.DecMapStringUint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]uint32:
+ v2, changed2 := fastpathTV.DecMapStringUint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]uint64:
+ fastpathTV.DecMapStringUint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]uint64:
+ v2, changed2 := fastpathTV.DecMapStringUint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]int:
+ fastpathTV.DecMapStringIntV(v, fastpathCheckNilFalse, false, d)
+ case *map[string]int:
+ v2, changed2 := fastpathTV.DecMapStringIntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]int8:
+ fastpathTV.DecMapStringInt8V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]int8:
+ v2, changed2 := fastpathTV.DecMapStringInt8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]int16:
+ fastpathTV.DecMapStringInt16V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]int16:
+ v2, changed2 := fastpathTV.DecMapStringInt16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]int32:
+ fastpathTV.DecMapStringInt32V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]int32:
+ v2, changed2 := fastpathTV.DecMapStringInt32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]int64:
+ fastpathTV.DecMapStringInt64V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]int64:
+ v2, changed2 := fastpathTV.DecMapStringInt64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]float32:
+ fastpathTV.DecMapStringFloat32V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]float32:
+ v2, changed2 := fastpathTV.DecMapStringFloat32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]float64:
+ fastpathTV.DecMapStringFloat64V(v, fastpathCheckNilFalse, false, d)
+ case *map[string]float64:
+ v2, changed2 := fastpathTV.DecMapStringFloat64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[string]bool:
+ fastpathTV.DecMapStringBoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[string]bool:
+ v2, changed2 := fastpathTV.DecMapStringBoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []float32:
+ fastpathTV.DecSliceFloat32V(v, fastpathCheckNilFalse, false, d)
+ case *[]float32:
+ v2, changed2 := fastpathTV.DecSliceFloat32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]interface{}:
+ fastpathTV.DecMapFloat32IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]interface{}:
+ v2, changed2 := fastpathTV.DecMapFloat32IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]string:
+ fastpathTV.DecMapFloat32StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]string:
+ v2, changed2 := fastpathTV.DecMapFloat32StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]uint:
+ fastpathTV.DecMapFloat32UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]uint:
+ v2, changed2 := fastpathTV.DecMapFloat32UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]uint8:
+ fastpathTV.DecMapFloat32Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]uint8:
+ v2, changed2 := fastpathTV.DecMapFloat32Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]uint16:
+ fastpathTV.DecMapFloat32Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]uint16:
+ v2, changed2 := fastpathTV.DecMapFloat32Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]uint32:
+ fastpathTV.DecMapFloat32Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]uint32:
+ v2, changed2 := fastpathTV.DecMapFloat32Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]uint64:
+ fastpathTV.DecMapFloat32Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]uint64:
+ v2, changed2 := fastpathTV.DecMapFloat32Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]int:
+ fastpathTV.DecMapFloat32IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]int:
+ v2, changed2 := fastpathTV.DecMapFloat32IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]int8:
+ fastpathTV.DecMapFloat32Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]int8:
+ v2, changed2 := fastpathTV.DecMapFloat32Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]int16:
+ fastpathTV.DecMapFloat32Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]int16:
+ v2, changed2 := fastpathTV.DecMapFloat32Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]int32:
+ fastpathTV.DecMapFloat32Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]int32:
+ v2, changed2 := fastpathTV.DecMapFloat32Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]int64:
+ fastpathTV.DecMapFloat32Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]int64:
+ v2, changed2 := fastpathTV.DecMapFloat32Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]float32:
+ fastpathTV.DecMapFloat32Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]float32:
+ v2, changed2 := fastpathTV.DecMapFloat32Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]float64:
+ fastpathTV.DecMapFloat32Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]float64:
+ v2, changed2 := fastpathTV.DecMapFloat32Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float32]bool:
+ fastpathTV.DecMapFloat32BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[float32]bool:
+ v2, changed2 := fastpathTV.DecMapFloat32BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []float64:
+ fastpathTV.DecSliceFloat64V(v, fastpathCheckNilFalse, false, d)
+ case *[]float64:
+ v2, changed2 := fastpathTV.DecSliceFloat64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]interface{}:
+ fastpathTV.DecMapFloat64IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]interface{}:
+ v2, changed2 := fastpathTV.DecMapFloat64IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]string:
+ fastpathTV.DecMapFloat64StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]string:
+ v2, changed2 := fastpathTV.DecMapFloat64StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]uint:
+ fastpathTV.DecMapFloat64UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]uint:
+ v2, changed2 := fastpathTV.DecMapFloat64UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]uint8:
+ fastpathTV.DecMapFloat64Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]uint8:
+ v2, changed2 := fastpathTV.DecMapFloat64Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]uint16:
+ fastpathTV.DecMapFloat64Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]uint16:
+ v2, changed2 := fastpathTV.DecMapFloat64Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]uint32:
+ fastpathTV.DecMapFloat64Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]uint32:
+ v2, changed2 := fastpathTV.DecMapFloat64Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]uint64:
+ fastpathTV.DecMapFloat64Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]uint64:
+ v2, changed2 := fastpathTV.DecMapFloat64Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]int:
+ fastpathTV.DecMapFloat64IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]int:
+ v2, changed2 := fastpathTV.DecMapFloat64IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]int8:
+ fastpathTV.DecMapFloat64Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]int8:
+ v2, changed2 := fastpathTV.DecMapFloat64Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]int16:
+ fastpathTV.DecMapFloat64Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]int16:
+ v2, changed2 := fastpathTV.DecMapFloat64Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]int32:
+ fastpathTV.DecMapFloat64Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]int32:
+ v2, changed2 := fastpathTV.DecMapFloat64Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]int64:
+ fastpathTV.DecMapFloat64Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]int64:
+ v2, changed2 := fastpathTV.DecMapFloat64Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]float32:
+ fastpathTV.DecMapFloat64Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]float32:
+ v2, changed2 := fastpathTV.DecMapFloat64Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]float64:
+ fastpathTV.DecMapFloat64Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]float64:
+ v2, changed2 := fastpathTV.DecMapFloat64Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[float64]bool:
+ fastpathTV.DecMapFloat64BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[float64]bool:
+ v2, changed2 := fastpathTV.DecMapFloat64BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []uint:
+ fastpathTV.DecSliceUintV(v, fastpathCheckNilFalse, false, d)
+ case *[]uint:
+ v2, changed2 := fastpathTV.DecSliceUintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]interface{}:
+ fastpathTV.DecMapUintIntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]interface{}:
+ v2, changed2 := fastpathTV.DecMapUintIntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]string:
+ fastpathTV.DecMapUintStringV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]string:
+ v2, changed2 := fastpathTV.DecMapUintStringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]uint:
+ fastpathTV.DecMapUintUintV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]uint:
+ v2, changed2 := fastpathTV.DecMapUintUintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]uint8:
+ fastpathTV.DecMapUintUint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]uint8:
+ v2, changed2 := fastpathTV.DecMapUintUint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]uint16:
+ fastpathTV.DecMapUintUint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]uint16:
+ v2, changed2 := fastpathTV.DecMapUintUint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]uint32:
+ fastpathTV.DecMapUintUint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]uint32:
+ v2, changed2 := fastpathTV.DecMapUintUint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]uint64:
+ fastpathTV.DecMapUintUint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]uint64:
+ v2, changed2 := fastpathTV.DecMapUintUint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]int:
+ fastpathTV.DecMapUintIntV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]int:
+ v2, changed2 := fastpathTV.DecMapUintIntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]int8:
+ fastpathTV.DecMapUintInt8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]int8:
+ v2, changed2 := fastpathTV.DecMapUintInt8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]int16:
+ fastpathTV.DecMapUintInt16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]int16:
+ v2, changed2 := fastpathTV.DecMapUintInt16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]int32:
+ fastpathTV.DecMapUintInt32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]int32:
+ v2, changed2 := fastpathTV.DecMapUintInt32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]int64:
+ fastpathTV.DecMapUintInt64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]int64:
+ v2, changed2 := fastpathTV.DecMapUintInt64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]float32:
+ fastpathTV.DecMapUintFloat32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]float32:
+ v2, changed2 := fastpathTV.DecMapUintFloat32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]float64:
+ fastpathTV.DecMapUintFloat64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]float64:
+ v2, changed2 := fastpathTV.DecMapUintFloat64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint]bool:
+ fastpathTV.DecMapUintBoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint]bool:
+ v2, changed2 := fastpathTV.DecMapUintBoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]interface{}:
+ fastpathTV.DecMapUint8IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]interface{}:
+ v2, changed2 := fastpathTV.DecMapUint8IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]string:
+ fastpathTV.DecMapUint8StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]string:
+ v2, changed2 := fastpathTV.DecMapUint8StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]uint:
+ fastpathTV.DecMapUint8UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]uint:
+ v2, changed2 := fastpathTV.DecMapUint8UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]uint8:
+ fastpathTV.DecMapUint8Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]uint8:
+ v2, changed2 := fastpathTV.DecMapUint8Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]uint16:
+ fastpathTV.DecMapUint8Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]uint16:
+ v2, changed2 := fastpathTV.DecMapUint8Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]uint32:
+ fastpathTV.DecMapUint8Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]uint32:
+ v2, changed2 := fastpathTV.DecMapUint8Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]uint64:
+ fastpathTV.DecMapUint8Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]uint64:
+ v2, changed2 := fastpathTV.DecMapUint8Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]int:
+ fastpathTV.DecMapUint8IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]int:
+ v2, changed2 := fastpathTV.DecMapUint8IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]int8:
+ fastpathTV.DecMapUint8Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]int8:
+ v2, changed2 := fastpathTV.DecMapUint8Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]int16:
+ fastpathTV.DecMapUint8Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]int16:
+ v2, changed2 := fastpathTV.DecMapUint8Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]int32:
+ fastpathTV.DecMapUint8Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]int32:
+ v2, changed2 := fastpathTV.DecMapUint8Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]int64:
+ fastpathTV.DecMapUint8Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]int64:
+ v2, changed2 := fastpathTV.DecMapUint8Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]float32:
+ fastpathTV.DecMapUint8Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]float32:
+ v2, changed2 := fastpathTV.DecMapUint8Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]float64:
+ fastpathTV.DecMapUint8Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]float64:
+ v2, changed2 := fastpathTV.DecMapUint8Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint8]bool:
+ fastpathTV.DecMapUint8BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint8]bool:
+ v2, changed2 := fastpathTV.DecMapUint8BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []uint16:
+ fastpathTV.DecSliceUint16V(v, fastpathCheckNilFalse, false, d)
+ case *[]uint16:
+ v2, changed2 := fastpathTV.DecSliceUint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]interface{}:
+ fastpathTV.DecMapUint16IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]interface{}:
+ v2, changed2 := fastpathTV.DecMapUint16IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]string:
+ fastpathTV.DecMapUint16StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]string:
+ v2, changed2 := fastpathTV.DecMapUint16StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]uint:
+ fastpathTV.DecMapUint16UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]uint:
+ v2, changed2 := fastpathTV.DecMapUint16UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]uint8:
+ fastpathTV.DecMapUint16Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]uint8:
+ v2, changed2 := fastpathTV.DecMapUint16Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]uint16:
+ fastpathTV.DecMapUint16Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]uint16:
+ v2, changed2 := fastpathTV.DecMapUint16Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]uint32:
+ fastpathTV.DecMapUint16Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]uint32:
+ v2, changed2 := fastpathTV.DecMapUint16Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]uint64:
+ fastpathTV.DecMapUint16Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]uint64:
+ v2, changed2 := fastpathTV.DecMapUint16Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]int:
+ fastpathTV.DecMapUint16IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]int:
+ v2, changed2 := fastpathTV.DecMapUint16IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]int8:
+ fastpathTV.DecMapUint16Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]int8:
+ v2, changed2 := fastpathTV.DecMapUint16Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]int16:
+ fastpathTV.DecMapUint16Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]int16:
+ v2, changed2 := fastpathTV.DecMapUint16Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]int32:
+ fastpathTV.DecMapUint16Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]int32:
+ v2, changed2 := fastpathTV.DecMapUint16Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]int64:
+ fastpathTV.DecMapUint16Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]int64:
+ v2, changed2 := fastpathTV.DecMapUint16Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]float32:
+ fastpathTV.DecMapUint16Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]float32:
+ v2, changed2 := fastpathTV.DecMapUint16Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]float64:
+ fastpathTV.DecMapUint16Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]float64:
+ v2, changed2 := fastpathTV.DecMapUint16Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint16]bool:
+ fastpathTV.DecMapUint16BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint16]bool:
+ v2, changed2 := fastpathTV.DecMapUint16BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []uint32:
+ fastpathTV.DecSliceUint32V(v, fastpathCheckNilFalse, false, d)
+ case *[]uint32:
+ v2, changed2 := fastpathTV.DecSliceUint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]interface{}:
+ fastpathTV.DecMapUint32IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]interface{}:
+ v2, changed2 := fastpathTV.DecMapUint32IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]string:
+ fastpathTV.DecMapUint32StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]string:
+ v2, changed2 := fastpathTV.DecMapUint32StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]uint:
+ fastpathTV.DecMapUint32UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]uint:
+ v2, changed2 := fastpathTV.DecMapUint32UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]uint8:
+ fastpathTV.DecMapUint32Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]uint8:
+ v2, changed2 := fastpathTV.DecMapUint32Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]uint16:
+ fastpathTV.DecMapUint32Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]uint16:
+ v2, changed2 := fastpathTV.DecMapUint32Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]uint32:
+ fastpathTV.DecMapUint32Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]uint32:
+ v2, changed2 := fastpathTV.DecMapUint32Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]uint64:
+ fastpathTV.DecMapUint32Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]uint64:
+ v2, changed2 := fastpathTV.DecMapUint32Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]int:
+ fastpathTV.DecMapUint32IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]int:
+ v2, changed2 := fastpathTV.DecMapUint32IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]int8:
+ fastpathTV.DecMapUint32Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]int8:
+ v2, changed2 := fastpathTV.DecMapUint32Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]int16:
+ fastpathTV.DecMapUint32Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]int16:
+ v2, changed2 := fastpathTV.DecMapUint32Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]int32:
+ fastpathTV.DecMapUint32Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]int32:
+ v2, changed2 := fastpathTV.DecMapUint32Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]int64:
+ fastpathTV.DecMapUint32Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]int64:
+ v2, changed2 := fastpathTV.DecMapUint32Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]float32:
+ fastpathTV.DecMapUint32Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]float32:
+ v2, changed2 := fastpathTV.DecMapUint32Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]float64:
+ fastpathTV.DecMapUint32Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]float64:
+ v2, changed2 := fastpathTV.DecMapUint32Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint32]bool:
+ fastpathTV.DecMapUint32BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint32]bool:
+ v2, changed2 := fastpathTV.DecMapUint32BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []uint64:
+ fastpathTV.DecSliceUint64V(v, fastpathCheckNilFalse, false, d)
+ case *[]uint64:
+ v2, changed2 := fastpathTV.DecSliceUint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]interface{}:
+ fastpathTV.DecMapUint64IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]interface{}:
+ v2, changed2 := fastpathTV.DecMapUint64IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]string:
+ fastpathTV.DecMapUint64StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]string:
+ v2, changed2 := fastpathTV.DecMapUint64StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]uint:
+ fastpathTV.DecMapUint64UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]uint:
+ v2, changed2 := fastpathTV.DecMapUint64UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]uint8:
+ fastpathTV.DecMapUint64Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]uint8:
+ v2, changed2 := fastpathTV.DecMapUint64Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]uint16:
+ fastpathTV.DecMapUint64Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]uint16:
+ v2, changed2 := fastpathTV.DecMapUint64Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]uint32:
+ fastpathTV.DecMapUint64Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]uint32:
+ v2, changed2 := fastpathTV.DecMapUint64Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]uint64:
+ fastpathTV.DecMapUint64Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]uint64:
+ v2, changed2 := fastpathTV.DecMapUint64Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]int:
+ fastpathTV.DecMapUint64IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]int:
+ v2, changed2 := fastpathTV.DecMapUint64IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]int8:
+ fastpathTV.DecMapUint64Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]int8:
+ v2, changed2 := fastpathTV.DecMapUint64Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]int16:
+ fastpathTV.DecMapUint64Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]int16:
+ v2, changed2 := fastpathTV.DecMapUint64Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]int32:
+ fastpathTV.DecMapUint64Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]int32:
+ v2, changed2 := fastpathTV.DecMapUint64Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]int64:
+ fastpathTV.DecMapUint64Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]int64:
+ v2, changed2 := fastpathTV.DecMapUint64Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]float32:
+ fastpathTV.DecMapUint64Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]float32:
+ v2, changed2 := fastpathTV.DecMapUint64Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]float64:
+ fastpathTV.DecMapUint64Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]float64:
+ v2, changed2 := fastpathTV.DecMapUint64Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[uint64]bool:
+ fastpathTV.DecMapUint64BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[uint64]bool:
+ v2, changed2 := fastpathTV.DecMapUint64BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []int:
+ fastpathTV.DecSliceIntV(v, fastpathCheckNilFalse, false, d)
+ case *[]int:
+ v2, changed2 := fastpathTV.DecSliceIntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]interface{}:
+ fastpathTV.DecMapIntIntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[int]interface{}:
+ v2, changed2 := fastpathTV.DecMapIntIntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]string:
+ fastpathTV.DecMapIntStringV(v, fastpathCheckNilFalse, false, d)
+ case *map[int]string:
+ v2, changed2 := fastpathTV.DecMapIntStringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]uint:
+ fastpathTV.DecMapIntUintV(v, fastpathCheckNilFalse, false, d)
+ case *map[int]uint:
+ v2, changed2 := fastpathTV.DecMapIntUintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]uint8:
+ fastpathTV.DecMapIntUint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]uint8:
+ v2, changed2 := fastpathTV.DecMapIntUint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]uint16:
+ fastpathTV.DecMapIntUint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]uint16:
+ v2, changed2 := fastpathTV.DecMapIntUint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]uint32:
+ fastpathTV.DecMapIntUint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]uint32:
+ v2, changed2 := fastpathTV.DecMapIntUint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]uint64:
+ fastpathTV.DecMapIntUint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]uint64:
+ v2, changed2 := fastpathTV.DecMapIntUint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]int:
+ fastpathTV.DecMapIntIntV(v, fastpathCheckNilFalse, false, d)
+ case *map[int]int:
+ v2, changed2 := fastpathTV.DecMapIntIntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]int8:
+ fastpathTV.DecMapIntInt8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]int8:
+ v2, changed2 := fastpathTV.DecMapIntInt8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]int16:
+ fastpathTV.DecMapIntInt16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]int16:
+ v2, changed2 := fastpathTV.DecMapIntInt16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]int32:
+ fastpathTV.DecMapIntInt32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]int32:
+ v2, changed2 := fastpathTV.DecMapIntInt32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]int64:
+ fastpathTV.DecMapIntInt64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]int64:
+ v2, changed2 := fastpathTV.DecMapIntInt64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]float32:
+ fastpathTV.DecMapIntFloat32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]float32:
+ v2, changed2 := fastpathTV.DecMapIntFloat32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]float64:
+ fastpathTV.DecMapIntFloat64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int]float64:
+ v2, changed2 := fastpathTV.DecMapIntFloat64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int]bool:
+ fastpathTV.DecMapIntBoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[int]bool:
+ v2, changed2 := fastpathTV.DecMapIntBoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []int8:
+ fastpathTV.DecSliceInt8V(v, fastpathCheckNilFalse, false, d)
+ case *[]int8:
+ v2, changed2 := fastpathTV.DecSliceInt8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]interface{}:
+ fastpathTV.DecMapInt8IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]interface{}:
+ v2, changed2 := fastpathTV.DecMapInt8IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]string:
+ fastpathTV.DecMapInt8StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]string:
+ v2, changed2 := fastpathTV.DecMapInt8StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]uint:
+ fastpathTV.DecMapInt8UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]uint:
+ v2, changed2 := fastpathTV.DecMapInt8UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]uint8:
+ fastpathTV.DecMapInt8Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]uint8:
+ v2, changed2 := fastpathTV.DecMapInt8Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]uint16:
+ fastpathTV.DecMapInt8Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]uint16:
+ v2, changed2 := fastpathTV.DecMapInt8Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]uint32:
+ fastpathTV.DecMapInt8Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]uint32:
+ v2, changed2 := fastpathTV.DecMapInt8Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]uint64:
+ fastpathTV.DecMapInt8Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]uint64:
+ v2, changed2 := fastpathTV.DecMapInt8Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]int:
+ fastpathTV.DecMapInt8IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]int:
+ v2, changed2 := fastpathTV.DecMapInt8IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]int8:
+ fastpathTV.DecMapInt8Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]int8:
+ v2, changed2 := fastpathTV.DecMapInt8Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]int16:
+ fastpathTV.DecMapInt8Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]int16:
+ v2, changed2 := fastpathTV.DecMapInt8Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]int32:
+ fastpathTV.DecMapInt8Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]int32:
+ v2, changed2 := fastpathTV.DecMapInt8Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]int64:
+ fastpathTV.DecMapInt8Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]int64:
+ v2, changed2 := fastpathTV.DecMapInt8Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]float32:
+ fastpathTV.DecMapInt8Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]float32:
+ v2, changed2 := fastpathTV.DecMapInt8Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]float64:
+ fastpathTV.DecMapInt8Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]float64:
+ v2, changed2 := fastpathTV.DecMapInt8Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int8]bool:
+ fastpathTV.DecMapInt8BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[int8]bool:
+ v2, changed2 := fastpathTV.DecMapInt8BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []int16:
+ fastpathTV.DecSliceInt16V(v, fastpathCheckNilFalse, false, d)
+ case *[]int16:
+ v2, changed2 := fastpathTV.DecSliceInt16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]interface{}:
+ fastpathTV.DecMapInt16IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]interface{}:
+ v2, changed2 := fastpathTV.DecMapInt16IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]string:
+ fastpathTV.DecMapInt16StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]string:
+ v2, changed2 := fastpathTV.DecMapInt16StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]uint:
+ fastpathTV.DecMapInt16UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]uint:
+ v2, changed2 := fastpathTV.DecMapInt16UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]uint8:
+ fastpathTV.DecMapInt16Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]uint8:
+ v2, changed2 := fastpathTV.DecMapInt16Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]uint16:
+ fastpathTV.DecMapInt16Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]uint16:
+ v2, changed2 := fastpathTV.DecMapInt16Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]uint32:
+ fastpathTV.DecMapInt16Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]uint32:
+ v2, changed2 := fastpathTV.DecMapInt16Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]uint64:
+ fastpathTV.DecMapInt16Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]uint64:
+ v2, changed2 := fastpathTV.DecMapInt16Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]int:
+ fastpathTV.DecMapInt16IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]int:
+ v2, changed2 := fastpathTV.DecMapInt16IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]int8:
+ fastpathTV.DecMapInt16Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]int8:
+ v2, changed2 := fastpathTV.DecMapInt16Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]int16:
+ fastpathTV.DecMapInt16Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]int16:
+ v2, changed2 := fastpathTV.DecMapInt16Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]int32:
+ fastpathTV.DecMapInt16Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]int32:
+ v2, changed2 := fastpathTV.DecMapInt16Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]int64:
+ fastpathTV.DecMapInt16Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]int64:
+ v2, changed2 := fastpathTV.DecMapInt16Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]float32:
+ fastpathTV.DecMapInt16Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]float32:
+ v2, changed2 := fastpathTV.DecMapInt16Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]float64:
+ fastpathTV.DecMapInt16Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]float64:
+ v2, changed2 := fastpathTV.DecMapInt16Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int16]bool:
+ fastpathTV.DecMapInt16BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[int16]bool:
+ v2, changed2 := fastpathTV.DecMapInt16BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []int32:
+ fastpathTV.DecSliceInt32V(v, fastpathCheckNilFalse, false, d)
+ case *[]int32:
+ v2, changed2 := fastpathTV.DecSliceInt32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]interface{}:
+ fastpathTV.DecMapInt32IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]interface{}:
+ v2, changed2 := fastpathTV.DecMapInt32IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]string:
+ fastpathTV.DecMapInt32StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]string:
+ v2, changed2 := fastpathTV.DecMapInt32StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]uint:
+ fastpathTV.DecMapInt32UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]uint:
+ v2, changed2 := fastpathTV.DecMapInt32UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]uint8:
+ fastpathTV.DecMapInt32Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]uint8:
+ v2, changed2 := fastpathTV.DecMapInt32Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]uint16:
+ fastpathTV.DecMapInt32Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]uint16:
+ v2, changed2 := fastpathTV.DecMapInt32Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]uint32:
+ fastpathTV.DecMapInt32Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]uint32:
+ v2, changed2 := fastpathTV.DecMapInt32Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]uint64:
+ fastpathTV.DecMapInt32Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]uint64:
+ v2, changed2 := fastpathTV.DecMapInt32Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]int:
+ fastpathTV.DecMapInt32IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]int:
+ v2, changed2 := fastpathTV.DecMapInt32IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]int8:
+ fastpathTV.DecMapInt32Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]int8:
+ v2, changed2 := fastpathTV.DecMapInt32Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]int16:
+ fastpathTV.DecMapInt32Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]int16:
+ v2, changed2 := fastpathTV.DecMapInt32Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]int32:
+ fastpathTV.DecMapInt32Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]int32:
+ v2, changed2 := fastpathTV.DecMapInt32Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]int64:
+ fastpathTV.DecMapInt32Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]int64:
+ v2, changed2 := fastpathTV.DecMapInt32Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]float32:
+ fastpathTV.DecMapInt32Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]float32:
+ v2, changed2 := fastpathTV.DecMapInt32Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]float64:
+ fastpathTV.DecMapInt32Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]float64:
+ v2, changed2 := fastpathTV.DecMapInt32Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int32]bool:
+ fastpathTV.DecMapInt32BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[int32]bool:
+ v2, changed2 := fastpathTV.DecMapInt32BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []int64:
+ fastpathTV.DecSliceInt64V(v, fastpathCheckNilFalse, false, d)
+ case *[]int64:
+ v2, changed2 := fastpathTV.DecSliceInt64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]interface{}:
+ fastpathTV.DecMapInt64IntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]interface{}:
+ v2, changed2 := fastpathTV.DecMapInt64IntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]string:
+ fastpathTV.DecMapInt64StringV(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]string:
+ v2, changed2 := fastpathTV.DecMapInt64StringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]uint:
+ fastpathTV.DecMapInt64UintV(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]uint:
+ v2, changed2 := fastpathTV.DecMapInt64UintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]uint8:
+ fastpathTV.DecMapInt64Uint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]uint8:
+ v2, changed2 := fastpathTV.DecMapInt64Uint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]uint16:
+ fastpathTV.DecMapInt64Uint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]uint16:
+ v2, changed2 := fastpathTV.DecMapInt64Uint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]uint32:
+ fastpathTV.DecMapInt64Uint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]uint32:
+ v2, changed2 := fastpathTV.DecMapInt64Uint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]uint64:
+ fastpathTV.DecMapInt64Uint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]uint64:
+ v2, changed2 := fastpathTV.DecMapInt64Uint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]int:
+ fastpathTV.DecMapInt64IntV(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]int:
+ v2, changed2 := fastpathTV.DecMapInt64IntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]int8:
+ fastpathTV.DecMapInt64Int8V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]int8:
+ v2, changed2 := fastpathTV.DecMapInt64Int8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]int16:
+ fastpathTV.DecMapInt64Int16V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]int16:
+ v2, changed2 := fastpathTV.DecMapInt64Int16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]int32:
+ fastpathTV.DecMapInt64Int32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]int32:
+ v2, changed2 := fastpathTV.DecMapInt64Int32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]int64:
+ fastpathTV.DecMapInt64Int64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]int64:
+ v2, changed2 := fastpathTV.DecMapInt64Int64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]float32:
+ fastpathTV.DecMapInt64Float32V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]float32:
+ v2, changed2 := fastpathTV.DecMapInt64Float32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]float64:
+ fastpathTV.DecMapInt64Float64V(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]float64:
+ v2, changed2 := fastpathTV.DecMapInt64Float64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[int64]bool:
+ fastpathTV.DecMapInt64BoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[int64]bool:
+ v2, changed2 := fastpathTV.DecMapInt64BoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case []bool:
+ fastpathTV.DecSliceBoolV(v, fastpathCheckNilFalse, false, d)
+ case *[]bool:
+ v2, changed2 := fastpathTV.DecSliceBoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]interface{}:
+ fastpathTV.DecMapBoolIntfV(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]interface{}:
+ v2, changed2 := fastpathTV.DecMapBoolIntfV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]string:
+ fastpathTV.DecMapBoolStringV(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]string:
+ v2, changed2 := fastpathTV.DecMapBoolStringV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]uint:
+ fastpathTV.DecMapBoolUintV(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]uint:
+ v2, changed2 := fastpathTV.DecMapBoolUintV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]uint8:
+ fastpathTV.DecMapBoolUint8V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]uint8:
+ v2, changed2 := fastpathTV.DecMapBoolUint8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]uint16:
+ fastpathTV.DecMapBoolUint16V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]uint16:
+ v2, changed2 := fastpathTV.DecMapBoolUint16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]uint32:
+ fastpathTV.DecMapBoolUint32V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]uint32:
+ v2, changed2 := fastpathTV.DecMapBoolUint32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]uint64:
+ fastpathTV.DecMapBoolUint64V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]uint64:
+ v2, changed2 := fastpathTV.DecMapBoolUint64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]int:
+ fastpathTV.DecMapBoolIntV(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]int:
+ v2, changed2 := fastpathTV.DecMapBoolIntV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]int8:
+ fastpathTV.DecMapBoolInt8V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]int8:
+ v2, changed2 := fastpathTV.DecMapBoolInt8V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]int16:
+ fastpathTV.DecMapBoolInt16V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]int16:
+ v2, changed2 := fastpathTV.DecMapBoolInt16V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]int32:
+ fastpathTV.DecMapBoolInt32V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]int32:
+ v2, changed2 := fastpathTV.DecMapBoolInt32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]int64:
+ fastpathTV.DecMapBoolInt64V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]int64:
+ v2, changed2 := fastpathTV.DecMapBoolInt64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]float32:
+ fastpathTV.DecMapBoolFloat32V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]float32:
+ v2, changed2 := fastpathTV.DecMapBoolFloat32V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]float64:
+ fastpathTV.DecMapBoolFloat64V(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]float64:
+ v2, changed2 := fastpathTV.DecMapBoolFloat64V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ case map[bool]bool:
+ fastpathTV.DecMapBoolBoolV(v, fastpathCheckNilFalse, false, d)
+ case *map[bool]bool:
+ v2, changed2 := fastpathTV.DecMapBoolBoolV(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+
+ default:
+ return false
+ }
+ return true
+}
+
+// -- -- fast path functions
+
+func (f *decFnInfo) fastpathDecSliceIntfR(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]interface{})
+ v, changed := fastpathTV.DecSliceIntfV(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]interface{})
+ fastpathTV.DecSliceIntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceIntfX(vp *[]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceIntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceIntfV(v []interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ []interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 16); xtrunc {
+ x2read = xlen
+ }
+ v = make([]interface{}, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 16); xtrunc {
+ x2read = xlen
+ }
+ v = make([]interface{}, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ d.decode(&v[j])
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, nil)
+ d.decode(&v[j])
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, nil)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ d.decode(&v[j])
+
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceStringR(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]string)
+ v, changed := fastpathTV.DecSliceStringV(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]string)
+ fastpathTV.DecSliceStringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceStringX(vp *[]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceStringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceStringV(v []string, checkNil bool, canChange bool,
+ d *Decoder) (_ []string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 16); xtrunc {
+ x2read = xlen
+ }
+ v = make([]string, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 16); xtrunc {
+ x2read = xlen
+ }
+ v = make([]string, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = dd.DecodeString()
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, "")
+ v[j] = dd.DecodeString()
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, "")
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = dd.DecodeString()
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceFloat32R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]float32)
+ v, changed := fastpathTV.DecSliceFloat32V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]float32)
+ fastpathTV.DecSliceFloat32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceFloat32X(vp *[]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceFloat32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceFloat32V(v []float32, checkNil bool, canChange bool,
+ d *Decoder) (_ []float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 4); xtrunc {
+ x2read = xlen
+ }
+ v = make([]float32, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 4); xtrunc {
+ x2read = xlen
+ }
+ v = make([]float32, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = float32(dd.DecodeFloat(true))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = float32(dd.DecodeFloat(true))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = float32(dd.DecodeFloat(true))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceFloat64R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]float64)
+ v, changed := fastpathTV.DecSliceFloat64V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]float64)
+ fastpathTV.DecSliceFloat64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceFloat64X(vp *[]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceFloat64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceFloat64V(v []float64, checkNil bool, canChange bool,
+ d *Decoder) (_ []float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]float64, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]float64, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = dd.DecodeFloat(false)
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = dd.DecodeFloat(false)
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = dd.DecodeFloat(false)
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceUintR(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]uint)
+ v, changed := fastpathTV.DecSliceUintV(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]uint)
+ fastpathTV.DecSliceUintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceUintX(vp *[]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceUintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceUintV(v []uint, checkNil bool, canChange bool,
+ d *Decoder) (_ []uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = uint(dd.DecodeUint(uintBitsize))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = uint(dd.DecodeUint(uintBitsize))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = uint(dd.DecodeUint(uintBitsize))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceUint16R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]uint16)
+ v, changed := fastpathTV.DecSliceUint16V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]uint16)
+ fastpathTV.DecSliceUint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceUint16X(vp *[]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceUint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceUint16V(v []uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ []uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 2); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint16, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 2); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint16, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = uint16(dd.DecodeUint(16))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = uint16(dd.DecodeUint(16))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = uint16(dd.DecodeUint(16))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceUint32R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]uint32)
+ v, changed := fastpathTV.DecSliceUint32V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]uint32)
+ fastpathTV.DecSliceUint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceUint32X(vp *[]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceUint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceUint32V(v []uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ []uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 4); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint32, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 4); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint32, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = uint32(dd.DecodeUint(32))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = uint32(dd.DecodeUint(32))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = uint32(dd.DecodeUint(32))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceUint64R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]uint64)
+ v, changed := fastpathTV.DecSliceUint64V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]uint64)
+ fastpathTV.DecSliceUint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceUint64X(vp *[]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceUint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceUint64V(v []uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ []uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint64, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]uint64, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = dd.DecodeUint(64)
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = dd.DecodeUint(64)
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = dd.DecodeUint(64)
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceIntR(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]int)
+ v, changed := fastpathTV.DecSliceIntV(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]int)
+ fastpathTV.DecSliceIntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceIntX(vp *[]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceIntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceIntV(v []int, checkNil bool, canChange bool,
+ d *Decoder) (_ []int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = int(dd.DecodeInt(intBitsize))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = int(dd.DecodeInt(intBitsize))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = int(dd.DecodeInt(intBitsize))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceInt8R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]int8)
+ v, changed := fastpathTV.DecSliceInt8V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]int8)
+ fastpathTV.DecSliceInt8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceInt8X(vp *[]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceInt8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceInt8V(v []int8, checkNil bool, canChange bool,
+ d *Decoder) (_ []int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 1); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int8, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 1); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int8, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = int8(dd.DecodeInt(8))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = int8(dd.DecodeInt(8))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = int8(dd.DecodeInt(8))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceInt16R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]int16)
+ v, changed := fastpathTV.DecSliceInt16V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]int16)
+ fastpathTV.DecSliceInt16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceInt16X(vp *[]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceInt16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceInt16V(v []int16, checkNil bool, canChange bool,
+ d *Decoder) (_ []int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 2); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int16, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 2); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int16, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = int16(dd.DecodeInt(16))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = int16(dd.DecodeInt(16))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = int16(dd.DecodeInt(16))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceInt32R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]int32)
+ v, changed := fastpathTV.DecSliceInt32V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]int32)
+ fastpathTV.DecSliceInt32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceInt32X(vp *[]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceInt32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceInt32V(v []int32, checkNil bool, canChange bool,
+ d *Decoder) (_ []int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 4); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int32, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 4); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int32, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = int32(dd.DecodeInt(32))
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = int32(dd.DecodeInt(32))
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = int32(dd.DecodeInt(32))
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceInt64R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]int64)
+ v, changed := fastpathTV.DecSliceInt64V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]int64)
+ fastpathTV.DecSliceInt64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceInt64X(vp *[]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceInt64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceInt64V(v []int64, checkNil bool, canChange bool,
+ d *Decoder) (_ []int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int64, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 8); xtrunc {
+ x2read = xlen
+ }
+ v = make([]int64, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = dd.DecodeInt(64)
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, 0)
+ v[j] = dd.DecodeInt(64)
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, 0)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = dd.DecodeInt(64)
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecSliceBoolR(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() {
+ vp := rv.Addr().Interface().(*[]bool)
+ v, changed := fastpathTV.DecSliceBoolV(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]bool)
+ fastpathTV.DecSliceBoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) DecSliceBoolX(vp *[]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecSliceBoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecSliceBoolV(v []bool, checkNil bool, canChange bool,
+ d *Decoder) (_ []bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 1); xtrunc {
+ x2read = xlen
+ }
+ v = make([]bool, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }
+ return v, changed
+ }
+
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, 1); xtrunc {
+ x2read = xlen
+ }
+ v = make([]bool, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+
+ j := 0
+ for ; j < x2read; j++ {
+ v[j] = dd.DecodeBool()
+ }
+ if xtrunc {
+ for ; j < containerLenS; j++ {
+ v = append(v, false)
+ v[j] = dd.DecodeBool()
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, false)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) {
+ v[j] = dd.DecodeBool()
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfIntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]interface{})
+ v, changed := fastpathTV.DecMapIntfIntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]interface{})
+ fastpathTV.DecMapIntfIntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfIntfX(vp *map[interface{}]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfIntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfIntfV(v map[interface{}]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 32)
+ v = make(map[interface{}]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfStringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]string)
+ v, changed := fastpathTV.DecMapIntfStringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]string)
+ fastpathTV.DecMapIntfStringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfStringX(vp *map[interface{}]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfStringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfStringV(v map[interface{}]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 32)
+ v = make(map[interface{}]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfUintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]uint)
+ v, changed := fastpathTV.DecMapIntfUintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]uint)
+ fastpathTV.DecMapIntfUintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfUintX(vp *map[interface{}]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfUintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfUintV(v map[interface{}]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[interface{}]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfUint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]uint8)
+ v, changed := fastpathTV.DecMapIntfUint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]uint8)
+ fastpathTV.DecMapIntfUint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfUint8X(vp *map[interface{}]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfUint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfUint8V(v map[interface{}]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[interface{}]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfUint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]uint16)
+ v, changed := fastpathTV.DecMapIntfUint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]uint16)
+ fastpathTV.DecMapIntfUint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfUint16X(vp *map[interface{}]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfUint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfUint16V(v map[interface{}]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[interface{}]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfUint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]uint32)
+ v, changed := fastpathTV.DecMapIntfUint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]uint32)
+ fastpathTV.DecMapIntfUint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfUint32X(vp *map[interface{}]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfUint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfUint32V(v map[interface{}]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[interface{}]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfUint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]uint64)
+ v, changed := fastpathTV.DecMapIntfUint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]uint64)
+ fastpathTV.DecMapIntfUint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfUint64X(vp *map[interface{}]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfUint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfUint64V(v map[interface{}]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[interface{}]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfIntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]int)
+ v, changed := fastpathTV.DecMapIntfIntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]int)
+ fastpathTV.DecMapIntfIntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfIntX(vp *map[interface{}]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfIntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfIntV(v map[interface{}]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[interface{}]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfInt8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]int8)
+ v, changed := fastpathTV.DecMapIntfInt8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]int8)
+ fastpathTV.DecMapIntfInt8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfInt8X(vp *map[interface{}]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfInt8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfInt8V(v map[interface{}]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[interface{}]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfInt16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]int16)
+ v, changed := fastpathTV.DecMapIntfInt16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]int16)
+ fastpathTV.DecMapIntfInt16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfInt16X(vp *map[interface{}]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfInt16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfInt16V(v map[interface{}]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[interface{}]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfInt32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]int32)
+ v, changed := fastpathTV.DecMapIntfInt32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]int32)
+ fastpathTV.DecMapIntfInt32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfInt32X(vp *map[interface{}]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfInt32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfInt32V(v map[interface{}]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[interface{}]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfInt64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]int64)
+ v, changed := fastpathTV.DecMapIntfInt64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]int64)
+ fastpathTV.DecMapIntfInt64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfInt64X(vp *map[interface{}]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfInt64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfInt64V(v map[interface{}]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[interface{}]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfFloat32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]float32)
+ v, changed := fastpathTV.DecMapIntfFloat32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]float32)
+ fastpathTV.DecMapIntfFloat32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfFloat32X(vp *map[interface{}]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfFloat32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfFloat32V(v map[interface{}]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[interface{}]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfFloat64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]float64)
+ v, changed := fastpathTV.DecMapIntfFloat64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]float64)
+ fastpathTV.DecMapIntfFloat64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfFloat64X(vp *map[interface{}]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfFloat64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfFloat64V(v map[interface{}]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[interface{}]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntfBoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[interface{}]bool)
+ v, changed := fastpathTV.DecMapIntfBoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[interface{}]bool)
+ fastpathTV.DecMapIntfBoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntfBoolX(vp *map[interface{}]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntfBoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntfBoolV(v map[interface{}]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[interface{}]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[interface{}]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv)
+ }
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringIntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]interface{})
+ v, changed := fastpathTV.DecMapStringIntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]interface{})
+ fastpathTV.DecMapStringIntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringIntfX(vp *map[string]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringIntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringIntfV(v map[string]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 32)
+ v = make(map[string]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringStringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]string)
+ v, changed := fastpathTV.DecMapStringStringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]string)
+ fastpathTV.DecMapStringStringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringStringX(vp *map[string]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringStringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringStringV(v map[string]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 32)
+ v = make(map[string]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringUintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]uint)
+ v, changed := fastpathTV.DecMapStringUintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]uint)
+ fastpathTV.DecMapStringUintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringUintX(vp *map[string]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringUintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringUintV(v map[string]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[string]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringUint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]uint8)
+ v, changed := fastpathTV.DecMapStringUint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]uint8)
+ fastpathTV.DecMapStringUint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringUint8X(vp *map[string]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringUint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringUint8V(v map[string]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[string]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringUint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]uint16)
+ v, changed := fastpathTV.DecMapStringUint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]uint16)
+ fastpathTV.DecMapStringUint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringUint16X(vp *map[string]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringUint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringUint16V(v map[string]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[string]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringUint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]uint32)
+ v, changed := fastpathTV.DecMapStringUint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]uint32)
+ fastpathTV.DecMapStringUint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringUint32X(vp *map[string]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringUint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringUint32V(v map[string]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[string]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringUint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]uint64)
+ v, changed := fastpathTV.DecMapStringUint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]uint64)
+ fastpathTV.DecMapStringUint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringUint64X(vp *map[string]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringUint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringUint64V(v map[string]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[string]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringIntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]int)
+ v, changed := fastpathTV.DecMapStringIntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]int)
+ fastpathTV.DecMapStringIntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringIntX(vp *map[string]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringIntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringIntV(v map[string]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[string]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringInt8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]int8)
+ v, changed := fastpathTV.DecMapStringInt8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]int8)
+ fastpathTV.DecMapStringInt8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringInt8X(vp *map[string]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringInt8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringInt8V(v map[string]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[string]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringInt16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]int16)
+ v, changed := fastpathTV.DecMapStringInt16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]int16)
+ fastpathTV.DecMapStringInt16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringInt16X(vp *map[string]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringInt16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringInt16V(v map[string]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[string]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringInt32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]int32)
+ v, changed := fastpathTV.DecMapStringInt32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]int32)
+ fastpathTV.DecMapStringInt32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringInt32X(vp *map[string]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringInt32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringInt32V(v map[string]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[string]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringInt64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]int64)
+ v, changed := fastpathTV.DecMapStringInt64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]int64)
+ fastpathTV.DecMapStringInt64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringInt64X(vp *map[string]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringInt64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringInt64V(v map[string]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[string]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringFloat32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]float32)
+ v, changed := fastpathTV.DecMapStringFloat32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]float32)
+ fastpathTV.DecMapStringFloat32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringFloat32X(vp *map[string]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringFloat32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringFloat32V(v map[string]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[string]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringFloat64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]float64)
+ v, changed := fastpathTV.DecMapStringFloat64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]float64)
+ fastpathTV.DecMapStringFloat64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringFloat64X(vp *map[string]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringFloat64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringFloat64V(v map[string]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[string]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapStringBoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[string]bool)
+ v, changed := fastpathTV.DecMapStringBoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[string]bool)
+ fastpathTV.DecMapStringBoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapStringBoolX(vp *map[string]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapStringBoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapStringBoolV(v map[string]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[string]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[string]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeString()
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]interface{})
+ v, changed := fastpathTV.DecMapFloat32IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]interface{})
+ fastpathTV.DecMapFloat32IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32IntfX(vp *map[float32]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32IntfV(v map[float32]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[float32]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]string)
+ v, changed := fastpathTV.DecMapFloat32StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]string)
+ fastpathTV.DecMapFloat32StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32StringX(vp *map[float32]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32StringV(v map[float32]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[float32]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]uint)
+ v, changed := fastpathTV.DecMapFloat32UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]uint)
+ fastpathTV.DecMapFloat32UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32UintX(vp *map[float32]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32UintV(v map[float32]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float32]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]uint8)
+ v, changed := fastpathTV.DecMapFloat32Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]uint8)
+ fastpathTV.DecMapFloat32Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Uint8X(vp *map[float32]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Uint8V(v map[float32]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[float32]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]uint16)
+ v, changed := fastpathTV.DecMapFloat32Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]uint16)
+ fastpathTV.DecMapFloat32Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Uint16X(vp *map[float32]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Uint16V(v map[float32]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[float32]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]uint32)
+ v, changed := fastpathTV.DecMapFloat32Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]uint32)
+ fastpathTV.DecMapFloat32Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Uint32X(vp *map[float32]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Uint32V(v map[float32]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[float32]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]uint64)
+ v, changed := fastpathTV.DecMapFloat32Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]uint64)
+ fastpathTV.DecMapFloat32Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Uint64X(vp *map[float32]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Uint64V(v map[float32]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float32]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]int)
+ v, changed := fastpathTV.DecMapFloat32IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]int)
+ fastpathTV.DecMapFloat32IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32IntX(vp *map[float32]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32IntV(v map[float32]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float32]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]int8)
+ v, changed := fastpathTV.DecMapFloat32Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]int8)
+ fastpathTV.DecMapFloat32Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Int8X(vp *map[float32]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Int8V(v map[float32]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[float32]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]int16)
+ v, changed := fastpathTV.DecMapFloat32Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]int16)
+ fastpathTV.DecMapFloat32Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Int16X(vp *map[float32]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Int16V(v map[float32]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[float32]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]int32)
+ v, changed := fastpathTV.DecMapFloat32Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]int32)
+ fastpathTV.DecMapFloat32Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Int32X(vp *map[float32]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Int32V(v map[float32]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[float32]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]int64)
+ v, changed := fastpathTV.DecMapFloat32Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]int64)
+ fastpathTV.DecMapFloat32Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Int64X(vp *map[float32]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Int64V(v map[float32]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float32]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]float32)
+ v, changed := fastpathTV.DecMapFloat32Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]float32)
+ fastpathTV.DecMapFloat32Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Float32X(vp *map[float32]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Float32V(v map[float32]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[float32]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]float64)
+ v, changed := fastpathTV.DecMapFloat32Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]float64)
+ fastpathTV.DecMapFloat32Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32Float64X(vp *map[float32]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32Float64V(v map[float32]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float32]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat32BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float32]bool)
+ v, changed := fastpathTV.DecMapFloat32BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float32]bool)
+ fastpathTV.DecMapFloat32BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat32BoolX(vp *map[float32]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat32BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat32BoolV(v map[float32]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float32]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[float32]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := float32(dd.DecodeFloat(true))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]interface{})
+ v, changed := fastpathTV.DecMapFloat64IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]interface{})
+ fastpathTV.DecMapFloat64IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64IntfX(vp *map[float64]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64IntfV(v map[float64]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[float64]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]string)
+ v, changed := fastpathTV.DecMapFloat64StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]string)
+ fastpathTV.DecMapFloat64StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64StringX(vp *map[float64]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64StringV(v map[float64]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[float64]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]uint)
+ v, changed := fastpathTV.DecMapFloat64UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]uint)
+ fastpathTV.DecMapFloat64UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64UintX(vp *map[float64]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64UintV(v map[float64]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[float64]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]uint8)
+ v, changed := fastpathTV.DecMapFloat64Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]uint8)
+ fastpathTV.DecMapFloat64Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Uint8X(vp *map[float64]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Uint8V(v map[float64]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[float64]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]uint16)
+ v, changed := fastpathTV.DecMapFloat64Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]uint16)
+ fastpathTV.DecMapFloat64Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Uint16X(vp *map[float64]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Uint16V(v map[float64]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[float64]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]uint32)
+ v, changed := fastpathTV.DecMapFloat64Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]uint32)
+ fastpathTV.DecMapFloat64Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Uint32X(vp *map[float64]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Uint32V(v map[float64]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float64]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]uint64)
+ v, changed := fastpathTV.DecMapFloat64Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]uint64)
+ fastpathTV.DecMapFloat64Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Uint64X(vp *map[float64]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Uint64V(v map[float64]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[float64]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]int)
+ v, changed := fastpathTV.DecMapFloat64IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]int)
+ fastpathTV.DecMapFloat64IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64IntX(vp *map[float64]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64IntV(v map[float64]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[float64]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]int8)
+ v, changed := fastpathTV.DecMapFloat64Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]int8)
+ fastpathTV.DecMapFloat64Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Int8X(vp *map[float64]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Int8V(v map[float64]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[float64]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]int16)
+ v, changed := fastpathTV.DecMapFloat64Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]int16)
+ fastpathTV.DecMapFloat64Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Int16X(vp *map[float64]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Int16V(v map[float64]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[float64]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]int32)
+ v, changed := fastpathTV.DecMapFloat64Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]int32)
+ fastpathTV.DecMapFloat64Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Int32X(vp *map[float64]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Int32V(v map[float64]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float64]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]int64)
+ v, changed := fastpathTV.DecMapFloat64Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]int64)
+ fastpathTV.DecMapFloat64Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Int64X(vp *map[float64]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Int64V(v map[float64]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[float64]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]float32)
+ v, changed := fastpathTV.DecMapFloat64Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]float32)
+ fastpathTV.DecMapFloat64Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Float32X(vp *map[float64]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Float32V(v map[float64]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[float64]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]float64)
+ v, changed := fastpathTV.DecMapFloat64Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]float64)
+ fastpathTV.DecMapFloat64Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64Float64X(vp *map[float64]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64Float64V(v map[float64]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[float64]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapFloat64BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[float64]bool)
+ v, changed := fastpathTV.DecMapFloat64BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[float64]bool)
+ fastpathTV.DecMapFloat64BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapFloat64BoolX(vp *map[float64]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapFloat64BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapFloat64BoolV(v map[float64]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[float64]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[float64]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeFloat(false)
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintIntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]interface{})
+ v, changed := fastpathTV.DecMapUintIntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]interface{})
+ fastpathTV.DecMapUintIntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintIntfX(vp *map[uint]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintIntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintIntfV(v map[uint]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[uint]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintStringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]string)
+ v, changed := fastpathTV.DecMapUintStringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]string)
+ fastpathTV.DecMapUintStringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintStringX(vp *map[uint]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintStringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintStringV(v map[uint]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[uint]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintUintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]uint)
+ v, changed := fastpathTV.DecMapUintUintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]uint)
+ fastpathTV.DecMapUintUintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintUintX(vp *map[uint]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintUintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintUintV(v map[uint]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintUint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]uint8)
+ v, changed := fastpathTV.DecMapUintUint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]uint8)
+ fastpathTV.DecMapUintUint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintUint8X(vp *map[uint]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintUint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintUint8V(v map[uint]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintUint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]uint16)
+ v, changed := fastpathTV.DecMapUintUint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]uint16)
+ fastpathTV.DecMapUintUint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintUint16X(vp *map[uint]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintUint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintUint16V(v map[uint]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintUint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]uint32)
+ v, changed := fastpathTV.DecMapUintUint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]uint32)
+ fastpathTV.DecMapUintUint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintUint32X(vp *map[uint]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintUint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintUint32V(v map[uint]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintUint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]uint64)
+ v, changed := fastpathTV.DecMapUintUint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]uint64)
+ fastpathTV.DecMapUintUint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintUint64X(vp *map[uint]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintUint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintUint64V(v map[uint]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintIntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]int)
+ v, changed := fastpathTV.DecMapUintIntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]int)
+ fastpathTV.DecMapUintIntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintIntX(vp *map[uint]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintIntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintIntV(v map[uint]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintInt8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]int8)
+ v, changed := fastpathTV.DecMapUintInt8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]int8)
+ fastpathTV.DecMapUintInt8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintInt8X(vp *map[uint]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintInt8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintInt8V(v map[uint]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintInt16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]int16)
+ v, changed := fastpathTV.DecMapUintInt16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]int16)
+ fastpathTV.DecMapUintInt16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintInt16X(vp *map[uint]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintInt16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintInt16V(v map[uint]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintInt32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]int32)
+ v, changed := fastpathTV.DecMapUintInt32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]int32)
+ fastpathTV.DecMapUintInt32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintInt32X(vp *map[uint]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintInt32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintInt32V(v map[uint]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintInt64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]int64)
+ v, changed := fastpathTV.DecMapUintInt64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]int64)
+ fastpathTV.DecMapUintInt64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintInt64X(vp *map[uint]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintInt64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintInt64V(v map[uint]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintFloat32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]float32)
+ v, changed := fastpathTV.DecMapUintFloat32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]float32)
+ fastpathTV.DecMapUintFloat32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintFloat32X(vp *map[uint]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintFloat32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintFloat32V(v map[uint]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintFloat64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]float64)
+ v, changed := fastpathTV.DecMapUintFloat64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]float64)
+ fastpathTV.DecMapUintFloat64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintFloat64X(vp *map[uint]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintFloat64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintFloat64V(v map[uint]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUintBoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint]bool)
+ v, changed := fastpathTV.DecMapUintBoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint]bool)
+ fastpathTV.DecMapUintBoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUintBoolX(vp *map[uint]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUintBoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUintBoolV(v map[uint]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint(dd.DecodeUint(uintBitsize))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]interface{})
+ v, changed := fastpathTV.DecMapUint8IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]interface{})
+ fastpathTV.DecMapUint8IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8IntfX(vp *map[uint8]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8IntfV(v map[uint8]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[uint8]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]string)
+ v, changed := fastpathTV.DecMapUint8StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]string)
+ fastpathTV.DecMapUint8StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8StringX(vp *map[uint8]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8StringV(v map[uint8]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[uint8]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]uint)
+ v, changed := fastpathTV.DecMapUint8UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]uint)
+ fastpathTV.DecMapUint8UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8UintX(vp *map[uint8]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8UintV(v map[uint8]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint8]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]uint8)
+ v, changed := fastpathTV.DecMapUint8Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]uint8)
+ fastpathTV.DecMapUint8Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Uint8X(vp *map[uint8]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Uint8V(v map[uint8]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[uint8]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]uint16)
+ v, changed := fastpathTV.DecMapUint8Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]uint16)
+ fastpathTV.DecMapUint8Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Uint16X(vp *map[uint8]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Uint16V(v map[uint8]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[uint8]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]uint32)
+ v, changed := fastpathTV.DecMapUint8Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]uint32)
+ fastpathTV.DecMapUint8Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Uint32X(vp *map[uint8]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Uint32V(v map[uint8]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[uint8]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]uint64)
+ v, changed := fastpathTV.DecMapUint8Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]uint64)
+ fastpathTV.DecMapUint8Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Uint64X(vp *map[uint8]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Uint64V(v map[uint8]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint8]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]int)
+ v, changed := fastpathTV.DecMapUint8IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]int)
+ fastpathTV.DecMapUint8IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8IntX(vp *map[uint8]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8IntV(v map[uint8]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint8]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]int8)
+ v, changed := fastpathTV.DecMapUint8Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]int8)
+ fastpathTV.DecMapUint8Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Int8X(vp *map[uint8]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Int8V(v map[uint8]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[uint8]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]int16)
+ v, changed := fastpathTV.DecMapUint8Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]int16)
+ fastpathTV.DecMapUint8Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Int16X(vp *map[uint8]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Int16V(v map[uint8]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[uint8]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]int32)
+ v, changed := fastpathTV.DecMapUint8Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]int32)
+ fastpathTV.DecMapUint8Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Int32X(vp *map[uint8]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Int32V(v map[uint8]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[uint8]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]int64)
+ v, changed := fastpathTV.DecMapUint8Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]int64)
+ fastpathTV.DecMapUint8Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Int64X(vp *map[uint8]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Int64V(v map[uint8]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint8]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]float32)
+ v, changed := fastpathTV.DecMapUint8Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]float32)
+ fastpathTV.DecMapUint8Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Float32X(vp *map[uint8]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Float32V(v map[uint8]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[uint8]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]float64)
+ v, changed := fastpathTV.DecMapUint8Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]float64)
+ fastpathTV.DecMapUint8Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8Float64X(vp *map[uint8]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8Float64V(v map[uint8]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint8]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint8BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint8]bool)
+ v, changed := fastpathTV.DecMapUint8BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint8]bool)
+ fastpathTV.DecMapUint8BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint8BoolX(vp *map[uint8]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint8BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint8BoolV(v map[uint8]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint8]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[uint8]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint8(dd.DecodeUint(8))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]interface{})
+ v, changed := fastpathTV.DecMapUint16IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]interface{})
+ fastpathTV.DecMapUint16IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16IntfX(vp *map[uint16]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16IntfV(v map[uint16]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[uint16]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]string)
+ v, changed := fastpathTV.DecMapUint16StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]string)
+ fastpathTV.DecMapUint16StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16StringX(vp *map[uint16]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16StringV(v map[uint16]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[uint16]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]uint)
+ v, changed := fastpathTV.DecMapUint16UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]uint)
+ fastpathTV.DecMapUint16UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16UintX(vp *map[uint16]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16UintV(v map[uint16]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint16]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]uint8)
+ v, changed := fastpathTV.DecMapUint16Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]uint8)
+ fastpathTV.DecMapUint16Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Uint8X(vp *map[uint16]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Uint8V(v map[uint16]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[uint16]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]uint16)
+ v, changed := fastpathTV.DecMapUint16Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]uint16)
+ fastpathTV.DecMapUint16Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Uint16X(vp *map[uint16]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Uint16V(v map[uint16]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 4)
+ v = make(map[uint16]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]uint32)
+ v, changed := fastpathTV.DecMapUint16Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]uint32)
+ fastpathTV.DecMapUint16Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Uint32X(vp *map[uint16]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Uint32V(v map[uint16]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[uint16]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]uint64)
+ v, changed := fastpathTV.DecMapUint16Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]uint64)
+ fastpathTV.DecMapUint16Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Uint64X(vp *map[uint16]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Uint64V(v map[uint16]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint16]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]int)
+ v, changed := fastpathTV.DecMapUint16IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]int)
+ fastpathTV.DecMapUint16IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16IntX(vp *map[uint16]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16IntV(v map[uint16]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint16]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]int8)
+ v, changed := fastpathTV.DecMapUint16Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]int8)
+ fastpathTV.DecMapUint16Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Int8X(vp *map[uint16]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Int8V(v map[uint16]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[uint16]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]int16)
+ v, changed := fastpathTV.DecMapUint16Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]int16)
+ fastpathTV.DecMapUint16Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Int16X(vp *map[uint16]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Int16V(v map[uint16]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 4)
+ v = make(map[uint16]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]int32)
+ v, changed := fastpathTV.DecMapUint16Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]int32)
+ fastpathTV.DecMapUint16Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Int32X(vp *map[uint16]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Int32V(v map[uint16]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[uint16]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]int64)
+ v, changed := fastpathTV.DecMapUint16Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]int64)
+ fastpathTV.DecMapUint16Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Int64X(vp *map[uint16]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Int64V(v map[uint16]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint16]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]float32)
+ v, changed := fastpathTV.DecMapUint16Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]float32)
+ fastpathTV.DecMapUint16Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Float32X(vp *map[uint16]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Float32V(v map[uint16]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[uint16]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]float64)
+ v, changed := fastpathTV.DecMapUint16Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]float64)
+ fastpathTV.DecMapUint16Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16Float64X(vp *map[uint16]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16Float64V(v map[uint16]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint16]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint16BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint16]bool)
+ v, changed := fastpathTV.DecMapUint16BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint16]bool)
+ fastpathTV.DecMapUint16BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint16BoolX(vp *map[uint16]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint16BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint16BoolV(v map[uint16]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint16]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[uint16]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint16(dd.DecodeUint(16))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]interface{})
+ v, changed := fastpathTV.DecMapUint32IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]interface{})
+ fastpathTV.DecMapUint32IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32IntfX(vp *map[uint32]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32IntfV(v map[uint32]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[uint32]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]string)
+ v, changed := fastpathTV.DecMapUint32StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]string)
+ fastpathTV.DecMapUint32StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32StringX(vp *map[uint32]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32StringV(v map[uint32]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[uint32]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]uint)
+ v, changed := fastpathTV.DecMapUint32UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]uint)
+ fastpathTV.DecMapUint32UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32UintX(vp *map[uint32]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32UintV(v map[uint32]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint32]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]uint8)
+ v, changed := fastpathTV.DecMapUint32Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]uint8)
+ fastpathTV.DecMapUint32Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Uint8X(vp *map[uint32]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Uint8V(v map[uint32]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[uint32]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]uint16)
+ v, changed := fastpathTV.DecMapUint32Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]uint16)
+ fastpathTV.DecMapUint32Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Uint16X(vp *map[uint32]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Uint16V(v map[uint32]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[uint32]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]uint32)
+ v, changed := fastpathTV.DecMapUint32Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]uint32)
+ fastpathTV.DecMapUint32Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Uint32X(vp *map[uint32]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Uint32V(v map[uint32]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[uint32]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]uint64)
+ v, changed := fastpathTV.DecMapUint32Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]uint64)
+ fastpathTV.DecMapUint32Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Uint64X(vp *map[uint32]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Uint64V(v map[uint32]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint32]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]int)
+ v, changed := fastpathTV.DecMapUint32IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]int)
+ fastpathTV.DecMapUint32IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32IntX(vp *map[uint32]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32IntV(v map[uint32]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint32]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]int8)
+ v, changed := fastpathTV.DecMapUint32Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]int8)
+ fastpathTV.DecMapUint32Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Int8X(vp *map[uint32]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Int8V(v map[uint32]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[uint32]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]int16)
+ v, changed := fastpathTV.DecMapUint32Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]int16)
+ fastpathTV.DecMapUint32Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Int16X(vp *map[uint32]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Int16V(v map[uint32]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[uint32]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]int32)
+ v, changed := fastpathTV.DecMapUint32Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]int32)
+ fastpathTV.DecMapUint32Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Int32X(vp *map[uint32]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Int32V(v map[uint32]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[uint32]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]int64)
+ v, changed := fastpathTV.DecMapUint32Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]int64)
+ fastpathTV.DecMapUint32Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Int64X(vp *map[uint32]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Int64V(v map[uint32]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint32]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]float32)
+ v, changed := fastpathTV.DecMapUint32Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]float32)
+ fastpathTV.DecMapUint32Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Float32X(vp *map[uint32]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Float32V(v map[uint32]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[uint32]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]float64)
+ v, changed := fastpathTV.DecMapUint32Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]float64)
+ fastpathTV.DecMapUint32Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32Float64X(vp *map[uint32]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32Float64V(v map[uint32]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint32]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint32BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint32]bool)
+ v, changed := fastpathTV.DecMapUint32BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint32]bool)
+ fastpathTV.DecMapUint32BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint32BoolX(vp *map[uint32]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint32BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint32BoolV(v map[uint32]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint32]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[uint32]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := uint32(dd.DecodeUint(32))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]interface{})
+ v, changed := fastpathTV.DecMapUint64IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]interface{})
+ fastpathTV.DecMapUint64IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64IntfX(vp *map[uint64]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64IntfV(v map[uint64]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[uint64]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]string)
+ v, changed := fastpathTV.DecMapUint64StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]string)
+ fastpathTV.DecMapUint64StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64StringX(vp *map[uint64]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64StringV(v map[uint64]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[uint64]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]uint)
+ v, changed := fastpathTV.DecMapUint64UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]uint)
+ fastpathTV.DecMapUint64UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64UintX(vp *map[uint64]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64UintV(v map[uint64]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint64]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]uint8)
+ v, changed := fastpathTV.DecMapUint64Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]uint8)
+ fastpathTV.DecMapUint64Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Uint8X(vp *map[uint64]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Uint8V(v map[uint64]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint64]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]uint16)
+ v, changed := fastpathTV.DecMapUint64Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]uint16)
+ fastpathTV.DecMapUint64Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Uint16X(vp *map[uint64]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Uint16V(v map[uint64]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint64]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]uint32)
+ v, changed := fastpathTV.DecMapUint64Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]uint32)
+ fastpathTV.DecMapUint64Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Uint32X(vp *map[uint64]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Uint32V(v map[uint64]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint64]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]uint64)
+ v, changed := fastpathTV.DecMapUint64Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]uint64)
+ fastpathTV.DecMapUint64Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Uint64X(vp *map[uint64]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Uint64V(v map[uint64]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint64]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]int)
+ v, changed := fastpathTV.DecMapUint64IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]int)
+ fastpathTV.DecMapUint64IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64IntX(vp *map[uint64]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64IntV(v map[uint64]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint64]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]int8)
+ v, changed := fastpathTV.DecMapUint64Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]int8)
+ fastpathTV.DecMapUint64Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Int8X(vp *map[uint64]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Int8V(v map[uint64]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint64]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]int16)
+ v, changed := fastpathTV.DecMapUint64Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]int16)
+ fastpathTV.DecMapUint64Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Int16X(vp *map[uint64]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Int16V(v map[uint64]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[uint64]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]int32)
+ v, changed := fastpathTV.DecMapUint64Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]int32)
+ fastpathTV.DecMapUint64Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Int32X(vp *map[uint64]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Int32V(v map[uint64]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint64]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]int64)
+ v, changed := fastpathTV.DecMapUint64Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]int64)
+ fastpathTV.DecMapUint64Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Int64X(vp *map[uint64]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Int64V(v map[uint64]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint64]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]float32)
+ v, changed := fastpathTV.DecMapUint64Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]float32)
+ fastpathTV.DecMapUint64Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Float32X(vp *map[uint64]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Float32V(v map[uint64]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[uint64]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]float64)
+ v, changed := fastpathTV.DecMapUint64Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]float64)
+ fastpathTV.DecMapUint64Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64Float64X(vp *map[uint64]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64Float64V(v map[uint64]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[uint64]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapUint64BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[uint64]bool)
+ v, changed := fastpathTV.DecMapUint64BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[uint64]bool)
+ fastpathTV.DecMapUint64BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapUint64BoolX(vp *map[uint64]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapUint64BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapUint64BoolV(v map[uint64]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[uint64]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[uint64]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeUint(64)
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntIntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]interface{})
+ v, changed := fastpathTV.DecMapIntIntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]interface{})
+ fastpathTV.DecMapIntIntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntIntfX(vp *map[int]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntIntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntIntfV(v map[int]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[int]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntStringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]string)
+ v, changed := fastpathTV.DecMapIntStringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]string)
+ fastpathTV.DecMapIntStringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntStringX(vp *map[int]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntStringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntStringV(v map[int]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[int]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntUintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]uint)
+ v, changed := fastpathTV.DecMapIntUintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]uint)
+ fastpathTV.DecMapIntUintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntUintX(vp *map[int]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntUintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntUintV(v map[int]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntUint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]uint8)
+ v, changed := fastpathTV.DecMapIntUint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]uint8)
+ fastpathTV.DecMapIntUint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntUint8X(vp *map[int]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntUint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntUint8V(v map[int]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntUint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]uint16)
+ v, changed := fastpathTV.DecMapIntUint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]uint16)
+ fastpathTV.DecMapIntUint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntUint16X(vp *map[int]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntUint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntUint16V(v map[int]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntUint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]uint32)
+ v, changed := fastpathTV.DecMapIntUint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]uint32)
+ fastpathTV.DecMapIntUint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntUint32X(vp *map[int]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntUint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntUint32V(v map[int]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntUint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]uint64)
+ v, changed := fastpathTV.DecMapIntUint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]uint64)
+ fastpathTV.DecMapIntUint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntUint64X(vp *map[int]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntUint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntUint64V(v map[int]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntIntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]int)
+ v, changed := fastpathTV.DecMapIntIntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]int)
+ fastpathTV.DecMapIntIntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntIntX(vp *map[int]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntIntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntIntV(v map[int]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntInt8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]int8)
+ v, changed := fastpathTV.DecMapIntInt8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]int8)
+ fastpathTV.DecMapIntInt8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntInt8X(vp *map[int]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntInt8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntInt8V(v map[int]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntInt16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]int16)
+ v, changed := fastpathTV.DecMapIntInt16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]int16)
+ fastpathTV.DecMapIntInt16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntInt16X(vp *map[int]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntInt16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntInt16V(v map[int]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntInt32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]int32)
+ v, changed := fastpathTV.DecMapIntInt32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]int32)
+ fastpathTV.DecMapIntInt32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntInt32X(vp *map[int]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntInt32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntInt32V(v map[int]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntInt64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]int64)
+ v, changed := fastpathTV.DecMapIntInt64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]int64)
+ fastpathTV.DecMapIntInt64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntInt64X(vp *map[int]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntInt64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntInt64V(v map[int]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntFloat32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]float32)
+ v, changed := fastpathTV.DecMapIntFloat32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]float32)
+ fastpathTV.DecMapIntFloat32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntFloat32X(vp *map[int]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntFloat32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntFloat32V(v map[int]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntFloat64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]float64)
+ v, changed := fastpathTV.DecMapIntFloat64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]float64)
+ fastpathTV.DecMapIntFloat64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntFloat64X(vp *map[int]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntFloat64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntFloat64V(v map[int]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapIntBoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int]bool)
+ v, changed := fastpathTV.DecMapIntBoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int]bool)
+ fastpathTV.DecMapIntBoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapIntBoolX(vp *map[int]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapIntBoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapIntBoolV(v map[int]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int(dd.DecodeInt(intBitsize))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]interface{})
+ v, changed := fastpathTV.DecMapInt8IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]interface{})
+ fastpathTV.DecMapInt8IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8IntfX(vp *map[int8]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8IntfV(v map[int8]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[int8]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]string)
+ v, changed := fastpathTV.DecMapInt8StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]string)
+ fastpathTV.DecMapInt8StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8StringX(vp *map[int8]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8StringV(v map[int8]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[int8]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]uint)
+ v, changed := fastpathTV.DecMapInt8UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]uint)
+ fastpathTV.DecMapInt8UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8UintX(vp *map[int8]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8UintV(v map[int8]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int8]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]uint8)
+ v, changed := fastpathTV.DecMapInt8Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]uint8)
+ fastpathTV.DecMapInt8Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Uint8X(vp *map[int8]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Uint8V(v map[int8]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[int8]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]uint16)
+ v, changed := fastpathTV.DecMapInt8Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]uint16)
+ fastpathTV.DecMapInt8Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Uint16X(vp *map[int8]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Uint16V(v map[int8]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[int8]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]uint32)
+ v, changed := fastpathTV.DecMapInt8Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]uint32)
+ fastpathTV.DecMapInt8Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Uint32X(vp *map[int8]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Uint32V(v map[int8]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[int8]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]uint64)
+ v, changed := fastpathTV.DecMapInt8Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]uint64)
+ fastpathTV.DecMapInt8Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Uint64X(vp *map[int8]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Uint64V(v map[int8]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int8]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]int)
+ v, changed := fastpathTV.DecMapInt8IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]int)
+ fastpathTV.DecMapInt8IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8IntX(vp *map[int8]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8IntV(v map[int8]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int8]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]int8)
+ v, changed := fastpathTV.DecMapInt8Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]int8)
+ fastpathTV.DecMapInt8Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Int8X(vp *map[int8]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Int8V(v map[int8]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[int8]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]int16)
+ v, changed := fastpathTV.DecMapInt8Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]int16)
+ fastpathTV.DecMapInt8Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Int16X(vp *map[int8]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Int16V(v map[int8]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[int8]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]int32)
+ v, changed := fastpathTV.DecMapInt8Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]int32)
+ fastpathTV.DecMapInt8Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Int32X(vp *map[int8]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Int32V(v map[int8]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[int8]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]int64)
+ v, changed := fastpathTV.DecMapInt8Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]int64)
+ fastpathTV.DecMapInt8Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Int64X(vp *map[int8]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Int64V(v map[int8]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int8]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]float32)
+ v, changed := fastpathTV.DecMapInt8Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]float32)
+ fastpathTV.DecMapInt8Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Float32X(vp *map[int8]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Float32V(v map[int8]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[int8]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]float64)
+ v, changed := fastpathTV.DecMapInt8Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]float64)
+ fastpathTV.DecMapInt8Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8Float64X(vp *map[int8]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8Float64V(v map[int8]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int8]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt8BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int8]bool)
+ v, changed := fastpathTV.DecMapInt8BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int8]bool)
+ fastpathTV.DecMapInt8BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt8BoolX(vp *map[int8]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt8BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt8BoolV(v map[int8]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int8]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[int8]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int8(dd.DecodeInt(8))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]interface{})
+ v, changed := fastpathTV.DecMapInt16IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]interface{})
+ fastpathTV.DecMapInt16IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16IntfX(vp *map[int16]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16IntfV(v map[int16]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[int16]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]string)
+ v, changed := fastpathTV.DecMapInt16StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]string)
+ fastpathTV.DecMapInt16StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16StringX(vp *map[int16]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16StringV(v map[int16]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 18)
+ v = make(map[int16]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]uint)
+ v, changed := fastpathTV.DecMapInt16UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]uint)
+ fastpathTV.DecMapInt16UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16UintX(vp *map[int16]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16UintV(v map[int16]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int16]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]uint8)
+ v, changed := fastpathTV.DecMapInt16Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]uint8)
+ fastpathTV.DecMapInt16Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Uint8X(vp *map[int16]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Uint8V(v map[int16]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[int16]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]uint16)
+ v, changed := fastpathTV.DecMapInt16Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]uint16)
+ fastpathTV.DecMapInt16Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Uint16X(vp *map[int16]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Uint16V(v map[int16]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 4)
+ v = make(map[int16]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]uint32)
+ v, changed := fastpathTV.DecMapInt16Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]uint32)
+ fastpathTV.DecMapInt16Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Uint32X(vp *map[int16]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Uint32V(v map[int16]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[int16]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]uint64)
+ v, changed := fastpathTV.DecMapInt16Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]uint64)
+ fastpathTV.DecMapInt16Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Uint64X(vp *map[int16]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Uint64V(v map[int16]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int16]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]int)
+ v, changed := fastpathTV.DecMapInt16IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]int)
+ fastpathTV.DecMapInt16IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16IntX(vp *map[int16]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16IntV(v map[int16]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int16]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]int8)
+ v, changed := fastpathTV.DecMapInt16Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]int8)
+ fastpathTV.DecMapInt16Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Int8X(vp *map[int16]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Int8V(v map[int16]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[int16]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]int16)
+ v, changed := fastpathTV.DecMapInt16Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]int16)
+ fastpathTV.DecMapInt16Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Int16X(vp *map[int16]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Int16V(v map[int16]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 4)
+ v = make(map[int16]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]int32)
+ v, changed := fastpathTV.DecMapInt16Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]int32)
+ fastpathTV.DecMapInt16Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Int32X(vp *map[int16]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Int32V(v map[int16]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[int16]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]int64)
+ v, changed := fastpathTV.DecMapInt16Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]int64)
+ fastpathTV.DecMapInt16Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Int64X(vp *map[int16]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Int64V(v map[int16]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int16]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]float32)
+ v, changed := fastpathTV.DecMapInt16Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]float32)
+ fastpathTV.DecMapInt16Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Float32X(vp *map[int16]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Float32V(v map[int16]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[int16]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]float64)
+ v, changed := fastpathTV.DecMapInt16Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]float64)
+ fastpathTV.DecMapInt16Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16Float64X(vp *map[int16]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16Float64V(v map[int16]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int16]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt16BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int16]bool)
+ v, changed := fastpathTV.DecMapInt16BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int16]bool)
+ fastpathTV.DecMapInt16BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt16BoolX(vp *map[int16]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt16BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt16BoolV(v map[int16]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int16]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[int16]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int16(dd.DecodeInt(16))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]interface{})
+ v, changed := fastpathTV.DecMapInt32IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]interface{})
+ fastpathTV.DecMapInt32IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32IntfX(vp *map[int32]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32IntfV(v map[int32]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[int32]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]string)
+ v, changed := fastpathTV.DecMapInt32StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]string)
+ fastpathTV.DecMapInt32StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32StringX(vp *map[int32]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32StringV(v map[int32]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 20)
+ v = make(map[int32]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]uint)
+ v, changed := fastpathTV.DecMapInt32UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]uint)
+ fastpathTV.DecMapInt32UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32UintX(vp *map[int32]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32UintV(v map[int32]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int32]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]uint8)
+ v, changed := fastpathTV.DecMapInt32Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]uint8)
+ fastpathTV.DecMapInt32Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Uint8X(vp *map[int32]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Uint8V(v map[int32]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[int32]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]uint16)
+ v, changed := fastpathTV.DecMapInt32Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]uint16)
+ fastpathTV.DecMapInt32Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Uint16X(vp *map[int32]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Uint16V(v map[int32]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[int32]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]uint32)
+ v, changed := fastpathTV.DecMapInt32Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]uint32)
+ fastpathTV.DecMapInt32Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Uint32X(vp *map[int32]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Uint32V(v map[int32]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[int32]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]uint64)
+ v, changed := fastpathTV.DecMapInt32Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]uint64)
+ fastpathTV.DecMapInt32Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Uint64X(vp *map[int32]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Uint64V(v map[int32]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int32]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]int)
+ v, changed := fastpathTV.DecMapInt32IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]int)
+ fastpathTV.DecMapInt32IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32IntX(vp *map[int32]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32IntV(v map[int32]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int32]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]int8)
+ v, changed := fastpathTV.DecMapInt32Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]int8)
+ fastpathTV.DecMapInt32Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Int8X(vp *map[int32]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Int8V(v map[int32]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[int32]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]int16)
+ v, changed := fastpathTV.DecMapInt32Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]int16)
+ fastpathTV.DecMapInt32Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Int16X(vp *map[int32]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Int16V(v map[int32]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 6)
+ v = make(map[int32]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]int32)
+ v, changed := fastpathTV.DecMapInt32Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]int32)
+ fastpathTV.DecMapInt32Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Int32X(vp *map[int32]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Int32V(v map[int32]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[int32]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]int64)
+ v, changed := fastpathTV.DecMapInt32Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]int64)
+ fastpathTV.DecMapInt32Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Int64X(vp *map[int32]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Int64V(v map[int32]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int32]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]float32)
+ v, changed := fastpathTV.DecMapInt32Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]float32)
+ fastpathTV.DecMapInt32Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Float32X(vp *map[int32]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Float32V(v map[int32]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 8)
+ v = make(map[int32]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]float64)
+ v, changed := fastpathTV.DecMapInt32Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]float64)
+ fastpathTV.DecMapInt32Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32Float64X(vp *map[int32]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32Float64V(v map[int32]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int32]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt32BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int32]bool)
+ v, changed := fastpathTV.DecMapInt32BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int32]bool)
+ fastpathTV.DecMapInt32BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt32BoolX(vp *map[int32]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt32BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt32BoolV(v map[int32]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int32]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[int32]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := int32(dd.DecodeInt(32))
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64IntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]interface{})
+ v, changed := fastpathTV.DecMapInt64IntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]interface{})
+ fastpathTV.DecMapInt64IntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64IntfX(vp *map[int64]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64IntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64IntfV(v map[int64]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[int64]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64StringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]string)
+ v, changed := fastpathTV.DecMapInt64StringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]string)
+ fastpathTV.DecMapInt64StringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64StringX(vp *map[int64]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64StringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64StringV(v map[int64]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 24)
+ v = make(map[int64]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64UintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]uint)
+ v, changed := fastpathTV.DecMapInt64UintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]uint)
+ fastpathTV.DecMapInt64UintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64UintX(vp *map[int64]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64UintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64UintV(v map[int64]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int64]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Uint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]uint8)
+ v, changed := fastpathTV.DecMapInt64Uint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]uint8)
+ fastpathTV.DecMapInt64Uint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Uint8X(vp *map[int64]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Uint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Uint8V(v map[int64]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int64]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Uint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]uint16)
+ v, changed := fastpathTV.DecMapInt64Uint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]uint16)
+ fastpathTV.DecMapInt64Uint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Uint16X(vp *map[int64]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Uint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Uint16V(v map[int64]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int64]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Uint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]uint32)
+ v, changed := fastpathTV.DecMapInt64Uint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]uint32)
+ fastpathTV.DecMapInt64Uint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Uint32X(vp *map[int64]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Uint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Uint32V(v map[int64]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int64]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Uint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]uint64)
+ v, changed := fastpathTV.DecMapInt64Uint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]uint64)
+ fastpathTV.DecMapInt64Uint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Uint64X(vp *map[int64]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Uint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Uint64V(v map[int64]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int64]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64IntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]int)
+ v, changed := fastpathTV.DecMapInt64IntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]int)
+ fastpathTV.DecMapInt64IntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64IntX(vp *map[int64]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64IntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64IntV(v map[int64]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int64]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Int8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]int8)
+ v, changed := fastpathTV.DecMapInt64Int8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]int8)
+ fastpathTV.DecMapInt64Int8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Int8X(vp *map[int64]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Int8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Int8V(v map[int64]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int64]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Int16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]int16)
+ v, changed := fastpathTV.DecMapInt64Int16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]int16)
+ fastpathTV.DecMapInt64Int16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Int16X(vp *map[int64]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Int16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Int16V(v map[int64]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 10)
+ v = make(map[int64]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Int32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]int32)
+ v, changed := fastpathTV.DecMapInt64Int32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]int32)
+ fastpathTV.DecMapInt64Int32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Int32X(vp *map[int64]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Int32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Int32V(v map[int64]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int64]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Int64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]int64)
+ v, changed := fastpathTV.DecMapInt64Int64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]int64)
+ fastpathTV.DecMapInt64Int64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Int64X(vp *map[int64]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Int64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Int64V(v map[int64]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int64]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Float32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]float32)
+ v, changed := fastpathTV.DecMapInt64Float32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]float32)
+ fastpathTV.DecMapInt64Float32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Float32X(vp *map[int64]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Float32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Float32V(v map[int64]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 12)
+ v = make(map[int64]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64Float64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]float64)
+ v, changed := fastpathTV.DecMapInt64Float64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]float64)
+ fastpathTV.DecMapInt64Float64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64Float64X(vp *map[int64]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64Float64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64Float64V(v map[int64]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 16)
+ v = make(map[int64]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapInt64BoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[int64]bool)
+ v, changed := fastpathTV.DecMapInt64BoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[int64]bool)
+ fastpathTV.DecMapInt64BoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapInt64BoolX(vp *map[int64]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapInt64BoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapInt64BoolV(v map[int64]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[int64]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[int64]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeInt(64)
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolIntfR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]interface{})
+ v, changed := fastpathTV.DecMapBoolIntfV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]interface{})
+ fastpathTV.DecMapBoolIntfV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolIntfX(vp *map[bool]interface{}, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolIntfV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolIntfV(v map[bool]interface{}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]interface{}, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[bool]interface{}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ d.decode(&mv)
+
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolStringR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]string)
+ v, changed := fastpathTV.DecMapBoolStringV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]string)
+ fastpathTV.DecMapBoolStringV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolStringX(vp *map[bool]string, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolStringV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolStringV(v map[bool]string, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]string, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 17)
+ v = make(map[bool]string, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeString()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolUintR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]uint)
+ v, changed := fastpathTV.DecMapBoolUintV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]uint)
+ fastpathTV.DecMapBoolUintV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolUintX(vp *map[bool]uint, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolUintV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolUintV(v map[bool]uint, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]uint, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[bool]uint, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint(dd.DecodeUint(uintBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolUint8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]uint8)
+ v, changed := fastpathTV.DecMapBoolUint8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]uint8)
+ fastpathTV.DecMapBoolUint8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolUint8X(vp *map[bool]uint8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolUint8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolUint8V(v map[bool]uint8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]uint8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[bool]uint8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint8(dd.DecodeUint(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolUint16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]uint16)
+ v, changed := fastpathTV.DecMapBoolUint16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]uint16)
+ fastpathTV.DecMapBoolUint16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolUint16X(vp *map[bool]uint16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolUint16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolUint16V(v map[bool]uint16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]uint16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[bool]uint16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint16(dd.DecodeUint(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolUint32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]uint32)
+ v, changed := fastpathTV.DecMapBoolUint32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]uint32)
+ fastpathTV.DecMapBoolUint32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolUint32X(vp *map[bool]uint32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolUint32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolUint32V(v map[bool]uint32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]uint32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[bool]uint32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = uint32(dd.DecodeUint(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolUint64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]uint64)
+ v, changed := fastpathTV.DecMapBoolUint64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]uint64)
+ fastpathTV.DecMapBoolUint64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolUint64X(vp *map[bool]uint64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolUint64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolUint64V(v map[bool]uint64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]uint64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[bool]uint64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeUint(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolIntR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]int)
+ v, changed := fastpathTV.DecMapBoolIntV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]int)
+ fastpathTV.DecMapBoolIntV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolIntX(vp *map[bool]int, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolIntV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolIntV(v map[bool]int, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]int, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[bool]int, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int(dd.DecodeInt(intBitsize))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolInt8R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]int8)
+ v, changed := fastpathTV.DecMapBoolInt8V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]int8)
+ fastpathTV.DecMapBoolInt8V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolInt8X(vp *map[bool]int8, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolInt8V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolInt8V(v map[bool]int8, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]int8, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[bool]int8, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int8(dd.DecodeInt(8))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolInt16R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]int16)
+ v, changed := fastpathTV.DecMapBoolInt16V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]int16)
+ fastpathTV.DecMapBoolInt16V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolInt16X(vp *map[bool]int16, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolInt16V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolInt16V(v map[bool]int16, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]int16, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 3)
+ v = make(map[bool]int16, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int16(dd.DecodeInt(16))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolInt32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]int32)
+ v, changed := fastpathTV.DecMapBoolInt32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]int32)
+ fastpathTV.DecMapBoolInt32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolInt32X(vp *map[bool]int32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolInt32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolInt32V(v map[bool]int32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]int32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[bool]int32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = int32(dd.DecodeInt(32))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolInt64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]int64)
+ v, changed := fastpathTV.DecMapBoolInt64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]int64)
+ fastpathTV.DecMapBoolInt64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolInt64X(vp *map[bool]int64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolInt64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolInt64V(v map[bool]int64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]int64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[bool]int64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeInt(64)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolFloat32R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]float32)
+ v, changed := fastpathTV.DecMapBoolFloat32V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]float32)
+ fastpathTV.DecMapBoolFloat32V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolFloat32X(vp *map[bool]float32, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolFloat32V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolFloat32V(v map[bool]float32, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]float32, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 5)
+ v = make(map[bool]float32, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = float32(dd.DecodeFloat(true))
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolFloat64R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]float64)
+ v, changed := fastpathTV.DecMapBoolFloat64V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]float64)
+ fastpathTV.DecMapBoolFloat64V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolFloat64X(vp *map[bool]float64, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolFloat64V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolFloat64V(v map[bool]float64, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]float64, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 9)
+ v = make(map[bool]float64, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeFloat(false)
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+func (f *decFnInfo) fastpathDecMapBoolBoolR(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[bool]bool)
+ v, changed := fastpathTV.DecMapBoolBoolV(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[bool]bool)
+ fastpathTV.DecMapBoolBoolV(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) DecMapBoolBoolX(vp *map[bool]bool, checkNil bool, d *Decoder) {
+ v, changed := f.DecMapBoolBoolV(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) DecMapBoolBoolV(v map[bool]bool, checkNil bool, canChange bool,
+ d *Decoder) (_ map[bool]bool, changed bool) {
+ dd := d.d
+
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, 2)
+ v = make(map[bool]bool, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ mk := dd.DecodeBool()
+ mv := v[mk]
+ mv = dd.DecodeBool()
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl
new file mode 100644
index 000000000..5fecafd21
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/fast-path.go.tmpl
@@ -0,0 +1,418 @@
+// //+build ignore
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED from fast-path.go.tmpl
+// ************************************************************
+
+package codec
+
+// Fast path functions try to create a fast path encode or decode implementation
+// for common maps and slices.
+//
+// We define the functions and register then in this single file
+// so as not to pollute the encode.go and decode.go, and create a dependency in there.
+// This file can be omitted without causing a build failure.
+//
+// The advantage of fast paths is:
+// - Many calls bypass reflection altogether
+//
+// Currently support
+// - slice of all builtin types,
+// - map of all builtin types to string or interface value
+// - symetrical maps of all builtin types (e.g. str-str, uint8-uint8)
+// This should provide adequate "typical" implementations.
+//
+// Note that fast track decode functions must handle values for which an address cannot be obtained.
+// For example:
+// m2 := map[string]int{}
+// p2 := []interface{}{m2}
+// // decoding into p2 will bomb if fast track functions do not treat like unaddressable.
+//
+
+import (
+ "reflect"
+ "sort"
+)
+
+const fastpathCheckNilFalse = false // for reflect
+const fastpathCheckNilTrue = true // for type switch
+
+type fastpathT struct {}
+
+var fastpathTV fastpathT
+
+type fastpathE struct {
+ rtid uintptr
+ rt reflect.Type
+ encfn func(*encFnInfo, reflect.Value)
+ decfn func(*decFnInfo, reflect.Value)
+}
+
+type fastpathA [{{ .FastpathLen }}]fastpathE
+
+func (x *fastpathA) index(rtid uintptr) int {
+ // use binary search to grab the index (adapted from sort/search.go)
+ h, i, j := 0, 0, {{ .FastpathLen }} // len(x)
+ for i < j {
+ h = i + (j-i)/2
+ if x[h].rtid < rtid {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ if i < {{ .FastpathLen }} && x[i].rtid == rtid {
+ return i
+ }
+ return -1
+}
+
+type fastpathAslice []fastpathE
+
+func (x fastpathAslice) Len() int { return len(x) }
+func (x fastpathAslice) Less(i, j int) bool { return x[i].rtid < x[j].rtid }
+func (x fastpathAslice) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+var fastpathAV fastpathA
+
+// due to possible initialization loop error, make fastpath in an init()
+func init() {
+ if !fastpathEnabled {
+ return
+ }
+ i := 0
+ fn := func(v interface{}, fe func(*encFnInfo, reflect.Value), fd func(*decFnInfo, reflect.Value)) (f fastpathE) {
+ xrt := reflect.TypeOf(v)
+ xptr := reflect.ValueOf(xrt).Pointer()
+ fastpathAV[i] = fastpathE{xptr, xrt, fe, fd}
+ i++
+ return
+ }
+
+ {{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+ fn([]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (*decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
+
+ {{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+ fn(map[{{ .MapKey }}]{{ .Elem }}(nil), (*encFnInfo).{{ .MethodNamePfx "fastpathEnc" false }}R, (*decFnInfo).{{ .MethodNamePfx "fastpathDec" false }}R){{end}}{{end}}{{end}}
+
+ sort.Sort(fastpathAslice(fastpathAV[:]))
+}
+
+// -- encode
+
+// -- -- fast path type switch
+func fastpathEncodeTypeSwitch(iv interface{}, e *Encoder) bool {
+ switch v := iv.(type) {
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+ case []{{ .Elem }}:{{else}}
+ case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e){{if not .MapKey }}
+ case *[]{{ .Elem }}:{{else}}
+ case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
+{{end}}{{end}}
+ default:
+ return false
+ }
+ return true
+}
+
+func fastpathEncodeTypeSwitchSlice(iv interface{}, e *Encoder) bool {
+ switch v := iv.(type) {
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+ case []{{ .Elem }}:
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
+ case *[]{{ .Elem }}:
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
+{{end}}{{end}}{{end}}
+ default:
+ return false
+ }
+ return true
+}
+
+func fastpathEncodeTypeSwitchMap(iv interface{}, e *Encoder) bool {
+ switch v := iv.(type) {
+{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+ case map[{{ .MapKey }}]{{ .Elem }}:
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(v, fastpathCheckNilTrue, e)
+ case *map[{{ .MapKey }}]{{ .Elem }}:
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(*v, fastpathCheckNilTrue, e)
+{{end}}{{end}}{{end}}
+ default:
+ return false
+ }
+ return true
+}
+
+// -- -- fast path functions
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+
+func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().([]{{ .Elem }}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v []{{ .Elem }}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeArrayStart(len(v))
+ for _, v2 := range v {
+ {{ encmd .Elem "v2"}}
+ }
+ ee.EncodeEnd()
+}
+
+{{end}}{{end}}{{end}}
+
+{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+
+func (f *encFnInfo) {{ .MethodNamePfx "fastpathEnc" false }}R(rv reflect.Value) {
+ fastpathTV.{{ .MethodNamePfx "Enc" false }}V(rv.Interface().(map[{{ .MapKey }}]{{ .Elem }}), fastpathCheckNilFalse, f.e)
+}
+func (_ fastpathT) {{ .MethodNamePfx "Enc" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, e *Encoder) {
+ ee := e.e
+ if checkNil && v == nil {
+ ee.EncodeNil()
+ return
+ }
+ ee.EncodeMapStart(len(v))
+ {{if eq .MapKey "string"}}asSymbols := e.h.AsSymbols&AsSymbolMapStringKeysFlag != 0{{end}}
+ for k2, v2 := range v {
+ {{if eq .MapKey "string"}}if asSymbols {
+ ee.EncodeSymbol(k2)
+ } else {
+ ee.EncodeString(c_UTF8, k2)
+ }{{else}}{{ encmd .MapKey "k2"}}{{end}}
+ {{ encmd .Elem "v2"}}
+ }
+ ee.EncodeEnd()
+}
+
+{{end}}{{end}}{{end}}
+
+// -- decode
+
+// -- -- fast path type switch
+func fastpathDecodeTypeSwitch(iv interface{}, d *Decoder) bool {
+ switch v := iv.(type) {
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+ case []{{ .Elem }}:{{else}}
+ case map[{{ .MapKey }}]{{ .Elem }}:{{end}}
+ fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, d){{if not .MapKey }}
+ case *[]{{ .Elem }}:{{else}}
+ case *map[{{ .MapKey }}]{{ .Elem }}:{{end}}
+ v2, changed2 := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*v, fastpathCheckNilFalse, true, d)
+ if changed2 {
+ *v = v2
+ }
+{{end}}{{end}}
+ default:
+ return false
+ }
+ return true
+}
+
+// -- -- fast path functions
+{{range .Values}}{{if not .Primitive}}{{if not .MapKey }}
+{{/*
+Slices can change if they
+- did not come from an array
+- are addressable (from a ptr)
+- are settable (e.g. contained in an interface{})
+*/}}
+func (f *decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) {
+ array := f.seq == seqTypeArray
+ if !array && rv.CanAddr() { {{/* // CanSet => CanAddr + Exported */}}
+ vp := rv.Addr().Interface().(*[]{{ .Elem }})
+ v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, !array, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().([]{{ .Elem }})
+ fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+
+func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *[]{{ .Elem }}, checkNil bool, d *Decoder) {
+ v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v []{{ .Elem }}, checkNil bool, canChange bool,
+ d *Decoder) (_ []{{ .Elem }}, changed bool) {
+ dd := d.d
+ {{/* // if dd.isContainerType(valueTypeNil) { dd.TryDecodeAsNil() */}}
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ slh, containerLenS := d.decSliceHelperStart()
+ x2read := containerLenS
+ var xtrunc bool
+ if canChange && v == nil {
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}); xtrunc {
+ x2read = xlen
+ }
+ v = make([]{{ .Elem }}, xlen)
+ changed = true
+ }
+ if containerLenS == 0 {
+ if canChange && len(v) != 0 {
+ v = v[:0]
+ changed = true
+ }{{/*
+ // slh.End() // dd.ReadArrayEnd()
+ */}}
+ return v, changed
+ }
+
+ {{/* // for j := 0; j < containerLenS; j++ { */}}
+ if containerLenS > 0 {
+ if containerLenS > cap(v) {
+ if canChange { {{/*
+ // fast-path is for "basic" immutable types, so no need to copy them over
+ // s := make([]{{ .Elem }}, decInferLen(containerLenS, d.h.MaxInitLen))
+ // copy(s, v[:cap(v)])
+ // v = s */}}
+ var xlen int
+ if xlen, xtrunc = decInferLen(containerLenS, d.h.MaxInitLen, {{ .Size }}); xtrunc {
+ x2read = xlen
+ }
+ v = make([]{{ .Elem }}, xlen)
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), containerLenS)
+ x2read = len(v)
+ }
+ } else if containerLenS != len(v) {
+ v = v[:containerLenS]
+ changed = true
+ }
+ {{/* // all checks done. cannot go past len. */}}
+ j := 0
+ for ; j < x2read; j++ {
+ {{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
+ }
+ if xtrunc { {{/* // means canChange=true, changed=true already. */}}
+ for ; j < containerLenS; j++ {
+ v = append(v, {{ zerocmd .Elem }})
+ {{ if eq .Elem "interface{}" }}d.decode(&v[j]){{ else }}v[j] = {{ decmd .Elem }}{{ end }}
+ }
+ } else if !canChange {
+ for ; j < containerLenS; j++ {
+ d.swallow()
+ }
+ }
+ } else {
+ j := 0
+ for ; !dd.CheckBreak(); j++ {
+ if j >= len(v) {
+ if canChange {
+ v = append(v, {{ zerocmd .Elem }})
+ changed = true
+ } else {
+ d.arrayCannotExpand(len(v), j+1)
+ }
+ }
+ if j < len(v) { {{/* // all checks done. cannot go past len. */}}
+ {{ if eq .Elem "interface{}" }}d.decode(&v[j])
+ {{ else }}v[j] = {{ decmd .Elem }}{{ end }}
+ } else {
+ d.swallow()
+ }
+ }
+ slh.End()
+ }
+ return v, changed
+}
+
+{{end}}{{end}}{{end}}
+
+
+{{range .Values}}{{if not .Primitive}}{{if .MapKey }}
+{{/*
+Maps can change if they are
+- addressable (from a ptr)
+- settable (e.g. contained in an interface{})
+*/}}
+func (f *decFnInfo) {{ .MethodNamePfx "fastpathDec" false }}R(rv reflect.Value) {
+ if rv.CanAddr() {
+ vp := rv.Addr().Interface().(*map[{{ .MapKey }}]{{ .Elem }})
+ v, changed := fastpathTV.{{ .MethodNamePfx "Dec" false }}V(*vp, fastpathCheckNilFalse, true, f.d)
+ if changed {
+ *vp = v
+ }
+ } else {
+ v := rv.Interface().(map[{{ .MapKey }}]{{ .Elem }})
+ fastpathTV.{{ .MethodNamePfx "Dec" false }}V(v, fastpathCheckNilFalse, false, f.d)
+ }
+}
+func (f fastpathT) {{ .MethodNamePfx "Dec" false }}X(vp *map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, d *Decoder) {
+ v, changed := f.{{ .MethodNamePfx "Dec" false }}V(*vp, checkNil, true, d)
+ if changed {
+ *vp = v
+ }
+}
+func (_ fastpathT) {{ .MethodNamePfx "Dec" false }}V(v map[{{ .MapKey }}]{{ .Elem }}, checkNil bool, canChange bool,
+ d *Decoder) (_ map[{{ .MapKey }}]{{ .Elem }}, changed bool) {
+ dd := d.d
+ {{/* // if dd.isContainerType(valueTypeNil) {dd.TryDecodeAsNil() */}}
+ if checkNil && dd.TryDecodeAsNil() {
+ if v != nil {
+ changed = true
+ }
+ return nil, changed
+ }
+
+ containerLen := dd.ReadMapStart()
+ if canChange && v == nil {
+ xlen, _ := decInferLen(containerLen, d.h.MaxInitLen, {{ .Size }})
+ v = make(map[{{ .MapKey }}]{{ .Elem }}, xlen)
+ changed = true
+ }
+ if containerLen > 0 {
+ for j := 0; j < containerLen; j++ {
+ {{ if eq .MapKey "interface{}" }}var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
+ }{{ else }}mk := {{ decmd .MapKey }}{{ end }}
+ mv := v[mk]
+ {{ if eq .Elem "interface{}" }}d.decode(&mv)
+ {{ else }}mv = {{ decmd .Elem }}{{ end }}
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ } else if containerLen < 0 {
+ for j := 0; !dd.CheckBreak(); j++ {
+ {{ if eq .MapKey "interface{}" }}var mk interface{}
+ d.decode(&mk)
+ if bv, bok := mk.([]byte); bok {
+ mk = string(bv) {{/* // maps cannot have []byte as key. switch to string. */}}
+ }{{ else }}mk := {{ decmd .MapKey }}{{ end }}
+ mv := v[mk]
+ {{ if eq .Elem "interface{}" }}d.decode(&mv)
+ {{ else }}mv = {{ decmd .Elem }}{{ end }}
+ if v != nil {
+ v[mk] = mv
+ }
+ }
+ dd.ReadEnd()
+ }
+ return v, changed
+}
+
+{{end}}{{end}}{{end}}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl
new file mode 100644
index 000000000..eb31a9a96
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-array.go.tmpl
@@ -0,0 +1,86 @@
+{{var "v"}} := {{ if not isArray}}*{{ end }}{{ .Varname }}
+{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}
+
+var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}}
+var {{var "c"}}, {{var "rt"}} bool {{/* // changed, truncated */}}
+_, _, _ = {{var "c"}}, {{var "rt"}}, {{var "rl"}}
+{{var "rr"}} = {{var "l"}}
+{{/* rl is NOT used. Only used for getting DecInferLen. len(r) used directly in code */}}
+
+{{ if not isArray }}if {{var "v"}} == nil {
+ if {{var "rl"}}, {{var "rt"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }}); {{var "rt"}} {
+ {{var "rr"}} = {{var "rl"}}
+ }
+ {{var "v"}} = make({{ .CTyp }}, {{var "rl"}})
+ {{var "c"}} = true
+}
+{{ end }}
+if {{var "l"}} == 0 { {{ if isSlice }}
+ if len({{var "v"}}) != 0 {
+ {{var "v"}} = {{var "v"}}[:0]
+ {{var "c"}} = true
+ } {{ end }}
+} else if {{var "l"}} > 0 {
+ {{ if isChan }}
+ for {{var "r"}} := 0; {{var "r"}} < {{var "l"}}; {{var "r"}}++ {
+ var {{var "t"}} {{ .Typ }}
+ {{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+ {{var "v"}} <- {{var "t"}}
+ {{ else }}
+ if {{var "l"}} > cap({{var "v"}}) {
+ {{ if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}})
+ {{ else }}{{var "rl"}}, {{var "rt"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
+ {{ if .Immutable }}
+ {{var "v2"}} := {{var "v"}}
+ {{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
+ if len({{var "v"}}) > 0 {
+ copy({{var "v"}}, {{var "v2"}}[:cap({{var "v2"}})])
+ }
+ {{ else }}{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
+ {{ end }}{{var "c"}} = true
+ {{ end }}
+ {{var "rr"}} = len({{var "v"}})
+ } else if {{var "l"}} != len({{var "v"}}) {
+ {{ if isSlice }}{{var "v"}} = {{var "v"}}[:{{var "l"}}]
+ {{var "c"}} = true {{ end }}
+ }
+ {{var "j"}} := 0
+ for ; {{var "j"}} < {{var "rr"}} ; {{var "j"}}++ {
+ {{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+ }
+ {{ if isArray }}for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
+ z.DecSwallow()
+ }
+ {{ else }}if {{var "rt"}} { {{/* means that it is mutable and slice */}}
+ for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
+ {{var "v"}} = append({{var "v"}}, {{ zero}})
+ {{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+ }
+ }
+ {{ end }}
+ {{ end }}{{/* closing 'if not chan' */}}
+} else {
+ for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
+ if {{var "j"}} >= len({{var "v"}}) {
+ {{ if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
+ {{ else if isSlice}}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
+ {{var "c"}} = true {{ end }}
+ }
+ {{ if isChan}}
+ var {{var "t"}} {{ .Typ }}
+ {{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+ {{var "v"}} <- {{var "t"}}
+ {{ else }}
+ if {{var "j"}} < len({{var "v"}}) {
+ {{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+ } else {
+ z.DecSwallow()
+ }
+ {{ end }}
+ }
+ {{var "h"}}.End()
+}
+{{ if not isArray }}if {{var "c"}} {
+ *{{ .Varname }} = {{var "v"}}
+}{{ end }}
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-map.go.tmpl b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-map.go.tmpl
new file mode 100644
index 000000000..d1535f4a6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-dec-map.go.tmpl
@@ -0,0 +1,39 @@
+{{var "v"}} := *{{ .Varname }}
+{{var "l"}} := r.ReadMapStart()
+if {{var "v"}} == nil {
+ {{var "rl"}}, _ := z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
+ {{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
+ *{{ .Varname }} = {{var "v"}}
+}
+if {{var "l"}} > 0 {
+for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
+ var {{var "mk"}} {{ .KTyp }}
+ {{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
+{{ if eq .KTyp "interface{}" }}// special case if a byte array.
+ if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+ {{var "mk"}} = string({{var "bv"}})
+ }
+{{ end }}
+ {{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+ {{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
+ if {{var "v"}} != nil {
+ {{var "v"}}[{{var "mk"}}] = {{var "mv"}}
+ }
+}
+} else if {{var "l"}} < 0 {
+for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
+ var {{var "mk"}} {{ .KTyp }}
+ {{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
+{{ if eq .KTyp "interface{}" }}// special case if a byte array.
+ if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+ {{var "mk"}} = string({{var "bv"}})
+ }
+{{ end }}
+ {{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+ {{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
+ if {{var "v"}} != nil {
+ {{var "v"}}[{{var "mk"}}] = {{var "mv"}}
+ }
+}
+r.ReadEnd()
+} // else len==0: TODO: Should we clear map entries?
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-helper.generated.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-helper.generated.go
new file mode 100644
index 000000000..9710eca50
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-helper.generated.go
@@ -0,0 +1,220 @@
+// //+build ignore
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED from gen-helper.go.tmpl
+// ************************************************************
+
+package codec
+
+import (
+ "encoding"
+ "reflect"
+)
+
+// This file is used to generate helper code for codecgen.
+// The values here i.e. genHelper(En|De)coder are not to be used directly by
+// library users. They WILL change continously and without notice.
+//
+// To help enforce this, we create an unexported type with exported members.
+// The only way to get the type is via the one exported type that we control (somewhat).
+//
+// When static codecs are created for types, they will use this value
+// to perform encoding or decoding of primitives or known slice or map types.
+
+// GenHelperEncoder is exported so that it can be used externally by codecgen.
+// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
+func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
+ return genHelperEncoder{e: e}, e.e
+}
+
+// GenHelperDecoder is exported so that it can be used externally by codecgen.
+// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
+func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
+ return genHelperDecoder{d: d}, d.d
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+type genHelperEncoder struct {
+ e *Encoder
+ F fastpathT
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+type genHelperDecoder struct {
+ d *Decoder
+ F fastpathT
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
+ return f.e.h
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBinary() bool {
+ return f.e.be // f.e.hh.isBinaryEncoding()
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncFallback(iv interface{}) {
+ // println(">>>>>>>>> EncFallback")
+ f.e.encodeI(iv, false, false)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
+ bs, fnerr := iv.MarshalText()
+ f.e.marshal(bs, fnerr, false, c_UTF8)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
+ bs, fnerr := iv.MarshalJSON()
+ f.e.marshal(bs, fnerr, true, c_UTF8)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
+ bs, fnerr := iv.MarshalBinary()
+ f.e.marshal(bs, fnerr, false, c_RAW)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
+ if _, ok := f.e.hh.(*BincHandle); ok {
+ return timeTypId
+ }
+ return 0
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) IsJSONHandle() bool {
+ return f.e.js
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) HasExtensions() bool {
+ return len(f.e.h.extHandle) != 0
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
+ rt := reflect.TypeOf(v)
+ if rt.Kind() == reflect.Ptr {
+ rt = rt.Elem()
+ }
+ rtid := reflect.ValueOf(rt).Pointer()
+ if xfFn := f.e.h.getExt(rtid); xfFn != nil {
+ f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
+ return true
+ }
+ return false
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
+ return f.d.h
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBinary() bool {
+ return f.d.be // f.d.hh.isBinaryEncoding()
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecSwallow() {
+ f.d.swallow()
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecScratchBuffer() []byte {
+ return f.d.b[:]
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
+ // println(">>>>>>>>> DecFallback")
+ f.d.decodeI(iv, chkPtr, false, false, false)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecSliceHelperStart() (decSliceHelper, int) {
+ return f.d.decSliceHelperStart()
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
+ f.d.structFieldNotFound(index, name)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
+ f.d.arrayCannotExpand(sliceLen, streamLen)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
+ fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
+ // bs := f.dd.DecodeBytes(f.d.b[:], true, true)
+ f.d.r.track()
+ f.d.swallow()
+ bs := f.d.r.stopTrack()
+ // fmt.Printf(">>>>>> CODECGEN JSON: %s\n", bs)
+ fnerr := tm.UnmarshalJSON(bs)
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
+ fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, false, true))
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
+ if _, ok := f.d.hh.(*BincHandle); ok {
+ return timeTypId
+ }
+ return 0
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) IsJSONHandle() bool {
+ return f.d.js
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) HasExtensions() bool {
+ return len(f.d.h.extHandle) != 0
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
+ rt := reflect.TypeOf(v).Elem()
+ rtid := reflect.ValueOf(rt).Pointer()
+ if xfFn := f.d.h.getExt(rtid); xfFn != nil {
+ f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
+ return true
+ }
+ return false
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int, truncated bool) {
+ return decInferLen(clen, maxlen, unit)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-helper.go.tmpl b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-helper.go.tmpl
new file mode 100644
index 000000000..8bc506112
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen-helper.go.tmpl
@@ -0,0 +1,353 @@
+// //+build ignore
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED from gen-helper.go.tmpl
+// ************************************************************
+
+package codec
+
+import (
+ "encoding"
+ "reflect"
+)
+
+// This file is used to generate helper code for codecgen.
+// The values here i.e. genHelper(En|De)coder are not to be used directly by
+// library users. They WILL change continously and without notice.
+//
+// To help enforce this, we create an unexported type with exported members.
+// The only way to get the type is via the one exported type that we control (somewhat).
+//
+// When static codecs are created for types, they will use this value
+// to perform encoding or decoding of primitives or known slice or map types.
+
+// GenHelperEncoder is exported so that it can be used externally by codecgen.
+// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
+func GenHelperEncoder(e *Encoder) (genHelperEncoder, encDriver) {
+ return genHelperEncoder{e:e}, e.e
+}
+
+// GenHelperDecoder is exported so that it can be used externally by codecgen.
+// Library users: DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.
+func GenHelperDecoder(d *Decoder) (genHelperDecoder, decDriver) {
+ return genHelperDecoder{d:d}, d.d
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+type genHelperEncoder struct {
+ e *Encoder
+ F fastpathT
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+type genHelperDecoder struct {
+ d *Decoder
+ F fastpathT
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
+ return f.e.h
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBinary() bool {
+ return f.e.be // f.e.hh.isBinaryEncoding()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncFallback(iv interface{}) {
+ // println(">>>>>>>>> EncFallback")
+ f.e.encodeI(iv, false, false)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
+ bs, fnerr := iv.MarshalText()
+ f.e.marshal(bs, fnerr, false, c_UTF8)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
+ bs, fnerr := iv.MarshalJSON()
+ f.e.marshal(bs, fnerr, true, c_UTF8)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
+ bs, fnerr := iv.MarshalBinary()
+ f.e.marshal(bs, fnerr, false, c_RAW)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
+ if _, ok := f.e.hh.(*BincHandle); ok {
+ return timeTypId
+ }
+ return 0
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) IsJSONHandle() bool {
+ return f.e.js
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) HasExtensions() bool {
+ return len(f.e.h.extHandle) != 0
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncExt(v interface{}) (r bool) {
+ rt := reflect.TypeOf(v)
+ if rt.Kind() == reflect.Ptr {
+ rt = rt.Elem()
+ }
+ rtid := reflect.ValueOf(rt).Pointer()
+ if xfFn := f.e.h.getExt(rtid); xfFn != nil {
+ f.e.e.EncodeExt(v, xfFn.tag, xfFn.ext, f.e)
+ return true
+ }
+ return false
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
+ return f.d.h
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBinary() bool {
+ return f.d.be // f.d.hh.isBinaryEncoding()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecSwallow() {
+ f.d.swallow()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecScratchBuffer() []byte {
+ return f.d.b[:]
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecFallback(iv interface{}, chkPtr bool) {
+ // println(">>>>>>>>> DecFallback")
+ f.d.decodeI(iv, chkPtr, false, false, false)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecSliceHelperStart() (decSliceHelper, int) {
+ return f.d.decSliceHelperStart()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
+ f.d.structFieldNotFound(index, name)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
+ f.d.arrayCannotExpand(sliceLen, streamLen)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
+ fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
+ // bs := f.dd.DecodeBytes(f.d.b[:], true, true)
+ f.d.r.track()
+ f.d.swallow()
+ bs := f.d.r.stopTrack()
+ // fmt.Printf(">>>>>> CODECGEN JSON: %s\n", bs)
+ fnerr := tm.UnmarshalJSON(bs)
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
+ fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, false, true))
+ if fnerr != nil {
+ panic(fnerr)
+ }
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
+ if _, ok := f.d.hh.(*BincHandle); ok {
+ return timeTypId
+ }
+ return 0
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) IsJSONHandle() bool {
+ return f.d.js
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) HasExtensions() bool {
+ return len(f.d.h.extHandle) != 0
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecExt(v interface{}) (r bool) {
+ rt := reflect.TypeOf(v).Elem()
+ rtid := reflect.ValueOf(rt).Pointer()
+ if xfFn := f.d.h.getExt(rtid); xfFn != nil {
+ f.d.d.DecodeExt(v, xfFn.tag, xfFn.ext)
+ return true
+ }
+ return false
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecInferLen(clen, maxlen, unit int) (rvlen int, truncated bool) {
+ return decInferLen(clen, maxlen, unit)
+}
+
+{{/*
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncDriver() encDriver {
+ return f.e.e
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecDriver() decDriver {
+ return f.d.d
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncNil() {
+ f.e.e.EncodeNil()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBytes(v []byte) {
+ f.e.e.EncodeStringBytes(c_RAW, v)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncArrayStart(length int) {
+ f.e.e.EncodeArrayStart(length)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncArrayEnd() {
+ f.e.e.EncodeArrayEnd()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncArrayEntrySeparator() {
+ f.e.e.EncodeArrayEntrySeparator()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncMapStart(length int) {
+ f.e.e.EncodeMapStart(length)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncMapEnd() {
+ f.e.e.EncodeMapEnd()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncMapEntrySeparator() {
+ f.e.e.EncodeMapEntrySeparator()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncMapKVSeparator() {
+ f.e.e.EncodeMapKVSeparator()
+}
+
+// ---------
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBytes(v *[]byte) {
+ *v = f.d.d.DecodeBytes(*v)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecTryNil() bool {
+ return f.d.d.TryDecodeAsNil()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecContainerIsNil() (b bool) {
+ return f.d.d.IsContainerType(valueTypeNil)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecContainerIsMap() (b bool) {
+ return f.d.d.IsContainerType(valueTypeMap)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecContainerIsArray() (b bool) {
+ return f.d.d.IsContainerType(valueTypeArray)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecCheckBreak() bool {
+ return f.d.d.CheckBreak()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecMapStart() int {
+ return f.d.d.ReadMapStart()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecArrayStart() int {
+ return f.d.d.ReadArrayStart()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecMapEnd() {
+ f.d.d.ReadMapEnd()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecArrayEnd() {
+ f.d.d.ReadArrayEnd()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecArrayEntrySeparator() {
+ f.d.d.ReadArrayEntrySeparator()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecMapEntrySeparator() {
+ f.d.d.ReadMapEntrySeparator()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecMapKVSeparator() {
+ f.d.d.ReadMapKVSeparator()
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) ReadStringAsBytes(bs []byte) []byte {
+ return f.d.d.DecodeStringAsBytes(bs)
+}
+
+
+// -- encode calls (primitives)
+{{range .Values}}{{if .Primitive }}{{if ne .Primitive "interface{}" }}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) {{ .MethodNamePfx "Enc" true }}(v {{ .Primitive }}) {
+ ee := f.e.e
+ {{ encmd .Primitive "v" }}
+}
+{{ end }}{{ end }}{{ end }}
+
+// -- decode calls (primitives)
+{{range .Values}}{{if .Primitive }}{{if ne .Primitive "interface{}" }}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) {{ .MethodNamePfx "Dec" true }}(vp *{{ .Primitive }}) {
+ dd := f.d.d
+ *vp = {{ decmd .Primitive }}
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) {{ .MethodNamePfx "Read" true }}() (v {{ .Primitive }}) {
+ dd := f.d.d
+ v = {{ decmd .Primitive }}
+ return
+}
+{{ end }}{{ end }}{{ end }}
+
+
+// -- encode calls (slices/maps)
+{{range .Values}}{{if not .Primitive }}{{if .Slice }}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) {{ .MethodNamePfx "Enc" false }}(v []{{ .Elem }}) { {{ else }}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) {{ .MethodNamePfx "Enc" false }}(v map[{{ .MapKey }}]{{ .Elem }}) { {{end}}
+ f.F.{{ .MethodNamePfx "Enc" false }}V(v, false, f.e)
+}
+{{ end }}{{ end }}
+
+// -- decode calls (slices/maps)
+{{range .Values}}{{if not .Primitive }}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+{{if .Slice }}func (f genHelperDecoder) {{ .MethodNamePfx "Dec" false }}(vp *[]{{ .Elem }}) {
+{{else}}func (f genHelperDecoder) {{ .MethodNamePfx "Dec" false }}(vp *map[{{ .MapKey }}]{{ .Elem }}) { {{end}}
+ v, changed := f.F.{{ .MethodNamePfx "Dec" false }}V(*vp, false, true, f.d)
+ if changed {
+ *vp = v
+ }
+}
+{{ end }}{{ end }}
+*/}}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go
new file mode 100644
index 000000000..9a35f02fa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.generated.go
@@ -0,0 +1,138 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// DO NOT EDIT. THIS FILE IS AUTO-GENERATED FROM gen-dec-(map|array).go.tmpl
+
+const genDecMapTmpl = `
+{{var "v"}} := *{{ .Varname }}
+{{var "l"}} := r.ReadMapStart()
+if {{var "v"}} == nil {
+ {{var "rl"}}, _ := z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
+ {{var "v"}} = make(map[{{ .KTyp }}]{{ .Typ }}, {{var "rl"}})
+ *{{ .Varname }} = {{var "v"}}
+}
+if {{var "l"}} > 0 {
+for {{var "j"}} := 0; {{var "j"}} < {{var "l"}}; {{var "j"}}++ {
+ var {{var "mk"}} {{ .KTyp }}
+ {{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
+{{ if eq .KTyp "interface{}" }}// special case if a byte array.
+ if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+ {{var "mk"}} = string({{var "bv"}})
+ }
+{{ end }}
+ {{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+ {{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
+ if {{var "v"}} != nil {
+ {{var "v"}}[{{var "mk"}}] = {{var "mv"}}
+ }
+}
+} else if {{var "l"}} < 0 {
+for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
+ var {{var "mk"}} {{ .KTyp }}
+ {{ $x := printf "%vmk%v" .TempVar .Rand }}{{ decLineVarK $x }}
+{{ if eq .KTyp "interface{}" }}// special case if a byte array.
+ if {{var "bv"}}, {{var "bok"}} := {{var "mk"}}.([]byte); {{var "bok"}} {
+ {{var "mk"}} = string({{var "bv"}})
+ }
+{{ end }}
+ {{var "mv"}} := {{var "v"}}[{{var "mk"}}]
+ {{ $x := printf "%vmv%v" .TempVar .Rand }}{{ decLineVar $x }}
+ if {{var "v"}} != nil {
+ {{var "v"}}[{{var "mk"}}] = {{var "mv"}}
+ }
+}
+r.ReadEnd()
+} // else len==0: TODO: Should we clear map entries?
+`
+
+const genDecListTmpl = `
+{{var "v"}} := {{ if not isArray}}*{{ end }}{{ .Varname }}
+{{var "h"}}, {{var "l"}} := z.DecSliceHelperStart() {{/* // helper, containerLenS */}}
+
+var {{var "rr"}}, {{var "rl"}} int {{/* // num2read, length of slice/array/chan */}}
+var {{var "c"}}, {{var "rt"}} bool {{/* // changed, truncated */}}
+_, _, _ = {{var "c"}}, {{var "rt"}}, {{var "rl"}}
+{{var "rr"}} = {{var "l"}}
+{{/* rl is NOT used. Only used for getting DecInferLen. len(r) used directly in code */}}
+
+{{ if not isArray }}if {{var "v"}} == nil {
+ if {{var "rl"}}, {{var "rt"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }}); {{var "rt"}} {
+ {{var "rr"}} = {{var "rl"}}
+ }
+ {{var "v"}} = make({{ .CTyp }}, {{var "rl"}})
+ {{var "c"}} = true
+}
+{{ end }}
+if {{var "l"}} == 0 { {{ if isSlice }}
+ if len({{var "v"}}) != 0 {
+ {{var "v"}} = {{var "v"}}[:0]
+ {{var "c"}} = true
+ } {{ end }}
+} else if {{var "l"}} > 0 {
+ {{ if isChan }}
+ for {{var "r"}} := 0; {{var "r"}} < {{var "l"}}; {{var "r"}}++ {
+ var {{var "t"}} {{ .Typ }}
+ {{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+ {{var "v"}} <- {{var "t"}}
+ {{ else }}
+ if {{var "l"}} > cap({{var "v"}}) {
+ {{ if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "l"}})
+ {{ else }}{{var "rl"}}, {{var "rt"}} = z.DecInferLen({{var "l"}}, z.DecBasicHandle().MaxInitLen, {{ .Size }})
+ {{ if .Immutable }}
+ {{var "v2"}} := {{var "v"}}
+ {{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
+ if len({{var "v"}}) > 0 {
+ copy({{var "v"}}, {{var "v2"}}[:cap({{var "v2"}})])
+ }
+ {{ else }}{{var "v"}} = make([]{{ .Typ }}, {{var "rl"}})
+ {{ end }}{{var "c"}} = true
+ {{ end }}
+ {{var "rr"}} = len({{var "v"}})
+ } else if {{var "l"}} != len({{var "v"}}) {
+ {{ if isSlice }}{{var "v"}} = {{var "v"}}[:{{var "l"}}]
+ {{var "c"}} = true {{ end }}
+ }
+ {{var "j"}} := 0
+ for ; {{var "j"}} < {{var "rr"}} ; {{var "j"}}++ {
+ {{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+ }
+ {{ if isArray }}for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
+ z.DecSwallow()
+ }
+ {{ else }}if {{var "rt"}} { {{/* means that it is mutable and slice */}}
+ for ; {{var "j"}} < {{var "l"}} ; {{var "j"}}++ {
+ {{var "v"}} = append({{var "v"}}, {{ zero}})
+ {{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+ }
+ }
+ {{ end }}
+ {{ end }}{{/* closing 'if not chan' */}}
+} else {
+ for {{var "j"}} := 0; !r.CheckBreak(); {{var "j"}}++ {
+ if {{var "j"}} >= len({{var "v"}}) {
+ {{ if isArray }}z.DecArrayCannotExpand(len({{var "v"}}), {{var "j"}}+1)
+ {{ else if isSlice}}{{var "v"}} = append({{var "v"}}, {{zero}})// var {{var "z"}} {{ .Typ }}
+ {{var "c"}} = true {{ end }}
+ }
+ {{ if isChan}}
+ var {{var "t"}} {{ .Typ }}
+ {{ $x := printf "%st%s" .TempVar .Rand }}{{ decLineVar $x }}
+ {{var "v"}} <- {{var "t"}}
+ {{ else }}
+ if {{var "j"}} < len({{var "v"}}) {
+ {{ $x := printf "%[1]vv%[2]v[%[1]vj%[2]v]" .TempVar .Rand }}{{ decLineVar $x }}
+ } else {
+ z.DecSwallow()
+ }
+ {{ end }}
+ }
+ {{var "h"}}.End()
+}
+{{ if not isArray }}if {{var "c"}} {
+ *{{ .Varname }} = {{var "v"}}
+}{{ end }}
+
+`
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go
new file mode 100644
index 000000000..726d1a760
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/gen.go
@@ -0,0 +1,1875 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "fmt"
+ "go/format"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "os"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "text/template"
+ "time"
+)
+
+// ---------------------------------------------------
+// codecgen supports the full cycle of reflection-based codec:
+// - RawExt
+// - Builtins
+// - Extensions
+// - (Binary|Text|JSON)(Unm|M)arshal
+// - generic by-kind
+//
+// This means that, for dynamic things, we MUST use reflection to at least get the reflect.Type.
+// In those areas, we try to only do reflection or interface-conversion when NECESSARY:
+// - Extensions, only if Extensions are configured.
+//
+// However, codecgen doesn't support the following:
+// - Canonical option. (codecgen IGNORES it currently)
+// This is just because it has not been implemented.
+//
+// During encode/decode, Selfer takes precedence.
+// A type implementing Selfer will know how to encode/decode itself statically.
+//
+// The following field types are supported:
+// array: [n]T
+// slice: []T
+// map: map[K]V
+// primitive: [u]int[n], float(32|64), bool, string
+// struct
+//
+// ---------------------------------------------------
+// Note that a Selfer cannot call (e|d).(En|De)code on itself,
+// as this will cause a circular reference, as (En|De)code will call Selfer methods.
+// Any type that implements Selfer must implement completely and not fallback to (En|De)code.
+//
+// In addition, code in this file manages the generation of fast-path implementations of
+// encode/decode of slices/maps of primitive keys/values.
+//
+// Users MUST re-generate their implementations whenever the code shape changes.
+// The generated code will panic if it was generated with a version older than the supporting library.
+// ---------------------------------------------------
+//
+// codec framework is very feature rich.
+// When encoding or decoding into an interface, it depends on the runtime type of the interface.
+// The type of the interface may be a named type, an extension, etc.
+// Consequently, we fallback to runtime codec for encoding/decoding interfaces.
+// In addition, we fallback for any value which cannot be guaranteed at runtime.
+// This allows us support ANY value, including any named types, specifically those which
+// do not implement our interfaces (e.g. Selfer).
+//
+// This explains some slowness compared to other code generation codecs (e.g. msgp).
+// This reduction in speed is only seen when your refers to interfaces,
+// e.g. type T struct { A interface{}; B []interface{}; C map[string]interface{} }
+//
+// codecgen will panic if the file was generated with an old version of the library in use.
+//
+// Note:
+// It was a concious decision to have gen.go always explicitly call EncodeNil or TryDecodeAsNil.
+// This way, there isn't a function call overhead just to see that we should not enter a block of code.
+
+// GenVersion is the current version of codecgen.
+//
+// NOTE: Increment this value each time codecgen changes fundamentally.
+// Fundamental changes are:
+// - helper methods change (signature change, new ones added, some removed, etc)
+// - codecgen command line changes
+//
+// v1: Initial Version
+// v2:
+// v3: Changes for Kubernetes:
+// changes in signature of some unpublished helper methods and codecgen cmdline arguments.
+// v4: Removed separator support from (en|de)cDriver, and refactored codec(gen)
+const GenVersion = 4
+
+const (
+ genCodecPkg = "codec1978"
+ genTempVarPfx = "yy"
+ genTopLevelVarName = "x"
+
+ // ignore canBeNil parameter, and always set to true.
+ // This is because nil can appear anywhere, so we should always check.
+ genAnythingCanBeNil = true
+
+ // if genUseOneFunctionForDecStructMap, make a single codecDecodeSelferFromMap function;
+ // else make codecDecodeSelferFromMap{LenPrefix,CheckBreak} so that conditionals
+ // are not executed a lot.
+ //
+ // From testing, it didn't make much difference in runtime, so keep as true (one function only)
+ genUseOneFunctionForDecStructMap = true
+)
+
+var (
+ genAllTypesSamePkgErr = errors.New("All types must be in the same package")
+ genExpectArrayOrMapErr = errors.New("unexpected type. Expecting array/map/slice")
+ genBase64enc = base64.NewEncoding("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789__")
+ genQNameRegex = regexp.MustCompile(`[A-Za-z_.]+`)
+)
+
+// genRunner holds some state used during a Gen run.
+type genRunner struct {
+ w io.Writer // output
+ c uint64 // counter used for generating varsfx
+ t []reflect.Type // list of types to run selfer on
+
+ tc reflect.Type // currently running selfer on this type
+ te map[uintptr]bool // types for which the encoder has been created
+ td map[uintptr]bool // types for which the decoder has been created
+ cp string // codec import path
+
+ im map[string]reflect.Type // imports to add
+ imn map[string]string // package names of imports to add
+ imc uint64 // counter for import numbers
+
+ is map[reflect.Type]struct{} // types seen during import search
+ bp string // base PkgPath, for which we are generating for
+
+ cpfx string // codec package prefix
+ unsafe bool // is unsafe to be used in generated code?
+
+ tm map[reflect.Type]struct{} // types for which enc/dec must be generated
+ ts []reflect.Type // types for which enc/dec must be generated
+
+ xs string // top level variable/constant suffix
+ hn string // fn helper type name
+
+ // rr *rand.Rand // random generator for file-specific types
+}
+
+// Gen will write a complete go file containing Selfer implementations for each
+// type passed. All the types must be in the same package.
+//
+// Library users: *DO NOT USE IT DIRECTLY. IT WILL CHANGE CONTINOUSLY WITHOUT NOTICE.*
+func Gen(w io.Writer, buildTags, pkgName, uid string, useUnsafe bool, typ ...reflect.Type) {
+ if len(typ) == 0 {
+ return
+ }
+ x := genRunner{
+ unsafe: useUnsafe,
+ w: w,
+ t: typ,
+ te: make(map[uintptr]bool),
+ td: make(map[uintptr]bool),
+ im: make(map[string]reflect.Type),
+ imn: make(map[string]string),
+ is: make(map[reflect.Type]struct{}),
+ tm: make(map[reflect.Type]struct{}),
+ ts: []reflect.Type{},
+ bp: genImportPath(typ[0]),
+ xs: uid,
+ }
+ if x.xs == "" {
+ rr := rand.New(rand.NewSource(time.Now().UnixNano()))
+ x.xs = strconv.FormatInt(rr.Int63n(9999), 10)
+ }
+
+ // gather imports first:
+ x.cp = genImportPath(reflect.TypeOf(x))
+ x.imn[x.cp] = genCodecPkg
+ for _, t := range typ {
+ // fmt.Printf("###########: PkgPath: '%v', Name: '%s'\n", genImportPath(t), t.Name())
+ if genImportPath(t) != x.bp {
+ panic(genAllTypesSamePkgErr)
+ }
+ x.genRefPkgs(t)
+ }
+ if buildTags != "" {
+ x.line("//+build " + buildTags)
+ x.line("")
+ }
+ x.line(`
+
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED BY codecgen.
+// ************************************************************
+
+`)
+ x.line("package " + pkgName)
+ x.line("")
+ x.line("import (")
+ if x.cp != x.bp {
+ x.cpfx = genCodecPkg + "."
+ x.linef("%s \"%s\"", genCodecPkg, x.cp)
+ }
+ // use a sorted set of im keys, so that we can get consistent output
+ imKeys := make([]string, 0, len(x.im))
+ for k, _ := range x.im {
+ imKeys = append(imKeys, k)
+ }
+ sort.Strings(imKeys)
+ for _, k := range imKeys { // for k, _ := range x.im {
+ x.linef("%s \"%s\"", x.imn[k], k)
+ }
+ // add required packages
+ for _, k := range [...]string{"reflect", "unsafe", "runtime", "fmt", "errors"} {
+ if _, ok := x.im[k]; !ok {
+ if k == "unsafe" && !x.unsafe {
+ continue
+ }
+ x.line("\"" + k + "\"")
+ }
+ }
+ x.line(")")
+ x.line("")
+
+ x.line("const (")
+ x.linef("codecSelferC_UTF8%s = %v", x.xs, int64(c_UTF8))
+ x.linef("codecSelferC_RAW%s = %v", x.xs, int64(c_RAW))
+ x.linef("codecSelferValueTypeArray%s = %v", x.xs, int64(valueTypeArray))
+ x.linef("codecSelferValueTypeMap%s = %v", x.xs, int64(valueTypeMap))
+ x.line(")")
+ x.line("var (")
+ x.line("codecSelferBitsize" + x.xs + " = uint8(reflect.TypeOf(uint(0)).Bits())")
+ x.line("codecSelferOnlyMapOrArrayEncodeToStructErr" + x.xs + " = errors.New(`only encoded map or array can be decoded into a struct`)")
+ x.line(")")
+ x.line("")
+
+ if x.unsafe {
+ x.line("type codecSelferUnsafeString" + x.xs + " struct { Data uintptr; Len int}")
+ x.line("")
+ }
+ x.hn = "codecSelfer" + x.xs
+ x.line("type " + x.hn + " struct{}")
+ x.line("")
+
+ x.line("func init() {")
+ x.linef("if %sGenVersion != %v {", x.cpfx, GenVersion)
+ x.line("_, file, _, _ := runtime.Caller(0)")
+ x.line(`err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", `)
+ x.linef(`%v, %sGenVersion, file)`, GenVersion, x.cpfx)
+ x.line("panic(err)")
+ // x.linef(`panic(fmt.Errorf("Re-run codecgen due to version mismatch: `+
+ // `current: %%v, need %%v, file: %%v", %v, %sGenVersion, file))`, GenVersion, x.cpfx)
+ x.linef("}")
+ x.line("if false { // reference the types, but skip this branch at build/run time")
+ var n int
+ // for k, t := range x.im {
+ for _, k := range imKeys {
+ t := x.im[k]
+ x.linef("var v%v %s.%s", n, x.imn[k], t.Name())
+ n++
+ }
+ if x.unsafe {
+ x.linef("var v%v unsafe.Pointer", n)
+ n++
+ }
+ if n > 0 {
+ x.out("_")
+ for i := 1; i < n; i++ {
+ x.out(", _")
+ }
+ x.out(" = v0")
+ for i := 1; i < n; i++ {
+ x.outf(", v%v", i)
+ }
+ }
+ x.line("} ") // close if false
+ x.line("}") // close init
+ x.line("")
+
+ // generate rest of type info
+ for _, t := range typ {
+ x.tc = t
+ x.selfer(true)
+ x.selfer(false)
+ }
+
+ for _, t := range x.ts {
+ rtid := reflect.ValueOf(t).Pointer()
+ // generate enc functions for all these slice/map types.
+ x.linef("func (x %s) enc%s(v %s%s, e *%sEncoder) {", x.hn, x.genMethodNameT(t), x.arr2str(t, "*"), x.genTypeName(t), x.cpfx)
+ x.genRequiredMethodVars(true)
+ switch t.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Chan:
+ x.encListFallback("v", t)
+ case reflect.Map:
+ x.encMapFallback("v", t)
+ default:
+ panic(genExpectArrayOrMapErr)
+ }
+ x.line("}")
+ x.line("")
+
+ // generate dec functions for all these slice/map types.
+ x.linef("func (x %s) dec%s(v *%s, d *%sDecoder) {", x.hn, x.genMethodNameT(t), x.genTypeName(t), x.cpfx)
+ x.genRequiredMethodVars(false)
+ switch t.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Chan:
+ x.decListFallback("v", rtid, t)
+ case reflect.Map:
+ x.decMapFallback("v", rtid, t)
+ default:
+ panic(genExpectArrayOrMapErr)
+ }
+ x.line("}")
+ x.line("")
+ }
+
+ x.line("")
+}
+
+func (x *genRunner) checkForSelfer(t reflect.Type, varname string) bool {
+ // return varname != genTopLevelVarName && t != x.tc
+ // the only time we checkForSelfer is if we are not at the TOP of the generated code.
+ return varname != genTopLevelVarName
+}
+
+func (x *genRunner) arr2str(t reflect.Type, s string) string {
+ if t.Kind() == reflect.Array {
+ return s
+ }
+ return ""
+}
+
+func (x *genRunner) genRequiredMethodVars(encode bool) {
+ x.line("var h " + x.hn)
+ if encode {
+ x.line("z, r := " + x.cpfx + "GenHelperEncoder(e)")
+ } else {
+ x.line("z, r := " + x.cpfx + "GenHelperDecoder(d)")
+ }
+ x.line("_, _, _ = h, z, r")
+}
+
+func (x *genRunner) genRefPkgs(t reflect.Type) {
+ if _, ok := x.is[t]; ok {
+ return
+ }
+ // fmt.Printf(">>>>>>: PkgPath: '%v', Name: '%s'\n", genImportPath(t), t.Name())
+ x.is[t] = struct{}{}
+ tpkg, tname := genImportPath(t), t.Name()
+ if tpkg != "" && tpkg != x.bp && tpkg != x.cp && tname != "" && tname[0] >= 'A' && tname[0] <= 'Z' {
+ if _, ok := x.im[tpkg]; !ok {
+ x.im[tpkg] = t
+ if idx := strings.LastIndex(tpkg, "/"); idx < 0 {
+ x.imn[tpkg] = tpkg
+ } else {
+ x.imc++
+ x.imn[tpkg] = "pkg" + strconv.FormatUint(x.imc, 10) + "_" + tpkg[idx+1:]
+ }
+ }
+ }
+ switch t.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Ptr, reflect.Chan:
+ x.genRefPkgs(t.Elem())
+ case reflect.Map:
+ x.genRefPkgs(t.Elem())
+ x.genRefPkgs(t.Key())
+ case reflect.Struct:
+ for i := 0; i < t.NumField(); i++ {
+ if fname := t.Field(i).Name; fname != "" && fname[0] >= 'A' && fname[0] <= 'Z' {
+ x.genRefPkgs(t.Field(i).Type)
+ }
+ }
+ }
+}
+
+func (x *genRunner) line(s string) {
+ x.out(s)
+ if len(s) == 0 || s[len(s)-1] != '\n' {
+ x.out("\n")
+ }
+}
+
+func (x *genRunner) varsfx() string {
+ x.c++
+ return strconv.FormatUint(x.c, 10)
+}
+
+func (x *genRunner) out(s string) {
+ if _, err := io.WriteString(x.w, s); err != nil {
+ panic(err)
+ }
+}
+
+func (x *genRunner) linef(s string, params ...interface{}) {
+ x.line(fmt.Sprintf(s, params...))
+}
+
+func (x *genRunner) outf(s string, params ...interface{}) {
+ x.out(fmt.Sprintf(s, params...))
+}
+
+func (x *genRunner) genTypeName(t reflect.Type) (n string) {
+ // defer func() { fmt.Printf(">>>> ####: genTypeName: t: %v, name: '%s'\n", t, n) }()
+
+ // if the type has a PkgPath, which doesn't match the current package,
+ // then include it.
+ // We cannot depend on t.String() because it includes current package,
+ // or t.PkgPath because it includes full import path,
+ //
+ var ptrPfx string
+ for t.Kind() == reflect.Ptr {
+ ptrPfx += "*"
+ t = t.Elem()
+ }
+ if tn := t.Name(); tn != "" {
+ return ptrPfx + x.genTypeNamePrim(t)
+ }
+ switch t.Kind() {
+ case reflect.Map:
+ return ptrPfx + "map[" + x.genTypeName(t.Key()) + "]" + x.genTypeName(t.Elem())
+ case reflect.Slice:
+ return ptrPfx + "[]" + x.genTypeName(t.Elem())
+ case reflect.Array:
+ return ptrPfx + "[" + strconv.FormatInt(int64(t.Len()), 10) + "]" + x.genTypeName(t.Elem())
+ case reflect.Chan:
+ return ptrPfx + t.ChanDir().String() + " " + x.genTypeName(t.Elem())
+ default:
+ if t == intfTyp {
+ return ptrPfx + "interface{}"
+ } else {
+ return ptrPfx + x.genTypeNamePrim(t)
+ }
+ }
+}
+
+func (x *genRunner) genTypeNamePrim(t reflect.Type) (n string) {
+ if t.Name() == "" {
+ return t.String()
+ } else if genImportPath(t) == "" || genImportPath(t) == genImportPath(x.tc) {
+ return t.Name()
+ } else {
+ return x.imn[genImportPath(t)] + "." + t.Name()
+ // return t.String() // best way to get the package name inclusive
+ }
+}
+
+func (x *genRunner) genZeroValueR(t reflect.Type) string {
+ // if t is a named type, w
+ switch t.Kind() {
+ case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func,
+ reflect.Slice, reflect.Map, reflect.Invalid:
+ return "nil"
+ case reflect.Bool:
+ return "false"
+ case reflect.String:
+ return `""`
+ case reflect.Struct, reflect.Array:
+ return x.genTypeName(t) + "{}"
+ default: // all numbers
+ return "0"
+ }
+}
+
+func (x *genRunner) genMethodNameT(t reflect.Type) (s string) {
+ return genMethodNameT(t, x.tc)
+}
+
+func (x *genRunner) selfer(encode bool) {
+ t := x.tc
+ t0 := t
+ // always make decode use a pointer receiver,
+ // and structs always use a ptr receiver (encode|decode)
+ isptr := !encode || t.Kind() == reflect.Struct
+ fnSigPfx := "func (x "
+ if isptr {
+ fnSigPfx += "*"
+ }
+ fnSigPfx += x.genTypeName(t)
+
+ x.out(fnSigPfx)
+ if isptr {
+ t = reflect.PtrTo(t)
+ }
+ if encode {
+ x.line(") CodecEncodeSelf(e *" + x.cpfx + "Encoder) {")
+ x.genRequiredMethodVars(true)
+ // x.enc(genTopLevelVarName, t)
+ x.encVar(genTopLevelVarName, t)
+ } else {
+ x.line(") CodecDecodeSelf(d *" + x.cpfx + "Decoder) {")
+ x.genRequiredMethodVars(false)
+ // do not use decVar, as there is no need to check TryDecodeAsNil
+ // or way to elegantly handle that, and also setting it to a
+ // non-nil value doesn't affect the pointer passed.
+ // x.decVar(genTopLevelVarName, t, false)
+ x.dec(genTopLevelVarName, t0)
+ }
+ x.line("}")
+ x.line("")
+
+ if encode || t0.Kind() != reflect.Struct {
+ return
+ }
+
+ // write is containerMap
+ if genUseOneFunctionForDecStructMap {
+ x.out(fnSigPfx)
+ x.line(") codecDecodeSelfFromMap(l int, d *" + x.cpfx + "Decoder) {")
+ x.genRequiredMethodVars(false)
+ x.decStructMap(genTopLevelVarName, "l", reflect.ValueOf(t0).Pointer(), t0, 0)
+ x.line("}")
+ x.line("")
+ } else {
+ x.out(fnSigPfx)
+ x.line(") codecDecodeSelfFromMapLenPrefix(l int, d *" + x.cpfx + "Decoder) {")
+ x.genRequiredMethodVars(false)
+ x.decStructMap(genTopLevelVarName, "l", reflect.ValueOf(t0).Pointer(), t0, 1)
+ x.line("}")
+ x.line("")
+
+ x.out(fnSigPfx)
+ x.line(") codecDecodeSelfFromMapCheckBreak(l int, d *" + x.cpfx + "Decoder) {")
+ x.genRequiredMethodVars(false)
+ x.decStructMap(genTopLevelVarName, "l", reflect.ValueOf(t0).Pointer(), t0, 2)
+ x.line("}")
+ x.line("")
+ }
+
+ // write containerArray
+ x.out(fnSigPfx)
+ x.line(") codecDecodeSelfFromArray(l int, d *" + x.cpfx + "Decoder) {")
+ x.genRequiredMethodVars(false)
+ x.decStructArray(genTopLevelVarName, "l", "return", reflect.ValueOf(t0).Pointer(), t0)
+ x.line("}")
+ x.line("")
+
+}
+
+// used for chan, array, slice, map
+func (x *genRunner) xtraSM(varname string, encode bool, t reflect.Type) {
+ if encode {
+ x.linef("h.enc%s((%s%s)(%s), e)", x.genMethodNameT(t), x.arr2str(t, "*"), x.genTypeName(t), varname)
+ // x.line("h.enc" + x.genMethodNameT(t) + "(" + x.genTypeName(t) + "(" + varname + "), e)")
+ } else {
+ x.linef("h.dec%s((*%s)(%s), d)", x.genMethodNameT(t), x.genTypeName(t), varname)
+ // x.line("h.dec" + x.genMethodNameT(t) + "((*" + x.genTypeName(t) + ")(" + varname + "), d)")
+ }
+ if _, ok := x.tm[t]; !ok {
+ x.tm[t] = struct{}{}
+ x.ts = append(x.ts, t)
+ }
+}
+
+// encVar will encode a variable.
+// The parameter, t, is the reflect.Type of the variable itself
+func (x *genRunner) encVar(varname string, t reflect.Type) {
+ // fmt.Printf(">>>>>> varname: %s, t: %v\n", varname, t)
+ var checkNil bool
+ switch t.Kind() {
+ case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map, reflect.Chan:
+ checkNil = true
+ }
+ if checkNil {
+ x.linef("if %s == nil { r.EncodeNil() } else { ", varname)
+ }
+ switch t.Kind() {
+ case reflect.Ptr:
+ switch t.Elem().Kind() {
+ case reflect.Struct, reflect.Array:
+ x.enc(varname, genNonPtr(t))
+ default:
+ i := x.varsfx()
+ x.line(genTempVarPfx + i + " := *" + varname)
+ x.enc(genTempVarPfx+i, genNonPtr(t))
+ }
+ case reflect.Struct, reflect.Array:
+ i := x.varsfx()
+ x.line(genTempVarPfx + i + " := &" + varname)
+ x.enc(genTempVarPfx+i, t)
+ default:
+ x.enc(varname, t)
+ }
+
+ if checkNil {
+ x.line("}")
+ }
+
+}
+
+// enc will encode a variable (varname) of type T,
+// except t is of kind reflect.Struct or reflect.Array, wherein varname is of type *T (to prevent copying)
+func (x *genRunner) enc(varname string, t reflect.Type) {
+ // varName here must be to a pointer to a struct/array, or to a value directly.
+ rtid := reflect.ValueOf(t).Pointer()
+ // We call CodecEncodeSelf if one of the following are honored:
+ // - the type already implements Selfer, call that
+ // - the type has a Selfer implementation just created, use that
+ // - the type is in the list of the ones we will generate for, but it is not currently being generated
+
+ tptr := reflect.PtrTo(t)
+ tk := t.Kind()
+ if x.checkForSelfer(t, varname) {
+ if t.Implements(selferTyp) || (tptr.Implements(selferTyp) && (tk == reflect.Array || tk == reflect.Struct)) {
+ x.line(varname + ".CodecEncodeSelf(e)")
+ return
+ }
+
+ if _, ok := x.te[rtid]; ok {
+ x.line(varname + ".CodecEncodeSelf(e)")
+ return
+ }
+ }
+
+ inlist := false
+ for _, t0 := range x.t {
+ if t == t0 {
+ inlist = true
+ if x.checkForSelfer(t, varname) {
+ x.line(varname + ".CodecEncodeSelf(e)")
+ return
+ }
+ break
+ }
+ }
+
+ var rtidAdded bool
+ if t == x.tc {
+ x.te[rtid] = true
+ rtidAdded = true
+ }
+
+ // check if
+ // - type is RawExt
+ // - the type implements (Text|JSON|Binary)(Unm|M)arshal
+ mi := x.varsfx()
+ x.linef("%sm%s := z.EncBinary()", genTempVarPfx, mi)
+ x.linef("_ = %sm%s", genTempVarPfx, mi)
+ x.line("if false {") //start if block
+ defer func() { x.line("}") }() //end if block
+
+ if t == rawExtTyp {
+ x.linef("} else { r.EncodeRawExt(%v, e)", varname)
+ return
+ }
+ // HACK: Support for Builtins.
+ // Currently, only Binc supports builtins, and the only builtin type is time.Time.
+ // Have a method that returns the rtid for time.Time if Handle is Binc.
+ if t == timeTyp {
+ vrtid := genTempVarPfx + "m" + x.varsfx()
+ x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
+ x.linef("r.EncodeBuiltin(%s, %s)", vrtid, varname)
+ }
+ // only check for extensions if the type is named, and has a packagePath.
+ if genImportPath(t) != "" && t.Name() != "" {
+ // first check if extensions are configued, before doing the interface conversion
+ x.linef("} else if z.HasExtensions() && z.EncExt(%s) {", varname)
+ }
+ if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) {
+ x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
+ }
+ if t.Implements(jsonMarshalerTyp) || tptr.Implements(jsonMarshalerTyp) {
+ x.linef("} else if !%sm%s && z.IsJSONHandle() { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname)
+ } else if t.Implements(textMarshalerTyp) || tptr.Implements(textMarshalerTyp) {
+ x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname)
+ }
+
+ x.line("} else {")
+
+ switch t.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ x.line("r.EncodeInt(int64(" + varname + "))")
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ x.line("r.EncodeUint(uint64(" + varname + "))")
+ case reflect.Float32:
+ x.line("r.EncodeFloat32(float32(" + varname + "))")
+ case reflect.Float64:
+ x.line("r.EncodeFloat64(float64(" + varname + "))")
+ case reflect.Bool:
+ x.line("r.EncodeBool(bool(" + varname + "))")
+ case reflect.String:
+ x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + ", string(" + varname + "))")
+ case reflect.Chan:
+ x.xtraSM(varname, true, t)
+ // x.encListFallback(varname, rtid, t)
+ case reflect.Array:
+ x.xtraSM(varname, true, t)
+ case reflect.Slice:
+ // if nil, call dedicated function
+ // if a []uint8, call dedicated function
+ // if a known fastpath slice, call dedicated function
+ // else write encode function in-line.
+ // - if elements are primitives or Selfers, call dedicated function on each member.
+ // - else call Encoder.encode(XXX) on it.
+ if rtid == uint8SliceTypId {
+ x.line("r.EncodeStringBytes(codecSelferC_RAW" + x.xs + ", []byte(" + varname + "))")
+ } else if fastpathAV.index(rtid) != -1 {
+ g := x.newGenV(t)
+ x.line("z.F." + g.MethodNamePfx("Enc", false) + "V(" + varname + ", false, e)")
+ } else {
+ x.xtraSM(varname, true, t)
+ // x.encListFallback(varname, rtid, t)
+ }
+ case reflect.Map:
+ // if nil, call dedicated function
+ // if a known fastpath map, call dedicated function
+ // else write encode function in-line.
+ // - if elements are primitives or Selfers, call dedicated function on each member.
+ // - else call Encoder.encode(XXX) on it.
+ // x.line("if " + varname + " == nil { \nr.EncodeNil()\n } else { ")
+ if fastpathAV.index(rtid) != -1 {
+ g := x.newGenV(t)
+ x.line("z.F." + g.MethodNamePfx("Enc", false) + "V(" + varname + ", false, e)")
+ } else {
+ x.xtraSM(varname, true, t)
+ // x.encMapFallback(varname, rtid, t)
+ }
+ case reflect.Struct:
+ if !inlist {
+ delete(x.te, rtid)
+ x.line("z.EncFallback(" + varname + ")")
+ break
+ }
+ x.encStruct(varname, rtid, t)
+ default:
+ if rtidAdded {
+ delete(x.te, rtid)
+ }
+ x.line("z.EncFallback(" + varname + ")")
+ }
+}
+
+func (x *genRunner) encZero(t reflect.Type) {
+ switch t.Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ x.line("r.EncodeInt(0)")
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ x.line("r.EncodeUint(0)")
+ case reflect.Float32:
+ x.line("r.EncodeFloat32(0)")
+ case reflect.Float64:
+ x.line("r.EncodeFloat64(0)")
+ case reflect.Bool:
+ x.line("r.EncodeBool(false)")
+ case reflect.String:
+ x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + `, "")`)
+ default:
+ x.line("r.EncodeNil()")
+ }
+}
+
+func (x *genRunner) encStruct(varname string, rtid uintptr, t reflect.Type) {
+ // Use knowledge from structfieldinfo (mbs, encodable fields. Ignore omitempty. )
+ // replicate code in kStruct i.e. for each field, deref type to non-pointer, and call x.enc on it
+
+ // if t === type currently running selfer on, do for all
+ ti := getTypeInfo(rtid, t)
+ i := x.varsfx()
+ sepVarname := genTempVarPfx + "sep" + i
+ numfieldsvar := genTempVarPfx + "q" + i
+ ti2arrayvar := genTempVarPfx + "r" + i
+ struct2arrvar := genTempVarPfx + "2arr" + i
+
+ x.line(sepVarname + " := !z.EncBinary()")
+ x.linef("%s := z.EncBasicHandle().StructToArray", struct2arrvar)
+ tisfi := ti.sfip // always use sequence from file. decStruct expects same thing.
+ // due to omitEmpty, we need to calculate the
+ // number of non-empty things we write out first.
+ // This is required as we need to pre-determine the size of the container,
+ // to support length-prefixing.
+ x.linef("var %s [%v]bool", numfieldsvar, len(tisfi))
+ x.linef("_, _, _ = %s, %s, %s", sepVarname, numfieldsvar, struct2arrvar)
+ x.linef("const %s bool = %v", ti2arrayvar, ti.toArray)
+ nn := 0
+ for j, si := range tisfi {
+ if !si.omitEmpty {
+ nn++
+ continue
+ }
+ var t2 reflect.StructField
+ var omitline string
+ if si.i != -1 {
+ t2 = t.Field(int(si.i))
+ } else {
+ t2typ := t
+ varname3 := varname
+ for _, ix := range si.is {
+ for t2typ.Kind() == reflect.Ptr {
+ t2typ = t2typ.Elem()
+ }
+ t2 = t2typ.Field(ix)
+ t2typ = t2.Type
+ varname3 = varname3 + "." + t2.Name
+ if t2typ.Kind() == reflect.Ptr {
+ omitline += varname3 + " != nil && "
+ }
+ }
+ }
+ // never check omitEmpty on a struct type, as it may contain uncomparable map/slice/etc.
+ // also, for maps/slices/arrays, check if len ! 0 (not if == zero value)
+ switch t2.Type.Kind() {
+ case reflect.Struct:
+ omitline += " true"
+ case reflect.Map, reflect.Slice, reflect.Array, reflect.Chan:
+ omitline += "len(" + varname + "." + t2.Name + ") != 0"
+ default:
+ omitline += varname + "." + t2.Name + " != " + x.genZeroValueR(t2.Type)
+ }
+ x.linef("%s[%v] = %s", numfieldsvar, j, omitline)
+ }
+ x.linef("if %s || %s {", ti2arrayvar, struct2arrvar) // if ti.toArray {
+ x.line("r.EncodeArrayStart(" + strconv.FormatInt(int64(len(tisfi)), 10) + ")")
+ x.linef("} else {") // if not ti.toArray
+ x.linef("var %snn%s int = %v", genTempVarPfx, i, nn)
+ x.linef("for _, b := range %s { if b { %snn%s++ } }", numfieldsvar, genTempVarPfx, i)
+ x.linef("r.EncodeMapStart(%snn%s)", genTempVarPfx, i)
+ // x.line("r.EncodeMapStart(" + strconv.FormatInt(int64(len(tisfi)), 10) + ")")
+ x.line("}") // close if not StructToArray
+
+ for j, si := range tisfi {
+ i := x.varsfx()
+ isNilVarName := genTempVarPfx + "n" + i
+ var labelUsed bool
+ var t2 reflect.StructField
+ if si.i != -1 {
+ t2 = t.Field(int(si.i))
+ } else {
+ t2typ := t
+ varname3 := varname
+ for _, ix := range si.is {
+ // fmt.Printf("%%%% %v, ix: %v\n", t2typ, ix)
+ for t2typ.Kind() == reflect.Ptr {
+ t2typ = t2typ.Elem()
+ }
+ t2 = t2typ.Field(ix)
+ t2typ = t2.Type
+ varname3 = varname3 + "." + t2.Name
+ if t2typ.Kind() == reflect.Ptr {
+ if !labelUsed {
+ x.line("var " + isNilVarName + " bool")
+ }
+ x.line("if " + varname3 + " == nil { " + isNilVarName + " = true ")
+ x.line("goto LABEL" + i)
+ x.line("}")
+ labelUsed = true
+ // "varname3 = new(" + x.genTypeName(t3.Elem()) + ") }")
+ }
+ }
+ // t2 = t.FieldByIndex(si.is)
+ }
+ if labelUsed {
+ x.line("LABEL" + i + ":")
+ }
+ // if the type of the field is a Selfer, or one of the ones
+
+ x.linef("if %s || %s {", ti2arrayvar, struct2arrvar) // if ti.toArray
+ if labelUsed {
+ x.line("if " + isNilVarName + " { r.EncodeNil() } else { ")
+ }
+ if si.omitEmpty {
+ x.linef("if %s[%v] {", numfieldsvar, j)
+ // omitEmptyVarNameX := genTempVarPfx + "ov" + i
+ // x.line("var " + omitEmptyVarNameX + " " + x.genTypeName(t2.Type))
+ // x.encVar(omitEmptyVarNameX, t2.Type)
+ }
+ x.encVar(varname+"."+t2.Name, t2.Type)
+ if si.omitEmpty {
+ x.linef("} else {")
+ x.encZero(t2.Type)
+ x.linef("}")
+ }
+ if labelUsed {
+ x.line("}")
+ }
+ x.linef("} else {") // if not ti.toArray
+ // omitEmptyVar := genTempVarPfx + "x" + i + t2.Name
+ // x.line("const " + omitEmptyVar + " bool = " + strconv.FormatBool(si.omitEmpty))
+ // doOmitEmpty := si.omitEmpty && t2.Type.Kind() != reflect.Struct
+ if si.omitEmpty {
+ x.linef("if %s[%v] {", numfieldsvar, j)
+ // x.linef(`println("Encoding field: %v")`, j)
+ // x.out("if ")
+ // if labelUsed {
+ // x.out("!" + isNilVarName + " && ")
+ // }
+ // x.line(varname + "." + t2.Name + " != " + genZeroValueR(t2.Type, x.tc) + " {")
+ }
+ // x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + ", string(\"" + t2.Name + "\"))")
+ x.line("r.EncodeString(codecSelferC_UTF8" + x.xs + ", string(\"" + si.encName + "\"))")
+ if labelUsed {
+ x.line("if " + isNilVarName + " { r.EncodeNil() } else { ")
+ x.encVar(varname+"."+t2.Name, t2.Type)
+ x.line("}")
+ } else {
+ x.encVar(varname+"."+t2.Name, t2.Type)
+ }
+ if si.omitEmpty {
+ x.line("}")
+ }
+ x.linef("} ") // end if/else ti.toArray
+ }
+ x.line("if " + sepVarname + " {")
+ x.line("r.EncodeEnd()")
+ x.line("}")
+}
+
+func (x *genRunner) encListFallback(varname string, t reflect.Type) {
+ i := x.varsfx()
+ g := genTempVarPfx
+ x.line("r.EncodeArrayStart(len(" + varname + "))")
+ if t.Kind() == reflect.Chan {
+ x.linef("for %si%s, %si2%s := 0, len(%s); %si%s < %si2%s; %si%s++ {", g, i, g, i, varname, g, i, g, i, g, i)
+ x.linef("%sv%s := <-%s", g, i, varname)
+ } else {
+ // x.linef("for %si%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
+ x.linef("for _, %sv%s := range %s {", genTempVarPfx, i, varname)
+ }
+ x.encVar(genTempVarPfx+"v"+i, t.Elem())
+ x.line("}")
+ x.line("r.EncodeEnd()")
+}
+
+func (x *genRunner) encMapFallback(varname string, t reflect.Type) {
+ i := x.varsfx()
+ x.line("r.EncodeMapStart(len(" + varname + "))")
+ x.linef("for %sk%s, %sv%s := range %s {", genTempVarPfx, i, genTempVarPfx, i, varname)
+ // x.line("for " + genTempVarPfx + "k" + i + ", " + genTempVarPfx + "v" + i + " := range " + varname + " {")
+ x.encVar(genTempVarPfx+"k"+i, t.Key())
+ x.encVar(genTempVarPfx+"v"+i, t.Elem())
+ x.line("}")
+ x.line("r.EncodeEnd()")
+}
+
+func (x *genRunner) decVar(varname string, t reflect.Type, canBeNil bool) {
+ // We only encode as nil if a nillable value.
+ // This removes some of the wasted checks for TryDecodeAsNil.
+ // We need to think about this more, to see what happens if omitempty, etc
+ // cause a nil value to be stored when something is expected.
+ // This could happen when decoding from a struct encoded as an array.
+ // For that, decVar should be called with canNil=true, to force true as its value.
+ i := x.varsfx()
+ if !canBeNil {
+ canBeNil = genAnythingCanBeNil || !genIsImmutable(t)
+ }
+ if canBeNil {
+ x.line("if r.TryDecodeAsNil() {")
+ if t.Kind() == reflect.Ptr {
+ x.line("if " + varname + " != nil { ")
+ // x.line("var " + genTempVarPfx + i + " " + x.genTypeName(t.Elem()))
+ // x.line("*" + varname + " = " + genTempVarPfx + i)
+
+ // if varname is a field of a struct (has a dot in it),
+ // then just set it to nil
+ if strings.IndexByte(varname, '.') != -1 {
+ x.line(varname + " = nil")
+ } else {
+ x.line("*" + varname + " = " + x.genZeroValueR(t.Elem()))
+ }
+ // x.line("*" + varname + " = nil")
+ x.line("}")
+
+ } else {
+ // x.line("var " + genTempVarPfx + i + " " + x.genTypeName(t))
+ // x.line(varname + " = " + genTempVarPfx + i)
+ x.line(varname + " = " + x.genZeroValueR(t))
+ }
+ x.line("} else {")
+ } else {
+ x.line("// cannot be nil")
+ }
+ if t.Kind() != reflect.Ptr {
+ if x.decTryAssignPrimitive(varname, t) {
+ x.line(genTempVarPfx + "v" + i + " := &" + varname)
+ x.dec(genTempVarPfx+"v"+i, t)
+ }
+ } else {
+ x.linef("if %s == nil { %s = new(%s) }", varname, varname, x.genTypeName(t.Elem()))
+ // Ensure we set underlying ptr to a non-nil value (so we can deref to it later).
+ // There's a chance of a **T in here which is nil.
+ var ptrPfx string
+ for t = t.Elem(); t.Kind() == reflect.Ptr; t = t.Elem() {
+ ptrPfx += "*"
+ x.linef("if %s%s == nil { %s%s = new(%s)}",
+ ptrPfx, varname, ptrPfx, varname, x.genTypeName(t))
+ }
+ // if varname has [ in it, then create temp variable for this ptr thingie
+ if strings.Index(varname, "[") >= 0 {
+ varname2 := genTempVarPfx + "w" + i
+ x.line(varname2 + " := " + varname)
+ varname = varname2
+ }
+
+ if ptrPfx == "" {
+ x.dec(varname, t)
+ } else {
+ x.line(genTempVarPfx + "z" + i + " := " + ptrPfx + varname)
+ x.dec(genTempVarPfx+"z"+i, t)
+ }
+
+ }
+
+ if canBeNil {
+ x.line("} ")
+ }
+}
+
+func (x *genRunner) dec(varname string, t reflect.Type) {
+ // assumptions:
+ // - the varname is to a pointer already. No need to take address of it
+ // - t is always a baseType T (not a *T, etc).
+ rtid := reflect.ValueOf(t).Pointer()
+ tptr := reflect.PtrTo(t)
+ if x.checkForSelfer(t, varname) {
+ if t.Implements(selferTyp) || tptr.Implements(selferTyp) {
+ x.line(varname + ".CodecDecodeSelf(d)")
+ return
+ }
+ if _, ok := x.td[rtid]; ok {
+ x.line(varname + ".CodecDecodeSelf(d)")
+ return
+ }
+ }
+
+ inlist := false
+ for _, t0 := range x.t {
+ if t == t0 {
+ inlist = true
+ if x.checkForSelfer(t, varname) {
+ x.line(varname + ".CodecDecodeSelf(d)")
+ return
+ }
+ break
+ }
+ }
+
+ var rtidAdded bool
+ if t == x.tc {
+ x.td[rtid] = true
+ rtidAdded = true
+ }
+
+ // check if
+ // - type is RawExt
+ // - the type implements (Text|JSON|Binary)(Unm|M)arshal
+ mi := x.varsfx()
+ x.linef("%sm%s := z.DecBinary()", genTempVarPfx, mi)
+ x.linef("_ = %sm%s", genTempVarPfx, mi)
+ x.line("if false {") //start if block
+ defer func() { x.line("}") }() //end if block
+
+ if t == rawExtTyp {
+ x.linef("} else { r.DecodeExt(%v, 0, nil)", varname)
+ return
+ }
+
+ // HACK: Support for Builtins.
+ // Currently, only Binc supports builtins, and the only builtin type is time.Time.
+ // Have a method that returns the rtid for time.Time if Handle is Binc.
+ if t == timeTyp {
+ vrtid := genTempVarPfx + "m" + x.varsfx()
+ x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
+ x.linef("r.DecodeBuiltin(%s, %s)", vrtid, varname)
+ }
+ // only check for extensions if the type is named, and has a packagePath.
+ if genImportPath(t) != "" && t.Name() != "" {
+ // first check if extensions are configued, before doing the interface conversion
+ x.linef("} else if z.HasExtensions() && z.DecExt(%s) {", varname)
+ }
+
+ if t.Implements(binaryUnmarshalerTyp) || tptr.Implements(binaryUnmarshalerTyp) {
+ x.linef("} else if %sm%s { z.DecBinaryUnmarshal(%v) ", genTempVarPfx, mi, varname)
+ }
+ if t.Implements(jsonUnmarshalerTyp) || tptr.Implements(jsonUnmarshalerTyp) {
+ x.linef("} else if !%sm%s && z.IsJSONHandle() { z.DecJSONUnmarshal(%v)", genTempVarPfx, mi, varname)
+ } else if t.Implements(textUnmarshalerTyp) || tptr.Implements(textUnmarshalerTyp) {
+ x.linef("} else if !%sm%s { z.DecTextUnmarshal(%v)", genTempVarPfx, mi, varname)
+ }
+
+ x.line("} else {")
+
+ // Since these are pointers, we cannot share, and have to use them one by one
+ switch t.Kind() {
+ case reflect.Int:
+ x.line("*((*int)(" + varname + ")) = int(r.DecodeInt(codecSelferBitsize" + x.xs + "))")
+ // x.line("z.DecInt((*int)(" + varname + "))")
+ case reflect.Int8:
+ x.line("*((*int8)(" + varname + ")) = int8(r.DecodeInt(8))")
+ // x.line("z.DecInt8((*int8)(" + varname + "))")
+ case reflect.Int16:
+ x.line("*((*int16)(" + varname + ")) = int16(r.DecodeInt(16))")
+ // x.line("z.DecInt16((*int16)(" + varname + "))")
+ case reflect.Int32:
+ x.line("*((*int32)(" + varname + ")) = int32(r.DecodeInt(32))")
+ // x.line("z.DecInt32((*int32)(" + varname + "))")
+ case reflect.Int64:
+ x.line("*((*int64)(" + varname + ")) = int64(r.DecodeInt(64))")
+ // x.line("z.DecInt64((*int64)(" + varname + "))")
+
+ case reflect.Uint:
+ x.line("*((*uint)(" + varname + ")) = uint(r.DecodeUint(codecSelferBitsize" + x.xs + "))")
+ // x.line("z.DecUint((*uint)(" + varname + "))")
+ case reflect.Uint8:
+ x.line("*((*uint8)(" + varname + ")) = uint8(r.DecodeUint(8))")
+ // x.line("z.DecUint8((*uint8)(" + varname + "))")
+ case reflect.Uint16:
+ x.line("*((*uint16)(" + varname + ")) = uint16(r.DecodeUint(16))")
+ //x.line("z.DecUint16((*uint16)(" + varname + "))")
+ case reflect.Uint32:
+ x.line("*((*uint32)(" + varname + ")) = uint32(r.DecodeUint(32))")
+ //x.line("z.DecUint32((*uint32)(" + varname + "))")
+ case reflect.Uint64:
+ x.line("*((*uint64)(" + varname + ")) = uint64(r.DecodeUint(64))")
+ //x.line("z.DecUint64((*uint64)(" + varname + "))")
+ case reflect.Uintptr:
+ x.line("*((*uintptr)(" + varname + ")) = uintptr(r.DecodeUint(codecSelferBitsize" + x.xs + "))")
+
+ case reflect.Float32:
+ x.line("*((*float32)(" + varname + ")) = float32(r.DecodeFloat(true))")
+ //x.line("z.DecFloat32((*float32)(" + varname + "))")
+ case reflect.Float64:
+ x.line("*((*float64)(" + varname + ")) = float64(r.DecodeFloat(false))")
+ // x.line("z.DecFloat64((*float64)(" + varname + "))")
+
+ case reflect.Bool:
+ x.line("*((*bool)(" + varname + ")) = r.DecodeBool()")
+ // x.line("z.DecBool((*bool)(" + varname + "))")
+ case reflect.String:
+ x.line("*((*string)(" + varname + ")) = r.DecodeString()")
+ // x.line("z.DecString((*string)(" + varname + "))")
+ case reflect.Array, reflect.Chan:
+ x.xtraSM(varname, false, t)
+ // x.decListFallback(varname, rtid, true, t)
+ case reflect.Slice:
+ // if a []uint8, call dedicated function
+ // if a known fastpath slice, call dedicated function
+ // else write encode function in-line.
+ // - if elements are primitives or Selfers, call dedicated function on each member.
+ // - else call Encoder.encode(XXX) on it.
+ if rtid == uint8SliceTypId {
+ x.line("*" + varname + " = r.DecodeBytes(*(*[]byte)(" + varname + "), false, false)")
+ } else if fastpathAV.index(rtid) != -1 {
+ g := x.newGenV(t)
+ x.line("z.F." + g.MethodNamePfx("Dec", false) + "X(" + varname + ", false, d)")
+ // x.line("z." + g.MethodNamePfx("Dec", false) + "(" + varname + ")")
+ // x.line(g.FastpathName(false) + "(" + varname + ", d)")
+ } else {
+ x.xtraSM(varname, false, t)
+ // x.decListFallback(varname, rtid, false, t)
+ }
+ case reflect.Map:
+ // if a known fastpath map, call dedicated function
+ // else write encode function in-line.
+ // - if elements are primitives or Selfers, call dedicated function on each member.
+ // - else call Encoder.encode(XXX) on it.
+ if fastpathAV.index(rtid) != -1 {
+ g := x.newGenV(t)
+ x.line("z.F." + g.MethodNamePfx("Dec", false) + "X(" + varname + ", false, d)")
+ // x.line("z." + g.MethodNamePfx("Dec", false) + "(" + varname + ")")
+ // x.line(g.FastpathName(false) + "(" + varname + ", d)")
+ } else {
+ x.xtraSM(varname, false, t)
+ // x.decMapFallback(varname, rtid, t)
+ }
+ case reflect.Struct:
+ if inlist {
+ x.decStruct(varname, rtid, t)
+ } else {
+ // delete(x.td, rtid)
+ x.line("z.DecFallback(" + varname + ", false)")
+ }
+ default:
+ if rtidAdded {
+ delete(x.te, rtid)
+ }
+ x.line("z.DecFallback(" + varname + ", true)")
+ }
+}
+
+func (x *genRunner) decTryAssignPrimitive(varname string, t reflect.Type) (tryAsPtr bool) {
+ // We have to use the actual type name when doing a direct assignment.
+ // We don't have the luxury of casting the pointer to the underlying type.
+ //
+ // Consequently, in the situation of a
+ // type Message int32
+ // var x Message
+ // var i int32 = 32
+ // x = i // this will bomb
+ // x = Message(i) // this will work
+ // *((*int32)(&x)) = i // this will work
+ //
+ // Consequently, we replace:
+ // case reflect.Uint32: x.line(varname + " = uint32(r.DecodeUint(32))")
+ // with:
+ // case reflect.Uint32: x.line(varname + " = " + genTypeNamePrim(t, x.tc) + "(r.DecodeUint(32))")
+
+ xfn := func(t reflect.Type) string {
+ return x.genTypeNamePrim(t)
+ }
+ switch t.Kind() {
+ case reflect.Int:
+ x.linef("%s = %s(r.DecodeInt(codecSelferBitsize%s))", varname, xfn(t), x.xs)
+ case reflect.Int8:
+ x.linef("%s = %s(r.DecodeInt(8))", varname, xfn(t))
+ case reflect.Int16:
+ x.linef("%s = %s(r.DecodeInt(16))", varname, xfn(t))
+ case reflect.Int32:
+ x.linef("%s = %s(r.DecodeInt(32))", varname, xfn(t))
+ case reflect.Int64:
+ x.linef("%s = %s(r.DecodeInt(64))", varname, xfn(t))
+
+ case reflect.Uint:
+ x.linef("%s = %s(r.DecodeUint(codecSelferBitsize%s))", varname, xfn(t), x.xs)
+ case reflect.Uint8:
+ x.linef("%s = %s(r.DecodeUint(8))", varname, xfn(t))
+ case reflect.Uint16:
+ x.linef("%s = %s(r.DecodeUint(16))", varname, xfn(t))
+ case reflect.Uint32:
+ x.linef("%s = %s(r.DecodeUint(32))", varname, xfn(t))
+ case reflect.Uint64:
+ x.linef("%s = %s(r.DecodeUint(64))", varname, xfn(t))
+ case reflect.Uintptr:
+ x.linef("%s = %s(r.DecodeUint(codecSelferBitsize%s))", varname, xfn(t), x.xs)
+
+ case reflect.Float32:
+ x.linef("%s = %s(r.DecodeFloat(true))", varname, xfn(t))
+ case reflect.Float64:
+ x.linef("%s = %s(r.DecodeFloat(false))", varname, xfn(t))
+
+ case reflect.Bool:
+ x.linef("%s = %s(r.DecodeBool())", varname, xfn(t))
+ case reflect.String:
+ x.linef("%s = %s(r.DecodeString())", varname, xfn(t))
+ default:
+ tryAsPtr = true
+ }
+ return
+}
+
+func (x *genRunner) decListFallback(varname string, rtid uintptr, t reflect.Type) {
+ type tstruc struct {
+ TempVar string
+ Rand string
+ Varname string
+ CTyp string
+ Typ string
+ Immutable bool
+ Size int
+ }
+ telem := t.Elem()
+ ts := tstruc{genTempVarPfx, x.varsfx(), varname, x.genTypeName(t), x.genTypeName(telem), genIsImmutable(telem), int(telem.Size())}
+
+ funcs := make(template.FuncMap)
+
+ funcs["decLineVar"] = func(varname string) string {
+ x.decVar(varname, telem, false)
+ return ""
+ }
+ funcs["decLine"] = func(pfx string) string {
+ x.decVar(ts.TempVar+pfx+ts.Rand, reflect.PtrTo(telem), false)
+ return ""
+ }
+ funcs["var"] = func(s string) string {
+ return ts.TempVar + s + ts.Rand
+ }
+ funcs["zero"] = func() string {
+ return x.genZeroValueR(telem)
+ }
+ funcs["isArray"] = func() bool {
+ return t.Kind() == reflect.Array
+ }
+ funcs["isSlice"] = func() bool {
+ return t.Kind() == reflect.Slice
+ }
+ funcs["isChan"] = func() bool {
+ return t.Kind() == reflect.Chan
+ }
+ tm, err := template.New("").Funcs(funcs).Parse(genDecListTmpl)
+ if err != nil {
+ panic(err)
+ }
+ if err = tm.Execute(x.w, &ts); err != nil {
+ panic(err)
+ }
+}
+
+func (x *genRunner) decMapFallback(varname string, rtid uintptr, t reflect.Type) {
+ type tstruc struct {
+ TempVar string
+ Rand string
+ Varname string
+ KTyp string
+ Typ string
+ Size int
+ }
+ telem := t.Elem()
+ tkey := t.Key()
+ ts := tstruc{genTempVarPfx, x.varsfx(), varname, x.genTypeName(tkey), x.genTypeName(telem), int(telem.Size() + tkey.Size())}
+ funcs := make(template.FuncMap)
+ funcs["decLineVarK"] = func(varname string) string {
+ x.decVar(varname, tkey, false)
+ return ""
+ }
+ funcs["decLineVar"] = func(varname string) string {
+ x.decVar(varname, telem, false)
+ return ""
+ }
+ funcs["decLineK"] = func(pfx string) string {
+ x.decVar(ts.TempVar+pfx+ts.Rand, reflect.PtrTo(tkey), false)
+ return ""
+ }
+ funcs["decLine"] = func(pfx string) string {
+ x.decVar(ts.TempVar+pfx+ts.Rand, reflect.PtrTo(telem), false)
+ return ""
+ }
+ funcs["var"] = func(s string) string {
+ return ts.TempVar + s + ts.Rand
+ }
+
+ tm, err := template.New("").Funcs(funcs).Parse(genDecMapTmpl)
+ if err != nil {
+ panic(err)
+ }
+ if err = tm.Execute(x.w, &ts); err != nil {
+ panic(err)
+ }
+}
+
+func (x *genRunner) decStructMapSwitch(kName string, varname string, rtid uintptr, t reflect.Type) {
+ ti := getTypeInfo(rtid, t)
+ tisfi := ti.sfip // always use sequence from file. decStruct expects same thing.
+ x.line("switch (" + kName + ") {")
+ for _, si := range tisfi {
+ x.line("case \"" + si.encName + "\":")
+ var t2 reflect.StructField
+ if si.i != -1 {
+ t2 = t.Field(int(si.i))
+ } else {
+ // t2 = t.FieldByIndex(si.is)
+ t2typ := t
+ varname3 := varname
+ for _, ix := range si.is {
+ for t2typ.Kind() == reflect.Ptr {
+ t2typ = t2typ.Elem()
+ }
+ t2 = t2typ.Field(ix)
+ t2typ = t2.Type
+ varname3 = varname3 + "." + t2.Name
+ if t2typ.Kind() == reflect.Ptr {
+ x.line("if " + varname3 + " == nil {" +
+ varname3 + " = new(" + x.genTypeName(t2typ.Elem()) + ") }")
+ }
+ }
+ }
+ x.decVar(varname+"."+t2.Name, t2.Type, false)
+ }
+ x.line("default:")
+ // pass the slice here, so that the string will not escape, and maybe save allocation
+ x.line("z.DecStructFieldNotFound(-1, " + kName + ")")
+ // x.line("z.DecStructFieldNotFoundB(" + kName + "Slc)")
+ x.line("} // end switch " + kName)
+}
+
+func (x *genRunner) decStructMap(varname, lenvarname string, rtid uintptr, t reflect.Type, style uint8) {
+ tpfx := genTempVarPfx
+ i := x.varsfx()
+ kName := tpfx + "s" + i
+
+ // We thought to use ReadStringAsBytes, as go compiler might optimize the copy out.
+ // However, using that was more expensive, as it seems that the switch expression
+ // is evaluated each time.
+ //
+ // We could depend on decodeString using a temporary/shared buffer internally.
+ // However, this model of creating a byte array, and using explicitly is faster,
+ // and allows optional use of unsafe []byte->string conversion without alloc.
+
+ // Also, ensure that the slice array doesn't escape.
+ // That will help escape analysis prevent allocation when it gets better.
+
+ // x.line("var " + kName + "Arr = [32]byte{} // default string to decode into")
+ // x.line("var " + kName + "Slc = " + kName + "Arr[:] // default slice to decode into")
+ // use the scratch buffer to avoid allocation (most field names are < 32).
+
+ x.line("var " + kName + "Slc = z.DecScratchBuffer() // default slice to decode into")
+
+ // x.line("var " + kName + " string // default string to decode into")
+ // x.line("_ = " + kName)
+ x.line("_ = " + kName + "Slc")
+ // x.linef("var %sb%s bool", tpfx, i) // break
+ switch style {
+ case 1:
+ x.linef("for %sj%s := 0; %sj%s < %s; %sj%s++ {", tpfx, i, tpfx, i, lenvarname, tpfx, i)
+ case 2:
+ x.linef("for %sj%s := 0; !r.CheckBreak(); %sj%s++ {", tpfx, i, tpfx, i)
+ default: // 0, otherwise.
+ x.linef("var %shl%s bool = %s >= 0", tpfx, i, lenvarname) // has length
+ x.linef("for %sj%s := 0; ; %sj%s++ {", tpfx, i, tpfx, i)
+ x.linef("if %shl%s { if %sj%s >= %s { break }", tpfx, i, tpfx, i, lenvarname)
+ x.line("} else { if r.CheckBreak() { break }; }")
+ }
+ // x.line(kName + " = z.ReadStringAsBytes(" + kName + ")")
+ // x.line(kName + " = z.ReadString()")
+ x.line(kName + "Slc = r.DecodeBytes(" + kName + "Slc, true, true)")
+ // let string be scoped to this loop alone, so it doesn't escape.
+ // x.line(kName + " := " + x.cpfx + "GenBytesToStringRO(" + kName + "Slc)")
+ if x.unsafe {
+ x.line(kName + "SlcHdr := codecSelferUnsafeString" + x.xs + "{uintptr(unsafe.Pointer(&" +
+ kName + "Slc[0])), len(" + kName + "Slc)}")
+ x.line(kName + " := *(*string)(unsafe.Pointer(&" + kName + "SlcHdr))")
+ } else {
+ x.line(kName + " := string(" + kName + "Slc)")
+ }
+ x.decStructMapSwitch(kName, varname, rtid, t)
+
+ x.line("} // end for " + tpfx + "j" + i)
+ switch style {
+ case 1:
+ case 2:
+ x.line("r.ReadEnd()")
+ default:
+ x.linef("if !%shl%s { r.ReadEnd() }", tpfx, i)
+ }
+}
+
+func (x *genRunner) decStructArray(varname, lenvarname, breakString string, rtid uintptr, t reflect.Type) {
+ tpfx := genTempVarPfx
+ i := x.varsfx()
+ ti := getTypeInfo(rtid, t)
+ tisfi := ti.sfip // always use sequence from file. decStruct expects same thing.
+ x.linef("var %sj%s int", tpfx, i)
+ x.linef("var %sb%s bool", tpfx, i) // break
+ // x.linef("var %sl%s := r.ReadArrayStart()", tpfx, i)
+ x.linef("var %shl%s bool = %s >= 0", tpfx, i, lenvarname) // has length
+ for _, si := range tisfi {
+ var t2 reflect.StructField
+ if si.i != -1 {
+ t2 = t.Field(int(si.i))
+ } else {
+ t2 = t.FieldByIndex(si.is)
+ }
+
+ x.linef("%sj%s++; if %shl%s { %sb%s = %sj%s > %s } else { %sb%s = r.CheckBreak() }",
+ tpfx, i, tpfx, i, tpfx, i,
+ tpfx, i, lenvarname, tpfx, i)
+ // x.line("if " + tpfx + "j" + i + "++; " + tpfx + "j" +
+ // i + " <= " + tpfx + "l" + i + " {")
+ x.linef("if %sb%s { r.ReadEnd(); %s }", tpfx, i, breakString)
+ x.decVar(varname+"."+t2.Name, t2.Type, true)
+ // x.line("} // end if " + tpfx + "j" + i + " <= " + tpfx + "l" + i)
+ }
+ // read remaining values and throw away.
+ x.line("for {")
+ x.linef("%sj%s++; if %shl%s { %sb%s = %sj%s > %s } else { %sb%s = r.CheckBreak() }",
+ tpfx, i, tpfx, i, tpfx, i,
+ tpfx, i, lenvarname, tpfx, i)
+ x.linef("if %sb%s { break }", tpfx, i)
+ x.linef(`z.DecStructFieldNotFound(%sj%s - 1, "")`, tpfx, i)
+ x.line("}")
+ x.line("r.ReadEnd()")
+}
+
+func (x *genRunner) decStruct(varname string, rtid uintptr, t reflect.Type) {
+ // if container is map
+ // x.line("if z.DecContainerIsMap() { ")
+ i := x.varsfx()
+ x.line("if r.IsContainerType(codecSelferValueTypeMap" + x.xs + ") {")
+ x.line(genTempVarPfx + "l" + i + " := r.ReadMapStart()")
+ x.linef("if %sl%s == 0 {", genTempVarPfx, i)
+ x.line("r.ReadEnd()")
+ if genUseOneFunctionForDecStructMap {
+ x.line("} else { ")
+ x.linef("x.codecDecodeSelfFromMap(%sl%s, d)", genTempVarPfx, i)
+ } else {
+ x.line("} else if " + genTempVarPfx + "l" + i + " > 0 { ")
+ x.line("x.codecDecodeSelfFromMapLenPrefix(" + genTempVarPfx + "l" + i + ", d)")
+ x.line("} else {")
+ x.line("x.codecDecodeSelfFromMapCheckBreak(" + genTempVarPfx + "l" + i + ", d)")
+ }
+ x.line("}")
+
+ // else if container is array
+ // x.line("} else if z.DecContainerIsArray() { ")
+ x.line("} else if r.IsContainerType(codecSelferValueTypeArray" + x.xs + ") {")
+ x.line(genTempVarPfx + "l" + i + " := r.ReadArrayStart()")
+ x.linef("if %sl%s == 0 {", genTempVarPfx, i)
+ x.line("r.ReadEnd()")
+ x.line("} else { ")
+ x.linef("x.codecDecodeSelfFromArray(%sl%s, d)", genTempVarPfx, i)
+ x.line("}")
+ // else panic
+ x.line("} else { ")
+ x.line("panic(codecSelferOnlyMapOrArrayEncodeToStructErr" + x.xs + ")")
+ // x.line("panic(`only encoded map or array can be decoded into a struct`)")
+ x.line("} ")
+}
+
+// --------
+
+type genV struct {
+ // genV is either a primitive (Primitive != "") or a map (MapKey != "") or a slice
+ MapKey string
+ Elem string
+ Primitive string
+ Size int
+}
+
+func (x *genRunner) newGenV(t reflect.Type) (v genV) {
+ switch t.Kind() {
+ case reflect.Slice, reflect.Array:
+ te := t.Elem()
+ v.Elem = x.genTypeName(te)
+ v.Size = int(te.Size())
+ case reflect.Map:
+ te, tk := t.Elem(), t.Key()
+ v.Elem = x.genTypeName(te)
+ v.MapKey = x.genTypeName(tk)
+ v.Size = int(te.Size() + tk.Size())
+ default:
+ panic("unexpected type for newGenV. Requires map or slice type")
+ }
+ return
+}
+
+func (x *genV) MethodNamePfx(prefix string, prim bool) string {
+ var name []byte
+ if prefix != "" {
+ name = append(name, prefix...)
+ }
+ if prim {
+ name = append(name, genTitleCaseName(x.Primitive)...)
+ } else {
+ if x.MapKey == "" {
+ name = append(name, "Slice"...)
+ } else {
+ name = append(name, "Map"...)
+ name = append(name, genTitleCaseName(x.MapKey)...)
+ }
+ name = append(name, genTitleCaseName(x.Elem)...)
+ }
+ return string(name)
+
+}
+
+var genCheckVendor = os.Getenv("GO15VENDOREXPERIMENT") == "1"
+
+// genImportPath returns import path of a non-predeclared named typed, or an empty string otherwise.
+//
+// This handles the misbehaviour that occurs when 1.5-style vendoring is enabled,
+// where PkgPath returns the full path, including the vendoring pre-fix that should have been stripped.
+// We strip it here.
+func genImportPath(t reflect.Type) (s string) {
+ s = t.PkgPath()
+ if genCheckVendor {
+ // HACK: Misbehaviour occurs in go 1.5. May have to re-visit this later.
+ // if s contains /vendor/ OR startsWith vendor/, then return everything after it.
+ const vendorStart = "vendor/"
+ const vendorInline = "/vendor/"
+ if i := strings.LastIndex(s, vendorInline); i >= 0 {
+ s = s[i+len(vendorInline):]
+ } else if strings.HasPrefix(s, vendorStart) {
+ s = s[len(vendorStart):]
+ }
+ }
+ return
+}
+
+func genNonPtr(t reflect.Type) reflect.Type {
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+ return t
+}
+
+func genTitleCaseName(s string) string {
+ switch s {
+ case "interface{}":
+ return "Intf"
+ default:
+ return strings.ToUpper(s[0:1]) + s[1:]
+ }
+}
+
+func genMethodNameT(t reflect.Type, tRef reflect.Type) (n string) {
+ var ptrPfx string
+ for t.Kind() == reflect.Ptr {
+ ptrPfx += "Ptrto"
+ t = t.Elem()
+ }
+ tstr := t.String()
+ if tn := t.Name(); tn != "" {
+ if tRef != nil && genImportPath(t) == genImportPath(tRef) {
+ return ptrPfx + tn
+ } else {
+ if genQNameRegex.MatchString(tstr) {
+ return ptrPfx + strings.Replace(tstr, ".", "_", 1000)
+ } else {
+ return ptrPfx + genCustomTypeName(tstr)
+ }
+ }
+ }
+ switch t.Kind() {
+ case reflect.Map:
+ return ptrPfx + "Map" + genMethodNameT(t.Key(), tRef) + genMethodNameT(t.Elem(), tRef)
+ case reflect.Slice:
+ return ptrPfx + "Slice" + genMethodNameT(t.Elem(), tRef)
+ case reflect.Array:
+ return ptrPfx + "Array" + strconv.FormatInt(int64(t.Len()), 10) + genMethodNameT(t.Elem(), tRef)
+ case reflect.Chan:
+ var cx string
+ switch t.ChanDir() {
+ case reflect.SendDir:
+ cx = "ChanSend"
+ case reflect.RecvDir:
+ cx = "ChanRecv"
+ default:
+ cx = "Chan"
+ }
+ return ptrPfx + cx + genMethodNameT(t.Elem(), tRef)
+ default:
+ if t == intfTyp {
+ return ptrPfx + "Interface"
+ } else {
+ if tRef != nil && genImportPath(t) == genImportPath(tRef) {
+ if t.Name() != "" {
+ return ptrPfx + t.Name()
+ } else {
+ return ptrPfx + genCustomTypeName(tstr)
+ }
+ } else {
+ // best way to get the package name inclusive
+ // return ptrPfx + strings.Replace(tstr, ".", "_", 1000)
+ // return ptrPfx + genBase64enc.EncodeToString([]byte(tstr))
+ if t.Name() != "" && genQNameRegex.MatchString(tstr) {
+ return ptrPfx + strings.Replace(tstr, ".", "_", 1000)
+ } else {
+ return ptrPfx + genCustomTypeName(tstr)
+ }
+ }
+ }
+ }
+}
+
+// genCustomNameForType base64encodes the t.String() value in such a way
+// that it can be used within a function name.
+func genCustomTypeName(tstr string) string {
+ len2 := genBase64enc.EncodedLen(len(tstr))
+ bufx := make([]byte, len2)
+ genBase64enc.Encode(bufx, []byte(tstr))
+ for i := len2 - 1; i >= 0; i-- {
+ if bufx[i] == '=' {
+ len2--
+ } else {
+ break
+ }
+ }
+ return string(bufx[:len2])
+}
+
+func genIsImmutable(t reflect.Type) (v bool) {
+ return isImmutableKind(t.Kind())
+}
+
+type genInternal struct {
+ Values []genV
+ Unsafe bool
+}
+
+func (x genInternal) FastpathLen() (l int) {
+ for _, v := range x.Values {
+ if v.Primitive == "" {
+ l++
+ }
+ }
+ return
+}
+
+func genInternalZeroValue(s string) string {
+ switch s {
+ case "interface{}":
+ return "nil"
+ case "bool":
+ return "false"
+ case "string":
+ return `""`
+ default:
+ return "0"
+ }
+}
+
+func genInternalEncCommandAsString(s string, vname string) string {
+ switch s {
+ case "uint", "uint8", "uint16", "uint32", "uint64":
+ return "ee.EncodeUint(uint64(" + vname + "))"
+ case "int", "int8", "int16", "int32", "int64":
+ return "ee.EncodeInt(int64(" + vname + "))"
+ case "string":
+ return "ee.EncodeString(c_UTF8, " + vname + ")"
+ case "float32":
+ return "ee.EncodeFloat32(" + vname + ")"
+ case "float64":
+ return "ee.EncodeFloat64(" + vname + ")"
+ case "bool":
+ return "ee.EncodeBool(" + vname + ")"
+ case "symbol":
+ return "ee.EncodeSymbol(" + vname + ")"
+ default:
+ return "e.encode(" + vname + ")"
+ }
+}
+
+func genInternalDecCommandAsString(s string) string {
+ switch s {
+ case "uint":
+ return "uint(dd.DecodeUint(uintBitsize))"
+ case "uint8":
+ return "uint8(dd.DecodeUint(8))"
+ case "uint16":
+ return "uint16(dd.DecodeUint(16))"
+ case "uint32":
+ return "uint32(dd.DecodeUint(32))"
+ case "uint64":
+ return "dd.DecodeUint(64)"
+ case "int":
+ return "int(dd.DecodeInt(intBitsize))"
+ case "int8":
+ return "int8(dd.DecodeInt(8))"
+ case "int16":
+ return "int16(dd.DecodeInt(16))"
+ case "int32":
+ return "int32(dd.DecodeInt(32))"
+ case "int64":
+ return "dd.DecodeInt(64)"
+
+ case "string":
+ return "dd.DecodeString()"
+ case "float32":
+ return "float32(dd.DecodeFloat(true))"
+ case "float64":
+ return "dd.DecodeFloat(false)"
+ case "bool":
+ return "dd.DecodeBool()"
+ default:
+ panic(errors.New("unknown type for decode: " + s))
+ }
+
+}
+
+// var genInternalMu sync.Mutex
+var genInternalV genInternal
+var genInternalTmplFuncs template.FuncMap
+var genInternalOnce sync.Once
+
+func genInternalInit() {
+ types := [...]string{
+ "interface{}",
+ "string",
+ "float32",
+ "float64",
+ "uint",
+ "uint8",
+ "uint16",
+ "uint32",
+ "uint64",
+ "int",
+ "int8",
+ "int16",
+ "int32",
+ "int64",
+ "bool",
+ }
+ // keep as slice, so it is in specific iteration order.
+ // Initial order was uint64, string, interface{}, int, int64
+ mapvaltypes := [...]string{
+ "interface{}",
+ "string",
+ "uint",
+ "uint8",
+ "uint16",
+ "uint32",
+ "uint64",
+ "int",
+ "int8",
+ "int16",
+ "int32",
+ "int64",
+ "float32",
+ "float64",
+ "bool",
+ }
+ wordSizeBytes := int(intBitsize) / 8
+
+ mapvaltypes2 := map[string]int{
+ "interface{}": 2 * wordSizeBytes,
+ "string": 2 * wordSizeBytes,
+ "uint": 1 * wordSizeBytes,
+ "uint8": 1,
+ "uint16": 2,
+ "uint32": 4,
+ "uint64": 8,
+ "int": 1 * wordSizeBytes,
+ "int8": 1,
+ "int16": 2,
+ "int32": 4,
+ "int64": 8,
+ "float32": 4,
+ "float64": 8,
+ "bool": 1,
+ }
+ // mapvaltypes2 := make(map[string]bool)
+ // for _, s := range mapvaltypes {
+ // mapvaltypes2[s] = true
+ // }
+ var gt genInternal
+
+ // For each slice or map type, there must be a (symetrical) Encode and Decode fast-path function
+ for _, s := range types {
+ gt.Values = append(gt.Values, genV{Primitive: s, Size: mapvaltypes2[s]})
+ if s != "uint8" { // do not generate fast path for slice of bytes. Treat specially already.
+ gt.Values = append(gt.Values, genV{Elem: s, Size: mapvaltypes2[s]})
+ }
+ if _, ok := mapvaltypes2[s]; !ok {
+ gt.Values = append(gt.Values, genV{MapKey: s, Elem: s, Size: 2 * mapvaltypes2[s]})
+ }
+ for _, ms := range mapvaltypes {
+ gt.Values = append(gt.Values, genV{MapKey: s, Elem: ms, Size: mapvaltypes2[s] + mapvaltypes2[ms]})
+ }
+ }
+
+ funcs := make(template.FuncMap)
+ // funcs["haspfx"] = strings.HasPrefix
+ funcs["encmd"] = genInternalEncCommandAsString
+ funcs["decmd"] = genInternalDecCommandAsString
+ funcs["zerocmd"] = genInternalZeroValue
+
+ genInternalV = gt
+ genInternalTmplFuncs = funcs
+}
+
+// GenInternalGoFile is used to generate source files from templates.
+// It is run by the program author alone.
+// Unfortunately, it has to be exported so that it can be called from a command line tool.
+// *** DO NOT USE ***
+func GenInternalGoFile(r io.Reader, w io.Writer, safe bool) (err error) {
+ genInternalOnce.Do(genInternalInit)
+
+ gt := genInternalV
+ gt.Unsafe = !safe
+
+ t := template.New("").Funcs(genInternalTmplFuncs)
+
+ tmplstr, err := ioutil.ReadAll(r)
+ if err != nil {
+ return
+ }
+
+ if t, err = t.Parse(string(tmplstr)); err != nil {
+ return
+ }
+
+ var out bytes.Buffer
+ err = t.Execute(&out, gt)
+ if err != nil {
+ return
+ }
+
+ bout, err := format.Source(out.Bytes())
+ if err != nil {
+ w.Write(out.Bytes()) // write out if error, so we can still see.
+ // w.Write(bout) // write out if error, as much as possible, so we can still see.
+ return
+ }
+ w.Write(bout)
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go
new file mode 100644
index 000000000..0b3e86e28
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper.go
@@ -0,0 +1,946 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// Contains code shared by both encode and decode.
+
+// Some shared ideas around encoding/decoding
+// ------------------------------------------
+//
+// If an interface{} is passed, we first do a type assertion to see if it is
+// a primitive type or a map/slice of primitive types, and use a fastpath to handle it.
+//
+// If we start with a reflect.Value, we are already in reflect.Value land and
+// will try to grab the function for the underlying Type and directly call that function.
+// This is more performant than calling reflect.Value.Interface().
+//
+// This still helps us bypass many layers of reflection, and give best performance.
+//
+// Containers
+// ------------
+// Containers in the stream are either associative arrays (key-value pairs) or
+// regular arrays (indexed by incrementing integers).
+//
+// Some streams support indefinite-length containers, and use a breaking
+// byte-sequence to denote that the container has come to an end.
+//
+// Some streams also are text-based, and use explicit separators to denote the
+// end/beginning of different values.
+//
+// During encode, we use a high-level condition to determine how to iterate through
+// the container. That decision is based on whether the container is text-based (with
+// separators) or binary (without separators). If binary, we do not even call the
+// encoding of separators.
+//
+// During decode, we use a different high-level condition to determine how to iterate
+// through the containers. That decision is based on whether the stream contained
+// a length prefix, or if it used explicit breaks. If length-prefixed, we assume that
+// it has to be binary, and we do not even try to read separators.
+//
+// The only codec that may suffer (slightly) is cbor, and only when decoding indefinite-length.
+// It may suffer because we treat it like a text-based codec, and read separators.
+// However, this read is a no-op and the cost is insignificant.
+//
+// Philosophy
+// ------------
+// On decode, this codec will update containers appropriately:
+// - If struct, update fields from stream into fields of struct.
+// If field in stream not found in struct, handle appropriately (based on option).
+// If a struct field has no corresponding value in the stream, leave it AS IS.
+// If nil in stream, set value to nil/zero value.
+// - If map, update map from stream.
+// If the stream value is NIL, set the map to nil.
+// - if slice, try to update up to length of array in stream.
+// if container len is less than stream array length,
+// and container cannot be expanded, handled (based on option).
+// This means you can decode 4-element stream array into 1-element array.
+//
+// ------------------------------------
+// On encode, user can specify omitEmpty. This means that the value will be omitted
+// if the zero value. The problem may occur during decode, where omitted values do not affect
+// the value being decoded into. This means that if decoding into a struct with an
+// int field with current value=5, and the field is omitted in the stream, then after
+// decoding, the value will still be 5 (not 0).
+// omitEmpty only works if you guarantee that you always decode into zero-values.
+//
+// ------------------------------------
+// We could have truncated a map to remove keys not available in the stream,
+// or set values in the struct which are not in the stream to their zero values.
+// We decided against it because there is no efficient way to do it.
+// We may introduce it as an option later.
+// However, that will require enabling it for both runtime and code generation modes.
+//
+// To support truncate, we need to do 2 passes over the container:
+// map
+// - first collect all keys (e.g. in k1)
+// - for each key in stream, mark k1 that the key should not be removed
+// - after updating map, do second pass and call delete for all keys in k1 which are not marked
+// struct:
+// - for each field, track the *typeInfo s1
+// - iterate through all s1, and for each one not marked, set value to zero
+// - this involves checking the possible anonymous fields which are nil ptrs.
+// too much work.
+//
+// ------------------------------------------
+// Error Handling is done within the library using panic.
+//
+// This way, the code doesn't have to keep checking if an error has happened,
+// and we don't have to keep sending the error value along with each call
+// or storing it in the En|Decoder and checking it constantly along the way.
+//
+// The disadvantage is that small functions which use panics cannot be inlined.
+// The code accounts for that by only using panics behind an interface;
+// since interface calls cannot be inlined, this is irrelevant.
+//
+// We considered storing the error is En|Decoder.
+// - once it has its err field set, it cannot be used again.
+// - panicing will be optional, controlled by const flag.
+// - code should always check error first and return early.
+// We eventually decided against it as it makes the code clumsier to always
+// check for these error conditions.
+
+import (
+ "encoding"
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+ "sort"
+ "strings"
+ "sync"
+ "time"
+ "unicode"
+ "unicode/utf8"
+)
+
+const (
+ scratchByteArrayLen = 32
+ initCollectionCap = 32 // 32 is defensive. 16 is preferred.
+
+ // Support encoding.(Binary|Text)(Unm|M)arshaler.
+ // This constant flag will enable or disable it.
+ supportMarshalInterfaces = true
+
+ // Each Encoder or Decoder uses a cache of functions based on conditionals,
+ // so that the conditionals are not run every time.
+ //
+ // Either a map or a slice is used to keep track of the functions.
+ // The map is more natural, but has a higher cost than a slice/array.
+ // This flag (useMapForCodecCache) controls which is used.
+ //
+ // From benchmarks, slices with linear search perform better with < 32 entries.
+ // We have typically seen a high threshold of about 24 entries.
+ useMapForCodecCache = false
+
+ // for debugging, set this to false, to catch panic traces.
+ // Note that this will always cause rpc tests to fail, since they need io.EOF sent via panic.
+ recoverPanicToErr = true
+
+ // Fast path functions try to create a fast path encode or decode implementation
+ // for common maps and slices, by by-passing reflection altogether.
+ fastpathEnabled = true
+
+ // if checkStructForEmptyValue, check structs fields to see if an empty value.
+ // This could be an expensive call, so possibly disable it.
+ checkStructForEmptyValue = false
+
+ // if derefForIsEmptyValue, deref pointers and interfaces when checking isEmptyValue
+ derefForIsEmptyValue = false
+
+ // if resetSliceElemToZeroValue, then on decoding a slice, reset the element to a zero value first.
+ // Only concern is that, if the slice already contained some garbage, we will decode into that garbage.
+ // The chances of this are slim, so leave this "optimization".
+ // TODO: should this be true, to ensure that we always decode into a "zero" "empty" value?
+ resetSliceElemToZeroValue bool = false
+)
+
+var oneByteArr = [1]byte{0}
+var zeroByteSlice = oneByteArr[:0:0]
+
+type charEncoding uint8
+
+const (
+ c_RAW charEncoding = iota
+ c_UTF8
+ c_UTF16LE
+ c_UTF16BE
+ c_UTF32LE
+ c_UTF32BE
+)
+
+// valueType is the stream type
+type valueType uint8
+
+const (
+ valueTypeUnset valueType = iota
+ valueTypeNil
+ valueTypeInt
+ valueTypeUint
+ valueTypeFloat
+ valueTypeBool
+ valueTypeString
+ valueTypeSymbol
+ valueTypeBytes
+ valueTypeMap
+ valueTypeArray
+ valueTypeTimestamp
+ valueTypeExt
+
+ // valueTypeInvalid = 0xff
+)
+
+type seqType uint8
+
+// mirror json.Marshaler and json.Unmarshaler here, so we don't import the encoding/json package
+type jsonMarshaler interface {
+ MarshalJSON() ([]byte, error)
+}
+type jsonUnmarshaler interface {
+ UnmarshalJSON([]byte) error
+}
+
+const (
+ _ seqType = iota
+ seqTypeArray
+ seqTypeSlice
+ seqTypeChan
+)
+
+var (
+ bigen = binary.BigEndian
+ structInfoFieldName = "_struct"
+
+ cachedTypeInfo = make(map[uintptr]*typeInfo, 64)
+ cachedTypeInfoMutex sync.RWMutex
+
+ // mapStrIntfTyp = reflect.TypeOf(map[string]interface{}(nil))
+ intfSliceTyp = reflect.TypeOf([]interface{}(nil))
+ intfTyp = intfSliceTyp.Elem()
+
+ stringTyp = reflect.TypeOf("")
+ timeTyp = reflect.TypeOf(time.Time{})
+ rawExtTyp = reflect.TypeOf(RawExt{})
+ uint8SliceTyp = reflect.TypeOf([]uint8(nil))
+
+ mapBySliceTyp = reflect.TypeOf((*MapBySlice)(nil)).Elem()
+
+ binaryMarshalerTyp = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
+ binaryUnmarshalerTyp = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
+
+ textMarshalerTyp = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
+ textUnmarshalerTyp = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
+
+ jsonMarshalerTyp = reflect.TypeOf((*jsonMarshaler)(nil)).Elem()
+ jsonUnmarshalerTyp = reflect.TypeOf((*jsonUnmarshaler)(nil)).Elem()
+
+ selferTyp = reflect.TypeOf((*Selfer)(nil)).Elem()
+
+ uint8SliceTypId = reflect.ValueOf(uint8SliceTyp).Pointer()
+ rawExtTypId = reflect.ValueOf(rawExtTyp).Pointer()
+ intfTypId = reflect.ValueOf(intfTyp).Pointer()
+ timeTypId = reflect.ValueOf(timeTyp).Pointer()
+ stringTypId = reflect.ValueOf(stringTyp).Pointer()
+
+ // mapBySliceTypId = reflect.ValueOf(mapBySliceTyp).Pointer()
+
+ intBitsize uint8 = uint8(reflect.TypeOf(int(0)).Bits())
+ uintBitsize uint8 = uint8(reflect.TypeOf(uint(0)).Bits())
+
+ bsAll0x00 = []byte{0, 0, 0, 0, 0, 0, 0, 0}
+ bsAll0xff = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+
+ chkOvf checkOverflow
+
+ noFieldNameToStructFieldInfoErr = errors.New("no field name passed to parseStructFieldInfo")
+)
+
+// Selfer defines methods by which a value can encode or decode itself.
+//
+// Any type which implements Selfer will be able to encode or decode itself.
+// Consequently, during (en|de)code, this takes precedence over
+// (text|binary)(M|Unm)arshal or extension support.
+type Selfer interface {
+ CodecEncodeSelf(*Encoder)
+ CodecDecodeSelf(*Decoder)
+}
+
+// MapBySlice represents a slice which should be encoded as a map in the stream.
+// The slice contains a sequence of key-value pairs.
+// This affords storing a map in a specific sequence in the stream.
+//
+// The support of MapBySlice affords the following:
+// - A slice type which implements MapBySlice will be encoded as a map
+// - A slice can be decoded from a map in the stream
+type MapBySlice interface {
+ MapBySlice()
+}
+
+// WARNING: DO NOT USE DIRECTLY. EXPORTED FOR GODOC BENEFIT. WILL BE REMOVED.
+//
+// BasicHandle encapsulates the common options and extension functions.
+type BasicHandle struct {
+ extHandle
+ EncodeOptions
+ DecodeOptions
+}
+
+func (x *BasicHandle) getBasicHandle() *BasicHandle {
+ return x
+}
+
+// Handle is the interface for a specific encoding format.
+//
+// Typically, a Handle is pre-configured before first time use,
+// and not modified while in use. Such a pre-configured Handle
+// is safe for concurrent access.
+type Handle interface {
+ getBasicHandle() *BasicHandle
+ newEncDriver(w *Encoder) encDriver
+ newDecDriver(r *Decoder) decDriver
+ isBinary() bool
+}
+
+// RawExt represents raw unprocessed extension data.
+// Some codecs will decode extension data as a *RawExt if there is no registered extension for the tag.
+//
+// Only one of Data or Value is nil. If Data is nil, then the content of the RawExt is in the Value.
+type RawExt struct {
+ Tag uint64
+ // Data is the []byte which represents the raw ext. If Data is nil, ext is exposed in Value.
+ // Data is used by codecs (e.g. binc, msgpack, simple) which do custom serialization of the types
+ Data []byte
+ // Value represents the extension, if Data is nil.
+ // Value is used by codecs (e.g. cbor) which use the format to do custom serialization of the types.
+ Value interface{}
+}
+
+// BytesExt handles custom (de)serialization of types to/from []byte.
+// It is used by codecs (e.g. binc, msgpack, simple) which do custom serialization of the types.
+type BytesExt interface {
+ // WriteExt converts a value to a []byte.
+ WriteExt(v interface{}) []byte
+
+ // ReadExt updates a value from a []byte.
+ ReadExt(dst interface{}, src []byte)
+}
+
+// InterfaceExt handles custom (de)serialization of types to/from another interface{} value.
+// The Encoder or Decoder will then handle the further (de)serialization of that known type.
+//
+// It is used by codecs (e.g. cbor, json) which use the format to do custom serialization of the types.
+type InterfaceExt interface {
+ // ConvertExt converts a value into a simpler interface for easy encoding e.g. convert time.Time to int64.
+ ConvertExt(v interface{}) interface{}
+
+ // UpdateExt updates a value from a simpler interface for easy decoding e.g. convert int64 to time.Time.
+ UpdateExt(dst interface{}, src interface{})
+}
+
+// Ext handles custom (de)serialization of custom types / extensions.
+type Ext interface {
+ BytesExt
+ InterfaceExt
+}
+
+// addExtWrapper is a wrapper implementation to support former AddExt exported method.
+type addExtWrapper struct {
+ encFn func(reflect.Value) ([]byte, error)
+ decFn func(reflect.Value, []byte) error
+}
+
+func (x addExtWrapper) WriteExt(v interface{}) []byte {
+ // fmt.Printf(">>>>>>>>>> WriteExt: %T, %v\n", v, v)
+ bs, err := x.encFn(reflect.ValueOf(v))
+ if err != nil {
+ panic(err)
+ }
+ return bs
+}
+
+func (x addExtWrapper) ReadExt(v interface{}, bs []byte) {
+ // fmt.Printf(">>>>>>>>>> ReadExt: %T, %v\n", v, v)
+ if err := x.decFn(reflect.ValueOf(v), bs); err != nil {
+ panic(err)
+ }
+}
+
+func (x addExtWrapper) ConvertExt(v interface{}) interface{} {
+ return x.WriteExt(v)
+}
+
+func (x addExtWrapper) UpdateExt(dest interface{}, v interface{}) {
+ x.ReadExt(dest, v.([]byte))
+}
+
+type setExtWrapper struct {
+ b BytesExt
+ i InterfaceExt
+}
+
+func (x *setExtWrapper) WriteExt(v interface{}) []byte {
+ if x.b == nil {
+ panic("BytesExt.WriteExt is not supported")
+ }
+ return x.b.WriteExt(v)
+}
+
+func (x *setExtWrapper) ReadExt(v interface{}, bs []byte) {
+ if x.b == nil {
+ panic("BytesExt.WriteExt is not supported")
+
+ }
+ x.b.ReadExt(v, bs)
+}
+
+func (x *setExtWrapper) ConvertExt(v interface{}) interface{} {
+ if x.i == nil {
+ panic("InterfaceExt.ConvertExt is not supported")
+
+ }
+ return x.i.ConvertExt(v)
+}
+
+func (x *setExtWrapper) UpdateExt(dest interface{}, v interface{}) {
+ if x.i == nil {
+ panic("InterfaceExxt.UpdateExt is not supported")
+
+ }
+ x.i.UpdateExt(dest, v)
+}
+
+// type errorString string
+// func (x errorString) Error() string { return string(x) }
+
+type binaryEncodingType struct{}
+
+func (_ binaryEncodingType) isBinary() bool { return true }
+
+type textEncodingType struct{}
+
+func (_ textEncodingType) isBinary() bool { return false }
+
+// noBuiltInTypes is embedded into many types which do not support builtins
+// e.g. msgpack, simple, cbor.
+type noBuiltInTypes struct{}
+
+func (_ noBuiltInTypes) IsBuiltinType(rt uintptr) bool { return false }
+func (_ noBuiltInTypes) EncodeBuiltin(rt uintptr, v interface{}) {}
+func (_ noBuiltInTypes) DecodeBuiltin(rt uintptr, v interface{}) {}
+
+type noStreamingCodec struct{}
+
+func (_ noStreamingCodec) CheckBreak() bool { return false }
+
+// bigenHelper.
+// Users must already slice the x completely, because we will not reslice.
+type bigenHelper struct {
+ x []byte // must be correctly sliced to appropriate len. slicing is a cost.
+ w encWriter
+}
+
+func (z bigenHelper) writeUint16(v uint16) {
+ bigen.PutUint16(z.x, v)
+ z.w.writeb(z.x)
+}
+
+func (z bigenHelper) writeUint32(v uint32) {
+ bigen.PutUint32(z.x, v)
+ z.w.writeb(z.x)
+}
+
+func (z bigenHelper) writeUint64(v uint64) {
+ bigen.PutUint64(z.x, v)
+ z.w.writeb(z.x)
+}
+
+type extTypeTagFn struct {
+ rtid uintptr
+ rt reflect.Type
+ tag uint64
+ ext Ext
+}
+
+type extHandle []*extTypeTagFn
+
+// DEPRECATED: Use SetBytesExt or SetInterfaceExt on the Handle instead.
+//
+// AddExt registes an encode and decode function for a reflect.Type.
+// AddExt internally calls SetExt.
+// To deregister an Ext, call AddExt with nil encfn and/or nil decfn.
+func (o *extHandle) AddExt(
+ rt reflect.Type, tag byte,
+ encfn func(reflect.Value) ([]byte, error), decfn func(reflect.Value, []byte) error,
+) (err error) {
+ if encfn == nil || decfn == nil {
+ return o.SetExt(rt, uint64(tag), nil)
+ }
+ return o.SetExt(rt, uint64(tag), addExtWrapper{encfn, decfn})
+}
+
+// DEPRECATED: Use SetBytesExt or SetInterfaceExt on the Handle instead.
+//
+// Note that the type must be a named type, and specifically not
+// a pointer or Interface. An error is returned if that is not honored.
+//
+// To Deregister an ext, call SetExt with nil Ext
+func (o *extHandle) SetExt(rt reflect.Type, tag uint64, ext Ext) (err error) {
+ // o is a pointer, because we may need to initialize it
+ if rt.PkgPath() == "" || rt.Kind() == reflect.Interface {
+ err = fmt.Errorf("codec.Handle.AddExt: Takes named type, especially not a pointer or interface: %T",
+ reflect.Zero(rt).Interface())
+ return
+ }
+
+ rtid := reflect.ValueOf(rt).Pointer()
+ for _, v := range *o {
+ if v.rtid == rtid {
+ v.tag, v.ext = tag, ext
+ return
+ }
+ }
+
+ *o = append(*o, &extTypeTagFn{rtid, rt, tag, ext})
+ return
+}
+
+func (o extHandle) getExt(rtid uintptr) *extTypeTagFn {
+ for _, v := range o {
+ if v.rtid == rtid {
+ return v
+ }
+ }
+ return nil
+}
+
+func (o extHandle) getExtForTag(tag uint64) *extTypeTagFn {
+ for _, v := range o {
+ if v.tag == tag {
+ return v
+ }
+ }
+ return nil
+}
+
+type structFieldInfo struct {
+ encName string // encode name
+
+ // only one of 'i' or 'is' can be set. If 'i' is -1, then 'is' has been set.
+
+ is []int // (recursive/embedded) field index in struct
+ i int16 // field index in struct
+ omitEmpty bool
+ toArray bool // if field is _struct, is the toArray set?
+}
+
+// func (si *structFieldInfo) isZero() bool {
+// return si.encName == "" && len(si.is) == 0 && si.i == 0 && !si.omitEmpty && !si.toArray
+// }
+
+// rv returns the field of the struct.
+// If anonymous, it returns an Invalid
+func (si *structFieldInfo) field(v reflect.Value, update bool) (rv2 reflect.Value) {
+ if si.i != -1 {
+ v = v.Field(int(si.i))
+ return v
+ }
+ // replicate FieldByIndex
+ for _, x := range si.is {
+ for v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ if !update {
+ return
+ }
+ v.Set(reflect.New(v.Type().Elem()))
+ }
+ v = v.Elem()
+ }
+ v = v.Field(x)
+ }
+ return v
+}
+
+func (si *structFieldInfo) setToZeroValue(v reflect.Value) {
+ if si.i != -1 {
+ v = v.Field(int(si.i))
+ v.Set(reflect.Zero(v.Type()))
+ // v.Set(reflect.New(v.Type()).Elem())
+ // v.Set(reflect.New(v.Type()))
+ } else {
+ // replicate FieldByIndex
+ for _, x := range si.is {
+ for v.Kind() == reflect.Ptr {
+ if v.IsNil() {
+ return
+ }
+ v = v.Elem()
+ }
+ v = v.Field(x)
+ }
+ v.Set(reflect.Zero(v.Type()))
+ }
+}
+
+func parseStructFieldInfo(fname string, stag string) *structFieldInfo {
+ // if fname == "" {
+ // panic(noFieldNameToStructFieldInfoErr)
+ // }
+ si := structFieldInfo{
+ encName: fname,
+ }
+
+ if stag != "" {
+ for i, s := range strings.Split(stag, ",") {
+ if i == 0 {
+ if s != "" {
+ si.encName = s
+ }
+ } else {
+ if s == "omitempty" {
+ si.omitEmpty = true
+ } else if s == "toarray" {
+ si.toArray = true
+ }
+ }
+ }
+ }
+ // si.encNameBs = []byte(si.encName)
+ return &si
+}
+
+type sfiSortedByEncName []*structFieldInfo
+
+func (p sfiSortedByEncName) Len() int {
+ return len(p)
+}
+
+func (p sfiSortedByEncName) Less(i, j int) bool {
+ return p[i].encName < p[j].encName
+}
+
+func (p sfiSortedByEncName) Swap(i, j int) {
+ p[i], p[j] = p[j], p[i]
+}
+
+// typeInfo keeps information about each type referenced in the encode/decode sequence.
+//
+// During an encode/decode sequence, we work as below:
+// - If base is a built in type, en/decode base value
+// - If base is registered as an extension, en/decode base value
+// - If type is binary(M/Unm)arshaler, call Binary(M/Unm)arshal method
+// - If type is text(M/Unm)arshaler, call Text(M/Unm)arshal method
+// - Else decode appropriately based on the reflect.Kind
+type typeInfo struct {
+ sfi []*structFieldInfo // sorted. Used when enc/dec struct to map.
+ sfip []*structFieldInfo // unsorted. Used when enc/dec struct to array.
+
+ rt reflect.Type
+ rtid uintptr
+
+ // baseId gives pointer to the base reflect.Type, after deferencing
+ // the pointers. E.g. base type of ***time.Time is time.Time.
+ base reflect.Type
+ baseId uintptr
+ baseIndir int8 // number of indirections to get to base
+
+ mbs bool // base type (T or *T) is a MapBySlice
+
+ bm bool // base type (T or *T) is a binaryMarshaler
+ bunm bool // base type (T or *T) is a binaryUnmarshaler
+ bmIndir int8 // number of indirections to get to binaryMarshaler type
+ bunmIndir int8 // number of indirections to get to binaryUnmarshaler type
+
+ tm bool // base type (T or *T) is a textMarshaler
+ tunm bool // base type (T or *T) is a textUnmarshaler
+ tmIndir int8 // number of indirections to get to textMarshaler type
+ tunmIndir int8 // number of indirections to get to textUnmarshaler type
+
+ jm bool // base type (T or *T) is a jsonMarshaler
+ junm bool // base type (T or *T) is a jsonUnmarshaler
+ jmIndir int8 // number of indirections to get to jsonMarshaler type
+ junmIndir int8 // number of indirections to get to jsonUnmarshaler type
+
+ cs bool // base type (T or *T) is a Selfer
+ csIndir int8 // number of indirections to get to Selfer type
+
+ toArray bool // whether this (struct) type should be encoded as an array
+}
+
+func (ti *typeInfo) indexForEncName(name string) int {
+ //tisfi := ti.sfi
+ const binarySearchThreshold = 16
+ if sfilen := len(ti.sfi); sfilen < binarySearchThreshold {
+ // linear search. faster than binary search in my testing up to 16-field structs.
+ for i, si := range ti.sfi {
+ if si.encName == name {
+ return i
+ }
+ }
+ } else {
+ // binary search. adapted from sort/search.go.
+ h, i, j := 0, 0, sfilen
+ for i < j {
+ h = i + (j-i)/2
+ if ti.sfi[h].encName < name {
+ i = h + 1
+ } else {
+ j = h
+ }
+ }
+ if i < sfilen && ti.sfi[i].encName == name {
+ return i
+ }
+ }
+ return -1
+}
+
+func getStructTag(t reflect.StructTag) (s string) {
+ // check for tags: codec, json, in that order.
+ // this allows seamless support for many configured structs.
+ s = t.Get("codec")
+ if s == "" {
+ s = t.Get("json")
+ }
+ return
+}
+
+func getTypeInfo(rtid uintptr, rt reflect.Type) (pti *typeInfo) {
+ var ok bool
+ cachedTypeInfoMutex.RLock()
+ pti, ok = cachedTypeInfo[rtid]
+ cachedTypeInfoMutex.RUnlock()
+ if ok {
+ return
+ }
+
+ cachedTypeInfoMutex.Lock()
+ defer cachedTypeInfoMutex.Unlock()
+ if pti, ok = cachedTypeInfo[rtid]; ok {
+ return
+ }
+
+ ti := typeInfo{rt: rt, rtid: rtid}
+ pti = &ti
+
+ var indir int8
+ if ok, indir = implementsIntf(rt, binaryMarshalerTyp); ok {
+ ti.bm, ti.bmIndir = true, indir
+ }
+ if ok, indir = implementsIntf(rt, binaryUnmarshalerTyp); ok {
+ ti.bunm, ti.bunmIndir = true, indir
+ }
+ if ok, indir = implementsIntf(rt, textMarshalerTyp); ok {
+ ti.tm, ti.tmIndir = true, indir
+ }
+ if ok, indir = implementsIntf(rt, textUnmarshalerTyp); ok {
+ ti.tunm, ti.tunmIndir = true, indir
+ }
+ if ok, indir = implementsIntf(rt, jsonMarshalerTyp); ok {
+ ti.jm, ti.jmIndir = true, indir
+ }
+ if ok, indir = implementsIntf(rt, jsonUnmarshalerTyp); ok {
+ ti.junm, ti.junmIndir = true, indir
+ }
+ if ok, indir = implementsIntf(rt, selferTyp); ok {
+ ti.cs, ti.csIndir = true, indir
+ }
+ if ok, _ = implementsIntf(rt, mapBySliceTyp); ok {
+ ti.mbs = true
+ }
+
+ pt := rt
+ var ptIndir int8
+ // for ; pt.Kind() == reflect.Ptr; pt, ptIndir = pt.Elem(), ptIndir+1 { }
+ for pt.Kind() == reflect.Ptr {
+ pt = pt.Elem()
+ ptIndir++
+ }
+ if ptIndir == 0 {
+ ti.base = rt
+ ti.baseId = rtid
+ } else {
+ ti.base = pt
+ ti.baseId = reflect.ValueOf(pt).Pointer()
+ ti.baseIndir = ptIndir
+ }
+
+ if rt.Kind() == reflect.Struct {
+ var siInfo *structFieldInfo
+ if f, ok := rt.FieldByName(structInfoFieldName); ok {
+ siInfo = parseStructFieldInfo(structInfoFieldName, getStructTag(f.Tag))
+ ti.toArray = siInfo.toArray
+ }
+ sfip := make([]*structFieldInfo, 0, rt.NumField())
+ rgetTypeInfo(rt, nil, make(map[string]bool, 16), &sfip, siInfo)
+
+ ti.sfip = make([]*structFieldInfo, len(sfip))
+ ti.sfi = make([]*structFieldInfo, len(sfip))
+ copy(ti.sfip, sfip)
+ sort.Sort(sfiSortedByEncName(sfip))
+ copy(ti.sfi, sfip)
+ }
+ // sfi = sfip
+ cachedTypeInfo[rtid] = pti
+ return
+}
+
+func rgetTypeInfo(rt reflect.Type, indexstack []int, fnameToHastag map[string]bool,
+ sfi *[]*structFieldInfo, siInfo *structFieldInfo,
+) {
+ for j := 0; j < rt.NumField(); j++ {
+ f := rt.Field(j)
+ // func types are skipped.
+ if tk := f.Type.Kind(); tk == reflect.Func {
+ continue
+ }
+ stag := getStructTag(f.Tag)
+ if stag == "-" {
+ continue
+ }
+ if r1, _ := utf8.DecodeRuneInString(f.Name); r1 == utf8.RuneError || !unicode.IsUpper(r1) {
+ continue
+ }
+ var si *structFieldInfo
+ // if anonymous and there is no struct tag (or it's blank)
+ // and its a struct (or pointer to struct), inline it.
+ var doInline bool
+ if f.Anonymous && f.Type.Kind() != reflect.Interface {
+ doInline = stag == ""
+ if !doInline {
+ si = parseStructFieldInfo("", stag)
+ doInline = si.encName == ""
+ // doInline = si.isZero()
+ // fmt.Printf(">>>> doInline for si.isZero: %s: %v\n", f.Name, doInline)
+ }
+ }
+
+ if doInline {
+ ft := f.Type
+ for ft.Kind() == reflect.Ptr {
+ ft = ft.Elem()
+ }
+ if ft.Kind() == reflect.Struct {
+ indexstack2 := make([]int, len(indexstack)+1, len(indexstack)+4)
+ copy(indexstack2, indexstack)
+ indexstack2[len(indexstack)] = j
+ // indexstack2 := append(append(make([]int, 0, len(indexstack)+4), indexstack...), j)
+ rgetTypeInfo(ft, indexstack2, fnameToHastag, sfi, siInfo)
+ continue
+ }
+ }
+ // do not let fields with same name in embedded structs override field at higher level.
+ // this must be done after anonymous check, to allow anonymous field
+ // still include their child fields
+ if _, ok := fnameToHastag[f.Name]; ok {
+ continue
+ }
+ if f.Name == "" {
+ panic(noFieldNameToStructFieldInfoErr)
+ }
+ if si == nil {
+ si = parseStructFieldInfo(f.Name, stag)
+ } else if si.encName == "" {
+ si.encName = f.Name
+ }
+ // si.ikind = int(f.Type.Kind())
+ if len(indexstack) == 0 {
+ si.i = int16(j)
+ } else {
+ si.i = -1
+ si.is = append(append(make([]int, 0, len(indexstack)+4), indexstack...), j)
+ }
+
+ if siInfo != nil {
+ if siInfo.omitEmpty {
+ si.omitEmpty = true
+ }
+ }
+ *sfi = append(*sfi, si)
+ fnameToHastag[f.Name] = stag != ""
+ }
+}
+
+func panicToErr(err *error) {
+ if recoverPanicToErr {
+ if x := recover(); x != nil {
+ //debug.PrintStack()
+ panicValToErr(x, err)
+ }
+ }
+}
+
+// func doPanic(tag string, format string, params ...interface{}) {
+// params2 := make([]interface{}, len(params)+1)
+// params2[0] = tag
+// copy(params2[1:], params)
+// panic(fmt.Errorf("%s: "+format, params2...))
+// }
+
+func isImmutableKind(k reflect.Kind) (v bool) {
+ return false ||
+ k == reflect.Int ||
+ k == reflect.Int8 ||
+ k == reflect.Int16 ||
+ k == reflect.Int32 ||
+ k == reflect.Int64 ||
+ k == reflect.Uint ||
+ k == reflect.Uint8 ||
+ k == reflect.Uint16 ||
+ k == reflect.Uint32 ||
+ k == reflect.Uint64 ||
+ k == reflect.Uintptr ||
+ k == reflect.Float32 ||
+ k == reflect.Float64 ||
+ k == reflect.Bool ||
+ k == reflect.String
+}
+
+// these functions must be inlinable, and not call anybody
+type checkOverflow struct{}
+
+func (_ checkOverflow) Float32(f float64) (overflow bool) {
+ if f < 0 {
+ f = -f
+ }
+ return math.MaxFloat32 < f && f <= math.MaxFloat64
+}
+
+func (_ checkOverflow) Uint(v uint64, bitsize uint8) (overflow bool) {
+ if bitsize == 0 || bitsize >= 64 || v == 0 {
+ return
+ }
+ if trunc := (v << (64 - bitsize)) >> (64 - bitsize); v != trunc {
+ overflow = true
+ }
+ return
+}
+
+func (_ checkOverflow) Int(v int64, bitsize uint8) (overflow bool) {
+ if bitsize == 0 || bitsize >= 64 || v == 0 {
+ return
+ }
+ if trunc := (v << (64 - bitsize)) >> (64 - bitsize); v != trunc {
+ overflow = true
+ }
+ return
+}
+
+func (_ checkOverflow) SignedInt(v uint64) (i int64, overflow bool) {
+ //e.g. -127 to 128 for int8
+ pos := (v >> 63) == 0
+ ui2 := v & 0x7fffffffffffffff
+ if pos {
+ if ui2 > math.MaxInt64 {
+ overflow = true
+ return
+ }
+ } else {
+ if ui2 > math.MaxInt64-1 {
+ overflow = true
+ return
+ }
+ }
+ i = int64(v)
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_internal.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_internal.go
new file mode 100644
index 000000000..dea981fbb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_internal.go
@@ -0,0 +1,242 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// All non-std package dependencies live in this file,
+// so porting to different environment is easy (just update functions).
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "reflect"
+)
+
+func panicValToErr(panicVal interface{}, err *error) {
+ if panicVal == nil {
+ return
+ }
+ // case nil
+ switch xerr := panicVal.(type) {
+ case error:
+ *err = xerr
+ case string:
+ *err = errors.New(xerr)
+ default:
+ *err = fmt.Errorf("%v", panicVal)
+ }
+ return
+}
+
+func hIsEmptyValue(v reflect.Value, deref, checkStruct bool) bool {
+ switch v.Kind() {
+ case reflect.Invalid:
+ return true
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ if deref {
+ if v.IsNil() {
+ return true
+ }
+ return hIsEmptyValue(v.Elem(), deref, checkStruct)
+ } else {
+ return v.IsNil()
+ }
+ case reflect.Struct:
+ if !checkStruct {
+ return false
+ }
+ // return true if all fields are empty. else return false.
+ // we cannot use equality check, because some fields may be maps/slices/etc
+ // and consequently the structs are not comparable.
+ // return v.Interface() == reflect.Zero(v.Type()).Interface()
+ for i, n := 0, v.NumField(); i < n; i++ {
+ if !hIsEmptyValue(v.Field(i), deref, checkStruct) {
+ return false
+ }
+ }
+ return true
+ }
+ return false
+}
+
+func isEmptyValue(v reflect.Value) bool {
+ return hIsEmptyValue(v, derefForIsEmptyValue, checkStructForEmptyValue)
+}
+
+func pruneSignExt(v []byte, pos bool) (n int) {
+ if len(v) < 2 {
+ } else if pos && v[0] == 0 {
+ for ; v[n] == 0 && n+1 < len(v) && (v[n+1]&(1<<7) == 0); n++ {
+ }
+ } else if !pos && v[0] == 0xff {
+ for ; v[n] == 0xff && n+1 < len(v) && (v[n+1]&(1<<7) != 0); n++ {
+ }
+ }
+ return
+}
+
+func implementsIntf(typ, iTyp reflect.Type) (success bool, indir int8) {
+ if typ == nil {
+ return
+ }
+ rt := typ
+ // The type might be a pointer and we need to keep
+ // dereferencing to the base type until we find an implementation.
+ for {
+ if rt.Implements(iTyp) {
+ return true, indir
+ }
+ if p := rt; p.Kind() == reflect.Ptr {
+ indir++
+ if indir >= math.MaxInt8 { // insane number of indirections
+ return false, 0
+ }
+ rt = p.Elem()
+ continue
+ }
+ break
+ }
+ // No luck yet, but if this is a base type (non-pointer), the pointer might satisfy.
+ if typ.Kind() != reflect.Ptr {
+ // Not a pointer, but does the pointer work?
+ if reflect.PtrTo(typ).Implements(iTyp) {
+ return true, -1
+ }
+ }
+ return false, 0
+}
+
+// validate that this function is correct ...
+// culled from OGRE (Object-Oriented Graphics Rendering Engine)
+// function: halfToFloatI (http://stderr.org/doc/ogre-doc/api/OgreBitwise_8h-source.html)
+func halfFloatToFloatBits(yy uint16) (d uint32) {
+ y := uint32(yy)
+ s := (y >> 15) & 0x01
+ e := (y >> 10) & 0x1f
+ m := y & 0x03ff
+
+ if e == 0 {
+ if m == 0 { // plu or minus 0
+ return s << 31
+ } else { // Denormalized number -- renormalize it
+ for (m & 0x00000400) == 0 {
+ m <<= 1
+ e -= 1
+ }
+ e += 1
+ const zz uint32 = 0x0400
+ m &= ^zz
+ }
+ } else if e == 31 {
+ if m == 0 { // Inf
+ return (s << 31) | 0x7f800000
+ } else { // NaN
+ return (s << 31) | 0x7f800000 | (m << 13)
+ }
+ }
+ e = e + (127 - 15)
+ m = m << 13
+ return (s << 31) | (e << 23) | m
+}
+
+// GrowCap will return a new capacity for a slice, given the following:
+// - oldCap: current capacity
+// - unit: in-memory size of an element
+// - num: number of elements to add
+func growCap(oldCap, unit, num int) (newCap int) {
+ // appendslice logic (if cap < 1024, *2, else *1.25):
+ // leads to many copy calls, especially when copying bytes.
+ // bytes.Buffer model (2*cap + n): much better for bytes.
+ // smarter way is to take the byte-size of the appended element(type) into account
+
+ // maintain 3 thresholds:
+ // t1: if cap <= t1, newcap = 2x
+ // t2: if cap <= t2, newcap = 1.75x
+ // t3: if cap <= t3, newcap = 1.5x
+ // else newcap = 1.25x
+ //
+ // t1, t2, t3 >= 1024 always.
+ // i.e. if unit size >= 16, then always do 2x or 1.25x (ie t1, t2, t3 are all same)
+ //
+ // With this, appending for bytes increase by:
+ // 100% up to 4K
+ // 75% up to 8K
+ // 50% up to 16K
+ // 25% beyond that
+
+ // unit can be 0 e.g. for struct{}{}; handle that appropriately
+ var t1, t2, t3 int // thresholds
+ if unit <= 1 {
+ t1, t2, t3 = 4*1024, 8*1024, 16*1024
+ } else if unit < 16 {
+ t3 = 16 / unit * 1024
+ t1 = t3 * 1 / 4
+ t2 = t3 * 2 / 4
+ } else {
+ t1, t2, t3 = 1024, 1024, 1024
+ }
+
+ var x int // temporary variable
+
+ // x is multiplier here: one of 5, 6, 7 or 8; incr of 25%, 50%, 75% or 100% respectively
+ if oldCap <= t1 { // [0,t1]
+ x = 8
+ } else if oldCap > t3 { // (t3,infinity]
+ x = 5
+ } else if oldCap <= t2 { // (t1,t2]
+ x = 7
+ } else { // (t2,t3]
+ x = 6
+ }
+ newCap = x * oldCap / 4
+
+ if num > 0 {
+ newCap += num
+ }
+
+ // ensure newCap is a multiple of 64 (if it is > 64) or 16.
+ if newCap > 64 {
+ if x = newCap % 64; x != 0 {
+ x = newCap / 64
+ newCap = 64 * (x + 1)
+ }
+ } else {
+ if x = newCap % 16; x != 0 {
+ x = newCap / 16
+ newCap = 16 * (x + 1)
+ }
+ }
+ return
+}
+
+func expandSliceValue(s reflect.Value, num int) reflect.Value {
+ if num <= 0 {
+ return s
+ }
+ l0 := s.Len()
+ l1 := l0 + num // new slice length
+ if l1 < l0 {
+ panic("ExpandSlice: slice overflow")
+ }
+ c0 := s.Cap()
+ if l1 <= c0 {
+ return s.Slice(0, l1)
+ }
+ st := s.Type()
+ c1 := growCap(c0, int(st.Elem().Size()), num)
+ s2 := reflect.MakeSlice(st, l1, c1)
+ // println("expandslicevalue: cap-old: ", c0, ", cap-new: ", c1, ", len-new: ", l1)
+ reflect.Copy(s2, s)
+ return s2
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_not_unsafe.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_not_unsafe.go
new file mode 100644
index 000000000..7c2ffc0fd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_not_unsafe.go
@@ -0,0 +1,20 @@
+//+build !unsafe
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// stringView returns a view of the []byte as a string.
+// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
+// In regular safe mode, it is an allocation and copy.
+func stringView(v []byte) string {
+ return string(v)
+}
+
+// bytesView returns a view of the string as a []byte.
+// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
+// In regular safe mode, it is an allocation and copy.
+func bytesView(v string) []byte {
+ return []byte(v)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go
new file mode 100644
index 000000000..685c576c4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_test.go
@@ -0,0 +1,155 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// All non-std package dependencies related to testing live in this file,
+// so porting to different environment is easy (just update functions).
+//
+// This file sets up the variables used, including testInitFns.
+// Each file should add initialization that should be performed
+// after flags are parsed.
+//
+// init is a multi-step process:
+// - setup vars (handled by init functions in each file)
+// - parse flags
+// - setup derived vars (handled by pre-init registered functions - registered in init function)
+// - post init (handled by post-init registered functions - registered in init function)
+// This way, no one has to manage carefully control the initialization
+// using file names, etc.
+//
+// Tests which require external dependencies need the -tag=x parameter.
+// They should be run as:
+// go test -tags=x -run=.
+// Benchmarks should also take this parameter, to include the sereal, xdr, etc.
+// To run against codecgen, etc, make sure you pass extra parameters.
+// Example usage:
+// go test "-tags=x codecgen unsafe" -bench=.
+//
+// To fully test everything:
+// go test -tags=x -benchtime=100ms -tv -bg -bi -brw -bu -v -run=. -bench=.
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "reflect"
+ "sync"
+ "testing"
+)
+
+const (
+ testLogToT = true
+ failNowOnFail = true
+)
+
+var (
+ testNoopH = NoopHandle(8)
+ testMsgpackH = &MsgpackHandle{}
+ testBincH = &BincHandle{}
+ testBincHNoSym = &BincHandle{}
+ testBincHSym = &BincHandle{}
+ testSimpleH = &SimpleHandle{}
+ testCborH = &CborHandle{}
+ testJsonH = &JsonHandle{}
+
+ testPreInitFns []func()
+ testPostInitFns []func()
+
+ testOnce sync.Once
+)
+
+func init() {
+ testBincHSym.AsSymbols = AsSymbolAll
+ testBincHNoSym.AsSymbols = AsSymbolNone
+}
+
+func testInitAll() {
+ flag.Parse()
+ for _, f := range testPreInitFns {
+ f()
+ }
+ for _, f := range testPostInitFns {
+ f()
+ }
+}
+
+func logT(x interface{}, format string, args ...interface{}) {
+ if t, ok := x.(*testing.T); ok && t != nil && testLogToT {
+ if testVerbose {
+ t.Logf(format, args...)
+ }
+ } else if b, ok := x.(*testing.B); ok && b != nil && testLogToT {
+ b.Logf(format, args...)
+ } else {
+ if len(format) == 0 || format[len(format)-1] != '\n' {
+ format = format + "\n"
+ }
+ fmt.Printf(format, args...)
+ }
+}
+
+func approxDataSize(rv reflect.Value) (sum int) {
+ switch rk := rv.Kind(); rk {
+ case reflect.Invalid:
+ case reflect.Ptr, reflect.Interface:
+ sum += int(rv.Type().Size())
+ sum += approxDataSize(rv.Elem())
+ case reflect.Slice:
+ sum += int(rv.Type().Size())
+ for j := 0; j < rv.Len(); j++ {
+ sum += approxDataSize(rv.Index(j))
+ }
+ case reflect.String:
+ sum += int(rv.Type().Size())
+ sum += rv.Len()
+ case reflect.Map:
+ sum += int(rv.Type().Size())
+ for _, mk := range rv.MapKeys() {
+ sum += approxDataSize(mk)
+ sum += approxDataSize(rv.MapIndex(mk))
+ }
+ case reflect.Struct:
+ //struct size already includes the full data size.
+ //sum += int(rv.Type().Size())
+ for j := 0; j < rv.NumField(); j++ {
+ sum += approxDataSize(rv.Field(j))
+ }
+ default:
+ //pure value types
+ sum += int(rv.Type().Size())
+ }
+ return
+}
+
+// ----- functions below are used only by tests (not benchmarks)
+
+func checkErrT(t *testing.T, err error) {
+ if err != nil {
+ logT(t, err.Error())
+ failT(t)
+ }
+}
+
+func checkEqualT(t *testing.T, v1 interface{}, v2 interface{}, desc string) (err error) {
+ if err = deepEqual(v1, v2); err != nil {
+ logT(t, "Not Equal: %s: %v. v1: %v, v2: %v", desc, err, v1, v2)
+ failT(t)
+ }
+ return
+}
+
+func failT(t *testing.T) {
+ if failNowOnFail {
+ t.FailNow()
+ } else {
+ t.Fail()
+ }
+}
+
+func deepEqual(v1, v2 interface{}) (err error) {
+ if !reflect.DeepEqual(v1, v2) {
+ err = errors.New("Not Match")
+ }
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_unsafe.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_unsafe.go
new file mode 100644
index 000000000..373b2b102
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/helper_unsafe.go
@@ -0,0 +1,45 @@
+//+build unsafe
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "unsafe"
+)
+
+// This file has unsafe variants of some helper methods.
+
+type unsafeString struct {
+ Data uintptr
+ Len int
+}
+
+type unsafeBytes struct {
+ Data uintptr
+ Len int
+ Cap int
+}
+
+// stringView returns a view of the []byte as a string.
+// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
+// In regular safe mode, it is an allocation and copy.
+func stringView(v []byte) string {
+ if len(v) == 0 {
+ return ""
+ }
+ x := unsafeString{uintptr(unsafe.Pointer(&v[0])), len(v)}
+ return *(*string)(unsafe.Pointer(&x))
+}
+
+// bytesView returns a view of the string as a []byte.
+// In unsafe mode, it doesn't incur allocation and copying caused by conversion.
+// In regular safe mode, it is an allocation and copy.
+func bytesView(v string) []byte {
+ if len(v) == 0 {
+ return zeroByteSlice
+ }
+ x := unsafeBytes{uintptr(unsafe.Pointer(&v)), len(v), len(v)}
+ return *(*[]byte)(unsafe.Pointer(&x))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go
new file mode 100644
index 000000000..59e0bc24a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/json.go
@@ -0,0 +1,1141 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// By default, this json support uses base64 encoding for bytes, because you cannot
+// store and read any arbitrary string in json (only unicode).
+// However, the user can configre how to encode/decode bytes.
+//
+// This library specifically supports UTF-8 for encoding and decoding only.
+//
+// Note that the library will happily encode/decode things which are not valid
+// json e.g. a map[int64]string. We do it for consistency. With valid json,
+// we will encode and decode appropriately.
+// Users can specify their map type if necessary to force it.
+//
+// Note:
+// - we cannot use strconv.Quote and strconv.Unquote because json quotes/unquotes differently.
+// We implement it here.
+// - Also, strconv.ParseXXX for floats and integers
+// - only works on strings resulting in unnecessary allocation and []byte-string conversion.
+// - it does a lot of redundant checks, because json numbers are simpler that what it supports.
+// - We parse numbers (floats and integers) directly here.
+// We only delegate parsing floats if it is a hairy float which could cause a loss of precision.
+// In that case, we delegate to strconv.ParseFloat.
+//
+// Note:
+// - encode does not beautify. There is no whitespace when encoding.
+// - rpc calls which take single integer arguments or write single numeric arguments will need care.
+
+// Top-level methods of json(End|Dec)Driver (which are implementations of (en|de)cDriver
+// MUST not call one-another.
+// They all must call sep(), and sep() MUST NOT be called more than once for each read.
+// If sep() is called and read is not done, you MUST call retryRead so separator wouldn't be read/written twice.
+
+import (
+ "bytes"
+ "encoding/base64"
+ "fmt"
+ "reflect"
+ "strconv"
+ "sync"
+ "unicode/utf16"
+ "unicode/utf8"
+)
+
+//--------------------------------
+
+var jsonLiterals = [...]byte{'t', 'r', 'u', 'e', 'f', 'a', 'l', 's', 'e', 'n', 'u', 'l', 'l'}
+
+var jsonFloat64Pow10 = [...]float64{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22,
+}
+
+var jsonUint64Pow10 = [...]uint64{
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+}
+
+const (
+ // if jsonTrackSkipWhitespace, we track Whitespace and reduce the number of redundant checks.
+ // Make it a const flag, so that it can be elided during linking if false.
+ //
+ // It is not a clear win, because we continually set a flag behind a pointer
+ // and then check it each time, as opposed to just 4 conditionals on a stack variable.
+ jsonTrackSkipWhitespace = true
+
+ // If !jsonValidateSymbols, decoding will be faster, by skipping some checks:
+ // - If we see first character of null, false or true,
+ // do not validate subsequent characters.
+ // - e.g. if we see a n, assume null and skip next 3 characters,
+ // and do not validate they are ull.
+ // P.S. Do not expect a significant decoding boost from this.
+ jsonValidateSymbols = true
+
+ // if jsonTruncateMantissa, truncate mantissa if trailing 0's.
+ // This is important because it could allow some floats to be decoded without
+ // deferring to strconv.ParseFloat.
+ jsonTruncateMantissa = true
+
+ // if mantissa >= jsonNumUintCutoff before multiplying by 10, this is an overflow
+ jsonNumUintCutoff = (1<<64-1)/uint64(10) + 1 // cutoff64(base)
+
+ // if mantissa >= jsonNumUintMaxVal, this is an overflow
+ jsonNumUintMaxVal = 1<= slen {
+ e.bs = e.bs[:slen]
+ } else {
+ e.bs = make([]byte, slen)
+ }
+ base64.StdEncoding.Encode(e.bs, v)
+ e.w.writen1('"')
+ e.w.writeb(e.bs)
+ e.w.writen1('"')
+ } else {
+ // e.EncodeString(c, string(v))
+ e.quoteStr(stringView(v))
+ }
+}
+
+func (e *jsonEncDriver) EncodeAsis(v []byte) {
+ if c := e.s.sc.sep(); c != 0 {
+ e.w.writen1(c)
+ }
+ e.w.writeb(v)
+}
+
+func (e *jsonEncDriver) quoteStr(s string) {
+ // adapted from std pkg encoding/json
+ const hex = "0123456789abcdef"
+ w := e.w
+ w.writen1('"')
+ start := 0
+ for i := 0; i < len(s); {
+ if b := s[i]; b < utf8.RuneSelf {
+ if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
+ i++
+ continue
+ }
+ if start < i {
+ w.writestr(s[start:i])
+ }
+ switch b {
+ case '\\', '"':
+ w.writen2('\\', b)
+ case '\n':
+ w.writen2('\\', 'n')
+ case '\r':
+ w.writen2('\\', 'r')
+ case '\b':
+ w.writen2('\\', 'b')
+ case '\f':
+ w.writen2('\\', 'f')
+ case '\t':
+ w.writen2('\\', 't')
+ default:
+ // encode all bytes < 0x20 (except \r, \n).
+ // also encode < > & to prevent security holes when served to some browsers.
+ w.writestr(`\u00`)
+ w.writen2(hex[b>>4], hex[b&0xF])
+ }
+ i++
+ start = i
+ continue
+ }
+ c, size := utf8.DecodeRuneInString(s[i:])
+ if c == utf8.RuneError && size == 1 {
+ if start < i {
+ w.writestr(s[start:i])
+ }
+ w.writestr(`\ufffd`)
+ i += size
+ start = i
+ continue
+ }
+ // U+2028 is LINE SEPARATOR. U+2029 is PARAGRAPH SEPARATOR.
+ // Both technically valid JSON, but bomb on JSONP, so fix here.
+ if c == '\u2028' || c == '\u2029' {
+ if start < i {
+ w.writestr(s[start:i])
+ }
+ w.writestr(`\u202`)
+ w.writen1(hex[c&0xF])
+ i += size
+ start = i
+ continue
+ }
+ i += size
+ }
+ if start < len(s) {
+ w.writestr(s[start:])
+ }
+ w.writen1('"')
+}
+
+//--------------------------------
+
+type jsonNum struct {
+ bytes []byte // may have [+-.eE0-9]
+ mantissa uint64 // where mantissa ends, and maybe dot begins.
+ exponent int16 // exponent value.
+ manOverflow bool
+ neg bool // started with -. No initial sign in the bytes above.
+ dot bool // has dot
+ explicitExponent bool // explicit exponent
+}
+
+func (x *jsonNum) reset() {
+ x.bytes = x.bytes[:0]
+ x.manOverflow = false
+ x.neg = false
+ x.dot = false
+ x.explicitExponent = false
+ x.mantissa = 0
+ x.exponent = 0
+}
+
+// uintExp is called only if exponent > 0.
+func (x *jsonNum) uintExp() (n uint64, overflow bool) {
+ n = x.mantissa
+ e := x.exponent
+ if e >= int16(len(jsonUint64Pow10)) {
+ overflow = true
+ return
+ }
+ n *= jsonUint64Pow10[e]
+ if n < x.mantissa || n > jsonNumUintMaxVal {
+ overflow = true
+ return
+ }
+ return
+ // for i := int16(0); i < e; i++ {
+ // if n >= jsonNumUintCutoff {
+ // overflow = true
+ // return
+ // }
+ // n *= 10
+ // }
+ // return
+}
+
+func (x *jsonNum) floatVal() (f float64) {
+ // We do not want to lose precision.
+ // Consequently, we will delegate to strconv.ParseFloat if any of the following happen:
+ // - There are more digits than in math.MaxUint64: 18446744073709551615 (20 digits)
+ // We expect up to 99.... (19 digits)
+ // - The mantissa cannot fit into a 52 bits of uint64
+ // - The exponent is beyond our scope ie beyong 22.
+ const uint64MantissaBits = 52
+ const maxExponent = int16(len(jsonFloat64Pow10)) - 1
+
+ parseUsingStrConv := x.manOverflow ||
+ x.exponent > maxExponent ||
+ (x.exponent < 0 && -(x.exponent) > maxExponent) ||
+ x.mantissa>>uint64MantissaBits != 0
+ if parseUsingStrConv {
+ var err error
+ if f, err = strconv.ParseFloat(stringView(x.bytes), 64); err != nil {
+ panic(fmt.Errorf("parse float: %s, %v", x.bytes, err))
+ return
+ }
+ if x.neg {
+ f = -f
+ }
+ return
+ }
+
+ // all good. so handle parse here.
+ f = float64(x.mantissa)
+ // fmt.Printf(".Float: uint64 value: %v, float: %v\n", m, f)
+ if x.neg {
+ f = -f
+ }
+ if x.exponent > 0 {
+ f *= jsonFloat64Pow10[x.exponent]
+ } else if x.exponent < 0 {
+ f /= jsonFloat64Pow10[-x.exponent]
+ }
+ return
+}
+
+type jsonDecDriver struct {
+ d *Decoder
+ h *JsonHandle
+ r decReader // *bytesDecReader decReader
+ ct valueType // container type. one of unset, array or map.
+ bstr [8]byte // scratch used for string \UXXX parsing
+ b [64]byte // scratch
+ b2 [64]byte
+
+ wsSkipped bool // whitespace skipped
+
+ se setExtWrapper
+
+ s jsonStack
+
+ n jsonNum
+ noBuiltInTypes
+}
+
+// This will skip whitespace characters and return the next byte to read.
+// The next byte determines what the value will be one of.
+func (d *jsonDecDriver) skipWhitespace(unread bool) (b byte) {
+ // as initReadNext is not called all the time, we set ct to unSet whenever
+ // we skipwhitespace, as this is the signal that something new is about to be read.
+ d.ct = valueTypeUnset
+ b = d.r.readn1()
+ if !jsonTrackSkipWhitespace || !d.wsSkipped {
+ for ; b == ' ' || b == '\t' || b == '\r' || b == '\n'; b = d.r.readn1() {
+ }
+ if jsonTrackSkipWhitespace {
+ d.wsSkipped = true
+ }
+ }
+ if unread {
+ d.r.unreadn1()
+ }
+ return b
+}
+
+func (d *jsonDecDriver) CheckBreak() bool {
+ b := d.skipWhitespace(true)
+ return b == '}' || b == ']'
+}
+
+func (d *jsonDecDriver) readStrIdx(fromIdx, toIdx uint8) {
+ bs := d.r.readx(int(toIdx - fromIdx))
+ if jsonValidateSymbols {
+ if !bytes.Equal(bs, jsonLiterals[fromIdx:toIdx]) {
+ d.d.errorf("json: expecting %s: got %s", jsonLiterals[fromIdx:toIdx], bs)
+ return
+ }
+ }
+ if jsonTrackSkipWhitespace {
+ d.wsSkipped = false
+ }
+}
+
+func (d *jsonDecDriver) TryDecodeAsNil() bool {
+ // we mustn't consume the state here, and end up trying to read separator twice.
+ // Instead, we keep track of the state and restore it if we couldn't decode as nil.
+
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ b := d.skipWhitespace(false)
+ if b == 'n' {
+ d.readStrIdx(10, 13) // ull
+ d.ct = valueTypeNil
+ return true
+ }
+ d.r.unreadn1()
+ d.s.sc.retryRead()
+ return false
+}
+
+func (d *jsonDecDriver) DecodeBool() bool {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ b := d.skipWhitespace(false)
+ if b == 'f' {
+ d.readStrIdx(5, 9) // alse
+ return false
+ }
+ if b == 't' {
+ d.readStrIdx(1, 4) // rue
+ return true
+ }
+ d.d.errorf("json: decode bool: got first char %c", b)
+ return false // "unreachable"
+}
+
+func (d *jsonDecDriver) ReadMapStart() int {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ d.s.start('}')
+ d.expectChar('{')
+ d.ct = valueTypeMap
+ return -1
+}
+
+func (d *jsonDecDriver) ReadArrayStart() int {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ d.s.start(']')
+ d.expectChar('[')
+ d.ct = valueTypeArray
+ return -1
+}
+
+func (d *jsonDecDriver) ReadEnd() {
+ b := d.s.sc.st
+ d.s.end()
+ d.expectChar(b)
+}
+
+func (d *jsonDecDriver) expectChar(c uint8) {
+ b := d.skipWhitespace(false)
+ if b != c {
+ d.d.errorf("json: expect char '%c' but got char '%c'", c, b)
+ return
+ }
+ if jsonTrackSkipWhitespace {
+ d.wsSkipped = false
+ }
+}
+
+// func (d *jsonDecDriver) maybeChar(c uint8) {
+// b := d.skipWhitespace(false)
+// if b != c {
+// d.r.unreadn1()
+// return
+// }
+// if jsonTrackSkipWhitespace {
+// d.wsSkipped = false
+// }
+// }
+
+func (d *jsonDecDriver) IsContainerType(vt valueType) bool {
+ // check container type by checking the first char
+ if d.ct == valueTypeUnset {
+ b := d.skipWhitespace(true)
+ if b == '{' {
+ d.ct = valueTypeMap
+ } else if b == '[' {
+ d.ct = valueTypeArray
+ } else if b == 'n' {
+ d.ct = valueTypeNil
+ } else if b == '"' {
+ d.ct = valueTypeString
+ }
+ }
+ if vt == valueTypeNil || vt == valueTypeBytes || vt == valueTypeString ||
+ vt == valueTypeArray || vt == valueTypeMap {
+ return d.ct == vt
+ }
+ // ugorji: made switch into conditionals, so that IsContainerType can be inlined.
+ // switch vt {
+ // case valueTypeNil, valueTypeBytes, valueTypeString, valueTypeArray, valueTypeMap:
+ // return d.ct == vt
+ // }
+ d.d.errorf("isContainerType: unsupported parameter: %v", vt)
+ return false // "unreachable"
+}
+
+func (d *jsonDecDriver) decNum(storeBytes bool) {
+ // storeBytes = true // TODO: remove.
+
+ // If it is has a . or an e|E, decode as a float; else decode as an int.
+ b := d.skipWhitespace(false)
+ if !(b == '+' || b == '-' || b == '.' || (b >= '0' && b <= '9')) {
+ d.d.errorf("json: decNum: got first char '%c'", b)
+ return
+ }
+
+ const cutoff = (1<<64-1)/uint64(10) + 1 // cutoff64(base)
+ const jsonNumUintMaxVal = 1<= jsonNumUintCutoff {
+ n.manOverflow = true
+ break
+ }
+ v := uint64(b - '0')
+ n.mantissa *= 10
+ if v != 0 {
+ n1 := n.mantissa + v
+ if n1 < n.mantissa || n1 > jsonNumUintMaxVal {
+ n.manOverflow = true // n+v overflows
+ break
+ }
+ n.mantissa = n1
+ }
+ case 6:
+ state = 7
+ fallthrough
+ case 7:
+ if !(b == '0' && e == 0) {
+ e = e*10 + int16(b-'0')
+ }
+ default:
+ break LOOP
+ }
+ default:
+ break LOOP
+ }
+ if storeBytes {
+ n.bytes = append(n.bytes, b)
+ }
+ b, eof = d.r.readn1eof()
+ }
+
+ if jsonTruncateMantissa && n.mantissa != 0 {
+ for n.mantissa%10 == 0 {
+ n.mantissa /= 10
+ n.exponent++
+ }
+ }
+
+ if e != 0 {
+ if eNeg {
+ n.exponent -= e
+ } else {
+ n.exponent += e
+ }
+ }
+
+ // d.n = n
+
+ if !eof {
+ d.r.unreadn1()
+ }
+ if jsonTrackSkipWhitespace {
+ d.wsSkipped = false
+ }
+ // fmt.Printf("1: n: bytes: %s, neg: %v, dot: %v, exponent: %v, mantissaEndIndex: %v\n",
+ // n.bytes, n.neg, n.dot, n.exponent, n.mantissaEndIndex)
+ return
+}
+
+func (d *jsonDecDriver) DecodeInt(bitsize uint8) (i int64) {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ d.decNum(false)
+ n := &d.n
+ if n.manOverflow {
+ d.d.errorf("json: overflow integer after: %v", n.mantissa)
+ return
+ }
+ var u uint64
+ if n.exponent == 0 {
+ u = n.mantissa
+ } else if n.exponent < 0 {
+ d.d.errorf("json: fractional integer")
+ return
+ } else if n.exponent > 0 {
+ var overflow bool
+ if u, overflow = n.uintExp(); overflow {
+ d.d.errorf("json: overflow integer")
+ return
+ }
+ }
+ i = int64(u)
+ if n.neg {
+ i = -i
+ }
+ if chkOvf.Int(i, bitsize) {
+ d.d.errorf("json: overflow %v bits: %s", bitsize, n.bytes)
+ return
+ }
+ // fmt.Printf("DecodeInt: %v\n", i)
+ return
+}
+
+func (d *jsonDecDriver) DecodeUint(bitsize uint8) (u uint64) {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ d.decNum(false)
+ n := &d.n
+ if n.neg {
+ d.d.errorf("json: unsigned integer cannot be negative")
+ return
+ }
+ if n.manOverflow {
+ d.d.errorf("json: overflow integer after: %v", n.mantissa)
+ return
+ }
+ if n.exponent == 0 {
+ u = n.mantissa
+ } else if n.exponent < 0 {
+ d.d.errorf("json: fractional integer")
+ return
+ } else if n.exponent > 0 {
+ var overflow bool
+ if u, overflow = n.uintExp(); overflow {
+ d.d.errorf("json: overflow integer")
+ return
+ }
+ }
+ if chkOvf.Uint(u, bitsize) {
+ d.d.errorf("json: overflow %v bits: %s", bitsize, n.bytes)
+ return
+ }
+ // fmt.Printf("DecodeUint: %v\n", u)
+ return
+}
+
+func (d *jsonDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ d.decNum(true)
+ n := &d.n
+ f = n.floatVal()
+ if chkOverflow32 && chkOvf.Float32(f) {
+ d.d.errorf("json: overflow float32: %v, %s", f, n.bytes)
+ return
+ }
+ return
+}
+
+func (d *jsonDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
+ // No need to call sep here, as d.d.decode() handles it
+ // if c := d.s.sc.sep(); c != 0 {
+ // d.expectChar(c)
+ // }
+ if ext == nil {
+ re := rv.(*RawExt)
+ re.Tag = xtag
+ d.d.decode(&re.Value)
+ } else {
+ var v interface{}
+ d.d.decode(&v)
+ ext.UpdateExt(rv, v)
+ }
+ return
+}
+
+func (d *jsonDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
+ // if decoding into raw bytes, and the RawBytesExt is configured, use it to decode.
+ if !isstring && d.se.i != nil {
+ bsOut = bs
+ d.DecodeExt(&bsOut, 0, &d.se)
+ return
+ }
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ bs0 := d.appendStringAsBytes(d.b[:0])
+ // if isstring, then just return the bytes, even if it is using the scratch buffer.
+ // the bytes will be converted to a string as needed.
+ if isstring {
+ return bs0
+ }
+ slen := base64.StdEncoding.DecodedLen(len(bs0))
+ if slen <= cap(bs) {
+ bsOut = bs[:slen]
+ } else if zerocopy && slen <= cap(d.b2) {
+ bsOut = d.b2[:slen]
+ } else {
+ bsOut = make([]byte, slen)
+ }
+ slen2, err := base64.StdEncoding.Decode(bsOut, bs0)
+ if err != nil {
+ d.d.errorf("json: error decoding base64 binary '%s': %v", bs0, err)
+ return nil
+ }
+ if slen != slen2 {
+ bsOut = bsOut[:slen2]
+ }
+ return
+}
+
+func (d *jsonDecDriver) DecodeString() (s string) {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ return string(d.appendStringAsBytes(d.b[:0]))
+}
+
+func (d *jsonDecDriver) appendStringAsBytes(v []byte) []byte {
+ d.expectChar('"')
+ for {
+ c := d.r.readn1()
+ if c == '"' {
+ break
+ } else if c == '\\' {
+ c = d.r.readn1()
+ switch c {
+ case '"', '\\', '/', '\'':
+ v = append(v, c)
+ case 'b':
+ v = append(v, '\b')
+ case 'f':
+ v = append(v, '\f')
+ case 'n':
+ v = append(v, '\n')
+ case 'r':
+ v = append(v, '\r')
+ case 't':
+ v = append(v, '\t')
+ case 'u':
+ rr := d.jsonU4(false)
+ // fmt.Printf("$$$$$$$$$: is surrogate: %v\n", utf16.IsSurrogate(rr))
+ if utf16.IsSurrogate(rr) {
+ rr = utf16.DecodeRune(rr, d.jsonU4(true))
+ }
+ w2 := utf8.EncodeRune(d.bstr[:], rr)
+ v = append(v, d.bstr[:w2]...)
+ default:
+ d.d.errorf("json: unsupported escaped value: %c", c)
+ return nil
+ }
+ } else {
+ v = append(v, c)
+ }
+ }
+ if jsonTrackSkipWhitespace {
+ d.wsSkipped = false
+ }
+ return v
+}
+
+func (d *jsonDecDriver) jsonU4(checkSlashU bool) rune {
+ if checkSlashU && !(d.r.readn1() == '\\' && d.r.readn1() == 'u') {
+ d.d.errorf(`json: unquoteStr: invalid unicode sequence. Expecting \u`)
+ return 0
+ }
+ // u, _ := strconv.ParseUint(string(d.bstr[:4]), 16, 64)
+ var u uint32
+ for i := 0; i < 4; i++ {
+ v := d.r.readn1()
+ if '0' <= v && v <= '9' {
+ v = v - '0'
+ } else if 'a' <= v && v <= 'z' {
+ v = v - 'a' + 10
+ } else if 'A' <= v && v <= 'Z' {
+ v = v - 'A' + 10
+ } else {
+ d.d.errorf(`json: unquoteStr: invalid hex char in \u unicode sequence: %q`, v)
+ return 0
+ }
+ u = u*16 + uint32(v)
+ }
+ return rune(u)
+}
+
+func (d *jsonDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+ if c := d.s.sc.sep(); c != 0 {
+ d.expectChar(c)
+ }
+ n := d.skipWhitespace(true)
+ switch n {
+ case 'n':
+ d.readStrIdx(9, 13) // null
+ vt = valueTypeNil
+ case 'f':
+ d.readStrIdx(4, 9) // false
+ vt = valueTypeBool
+ v = false
+ case 't':
+ d.readStrIdx(0, 4) // true
+ vt = valueTypeBool
+ v = true
+ case '{':
+ vt = valueTypeMap
+ decodeFurther = true
+ case '[':
+ vt = valueTypeArray
+ decodeFurther = true
+ case '"':
+ vt = valueTypeString
+ v = string(d.appendStringAsBytes(d.b[:0])) // same as d.DecodeString(), but skipping sep() call.
+ default: // number
+ d.decNum(true)
+ n := &d.n
+ // if the string had a any of [.eE], then decode as float.
+ switch {
+ case n.explicitExponent, n.dot, n.exponent < 0, n.manOverflow:
+ vt = valueTypeFloat
+ v = n.floatVal()
+ case n.exponent == 0:
+ u := n.mantissa
+ switch {
+ case n.neg:
+ vt = valueTypeInt
+ v = -int64(u)
+ case d.h.SignedInteger:
+ vt = valueTypeInt
+ v = int64(u)
+ default:
+ vt = valueTypeUint
+ v = u
+ }
+ default:
+ u, overflow := n.uintExp()
+ switch {
+ case overflow:
+ vt = valueTypeFloat
+ v = n.floatVal()
+ case n.neg:
+ vt = valueTypeInt
+ v = -int64(u)
+ case d.h.SignedInteger:
+ vt = valueTypeInt
+ v = int64(u)
+ default:
+ vt = valueTypeUint
+ v = u
+ }
+ }
+ // fmt.Printf("DecodeNaked: Number: %T, %v\n", v, v)
+ }
+ if decodeFurther {
+ d.s.sc.retryRead()
+ }
+ return
+}
+
+//----------------------
+
+// JsonHandle is a handle for JSON encoding format.
+//
+// Json is comprehensively supported:
+// - decodes numbers into interface{} as int, uint or float64
+// - configurable way to encode/decode []byte .
+// by default, encodes and decodes []byte using base64 Std Encoding
+// - UTF-8 support for encoding and decoding
+//
+// It has better performance than the json library in the standard library,
+// by leveraging the performance improvements of the codec library and
+// minimizing allocations.
+//
+// In addition, it doesn't read more bytes than necessary during a decode, which allows
+// reading multiple values from a stream containing json and non-json content.
+// For example, a user can read a json value, then a cbor value, then a msgpack value,
+// all from the same stream in sequence.
+type JsonHandle struct {
+ BasicHandle
+ textEncodingType
+ // RawBytesExt, if configured, is used to encode and decode raw bytes in a custom way.
+ // If not configured, raw bytes are encoded to/from base64 text.
+ RawBytesExt InterfaceExt
+}
+
+func (h *JsonHandle) newEncDriver(e *Encoder) encDriver {
+ hd := jsonEncDriver{e: e, w: e.w, h: h}
+ hd.se.i = h.RawBytesExt
+ return &hd
+}
+
+func (h *JsonHandle) newDecDriver(d *Decoder) decDriver {
+ // d := jsonDecDriver{r: r.(*bytesDecReader), h: h}
+ hd := jsonDecDriver{d: d, r: d.r, h: h}
+ hd.se.i = h.RawBytesExt
+ hd.n.bytes = d.b[:]
+ return &hd
+}
+
+func (h *JsonHandle) SetInterfaceExt(rt reflect.Type, tag uint64, ext InterfaceExt) (err error) {
+ return h.SetExt(rt, tag, &setExtWrapper{i: ext})
+}
+
+var jsonEncodeTerminate = []byte{' '}
+
+func (h *JsonHandle) rpcEncodeTerminate() []byte {
+ return jsonEncodeTerminate
+}
+
+var _ decDriver = (*jsonDecDriver)(nil)
+var _ encDriver = (*jsonEncDriver)(nil)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go
new file mode 100644
index 000000000..fd5f3895d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/msgpack.go
@@ -0,0 +1,844 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+/*
+MSGPACK
+
+Msgpack-c implementation powers the c, c++, python, ruby, etc libraries.
+We need to maintain compatibility with it and how it encodes integer values
+without caring about the type.
+
+For compatibility with behaviour of msgpack-c reference implementation:
+ - Go intX (>0) and uintX
+ IS ENCODED AS
+ msgpack +ve fixnum, unsigned
+ - Go intX (<0)
+ IS ENCODED AS
+ msgpack -ve fixnum, signed
+
+*/
+package codec
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "net/rpc"
+ "reflect"
+)
+
+const (
+ mpPosFixNumMin byte = 0x00
+ mpPosFixNumMax = 0x7f
+ mpFixMapMin = 0x80
+ mpFixMapMax = 0x8f
+ mpFixArrayMin = 0x90
+ mpFixArrayMax = 0x9f
+ mpFixStrMin = 0xa0
+ mpFixStrMax = 0xbf
+ mpNil = 0xc0
+ _ = 0xc1
+ mpFalse = 0xc2
+ mpTrue = 0xc3
+ mpFloat = 0xca
+ mpDouble = 0xcb
+ mpUint8 = 0xcc
+ mpUint16 = 0xcd
+ mpUint32 = 0xce
+ mpUint64 = 0xcf
+ mpInt8 = 0xd0
+ mpInt16 = 0xd1
+ mpInt32 = 0xd2
+ mpInt64 = 0xd3
+
+ // extensions below
+ mpBin8 = 0xc4
+ mpBin16 = 0xc5
+ mpBin32 = 0xc6
+ mpExt8 = 0xc7
+ mpExt16 = 0xc8
+ mpExt32 = 0xc9
+ mpFixExt1 = 0xd4
+ mpFixExt2 = 0xd5
+ mpFixExt4 = 0xd6
+ mpFixExt8 = 0xd7
+ mpFixExt16 = 0xd8
+
+ mpStr8 = 0xd9 // new
+ mpStr16 = 0xda
+ mpStr32 = 0xdb
+
+ mpArray16 = 0xdc
+ mpArray32 = 0xdd
+
+ mpMap16 = 0xde
+ mpMap32 = 0xdf
+
+ mpNegFixNumMin = 0xe0
+ mpNegFixNumMax = 0xff
+)
+
+// MsgpackSpecRpcMultiArgs is a special type which signifies to the MsgpackSpecRpcCodec
+// that the backend RPC service takes multiple arguments, which have been arranged
+// in sequence in the slice.
+//
+// The Codec then passes it AS-IS to the rpc service (without wrapping it in an
+// array of 1 element).
+type MsgpackSpecRpcMultiArgs []interface{}
+
+// A MsgpackContainer type specifies the different types of msgpackContainers.
+type msgpackContainerType struct {
+ fixCutoff int
+ bFixMin, b8, b16, b32 byte
+ hasFixMin, has8, has8Always bool
+}
+
+var (
+ msgpackContainerStr = msgpackContainerType{32, mpFixStrMin, mpStr8, mpStr16, mpStr32, true, true, false}
+ msgpackContainerBin = msgpackContainerType{0, 0, mpBin8, mpBin16, mpBin32, false, true, true}
+ msgpackContainerList = msgpackContainerType{16, mpFixArrayMin, 0, mpArray16, mpArray32, true, false, false}
+ msgpackContainerMap = msgpackContainerType{16, mpFixMapMin, 0, mpMap16, mpMap32, true, false, false}
+)
+
+//---------------------------------------------
+
+type msgpackEncDriver struct {
+ e *Encoder
+ w encWriter
+ h *MsgpackHandle
+ noBuiltInTypes
+ encNoSeparator
+ x [8]byte
+}
+
+func (e *msgpackEncDriver) EncodeNil() {
+ e.w.writen1(mpNil)
+}
+
+func (e *msgpackEncDriver) EncodeInt(i int64) {
+ if i >= 0 {
+ e.EncodeUint(uint64(i))
+ } else if i >= -32 {
+ e.w.writen1(byte(i))
+ } else if i >= math.MinInt8 {
+ e.w.writen2(mpInt8, byte(i))
+ } else if i >= math.MinInt16 {
+ e.w.writen1(mpInt16)
+ bigenHelper{e.x[:2], e.w}.writeUint16(uint16(i))
+ } else if i >= math.MinInt32 {
+ e.w.writen1(mpInt32)
+ bigenHelper{e.x[:4], e.w}.writeUint32(uint32(i))
+ } else {
+ e.w.writen1(mpInt64)
+ bigenHelper{e.x[:8], e.w}.writeUint64(uint64(i))
+ }
+}
+
+func (e *msgpackEncDriver) EncodeUint(i uint64) {
+ if i <= math.MaxInt8 {
+ e.w.writen1(byte(i))
+ } else if i <= math.MaxUint8 {
+ e.w.writen2(mpUint8, byte(i))
+ } else if i <= math.MaxUint16 {
+ e.w.writen1(mpUint16)
+ bigenHelper{e.x[:2], e.w}.writeUint16(uint16(i))
+ } else if i <= math.MaxUint32 {
+ e.w.writen1(mpUint32)
+ bigenHelper{e.x[:4], e.w}.writeUint32(uint32(i))
+ } else {
+ e.w.writen1(mpUint64)
+ bigenHelper{e.x[:8], e.w}.writeUint64(uint64(i))
+ }
+}
+
+func (e *msgpackEncDriver) EncodeBool(b bool) {
+ if b {
+ e.w.writen1(mpTrue)
+ } else {
+ e.w.writen1(mpFalse)
+ }
+}
+
+func (e *msgpackEncDriver) EncodeFloat32(f float32) {
+ e.w.writen1(mpFloat)
+ bigenHelper{e.x[:4], e.w}.writeUint32(math.Float32bits(f))
+}
+
+func (e *msgpackEncDriver) EncodeFloat64(f float64) {
+ e.w.writen1(mpDouble)
+ bigenHelper{e.x[:8], e.w}.writeUint64(math.Float64bits(f))
+}
+
+func (e *msgpackEncDriver) EncodeExt(v interface{}, xtag uint64, ext Ext, _ *Encoder) {
+ bs := ext.WriteExt(v)
+ if bs == nil {
+ e.EncodeNil()
+ return
+ }
+ if e.h.WriteExt {
+ e.encodeExtPreamble(uint8(xtag), len(bs))
+ e.w.writeb(bs)
+ } else {
+ e.EncodeStringBytes(c_RAW, bs)
+ }
+}
+
+func (e *msgpackEncDriver) EncodeRawExt(re *RawExt, _ *Encoder) {
+ e.encodeExtPreamble(uint8(re.Tag), len(re.Data))
+ e.w.writeb(re.Data)
+}
+
+func (e *msgpackEncDriver) encodeExtPreamble(xtag byte, l int) {
+ if l == 1 {
+ e.w.writen2(mpFixExt1, xtag)
+ } else if l == 2 {
+ e.w.writen2(mpFixExt2, xtag)
+ } else if l == 4 {
+ e.w.writen2(mpFixExt4, xtag)
+ } else if l == 8 {
+ e.w.writen2(mpFixExt8, xtag)
+ } else if l == 16 {
+ e.w.writen2(mpFixExt16, xtag)
+ } else if l < 256 {
+ e.w.writen2(mpExt8, byte(l))
+ e.w.writen1(xtag)
+ } else if l < 65536 {
+ e.w.writen1(mpExt16)
+ bigenHelper{e.x[:2], e.w}.writeUint16(uint16(l))
+ e.w.writen1(xtag)
+ } else {
+ e.w.writen1(mpExt32)
+ bigenHelper{e.x[:4], e.w}.writeUint32(uint32(l))
+ e.w.writen1(xtag)
+ }
+}
+
+func (e *msgpackEncDriver) EncodeArrayStart(length int) {
+ e.writeContainerLen(msgpackContainerList, length)
+}
+
+func (e *msgpackEncDriver) EncodeMapStart(length int) {
+ e.writeContainerLen(msgpackContainerMap, length)
+}
+
+func (e *msgpackEncDriver) EncodeString(c charEncoding, s string) {
+ if c == c_RAW && e.h.WriteExt {
+ e.writeContainerLen(msgpackContainerBin, len(s))
+ } else {
+ e.writeContainerLen(msgpackContainerStr, len(s))
+ }
+ if len(s) > 0 {
+ e.w.writestr(s)
+ }
+}
+
+func (e *msgpackEncDriver) EncodeSymbol(v string) {
+ e.EncodeString(c_UTF8, v)
+}
+
+func (e *msgpackEncDriver) EncodeStringBytes(c charEncoding, bs []byte) {
+ if c == c_RAW && e.h.WriteExt {
+ e.writeContainerLen(msgpackContainerBin, len(bs))
+ } else {
+ e.writeContainerLen(msgpackContainerStr, len(bs))
+ }
+ if len(bs) > 0 {
+ e.w.writeb(bs)
+ }
+}
+
+func (e *msgpackEncDriver) writeContainerLen(ct msgpackContainerType, l int) {
+ if ct.hasFixMin && l < ct.fixCutoff {
+ e.w.writen1(ct.bFixMin | byte(l))
+ } else if ct.has8 && l < 256 && (ct.has8Always || e.h.WriteExt) {
+ e.w.writen2(ct.b8, uint8(l))
+ } else if l < 65536 {
+ e.w.writen1(ct.b16)
+ bigenHelper{e.x[:2], e.w}.writeUint16(uint16(l))
+ } else {
+ e.w.writen1(ct.b32)
+ bigenHelper{e.x[:4], e.w}.writeUint32(uint32(l))
+ }
+}
+
+//---------------------------------------------
+
+type msgpackDecDriver struct {
+ d *Decoder
+ r decReader // *Decoder decReader decReaderT
+ h *MsgpackHandle
+ b [scratchByteArrayLen]byte
+ bd byte
+ bdRead bool
+ br bool // bytes reader
+ bdType valueType
+ noBuiltInTypes
+ noStreamingCodec
+ decNoSeparator
+}
+
+// Note: This returns either a primitive (int, bool, etc) for non-containers,
+// or a containerType, or a specific type denoting nil or extension.
+// It is called when a nil interface{} is passed, leaving it up to the DecDriver
+// to introspect the stream and decide how best to decode.
+// It deciphers the value by looking at the stream first.
+func (d *msgpackDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ bd := d.bd
+
+ switch bd {
+ case mpNil:
+ vt = valueTypeNil
+ d.bdRead = false
+ case mpFalse:
+ vt = valueTypeBool
+ v = false
+ case mpTrue:
+ vt = valueTypeBool
+ v = true
+
+ case mpFloat:
+ vt = valueTypeFloat
+ v = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
+ case mpDouble:
+ vt = valueTypeFloat
+ v = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
+
+ case mpUint8:
+ vt = valueTypeUint
+ v = uint64(d.r.readn1())
+ case mpUint16:
+ vt = valueTypeUint
+ v = uint64(bigen.Uint16(d.r.readx(2)))
+ case mpUint32:
+ vt = valueTypeUint
+ v = uint64(bigen.Uint32(d.r.readx(4)))
+ case mpUint64:
+ vt = valueTypeUint
+ v = uint64(bigen.Uint64(d.r.readx(8)))
+
+ case mpInt8:
+ vt = valueTypeInt
+ v = int64(int8(d.r.readn1()))
+ case mpInt16:
+ vt = valueTypeInt
+ v = int64(int16(bigen.Uint16(d.r.readx(2))))
+ case mpInt32:
+ vt = valueTypeInt
+ v = int64(int32(bigen.Uint32(d.r.readx(4))))
+ case mpInt64:
+ vt = valueTypeInt
+ v = int64(int64(bigen.Uint64(d.r.readx(8))))
+
+ default:
+ switch {
+ case bd >= mpPosFixNumMin && bd <= mpPosFixNumMax:
+ // positive fixnum (always signed)
+ vt = valueTypeInt
+ v = int64(int8(bd))
+ case bd >= mpNegFixNumMin && bd <= mpNegFixNumMax:
+ // negative fixnum
+ vt = valueTypeInt
+ v = int64(int8(bd))
+ case bd == mpStr8, bd == mpStr16, bd == mpStr32, bd >= mpFixStrMin && bd <= mpFixStrMax:
+ if d.h.RawToString {
+ var rvm string
+ vt = valueTypeString
+ v = &rvm
+ } else {
+ var rvm = zeroByteSlice
+ vt = valueTypeBytes
+ v = &rvm
+ }
+ decodeFurther = true
+ case bd == mpBin8, bd == mpBin16, bd == mpBin32:
+ var rvm = zeroByteSlice
+ vt = valueTypeBytes
+ v = &rvm
+ decodeFurther = true
+ case bd == mpArray16, bd == mpArray32, bd >= mpFixArrayMin && bd <= mpFixArrayMax:
+ vt = valueTypeArray
+ decodeFurther = true
+ case bd == mpMap16, bd == mpMap32, bd >= mpFixMapMin && bd <= mpFixMapMax:
+ vt = valueTypeMap
+ decodeFurther = true
+ case bd >= mpFixExt1 && bd <= mpFixExt16, bd >= mpExt8 && bd <= mpExt32:
+ clen := d.readExtLen()
+ var re RawExt
+ re.Tag = uint64(d.r.readn1())
+ re.Data = d.r.readx(clen)
+ v = &re
+ vt = valueTypeExt
+ default:
+ d.d.errorf("Nil-Deciphered DecodeValue: %s: hex: %x, dec: %d", msgBadDesc, bd, bd)
+ return
+ }
+ }
+ if !decodeFurther {
+ d.bdRead = false
+ }
+ if vt == valueTypeUint && d.h.SignedInteger {
+ d.bdType = valueTypeInt
+ v = int64(v.(uint64))
+ }
+ return
+}
+
+// int can be decoded from msgpack type: intXXX or uintXXX
+func (d *msgpackDecDriver) DecodeInt(bitsize uint8) (i int64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ switch d.bd {
+ case mpUint8:
+ i = int64(uint64(d.r.readn1()))
+ case mpUint16:
+ i = int64(uint64(bigen.Uint16(d.r.readx(2))))
+ case mpUint32:
+ i = int64(uint64(bigen.Uint32(d.r.readx(4))))
+ case mpUint64:
+ i = int64(bigen.Uint64(d.r.readx(8)))
+ case mpInt8:
+ i = int64(int8(d.r.readn1()))
+ case mpInt16:
+ i = int64(int16(bigen.Uint16(d.r.readx(2))))
+ case mpInt32:
+ i = int64(int32(bigen.Uint32(d.r.readx(4))))
+ case mpInt64:
+ i = int64(bigen.Uint64(d.r.readx(8)))
+ default:
+ switch {
+ case d.bd >= mpPosFixNumMin && d.bd <= mpPosFixNumMax:
+ i = int64(int8(d.bd))
+ case d.bd >= mpNegFixNumMin && d.bd <= mpNegFixNumMax:
+ i = int64(int8(d.bd))
+ default:
+ d.d.errorf("Unhandled single-byte unsigned integer value: %s: %x", msgBadDesc, d.bd)
+ return
+ }
+ }
+ // check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
+ if bitsize > 0 {
+ if trunc := (i << (64 - bitsize)) >> (64 - bitsize); i != trunc {
+ d.d.errorf("Overflow int value: %v", i)
+ return
+ }
+ }
+ d.bdRead = false
+ return
+}
+
+// uint can be decoded from msgpack type: intXXX or uintXXX
+func (d *msgpackDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ switch d.bd {
+ case mpUint8:
+ ui = uint64(d.r.readn1())
+ case mpUint16:
+ ui = uint64(bigen.Uint16(d.r.readx(2)))
+ case mpUint32:
+ ui = uint64(bigen.Uint32(d.r.readx(4)))
+ case mpUint64:
+ ui = bigen.Uint64(d.r.readx(8))
+ case mpInt8:
+ if i := int64(int8(d.r.readn1())); i >= 0 {
+ ui = uint64(i)
+ } else {
+ d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
+ return
+ }
+ case mpInt16:
+ if i := int64(int16(bigen.Uint16(d.r.readx(2)))); i >= 0 {
+ ui = uint64(i)
+ } else {
+ d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
+ return
+ }
+ case mpInt32:
+ if i := int64(int32(bigen.Uint32(d.r.readx(4)))); i >= 0 {
+ ui = uint64(i)
+ } else {
+ d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
+ return
+ }
+ case mpInt64:
+ if i := int64(bigen.Uint64(d.r.readx(8))); i >= 0 {
+ ui = uint64(i)
+ } else {
+ d.d.errorf("Assigning negative signed value: %v, to unsigned type", i)
+ return
+ }
+ default:
+ switch {
+ case d.bd >= mpPosFixNumMin && d.bd <= mpPosFixNumMax:
+ ui = uint64(d.bd)
+ case d.bd >= mpNegFixNumMin && d.bd <= mpNegFixNumMax:
+ d.d.errorf("Assigning negative signed value: %v, to unsigned type", int(d.bd))
+ return
+ default:
+ d.d.errorf("Unhandled single-byte unsigned integer value: %s: %x", msgBadDesc, d.bd)
+ return
+ }
+ }
+ // check overflow (logic adapted from std pkg reflect/value.go OverflowUint()
+ if bitsize > 0 {
+ if trunc := (ui << (64 - bitsize)) >> (64 - bitsize); ui != trunc {
+ d.d.errorf("Overflow uint value: %v", ui)
+ return
+ }
+ }
+ d.bdRead = false
+ return
+}
+
+// float can either be decoded from msgpack type: float, double or intX
+func (d *msgpackDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == mpFloat {
+ f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
+ } else if d.bd == mpDouble {
+ f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
+ } else {
+ f = float64(d.DecodeInt(0))
+ }
+ if chkOverflow32 && chkOvf.Float32(f) {
+ d.d.errorf("msgpack: float32 overflow: %v", f)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+// bool can be decoded from bool, fixnum 0 or 1.
+func (d *msgpackDecDriver) DecodeBool() (b bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == mpFalse || d.bd == 0 {
+ // b = false
+ } else if d.bd == mpTrue || d.bd == 1 {
+ b = true
+ } else {
+ d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *msgpackDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ var clen int
+ // ignore isstring. Expect that the bytes may be found from msgpackContainerStr or msgpackContainerBin
+ if bd := d.bd; bd == mpBin8 || bd == mpBin16 || bd == mpBin32 {
+ clen = d.readContainerLen(msgpackContainerBin)
+ } else {
+ clen = d.readContainerLen(msgpackContainerStr)
+ }
+ // println("DecodeBytes: clen: ", clen)
+ d.bdRead = false
+ // bytes may be nil, so handle it. if nil, clen=-1.
+ if clen < 0 {
+ return nil
+ }
+ if zerocopy {
+ if d.br {
+ return d.r.readx(clen)
+ } else if len(bs) == 0 {
+ bs = d.b[:]
+ }
+ }
+ return decByteSlice(d.r, clen, bs)
+}
+
+func (d *msgpackDecDriver) DecodeString() (s string) {
+ return string(d.DecodeBytes(d.b[:], true, true))
+}
+
+func (d *msgpackDecDriver) readNextBd() {
+ d.bd = d.r.readn1()
+ d.bdRead = true
+ d.bdType = valueTypeUnset
+}
+
+func (d *msgpackDecDriver) IsContainerType(vt valueType) bool {
+ bd := d.bd
+ switch vt {
+ case valueTypeNil:
+ return bd == mpNil
+ case valueTypeBytes:
+ return bd == mpBin8 || bd == mpBin16 || bd == mpBin32 ||
+ (!d.h.RawToString &&
+ (bd == mpStr8 || bd == mpStr16 || bd == mpStr32 || (bd >= mpFixStrMin && bd <= mpFixStrMax)))
+ case valueTypeString:
+ return d.h.RawToString &&
+ (bd == mpStr8 || bd == mpStr16 || bd == mpStr32 || (bd >= mpFixStrMin && bd <= mpFixStrMax))
+ case valueTypeArray:
+ return bd == mpArray16 || bd == mpArray32 || (bd >= mpFixArrayMin && bd <= mpFixArrayMax)
+ case valueTypeMap:
+ return bd == mpMap16 || bd == mpMap32 || (bd >= mpFixMapMin && bd <= mpFixMapMax)
+ }
+ d.d.errorf("isContainerType: unsupported parameter: %v", vt)
+ return false // "unreachable"
+}
+
+func (d *msgpackDecDriver) TryDecodeAsNil() (v bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == mpNil {
+ d.bdRead = false
+ v = true
+ }
+ return
+}
+
+func (d *msgpackDecDriver) readContainerLen(ct msgpackContainerType) (clen int) {
+ bd := d.bd
+ if bd == mpNil {
+ clen = -1 // to represent nil
+ } else if bd == ct.b8 {
+ clen = int(d.r.readn1())
+ } else if bd == ct.b16 {
+ clen = int(bigen.Uint16(d.r.readx(2)))
+ } else if bd == ct.b32 {
+ clen = int(bigen.Uint32(d.r.readx(4)))
+ } else if (ct.bFixMin & bd) == ct.bFixMin {
+ clen = int(ct.bFixMin ^ bd)
+ } else {
+ d.d.errorf("readContainerLen: %s: hex: %x, decimal: %d", msgBadDesc, bd, bd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *msgpackDecDriver) ReadMapStart() int {
+ return d.readContainerLen(msgpackContainerMap)
+}
+
+func (d *msgpackDecDriver) ReadArrayStart() int {
+ return d.readContainerLen(msgpackContainerList)
+}
+
+func (d *msgpackDecDriver) readExtLen() (clen int) {
+ switch d.bd {
+ case mpNil:
+ clen = -1 // to represent nil
+ case mpFixExt1:
+ clen = 1
+ case mpFixExt2:
+ clen = 2
+ case mpFixExt4:
+ clen = 4
+ case mpFixExt8:
+ clen = 8
+ case mpFixExt16:
+ clen = 16
+ case mpExt8:
+ clen = int(d.r.readn1())
+ case mpExt16:
+ clen = int(bigen.Uint16(d.r.readx(2)))
+ case mpExt32:
+ clen = int(bigen.Uint32(d.r.readx(4)))
+ default:
+ d.d.errorf("decoding ext bytes: found unexpected byte: %x", d.bd)
+ return
+ }
+ return
+}
+
+func (d *msgpackDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
+ if xtag > 0xff {
+ d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
+ return
+ }
+ realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
+ realxtag = uint64(realxtag1)
+ if ext == nil {
+ re := rv.(*RawExt)
+ re.Tag = realxtag
+ re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
+ } else {
+ ext.ReadExt(rv, xbs)
+ }
+ return
+}
+
+func (d *msgpackDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ xbd := d.bd
+ if xbd == mpBin8 || xbd == mpBin16 || xbd == mpBin32 {
+ xbs = d.DecodeBytes(nil, false, true)
+ } else if xbd == mpStr8 || xbd == mpStr16 || xbd == mpStr32 ||
+ (xbd >= mpFixStrMin && xbd <= mpFixStrMax) {
+ xbs = d.DecodeBytes(nil, true, true)
+ } else {
+ clen := d.readExtLen()
+ xtag = d.r.readn1()
+ if verifyTag && xtag != tag {
+ d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
+ return
+ }
+ xbs = d.r.readx(clen)
+ }
+ d.bdRead = false
+ return
+}
+
+//--------------------------------------------------
+
+//MsgpackHandle is a Handle for the Msgpack Schema-Free Encoding Format.
+type MsgpackHandle struct {
+ BasicHandle
+ binaryEncodingType
+
+ // RawToString controls how raw bytes are decoded into a nil interface{}.
+ RawToString bool
+
+ // WriteExt flag supports encoding configured extensions with extension tags.
+ // It also controls whether other elements of the new spec are encoded (ie Str8).
+ //
+ // With WriteExt=false, configured extensions are serialized as raw bytes
+ // and Str8 is not encoded.
+ //
+ // A stream can still be decoded into a typed value, provided an appropriate value
+ // is provided, but the type cannot be inferred from the stream. If no appropriate
+ // type is provided (e.g. decoding into a nil interface{}), you get back
+ // a []byte or string based on the setting of RawToString.
+ WriteExt bool
+}
+
+func (h *MsgpackHandle) newEncDriver(e *Encoder) encDriver {
+ return &msgpackEncDriver{e: e, w: e.w, h: h}
+}
+
+func (h *MsgpackHandle) newDecDriver(d *Decoder) decDriver {
+ return &msgpackDecDriver{d: d, r: d.r, h: h, br: d.bytes}
+}
+
+func (h *MsgpackHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
+ return h.SetExt(rt, tag, &setExtWrapper{b: ext})
+}
+
+//--------------------------------------------------
+
+type msgpackSpecRpcCodec struct {
+ rpcCodec
+}
+
+// /////////////// Spec RPC Codec ///////////////////
+func (c *msgpackSpecRpcCodec) WriteRequest(r *rpc.Request, body interface{}) error {
+ // WriteRequest can write to both a Go service, and other services that do
+ // not abide by the 1 argument rule of a Go service.
+ // We discriminate based on if the body is a MsgpackSpecRpcMultiArgs
+ var bodyArr []interface{}
+ if m, ok := body.(MsgpackSpecRpcMultiArgs); ok {
+ bodyArr = ([]interface{})(m)
+ } else {
+ bodyArr = []interface{}{body}
+ }
+ r2 := []interface{}{0, uint32(r.Seq), r.ServiceMethod, bodyArr}
+ return c.write(r2, nil, false, true)
+}
+
+func (c *msgpackSpecRpcCodec) WriteResponse(r *rpc.Response, body interface{}) error {
+ var moe interface{}
+ if r.Error != "" {
+ moe = r.Error
+ }
+ if moe != nil && body != nil {
+ body = nil
+ }
+ r2 := []interface{}{1, uint32(r.Seq), moe, body}
+ return c.write(r2, nil, false, true)
+}
+
+func (c *msgpackSpecRpcCodec) ReadResponseHeader(r *rpc.Response) error {
+ return c.parseCustomHeader(1, &r.Seq, &r.Error)
+}
+
+func (c *msgpackSpecRpcCodec) ReadRequestHeader(r *rpc.Request) error {
+ return c.parseCustomHeader(0, &r.Seq, &r.ServiceMethod)
+}
+
+func (c *msgpackSpecRpcCodec) ReadRequestBody(body interface{}) error {
+ if body == nil { // read and discard
+ return c.read(nil)
+ }
+ bodyArr := []interface{}{body}
+ return c.read(&bodyArr)
+}
+
+func (c *msgpackSpecRpcCodec) parseCustomHeader(expectTypeByte byte, msgid *uint64, methodOrError *string) (err error) {
+
+ if c.isClosed() {
+ return io.EOF
+ }
+
+ // We read the response header by hand
+ // so that the body can be decoded on its own from the stream at a later time.
+
+ const fia byte = 0x94 //four item array descriptor value
+ // Not sure why the panic of EOF is swallowed above.
+ // if bs1 := c.dec.r.readn1(); bs1 != fia {
+ // err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, bs1)
+ // return
+ // }
+ var b byte
+ b, err = c.br.ReadByte()
+ if err != nil {
+ return
+ }
+ if b != fia {
+ err = fmt.Errorf("Unexpected value for array descriptor: Expecting %v. Received %v", fia, b)
+ return
+ }
+
+ if err = c.read(&b); err != nil {
+ return
+ }
+ if b != expectTypeByte {
+ err = fmt.Errorf("Unexpected byte descriptor in header. Expecting %v. Received %v", expectTypeByte, b)
+ return
+ }
+ if err = c.read(msgid); err != nil {
+ return
+ }
+ if err = c.read(methodOrError); err != nil {
+ return
+ }
+ return
+}
+
+//--------------------------------------------------
+
+// msgpackSpecRpc is the implementation of Rpc that uses custom communication protocol
+// as defined in the msgpack spec at https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md
+type msgpackSpecRpc struct{}
+
+// MsgpackSpecRpc implements Rpc using the communication protocol defined in
+// the msgpack spec at https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md .
+// Its methods (ServerCodec and ClientCodec) return values that implement RpcCodecBuffered.
+var MsgpackSpecRpc msgpackSpecRpc
+
+func (x msgpackSpecRpc) ServerCodec(conn io.ReadWriteCloser, h Handle) rpc.ServerCodec {
+ return &msgpackSpecRpcCodec{newRPCCodec(conn, h)}
+}
+
+func (x msgpackSpecRpc) ClientCodec(conn io.ReadWriteCloser, h Handle) rpc.ClientCodec {
+ return &msgpackSpecRpcCodec{newRPCCodec(conn, h)}
+}
+
+var _ decDriver = (*msgpackDecDriver)(nil)
+var _ encDriver = (*msgpackEncDriver)(nil)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/noop.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/noop.go
new file mode 100644
index 000000000..ca02c6a7e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/noop.go
@@ -0,0 +1,175 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "math/rand"
+ "time"
+)
+
+// NoopHandle returns a no-op handle. It basically does nothing.
+// It is only useful for benchmarking, as it gives an idea of the
+// overhead from the codec framework.
+//
+// LIBRARY USERS: *** DO NOT USE ***
+func NoopHandle(slen int) *noopHandle {
+ h := noopHandle{}
+ h.rand = rand.New(rand.NewSource(time.Now().UnixNano()))
+ h.B = make([][]byte, slen)
+ h.S = make([]string, slen)
+ for i := 0; i < len(h.S); i++ {
+ b := make([]byte, i+1)
+ for j := 0; j < len(b); j++ {
+ b[j] = 'a' + byte(i)
+ }
+ h.B[i] = b
+ h.S[i] = string(b)
+ }
+ return &h
+}
+
+// noopHandle does nothing.
+// It is used to simulate the overhead of the codec framework.
+type noopHandle struct {
+ BasicHandle
+ binaryEncodingType
+ noopDrv // noopDrv is unexported here, so we can get a copy of it when needed.
+}
+
+type noopDrv struct {
+ i int
+ S []string
+ B [][]byte
+ mks []bool // stack. if map (true), else if array (false)
+ mk bool // top of stack. what container are we on? map or array?
+ ct valueType // last request for IsContainerType.
+ cb bool // last response for IsContainerType.
+ rand *rand.Rand
+}
+
+func (h *noopDrv) r(v int) int { return h.rand.Intn(v) }
+func (h *noopDrv) m(v int) int { h.i++; return h.i % v }
+
+func (h *noopDrv) newEncDriver(_ *Encoder) encDriver { return h }
+func (h *noopDrv) newDecDriver(_ *Decoder) decDriver { return h }
+
+// --- encDriver
+
+// stack functions (for map and array)
+func (h *noopDrv) start(b bool) {
+ // println("start", len(h.mks)+1)
+ h.mks = append(h.mks, b)
+ h.mk = b
+}
+func (h *noopDrv) end() {
+ // println("end: ", len(h.mks)-1)
+ h.mks = h.mks[:len(h.mks)-1]
+ if len(h.mks) > 0 {
+ h.mk = h.mks[len(h.mks)-1]
+ } else {
+ h.mk = false
+ }
+}
+
+func (h *noopDrv) EncodeBuiltin(rt uintptr, v interface{}) {}
+func (h *noopDrv) EncodeNil() {}
+func (h *noopDrv) EncodeInt(i int64) {}
+func (h *noopDrv) EncodeUint(i uint64) {}
+func (h *noopDrv) EncodeBool(b bool) {}
+func (h *noopDrv) EncodeFloat32(f float32) {}
+func (h *noopDrv) EncodeFloat64(f float64) {}
+func (h *noopDrv) EncodeRawExt(re *RawExt, e *Encoder) {}
+func (h *noopDrv) EncodeArrayStart(length int) { h.start(true) }
+func (h *noopDrv) EncodeMapStart(length int) { h.start(false) }
+func (h *noopDrv) EncodeEnd() { h.end() }
+
+func (h *noopDrv) EncodeString(c charEncoding, v string) {}
+func (h *noopDrv) EncodeSymbol(v string) {}
+func (h *noopDrv) EncodeStringBytes(c charEncoding, v []byte) {}
+
+func (h *noopDrv) EncodeExt(rv interface{}, xtag uint64, ext Ext, e *Encoder) {}
+
+// ---- decDriver
+func (h *noopDrv) initReadNext() {}
+func (h *noopDrv) CheckBreak() bool { return false }
+func (h *noopDrv) IsBuiltinType(rt uintptr) bool { return false }
+func (h *noopDrv) DecodeBuiltin(rt uintptr, v interface{}) {}
+func (h *noopDrv) DecodeInt(bitsize uint8) (i int64) { return int64(h.m(15)) }
+func (h *noopDrv) DecodeUint(bitsize uint8) (ui uint64) { return uint64(h.m(35)) }
+func (h *noopDrv) DecodeFloat(chkOverflow32 bool) (f float64) { return float64(h.m(95)) }
+func (h *noopDrv) DecodeBool() (b bool) { return h.m(2) == 0 }
+func (h *noopDrv) DecodeString() (s string) { return h.S[h.m(8)] }
+
+// func (h *noopDrv) DecodeStringAsBytes(bs []byte) []byte { return h.DecodeBytes(bs) }
+
+func (h *noopDrv) DecodeBytes(bs []byte, isstring, zerocopy bool) []byte { return h.B[h.m(len(h.B))] }
+
+func (h *noopDrv) ReadEnd() { h.end() }
+
+// toggle map/slice
+func (h *noopDrv) ReadMapStart() int { h.start(true); return h.m(10) }
+func (h *noopDrv) ReadArrayStart() int { h.start(false); return h.m(10) }
+
+func (h *noopDrv) IsContainerType(vt valueType) bool {
+ // return h.m(2) == 0
+ // handle kStruct
+ if h.ct == valueTypeMap && vt == valueTypeArray || h.ct == valueTypeArray && vt == valueTypeMap {
+ h.cb = !h.cb
+ h.ct = vt
+ return h.cb
+ }
+ // go in a loop and check it.
+ h.ct = vt
+ h.cb = h.m(7) == 0
+ return h.cb
+}
+func (h *noopDrv) TryDecodeAsNil() bool {
+ if h.mk {
+ return false
+ } else {
+ return h.m(8) == 0
+ }
+}
+func (h *noopDrv) DecodeExt(rv interface{}, xtag uint64, ext Ext) uint64 {
+ return 0
+}
+
+func (h *noopDrv) DecodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+ // use h.r (random) not h.m() because h.m() could cause the same value to be given.
+ var sk int
+ if h.mk {
+ // if mapkey, do not support values of nil OR bytes, array, map or rawext
+ sk = h.r(7) + 1
+ } else {
+ sk = h.r(12)
+ }
+ switch sk {
+ case 0:
+ vt = valueTypeNil
+ case 1:
+ vt, v = valueTypeBool, false
+ case 2:
+ vt, v = valueTypeBool, true
+ case 3:
+ vt, v = valueTypeInt, h.DecodeInt(64)
+ case 4:
+ vt, v = valueTypeUint, h.DecodeUint(64)
+ case 5:
+ vt, v = valueTypeFloat, h.DecodeFloat(true)
+ case 6:
+ vt, v = valueTypeFloat, h.DecodeFloat(false)
+ case 7:
+ vt, v = valueTypeString, h.DecodeString()
+ case 8:
+ vt, v = valueTypeBytes, h.B[h.m(len(h.B))]
+ case 9:
+ vt, decodeFurther = valueTypeArray, true
+ case 10:
+ vt, decodeFurther = valueTypeMap, true
+ default:
+ vt, v = valueTypeExt, &RawExt{Tag: h.DecodeUint(64), Data: h.B[h.m(len(h.B))]}
+ }
+ h.ct = vt
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.go
new file mode 100644
index 000000000..2353263e8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.go
@@ -0,0 +1,3 @@
+package codec
+
+//go:generate bash prebuild.sh
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh
new file mode 100644
index 000000000..781e35ee3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/prebuild.sh
@@ -0,0 +1,193 @@
+#!/bin/bash
+
+# _needgen is a helper function to tell if we need to generate files for msgp, codecgen.
+_needgen() {
+ local a="$1"
+ zneedgen=0
+ if [[ ! -e "$a" ]]
+ then
+ zneedgen=1
+ echo 1
+ return 0
+ fi
+ for i in `ls -1 *.go.tmpl gen.go values_test.go`
+ do
+ if [[ "$a" -ot "$i" ]]
+ then
+ zneedgen=1
+ echo 1
+ return 0
+ fi
+ done
+ echo 0
+}
+
+# _build generates fast-path.go and gen-helper.go.
+#
+# It is needed because there is some dependency between the generated code
+# and the other classes. Consequently, we have to totally remove the
+# generated files and put stubs in place, before calling "go run" again
+# to recreate them.
+_build() {
+ if ! [[ "${zforce}" == "1" ||
+ "1" == $( _needgen "fast-path.generated.go" ) ||
+ "1" == $( _needgen "gen-helper.generated.go" ) ||
+ "1" == $( _needgen "gen.generated.go" ) ||
+ 1 == 0 ]]
+ then
+ return 0
+ fi
+
+ # echo "Running prebuild"
+ if [ "${zbak}" == "1" ]
+ then
+ # echo "Backing up old generated files"
+ _zts=`date '+%m%d%Y_%H%M%S'`
+ _gg=".generated.go"
+ [ -e "gen-helper${_gg}" ] && mv gen-helper${_gg} gen-helper${_gg}__${_zts}.bak
+ [ -e "fast-path${_gg}" ] && mv fast-path${_gg} fast-path${_gg}__${_zts}.bak
+ # [ -e "safe${_gg}" ] && mv safe${_gg} safe${_gg}__${_zts}.bak
+ # [ -e "unsafe${_gg}" ] && mv unsafe${_gg} unsafe${_gg}__${_zts}.bak
+ else
+ rm -f fast-path.generated.go gen.generated.go gen-helper.generated.go *safe.generated.go *_generated_test.go *.generated_ffjson_expose.go
+ fi
+
+ cat > gen.generated.go <> gen.generated.go < gen-dec-map.go.tmpl
+
+ cat >> gen.generated.go <> gen.generated.go < gen-dec-array.go.tmpl
+
+ cat >> gen.generated.go < fast-path.generated.go < gen-from-tmpl.generated.go < math.MaxInt64 {
+ // d.d.errorf("decIntAny: Integer out of range for signed int64: %v", ui)
+ // return
+ // }
+ return
+}
+
+func (d *simpleDecDriver) DecodeInt(bitsize uint8) (i int64) {
+ ui, neg := d.decCheckInteger()
+ i, overflow := chkOvf.SignedInt(ui)
+ if overflow {
+ d.d.errorf("simple: overflow converting %v to signed integer", ui)
+ return
+ }
+ if neg {
+ i = -i
+ }
+ if chkOvf.Int(i, bitsize) {
+ d.d.errorf("simple: overflow integer: %v", i)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *simpleDecDriver) DecodeUint(bitsize uint8) (ui uint64) {
+ ui, neg := d.decCheckInteger()
+ if neg {
+ d.d.errorf("Assigning negative signed value to unsigned type")
+ return
+ }
+ if chkOvf.Uint(ui, bitsize) {
+ d.d.errorf("simple: overflow integer: %v", ui)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *simpleDecDriver) DecodeFloat(chkOverflow32 bool) (f float64) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == simpleVdFloat32 {
+ f = float64(math.Float32frombits(bigen.Uint32(d.r.readx(4))))
+ } else if d.bd == simpleVdFloat64 {
+ f = math.Float64frombits(bigen.Uint64(d.r.readx(8)))
+ } else {
+ if d.bd >= simpleVdPosInt && d.bd <= simpleVdNegInt+3 {
+ f = float64(d.DecodeInt(64))
+ } else {
+ d.d.errorf("Float only valid from float32/64: Invalid descriptor: %v", d.bd)
+ return
+ }
+ }
+ if chkOverflow32 && chkOvf.Float32(f) {
+ d.d.errorf("msgpack: float32 overflow: %v", f)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+// bool can be decoded from bool only (single byte).
+func (d *simpleDecDriver) DecodeBool() (b bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == simpleVdTrue {
+ b = true
+ } else if d.bd == simpleVdFalse {
+ } else {
+ d.d.errorf("Invalid single-byte value for bool: %s: %x", msgBadDesc, d.bd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *simpleDecDriver) ReadMapStart() (length int) {
+ d.bdRead = false
+ return d.decLen()
+}
+
+func (d *simpleDecDriver) ReadArrayStart() (length int) {
+ d.bdRead = false
+ return d.decLen()
+}
+
+func (d *simpleDecDriver) decLen() int {
+ switch d.bd % 8 {
+ case 0:
+ return 0
+ case 1:
+ return int(d.r.readn1())
+ case 2:
+ return int(bigen.Uint16(d.r.readx(2)))
+ case 3:
+ ui := uint64(bigen.Uint32(d.r.readx(4)))
+ if chkOvf.Uint(ui, intBitsize) {
+ d.d.errorf("simple: overflow integer: %v", ui)
+ return 0
+ }
+ return int(ui)
+ case 4:
+ ui := bigen.Uint64(d.r.readx(8))
+ if chkOvf.Uint(ui, intBitsize) {
+ d.d.errorf("simple: overflow integer: %v", ui)
+ return 0
+ }
+ return int(ui)
+ }
+ d.d.errorf("decLen: Cannot read length: bd%8 must be in range 0..4. Got: %d", d.bd%8)
+ return -1
+}
+
+func (d *simpleDecDriver) DecodeString() (s string) {
+ return string(d.DecodeBytes(d.b[:], true, true))
+}
+
+func (d *simpleDecDriver) DecodeBytes(bs []byte, isstring, zerocopy bool) (bsOut []byte) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ if d.bd == simpleVdNil {
+ d.bdRead = false
+ return
+ }
+ clen := d.decLen()
+ d.bdRead = false
+ if zerocopy {
+ if d.br {
+ return d.r.readx(clen)
+ } else if len(bs) == 0 {
+ bs = d.b[:]
+ }
+ }
+ return decByteSlice(d.r, clen, bs)
+}
+
+func (d *simpleDecDriver) DecodeExt(rv interface{}, xtag uint64, ext Ext) (realxtag uint64) {
+ if xtag > 0xff {
+ d.d.errorf("decodeExt: tag must be <= 0xff; got: %v", xtag)
+ return
+ }
+ realxtag1, xbs := d.decodeExtV(ext != nil, uint8(xtag))
+ realxtag = uint64(realxtag1)
+ if ext == nil {
+ re := rv.(*RawExt)
+ re.Tag = realxtag
+ re.Data = detachZeroCopyBytes(d.br, re.Data, xbs)
+ } else {
+ ext.ReadExt(rv, xbs)
+ }
+ return
+}
+
+func (d *simpleDecDriver) decodeExtV(verifyTag bool, tag byte) (xtag byte, xbs []byte) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+ switch d.bd {
+ case simpleVdExt, simpleVdExt + 1, simpleVdExt + 2, simpleVdExt + 3, simpleVdExt + 4:
+ l := d.decLen()
+ xtag = d.r.readn1()
+ if verifyTag && xtag != tag {
+ d.d.errorf("Wrong extension tag. Got %b. Expecting: %v", xtag, tag)
+ return
+ }
+ xbs = d.r.readx(l)
+ case simpleVdByteArray, simpleVdByteArray + 1, simpleVdByteArray + 2, simpleVdByteArray + 3, simpleVdByteArray + 4:
+ xbs = d.DecodeBytes(nil, false, true)
+ default:
+ d.d.errorf("Invalid d.bd for extensions (Expecting extensions or byte array). Got: 0x%x", d.bd)
+ return
+ }
+ d.bdRead = false
+ return
+}
+
+func (d *simpleDecDriver) DecodeNaked() (v interface{}, vt valueType, decodeFurther bool) {
+ if !d.bdRead {
+ d.readNextBd()
+ }
+
+ switch d.bd {
+ case simpleVdNil:
+ vt = valueTypeNil
+ case simpleVdFalse:
+ vt = valueTypeBool
+ v = false
+ case simpleVdTrue:
+ vt = valueTypeBool
+ v = true
+ case simpleVdPosInt, simpleVdPosInt + 1, simpleVdPosInt + 2, simpleVdPosInt + 3:
+ if d.h.SignedInteger {
+ vt = valueTypeInt
+ v = d.DecodeInt(64)
+ } else {
+ vt = valueTypeUint
+ v = d.DecodeUint(64)
+ }
+ case simpleVdNegInt, simpleVdNegInt + 1, simpleVdNegInt + 2, simpleVdNegInt + 3:
+ vt = valueTypeInt
+ v = d.DecodeInt(64)
+ case simpleVdFloat32:
+ vt = valueTypeFloat
+ v = d.DecodeFloat(true)
+ case simpleVdFloat64:
+ vt = valueTypeFloat
+ v = d.DecodeFloat(false)
+ case simpleVdString, simpleVdString + 1, simpleVdString + 2, simpleVdString + 3, simpleVdString + 4:
+ vt = valueTypeString
+ v = d.DecodeString()
+ case simpleVdByteArray, simpleVdByteArray + 1, simpleVdByteArray + 2, simpleVdByteArray + 3, simpleVdByteArray + 4:
+ vt = valueTypeBytes
+ v = d.DecodeBytes(nil, false, false)
+ case simpleVdExt, simpleVdExt + 1, simpleVdExt + 2, simpleVdExt + 3, simpleVdExt + 4:
+ vt = valueTypeExt
+ l := d.decLen()
+ var re RawExt
+ re.Tag = uint64(d.r.readn1())
+ re.Data = d.r.readx(l)
+ v = &re
+ case simpleVdArray, simpleVdArray + 1, simpleVdArray + 2, simpleVdArray + 3, simpleVdArray + 4:
+ vt = valueTypeArray
+ decodeFurther = true
+ case simpleVdMap, simpleVdMap + 1, simpleVdMap + 2, simpleVdMap + 3, simpleVdMap + 4:
+ vt = valueTypeMap
+ decodeFurther = true
+ default:
+ d.d.errorf("decodeNaked: Unrecognized d.bd: 0x%x", d.bd)
+ return
+ }
+
+ if !decodeFurther {
+ d.bdRead = false
+ }
+ return
+}
+
+//------------------------------------
+
+// SimpleHandle is a Handle for a very simple encoding format.
+//
+// simple is a simplistic codec similar to binc, but not as compact.
+// - Encoding of a value is always preceeded by the descriptor byte (bd)
+// - True, false, nil are encoded fully in 1 byte (the descriptor)
+// - Integers (intXXX, uintXXX) are encoded in 1, 2, 4 or 8 bytes (plus a descriptor byte).
+// There are positive (uintXXX and intXXX >= 0) and negative (intXXX < 0) integers.
+// - Floats are encoded in 4 or 8 bytes (plus a descriptor byte)
+// - Lenght of containers (strings, bytes, array, map, extensions)
+// are encoded in 0, 1, 2, 4 or 8 bytes.
+// Zero-length containers have no length encoded.
+// For others, the number of bytes is given by pow(2, bd%3)
+// - maps are encoded as [bd] [length] [[key][value]]...
+// - arrays are encoded as [bd] [length] [value]...
+// - extensions are encoded as [bd] [length] [tag] [byte]...
+// - strings/bytearrays are encoded as [bd] [length] [byte]...
+//
+// The full spec will be published soon.
+type SimpleHandle struct {
+ BasicHandle
+ binaryEncodingType
+}
+
+func (h *SimpleHandle) newEncDriver(e *Encoder) encDriver {
+ return &simpleEncDriver{e: e, w: e.w, h: h}
+}
+
+func (h *SimpleHandle) newDecDriver(d *Decoder) decDriver {
+ return &simpleDecDriver{d: d, r: d.r, h: h, br: d.bytes}
+}
+
+func (h *SimpleHandle) SetBytesExt(rt reflect.Type, tag uint64, ext BytesExt) (err error) {
+ return h.SetExt(rt, tag, &setExtWrapper{b: ext})
+}
+
+var _ decDriver = (*simpleDecDriver)(nil)
+var _ encDriver = (*simpleEncDriver)(nil)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/test-cbor-goldens.json b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/test-cbor-goldens.json
new file mode 100644
index 000000000..902858671
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/test-cbor-goldens.json
@@ -0,0 +1,639 @@
+[
+ {
+ "cbor": "AA==",
+ "hex": "00",
+ "roundtrip": true,
+ "decoded": 0
+ },
+ {
+ "cbor": "AQ==",
+ "hex": "01",
+ "roundtrip": true,
+ "decoded": 1
+ },
+ {
+ "cbor": "Cg==",
+ "hex": "0a",
+ "roundtrip": true,
+ "decoded": 10
+ },
+ {
+ "cbor": "Fw==",
+ "hex": "17",
+ "roundtrip": true,
+ "decoded": 23
+ },
+ {
+ "cbor": "GBg=",
+ "hex": "1818",
+ "roundtrip": true,
+ "decoded": 24
+ },
+ {
+ "cbor": "GBk=",
+ "hex": "1819",
+ "roundtrip": true,
+ "decoded": 25
+ },
+ {
+ "cbor": "GGQ=",
+ "hex": "1864",
+ "roundtrip": true,
+ "decoded": 100
+ },
+ {
+ "cbor": "GQPo",
+ "hex": "1903e8",
+ "roundtrip": true,
+ "decoded": 1000
+ },
+ {
+ "cbor": "GgAPQkA=",
+ "hex": "1a000f4240",
+ "roundtrip": true,
+ "decoded": 1000000
+ },
+ {
+ "cbor": "GwAAAOjUpRAA",
+ "hex": "1b000000e8d4a51000",
+ "roundtrip": true,
+ "decoded": 1000000000000
+ },
+ {
+ "cbor": "G///////////",
+ "hex": "1bffffffffffffffff",
+ "roundtrip": true,
+ "decoded": 18446744073709551615
+ },
+ {
+ "cbor": "wkkBAAAAAAAAAAA=",
+ "hex": "c249010000000000000000",
+ "roundtrip": true,
+ "decoded": 18446744073709551616
+ },
+ {
+ "cbor": "O///////////",
+ "hex": "3bffffffffffffffff",
+ "roundtrip": true,
+ "decoded": -18446744073709551616,
+ "skip": true
+ },
+ {
+ "cbor": "w0kBAAAAAAAAAAA=",
+ "hex": "c349010000000000000000",
+ "roundtrip": true,
+ "decoded": -18446744073709551617
+ },
+ {
+ "cbor": "IA==",
+ "hex": "20",
+ "roundtrip": true,
+ "decoded": -1
+ },
+ {
+ "cbor": "KQ==",
+ "hex": "29",
+ "roundtrip": true,
+ "decoded": -10
+ },
+ {
+ "cbor": "OGM=",
+ "hex": "3863",
+ "roundtrip": true,
+ "decoded": -100
+ },
+ {
+ "cbor": "OQPn",
+ "hex": "3903e7",
+ "roundtrip": true,
+ "decoded": -1000
+ },
+ {
+ "cbor": "+QAA",
+ "hex": "f90000",
+ "roundtrip": true,
+ "decoded": 0.0
+ },
+ {
+ "cbor": "+YAA",
+ "hex": "f98000",
+ "roundtrip": true,
+ "decoded": -0.0
+ },
+ {
+ "cbor": "+TwA",
+ "hex": "f93c00",
+ "roundtrip": true,
+ "decoded": 1.0
+ },
+ {
+ "cbor": "+z/xmZmZmZma",
+ "hex": "fb3ff199999999999a",
+ "roundtrip": true,
+ "decoded": 1.1
+ },
+ {
+ "cbor": "+T4A",
+ "hex": "f93e00",
+ "roundtrip": true,
+ "decoded": 1.5
+ },
+ {
+ "cbor": "+Xv/",
+ "hex": "f97bff",
+ "roundtrip": true,
+ "decoded": 65504.0
+ },
+ {
+ "cbor": "+kfDUAA=",
+ "hex": "fa47c35000",
+ "roundtrip": true,
+ "decoded": 100000.0
+ },
+ {
+ "cbor": "+n9///8=",
+ "hex": "fa7f7fffff",
+ "roundtrip": true,
+ "decoded": 3.4028234663852886e+38
+ },
+ {
+ "cbor": "+3435DyIAHWc",
+ "hex": "fb7e37e43c8800759c",
+ "roundtrip": true,
+ "decoded": 1.0e+300
+ },
+ {
+ "cbor": "+QAB",
+ "hex": "f90001",
+ "roundtrip": true,
+ "decoded": 5.960464477539063e-08
+ },
+ {
+ "cbor": "+QQA",
+ "hex": "f90400",
+ "roundtrip": true,
+ "decoded": 6.103515625e-05
+ },
+ {
+ "cbor": "+cQA",
+ "hex": "f9c400",
+ "roundtrip": true,
+ "decoded": -4.0
+ },
+ {
+ "cbor": "+8AQZmZmZmZm",
+ "hex": "fbc010666666666666",
+ "roundtrip": true,
+ "decoded": -4.1
+ },
+ {
+ "cbor": "+XwA",
+ "hex": "f97c00",
+ "roundtrip": true,
+ "diagnostic": "Infinity"
+ },
+ {
+ "cbor": "+X4A",
+ "hex": "f97e00",
+ "roundtrip": true,
+ "diagnostic": "NaN"
+ },
+ {
+ "cbor": "+fwA",
+ "hex": "f9fc00",
+ "roundtrip": true,
+ "diagnostic": "-Infinity"
+ },
+ {
+ "cbor": "+n+AAAA=",
+ "hex": "fa7f800000",
+ "roundtrip": false,
+ "diagnostic": "Infinity"
+ },
+ {
+ "cbor": "+n/AAAA=",
+ "hex": "fa7fc00000",
+ "roundtrip": false,
+ "diagnostic": "NaN"
+ },
+ {
+ "cbor": "+v+AAAA=",
+ "hex": "faff800000",
+ "roundtrip": false,
+ "diagnostic": "-Infinity"
+ },
+ {
+ "cbor": "+3/wAAAAAAAA",
+ "hex": "fb7ff0000000000000",
+ "roundtrip": false,
+ "diagnostic": "Infinity"
+ },
+ {
+ "cbor": "+3/4AAAAAAAA",
+ "hex": "fb7ff8000000000000",
+ "roundtrip": false,
+ "diagnostic": "NaN"
+ },
+ {
+ "cbor": "+//wAAAAAAAA",
+ "hex": "fbfff0000000000000",
+ "roundtrip": false,
+ "diagnostic": "-Infinity"
+ },
+ {
+ "cbor": "9A==",
+ "hex": "f4",
+ "roundtrip": true,
+ "decoded": false
+ },
+ {
+ "cbor": "9Q==",
+ "hex": "f5",
+ "roundtrip": true,
+ "decoded": true
+ },
+ {
+ "cbor": "9g==",
+ "hex": "f6",
+ "roundtrip": true,
+ "decoded": null
+ },
+ {
+ "cbor": "9w==",
+ "hex": "f7",
+ "roundtrip": true,
+ "diagnostic": "undefined"
+ },
+ {
+ "cbor": "8A==",
+ "hex": "f0",
+ "roundtrip": true,
+ "diagnostic": "simple(16)"
+ },
+ {
+ "cbor": "+Bg=",
+ "hex": "f818",
+ "roundtrip": true,
+ "diagnostic": "simple(24)"
+ },
+ {
+ "cbor": "+P8=",
+ "hex": "f8ff",
+ "roundtrip": true,
+ "diagnostic": "simple(255)"
+ },
+ {
+ "cbor": "wHQyMDEzLTAzLTIxVDIwOjA0OjAwWg==",
+ "hex": "c074323031332d30332d32315432303a30343a30305a",
+ "roundtrip": true,
+ "diagnostic": "0(\"2013-03-21T20:04:00Z\")"
+ },
+ {
+ "cbor": "wRpRS2ew",
+ "hex": "c11a514b67b0",
+ "roundtrip": true,
+ "diagnostic": "1(1363896240)"
+ },
+ {
+ "cbor": "wftB1FLZ7CAAAA==",
+ "hex": "c1fb41d452d9ec200000",
+ "roundtrip": true,
+ "diagnostic": "1(1363896240.5)"
+ },
+ {
+ "cbor": "10QBAgME",
+ "hex": "d74401020304",
+ "roundtrip": true,
+ "diagnostic": "23(h'01020304')"
+ },
+ {
+ "cbor": "2BhFZElFVEY=",
+ "hex": "d818456449455446",
+ "roundtrip": true,
+ "diagnostic": "24(h'6449455446')"
+ },
+ {
+ "cbor": "2CB2aHR0cDovL3d3dy5leGFtcGxlLmNvbQ==",
+ "hex": "d82076687474703a2f2f7777772e6578616d706c652e636f6d",
+ "roundtrip": true,
+ "diagnostic": "32(\"http://www.example.com\")"
+ },
+ {
+ "cbor": "QA==",
+ "hex": "40",
+ "roundtrip": true,
+ "diagnostic": "h''"
+ },
+ {
+ "cbor": "RAECAwQ=",
+ "hex": "4401020304",
+ "roundtrip": true,
+ "diagnostic": "h'01020304'"
+ },
+ {
+ "cbor": "YA==",
+ "hex": "60",
+ "roundtrip": true,
+ "decoded": ""
+ },
+ {
+ "cbor": "YWE=",
+ "hex": "6161",
+ "roundtrip": true,
+ "decoded": "a"
+ },
+ {
+ "cbor": "ZElFVEY=",
+ "hex": "6449455446",
+ "roundtrip": true,
+ "decoded": "IETF"
+ },
+ {
+ "cbor": "YiJc",
+ "hex": "62225c",
+ "roundtrip": true,
+ "decoded": "\"\\"
+ },
+ {
+ "cbor": "YsO8",
+ "hex": "62c3bc",
+ "roundtrip": true,
+ "decoded": "ü"
+ },
+ {
+ "cbor": "Y+awtA==",
+ "hex": "63e6b0b4",
+ "roundtrip": true,
+ "decoded": "水"
+ },
+ {
+ "cbor": "ZPCQhZE=",
+ "hex": "64f0908591",
+ "roundtrip": true,
+ "decoded": "𐅑"
+ },
+ {
+ "cbor": "gA==",
+ "hex": "80",
+ "roundtrip": true,
+ "decoded": [
+
+ ]
+ },
+ {
+ "cbor": "gwECAw==",
+ "hex": "83010203",
+ "roundtrip": true,
+ "decoded": [
+ 1,
+ 2,
+ 3
+ ]
+ },
+ {
+ "cbor": "gwGCAgOCBAU=",
+ "hex": "8301820203820405",
+ "roundtrip": true,
+ "decoded": [
+ 1,
+ [
+ 2,
+ 3
+ ],
+ [
+ 4,
+ 5
+ ]
+ ]
+ },
+ {
+ "cbor": "mBkBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgYGBk=",
+ "hex": "98190102030405060708090a0b0c0d0e0f101112131415161718181819",
+ "roundtrip": true,
+ "decoded": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25
+ ]
+ },
+ {
+ "cbor": "oA==",
+ "hex": "a0",
+ "roundtrip": true,
+ "decoded": {
+ }
+ },
+ {
+ "cbor": "ogECAwQ=",
+ "hex": "a201020304",
+ "roundtrip": true,
+ "skip": true,
+ "diagnostic": "{1: 2, 3: 4}"
+ },
+ {
+ "cbor": "omFhAWFiggID",
+ "hex": "a26161016162820203",
+ "roundtrip": true,
+ "decoded": {
+ "a": 1,
+ "b": [
+ 2,
+ 3
+ ]
+ }
+ },
+ {
+ "cbor": "gmFhoWFiYWM=",
+ "hex": "826161a161626163",
+ "roundtrip": true,
+ "decoded": [
+ "a",
+ {
+ "b": "c"
+ }
+ ]
+ },
+ {
+ "cbor": "pWFhYUFhYmFCYWNhQ2FkYURhZWFF",
+ "hex": "a56161614161626142616361436164614461656145",
+ "roundtrip": true,
+ "decoded": {
+ "a": "A",
+ "b": "B",
+ "c": "C",
+ "d": "D",
+ "e": "E"
+ }
+ },
+ {
+ "cbor": "X0IBAkMDBAX/",
+ "hex": "5f42010243030405ff",
+ "roundtrip": false,
+ "skip": true,
+ "diagnostic": "(_ h'0102', h'030405')"
+ },
+ {
+ "cbor": "f2VzdHJlYWRtaW5n/w==",
+ "hex": "7f657374726561646d696e67ff",
+ "roundtrip": false,
+ "decoded": "streaming"
+ },
+ {
+ "cbor": "n/8=",
+ "hex": "9fff",
+ "roundtrip": false,
+ "decoded": [
+
+ ]
+ },
+ {
+ "cbor": "nwGCAgOfBAX//w==",
+ "hex": "9f018202039f0405ffff",
+ "roundtrip": false,
+ "decoded": [
+ 1,
+ [
+ 2,
+ 3
+ ],
+ [
+ 4,
+ 5
+ ]
+ ]
+ },
+ {
+ "cbor": "nwGCAgOCBAX/",
+ "hex": "9f01820203820405ff",
+ "roundtrip": false,
+ "decoded": [
+ 1,
+ [
+ 2,
+ 3
+ ],
+ [
+ 4,
+ 5
+ ]
+ ]
+ },
+ {
+ "cbor": "gwGCAgOfBAX/",
+ "hex": "83018202039f0405ff",
+ "roundtrip": false,
+ "decoded": [
+ 1,
+ [
+ 2,
+ 3
+ ],
+ [
+ 4,
+ 5
+ ]
+ ]
+ },
+ {
+ "cbor": "gwGfAgP/ggQF",
+ "hex": "83019f0203ff820405",
+ "roundtrip": false,
+ "decoded": [
+ 1,
+ [
+ 2,
+ 3
+ ],
+ [
+ 4,
+ 5
+ ]
+ ]
+ },
+ {
+ "cbor": "nwECAwQFBgcICQoLDA0ODxAREhMUFRYXGBgYGf8=",
+ "hex": "9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff",
+ "roundtrip": false,
+ "decoded": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25
+ ]
+ },
+ {
+ "cbor": "v2FhAWFinwID//8=",
+ "hex": "bf61610161629f0203ffff",
+ "roundtrip": false,
+ "decoded": {
+ "a": 1,
+ "b": [
+ 2,
+ 3
+ ]
+ }
+ },
+ {
+ "cbor": "gmFhv2FiYWP/",
+ "hex": "826161bf61626163ff",
+ "roundtrip": false,
+ "decoded": [
+ "a",
+ {
+ "b": "c"
+ }
+ ]
+ },
+ {
+ "cbor": "v2NGdW71Y0FtdCH/",
+ "hex": "bf6346756ef563416d7421ff",
+ "roundtrip": false,
+ "decoded": {
+ "Fun": true,
+ "Amt": -2
+ }
+ }
+]
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py
new file mode 100644
index 000000000..dfe3b0c9a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/test.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+# This will create golden files in a directory passed to it.
+# A Test calls this internally to create the golden files
+# So it can process them (so we don't have to checkin the files).
+
+# Ensure msgpack-python and cbor are installed first, using:
+# sudo apt-get install python-dev
+# sudo apt-get install python-pip
+# pip install --user msgpack-python msgpack-rpc-python cbor
+
+import cbor, msgpack, msgpackrpc, sys, os, threading
+
+def get_test_data_list():
+ # get list with all primitive types, and a combo type
+ l0 = [
+ -8,
+ -1616,
+ -32323232,
+ -6464646464646464,
+ 192,
+ 1616,
+ 32323232,
+ 6464646464646464,
+ 192,
+ -3232.0,
+ -6464646464.0,
+ 3232.0,
+ 6464646464.0,
+ False,
+ True,
+ None,
+ u"someday",
+ u"",
+ u"bytestring",
+ 1328176922000002000,
+ -2206187877999998000,
+ 270,
+ -2013855847999995777,
+ #-6795364578871345152,
+ ]
+ l1 = [
+ { "true": True,
+ "false": False },
+ { "true": "True",
+ "false": False,
+ "uint16(1616)": 1616 },
+ { "list": [1616, 32323232, True, -3232.0, {"TRUE":True, "FALSE":False}, [True, False] ],
+ "int32":32323232, "bool": True,
+ "LONG STRING": "123456789012345678901234567890123456789012345678901234567890",
+ "SHORT STRING": "1234567890" },
+ { True: "true", 8: False, "false": 0 }
+ ]
+
+ l = []
+ l.extend(l0)
+ l.append(l0)
+ l.extend(l1)
+ return l
+
+def build_test_data(destdir):
+ l = get_test_data_list()
+ for i in range(len(l)):
+ # packer = msgpack.Packer()
+ serialized = msgpack.dumps(l[i])
+ f = open(os.path.join(destdir, str(i) + '.msgpack.golden'), 'wb')
+ f.write(serialized)
+ f.close()
+ serialized = cbor.dumps(l[i])
+ f = open(os.path.join(destdir, str(i) + '.cbor.golden'), 'wb')
+ f.write(serialized)
+ f.close()
+
+def doRpcServer(port, stopTimeSec):
+ class EchoHandler(object):
+ def Echo123(self, msg1, msg2, msg3):
+ return ("1:%s 2:%s 3:%s" % (msg1, msg2, msg3))
+ def EchoStruct(self, msg):
+ return ("%s" % msg)
+
+ addr = msgpackrpc.Address('localhost', port)
+ server = msgpackrpc.Server(EchoHandler())
+ server.listen(addr)
+ # run thread to stop it after stopTimeSec seconds if > 0
+ if stopTimeSec > 0:
+ def myStopRpcServer():
+ server.stop()
+ t = threading.Timer(stopTimeSec, myStopRpcServer)
+ t.start()
+ server.start()
+
+def doRpcClientToPythonSvc(port):
+ address = msgpackrpc.Address('localhost', port)
+ client = msgpackrpc.Client(address, unpack_encoding='utf-8')
+ print client.call("Echo123", "A1", "B2", "C3")
+ print client.call("EchoStruct", {"A" :"Aa", "B":"Bb", "C":"Cc"})
+
+def doRpcClientToGoSvc(port):
+ # print ">>>> port: ", port, " <<<<<"
+ address = msgpackrpc.Address('localhost', port)
+ client = msgpackrpc.Client(address, unpack_encoding='utf-8')
+ print client.call("TestRpcInt.Echo123", ["A1", "B2", "C3"])
+ print client.call("TestRpcInt.EchoStruct", {"A" :"Aa", "B":"Bb", "C":"Cc"})
+
+def doMain(args):
+ if len(args) == 2 and args[0] == "testdata":
+ build_test_data(args[1])
+ elif len(args) == 3 and args[0] == "rpc-server":
+ doRpcServer(int(args[1]), int(args[2]))
+ elif len(args) == 2 and args[0] == "rpc-client-python-service":
+ doRpcClientToPythonSvc(int(args[1]))
+ elif len(args) == 2 and args[0] == "rpc-client-go-service":
+ doRpcClientToGoSvc(int(args[1]))
+ else:
+ print("Usage: test.py " +
+ "[testdata|rpc-server|rpc-client-python-service|rpc-client-go-service] ...")
+
+if __name__ == "__main__":
+ doMain(sys.argv[1:])
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go
new file mode 100644
index 000000000..733fc3fb7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/time.go
@@ -0,0 +1,193 @@
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+import (
+ "time"
+)
+
+var (
+ timeDigits = [...]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
+)
+
+// EncodeTime encodes a time.Time as a []byte, including
+// information on the instant in time and UTC offset.
+//
+// Format Description
+//
+// A timestamp is composed of 3 components:
+//
+// - secs: signed integer representing seconds since unix epoch
+// - nsces: unsigned integer representing fractional seconds as a
+// nanosecond offset within secs, in the range 0 <= nsecs < 1e9
+// - tz: signed integer representing timezone offset in minutes east of UTC,
+// and a dst (daylight savings time) flag
+//
+// When encoding a timestamp, the first byte is the descriptor, which
+// defines which components are encoded and how many bytes are used to
+// encode secs and nsecs components. *If secs/nsecs is 0 or tz is UTC, it
+// is not encoded in the byte array explicitly*.
+//
+// Descriptor 8 bits are of the form `A B C DDD EE`:
+// A: Is secs component encoded? 1 = true
+// B: Is nsecs component encoded? 1 = true
+// C: Is tz component encoded? 1 = true
+// DDD: Number of extra bytes for secs (range 0-7).
+// If A = 1, secs encoded in DDD+1 bytes.
+// If A = 0, secs is not encoded, and is assumed to be 0.
+// If A = 1, then we need at least 1 byte to encode secs.
+// DDD says the number of extra bytes beyond that 1.
+// E.g. if DDD=0, then secs is represented in 1 byte.
+// if DDD=2, then secs is represented in 3 bytes.
+// EE: Number of extra bytes for nsecs (range 0-3).
+// If B = 1, nsecs encoded in EE+1 bytes (similar to secs/DDD above)
+//
+// Following the descriptor bytes, subsequent bytes are:
+//
+// secs component encoded in `DDD + 1` bytes (if A == 1)
+// nsecs component encoded in `EE + 1` bytes (if B == 1)
+// tz component encoded in 2 bytes (if C == 1)
+//
+// secs and nsecs components are integers encoded in a BigEndian
+// 2-complement encoding format.
+//
+// tz component is encoded as 2 bytes (16 bits). Most significant bit 15 to
+// Least significant bit 0 are described below:
+//
+// Timezone offset has a range of -12:00 to +14:00 (ie -720 to +840 minutes).
+// Bit 15 = have\_dst: set to 1 if we set the dst flag.
+// Bit 14 = dst\_on: set to 1 if dst is in effect at the time, or 0 if not.
+// Bits 13..0 = timezone offset in minutes. It is a signed integer in Big Endian format.
+//
+func encodeTime(t time.Time) []byte {
+ //t := rv.Interface().(time.Time)
+ tsecs, tnsecs := t.Unix(), t.Nanosecond()
+ var (
+ bd byte
+ btmp [8]byte
+ bs [16]byte
+ i int = 1
+ )
+ l := t.Location()
+ if l == time.UTC {
+ l = nil
+ }
+ if tsecs != 0 {
+ bd = bd | 0x80
+ bigen.PutUint64(btmp[:], uint64(tsecs))
+ f := pruneSignExt(btmp[:], tsecs >= 0)
+ bd = bd | (byte(7-f) << 2)
+ copy(bs[i:], btmp[f:])
+ i = i + (8 - f)
+ }
+ if tnsecs != 0 {
+ bd = bd | 0x40
+ bigen.PutUint32(btmp[:4], uint32(tnsecs))
+ f := pruneSignExt(btmp[:4], true)
+ bd = bd | byte(3-f)
+ copy(bs[i:], btmp[f:4])
+ i = i + (4 - f)
+ }
+ if l != nil {
+ bd = bd | 0x20
+ // Note that Go Libs do not give access to dst flag.
+ _, zoneOffset := t.Zone()
+ //zoneName, zoneOffset := t.Zone()
+ zoneOffset /= 60
+ z := uint16(zoneOffset)
+ bigen.PutUint16(btmp[:2], z)
+ // clear dst flags
+ bs[i] = btmp[0] & 0x3f
+ bs[i+1] = btmp[1]
+ i = i + 2
+ }
+ bs[0] = bd
+ return bs[0:i]
+}
+
+// DecodeTime decodes a []byte into a time.Time.
+func decodeTime(bs []byte) (tt time.Time, err error) {
+ bd := bs[0]
+ var (
+ tsec int64
+ tnsec uint32
+ tz uint16
+ i byte = 1
+ i2 byte
+ n byte
+ )
+ if bd&(1<<7) != 0 {
+ var btmp [8]byte
+ n = ((bd >> 2) & 0x7) + 1
+ i2 = i + n
+ copy(btmp[8-n:], bs[i:i2])
+ //if first bit of bs[i] is set, then fill btmp[0..8-n] with 0xff (ie sign extend it)
+ if bs[i]&(1<<7) != 0 {
+ copy(btmp[0:8-n], bsAll0xff)
+ //for j,k := byte(0), 8-n; j < k; j++ { btmp[j] = 0xff }
+ }
+ i = i2
+ tsec = int64(bigen.Uint64(btmp[:]))
+ }
+ if bd&(1<<6) != 0 {
+ var btmp [4]byte
+ n = (bd & 0x3) + 1
+ i2 = i + n
+ copy(btmp[4-n:], bs[i:i2])
+ i = i2
+ tnsec = bigen.Uint32(btmp[:])
+ }
+ if bd&(1<<5) == 0 {
+ tt = time.Unix(tsec, int64(tnsec)).UTC()
+ return
+ }
+ // In stdlib time.Parse, when a date is parsed without a zone name, it uses "" as zone name.
+ // However, we need name here, so it can be shown when time is printed.
+ // Zone name is in form: UTC-08:00.
+ // Note that Go Libs do not give access to dst flag, so we ignore dst bits
+
+ i2 = i + 2
+ tz = bigen.Uint16(bs[i:i2])
+ i = i2
+ // sign extend sign bit into top 2 MSB (which were dst bits):
+ if tz&(1<<13) == 0 { // positive
+ tz = tz & 0x3fff //clear 2 MSBs: dst bits
+ } else { // negative
+ tz = tz | 0xc000 //set 2 MSBs: dst bits
+ //tzname[3] = '-' (TODO: verify. this works here)
+ }
+ tzint := int16(tz)
+ if tzint == 0 {
+ tt = time.Unix(tsec, int64(tnsec)).UTC()
+ } else {
+ // For Go Time, do not use a descriptive timezone.
+ // It's unnecessary, and makes it harder to do a reflect.DeepEqual.
+ // The Offset already tells what the offset should be, if not on UTC and unknown zone name.
+ // var zoneName = timeLocUTCName(tzint)
+ tt = time.Unix(tsec, int64(tnsec)).In(time.FixedZone("", int(tzint)*60))
+ }
+ return
+}
+
+func timeLocUTCName(tzint int16) string {
+ if tzint == 0 {
+ return "UTC"
+ }
+ var tzname = []byte("UTC+00:00")
+ //tzname := fmt.Sprintf("UTC%s%02d:%02d", tzsign, tz/60, tz%60) //perf issue using Sprintf. inline below.
+ //tzhr, tzmin := tz/60, tz%60 //faster if u convert to int first
+ var tzhr, tzmin int16
+ if tzint < 0 {
+ tzname[3] = '-' // (TODO: verify. this works here)
+ tzhr, tzmin = -tzint/60, (-tzint)%60
+ } else {
+ tzhr, tzmin = tzint/60, tzint%60
+ }
+ tzname[4] = timeDigits[tzhr/10]
+ tzname[5] = timeDigits[tzhr%10]
+ tzname[7] = timeDigits[tzmin/10]
+ tzname[8] = timeDigits[tzmin%10]
+ return string(tzname)
+ //return time.FixedZone(string(tzname), int(tzint)*60)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go
new file mode 100644
index 000000000..4ec28e131
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec/values_test.go
@@ -0,0 +1,203 @@
+// // +build testing
+
+// Copyright (c) 2012-2015 Ugorji Nwoke. All rights reserved.
+// Use of this source code is governed by a MIT license found in the LICENSE file.
+
+package codec
+
+// This file contains values used by tests and benchmarks.
+// JSON/BSON do not like maps with keys that are not strings,
+// so we only use maps with string keys here.
+
+import (
+ "math"
+ "time"
+)
+
+var testStrucTime = time.Date(2012, 2, 2, 2, 2, 2, 2000, time.UTC).UTC()
+
+type AnonInTestStruc struct {
+ AS string
+ AI64 int64
+ AI16 int16
+ AUi64 uint64
+ ASslice []string
+ AI64slice []int64
+ AF64slice []float64
+ // AMI32U32 map[int32]uint32
+ // AMU32F64 map[uint32]float64 // json/bson do not like it
+ AMSU16 map[string]uint16
+}
+
+type AnonInTestStrucIntf struct {
+ Islice []interface{}
+ Ms map[string]interface{}
+ Nintf interface{} //don't set this, so we can test for nil
+ T time.Time
+}
+
+type TestStruc struct {
+ _struct struct{} `codec:",omitempty"` //set omitempty for every field
+
+ S string
+ I64 int64
+ I16 int16
+ Ui64 uint64
+ Ui8 uint8
+ B bool
+ By uint8 // byte: msgp doesn't like byte
+
+ Sslice []string
+ I64slice []int64
+ I16slice []int16
+ Ui64slice []uint64
+ Ui8slice []uint8
+ Bslice []bool
+ Byslice []byte
+
+ Iptrslice []*int64
+
+ // TODO: test these separately, specifically for reflection and codecgen.
+ // Unfortunately, ffjson doesn't support these. Its compilation even fails.
+ // Ui64array [4]uint64
+ // Ui64slicearray [][4]uint64
+
+ AnonInTestStruc
+
+ //M map[interface{}]interface{} `json:"-",bson:"-"`
+ Msi64 map[string]int64
+
+ // make this a ptr, so that it could be set or not.
+ // for comparison (e.g. with msgp), give it a struct tag (so it is not inlined),
+ // make this one omitempty (so it is included if nil).
+ *AnonInTestStrucIntf `codec:",omitempty"`
+
+ Nmap map[string]bool //don't set this, so we can test for nil
+ Nslice []byte //don't set this, so we can test for nil
+ Nint64 *int64 //don't set this, so we can test for nil
+ Mtsptr map[string]*TestStruc
+ Mts map[string]TestStruc
+ Its []*TestStruc
+ Nteststruc *TestStruc
+}
+
+// small struct for testing that codecgen works for unexported types
+type tLowerFirstLetter struct {
+ I int
+ u uint64
+ S string
+ b []byte
+}
+
+func newTestStruc(depth int, bench bool, useInterface, useStringKeyOnly bool) (ts *TestStruc) {
+ var i64a, i64b, i64c, i64d int64 = 64, 6464, 646464, 64646464
+
+ ts = &TestStruc{
+ S: "some string",
+ I64: math.MaxInt64 * 2 / 3, // 64,
+ I16: 1616,
+ Ui64: uint64(int64(math.MaxInt64 * 2 / 3)), // 64, //don't use MaxUint64, as bson can't write it
+ Ui8: 160,
+ B: true,
+ By: 5,
+
+ Sslice: []string{"one", "two", "three"},
+ I64slice: []int64{1111, 2222, 3333},
+ I16slice: []int16{44, 55, 66},
+ Ui64slice: []uint64{12121212, 34343434, 56565656},
+ Ui8slice: []uint8{210, 211, 212},
+ Bslice: []bool{true, false, true, false},
+ Byslice: []byte{13, 14, 15},
+
+ Msi64: map[string]int64{
+ "one": 1,
+ "two": 2,
+ },
+ AnonInTestStruc: AnonInTestStruc{
+ // There's more leeway in altering this.
+ AS: "A-String",
+ AI64: -64646464,
+ AI16: 1616,
+ AUi64: 64646464,
+ // (U+1D11E)G-clef character may be represented in json as "\uD834\uDD1E".
+ // single reverse solidus character may be represented in json as "\u005C".
+ // include these in ASslice below.
+ ASslice: []string{"Aone", "Atwo", "Athree",
+ "Afour.reverse_solidus.\u005c", "Afive.Gclef.\U0001d11E"},
+ AI64slice: []int64{1, -22, 333, -4444, 55555, -666666},
+ AMSU16: map[string]uint16{"1": 1, "22": 2, "333": 3, "4444": 4},
+ AF64slice: []float64{11.11e-11, 22.22E+22, 33.33E-33, 44.44e+44, 555.55E-6, 666.66E6},
+ },
+ }
+ if useInterface {
+ ts.AnonInTestStrucIntf = &AnonInTestStrucIntf{
+ Islice: []interface{}{"true", true, "no", false, uint64(288), float64(0.4)},
+ Ms: map[string]interface{}{
+ "true": "true",
+ "int64(9)": false,
+ },
+ T: testStrucTime,
+ }
+ }
+
+ //For benchmarks, some things will not work.
+ if !bench {
+ //json and bson require string keys in maps
+ //ts.M = map[interface{}]interface{}{
+ // true: "true",
+ // int8(9): false,
+ //}
+ //gob cannot encode nil in element in array (encodeArray: nil element)
+ ts.Iptrslice = []*int64{nil, &i64a, nil, &i64b, nil, &i64c, nil, &i64d, nil}
+ // ts.Iptrslice = nil
+ }
+ if !useStringKeyOnly {
+ // ts.AnonInTestStruc.AMU32F64 = map[uint32]float64{1: 1, 2: 2, 3: 3} // Json/Bson barf
+ }
+ if depth > 0 {
+ depth--
+ if ts.Mtsptr == nil {
+ ts.Mtsptr = make(map[string]*TestStruc)
+ }
+ if ts.Mts == nil {
+ ts.Mts = make(map[string]TestStruc)
+ }
+ ts.Mtsptr["0"] = newTestStruc(depth, bench, useInterface, useStringKeyOnly)
+ ts.Mts["0"] = *(ts.Mtsptr["0"])
+ ts.Its = append(ts.Its, ts.Mtsptr["0"])
+ }
+ return
+}
+
+// Some other types
+
+type Sstring string
+type Bbool bool
+type Sstructsmall struct {
+ A int
+}
+
+type Sstructbig struct {
+ A int
+ B bool
+ c string
+ // Sval Sstruct
+ Ssmallptr *Sstructsmall
+ Ssmall *Sstructsmall
+ Sptr *Sstructbig
+}
+
+type SstructbigMapBySlice struct {
+ _struct struct{} `codec:",toarray"`
+ A int
+ B bool
+ c string
+ // Sval Sstruct
+ Ssmallptr *Sstructsmall
+ Ssmall *Sstructsmall
+ Sptr *Sstructbig
+}
+
+type Sinterface interface {
+ Noop()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/LICENSE
new file mode 100644
index 000000000..cde8b8b05
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Xiang Li
+
+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/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/README.md
new file mode 100644
index 000000000..2ff682057
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/README.md
@@ -0,0 +1,39 @@
+## Getting Started
+
+### Install the handler
+
+We first need to serve the probing HTTP handler.
+
+```go
+ http.HandleFunc("/health", probing.NewHandler())
+ err := http.ListenAndServe(":12345", nil)
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err)
+ }
+```
+
+### Start to probe
+
+Now we can start to probe the endpoint.
+
+``` go
+ id := "example"
+ probingInterval = 5 * time.Second
+ url := "http://example.com:12345/health"
+ p.AddHTTP(id, probingInterval, url)
+
+ time.Sleep(13 * time.Second)
+ status, err := p.Status(id)
+ fmt.Printf("Total Probing: %d, Total Loss: %d, Estimated RTT: %v, Estimated Clock Difference: %v\n",
+ status.Total(), status.Loss(), status.SRTT(), status.ClockDiff())
+ // Total Probing: 2, Total Loss: 0, Estimated RTT: 320.771µs, Estimated Clock Difference: -35.869µs
+```
+
+### TODOs:
+
+- TCP probing
+- UDP probing
+- Gossip based probing
+- More accurate RTT estimation
+- More accurate Clock difference estimation
+- Use a clock interface rather than the real clock
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/prober.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/prober.go
new file mode 100644
index 000000000..9f04f8602
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/prober.go
@@ -0,0 +1,134 @@
+package probing
+
+import (
+ "encoding/json"
+ "errors"
+ "net/http"
+ "sync"
+ "time"
+)
+
+var (
+ ErrNotFound = errors.New("probing: id not found")
+ ErrExist = errors.New("probing: id exists")
+)
+
+type Prober interface {
+ AddHTTP(id string, probingInterval time.Duration, endpoints []string) error
+ Remove(id string) error
+ RemoveAll()
+ Reset(id string) error
+ Status(id string) (Status, error)
+}
+
+type prober struct {
+ mu sync.Mutex
+ targets map[string]*status
+ tr http.RoundTripper
+}
+
+func NewProber(tr http.RoundTripper) Prober {
+ p := &prober{targets: make(map[string]*status)}
+ if tr == nil {
+ p.tr = http.DefaultTransport
+ } else {
+ p.tr = tr
+ }
+ return p
+}
+
+func (p *prober) AddHTTP(id string, probingInterval time.Duration, endpoints []string) error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if _, ok := p.targets[id]; ok {
+ return ErrExist
+ }
+
+ s := &status{stopC: make(chan struct{})}
+ p.targets[id] = s
+
+ ticker := time.NewTicker(probingInterval)
+
+ go func() {
+ pinned := 0
+ for {
+ select {
+ case <-ticker.C:
+ start := time.Now()
+ req, err := http.NewRequest("GET", endpoints[pinned], nil)
+ if err != nil {
+ panic(err)
+ }
+ resp, err := p.tr.RoundTrip(req)
+ if err != nil {
+ s.recordFailure()
+ pinned = (pinned + 1) % len(endpoints)
+ continue
+ }
+
+ var hh Health
+ d := json.NewDecoder(resp.Body)
+ err = d.Decode(&hh)
+ resp.Body.Close()
+ if err != nil || !hh.OK {
+ s.recordFailure()
+ pinned = (pinned + 1) % len(endpoints)
+ continue
+ }
+
+ s.record(time.Since(start), hh.Now)
+ case <-s.stopC:
+ ticker.Stop()
+ return
+ }
+ }
+ }()
+
+ return nil
+}
+
+func (p *prober) Remove(id string) error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ s, ok := p.targets[id]
+ if !ok {
+ return ErrNotFound
+ }
+ close(s.stopC)
+ delete(p.targets, id)
+ return nil
+}
+
+func (p *prober) RemoveAll() {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ for _, s := range p.targets {
+ close(s.stopC)
+ }
+ p.targets = make(map[string]*status)
+}
+
+func (p *prober) Reset(id string) error {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ s, ok := p.targets[id]
+ if !ok {
+ return ErrNotFound
+ }
+ s.reset()
+ return nil
+}
+
+func (p *prober) Status(id string) (Status, error) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+
+ s, ok := p.targets[id]
+ if !ok {
+ return nil, ErrNotFound
+ }
+ return s, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/prober_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/prober_test.go
new file mode 100644
index 000000000..147f6bfa6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/prober_test.go
@@ -0,0 +1,90 @@
+package probing
+
+import (
+ "net/http/httptest"
+ "testing"
+ "time"
+)
+
+var (
+ testID = "testID"
+)
+
+func TestProbe(t *testing.T) {
+ s := httptest.NewServer(NewHandler())
+
+ p := NewProber(nil)
+ p.AddHTTP(testID, time.Millisecond, []string{s.URL})
+ defer p.Remove(testID)
+
+ time.Sleep(100 * time.Millisecond)
+ status, err := p.Status(testID)
+ if err != nil {
+ t.Fatalf("err = %v, want %v", err, nil)
+ }
+ if total := status.Total(); total < 50 || total > 150 {
+ t.Fatalf("total = %v, want around %v", total, 100)
+ }
+ if health := status.Health(); health != true {
+ t.Fatalf("health = %v, want %v", health, true)
+ }
+
+ // become unhealthy
+ s.Close()
+
+ time.Sleep(100 * time.Millisecond)
+ if total := status.Total(); total < 150 || total > 250 {
+ t.Fatalf("total = %v, want around %v", total, 200)
+ }
+ if loss := status.Loss(); loss < 50 || loss > 150 {
+ t.Fatalf("loss = %v, want around %v", loss, 200)
+ }
+ if health := status.Health(); health != false {
+ t.Fatalf("health = %v, want %v", health, false)
+ }
+}
+
+func TestProbeReset(t *testing.T) {
+ s := httptest.NewServer(NewHandler())
+ defer s.Close()
+
+ p := NewProber(nil)
+ p.AddHTTP(testID, time.Millisecond, []string{s.URL})
+ defer p.Remove(testID)
+
+ time.Sleep(100 * time.Millisecond)
+ status, err := p.Status(testID)
+ if err != nil {
+ t.Fatalf("err = %v, want %v", err, nil)
+ }
+ if total := status.Total(); total < 50 || total > 150 {
+ t.Fatalf("total = %v, want around %v", total, 100)
+ }
+ if health := status.Health(); health != true {
+ t.Fatalf("health = %v, want %v", health, true)
+ }
+
+ p.Reset(testID)
+
+ time.Sleep(100 * time.Millisecond)
+ if total := status.Total(); total < 50 || total > 150 {
+ t.Fatalf("total = %v, want around %v", total, 100)
+ }
+ if health := status.Health(); health != true {
+ t.Fatalf("health = %v, want %v", health, true)
+ }
+}
+
+func TestProbeRemove(t *testing.T) {
+ s := httptest.NewServer(NewHandler())
+ defer s.Close()
+
+ p := NewProber(nil)
+ p.AddHTTP(testID, time.Millisecond, []string{s.URL})
+
+ p.Remove(testID)
+ _, err := p.Status(testID)
+ if err != ErrNotFound {
+ t.Fatalf("err = %v, want %v", err, ErrNotFound)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/server.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/server.go
new file mode 100644
index 000000000..0e7b797d2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/server.go
@@ -0,0 +1,25 @@
+package probing
+
+import (
+ "encoding/json"
+ "net/http"
+ "time"
+)
+
+func NewHandler() http.Handler {
+ return &httpHealth{}
+}
+
+type httpHealth struct {
+}
+
+type Health struct {
+ OK bool
+ Now time.Time
+}
+
+func (h *httpHealth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ health := Health{OK: true, Now: time.Now()}
+ e := json.NewEncoder(w)
+ e.Encode(health)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/status.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/status.go
new file mode 100644
index 000000000..bdfab2761
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing/status.go
@@ -0,0 +1,96 @@
+package probing
+
+import (
+ "sync"
+ "time"
+)
+
+var (
+ // weight factor
+ α = 0.125
+)
+
+type Status interface {
+ Total() int64
+ Loss() int64
+ Health() bool
+ // Estimated smoothed round trip time
+ SRTT() time.Duration
+ // Estimated clock difference
+ ClockDiff() time.Duration
+ StopNotify() <-chan struct{}
+}
+
+type status struct {
+ mu sync.Mutex
+ srtt time.Duration
+ total int64
+ loss int64
+ health bool
+ clockdiff time.Duration
+ stopC chan struct{}
+}
+
+// SRTT = (1-α) * SRTT + α * RTT
+func (s *status) SRTT() time.Duration {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.srtt
+}
+
+func (s *status) Total() int64 {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.total
+}
+
+func (s *status) Loss() int64 {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.loss
+}
+
+func (s *status) Health() bool {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.health
+}
+
+func (s *status) ClockDiff() time.Duration {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.clockdiff
+}
+
+func (s *status) StopNotify() <-chan struct{} {
+ return s.stopC
+}
+
+func (s *status) record(rtt time.Duration, when time.Time) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.total += 1
+ s.health = true
+ s.srtt = time.Duration((1-α)*float64(s.srtt) + α*float64(rtt))
+ s.clockdiff = time.Now().Sub(when) - s.srtt/2
+}
+
+func (s *status) recordFailure() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.total++
+ s.health = false
+ s.loss += 1
+}
+
+func (s *status) reset() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ s.srtt = 0
+ s.total = 0
+ s.health = false
+ s.clockdiff = 0
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/base64.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/base64.go
new file mode 100644
index 000000000..fc3116090
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/base64.go
@@ -0,0 +1,35 @@
+// Copyright 2011 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.
+
+package bcrypt
+
+import "encoding/base64"
+
+const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+
+var bcEncoding = base64.NewEncoding(alphabet)
+
+func base64Encode(src []byte) []byte {
+ n := bcEncoding.EncodedLen(len(src))
+ dst := make([]byte, n)
+ bcEncoding.Encode(dst, src)
+ for dst[n-1] == '=' {
+ n--
+ }
+ return dst[:n]
+}
+
+func base64Decode(src []byte) ([]byte, error) {
+ numOfEquals := 4 - (len(src) % 4)
+ for i := 0; i < numOfEquals; i++ {
+ src = append(src, '=')
+ }
+
+ dst := make([]byte, bcEncoding.DecodedLen(len(src)))
+ n, err := bcEncoding.Decode(dst, src)
+ if err != nil {
+ return nil, err
+ }
+ return dst[:n], nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/bcrypt.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/bcrypt.go
new file mode 100644
index 000000000..4d00cc055
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/bcrypt.go
@@ -0,0 +1,294 @@
+// Copyright 2011 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.
+
+// Package bcrypt implements Provos and Mazières's bcrypt adaptive hashing
+// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
+package bcrypt
+
+// The code is a port of Provos and Mazières's C implementation.
+import (
+ "crypto/rand"
+ "crypto/subtle"
+ "errors"
+ "fmt"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish"
+ "io"
+ "strconv"
+)
+
+const (
+ MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
+ MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
+ DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
+)
+
+// The error returned from CompareHashAndPassword when a password and hash do
+// not match.
+var ErrMismatchedHashAndPassword = errors.New("crypto/bcrypt: hashedPassword is not the hash of the given password")
+
+// The error returned from CompareHashAndPassword when a hash is too short to
+// be a bcrypt hash.
+var ErrHashTooShort = errors.New("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
+
+// The error returned from CompareHashAndPassword when a hash was created with
+// a bcrypt algorithm newer than this implementation.
+type HashVersionTooNewError byte
+
+func (hv HashVersionTooNewError) Error() string {
+ return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
+}
+
+// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
+type InvalidHashPrefixError byte
+
+func (ih InvalidHashPrefixError) Error() string {
+ return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
+}
+
+type InvalidCostError int
+
+func (ic InvalidCostError) Error() string {
+ return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
+}
+
+const (
+ majorVersion = '2'
+ minorVersion = 'a'
+ maxSaltSize = 16
+ maxCryptedHashSize = 23
+ encodedSaltSize = 22
+ encodedHashSize = 31
+ minHashSize = 59
+)
+
+// magicCipherData is an IV for the 64 Blowfish encryption calls in
+// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
+var magicCipherData = []byte{
+ 0x4f, 0x72, 0x70, 0x68,
+ 0x65, 0x61, 0x6e, 0x42,
+ 0x65, 0x68, 0x6f, 0x6c,
+ 0x64, 0x65, 0x72, 0x53,
+ 0x63, 0x72, 0x79, 0x44,
+ 0x6f, 0x75, 0x62, 0x74,
+}
+
+type hashed struct {
+ hash []byte
+ salt []byte
+ cost int // allowed range is MinCost to MaxCost
+ major byte
+ minor byte
+}
+
+// GenerateFromPassword returns the bcrypt hash of the password at the given
+// cost. If the cost given is less than MinCost, the cost will be set to
+// DefaultCost, instead. Use CompareHashAndPassword, as defined in this package,
+// to compare the returned hashed password with its cleartext version.
+func GenerateFromPassword(password []byte, cost int) ([]byte, error) {
+ p, err := newFromPassword(password, cost)
+ if err != nil {
+ return nil, err
+ }
+ return p.Hash(), nil
+}
+
+// CompareHashAndPassword compares a bcrypt hashed password with its possible
+// plaintext equivalent. Returns nil on success, or an error on failure.
+func CompareHashAndPassword(hashedPassword, password []byte) error {
+ p, err := newFromHash(hashedPassword)
+ if err != nil {
+ return err
+ }
+
+ otherHash, err := bcrypt(password, p.cost, p.salt)
+ if err != nil {
+ return err
+ }
+
+ otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
+ if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
+ return nil
+ }
+
+ return ErrMismatchedHashAndPassword
+}
+
+// Cost returns the hashing cost used to create the given hashed
+// password. When, in the future, the hashing cost of a password system needs
+// to be increased in order to adjust for greater computational power, this
+// function allows one to establish which passwords need to be updated.
+func Cost(hashedPassword []byte) (int, error) {
+ p, err := newFromHash(hashedPassword)
+ if err != nil {
+ return 0, err
+ }
+ return p.cost, nil
+}
+
+func newFromPassword(password []byte, cost int) (*hashed, error) {
+ if cost < MinCost {
+ cost = DefaultCost
+ }
+ p := new(hashed)
+ p.major = majorVersion
+ p.minor = minorVersion
+
+ err := checkCost(cost)
+ if err != nil {
+ return nil, err
+ }
+ p.cost = cost
+
+ unencodedSalt := make([]byte, maxSaltSize)
+ _, err = io.ReadFull(rand.Reader, unencodedSalt)
+ if err != nil {
+ return nil, err
+ }
+
+ p.salt = base64Encode(unencodedSalt)
+ hash, err := bcrypt(password, p.cost, p.salt)
+ if err != nil {
+ return nil, err
+ }
+ p.hash = hash
+ return p, err
+}
+
+func newFromHash(hashedSecret []byte) (*hashed, error) {
+ if len(hashedSecret) < minHashSize {
+ return nil, ErrHashTooShort
+ }
+ p := new(hashed)
+ n, err := p.decodeVersion(hashedSecret)
+ if err != nil {
+ return nil, err
+ }
+ hashedSecret = hashedSecret[n:]
+ n, err = p.decodeCost(hashedSecret)
+ if err != nil {
+ return nil, err
+ }
+ hashedSecret = hashedSecret[n:]
+
+ // The "+2" is here because we'll have to append at most 2 '=' to the salt
+ // when base64 decoding it in expensiveBlowfishSetup().
+ p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
+ copy(p.salt, hashedSecret[:encodedSaltSize])
+
+ hashedSecret = hashedSecret[encodedSaltSize:]
+ p.hash = make([]byte, len(hashedSecret))
+ copy(p.hash, hashedSecret)
+
+ return p, nil
+}
+
+func bcrypt(password []byte, cost int, salt []byte) ([]byte, error) {
+ cipherData := make([]byte, len(magicCipherData))
+ copy(cipherData, magicCipherData)
+
+ c, err := expensiveBlowfishSetup(password, uint32(cost), salt)
+ if err != nil {
+ return nil, err
+ }
+
+ for i := 0; i < 24; i += 8 {
+ for j := 0; j < 64; j++ {
+ c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
+ }
+ }
+
+ // Bug compatibility with C bcrypt implementations. We only encode 23 of
+ // the 24 bytes encrypted.
+ hsh := base64Encode(cipherData[:maxCryptedHashSize])
+ return hsh, nil
+}
+
+func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
+
+ csalt, err := base64Decode(salt)
+ if err != nil {
+ return nil, err
+ }
+
+ // Bug compatibility with C bcrypt implementations. They use the trailing
+ // NULL in the key string during expansion.
+ ckey := append(key, 0)
+
+ c, err := blowfish.NewSaltedCipher(ckey, csalt)
+ if err != nil {
+ return nil, err
+ }
+
+ var i, rounds uint64
+ rounds = 1 << cost
+ for i = 0; i < rounds; i++ {
+ blowfish.ExpandKey(ckey, c)
+ blowfish.ExpandKey(csalt, c)
+ }
+
+ return c, nil
+}
+
+func (p *hashed) Hash() []byte {
+ arr := make([]byte, 60)
+ arr[0] = '$'
+ arr[1] = p.major
+ n := 2
+ if p.minor != 0 {
+ arr[2] = p.minor
+ n = 3
+ }
+ arr[n] = '$'
+ n += 1
+ copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
+ n += 2
+ arr[n] = '$'
+ n += 1
+ copy(arr[n:], p.salt)
+ n += encodedSaltSize
+ copy(arr[n:], p.hash)
+ n += encodedHashSize
+ return arr[:n]
+}
+
+func (p *hashed) decodeVersion(sbytes []byte) (int, error) {
+ if sbytes[0] != '$' {
+ return -1, InvalidHashPrefixError(sbytes[0])
+ }
+ if sbytes[1] > majorVersion {
+ return -1, HashVersionTooNewError(sbytes[1])
+ }
+ p.major = sbytes[1]
+ n := 3
+ if sbytes[2] != '$' {
+ p.minor = sbytes[2]
+ n++
+ }
+ return n, nil
+}
+
+// sbytes should begin where decodeVersion left off.
+func (p *hashed) decodeCost(sbytes []byte) (int, error) {
+ cost, err := strconv.Atoi(string(sbytes[0:2]))
+ if err != nil {
+ return -1, err
+ }
+ err = checkCost(cost)
+ if err != nil {
+ return -1, err
+ }
+ p.cost = cost
+ return 3, nil
+}
+
+func (p *hashed) String() string {
+ return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
+}
+
+func checkCost(cost int) error {
+ if cost < MinCost || cost > MaxCost {
+ return InvalidCostError(cost)
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/bcrypt_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/bcrypt_test.go
new file mode 100644
index 000000000..f08a6f5b2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt/bcrypt_test.go
@@ -0,0 +1,226 @@
+// Copyright 2011 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.
+
+package bcrypt
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+)
+
+func TestBcryptingIsEasy(t *testing.T) {
+ pass := []byte("mypassword")
+ hp, err := GenerateFromPassword(pass, 0)
+ if err != nil {
+ t.Fatalf("GenerateFromPassword error: %s", err)
+ }
+
+ if CompareHashAndPassword(hp, pass) != nil {
+ t.Errorf("%v should hash %s correctly", hp, pass)
+ }
+
+ notPass := "notthepass"
+ err = CompareHashAndPassword(hp, []byte(notPass))
+ if err != ErrMismatchedHashAndPassword {
+ t.Errorf("%v and %s should be mismatched", hp, notPass)
+ }
+}
+
+func TestBcryptingIsCorrect(t *testing.T) {
+ pass := []byte("allmine")
+ salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
+ expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
+
+ hash, err := bcrypt(pass, 10, salt)
+ if err != nil {
+ t.Fatalf("bcrypt blew up: %v", err)
+ }
+ if !bytes.HasSuffix(expectedHash, hash) {
+ t.Errorf("%v should be the suffix of %v", hash, expectedHash)
+ }
+
+ h, err := newFromHash(expectedHash)
+ if err != nil {
+ t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
+ }
+
+ // This is not the safe way to compare these hashes. We do this only for
+ // testing clarity. Use bcrypt.CompareHashAndPassword()
+ if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
+ t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
+ }
+}
+
+func TestVeryShortPasswords(t *testing.T) {
+ key := []byte("k")
+ salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
+ _, err := bcrypt(key, 10, salt)
+ if err != nil {
+ t.Errorf("One byte key resulted in error: %s", err)
+ }
+}
+
+func TestTooLongPasswordsWork(t *testing.T) {
+ salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
+ // One byte over the usual 56 byte limit that blowfish has
+ tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
+ tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
+ hash, err := bcrypt(tooLongPass, 10, salt)
+ if err != nil {
+ t.Fatalf("bcrypt blew up on long password: %v", err)
+ }
+ if !bytes.HasSuffix(tooLongExpected, hash) {
+ t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
+ }
+}
+
+type InvalidHashTest struct {
+ err error
+ hash []byte
+}
+
+var invalidTests = []InvalidHashTest{
+ {ErrHashTooShort, []byte("$2a$10$fooo")},
+ {ErrHashTooShort, []byte("$2a")},
+ {HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
+ {InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
+ {InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
+}
+
+func TestInvalidHashErrors(t *testing.T) {
+ check := func(name string, expected, err error) {
+ if err == nil {
+ t.Errorf("%s: Should have returned an error", name)
+ }
+ if err != nil && err != expected {
+ t.Errorf("%s gave err %v but should have given %v", name, err, expected)
+ }
+ }
+ for _, iht := range invalidTests {
+ _, err := newFromHash(iht.hash)
+ check("newFromHash", iht.err, err)
+ err = CompareHashAndPassword(iht.hash, []byte("anything"))
+ check("CompareHashAndPassword", iht.err, err)
+ }
+}
+
+func TestUnpaddedBase64Encoding(t *testing.T) {
+ original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
+ encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
+
+ encoded := base64Encode(original)
+
+ if !bytes.Equal(encodedOriginal, encoded) {
+ t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
+ }
+
+ decoded, err := base64Decode(encodedOriginal)
+ if err != nil {
+ t.Fatalf("base64Decode blew up: %s", err)
+ }
+
+ if !bytes.Equal(decoded, original) {
+ t.Errorf("Decoded %v should have equaled %v", decoded, original)
+ }
+}
+
+func TestCost(t *testing.T) {
+ suffix := "XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C"
+ for _, vers := range []string{"2a", "2"} {
+ for _, cost := range []int{4, 10} {
+ s := fmt.Sprintf("$%s$%02d$%s", vers, cost, suffix)
+ h := []byte(s)
+ actual, err := Cost(h)
+ if err != nil {
+ t.Errorf("Cost, error: %s", err)
+ continue
+ }
+ if actual != cost {
+ t.Errorf("Cost, expected: %d, actual: %d", cost, actual)
+ }
+ }
+ }
+ _, err := Cost([]byte("$a$a$" + suffix))
+ if err == nil {
+ t.Errorf("Cost, malformed but no error returned")
+ }
+}
+
+func TestCostValidationInHash(t *testing.T) {
+ if testing.Short() {
+ return
+ }
+
+ pass := []byte("mypassword")
+
+ for c := 0; c < MinCost; c++ {
+ p, _ := newFromPassword(pass, c)
+ if p.cost != DefaultCost {
+ t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
+ }
+ }
+
+ p, _ := newFromPassword(pass, 14)
+ if p.cost != 14 {
+ t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
+ }
+
+ hp, _ := newFromHash(p.Hash())
+ if p.cost != hp.cost {
+ t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
+ }
+
+ _, err := newFromPassword(pass, 32)
+ if err == nil {
+ t.Fatalf("newFromPassword: should return a cost error")
+ }
+ if err != InvalidCostError(32) {
+ t.Errorf("newFromPassword: should return cost error, got %#v", err)
+ }
+}
+
+func TestCostReturnsWithLeadingZeroes(t *testing.T) {
+ hp, _ := newFromPassword([]byte("abcdefgh"), 7)
+ cost := hp.Hash()[4:7]
+ expected := []byte("07$")
+
+ if !bytes.Equal(expected, cost) {
+ t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
+ }
+}
+
+func TestMinorNotRequired(t *testing.T) {
+ noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
+ h, err := newFromHash(noMinorHash)
+ if err != nil {
+ t.Fatalf("No minor hash blew up: %s", err)
+ }
+ if h.minor != 0 {
+ t.Errorf("Should leave minor version at 0, but was %d", h.minor)
+ }
+
+ if !bytes.Equal(noMinorHash, h.Hash()) {
+ t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
+ }
+}
+
+func BenchmarkEqual(b *testing.B) {
+ b.StopTimer()
+ passwd := []byte("somepasswordyoulike")
+ hash, _ := GenerateFromPassword(passwd, 10)
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ CompareHashAndPassword(hash, passwd)
+ }
+}
+
+func BenchmarkGeneration(b *testing.B) {
+ b.StopTimer()
+ passwd := []byte("mylongpassword1234")
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ GenerateFromPassword(passwd, 10)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/block.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/block.go
new file mode 100644
index 000000000..9d80f1952
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/block.go
@@ -0,0 +1,159 @@
+// Copyright 2010 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.
+
+package blowfish
+
+// getNextWord returns the next big-endian uint32 value from the byte slice
+// at the given position in a circular manner, updating the position.
+func getNextWord(b []byte, pos *int) uint32 {
+ var w uint32
+ j := *pos
+ for i := 0; i < 4; i++ {
+ w = w<<8 | uint32(b[j])
+ j++
+ if j >= len(b) {
+ j = 0
+ }
+ }
+ *pos = j
+ return w
+}
+
+// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
+// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
+// pi and substitution tables for calls to Encrypt. This is used, primarily,
+// by the bcrypt package to reuse the Blowfish key schedule during its
+// set up. It's unlikely that you need to use this directly.
+func ExpandKey(key []byte, c *Cipher) {
+ j := 0
+ for i := 0; i < 18; i++ {
+ // Using inlined getNextWord for performance.
+ var d uint32
+ for k := 0; k < 4; k++ {
+ d = d<<8 | uint32(key[j])
+ j++
+ if j >= len(key) {
+ j = 0
+ }
+ }
+ c.p[i] ^= d
+ }
+
+ var l, r uint32
+ for i := 0; i < 18; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.p[i], c.p[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s0[i], c.s0[i+1] = l, r
+ }
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s1[i], c.s1[i+1] = l, r
+ }
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s2[i], c.s2[i+1] = l, r
+ }
+ for i := 0; i < 256; i += 2 {
+ l, r = encryptBlock(l, r, c)
+ c.s3[i], c.s3[i+1] = l, r
+ }
+}
+
+// This is similar to ExpandKey, but folds the salt during the key
+// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
+// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
+// and specializing it here is useful.
+func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
+ j := 0
+ for i := 0; i < 18; i++ {
+ c.p[i] ^= getNextWord(key, &j)
+ }
+
+ j = 0
+ var l, r uint32
+ for i := 0; i < 18; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.p[i], c.p[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s0[i], c.s0[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s1[i], c.s1[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s2[i], c.s2[i+1] = l, r
+ }
+
+ for i := 0; i < 256; i += 2 {
+ l ^= getNextWord(salt, &j)
+ r ^= getNextWord(salt, &j)
+ l, r = encryptBlock(l, r, c)
+ c.s3[i], c.s3[i+1] = l, r
+ }
+}
+
+func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
+ xl, xr := l, r
+ xl ^= c.p[0]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[1]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[2]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[3]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[4]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[5]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[6]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[7]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[8]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[9]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[10]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[11]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[12]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[13]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[14]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[15]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[16]
+ xr ^= c.p[17]
+ return xr, xl
+}
+
+func decryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
+ xl, xr := l, r
+ xl ^= c.p[17]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[16]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[15]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[14]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[13]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[12]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[11]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[10]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[9]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[8]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[7]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[6]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[5]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[4]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[3]
+ xr ^= ((c.s0[byte(xl>>24)] + c.s1[byte(xl>>16)]) ^ c.s2[byte(xl>>8)]) + c.s3[byte(xl)] ^ c.p[2]
+ xl ^= ((c.s0[byte(xr>>24)] + c.s1[byte(xr>>16)]) ^ c.s2[byte(xr>>8)]) + c.s3[byte(xr)] ^ c.p[1]
+ xr ^= c.p[0]
+ return xr, xl
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/blowfish_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/blowfish_test.go
new file mode 100644
index 000000000..7afa1fdf3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/blowfish_test.go
@@ -0,0 +1,274 @@
+// Copyright 2010 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.
+
+package blowfish
+
+import "testing"
+
+type CryptTest struct {
+ key []byte
+ in []byte
+ out []byte
+}
+
+// Test vector values are from http://www.schneier.com/code/vectors.txt.
+var encryptTests = []CryptTest{
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
+ {
+ []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ []byte{0x51, 0x86, 0x6F, 0xD5, 0xB8, 0x5E, 0xCB, 0x8A}},
+ {
+ []byte{0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+ []byte{0x7D, 0x85, 0x6F, 0x9A, 0x61, 0x30, 0x63, 0xF2}},
+ {
+ []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+ []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+ []byte{0x24, 0x66, 0xDD, 0x87, 0x8B, 0x96, 0x3C, 0x9D}},
+
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+ []byte{0x61, 0xF9, 0xC3, 0x80, 0x22, 0x81, 0xB0, 0x96}},
+ {
+ []byte{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0x7D, 0x0C, 0xC6, 0x30, 0xAF, 0xDA, 0x1E, 0xC7}},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x4E, 0xF9, 0x97, 0x45, 0x61, 0x98, 0xDD, 0x78}},
+ {
+ []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0x0A, 0xCE, 0xAB, 0x0F, 0xC6, 0xA0, 0xA2, 0x8D}},
+ {
+ []byte{0x7C, 0xA1, 0x10, 0x45, 0x4A, 0x1A, 0x6E, 0x57},
+ []byte{0x01, 0xA1, 0xD6, 0xD0, 0x39, 0x77, 0x67, 0x42},
+ []byte{0x59, 0xC6, 0x82, 0x45, 0xEB, 0x05, 0x28, 0x2B}},
+ {
+ []byte{0x01, 0x31, 0xD9, 0x61, 0x9D, 0xC1, 0x37, 0x6E},
+ []byte{0x5C, 0xD5, 0x4C, 0xA8, 0x3D, 0xEF, 0x57, 0xDA},
+ []byte{0xB1, 0xB8, 0xCC, 0x0B, 0x25, 0x0F, 0x09, 0xA0}},
+ {
+ []byte{0x07, 0xA1, 0x13, 0x3E, 0x4A, 0x0B, 0x26, 0x86},
+ []byte{0x02, 0x48, 0xD4, 0x38, 0x06, 0xF6, 0x71, 0x72},
+ []byte{0x17, 0x30, 0xE5, 0x77, 0x8B, 0xEA, 0x1D, 0xA4}},
+ {
+ []byte{0x38, 0x49, 0x67, 0x4C, 0x26, 0x02, 0x31, 0x9E},
+ []byte{0x51, 0x45, 0x4B, 0x58, 0x2D, 0xDF, 0x44, 0x0A},
+ []byte{0xA2, 0x5E, 0x78, 0x56, 0xCF, 0x26, 0x51, 0xEB}},
+ {
+ []byte{0x04, 0xB9, 0x15, 0xBA, 0x43, 0xFE, 0xB5, 0xB6},
+ []byte{0x42, 0xFD, 0x44, 0x30, 0x59, 0x57, 0x7F, 0xA2},
+ []byte{0x35, 0x38, 0x82, 0xB1, 0x09, 0xCE, 0x8F, 0x1A}},
+ {
+ []byte{0x01, 0x13, 0xB9, 0x70, 0xFD, 0x34, 0xF2, 0xCE},
+ []byte{0x05, 0x9B, 0x5E, 0x08, 0x51, 0xCF, 0x14, 0x3A},
+ []byte{0x48, 0xF4, 0xD0, 0x88, 0x4C, 0x37, 0x99, 0x18}},
+ {
+ []byte{0x01, 0x70, 0xF1, 0x75, 0x46, 0x8F, 0xB5, 0xE6},
+ []byte{0x07, 0x56, 0xD8, 0xE0, 0x77, 0x47, 0x61, 0xD2},
+ []byte{0x43, 0x21, 0x93, 0xB7, 0x89, 0x51, 0xFC, 0x98}},
+ {
+ []byte{0x43, 0x29, 0x7F, 0xAD, 0x38, 0xE3, 0x73, 0xFE},
+ []byte{0x76, 0x25, 0x14, 0xB8, 0x29, 0xBF, 0x48, 0x6A},
+ []byte{0x13, 0xF0, 0x41, 0x54, 0xD6, 0x9D, 0x1A, 0xE5}},
+ {
+ []byte{0x07, 0xA7, 0x13, 0x70, 0x45, 0xDA, 0x2A, 0x16},
+ []byte{0x3B, 0xDD, 0x11, 0x90, 0x49, 0x37, 0x28, 0x02},
+ []byte{0x2E, 0xED, 0xDA, 0x93, 0xFF, 0xD3, 0x9C, 0x79}},
+ {
+ []byte{0x04, 0x68, 0x91, 0x04, 0xC2, 0xFD, 0x3B, 0x2F},
+ []byte{0x26, 0x95, 0x5F, 0x68, 0x35, 0xAF, 0x60, 0x9A},
+ []byte{0xD8, 0x87, 0xE0, 0x39, 0x3C, 0x2D, 0xA6, 0xE3}},
+ {
+ []byte{0x37, 0xD0, 0x6B, 0xB5, 0x16, 0xCB, 0x75, 0x46},
+ []byte{0x16, 0x4D, 0x5E, 0x40, 0x4F, 0x27, 0x52, 0x32},
+ []byte{0x5F, 0x99, 0xD0, 0x4F, 0x5B, 0x16, 0x39, 0x69}},
+ {
+ []byte{0x1F, 0x08, 0x26, 0x0D, 0x1A, 0xC2, 0x46, 0x5E},
+ []byte{0x6B, 0x05, 0x6E, 0x18, 0x75, 0x9F, 0x5C, 0xCA},
+ []byte{0x4A, 0x05, 0x7A, 0x3B, 0x24, 0xD3, 0x97, 0x7B}},
+ {
+ []byte{0x58, 0x40, 0x23, 0x64, 0x1A, 0xBA, 0x61, 0x76},
+ []byte{0x00, 0x4B, 0xD6, 0xEF, 0x09, 0x17, 0x60, 0x62},
+ []byte{0x45, 0x20, 0x31, 0xC1, 0xE4, 0xFA, 0xDA, 0x8E}},
+ {
+ []byte{0x02, 0x58, 0x16, 0x16, 0x46, 0x29, 0xB0, 0x07},
+ []byte{0x48, 0x0D, 0x39, 0x00, 0x6E, 0xE7, 0x62, 0xF2},
+ []byte{0x75, 0x55, 0xAE, 0x39, 0xF5, 0x9B, 0x87, 0xBD}},
+ {
+ []byte{0x49, 0x79, 0x3E, 0xBC, 0x79, 0xB3, 0x25, 0x8F},
+ []byte{0x43, 0x75, 0x40, 0xC8, 0x69, 0x8F, 0x3C, 0xFA},
+ []byte{0x53, 0xC5, 0x5F, 0x9C, 0xB4, 0x9F, 0xC0, 0x19}},
+ {
+ []byte{0x4F, 0xB0, 0x5E, 0x15, 0x15, 0xAB, 0x73, 0xA7},
+ []byte{0x07, 0x2D, 0x43, 0xA0, 0x77, 0x07, 0x52, 0x92},
+ []byte{0x7A, 0x8E, 0x7B, 0xFA, 0x93, 0x7E, 0x89, 0xA3}},
+ {
+ []byte{0x49, 0xE9, 0x5D, 0x6D, 0x4C, 0xA2, 0x29, 0xBF},
+ []byte{0x02, 0xFE, 0x55, 0x77, 0x81, 0x17, 0xF1, 0x2A},
+ []byte{0xCF, 0x9C, 0x5D, 0x7A, 0x49, 0x86, 0xAD, 0xB5}},
+ {
+ []byte{0x01, 0x83, 0x10, 0xDC, 0x40, 0x9B, 0x26, 0xD6},
+ []byte{0x1D, 0x9D, 0x5C, 0x50, 0x18, 0xF7, 0x28, 0xC2},
+ []byte{0xD1, 0xAB, 0xB2, 0x90, 0x65, 0x8B, 0xC7, 0x78}},
+ {
+ []byte{0x1C, 0x58, 0x7F, 0x1C, 0x13, 0x92, 0x4F, 0xEF},
+ []byte{0x30, 0x55, 0x32, 0x28, 0x6D, 0x6F, 0x29, 0x5A},
+ []byte{0x55, 0xCB, 0x37, 0x74, 0xD1, 0x3E, 0xF2, 0x01}},
+ {
+ []byte{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0xFA, 0x34, 0xEC, 0x48, 0x47, 0xB2, 0x68, 0xB2}},
+ {
+ []byte{0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0xA7, 0x90, 0x79, 0x51, 0x08, 0xEA, 0x3C, 0xAE}},
+ {
+ []byte{0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE},
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0xC3, 0x9E, 0x07, 0x2D, 0x9F, 0xAC, 0x63, 0x1D}},
+ {
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ []byte{0x01, 0x49, 0x33, 0xE0, 0xCD, 0xAF, 0xF6, 0xE4}},
+ {
+ []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0xF2, 0x1E, 0x9A, 0x77, 0xB7, 0x1C, 0x49, 0xBC}},
+ {
+ []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF},
+ []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+ []byte{0x24, 0x59, 0x46, 0x88, 0x57, 0x54, 0x36, 0x9A}},
+ {
+ []byte{0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10},
+ []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+ []byte{0x6B, 0x5C, 0x5A, 0x9C, 0x5D, 0x9E, 0x0A, 0x5A}},
+}
+
+func TestCipherEncrypt(t *testing.T) {
+ for i, tt := range encryptTests {
+ c, err := NewCipher(tt.key)
+ if err != nil {
+ t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
+ continue
+ }
+ ct := make([]byte, len(tt.out))
+ c.Encrypt(ct, tt.in)
+ for j, v := range ct {
+ if v != tt.out[j] {
+ t.Errorf("Cipher.Encrypt, test vector #%d: cipher-text[%d] = %#x, expected %#x", i, j, v, tt.out[j])
+ break
+ }
+ }
+ }
+}
+
+func TestCipherDecrypt(t *testing.T) {
+ for i, tt := range encryptTests {
+ c, err := NewCipher(tt.key)
+ if err != nil {
+ t.Errorf("NewCipher(%d bytes) = %s", len(tt.key), err)
+ continue
+ }
+ pt := make([]byte, len(tt.in))
+ c.Decrypt(pt, tt.out)
+ for j, v := range pt {
+ if v != tt.in[j] {
+ t.Errorf("Cipher.Decrypt, test vector #%d: plain-text[%d] = %#x, expected %#x", i, j, v, tt.in[j])
+ break
+ }
+ }
+ }
+}
+
+func TestSaltedCipherKeyLength(t *testing.T) {
+ if _, err := NewSaltedCipher(nil, []byte{'a'}); err != KeySizeError(0) {
+ t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(0))
+ }
+
+ // A 57-byte key. One over the typical blowfish restriction.
+ key := []byte("012345678901234567890123456789012345678901234567890123456")
+ if _, err := NewSaltedCipher(key, []byte{'a'}); err != nil {
+ t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
+ }
+}
+
+// Test vectors generated with Blowfish from OpenSSH.
+var saltedVectors = [][8]byte{
+ {0x0c, 0x82, 0x3b, 0x7b, 0x8d, 0x01, 0x4b, 0x7e},
+ {0xd1, 0xe1, 0x93, 0xf0, 0x70, 0xa6, 0xdb, 0x12},
+ {0xfc, 0x5e, 0xba, 0xde, 0xcb, 0xf8, 0x59, 0xad},
+ {0x8a, 0x0c, 0x76, 0xe7, 0xdd, 0x2c, 0xd3, 0xa8},
+ {0x2c, 0xcb, 0x7b, 0xee, 0xac, 0x7b, 0x7f, 0xf8},
+ {0xbb, 0xf6, 0x30, 0x6f, 0xe1, 0x5d, 0x62, 0xbf},
+ {0x97, 0x1e, 0xc1, 0x3d, 0x3d, 0xe0, 0x11, 0xe9},
+ {0x06, 0xd7, 0x4d, 0xb1, 0x80, 0xa3, 0xb1, 0x38},
+ {0x67, 0xa1, 0xa9, 0x75, 0x0e, 0x5b, 0xc6, 0xb4},
+ {0x51, 0x0f, 0x33, 0x0e, 0x4f, 0x67, 0xd2, 0x0c},
+ {0xf1, 0x73, 0x7e, 0xd8, 0x44, 0xea, 0xdb, 0xe5},
+ {0x14, 0x0e, 0x16, 0xce, 0x7f, 0x4a, 0x9c, 0x7b},
+ {0x4b, 0xfe, 0x43, 0xfd, 0xbf, 0x36, 0x04, 0x47},
+ {0xb1, 0xeb, 0x3e, 0x15, 0x36, 0xa7, 0xbb, 0xe2},
+ {0x6d, 0x0b, 0x41, 0xdd, 0x00, 0x98, 0x0b, 0x19},
+ {0xd3, 0xce, 0x45, 0xce, 0x1d, 0x56, 0xb7, 0xfc},
+ {0xd9, 0xf0, 0xfd, 0xda, 0xc0, 0x23, 0xb7, 0x93},
+ {0x4c, 0x6f, 0xa1, 0xe4, 0x0c, 0xa8, 0xca, 0x57},
+ {0xe6, 0x2f, 0x28, 0xa7, 0x0c, 0x94, 0x0d, 0x08},
+ {0x8f, 0xe3, 0xf0, 0xb6, 0x29, 0xe3, 0x44, 0x03},
+ {0xff, 0x98, 0xdd, 0x04, 0x45, 0xb4, 0x6d, 0x1f},
+ {0x9e, 0x45, 0x4d, 0x18, 0x40, 0x53, 0xdb, 0xef},
+ {0xb7, 0x3b, 0xef, 0x29, 0xbe, 0xa8, 0x13, 0x71},
+ {0x02, 0x54, 0x55, 0x41, 0x8e, 0x04, 0xfc, 0xad},
+ {0x6a, 0x0a, 0xee, 0x7c, 0x10, 0xd9, 0x19, 0xfe},
+ {0x0a, 0x22, 0xd9, 0x41, 0xcc, 0x23, 0x87, 0x13},
+ {0x6e, 0xff, 0x1f, 0xff, 0x36, 0x17, 0x9c, 0xbe},
+ {0x79, 0xad, 0xb7, 0x40, 0xf4, 0x9f, 0x51, 0xa6},
+ {0x97, 0x81, 0x99, 0xa4, 0xde, 0x9e, 0x9f, 0xb6},
+ {0x12, 0x19, 0x7a, 0x28, 0xd0, 0xdc, 0xcc, 0x92},
+ {0x81, 0xda, 0x60, 0x1e, 0x0e, 0xdd, 0x65, 0x56},
+ {0x7d, 0x76, 0x20, 0xb2, 0x73, 0xc9, 0x9e, 0xee},
+}
+
+func TestSaltedCipher(t *testing.T) {
+ var key, salt [32]byte
+ for i := range key {
+ key[i] = byte(i)
+ salt[i] = byte(i + 32)
+ }
+ for i, v := range saltedVectors {
+ c, err := NewSaltedCipher(key[:], salt[:i])
+ if err != nil {
+ t.Fatal(err)
+ }
+ var buf [8]byte
+ c.Encrypt(buf[:], buf[:])
+ if v != buf {
+ t.Errorf("%d: expected %x, got %x", i, v, buf)
+ }
+ }
+}
+
+func BenchmarkExpandKeyWithSalt(b *testing.B) {
+ key := make([]byte, 32)
+ salt := make([]byte, 16)
+ c, _ := NewCipher(key)
+ for i := 0; i < b.N; i++ {
+ expandKeyWithSalt(key, salt, c)
+ }
+}
+
+func BenchmarkExpandKey(b *testing.B) {
+ key := make([]byte, 32)
+ c, _ := NewCipher(key)
+ for i := 0; i < b.N; i++ {
+ ExpandKey(key, c)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/cipher.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/cipher.go
new file mode 100644
index 000000000..5019658a4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/cipher.go
@@ -0,0 +1,91 @@
+// Copyright 2010 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.
+
+// Package blowfish implements Bruce Schneier's Blowfish encryption algorithm.
+package blowfish
+
+// The code is a port of Bruce Schneier's C implementation.
+// See http://www.schneier.com/blowfish.html.
+
+import "strconv"
+
+// The Blowfish block size in bytes.
+const BlockSize = 8
+
+// A Cipher is an instance of Blowfish encryption using a particular key.
+type Cipher struct {
+ p [18]uint32
+ s0, s1, s2, s3 [256]uint32
+}
+
+type KeySizeError int
+
+func (k KeySizeError) Error() string {
+ return "crypto/blowfish: invalid key size " + strconv.Itoa(int(k))
+}
+
+// NewCipher creates and returns a Cipher.
+// The key argument should be the Blowfish key, from 1 to 56 bytes.
+func NewCipher(key []byte) (*Cipher, error) {
+ var result Cipher
+ if k := len(key); k < 1 || k > 56 {
+ return nil, KeySizeError(k)
+ }
+ initCipher(&result)
+ ExpandKey(key, &result)
+ return &result, nil
+}
+
+// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
+// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
+// sufficient and desirable. For bcrypt compatiblity, the key can be over 56
+// bytes.
+func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
+ if len(salt) == 0 {
+ return NewCipher(key)
+ }
+ var result Cipher
+ if k := len(key); k < 1 {
+ return nil, KeySizeError(k)
+ }
+ initCipher(&result)
+ expandKeyWithSalt(key, salt, &result)
+ return &result, nil
+}
+
+// BlockSize returns the Blowfish block size, 8 bytes.
+// It is necessary to satisfy the Block interface in the
+// package "crypto/cipher".
+func (c *Cipher) BlockSize() int { return BlockSize }
+
+// Encrypt encrypts the 8-byte buffer src using the key k
+// and stores the result in dst.
+// Note that for amounts of data larger than a block,
+// it is not safe to just call Encrypt on successive blocks;
+// instead, use an encryption mode like CBC (see crypto/cipher/cbc.go).
+func (c *Cipher) Encrypt(dst, src []byte) {
+ l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
+ r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
+ l, r = encryptBlock(l, r, c)
+ dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+ dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
+}
+
+// Decrypt decrypts the 8-byte buffer src using the key k
+// and stores the result in dst.
+func (c *Cipher) Decrypt(dst, src []byte) {
+ l := uint32(src[0])<<24 | uint32(src[1])<<16 | uint32(src[2])<<8 | uint32(src[3])
+ r := uint32(src[4])<<24 | uint32(src[5])<<16 | uint32(src[6])<<8 | uint32(src[7])
+ l, r = decryptBlock(l, r, c)
+ dst[0], dst[1], dst[2], dst[3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
+ dst[4], dst[5], dst[6], dst[7] = byte(r>>24), byte(r>>16), byte(r>>8), byte(r)
+}
+
+func initCipher(c *Cipher) {
+ copy(c.p[0:], p[0:])
+ copy(c.s0[0:], s0[0:])
+ copy(c.s1[0:], s1[0:])
+ copy(c.s2[0:], s2[0:])
+ copy(c.s3[0:], s3[0:])
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/const.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/const.go
new file mode 100644
index 000000000..8c5ee4cb0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/blowfish/const.go
@@ -0,0 +1,199 @@
+// Copyright 2010 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.
+
+// The startup permutation array and substitution boxes.
+// They are the hexadecimal digits of PI; see:
+// http://www.schneier.com/code/constants.txt.
+
+package blowfish
+
+var s0 = [256]uint32{
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
+ 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
+ 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
+ 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
+ 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
+ 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
+ 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
+ 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
+ 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
+ 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
+ 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
+ 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
+ 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
+ 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
+ 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
+ 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
+ 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
+ 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
+ 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
+ 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
+ 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
+ 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+}
+
+var s1 = [256]uint32{
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
+ 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
+ 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
+ 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
+ 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
+ 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
+ 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
+ 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
+ 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
+ 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
+ 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
+ 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
+ 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
+ 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
+ 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
+ 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
+ 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
+ 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
+ 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
+ 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
+ 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
+ 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+}
+
+var s2 = [256]uint32{
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
+ 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
+ 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
+ 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
+ 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
+ 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
+ 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
+ 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
+ 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
+ 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
+ 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
+ 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
+ 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
+ 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
+ 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
+ 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
+ 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
+ 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
+ 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
+ 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
+ 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
+ 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+}
+
+var s3 = [256]uint32{
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
+ 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
+ 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
+ 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
+ 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
+ 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
+ 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
+ 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
+ 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
+ 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
+ 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
+ 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
+ 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
+ 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
+ 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
+ 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
+ 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
+ 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
+ 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
+ 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
+ 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
+ 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+}
+
+var p = [18]uint32{
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
+ 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/context.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/context.go
new file mode 100644
index 000000000..ef2f3e86f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/context.go
@@ -0,0 +1,447 @@
+// Copyright 2014 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.
+
+// Package context defines the Context type, which carries deadlines,
+// cancelation signals, and other request-scoped values across API boundaries
+// and between processes.
+//
+// Incoming requests to a server should create a Context, and outgoing calls to
+// servers should accept a Context. The chain of function calls between must
+// propagate the Context, optionally replacing it with a modified copy created
+// using WithDeadline, WithTimeout, WithCancel, or WithValue.
+//
+// Programs that use Contexts should follow these rules to keep interfaces
+// consistent across packages and enable static analysis tools to check context
+// propagation:
+//
+// Do not store Contexts inside a struct type; instead, pass a Context
+// explicitly to each function that needs it. The Context should be the first
+// parameter, typically named ctx:
+//
+// func DoSomething(ctx context.Context, arg Arg) error {
+// // ... use ctx ...
+// }
+//
+// Do not pass a nil Context, even if a function permits it. Pass context.TODO
+// if you are unsure about which Context to use.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+//
+// The same Context may be passed to functions running in different goroutines;
+// Contexts are safe for simultaneous use by multiple goroutines.
+//
+// See http://blog.golang.org/context for example code for a server that uses
+// Contexts.
+package context
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+)
+
+// A Context carries a deadline, a cancelation signal, and other values across
+// API boundaries.
+//
+// Context's methods may be called by multiple goroutines simultaneously.
+type Context interface {
+ // Deadline returns the time when work done on behalf of this context
+ // should be canceled. Deadline returns ok==false when no deadline is
+ // set. Successive calls to Deadline return the same results.
+ Deadline() (deadline time.Time, ok bool)
+
+ // Done returns a channel that's closed when work done on behalf of this
+ // context should be canceled. Done may return nil if this context can
+ // never be canceled. Successive calls to Done return the same value.
+ //
+ // WithCancel arranges for Done to be closed when cancel is called;
+ // WithDeadline arranges for Done to be closed when the deadline
+ // expires; WithTimeout arranges for Done to be closed when the timeout
+ // elapses.
+ //
+ // Done is provided for use in select statements:
+ //
+ // // Stream generates values with DoSomething and sends them to out
+ // // until DoSomething returns an error or ctx.Done is closed.
+ // func Stream(ctx context.Context, out <-chan Value) error {
+ // for {
+ // v, err := DoSomething(ctx)
+ // if err != nil {
+ // return err
+ // }
+ // select {
+ // case <-ctx.Done():
+ // return ctx.Err()
+ // case out <- v:
+ // }
+ // }
+ // }
+ //
+ // See http://blog.golang.org/pipelines for more examples of how to use
+ // a Done channel for cancelation.
+ Done() <-chan struct{}
+
+ // Err returns a non-nil error value after Done is closed. Err returns
+ // Canceled if the context was canceled or DeadlineExceeded if the
+ // context's deadline passed. No other values for Err are defined.
+ // After Done is closed, successive calls to Err return the same value.
+ Err() error
+
+ // Value returns the value associated with this context for key, or nil
+ // if no value is associated with key. Successive calls to Value with
+ // the same key returns the same result.
+ //
+ // Use context values only for request-scoped data that transits
+ // processes and API boundaries, not for passing optional parameters to
+ // functions.
+ //
+ // A key identifies a specific value in a Context. Functions that wish
+ // to store values in Context typically allocate a key in a global
+ // variable then use that key as the argument to context.WithValue and
+ // Context.Value. A key can be any type that supports equality;
+ // packages should define keys as an unexported type to avoid
+ // collisions.
+ //
+ // Packages that define a Context key should provide type-safe accessors
+ // for the values stores using that key:
+ //
+ // // Package user defines a User type that's stored in Contexts.
+ // package user
+ //
+ // import "golang.org/x/net/context"
+ //
+ // // User is the type of value stored in the Contexts.
+ // type User struct {...}
+ //
+ // // key is an unexported type for keys defined in this package.
+ // // This prevents collisions with keys defined in other packages.
+ // type key int
+ //
+ // // userKey is the key for user.User values in Contexts. It is
+ // // unexported; clients use user.NewContext and user.FromContext
+ // // instead of using this key directly.
+ // var userKey key = 0
+ //
+ // // NewContext returns a new Context that carries value u.
+ // func NewContext(ctx context.Context, u *User) context.Context {
+ // return context.WithValue(ctx, userKey, u)
+ // }
+ //
+ // // FromContext returns the User value stored in ctx, if any.
+ // func FromContext(ctx context.Context) (*User, bool) {
+ // u, ok := ctx.Value(userKey).(*User)
+ // return u, ok
+ // }
+ Value(key interface{}) interface{}
+}
+
+// Canceled is the error returned by Context.Err when the context is canceled.
+var Canceled = errors.New("context canceled")
+
+// DeadlineExceeded is the error returned by Context.Err when the context's
+// deadline passes.
+var DeadlineExceeded = errors.New("context deadline exceeded")
+
+// An emptyCtx is never canceled, has no values, and has no deadline. It is not
+// struct{}, since vars of this type must have distinct addresses.
+type emptyCtx int
+
+func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
+ return
+}
+
+func (*emptyCtx) Done() <-chan struct{} {
+ return nil
+}
+
+func (*emptyCtx) Err() error {
+ return nil
+}
+
+func (*emptyCtx) Value(key interface{}) interface{} {
+ return nil
+}
+
+func (e *emptyCtx) String() string {
+ switch e {
+ case background:
+ return "context.Background"
+ case todo:
+ return "context.TODO"
+ }
+ return "unknown empty Context"
+}
+
+var (
+ background = new(emptyCtx)
+ todo = new(emptyCtx)
+)
+
+// Background returns a non-nil, empty Context. It is never canceled, has no
+// values, and has no deadline. It is typically used by the main function,
+// initialization, and tests, and as the top-level Context for incoming
+// requests.
+func Background() Context {
+ return background
+}
+
+// TODO returns a non-nil, empty Context. Code should use context.TODO when
+// it's unclear which Context to use or it's is not yet available (because the
+// surrounding function has not yet been extended to accept a Context
+// parameter). TODO is recognized by static analysis tools that determine
+// whether Contexts are propagated correctly in a program.
+func TODO() Context {
+ return todo
+}
+
+// A CancelFunc tells an operation to abandon its work.
+// A CancelFunc does not wait for the work to stop.
+// After the first call, subsequent calls to a CancelFunc do nothing.
+type CancelFunc func()
+
+// WithCancel returns a copy of parent with a new Done channel. The returned
+// context's Done channel is closed when the returned cancel function is called
+// or when the parent context's Done channel is closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
+ c := newCancelCtx(parent)
+ propagateCancel(parent, &c)
+ return &c, func() { c.cancel(true, Canceled) }
+}
+
+// newCancelCtx returns an initialized cancelCtx.
+func newCancelCtx(parent Context) cancelCtx {
+ return cancelCtx{
+ Context: parent,
+ done: make(chan struct{}),
+ }
+}
+
+// propagateCancel arranges for child to be canceled when parent is.
+func propagateCancel(parent Context, child canceler) {
+ if parent.Done() == nil {
+ return // parent is never canceled
+ }
+ if p, ok := parentCancelCtx(parent); ok {
+ p.mu.Lock()
+ if p.err != nil {
+ // parent has already been canceled
+ child.cancel(false, p.err)
+ } else {
+ if p.children == nil {
+ p.children = make(map[canceler]bool)
+ }
+ p.children[child] = true
+ }
+ p.mu.Unlock()
+ } else {
+ go func() {
+ select {
+ case <-parent.Done():
+ child.cancel(false, parent.Err())
+ case <-child.Done():
+ }
+ }()
+ }
+}
+
+// parentCancelCtx follows a chain of parent references until it finds a
+// *cancelCtx. This function understands how each of the concrete types in this
+// package represents its parent.
+func parentCancelCtx(parent Context) (*cancelCtx, bool) {
+ for {
+ switch c := parent.(type) {
+ case *cancelCtx:
+ return c, true
+ case *timerCtx:
+ return &c.cancelCtx, true
+ case *valueCtx:
+ parent = c.Context
+ default:
+ return nil, false
+ }
+ }
+}
+
+// removeChild removes a context from its parent.
+func removeChild(parent Context, child canceler) {
+ p, ok := parentCancelCtx(parent)
+ if !ok {
+ return
+ }
+ p.mu.Lock()
+ if p.children != nil {
+ delete(p.children, child)
+ }
+ p.mu.Unlock()
+}
+
+// A canceler is a context type that can be canceled directly. The
+// implementations are *cancelCtx and *timerCtx.
+type canceler interface {
+ cancel(removeFromParent bool, err error)
+ Done() <-chan struct{}
+}
+
+// A cancelCtx can be canceled. When canceled, it also cancels any children
+// that implement canceler.
+type cancelCtx struct {
+ Context
+
+ done chan struct{} // closed by the first cancel call.
+
+ mu sync.Mutex
+ children map[canceler]bool // set to nil by the first cancel call
+ err error // set to non-nil by the first cancel call
+}
+
+func (c *cancelCtx) Done() <-chan struct{} {
+ return c.done
+}
+
+func (c *cancelCtx) Err() error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ return c.err
+}
+
+func (c *cancelCtx) String() string {
+ return fmt.Sprintf("%v.WithCancel", c.Context)
+}
+
+// cancel closes c.done, cancels each of c's children, and, if
+// removeFromParent is true, removes c from its parent's children.
+func (c *cancelCtx) cancel(removeFromParent bool, err error) {
+ if err == nil {
+ panic("context: internal error: missing cancel error")
+ }
+ c.mu.Lock()
+ if c.err != nil {
+ c.mu.Unlock()
+ return // already canceled
+ }
+ c.err = err
+ close(c.done)
+ for child := range c.children {
+ // NOTE: acquiring the child's lock while holding parent's lock.
+ child.cancel(false, err)
+ }
+ c.children = nil
+ c.mu.Unlock()
+
+ if removeFromParent {
+ removeChild(c.Context, c)
+ }
+}
+
+// WithDeadline returns a copy of the parent context with the deadline adjusted
+// to be no later than d. If the parent's deadline is already earlier than d,
+// WithDeadline(parent, d) is semantically equivalent to parent. The returned
+// context's Done channel is closed when the deadline expires, when the returned
+// cancel function is called, or when the parent context's Done channel is
+// closed, whichever happens first.
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete.
+func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) {
+ if cur, ok := parent.Deadline(); ok && cur.Before(deadline) {
+ // The current deadline is already sooner than the new one.
+ return WithCancel(parent)
+ }
+ c := &timerCtx{
+ cancelCtx: newCancelCtx(parent),
+ deadline: deadline,
+ }
+ propagateCancel(parent, c)
+ d := deadline.Sub(time.Now())
+ if d <= 0 {
+ c.cancel(true, DeadlineExceeded) // deadline has already passed
+ return c, func() { c.cancel(true, Canceled) }
+ }
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.err == nil {
+ c.timer = time.AfterFunc(d, func() {
+ c.cancel(true, DeadlineExceeded)
+ })
+ }
+ return c, func() { c.cancel(true, Canceled) }
+}
+
+// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
+// implement Done and Err. It implements cancel by stopping its timer then
+// delegating to cancelCtx.cancel.
+type timerCtx struct {
+ cancelCtx
+ timer *time.Timer // Under cancelCtx.mu.
+
+ deadline time.Time
+}
+
+func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
+ return c.deadline, true
+}
+
+func (c *timerCtx) String() string {
+ return fmt.Sprintf("%v.WithDeadline(%s [%s])", c.cancelCtx.Context, c.deadline, c.deadline.Sub(time.Now()))
+}
+
+func (c *timerCtx) cancel(removeFromParent bool, err error) {
+ c.cancelCtx.cancel(false, err)
+ if removeFromParent {
+ // Remove this timerCtx from its parent cancelCtx's children.
+ removeChild(c.cancelCtx.Context, c)
+ }
+ c.mu.Lock()
+ if c.timer != nil {
+ c.timer.Stop()
+ c.timer = nil
+ }
+ c.mu.Unlock()
+}
+
+// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
+//
+// Canceling this context releases resources associated with it, so code should
+// call cancel as soon as the operations running in this Context complete:
+//
+// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// defer cancel() // releases resources if slowOperation completes before timeout elapses
+// return slowOperation(ctx)
+// }
+func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
+ return WithDeadline(parent, time.Now().Add(timeout))
+}
+
+// WithValue returns a copy of parent in which the value associated with key is
+// val.
+//
+// Use context Values only for request-scoped data that transits processes and
+// APIs, not for passing optional parameters to functions.
+func WithValue(parent Context, key interface{}, val interface{}) Context {
+ return &valueCtx{parent, key, val}
+}
+
+// A valueCtx carries a key-value pair. It implements Value for that key and
+// delegates all other calls to the embedded Context.
+type valueCtx struct {
+ Context
+ key, val interface{}
+}
+
+func (c *valueCtx) String() string {
+ return fmt.Sprintf("%v.WithValue(%#v, %#v)", c.Context, c.key, c.val)
+}
+
+func (c *valueCtx) Value(key interface{}) interface{} {
+ if c.key == key {
+ return c.val
+ }
+ return c.Context.Value(key)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/context_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/context_test.go
new file mode 100644
index 000000000..faf67722a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/context_test.go
@@ -0,0 +1,575 @@
+// Copyright 2014 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.
+
+package context
+
+import (
+ "fmt"
+ "math/rand"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+)
+
+// otherContext is a Context that's not one of the types defined in context.go.
+// This lets us test code paths that differ based on the underlying type of the
+// Context.
+type otherContext struct {
+ Context
+}
+
+func TestBackground(t *testing.T) {
+ c := Background()
+ if c == nil {
+ t.Fatalf("Background returned nil")
+ }
+ select {
+ case x := <-c.Done():
+ t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ if got, want := fmt.Sprint(c), "context.Background"; got != want {
+ t.Errorf("Background().String() = %q want %q", got, want)
+ }
+}
+
+func TestTODO(t *testing.T) {
+ c := TODO()
+ if c == nil {
+ t.Fatalf("TODO returned nil")
+ }
+ select {
+ case x := <-c.Done():
+ t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ if got, want := fmt.Sprint(c), "context.TODO"; got != want {
+ t.Errorf("TODO().String() = %q want %q", got, want)
+ }
+}
+
+func TestWithCancel(t *testing.T) {
+ c1, cancel := WithCancel(Background())
+
+ if got, want := fmt.Sprint(c1), "context.Background.WithCancel"; got != want {
+ t.Errorf("c1.String() = %q want %q", got, want)
+ }
+
+ o := otherContext{c1}
+ c2, _ := WithCancel(o)
+ contexts := []Context{c1, o, c2}
+
+ for i, c := range contexts {
+ if d := c.Done(); d == nil {
+ t.Errorf("c[%d].Done() == %v want non-nil", i, d)
+ }
+ if e := c.Err(); e != nil {
+ t.Errorf("c[%d].Err() == %v want nil", i, e)
+ }
+
+ select {
+ case x := <-c.Done():
+ t.Errorf("<-c.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ }
+
+ cancel()
+ time.Sleep(100 * time.Millisecond) // let cancelation propagate
+
+ for i, c := range contexts {
+ select {
+ case <-c.Done():
+ default:
+ t.Errorf("<-c[%d].Done() blocked, but shouldn't have", i)
+ }
+ if e := c.Err(); e != Canceled {
+ t.Errorf("c[%d].Err() == %v want %v", i, e, Canceled)
+ }
+ }
+}
+
+func TestParentFinishesChild(t *testing.T) {
+ // Context tree:
+ // parent -> cancelChild
+ // parent -> valueChild -> timerChild
+ parent, cancel := WithCancel(Background())
+ cancelChild, stop := WithCancel(parent)
+ defer stop()
+ valueChild := WithValue(parent, "key", "value")
+ timerChild, stop := WithTimeout(valueChild, 10000*time.Hour)
+ defer stop()
+
+ select {
+ case x := <-parent.Done():
+ t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+ case x := <-cancelChild.Done():
+ t.Errorf("<-cancelChild.Done() == %v want nothing (it should block)", x)
+ case x := <-timerChild.Done():
+ t.Errorf("<-timerChild.Done() == %v want nothing (it should block)", x)
+ case x := <-valueChild.Done():
+ t.Errorf("<-valueChild.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+
+ // The parent's children should contain the two cancelable children.
+ pc := parent.(*cancelCtx)
+ cc := cancelChild.(*cancelCtx)
+ tc := timerChild.(*timerCtx)
+ pc.mu.Lock()
+ if len(pc.children) != 2 || !pc.children[cc] || !pc.children[tc] {
+ t.Errorf("bad linkage: pc.children = %v, want %v and %v",
+ pc.children, cc, tc)
+ }
+ pc.mu.Unlock()
+
+ if p, ok := parentCancelCtx(cc.Context); !ok || p != pc {
+ t.Errorf("bad linkage: parentCancelCtx(cancelChild.Context) = %v, %v want %v, true", p, ok, pc)
+ }
+ if p, ok := parentCancelCtx(tc.Context); !ok || p != pc {
+ t.Errorf("bad linkage: parentCancelCtx(timerChild.Context) = %v, %v want %v, true", p, ok, pc)
+ }
+
+ cancel()
+
+ pc.mu.Lock()
+ if len(pc.children) != 0 {
+ t.Errorf("pc.cancel didn't clear pc.children = %v", pc.children)
+ }
+ pc.mu.Unlock()
+
+ // parent and children should all be finished.
+ check := func(ctx Context, name string) {
+ select {
+ case <-ctx.Done():
+ default:
+ t.Errorf("<-%s.Done() blocked, but shouldn't have", name)
+ }
+ if e := ctx.Err(); e != Canceled {
+ t.Errorf("%s.Err() == %v want %v", name, e, Canceled)
+ }
+ }
+ check(parent, "parent")
+ check(cancelChild, "cancelChild")
+ check(valueChild, "valueChild")
+ check(timerChild, "timerChild")
+
+ // WithCancel should return a canceled context on a canceled parent.
+ precanceledChild := WithValue(parent, "key", "value")
+ select {
+ case <-precanceledChild.Done():
+ default:
+ t.Errorf("<-precanceledChild.Done() blocked, but shouldn't have")
+ }
+ if e := precanceledChild.Err(); e != Canceled {
+ t.Errorf("precanceledChild.Err() == %v want %v", e, Canceled)
+ }
+}
+
+func TestChildFinishesFirst(t *testing.T) {
+ cancelable, stop := WithCancel(Background())
+ defer stop()
+ for _, parent := range []Context{Background(), cancelable} {
+ child, cancel := WithCancel(parent)
+
+ select {
+ case x := <-parent.Done():
+ t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+ case x := <-child.Done():
+ t.Errorf("<-child.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+
+ cc := child.(*cancelCtx)
+ pc, pcok := parent.(*cancelCtx) // pcok == false when parent == Background()
+ if p, ok := parentCancelCtx(cc.Context); ok != pcok || (ok && pc != p) {
+ t.Errorf("bad linkage: parentCancelCtx(cc.Context) = %v, %v want %v, %v", p, ok, pc, pcok)
+ }
+
+ if pcok {
+ pc.mu.Lock()
+ if len(pc.children) != 1 || !pc.children[cc] {
+ t.Errorf("bad linkage: pc.children = %v, cc = %v", pc.children, cc)
+ }
+ pc.mu.Unlock()
+ }
+
+ cancel()
+
+ if pcok {
+ pc.mu.Lock()
+ if len(pc.children) != 0 {
+ t.Errorf("child's cancel didn't remove self from pc.children = %v", pc.children)
+ }
+ pc.mu.Unlock()
+ }
+
+ // child should be finished.
+ select {
+ case <-child.Done():
+ default:
+ t.Errorf("<-child.Done() blocked, but shouldn't have")
+ }
+ if e := child.Err(); e != Canceled {
+ t.Errorf("child.Err() == %v want %v", e, Canceled)
+ }
+
+ // parent should not be finished.
+ select {
+ case x := <-parent.Done():
+ t.Errorf("<-parent.Done() == %v want nothing (it should block)", x)
+ default:
+ }
+ if e := parent.Err(); e != nil {
+ t.Errorf("parent.Err() == %v want nil", e)
+ }
+ }
+}
+
+func testDeadline(c Context, wait time.Duration, t *testing.T) {
+ select {
+ case <-time.After(wait):
+ t.Fatalf("context should have timed out")
+ case <-c.Done():
+ }
+ if e := c.Err(); e != DeadlineExceeded {
+ t.Errorf("c.Err() == %v want %v", e, DeadlineExceeded)
+ }
+}
+
+func TestDeadline(t *testing.T) {
+ c, _ := WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
+ t.Errorf("c.String() = %q want prefix %q", got, prefix)
+ }
+ testDeadline(c, 200*time.Millisecond, t)
+
+ c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ o := otherContext{c}
+ testDeadline(o, 200*time.Millisecond, t)
+
+ c, _ = WithDeadline(Background(), time.Now().Add(100*time.Millisecond))
+ o = otherContext{c}
+ c, _ = WithDeadline(o, time.Now().Add(300*time.Millisecond))
+ testDeadline(c, 200*time.Millisecond, t)
+}
+
+func TestTimeout(t *testing.T) {
+ c, _ := WithTimeout(Background(), 100*time.Millisecond)
+ if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
+ t.Errorf("c.String() = %q want prefix %q", got, prefix)
+ }
+ testDeadline(c, 200*time.Millisecond, t)
+
+ c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ o := otherContext{c}
+ testDeadline(o, 200*time.Millisecond, t)
+
+ c, _ = WithTimeout(Background(), 100*time.Millisecond)
+ o = otherContext{c}
+ c, _ = WithTimeout(o, 300*time.Millisecond)
+ testDeadline(c, 200*time.Millisecond, t)
+}
+
+func TestCanceledTimeout(t *testing.T) {
+ c, _ := WithTimeout(Background(), 200*time.Millisecond)
+ o := otherContext{c}
+ c, cancel := WithTimeout(o, 400*time.Millisecond)
+ cancel()
+ time.Sleep(100 * time.Millisecond) // let cancelation propagate
+ select {
+ case <-c.Done():
+ default:
+ t.Errorf("<-c.Done() blocked, but shouldn't have")
+ }
+ if e := c.Err(); e != Canceled {
+ t.Errorf("c.Err() == %v want %v", e, Canceled)
+ }
+}
+
+type key1 int
+type key2 int
+
+var k1 = key1(1)
+var k2 = key2(1) // same int as k1, different type
+var k3 = key2(3) // same type as k2, different int
+
+func TestValues(t *testing.T) {
+ check := func(c Context, nm, v1, v2, v3 string) {
+ if v, ok := c.Value(k1).(string); ok == (len(v1) == 0) || v != v1 {
+ t.Errorf(`%s.Value(k1).(string) = %q, %t want %q, %t`, nm, v, ok, v1, len(v1) != 0)
+ }
+ if v, ok := c.Value(k2).(string); ok == (len(v2) == 0) || v != v2 {
+ t.Errorf(`%s.Value(k2).(string) = %q, %t want %q, %t`, nm, v, ok, v2, len(v2) != 0)
+ }
+ if v, ok := c.Value(k3).(string); ok == (len(v3) == 0) || v != v3 {
+ t.Errorf(`%s.Value(k3).(string) = %q, %t want %q, %t`, nm, v, ok, v3, len(v3) != 0)
+ }
+ }
+
+ c0 := Background()
+ check(c0, "c0", "", "", "")
+
+ c1 := WithValue(Background(), k1, "c1k1")
+ check(c1, "c1", "c1k1", "", "")
+
+ if got, want := fmt.Sprint(c1), `context.Background.WithValue(1, "c1k1")`; got != want {
+ t.Errorf("c.String() = %q want %q", got, want)
+ }
+
+ c2 := WithValue(c1, k2, "c2k2")
+ check(c2, "c2", "c1k1", "c2k2", "")
+
+ c3 := WithValue(c2, k3, "c3k3")
+ check(c3, "c2", "c1k1", "c2k2", "c3k3")
+
+ c4 := WithValue(c3, k1, nil)
+ check(c4, "c4", "", "c2k2", "c3k3")
+
+ o0 := otherContext{Background()}
+ check(o0, "o0", "", "", "")
+
+ o1 := otherContext{WithValue(Background(), k1, "c1k1")}
+ check(o1, "o1", "c1k1", "", "")
+
+ o2 := WithValue(o1, k2, "o2k2")
+ check(o2, "o2", "c1k1", "o2k2", "")
+
+ o3 := otherContext{c4}
+ check(o3, "o3", "", "c2k2", "c3k3")
+
+ o4 := WithValue(o3, k3, nil)
+ check(o4, "o4", "", "c2k2", "")
+}
+
+func TestAllocs(t *testing.T) {
+ bg := Background()
+ for _, test := range []struct {
+ desc string
+ f func()
+ limit float64
+ gccgoLimit float64
+ }{
+ {
+ desc: "Background()",
+ f: func() { Background() },
+ limit: 0,
+ gccgoLimit: 0,
+ },
+ {
+ desc: fmt.Sprintf("WithValue(bg, %v, nil)", k1),
+ f: func() {
+ c := WithValue(bg, k1, nil)
+ c.Value(k1)
+ },
+ limit: 3,
+ gccgoLimit: 3,
+ },
+ {
+ desc: "WithTimeout(bg, 15*time.Millisecond)",
+ f: func() {
+ c, _ := WithTimeout(bg, 15*time.Millisecond)
+ <-c.Done()
+ },
+ limit: 8,
+ gccgoLimit: 13,
+ },
+ {
+ desc: "WithCancel(bg)",
+ f: func() {
+ c, cancel := WithCancel(bg)
+ cancel()
+ <-c.Done()
+ },
+ limit: 5,
+ gccgoLimit: 8,
+ },
+ {
+ desc: "WithTimeout(bg, 100*time.Millisecond)",
+ f: func() {
+ c, cancel := WithTimeout(bg, 100*time.Millisecond)
+ cancel()
+ <-c.Done()
+ },
+ limit: 8,
+ gccgoLimit: 25,
+ },
+ } {
+ limit := test.limit
+ if runtime.Compiler == "gccgo" {
+ // gccgo does not yet do escape analysis.
+ // TOOD(iant): Remove this when gccgo does do escape analysis.
+ limit = test.gccgoLimit
+ }
+ if n := testing.AllocsPerRun(100, test.f); n > limit {
+ t.Errorf("%s allocs = %f want %d", test.desc, n, int(limit))
+ }
+ }
+}
+
+func TestSimultaneousCancels(t *testing.T) {
+ root, cancel := WithCancel(Background())
+ m := map[Context]CancelFunc{root: cancel}
+ q := []Context{root}
+ // Create a tree of contexts.
+ for len(q) != 0 && len(m) < 100 {
+ parent := q[0]
+ q = q[1:]
+ for i := 0; i < 4; i++ {
+ ctx, cancel := WithCancel(parent)
+ m[ctx] = cancel
+ q = append(q, ctx)
+ }
+ }
+ // Start all the cancels in a random order.
+ var wg sync.WaitGroup
+ wg.Add(len(m))
+ for _, cancel := range m {
+ go func(cancel CancelFunc) {
+ cancel()
+ wg.Done()
+ }(cancel)
+ }
+ // Wait on all the contexts in a random order.
+ for ctx := range m {
+ select {
+ case <-ctx.Done():
+ case <-time.After(1 * time.Second):
+ buf := make([]byte, 10<<10)
+ n := runtime.Stack(buf, true)
+ t.Fatalf("timed out waiting for <-ctx.Done(); stacks:\n%s", buf[:n])
+ }
+ }
+ // Wait for all the cancel functions to return.
+ done := make(chan struct{})
+ go func() {
+ wg.Wait()
+ close(done)
+ }()
+ select {
+ case <-done:
+ case <-time.After(1 * time.Second):
+ buf := make([]byte, 10<<10)
+ n := runtime.Stack(buf, true)
+ t.Fatalf("timed out waiting for cancel functions; stacks:\n%s", buf[:n])
+ }
+}
+
+func TestInterlockedCancels(t *testing.T) {
+ parent, cancelParent := WithCancel(Background())
+ child, cancelChild := WithCancel(parent)
+ go func() {
+ parent.Done()
+ cancelChild()
+ }()
+ cancelParent()
+ select {
+ case <-child.Done():
+ case <-time.After(1 * time.Second):
+ buf := make([]byte, 10<<10)
+ n := runtime.Stack(buf, true)
+ t.Fatalf("timed out waiting for child.Done(); stacks:\n%s", buf[:n])
+ }
+}
+
+func TestLayersCancel(t *testing.T) {
+ testLayers(t, time.Now().UnixNano(), false)
+}
+
+func TestLayersTimeout(t *testing.T) {
+ testLayers(t, time.Now().UnixNano(), true)
+}
+
+func testLayers(t *testing.T, seed int64, testTimeout bool) {
+ rand.Seed(seed)
+ errorf := func(format string, a ...interface{}) {
+ t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
+ }
+ const (
+ timeout = 200 * time.Millisecond
+ minLayers = 30
+ )
+ type value int
+ var (
+ vals []*value
+ cancels []CancelFunc
+ numTimers int
+ ctx = Background()
+ )
+ for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
+ switch rand.Intn(3) {
+ case 0:
+ v := new(value)
+ ctx = WithValue(ctx, v, v)
+ vals = append(vals, v)
+ case 1:
+ var cancel CancelFunc
+ ctx, cancel = WithCancel(ctx)
+ cancels = append(cancels, cancel)
+ case 2:
+ var cancel CancelFunc
+ ctx, cancel = WithTimeout(ctx, timeout)
+ cancels = append(cancels, cancel)
+ numTimers++
+ }
+ }
+ checkValues := func(when string) {
+ for _, key := range vals {
+ if val := ctx.Value(key).(*value); key != val {
+ errorf("%s: ctx.Value(%p) = %p want %p", when, key, val, key)
+ }
+ }
+ }
+ select {
+ case <-ctx.Done():
+ errorf("ctx should not be canceled yet")
+ default:
+ }
+ if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
+ t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
+ }
+ t.Log(ctx)
+ checkValues("before cancel")
+ if testTimeout {
+ select {
+ case <-ctx.Done():
+ case <-time.After(timeout + timeout/10):
+ errorf("ctx should have timed out")
+ }
+ checkValues("after timeout")
+ } else {
+ cancel := cancels[rand.Intn(len(cancels))]
+ cancel()
+ select {
+ case <-ctx.Done():
+ default:
+ errorf("ctx should be canceled")
+ }
+ checkValues("after cancel")
+ }
+}
+
+func TestCancelRemoves(t *testing.T) {
+ checkChildren := func(when string, ctx Context, want int) {
+ if got := len(ctx.(*cancelCtx).children); got != want {
+ t.Errorf("%s: context has %d children, want %d", when, got, want)
+ }
+ }
+
+ ctx, _ := WithCancel(Background())
+ checkChildren("after creation", ctx, 0)
+ _, cancel := WithCancel(ctx)
+ checkChildren("with WithCancel child ", ctx, 1)
+ cancel()
+ checkChildren("after cancelling WithCancel child", ctx, 0)
+
+ ctx, _ = WithCancel(Background())
+ checkChildren("after creation", ctx, 0)
+ _, cancel = WithTimeout(ctx, 60*time.Minute)
+ checkChildren("with WithTimeout child ", ctx, 1)
+ cancel()
+ checkChildren("after cancelling WithTimeout child", ctx, 0)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go
new file mode 100644
index 000000000..04adc00f7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context/withtimeout_test.go
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+package context_test
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+func ExampleWithTimeout() {
+ // Pass a context with a timeout to tell a blocking function that it
+ // should abandon its work after the timeout elapses.
+ ctx, _ := context.WithTimeout(context.Background(), 100*time.Millisecond)
+ select {
+ case <-time.After(200 * time.Millisecond):
+ fmt.Println("overslept")
+ case <-ctx.Done():
+ fmt.Println(ctx.Err()) // prints "context deadline exceeded"
+ }
+ // Output:
+ // context deadline exceeded
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/AUTHORS
new file mode 100644
index 000000000..15167cd74
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/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/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md
new file mode 100644
index 000000000..46aa2b12d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTING.md
@@ -0,0 +1,31 @@
+# Contributing to Go
+
+Go is an open source project.
+
+It is the work of hundreds of contributors. We appreciate your help!
+
+
+## Filing issues
+
+When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
+The gophers there will answer or ask you to file an issue if you've tripped over a bug.
+
+## Contributing code
+
+Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
+before sending patches.
+
+**We do not accept GitHub pull requests**
+(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
+
+Unless otherwise noted, the Go source files are distributed under
+the BSD-style license found in the LICENSE file.
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTORS b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/CONTRIBUTORS
new file mode 100644
index 000000000..1c4577e96
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/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/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/LICENSE
new file mode 100644
index 000000000..d02f24fd5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2009 The oauth2 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/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/README.md
new file mode 100644
index 000000000..a5afeca22
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/README.md
@@ -0,0 +1,64 @@
+# OAuth2 for Go
+
+[](https://travis-ci.org/golang/oauth2)
+
+oauth2 package contains a client implementation for OAuth 2.0 spec.
+
+## Installation
+
+~~~~
+go get golang.org/x/oauth2
+~~~~
+
+See godoc for further documentation and examples.
+
+* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
+* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
+
+
+## App Engine
+
+In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor
+of the [`context.Context`](https://golang.org/x/net/context#Context) type from
+the `golang.org/x/net/context` package
+
+This means its no longer possible to use the "Classic App Engine"
+`appengine.Context` type with the `oauth2` package. (You're using
+Classic App Engine if you import the package `"appengine"`.)
+
+To work around this, you may use the new `"google.golang.org/appengine"`
+package. This package has almost the same API as the `"appengine"` package,
+but it can be fetched with `go get` and used on "Managed VMs" and well as
+Classic App Engine.
+
+See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
+for information on updating your app.
+
+If you don't want to update your entire app to use the new App Engine packages,
+you may use both sets of packages in parallel, using only the new packages
+with the `oauth2` package.
+
+ import (
+ "golang.org/x/net/context"
+ "golang.org/x/oauth2"
+ "golang.org/x/oauth2/google"
+ newappengine "google.golang.org/appengine"
+ newurlftech "google.golang.org/urlfetch"
+
+ "appengine"
+ )
+
+ func handler(w http.ResponseWriter, r *http.Request) {
+ var c appengine.Context = appengine.NewContext(r)
+ c.Infof("Logging a message with the old package")
+
+ var ctx context.Context = newappengine.NewContext(r)
+ client := &http.Client{
+ Transport: &oauth2.Transport{
+ Source: google.AppEngineTokenSource(ctx, "scope"),
+ Base: &newurlfetch.Transport{Context: ctx},
+ },
+ }
+ client.Get("...")
+ }
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go
new file mode 100644
index 000000000..52585bc77
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/client_appengine.go
@@ -0,0 +1,24 @@
+// Copyright 2014 The oauth2 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 appenginevm
+
+// App Engine hooks.
+
+package oauth2
+
+import (
+ "net/http"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "google.golang.org/appengine/urlfetch"
+)
+
+func init() {
+ registerContextClientFunc(contextClientAppEngine)
+}
+
+func contextClientAppEngine(ctx context.Context) (*http.Client, error) {
+ return urlfetch.Client(ctx), nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go
new file mode 100644
index 000000000..4013abb35
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/example_test.go
@@ -0,0 +1,45 @@
+// Copyright 2014 The oauth2 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 oauth2_test
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+func ExampleConfig() {
+ conf := &oauth2.Config{
+ ClientID: "YOUR_CLIENT_ID",
+ ClientSecret: "YOUR_CLIENT_SECRET",
+ Scopes: []string{"SCOPE1", "SCOPE2"},
+ Endpoint: oauth2.Endpoint{
+ AuthURL: "https://provider.com/o/oauth2/auth",
+ TokenURL: "https://provider.com/o/oauth2/token",
+ },
+ }
+
+ // Redirect user to consent page to ask for permission
+ // for the scopes specified above.
+ url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
+ fmt.Printf("Visit the URL for the auth dialog: %v", url)
+
+ // Use the authorization code that is pushed to the redirect URL.
+ // NewTransportWithCode will do the handshake to retrieve
+ // an access token and initiate a Transport that is
+ // authorized and authenticated by the retrieved token.
+ var code string
+ if _, err := fmt.Scan(&code); err != nil {
+ log.Fatal(err)
+ }
+ tok, err := conf.Exchange(oauth2.NoContext, code)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ client := conf.Client(oauth2.NoContext, tok)
+ client.Get("...")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go
new file mode 100644
index 000000000..c1634d3da
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/facebook/facebook.go
@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 facebook provides constants for using OAuth2 to access Facebook.
+package facebook
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Endpoint is Facebook's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://www.facebook.com/dialog/oauth",
+ TokenURL: "https://graph.facebook.com/oauth/access_token",
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go
new file mode 100644
index 000000000..4ea036a86
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/github/github.go
@@ -0,0 +1,16 @@
+// Copyright 2014 The oauth2 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 github provides constants for using OAuth2 to access Github.
+package github
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Endpoint is Github's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://github.com/login/oauth/authorize",
+ TokenURL: "https://github.com/login/oauth/access_token",
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go
new file mode 100644
index 000000000..72622284a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine.go
@@ -0,0 +1,83 @@
+// Copyright 2014 The oauth2 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 google
+
+import (
+ "sort"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
+var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)
+
+// AppEngineTokenSource returns a token source that fetches tokens
+// issued to the current App Engine application's service account.
+// If you are implementing a 3-legged OAuth 2.0 flow on App Engine
+// that involves user accounts, see oauth2.Config instead.
+//
+// The provided context must have come from appengine.NewContext.
+func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource {
+ if appengineTokenFunc == nil {
+ panic("google: AppEngineTokenSource can only be used on App Engine.")
+ }
+ scopes := append([]string{}, scope...)
+ sort.Strings(scopes)
+ return &appEngineTokenSource{
+ ctx: ctx,
+ scopes: scopes,
+ key: strings.Join(scopes, " "),
+ }
+}
+
+// aeTokens helps the fetched tokens to be reused until their expiration.
+var (
+ aeTokensMu sync.Mutex
+ aeTokens = make(map[string]*tokenLock) // key is space-separated scopes
+)
+
+type tokenLock struct {
+ mu sync.Mutex // guards t; held while fetching or updating t
+ t *oauth2.Token
+}
+
+type appEngineTokenSource struct {
+ ctx context.Context
+ scopes []string
+ key string // to aeTokens map; space-separated scopes
+}
+
+func (ts *appEngineTokenSource) Token() (*oauth2.Token, error) {
+ if appengineTokenFunc == nil {
+ panic("google: AppEngineTokenSource can only be used on App Engine.")
+ }
+
+ aeTokensMu.Lock()
+ tok, ok := aeTokens[ts.key]
+ if !ok {
+ tok = &tokenLock{}
+ aeTokens[ts.key] = tok
+ }
+ aeTokensMu.Unlock()
+
+ tok.mu.Lock()
+ defer tok.mu.Unlock()
+ if tok.t.Valid() {
+ return tok.t, nil
+ }
+ access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...)
+ if err != nil {
+ return nil, err
+ }
+ tok.t = &oauth2.Token{
+ AccessToken: access,
+ Expiry: exp,
+ }
+ return tok.t, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go
new file mode 100644
index 000000000..2f9b15432
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/appengine_hook.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The oauth2 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 appenginevm
+
+package google
+
+import "google.golang.org/appengine"
+
+func init() {
+ appengineTokenFunc = appengine.AccessToken
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go
new file mode 100644
index 000000000..9a622be4a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/default.go
@@ -0,0 +1,154 @@
+// Copyright 2015 The oauth2 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 google
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "path/filepath"
+ "runtime"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata"
+)
+
+// DefaultClient returns an HTTP Client that uses the
+// DefaultTokenSource to obtain authentication credentials.
+//
+// This client should be used when developing services
+// that run on Google App Engine or Google Compute Engine
+// and use "Application Default Credentials."
+//
+// For more details, see:
+// https://developers.google.com/accounts/application-default-credentials
+//
+func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
+ ts, err := DefaultTokenSource(ctx, scope...)
+ if err != nil {
+ return nil, err
+ }
+ return oauth2.NewClient(ctx, ts), nil
+}
+
+// DefaultTokenSource is a token source that uses
+// "Application Default Credentials".
+//
+// It looks for credentials in the following places,
+// preferring the first location found:
+//
+// 1. A JSON file whose path is specified by the
+// GOOGLE_APPLICATION_CREDENTIALS environment variable.
+// 2. A JSON file in a location known to the gcloud command-line tool.
+// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
+// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
+// 3. On Google App Engine it uses the appengine.AccessToken function.
+// 4. On Google Compute Engine, it fetches credentials from the metadata server.
+// (In this final case any provided scopes are ignored.)
+//
+// For more details, see:
+// https://developers.google.com/accounts/application-default-credentials
+//
+func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
+ // First, try the environment variable.
+ const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
+ if filename := os.Getenv(envVar); filename != "" {
+ ts, err := tokenSourceFromFile(ctx, filename, scope)
+ if err != nil {
+ return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
+ }
+ return ts, nil
+ }
+
+ // Second, try a well-known file.
+ filename := wellKnownFile()
+ _, err := os.Stat(filename)
+ if err == nil {
+ ts, err2 := tokenSourceFromFile(ctx, filename, scope)
+ if err2 == nil {
+ return ts, nil
+ }
+ err = err2
+ } else if os.IsNotExist(err) {
+ err = nil // ignore this error
+ }
+ if err != nil {
+ return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
+ }
+
+ // Third, if we're on Google App Engine use those credentials.
+ if appengineTokenFunc != nil {
+ return AppEngineTokenSource(ctx, scope...), nil
+ }
+
+ // Fourth, if we're on Google Compute Engine use the metadata server.
+ if metadata.OnGCE() {
+ return ComputeTokenSource(""), nil
+ }
+
+ // None are found; return helpful error.
+ const url = "https://developers.google.com/accounts/application-default-credentials"
+ return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
+}
+
+func wellKnownFile() string {
+ const f = "application_default_credentials.json"
+ if runtime.GOOS == "windows" {
+ return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
+ }
+ return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
+}
+
+func tokenSourceFromFile(ctx context.Context, filename string, scopes []string) (oauth2.TokenSource, error) {
+ b, err := ioutil.ReadFile(filename)
+ if err != nil {
+ return nil, err
+ }
+ var d struct {
+ // Common fields
+ Type string
+ ClientID string `json:"client_id"`
+
+ // User Credential fields
+ ClientSecret string `json:"client_secret"`
+ RefreshToken string `json:"refresh_token"`
+
+ // Service Account fields
+ ClientEmail string `json:"client_email"`
+ PrivateKeyID string `json:"private_key_id"`
+ PrivateKey string `json:"private_key"`
+ }
+ if err := json.Unmarshal(b, &d); err != nil {
+ return nil, err
+ }
+ switch d.Type {
+ case "authorized_user":
+ cfg := &oauth2.Config{
+ ClientID: d.ClientID,
+ ClientSecret: d.ClientSecret,
+ Scopes: append([]string{}, scopes...), // copy
+ Endpoint: Endpoint,
+ }
+ tok := &oauth2.Token{RefreshToken: d.RefreshToken}
+ return cfg.TokenSource(ctx, tok), nil
+ case "service_account":
+ cfg := &jwt.Config{
+ Email: d.ClientEmail,
+ PrivateKey: []byte(d.PrivateKey),
+ Scopes: append([]string{}, scopes...), // copy
+ TokenURL: JWTTokenURL,
+ }
+ return cfg.TokenSource(ctx), nil
+ case "":
+ return nil, errors.New("missing 'type' field in credentials")
+ default:
+ return nil, fmt.Errorf("unknown credential type: %q", d.Type)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go
new file mode 100644
index 000000000..f0b57416c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/example_test.go
@@ -0,0 +1,150 @@
+// Copyright 2014 The oauth2 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 appenginevm !appengine
+
+package google_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt"
+ "google.golang.org/appengine"
+ "google.golang.org/appengine/urlfetch"
+)
+
+func ExampleDefaultClient() {
+ client, err := google.DefaultClient(oauth2.NoContext,
+ "https://www.googleapis.com/auth/devstorage.full_control")
+ if err != nil {
+ log.Fatal(err)
+ }
+ client.Get("...")
+}
+
+func Example_webServer() {
+ // Your credentials should be obtained from the Google
+ // Developer Console (https://console.developers.google.com).
+ conf := &oauth2.Config{
+ ClientID: "YOUR_CLIENT_ID",
+ ClientSecret: "YOUR_CLIENT_SECRET",
+ RedirectURL: "YOUR_REDIRECT_URL",
+ Scopes: []string{
+ "https://www.googleapis.com/auth/bigquery",
+ "https://www.googleapis.com/auth/blogger",
+ },
+ Endpoint: google.Endpoint,
+ }
+ // Redirect user to Google's consent page to ask for permission
+ // for the scopes specified above.
+ url := conf.AuthCodeURL("state")
+ fmt.Printf("Visit the URL for the auth dialog: %v", url)
+
+ // Handle the exchange code to initiate a transport.
+ tok, err := conf.Exchange(oauth2.NoContext, "authorization-code")
+ if err != nil {
+ log.Fatal(err)
+ }
+ client := conf.Client(oauth2.NoContext, tok)
+ client.Get("...")
+}
+
+func ExampleJWTConfigFromJSON() {
+ // Your credentials should be obtained from the Google
+ // Developer Console (https://console.developers.google.com).
+ // Navigate to your project, then see the "Credentials" page
+ // under "APIs & Auth".
+ // To create a service account client, click "Create new Client ID",
+ // select "Service Account", and click "Create Client ID". A JSON
+ // key file will then be downloaded to your computer.
+ data, err := ioutil.ReadFile("/path/to/your-project-key.json")
+ if err != nil {
+ log.Fatal(err)
+ }
+ conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery")
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Initiate an http.Client. The following GET request will be
+ // authorized and authenticated on the behalf of
+ // your service account.
+ client := conf.Client(oauth2.NoContext)
+ client.Get("...")
+}
+
+func ExampleSDKConfig() {
+ // The credentials will be obtained from the first account that
+ // has been authorized with `gcloud auth login`.
+ conf, err := google.NewSDKConfig("")
+ if err != nil {
+ log.Fatal(err)
+ }
+ // Initiate an http.Client. The following GET request will be
+ // authorized and authenticated on the behalf of the SDK user.
+ client := conf.Client(oauth2.NoContext)
+ client.Get("...")
+}
+
+func Example_serviceAccount() {
+ // Your credentials should be obtained from the Google
+ // Developer Console (https://console.developers.google.com).
+ conf := &jwt.Config{
+ Email: "xxx@developer.gserviceaccount.com",
+ // The contents of your RSA private key or your PEM file
+ // that contains a private key.
+ // If you have a p12 file instead, you
+ // can use `openssl` to export the private key into a pem file.
+ //
+ // $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
+ //
+ // The field only supports PEM containers with no passphrase.
+ // The openssl command will convert p12 keys to passphrase-less PEM containers.
+ PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
+ Scopes: []string{
+ "https://www.googleapis.com/auth/bigquery",
+ "https://www.googleapis.com/auth/blogger",
+ },
+ TokenURL: google.JWTTokenURL,
+ // If you would like to impersonate a user, you can
+ // create a transport with a subject. The following GET
+ // request will be made on the behalf of user@example.com.
+ // Optional.
+ Subject: "user@example.com",
+ }
+ // Initiate an http.Client, the following GET request will be
+ // authorized and authenticated on the behalf of user@example.com.
+ client := conf.Client(oauth2.NoContext)
+ client.Get("...")
+}
+
+func ExampleAppEngineTokenSource() {
+ var req *http.Request // from the ServeHTTP handler
+ ctx := appengine.NewContext(req)
+ client := &http.Client{
+ Transport: &oauth2.Transport{
+ Source: google.AppEngineTokenSource(ctx, "https://www.googleapis.com/auth/bigquery"),
+ Base: &urlfetch.Transport{
+ Context: ctx,
+ },
+ },
+ }
+ client.Get("...")
+}
+
+func ExampleComputeTokenSource() {
+ client := &http.Client{
+ Transport: &oauth2.Transport{
+ // Fetch from Google Compute Engine's metadata server to retrieve
+ // an access token for the provided account.
+ // If no account is specified, "default" is used.
+ Source: google.ComputeTokenSource(""),
+ },
+ }
+ client.Get("...")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go
new file mode 100644
index 000000000..b8abae7a8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/google.go
@@ -0,0 +1,145 @@
+// Copyright 2014 The oauth2 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 google provides support for making OAuth2 authorized and
+// authenticated HTTP requests to Google APIs.
+// It supports the Web server flow, client-side credentials, service accounts,
+// Google Compute Engine service accounts, and Google App Engine service
+// accounts.
+//
+// For more information, please read
+// https://developers.google.com/accounts/docs/OAuth2
+// and
+// https://developers.google.com/accounts/application-default-credentials.
+package google
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata"
+)
+
+// Endpoint is Google's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://accounts.google.com/o/oauth2/auth",
+ TokenURL: "https://accounts.google.com/o/oauth2/token",
+}
+
+// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
+const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
+
+// ConfigFromJSON uses a Google Developers Console client_credentials.json
+// file to construct a config.
+// client_credentials.json can be downloadable from https://console.developers.google.com,
+// under "APIs & Auth" > "Credentials". Download the Web application credentials in the
+// JSON format and provide the contents of the file as jsonKey.
+func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
+ type cred struct {
+ ClientID string `json:"client_id"`
+ ClientSecret string `json:"client_secret"`
+ RedirectURIs []string `json:"redirect_uris"`
+ AuthURI string `json:"auth_uri"`
+ TokenURI string `json:"token_uri"`
+ }
+ var j struct {
+ Web *cred `json:"web"`
+ Installed *cred `json:"installed"`
+ }
+ if err := json.Unmarshal(jsonKey, &j); err != nil {
+ return nil, err
+ }
+ var c *cred
+ switch {
+ case j.Web != nil:
+ c = j.Web
+ case j.Installed != nil:
+ c = j.Installed
+ default:
+ return nil, fmt.Errorf("oauth2/google: no credentials found")
+ }
+ if len(c.RedirectURIs) < 1 {
+ return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json")
+ }
+ return &oauth2.Config{
+ ClientID: c.ClientID,
+ ClientSecret: c.ClientSecret,
+ RedirectURL: c.RedirectURIs[0],
+ Scopes: scope,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: c.AuthURI,
+ TokenURL: c.TokenURI,
+ },
+ }, nil
+}
+
+// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
+// the credentials that authorize and authenticate the requests.
+// Create a service account on "Credentials" page under "APIs & Auth" for your
+// project at https://console.developers.google.com to download a JSON key file.
+func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
+ var key struct {
+ Email string `json:"client_email"`
+ PrivateKey string `json:"private_key"`
+ }
+ if err := json.Unmarshal(jsonKey, &key); err != nil {
+ return nil, err
+ }
+ return &jwt.Config{
+ Email: key.Email,
+ PrivateKey: []byte(key.PrivateKey),
+ Scopes: scope,
+ TokenURL: JWTTokenURL,
+ }, nil
+}
+
+// ComputeTokenSource returns a token source that fetches access tokens
+// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
+// this token source if your program is running on a GCE instance.
+// If no account is specified, "default" is used.
+// Further information about retrieving access tokens from the GCE metadata
+// server can be found at https://cloud.google.com/compute/docs/authentication.
+func ComputeTokenSource(account string) oauth2.TokenSource {
+ return oauth2.ReuseTokenSource(nil, computeSource{account: account})
+}
+
+type computeSource struct {
+ account string
+}
+
+func (cs computeSource) Token() (*oauth2.Token, error) {
+ if !metadata.OnGCE() {
+ return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
+ }
+ acct := cs.account
+ if acct == "" {
+ acct = "default"
+ }
+ tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token")
+ if err != nil {
+ return nil, err
+ }
+ var res struct {
+ AccessToken string `json:"access_token"`
+ ExpiresInSec int `json:"expires_in"`
+ TokenType string `json:"token_type"`
+ }
+ err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
+ if err != nil {
+ return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
+ }
+ if res.ExpiresInSec == 0 || res.AccessToken == "" {
+ return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
+ }
+ return &oauth2.Token{
+ AccessToken: res.AccessToken,
+ TokenType: res.TokenType,
+ Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
+ }, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go
new file mode 100644
index 000000000..4cc01884b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/google_test.go
@@ -0,0 +1,67 @@
+// Copyright 2015 The oauth2 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 google
+
+import (
+ "strings"
+ "testing"
+)
+
+var webJSONKey = []byte(`
+{
+ "web": {
+ "auth_uri": "https://google.com/o/oauth2/auth",
+ "client_secret": "3Oknc4jS_wA2r9i",
+ "token_uri": "https://google.com/o/oauth2/token",
+ "client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
+ "redirect_uris": ["https://www.example.com/oauth2callback"],
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
+ "client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com",
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
+ "javascript_origins": ["https://www.example.com"]
+ }
+}`)
+
+var installedJSONKey = []byte(`{
+ "installed": {
+ "client_id": "222-installed.apps.googleusercontent.com",
+ "redirect_uris": ["https://www.example.com/oauth2callback"]
+ }
+}`)
+
+func TestConfigFromJSON(t *testing.T) {
+ conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2")
+ if err != nil {
+ t.Error(err)
+ }
+ if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want {
+ t.Errorf("ClientID = %q; want %q", got, want)
+ }
+ if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want {
+ t.Errorf("ClientSecret = %q; want %q", got, want)
+ }
+ if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want {
+ t.Errorf("RedictURL = %q; want %q", got, want)
+ }
+ if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want {
+ t.Errorf("Scopes = %q; want %q", got, want)
+ }
+ if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want {
+ t.Errorf("AuthURL = %q; want %q", got, want)
+ }
+ if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want {
+ t.Errorf("TokenURL = %q; want %q", got, want)
+ }
+}
+
+func TestConfigFromJSON_Installed(t *testing.T) {
+ conf, err := ConfigFromJSON(installedJSONKey)
+ if err != nil {
+ t.Error(err)
+ }
+ if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want {
+ t.Errorf("ClientID = %q; want %q", got, want)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go
new file mode 100644
index 000000000..f56a7737c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk.go
@@ -0,0 +1,168 @@
+// Copyright 2015 The oauth2 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 google
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "os"
+ "os/user"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal"
+)
+
+type sdkCredentials struct {
+ Data []struct {
+ Credential struct {
+ ClientID string `json:"client_id"`
+ ClientSecret string `json:"client_secret"`
+ AccessToken string `json:"access_token"`
+ RefreshToken string `json:"refresh_token"`
+ TokenExpiry *time.Time `json:"token_expiry"`
+ } `json:"credential"`
+ Key struct {
+ Account string `json:"account"`
+ Scope string `json:"scope"`
+ } `json:"key"`
+ }
+}
+
+// An SDKConfig provides access to tokens from an account already
+// authorized via the Google Cloud SDK.
+type SDKConfig struct {
+ conf oauth2.Config
+ initialToken *oauth2.Token
+}
+
+// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK
+// account. If account is empty, the account currently active in
+// Google Cloud SDK properties is used.
+// Google Cloud SDK credentials must be created by running `gcloud auth`
+// before using this function.
+// The Google Cloud SDK is available at https://cloud.google.com/sdk/.
+func NewSDKConfig(account string) (*SDKConfig, error) {
+ configPath, err := sdkConfigPath()
+ if err != nil {
+ return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err)
+ }
+ credentialsPath := filepath.Join(configPath, "credentials")
+ f, err := os.Open(credentialsPath)
+ if err != nil {
+ return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err)
+ }
+ defer f.Close()
+
+ var c sdkCredentials
+ if err := json.NewDecoder(f).Decode(&c); err != nil {
+ return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err)
+ }
+ if len(c.Data) == 0 {
+ return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath)
+ }
+ if account == "" {
+ propertiesPath := filepath.Join(configPath, "properties")
+ f, err := os.Open(propertiesPath)
+ if err != nil {
+ return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
+ }
+ defer f.Close()
+ ini, err := internal.ParseINI(f)
+ if err != nil {
+ return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
+ }
+ core, ok := ini["core"]
+ if !ok {
+ return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini)
+ }
+ active, ok := core["account"]
+ if !ok {
+ return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core)
+ }
+ account = active
+ }
+
+ for _, d := range c.Data {
+ if account == "" || d.Key.Account == account {
+ if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" {
+ return nil, fmt.Errorf("oauth2/google: no token available for account %q", account)
+ }
+ var expiry time.Time
+ if d.Credential.TokenExpiry != nil {
+ expiry = *d.Credential.TokenExpiry
+ }
+ return &SDKConfig{
+ conf: oauth2.Config{
+ ClientID: d.Credential.ClientID,
+ ClientSecret: d.Credential.ClientSecret,
+ Scopes: strings.Split(d.Key.Scope, " "),
+ Endpoint: Endpoint,
+ RedirectURL: "oob",
+ },
+ initialToken: &oauth2.Token{
+ AccessToken: d.Credential.AccessToken,
+ RefreshToken: d.Credential.RefreshToken,
+ Expiry: expiry,
+ },
+ }, nil
+ }
+ }
+ return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account)
+}
+
+// Client returns an HTTP client using Google Cloud SDK credentials to
+// authorize requests. The token will auto-refresh as necessary. The
+// underlying http.RoundTripper will be obtained using the provided
+// context. The returned client and its Transport should not be
+// modified.
+func (c *SDKConfig) Client(ctx context.Context) *http.Client {
+ return &http.Client{
+ Transport: &oauth2.Transport{
+ Source: c.TokenSource(ctx),
+ },
+ }
+}
+
+// TokenSource returns an oauth2.TokenSource that retrieve tokens from
+// Google Cloud SDK credentials using the provided context.
+// It will returns the current access token stored in the credentials,
+// and refresh it when it expires, but it won't update the credentials
+// with the new access token.
+func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource {
+ return c.conf.TokenSource(ctx, c.initialToken)
+}
+
+// Scopes are the OAuth 2.0 scopes the current account is authorized for.
+func (c *SDKConfig) Scopes() []string {
+ return c.conf.Scopes
+}
+
+// sdkConfigPath tries to guess where the gcloud config is located.
+// It can be overridden during tests.
+var sdkConfigPath = func() (string, error) {
+ if runtime.GOOS == "windows" {
+ return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil
+ }
+ homeDir := guessUnixHomeDir()
+ if homeDir == "" {
+ return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
+ }
+ return filepath.Join(homeDir, ".config", "gcloud"), nil
+}
+
+func guessUnixHomeDir() string {
+ usr, err := user.Current()
+ if err == nil {
+ return usr.HomeDir
+ }
+ return os.Getenv("HOME")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go
new file mode 100644
index 000000000..79df88964
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/sdk_test.go
@@ -0,0 +1,46 @@
+// Copyright 2015 The oauth2 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 google
+
+import "testing"
+
+func TestSDKConfig(t *testing.T) {
+ sdkConfigPath = func() (string, error) {
+ return "testdata/gcloud", nil
+ }
+
+ tests := []struct {
+ account string
+ accessToken string
+ err bool
+ }{
+ {"", "bar_access_token", false},
+ {"foo@example.com", "foo_access_token", false},
+ {"bar@example.com", "bar_access_token", false},
+ {"baz@serviceaccount.example.com", "", true},
+ }
+ for _, tt := range tests {
+ c, err := NewSDKConfig(tt.account)
+ if got, want := err != nil, tt.err; got != want {
+ if !tt.err {
+ t.Errorf("expected no error, got error: %v", tt.err, err)
+ } else {
+ t.Errorf("expected error, got none")
+ }
+ continue
+ }
+ if err != nil {
+ continue
+ }
+ tok := c.initialToken
+ if tok == nil {
+ t.Errorf("expected token %q, got: nil", tt.accessToken)
+ continue
+ }
+ if tok.AccessToken != tt.accessToken {
+ t.Errorf("expected token %q, got: %q", tt.accessToken, tok.AccessToken)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials
new file mode 100644
index 000000000..ff5eefbd0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/credentials
@@ -0,0 +1,122 @@
+{
+ "data": [
+ {
+ "credential": {
+ "_class": "OAuth2Credentials",
+ "_module": "oauth2client.client",
+ "access_token": "foo_access_token",
+ "client_id": "foo_client_id",
+ "client_secret": "foo_client_secret",
+ "id_token": {
+ "at_hash": "foo_at_hash",
+ "aud": "foo_aud",
+ "azp": "foo_azp",
+ "cid": "foo_cid",
+ "email": "foo@example.com",
+ "email_verified": true,
+ "exp": 1420573614,
+ "iat": 1420569714,
+ "id": "1337",
+ "iss": "accounts.google.com",
+ "sub": "1337",
+ "token_hash": "foo_token_hash",
+ "verified_email": true
+ },
+ "invalid": false,
+ "refresh_token": "foo_refresh_token",
+ "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+ "token_expiry": "2015-01-09T00:51:51Z",
+ "token_response": {
+ "access_token": "foo_access_token",
+ "expires_in": 3600,
+ "id_token": "foo_id_token",
+ "token_type": "Bearer"
+ },
+ "token_uri": "https://accounts.google.com/o/oauth2/token",
+ "user_agent": "Cloud SDK Command Line Tool"
+ },
+ "key": {
+ "account": "foo@example.com",
+ "clientId": "foo_client_id",
+ "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+ "type": "google-cloud-sdk"
+ }
+ },
+ {
+ "credential": {
+ "_class": "OAuth2Credentials",
+ "_module": "oauth2client.client",
+ "access_token": "bar_access_token",
+ "client_id": "bar_client_id",
+ "client_secret": "bar_client_secret",
+ "id_token": {
+ "at_hash": "bar_at_hash",
+ "aud": "bar_aud",
+ "azp": "bar_azp",
+ "cid": "bar_cid",
+ "email": "bar@example.com",
+ "email_verified": true,
+ "exp": 1420573614,
+ "iat": 1420569714,
+ "id": "1337",
+ "iss": "accounts.google.com",
+ "sub": "1337",
+ "token_hash": "bar_token_hash",
+ "verified_email": true
+ },
+ "invalid": false,
+ "refresh_token": "bar_refresh_token",
+ "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+ "token_expiry": "2015-01-09T00:51:51Z",
+ "token_response": {
+ "access_token": "bar_access_token",
+ "expires_in": 3600,
+ "id_token": "bar_id_token",
+ "token_type": "Bearer"
+ },
+ "token_uri": "https://accounts.google.com/o/oauth2/token",
+ "user_agent": "Cloud SDK Command Line Tool"
+ },
+ "key": {
+ "account": "bar@example.com",
+ "clientId": "bar_client_id",
+ "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+ "type": "google-cloud-sdk"
+ }
+ },
+ {
+ "credential": {
+ "_class": "ServiceAccountCredentials",
+ "_kwargs": {},
+ "_module": "oauth2client.client",
+ "_private_key_id": "00000000000000000000000000000000",
+ "_private_key_pkcs8_text": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCt3fpiynPSaUhWSIKMGV331zudwJ6GkGmvQtwsoK2S2LbvnSwU\nNxgj4fp08kIDR5p26wF4+t/HrKydMwzftXBfZ9UmLVJgRdSswmS5SmChCrfDS5OE\nvFFcN5+6w1w8/Nu657PF/dse8T0bV95YrqyoR0Osy8WHrUOMSIIbC3hRuwIDAQAB\nAoGAJrGE/KFjn0sQ7yrZ6sXmdLawrM3mObo/2uI9T60+k7SpGbBX0/Pi6nFrJMWZ\nTVONG7P3Mu5aCPzzuVRYJB0j8aldSfzABTY3HKoWCczqw1OztJiEseXGiYz4QOyr\nYU3qDyEpdhS6q6wcoLKGH+hqRmz6pcSEsc8XzOOu7s4xW8kCQQDkc75HjhbarCnd\nJJGMe3U76+6UGmdK67ltZj6k6xoB5WbTNChY9TAyI2JC+ppYV89zv3ssj4L+02u3\nHIHFGxsHAkEAwtU1qYb1tScpchPobnYUFiVKJ7KA8EZaHVaJJODW/cghTCV7BxcJ\nbgVvlmk4lFKn3lPKAgWw7PdQsBTVBUcCrQJATPwoIirizrv3u5soJUQxZIkENAqV\nxmybZx9uetrzP7JTrVbFRf0SScMcyN90hdLJiQL8+i4+gaszgFht7sNMnwJAAbfj\nq0UXcauQwALQ7/h2oONfTg5S+MuGC/AxcXPSMZbMRGGoPh3D5YaCv27aIuS/ukQ+\n6dmm/9AGlCb64fsIWQJAPaokbjIifo+LwC5gyK73Mc4t8nAOSZDenzd/2f6TCq76\nS1dcnKiPxaED7W/y6LJiuBT2rbZiQ2L93NJpFZD/UA==\n-----END RSA PRIVATE KEY-----\n",
+ "_revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+ "_scopes": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+ "_service_account_email": "baz@serviceaccount.example.com",
+ "_service_account_id": "baz.serviceaccount.example.com",
+ "_token_uri": "https://accounts.google.com/o/oauth2/token",
+ "_user_agent": "Cloud SDK Command Line Tool",
+ "access_token": null,
+ "assertion_type": null,
+ "client_id": null,
+ "client_secret": null,
+ "id_token": null,
+ "invalid": false,
+ "refresh_token": null,
+ "revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
+ "service_account_name": "baz@serviceaccount.example.com",
+ "token_expiry": null,
+ "token_response": null,
+ "user_agent": "Cloud SDK Command Line Tool"
+ },
+ "key": {
+ "account": "baz@serviceaccount.example.com",
+ "clientId": "baz_client_id",
+ "scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
+ "type": "google-cloud-sdk"
+ }
+ }
+ ],
+ "file_version": 1
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties
new file mode 100644
index 000000000..025de886c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google/testdata/gcloud/properties
@@ -0,0 +1,2 @@
+[core]
+account = bar@example.com
\ No newline at end of file
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go
new file mode 100644
index 000000000..37571a129
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2.go
@@ -0,0 +1,69 @@
+// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package.
+package internal
+
+import (
+ "bufio"
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+// ParseKey converts the binary contents of a private key file
+// to an *rsa.PrivateKey. It detects whether the private key is in a
+// PEM container or not. If so, it extracts the the private key
+// from PEM container before conversion. It only supports PEM
+// containers with no passphrase.
+func ParseKey(key []byte) (*rsa.PrivateKey, error) {
+ block, _ := pem.Decode(key)
+ if block != nil {
+ key = block.Bytes
+ }
+ parsedKey, err := x509.ParsePKCS8PrivateKey(key)
+ if err != nil {
+ parsedKey, err = x509.ParsePKCS1PrivateKey(key)
+ if err != nil {
+ return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
+ }
+ }
+ parsed, ok := parsedKey.(*rsa.PrivateKey)
+ if !ok {
+ return nil, errors.New("private key is invalid")
+ }
+ return parsed, nil
+}
+
+func ParseINI(ini io.Reader) (map[string]map[string]string, error) {
+ result := map[string]map[string]string{
+ "": map[string]string{}, // root section
+ }
+ scanner := bufio.NewScanner(ini)
+ currentSection := ""
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if strings.HasPrefix(line, ";") {
+ // comment.
+ continue
+ }
+ if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
+ currentSection = strings.TrimSpace(line[1 : len(line)-1])
+ result[currentSection] = map[string]string{}
+ continue
+ }
+ parts := strings.SplitN(line, "=", 2)
+ if len(parts) == 2 && parts[0] != "" {
+ result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, fmt.Errorf("error scanning ini: %v", err)
+ }
+ return result, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go
new file mode 100644
index 000000000..014a351e0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal/oauth2_test.go
@@ -0,0 +1,62 @@
+// Copyright 2014 The oauth2 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 internal contains support packages for oauth2 package.
+package internal
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestParseINI(t *testing.T) {
+ tests := []struct {
+ ini string
+ want map[string]map[string]string
+ }{
+ {
+ `root = toor
+[foo]
+bar = hop
+ini = nin
+`,
+ map[string]map[string]string{
+ "": map[string]string{"root": "toor"},
+ "foo": map[string]string{"bar": "hop", "ini": "nin"},
+ },
+ },
+ {
+ `[empty]
+[section]
+empty=
+`,
+ map[string]map[string]string{
+ "": map[string]string{},
+ "empty": map[string]string{},
+ "section": map[string]string{"empty": ""},
+ },
+ },
+ {
+ `ignore
+[invalid
+=stuff
+;comment=true
+`,
+ map[string]map[string]string{
+ "": map[string]string{},
+ },
+ },
+ }
+ for _, tt := range tests {
+ result, err := ParseINI(strings.NewReader(tt.ini))
+ if err != nil {
+ t.Errorf("ParseINI(%q) error %v, want: no error", tt.ini, err)
+ continue
+ }
+ if !reflect.DeepEqual(result, tt.want) {
+ t.Errorf("ParseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go
new file mode 100644
index 000000000..396b3fac8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jws/jws.go
@@ -0,0 +1,160 @@
+// Copyright 2014 The oauth2 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 jws provides encoding and decoding utilities for
+// signed JWS messages.
+package jws
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/sha256"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+ "time"
+)
+
+// ClaimSet contains information about the JWT signature including the
+// permissions being requested (scopes), the target of the token, the issuer,
+// the time the token was issued, and the lifetime of the token.
+type ClaimSet struct {
+ Iss string `json:"iss"` // email address of the client_id of the application making the access token request
+ Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests
+ Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional).
+ Exp int64 `json:"exp"` // the expiration time of the assertion
+ Iat int64 `json:"iat"` // the time the assertion was issued.
+ Typ string `json:"typ,omitempty"` // token type (Optional).
+
+ // Email for which the application is requesting delegated access (Optional).
+ Sub string `json:"sub,omitempty"`
+
+ // The old name of Sub. Client keeps setting Prn to be
+ // complaint with legacy OAuth 2.0 providers. (Optional)
+ Prn string `json:"prn,omitempty"`
+
+ // See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
+ // This array is marshalled using custom code (see (c *ClaimSet) encode()).
+ PrivateClaims map[string]interface{} `json:"-"`
+
+ exp time.Time
+ iat time.Time
+}
+
+func (c *ClaimSet) encode() (string, error) {
+ if c.exp.IsZero() || c.iat.IsZero() {
+ // Reverting time back for machines whose time is not perfectly in sync.
+ // If client machine's time is in the future according
+ // to Google servers, an access token will not be issued.
+ now := time.Now().Add(-10 * time.Second)
+ c.iat = now
+ c.exp = now.Add(time.Hour)
+ }
+
+ c.Exp = c.exp.Unix()
+ c.Iat = c.iat.Unix()
+
+ b, err := json.Marshal(c)
+ if err != nil {
+ return "", err
+ }
+
+ if len(c.PrivateClaims) == 0 {
+ return base64Encode(b), nil
+ }
+
+ // Marshal private claim set and then append it to b.
+ prv, err := json.Marshal(c.PrivateClaims)
+ if err != nil {
+ return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims)
+ }
+
+ // Concatenate public and private claim JSON objects.
+ if !bytes.HasSuffix(b, []byte{'}'}) {
+ return "", fmt.Errorf("jws: invalid JSON %s", b)
+ }
+ if !bytes.HasPrefix(prv, []byte{'{'}) {
+ return "", fmt.Errorf("jws: invalid JSON %s", prv)
+ }
+ b[len(b)-1] = ',' // Replace closing curly brace with a comma.
+ b = append(b, prv[1:]...) // Append private claims.
+ return base64Encode(b), nil
+}
+
+// Header represents the header for the signed JWS payloads.
+type Header struct {
+ // The algorithm used for signature.
+ Algorithm string `json:"alg"`
+
+ // Represents the token type.
+ Typ string `json:"typ"`
+}
+
+func (h *Header) encode() (string, error) {
+ b, err := json.Marshal(h)
+ if err != nil {
+ return "", err
+ }
+ return base64Encode(b), nil
+}
+
+// Decode decodes a claim set from a JWS payload.
+func Decode(payload string) (*ClaimSet, error) {
+ // decode returned id token to get expiry
+ s := strings.Split(payload, ".")
+ if len(s) < 2 {
+ // TODO(jbd): Provide more context about the error.
+ return nil, errors.New("jws: invalid token received")
+ }
+ decoded, err := base64Decode(s[1])
+ if err != nil {
+ return nil, err
+ }
+ c := &ClaimSet{}
+ err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c)
+ return c, err
+}
+
+// Encode encodes a signed JWS with provided header and claim set.
+func Encode(header *Header, c *ClaimSet, signature *rsa.PrivateKey) (string, error) {
+ head, err := header.encode()
+ if err != nil {
+ return "", err
+ }
+ cs, err := c.encode()
+ if err != nil {
+ return "", err
+ }
+ ss := fmt.Sprintf("%s.%s", head, cs)
+ h := sha256.New()
+ h.Write([]byte(ss))
+ b, err := rsa.SignPKCS1v15(rand.Reader, signature, crypto.SHA256, h.Sum(nil))
+ if err != nil {
+ return "", err
+ }
+ sig := base64Encode(b)
+ return fmt.Sprintf("%s.%s", ss, sig), nil
+}
+
+// base64Encode returns and Base64url encoded version of the input string with any
+// trailing "=" stripped.
+func base64Encode(b []byte) string {
+ return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
+}
+
+// base64Decode decodes the Base64url encoded string
+func base64Decode(s string) ([]byte, error) {
+ // add back missing padding
+ switch len(s) % 4 {
+ case 2:
+ s += "=="
+ case 3:
+ s += "="
+ }
+ return base64.URLEncoding.DecodeString(s)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go
new file mode 100644
index 000000000..0004bcf2e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/example_test.go
@@ -0,0 +1,31 @@
+// Copyright 2014 The oauth2 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 jwt_test
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt"
+)
+
+func ExampleJWTConfig() {
+ conf := &jwt.Config{
+ Email: "xxx@developer.com",
+ // The contents of your RSA private key or your PEM file
+ // that contains a private key.
+ // If you have a p12 file instead, you
+ // can use `openssl` to export the private key into a pem file.
+ //
+ // $ openssl pkcs12 -in key.p12 -out key.pem -nodes
+ //
+ // It only supports PEM containers with no passphrase.
+ PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
+ Subject: "user@example.com",
+ TokenURL: "https://provider.com/o/oauth2/token",
+ }
+ // Initiate an http.Client, the following GET request will be
+ // authorized and authenticated on the behalf of user@example.com.
+ client := conf.Client(oauth2.NoContext)
+ client.Get("...")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go
new file mode 100644
index 000000000..fa34de455
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt.go
@@ -0,0 +1,147 @@
+// Copyright 2014 The oauth2 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 jwt implements the OAuth 2.0 JSON Web Token flow, commonly
+// known as "two-legged OAuth 2.0".
+//
+// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12
+package jwt
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/internal"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jws"
+)
+
+var (
+ defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
+ defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
+)
+
+// Config is the configuration for using JWT to fetch tokens,
+// commonly known as "two-legged OAuth 2.0".
+type Config struct {
+ // Email is the OAuth client identifier used when communicating with
+ // the configured OAuth provider.
+ Email string
+
+ // PrivateKey contains the contents of an RSA private key or the
+ // contents of a PEM file that contains a private key. The provided
+ // private key is used to sign JWT payloads.
+ // PEM containers with a passphrase are not supported.
+ // Use the following command to convert a PKCS 12 file into a PEM.
+ //
+ // $ openssl pkcs12 -in key.p12 -out key.pem -nodes
+ //
+ PrivateKey []byte
+
+ // Subject is the optional user to impersonate.
+ Subject string
+
+ // Scopes optionally specifies a list of requested permission scopes.
+ Scopes []string
+
+ // TokenURL is the endpoint required to complete the 2-legged JWT flow.
+ TokenURL string
+}
+
+// TokenSource returns a JWT TokenSource using the configuration
+// in c and the HTTP client from the provided context.
+func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
+ return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c})
+}
+
+// Client returns an HTTP client wrapping the context's
+// HTTP transport and adding Authorization headers with tokens
+// obtained from c.
+//
+// The returned client and its Transport should not be modified.
+func (c *Config) Client(ctx context.Context) *http.Client {
+ return oauth2.NewClient(ctx, c.TokenSource(ctx))
+}
+
+// jwtSource is a source that always does a signed JWT request for a token.
+// It should typically be wrapped with a reuseTokenSource.
+type jwtSource struct {
+ ctx context.Context
+ conf *Config
+}
+
+func (js jwtSource) Token() (*oauth2.Token, error) {
+ pk, err := internal.ParseKey(js.conf.PrivateKey)
+ if err != nil {
+ return nil, err
+ }
+ hc := oauth2.NewClient(js.ctx, nil)
+ claimSet := &jws.ClaimSet{
+ Iss: js.conf.Email,
+ Scope: strings.Join(js.conf.Scopes, " "),
+ Aud: js.conf.TokenURL,
+ }
+ if subject := js.conf.Subject; subject != "" {
+ claimSet.Sub = subject
+ // prn is the old name of sub. Keep setting it
+ // to be compatible with legacy OAuth 2.0 providers.
+ claimSet.Prn = subject
+ }
+ payload, err := jws.Encode(defaultHeader, claimSet, pk)
+ if err != nil {
+ return nil, err
+ }
+ v := url.Values{}
+ v.Set("grant_type", defaultGrantType)
+ v.Set("assertion", payload)
+ resp, err := hc.PostForm(js.conf.TokenURL, v)
+ if err != nil {
+ return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
+ if err != nil {
+ return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+ }
+ if c := resp.StatusCode; c < 200 || c > 299 {
+ return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", resp.Status, body)
+ }
+ // tokenRes is the JSON response body.
+ var tokenRes struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ IDToken string `json:"id_token"`
+ ExpiresIn int64 `json:"expires_in"` // relative seconds from now
+ }
+ if err := json.Unmarshal(body, &tokenRes); err != nil {
+ return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+ }
+ token := &oauth2.Token{
+ AccessToken: tokenRes.AccessToken,
+ TokenType: tokenRes.TokenType,
+ }
+ raw := make(map[string]interface{})
+ json.Unmarshal(body, &raw) // no error checks for optional fields
+ token = token.WithExtra(raw)
+
+ if secs := tokenRes.ExpiresIn; secs > 0 {
+ token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
+ }
+ if v := tokenRes.IDToken; v != "" {
+ // decode returned id token to get expiry
+ claimSet, err := jws.Decode(v)
+ if err != nil {
+ return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
+ }
+ token.Expiry = time.Unix(claimSet.Exp, 0)
+ }
+ return token, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go
new file mode 100644
index 000000000..22c9e6687
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt/jwt_test.go
@@ -0,0 +1,134 @@
+// Copyright 2014 The oauth2 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 jwt
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE
+DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY
+fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK
+1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr
+k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9
+/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt
+3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn
+2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3
+nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK
+6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf
+5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e
+DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1
+M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g
+z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y
+1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK
+J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U
+f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx
+QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA
+cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr
+Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw
+5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg
+KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84
+OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd
+mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ
+5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg==
+-----END RSA PRIVATE KEY-----`)
+
+func TestJWTFetch_JSONResponse(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{
+ "access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
+ "scope": "user",
+ "token_type": "bearer",
+ "expires_in": 3600
+ }`))
+ }))
+ defer ts.Close()
+
+ conf := &Config{
+ Email: "aaa@xxx.com",
+ PrivateKey: dummyPrivateKey,
+ TokenURL: ts.URL,
+ }
+ tok, err := conf.TokenSource(oauth2.NoContext).Token()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !tok.Valid() {
+ t.Errorf("Token invalid")
+ }
+ if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
+ t.Errorf("Unexpected access token, %#v", tok.AccessToken)
+ }
+ if tok.TokenType != "bearer" {
+ t.Errorf("Unexpected token type, %#v", tok.TokenType)
+ }
+ if tok.Expiry.IsZero() {
+ t.Errorf("Unexpected token expiry, %#v", tok.Expiry)
+ }
+ scope := tok.Extra("scope")
+ if scope != "user" {
+ t.Errorf("Unexpected value for scope: %v", scope)
+ }
+}
+
+func TestJWTFetch_BadResponse(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
+ }))
+ defer ts.Close()
+
+ conf := &Config{
+ Email: "aaa@xxx.com",
+ PrivateKey: dummyPrivateKey,
+ TokenURL: ts.URL,
+ }
+ tok, err := conf.TokenSource(oauth2.NoContext).Token()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if tok == nil {
+ t.Fatalf("token is nil")
+ }
+ if tok.Valid() {
+ t.Errorf("token is valid. want invalid.")
+ }
+ if tok.AccessToken != "" {
+ t.Errorf("Unexpected non-empty access token %q.", tok.AccessToken)
+ }
+ if want := "bearer"; tok.TokenType != want {
+ t.Errorf("TokenType = %q; want %q", tok.TokenType, want)
+ }
+ scope := tok.Extra("scope")
+ if want := "user"; scope != want {
+ t.Errorf("token scope = %q; want %q", scope, want)
+ }
+}
+
+func TestJWTFetch_BadResponseType(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
+ }))
+ defer ts.Close()
+ conf := &Config{
+ Email: "aaa@xxx.com",
+ PrivateKey: dummyPrivateKey,
+ TokenURL: ts.URL,
+ }
+ tok, err := conf.TokenSource(oauth2.NoContext).Token()
+ if err == nil {
+ t.Error("got a token; expected error")
+ if tok.AccessToken != "" {
+ t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go
new file mode 100644
index 000000000..cdab73217
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/linkedin/linkedin.go
@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 linkedin provides constants for using OAuth2 to access LinkedIn.
+package linkedin
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Endpoint is LinkedIn's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://www.linkedin.com/uas/oauth2/authorization",
+ TokenURL: "https://www.linkedin.com/uas/oauth2/accessToken",
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go
new file mode 100644
index 000000000..b27f9ecce
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/oauth2.go
@@ -0,0 +1,523 @@
+// Copyright 2014 The oauth2 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 oauth2 provides support for making
+// OAuth2 authorized and authenticated HTTP requests.
+// It can additionally grant authorization with Bearer JWT.
+package oauth2
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+// NoContext is the default context you should supply if not using
+// your own context.Context (see https://golang.org/x/net/context).
+var NoContext = context.TODO()
+
+// Config describes a typical 3-legged OAuth2 flow, with both the
+// client application information and the server's endpoint URLs.
+type Config struct {
+ // ClientID is the application's ID.
+ ClientID string
+
+ // ClientSecret is the application's secret.
+ ClientSecret string
+
+ // Endpoint contains the resource server's token endpoint
+ // URLs. These are constants specific to each server and are
+ // often available via site-specific packages, such as
+ // google.Endpoint or github.Endpoint.
+ Endpoint Endpoint
+
+ // RedirectURL is the URL to redirect users going through
+ // the OAuth flow, after the resource owner's URLs.
+ RedirectURL string
+
+ // Scope specifies optional requested permissions.
+ Scopes []string
+}
+
+// A TokenSource is anything that can return a token.
+type TokenSource interface {
+ // Token returns a token or an error.
+ // Token must be safe for concurrent use by multiple goroutines.
+ // The returned Token must not be modified.
+ Token() (*Token, error)
+}
+
+// Endpoint contains the OAuth 2.0 provider's authorization and token
+// endpoint URLs.
+type Endpoint struct {
+ AuthURL string
+ TokenURL string
+}
+
+var (
+ // AccessTypeOnline and AccessTypeOffline are options passed
+ // to the Options.AuthCodeURL method. They modify the
+ // "access_type" field that gets sent in the URL returned by
+ // AuthCodeURL.
+ //
+ // Online is the default if neither is specified. If your
+ // application needs to refresh access tokens when the user
+ // is not present at the browser, then use offline. This will
+ // result in your application obtaining a refresh token the
+ // first time your application exchanges an authorization
+ // code for a user.
+ AccessTypeOnline AuthCodeOption = SetParam("access_type", "online")
+ AccessTypeOffline AuthCodeOption = SetParam("access_type", "offline")
+
+ // ApprovalForce forces the users to view the consent dialog
+ // and confirm the permissions request at the URL returned
+ // from AuthCodeURL, even if they've already done so.
+ ApprovalForce AuthCodeOption = SetParam("approval_prompt", "force")
+)
+
+// An AuthCodeOption is passed to Config.AuthCodeURL.
+type AuthCodeOption interface {
+ setValue(url.Values)
+}
+
+type setParam struct{ k, v string }
+
+func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
+
+// SetParam builds an AuthCodeOption which passes key/value parameters
+// to a provider's authorization endpoint.
+func SetParam(key, value string) AuthCodeOption {
+ return setParam{key, value}
+}
+
+// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
+// that asks for permissions for the required scopes explicitly.
+//
+// State is a token to protect the user from CSRF attacks. You must
+// always provide a non-zero string and validate that it matches the
+// the state query parameter on your redirect callback.
+// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
+//
+// Opts may include AccessTypeOnline or AccessTypeOffline, as well
+// as ApprovalForce.
+func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
+ var buf bytes.Buffer
+ buf.WriteString(c.Endpoint.AuthURL)
+ v := url.Values{
+ "response_type": {"code"},
+ "client_id": {c.ClientID},
+ "redirect_uri": condVal(c.RedirectURL),
+ "scope": condVal(strings.Join(c.Scopes, " ")),
+ "state": condVal(state),
+ }
+ for _, opt := range opts {
+ opt.setValue(v)
+ }
+ if strings.Contains(c.Endpoint.AuthURL, "?") {
+ buf.WriteByte('&')
+ } else {
+ buf.WriteByte('?')
+ }
+ buf.WriteString(v.Encode())
+ return buf.String()
+}
+
+// PasswordCredentialsToken converts a resource owner username and password
+// pair into a token.
+//
+// Per the RFC, this grant type should only be used "when there is a high
+// degree of trust between the resource owner and the client (e.g., the client
+// is part of the device operating system or a highly privileged application),
+// and when other authorization grant types are not available."
+// See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
+//
+// The HTTP client to use is derived from the context.
+// If nil, http.DefaultClient is used.
+func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
+ return retrieveToken(ctx, c, url.Values{
+ "grant_type": {"password"},
+ "username": {username},
+ "password": {password},
+ "scope": condVal(strings.Join(c.Scopes, " ")),
+ })
+}
+
+// Exchange converts an authorization code into a token.
+//
+// It is used after a resource provider redirects the user back
+// to the Redirect URI (the URL obtained from AuthCodeURL).
+//
+// The HTTP client to use is derived from the context.
+// If a client is not provided via the context, http.DefaultClient is used.
+//
+// The code will be in the *http.Request.FormValue("code"). Before
+// calling Exchange, be sure to validate FormValue("state").
+func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
+ return retrieveToken(ctx, c, url.Values{
+ "grant_type": {"authorization_code"},
+ "code": {code},
+ "redirect_uri": condVal(c.RedirectURL),
+ "scope": condVal(strings.Join(c.Scopes, " ")),
+ })
+}
+
+// contextClientFunc is a func which tries to return an *http.Client
+// given a Context value. If it returns an error, the search stops
+// with that error. If it returns (nil, nil), the search continues
+// down the list of registered funcs.
+type contextClientFunc func(context.Context) (*http.Client, error)
+
+var contextClientFuncs []contextClientFunc
+
+func registerContextClientFunc(fn contextClientFunc) {
+ contextClientFuncs = append(contextClientFuncs, fn)
+}
+
+func contextClient(ctx context.Context) (*http.Client, error) {
+ for _, fn := range contextClientFuncs {
+ c, err := fn(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if c != nil {
+ return c, nil
+ }
+ }
+ if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
+ return hc, nil
+ }
+ return http.DefaultClient, nil
+}
+
+func contextTransport(ctx context.Context) http.RoundTripper {
+ hc, err := contextClient(ctx)
+ if err != nil {
+ // This is a rare error case (somebody using nil on App Engine),
+ // so I'd rather not everybody do an error check on this Client
+ // method. They can get the error that they're doing it wrong
+ // later, at client.Get/PostForm time.
+ return errorTransport{err}
+ }
+ return hc.Transport
+}
+
+// Client returns an HTTP client using the provided token.
+// The token will auto-refresh as necessary. The underlying
+// HTTP transport will be obtained using the provided context.
+// The returned client and its Transport should not be modified.
+func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
+ return NewClient(ctx, c.TokenSource(ctx, t))
+}
+
+// TokenSource returns a TokenSource that returns t until t expires,
+// automatically refreshing it as necessary using the provided context.
+//
+// Most users will use Config.Client instead.
+func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
+ tkr := &tokenRefresher{
+ ctx: ctx,
+ conf: c,
+ }
+ if t != nil {
+ tkr.refreshToken = t.RefreshToken
+ }
+ return &reuseTokenSource{
+ t: t,
+ new: tkr,
+ }
+}
+
+// tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
+// HTTP requests to renew a token using a RefreshToken.
+type tokenRefresher struct {
+ ctx context.Context // used to get HTTP requests
+ conf *Config
+ refreshToken string
+}
+
+// WARNING: Token is not safe for concurrent access, as it
+// updates the tokenRefresher's refreshToken field.
+// Within this package, it is used by reuseTokenSource which
+// synchronizes calls to this method with its own mutex.
+func (tf *tokenRefresher) Token() (*Token, error) {
+ if tf.refreshToken == "" {
+ return nil, errors.New("oauth2: token expired and refresh token is not set")
+ }
+
+ tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
+ "grant_type": {"refresh_token"},
+ "refresh_token": {tf.refreshToken},
+ })
+
+ if err != nil {
+ return nil, err
+ }
+ if tf.refreshToken != tk.RefreshToken {
+ tf.refreshToken = tk.RefreshToken
+ }
+ return tk, err
+}
+
+// reuseTokenSource is a TokenSource that holds a single token in memory
+// and validates its expiry before each call to retrieve it with
+// Token. If it's expired, it will be auto-refreshed using the
+// new TokenSource.
+type reuseTokenSource struct {
+ new TokenSource // called when t is expired.
+
+ mu sync.Mutex // guards t
+ t *Token
+}
+
+// Token returns the current token if it's still valid, else will
+// refresh the current token (using r.Context for HTTP client
+// information) and return the new one.
+func (s *reuseTokenSource) Token() (*Token, error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.t.Valid() {
+ return s.t, nil
+ }
+ t, err := s.new.Token()
+ if err != nil {
+ return nil, err
+ }
+ s.t = t
+ return t, nil
+}
+
+func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
+ hc, err := contextClient(ctx)
+ if err != nil {
+ return nil, err
+ }
+ v.Set("client_id", c.ClientID)
+ bustedAuth := !providerAuthHeaderWorks(c.Endpoint.TokenURL)
+ if bustedAuth && c.ClientSecret != "" {
+ v.Set("client_secret", c.ClientSecret)
+ }
+ req, err := http.NewRequest("POST", c.Endpoint.TokenURL, strings.NewReader(v.Encode()))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ if !bustedAuth {
+ req.SetBasicAuth(c.ClientID, c.ClientSecret)
+ }
+ r, err := hc.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer r.Body.Close()
+ body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
+ if err != nil {
+ return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
+ }
+ if code := r.StatusCode; code < 200 || code > 299 {
+ return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
+ }
+
+ var token *Token
+ content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
+ switch content {
+ case "application/x-www-form-urlencoded", "text/plain":
+ vals, err := url.ParseQuery(string(body))
+ if err != nil {
+ return nil, err
+ }
+ token = &Token{
+ AccessToken: vals.Get("access_token"),
+ TokenType: vals.Get("token_type"),
+ RefreshToken: vals.Get("refresh_token"),
+ raw: vals,
+ }
+ e := vals.Get("expires_in")
+ if e == "" {
+ // TODO(jbd): Facebook's OAuth2 implementation is broken and
+ // returns expires_in field in expires. Remove the fallback to expires,
+ // when Facebook fixes their implementation.
+ e = vals.Get("expires")
+ }
+ expires, _ := strconv.Atoi(e)
+ if expires != 0 {
+ token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
+ }
+ default:
+ var tj tokenJSON
+ if err = json.Unmarshal(body, &tj); err != nil {
+ return nil, err
+ }
+ token = &Token{
+ AccessToken: tj.AccessToken,
+ TokenType: tj.TokenType,
+ RefreshToken: tj.RefreshToken,
+ Expiry: tj.expiry(),
+ raw: make(map[string]interface{}),
+ }
+ json.Unmarshal(body, &token.raw) // no error checks for optional fields
+ }
+ // Don't overwrite `RefreshToken` with an empty value
+ // if this was a token refreshing request.
+ if token.RefreshToken == "" {
+ token.RefreshToken = v.Get("refresh_token")
+ }
+ return token, nil
+}
+
+// tokenJSON is the struct representing the HTTP response from OAuth2
+// providers returning a token in JSON form.
+type tokenJSON struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ RefreshToken string `json:"refresh_token"`
+ ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
+ Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in
+}
+
+func (e *tokenJSON) expiry() (t time.Time) {
+ if v := e.ExpiresIn; v != 0 {
+ return time.Now().Add(time.Duration(v) * time.Second)
+ }
+ if v := e.Expires; v != 0 {
+ return time.Now().Add(time.Duration(v) * time.Second)
+ }
+ return
+}
+
+type expirationTime int32
+
+func (e *expirationTime) UnmarshalJSON(b []byte) error {
+ var n json.Number
+ err := json.Unmarshal(b, &n)
+ if err != nil {
+ return err
+ }
+ i, err := n.Int64()
+ if err != nil {
+ return err
+ }
+ *e = expirationTime(i)
+ return nil
+}
+
+func condVal(v string) []string {
+ if v == "" {
+ return nil
+ }
+ return []string{v}
+}
+
+var brokenAuthHeaderProviders = []string{
+ "https://accounts.google.com/",
+ "https://www.googleapis.com/",
+ "https://github.com/",
+ "https://api.instagram.com/",
+ "https://www.douban.com/",
+ "https://api.dropbox.com/",
+ "https://api.soundcloud.com/",
+ "https://www.linkedin.com/",
+ "https://api.twitch.tv/",
+ "https://oauth.vk.com/",
+ "https://api.odnoklassniki.ru/",
+ "https://connect.stripe.com/",
+ "https://api.pushbullet.com/",
+ "https://oauth.sandbox.trainingpeaks.com/",
+ "https://oauth.trainingpeaks.com/",
+ "https://www.strava.com/oauth/",
+}
+
+// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
+// implements the OAuth2 spec correctly
+// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
+// In summary:
+// - Reddit only accepts client secret in the Authorization header
+// - Dropbox accepts either it in URL param or Auth header, but not both.
+// - Google only accepts URL param (not spec compliant?), not Auth header
+// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
+func providerAuthHeaderWorks(tokenURL string) bool {
+ for _, s := range brokenAuthHeaderProviders {
+ if strings.HasPrefix(tokenURL, s) {
+ // Some sites fail to implement the OAuth2 spec fully.
+ return false
+ }
+ }
+
+ // Assume the provider implements the spec properly
+ // otherwise. We can add more exceptions as they're
+ // discovered. We will _not_ be adding configurable hooks
+ // to this package to let users select server bugs.
+ return true
+}
+
+// HTTPClient is the context key to use with golang.org/x/net/context's
+// WithValue function to associate an *http.Client value with a context.
+var HTTPClient contextKey
+
+// contextKey is just an empty struct. It exists so HTTPClient can be
+// an immutable public variable with a unique type. It's immutable
+// because nobody else can create a contextKey, being unexported.
+type contextKey struct{}
+
+// NewClient creates an *http.Client from a Context and TokenSource.
+// The returned client is not valid beyond the lifetime of the context.
+//
+// As a special case, if src is nil, a non-OAuth2 client is returned
+// using the provided context. This exists to support related OAuth2
+// packages.
+func NewClient(ctx context.Context, src TokenSource) *http.Client {
+ if src == nil {
+ c, err := contextClient(ctx)
+ if err != nil {
+ return &http.Client{Transport: errorTransport{err}}
+ }
+ return c
+ }
+ return &http.Client{
+ Transport: &Transport{
+ Base: contextTransport(ctx),
+ Source: ReuseTokenSource(nil, src),
+ },
+ }
+}
+
+// ReuseTokenSource returns a TokenSource which repeatedly returns the
+// same token as long as it's valid, starting with t.
+// When its cached token is invalid, a new token is obtained from src.
+//
+// ReuseTokenSource is typically used to reuse tokens from a cache
+// (such as a file on disk) between runs of a program, rather than
+// obtaining new tokens unnecessarily.
+//
+// The initial token t may be nil, in which case the TokenSource is
+// wrapped in a caching version if it isn't one already. This also
+// means it's always safe to wrap ReuseTokenSource around any other
+// TokenSource without adverse effects.
+func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
+ // Don't wrap a reuseTokenSource in itself. That would work,
+ // but cause an unnecessary number of mutex operations.
+ // Just build the equivalent one.
+ if rt, ok := src.(*reuseTokenSource); ok {
+ if t == nil {
+ // Just use it directly.
+ return rt
+ }
+ src = rt.new
+ }
+ return &reuseTokenSource{
+ t: t,
+ new: src,
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go
new file mode 100644
index 000000000..1a9241779
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/oauth2_test.go
@@ -0,0 +1,435 @@
+// Copyright 2014 The oauth2 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 oauth2
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+type mockTransport struct {
+ rt func(req *http.Request) (resp *http.Response, err error)
+}
+
+func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
+ return t.rt(req)
+}
+
+type mockCache struct {
+ token *Token
+ readErr error
+}
+
+func (c *mockCache) ReadToken() (*Token, error) {
+ return c.token, c.readErr
+}
+
+func (c *mockCache) WriteToken(*Token) {
+ // do nothing
+}
+
+func newConf(url string) *Config {
+ return &Config{
+ ClientID: "CLIENT_ID",
+ ClientSecret: "CLIENT_SECRET",
+ RedirectURL: "REDIRECT_URL",
+ Scopes: []string{"scope1", "scope2"},
+ Endpoint: Endpoint{
+ AuthURL: url + "/auth",
+ TokenURL: url + "/token",
+ },
+ }
+}
+
+func TestAuthCodeURL(t *testing.T) {
+ conf := newConf("server")
+ url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce)
+ if url != "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo" {
+ t.Errorf("Auth code URL doesn't match the expected, found: %v", url)
+ }
+}
+
+func TestAuthCodeURL_CustomParam(t *testing.T) {
+ conf := newConf("server")
+ param := SetParam("foo", "bar")
+ url := conf.AuthCodeURL("baz", param)
+ if url != "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz" {
+ t.Errorf("Auth code URL doesn't match the expected, found: %v", url)
+ }
+}
+
+func TestAuthCodeURL_Optional(t *testing.T) {
+ conf := &Config{
+ ClientID: "CLIENT_ID",
+ Endpoint: Endpoint{
+ AuthURL: "/auth-url",
+ TokenURL: "/token-url",
+ },
+ }
+ url := conf.AuthCodeURL("")
+ if url != "/auth-url?client_id=CLIENT_ID&response_type=code" {
+ t.Fatalf("Auth code URL doesn't match the expected, found: %v", url)
+ }
+}
+
+func TestExchangeRequest(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.String() != "/token" {
+ t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
+ }
+ headerAuth := r.Header.Get("Authorization")
+ if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
+ t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
+ }
+ headerContentType := r.Header.Get("Content-Type")
+ if headerContentType != "application/x-www-form-urlencoded" {
+ t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+ }
+ body, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ t.Errorf("Failed reading request body: %s.", err)
+ }
+ if string(body) != "client_id=CLIENT_ID&code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL&scope=scope1+scope2" {
+ t.Errorf("Unexpected exchange payload, %v is found.", string(body))
+ }
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ tok, err := conf.Exchange(NoContext, "exchange-code")
+ if err != nil {
+ t.Error(err)
+ }
+ if !tok.Valid() {
+ t.Fatalf("Token invalid. Got: %#v", tok)
+ }
+ if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
+ t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+ }
+ if tok.TokenType != "bearer" {
+ t.Errorf("Unexpected token type, %#v.", tok.TokenType)
+ }
+ scope := tok.Extra("scope")
+ if scope != "user" {
+ t.Errorf("Unexpected value for scope: %v", scope)
+ }
+}
+
+func TestExchangeRequest_JSONResponse(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.String() != "/token" {
+ t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
+ }
+ headerAuth := r.Header.Get("Authorization")
+ if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
+ t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
+ }
+ headerContentType := r.Header.Get("Content-Type")
+ if headerContentType != "application/x-www-form-urlencoded" {
+ t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+ }
+ body, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ t.Errorf("Failed reading request body: %s.", err)
+ }
+ if string(body) != "client_id=CLIENT_ID&code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL&scope=scope1+scope2" {
+ t.Errorf("Unexpected exchange payload, %v is found.", string(body))
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", "scope": "user", "token_type": "bearer", "expires_in": 86400}`))
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ tok, err := conf.Exchange(NoContext, "exchange-code")
+ if err != nil {
+ t.Error(err)
+ }
+ if !tok.Valid() {
+ t.Fatalf("Token invalid. Got: %#v", tok)
+ }
+ if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
+ t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+ }
+ if tok.TokenType != "bearer" {
+ t.Errorf("Unexpected token type, %#v.", tok.TokenType)
+ }
+ scope := tok.Extra("scope")
+ if scope != "user" {
+ t.Errorf("Unexpected value for scope: %v", scope)
+ }
+}
+
+const day = 24 * time.Hour
+
+func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) {
+ seconds := int32(day.Seconds())
+ jsonNumberType := reflect.TypeOf(json.Number("0"))
+ for _, c := range []struct {
+ expires string
+ expect error
+ }{
+ {fmt.Sprintf(`"expires_in": %d`, seconds), nil},
+ {fmt.Sprintf(`"expires_in": "%d"`, seconds), nil}, // PayPal case
+ {fmt.Sprintf(`"expires": %d`, seconds), nil}, // Facebook case
+ {`"expires": false`, &json.UnmarshalTypeError{"bool", jsonNumberType}}, // wrong type
+ {`"expires": {}`, &json.UnmarshalTypeError{"object", jsonNumberType}}, // wrong type
+ {`"expires": "zzz"`, &strconv.NumError{"ParseInt", "zzz", strconv.ErrSyntax}}, // wrong value
+ } {
+ testExchangeRequest_JSONResponse_expiry(t, c.expires, c.expect)
+ }
+}
+
+func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, expect error) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp)))
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ t1 := time.Now().Add(day)
+ tok, err := conf.Exchange(NoContext, "exchange-code")
+ t2 := time.Now().Add(day)
+ if err == nil && expect != nil {
+ t.Errorf("Incorrect state, conf.Exchange() should return an error: %v", expect)
+ } else if err != nil {
+ if reflect.DeepEqual(err, expect) {
+ t.Logf("Expected error: %v", err)
+ return
+ } else {
+ t.Error(err)
+ }
+
+ }
+ if !tok.Valid() {
+ t.Fatalf("Token invalid. Got: %#v", tok)
+ }
+ expiry := tok.Expiry
+ if expiry.Before(t1) || expiry.After(t2) {
+ t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2)
+ }
+}
+
+func TestExchangeRequest_BadResponse(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ tok, err := conf.Exchange(NoContext, "code")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if tok.AccessToken != "" {
+ t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
+ }
+}
+
+func TestExchangeRequest_BadResponseType(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ _, err := conf.Exchange(NoContext, "exchange-code")
+ if err == nil {
+ t.Error("expected error from invalid access_token type")
+ }
+}
+
+func TestExchangeRequest_NonBasicAuth(t *testing.T) {
+ tr := &mockTransport{
+ rt: func(r *http.Request) (w *http.Response, err error) {
+ headerAuth := r.Header.Get("Authorization")
+ if headerAuth != "" {
+ t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
+ }
+ return nil, errors.New("no response")
+ },
+ }
+ c := &http.Client{Transport: tr}
+ conf := &Config{
+ ClientID: "CLIENT_ID",
+ Endpoint: Endpoint{
+ AuthURL: "https://accounts.google.com/auth",
+ TokenURL: "https://accounts.google.com/token",
+ },
+ }
+
+ ctx := context.WithValue(context.Background(), HTTPClient, c)
+ conf.Exchange(ctx, "code")
+}
+
+func TestPasswordCredentialsTokenRequest(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ defer r.Body.Close()
+ expected := "/token"
+ if r.URL.String() != expected {
+ t.Errorf("URL = %q; want %q", r.URL, expected)
+ }
+ headerAuth := r.Header.Get("Authorization")
+ expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ="
+ if headerAuth != expected {
+ t.Errorf("Authorization header = %q; want %q", headerAuth, expected)
+ }
+ headerContentType := r.Header.Get("Content-Type")
+ expected = "application/x-www-form-urlencoded"
+ if headerContentType != expected {
+ t.Errorf("Content-Type header = %q; want %q", headerContentType, expected)
+ }
+ body, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ t.Errorf("Failed reading request body: %s.", err)
+ }
+ expected = "client_id=CLIENT_ID&grant_type=password&password=password1&scope=scope1+scope2&username=user1"
+ if string(body) != expected {
+ t.Errorf("res.Body = %q; want %q", string(body), expected)
+ }
+ w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
+ w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ tok, err := conf.PasswordCredentialsToken(NoContext, "user1", "password1")
+ if err != nil {
+ t.Error(err)
+ }
+ if !tok.Valid() {
+ t.Fatalf("Token invalid. Got: %#v", tok)
+ }
+ expected := "90d64460d14870c08c81352a05dedd3465940a7c"
+ if tok.AccessToken != expected {
+ t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected)
+ }
+ expected = "bearer"
+ if tok.TokenType != expected {
+ t.Errorf("TokenType = %q; want %q", tok.TokenType, expected)
+ }
+}
+
+func TestTokenRefreshRequest(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.String() == "/somethingelse" {
+ return
+ }
+ if r.URL.String() != "/token" {
+ t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
+ }
+ headerContentType := r.Header.Get("Content-Type")
+ if headerContentType != "application/x-www-form-urlencoded" {
+ t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+ }
+ body, _ := ioutil.ReadAll(r.Body)
+ if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
+ t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
+ }
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ c := conf.Client(NoContext, &Token{RefreshToken: "REFRESH_TOKEN"})
+ c.Get(ts.URL + "/somethingelse")
+}
+
+func TestFetchWithNoRefreshToken(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.String() == "/somethingelse" {
+ return
+ }
+ if r.URL.String() != "/token" {
+ t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
+ }
+ headerContentType := r.Header.Get("Content-Type")
+ if headerContentType != "application/x-www-form-urlencoded" {
+ t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
+ }
+ body, _ := ioutil.ReadAll(r.Body)
+ if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
+ t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
+ }
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ c := conf.Client(NoContext, nil)
+ _, err := c.Get(ts.URL + "/somethingelse")
+ if err == nil {
+ t.Errorf("Fetch should return an error if no refresh token is set")
+ }
+}
+
+func TestRefreshToken_RefreshTokenReplacement(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(`{"access_token":"ACCESS TOKEN", "scope": "user", "token_type": "bearer", "refresh_token": "NEW REFRESH TOKEN"}`))
+ return
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+ tkr := tokenRefresher{
+ conf: conf,
+ ctx: NoContext,
+ refreshToken: "OLD REFRESH TOKEN",
+ }
+ tk, err := tkr.Token()
+ if err != nil {
+ t.Errorf("Unexpected refreshToken error returned: %v", err)
+ return
+ }
+ if tk.RefreshToken != tkr.refreshToken {
+ t.Errorf("tokenRefresher.refresh_token = %s; want %s", tkr.refreshToken, tk.RefreshToken)
+ }
+}
+
+func TestConfigClientWithToken(t *testing.T) {
+ tok := &Token{
+ AccessToken: "abc123",
+ }
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want {
+ t.Errorf("Authorization header = %q; want %q", got, want)
+ }
+ return
+ }))
+ defer ts.Close()
+ conf := newConf(ts.URL)
+
+ c := conf.Client(NoContext, tok)
+ req, err := http.NewRequest("GET", ts.URL, nil)
+ if err != nil {
+ t.Error(err)
+ }
+ _, err = c.Do(req)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func Test_providerAuthHeaderWorks(t *testing.T) {
+ for _, p := range brokenAuthHeaderProviders {
+ if providerAuthHeaderWorks(p) {
+ t.Errorf("URL: %s not found in list", p)
+ }
+ p := fmt.Sprintf("%ssomesuffix", p)
+ if providerAuthHeaderWorks(p) {
+ t.Errorf("URL: %s not found in list", p)
+ }
+ }
+ p := "https://api.not-in-the-list-example.com/"
+ if !providerAuthHeaderWorks(p) {
+ t.Errorf("URL: %s found in list", p)
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go
new file mode 100644
index 000000000..f7b299902
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/odnoklassniki/odnoklassniki.go
@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 odnoklassniki provides constants for using OAuth2 to access Odnoklassniki.
+package odnoklassniki
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Endpoint is Odnoklassniki's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://www.odnoklassniki.ru/oauth/authorize",
+ TokenURL: "https://api.odnoklassniki.ru/oauth/token.do",
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go
new file mode 100644
index 000000000..e0f624b96
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/paypal/paypal.go
@@ -0,0 +1,22 @@
+// Copyright 2015 The oauth2 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 paypal provides constants for using OAuth2 to access PayPal.
+package paypal
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
+ TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice",
+}
+
+// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment.
+var SandboxEndpoint = oauth2.Endpoint{
+ AuthURL: "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
+ TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice",
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/token.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/token.go
new file mode 100644
index 000000000..9852ceb4a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/token.go
@@ -0,0 +1,104 @@
+// Copyright 2014 The oauth2 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 oauth2
+
+import (
+ "net/http"
+ "net/url"
+ "time"
+)
+
+// expiryDelta determines how earlier a token should be considered
+// expired than its actual expiration time. It is used to avoid late
+// expirations due to client-server time mismatches.
+const expiryDelta = 10 * time.Second
+
+// Token represents the crendentials used to authorize
+// the requests to access protected resources on the OAuth 2.0
+// provider's backend.
+//
+// Most users of this package should not access fields of Token
+// directly. They're exported mostly for use by related packages
+// implementing derivative OAuth2 flows.
+type Token struct {
+ // AccessToken is the token that authorizes and authenticates
+ // the requests.
+ AccessToken string `json:"access_token"`
+
+ // TokenType is the type of token.
+ // The Type method returns either this or "Bearer", the default.
+ TokenType string `json:"token_type,omitempty"`
+
+ // RefreshToken is a token that's used by the application
+ // (as opposed to the user) to refresh the access token
+ // if it expires.
+ RefreshToken string `json:"refresh_token,omitempty"`
+
+ // Expiry is the optional expiration time of the access token.
+ //
+ // If zero, TokenSource implementations will reuse the same
+ // token forever and RefreshToken or equivalent
+ // mechanisms for that TokenSource will not be used.
+ Expiry time.Time `json:"expiry,omitempty"`
+
+ // raw optionally contains extra metadata from the server
+ // when updating a token.
+ raw interface{}
+}
+
+// Type returns t.TokenType if non-empty, else "Bearer".
+func (t *Token) Type() string {
+ if t.TokenType != "" {
+ return t.TokenType
+ }
+ return "Bearer"
+}
+
+// SetAuthHeader sets the Authorization header to r using the access
+// token in t.
+//
+// This method is unnecessary when using Transport or an HTTP Client
+// returned by this package.
+func (t *Token) SetAuthHeader(r *http.Request) {
+ r.Header.Set("Authorization", t.Type()+" "+t.AccessToken)
+}
+
+// WithExtra returns a new Token that's a clone of t, but using the
+// provided raw extra map. This is only intended for use by packages
+// implementing derivative OAuth2 flows.
+func (t *Token) WithExtra(extra interface{}) *Token {
+ t2 := new(Token)
+ *t2 = *t
+ t2.raw = extra
+ return t2
+}
+
+// Extra returns an extra field.
+// Extra fields are key-value pairs returned by the server as a
+// part of the token retrieval response.
+func (t *Token) Extra(key string) interface{} {
+ if vals, ok := t.raw.(url.Values); ok {
+ // TODO(jbd): Cast numeric values to int64 or float64.
+ return vals.Get(key)
+ }
+ if raw, ok := t.raw.(map[string]interface{}); ok {
+ return raw[key]
+ }
+ return nil
+}
+
+// expired reports whether the token is expired.
+// t must be non-nil.
+func (t *Token) expired() bool {
+ if t.Expiry.IsZero() {
+ return false
+ }
+ return t.Expiry.Add(-expiryDelta).Before(time.Now())
+}
+
+// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
+func (t *Token) Valid() bool {
+ return t != nil && t.AccessToken != "" && !t.expired()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go
new file mode 100644
index 000000000..739eeb2a2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/token_test.go
@@ -0,0 +1,50 @@
+// Copyright 2014 The oauth2 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 oauth2
+
+import (
+ "testing"
+ "time"
+)
+
+func TestTokenExtra(t *testing.T) {
+ type testCase struct {
+ key string
+ val interface{}
+ want interface{}
+ }
+ const key = "extra-key"
+ cases := []testCase{
+ {key: key, val: "abc", want: "abc"},
+ {key: key, val: 123, want: 123},
+ {key: key, val: "", want: ""},
+ {key: "other-key", val: "def", want: nil},
+ }
+ for _, tc := range cases {
+ extra := make(map[string]interface{})
+ extra[tc.key] = tc.val
+ tok := &Token{raw: extra}
+ if got, want := tok.Extra(key), tc.want; got != want {
+ t.Errorf("Extra(%q) = %q; want %q", key, got, want)
+ }
+ }
+}
+
+func TestTokenExpiry(t *testing.T) {
+ now := time.Now()
+ cases := []struct {
+ name string
+ tok *Token
+ want bool
+ }{
+ {name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false},
+ {name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true},
+ }
+ for _, tc := range cases {
+ if got, want := tc.tok.expired(), tc.want; got != want {
+ t.Errorf("expired (%q) = %v; want %v", tc.name, got, want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/transport.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/transport.go
new file mode 100644
index 000000000..10339a0be
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/transport.go
@@ -0,0 +1,138 @@
+// Copyright 2014 The oauth2 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 oauth2
+
+import (
+ "errors"
+ "io"
+ "net/http"
+ "sync"
+)
+
+// Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
+// wrapping a base RoundTripper and adding an Authorization header
+// with a token from the supplied Sources.
+//
+// Transport is a low-level mechanism. Most code will use the
+// higher-level Config.Client method instead.
+type Transport struct {
+ // Source supplies the token to add to outgoing requests'
+ // Authorization headers.
+ Source TokenSource
+
+ // Base is the base RoundTripper used to make HTTP requests.
+ // If nil, http.DefaultTransport is used.
+ Base http.RoundTripper
+
+ mu sync.Mutex // guards modReq
+ modReq map[*http.Request]*http.Request // original -> modified
+}
+
+// RoundTrip authorizes and authenticates the request with an
+// access token. If no token exists or token is expired,
+// tries to refresh/fetch a new token.
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+ if t.Source == nil {
+ return nil, errors.New("oauth2: Transport's Source is nil")
+ }
+ token, err := t.Source.Token()
+ if err != nil {
+ return nil, err
+ }
+
+ req2 := cloneRequest(req) // per RoundTripper contract
+ token.SetAuthHeader(req2)
+ t.setModReq(req, req2)
+ res, err := t.base().RoundTrip(req2)
+ if err != nil {
+ t.setModReq(req, nil)
+ return nil, err
+ }
+ res.Body = &onEOFReader{
+ rc: res.Body,
+ fn: func() { t.setModReq(req, nil) },
+ }
+ return res, nil
+}
+
+// CancelRequest cancels an in-flight request by closing its connection.
+func (t *Transport) CancelRequest(req *http.Request) {
+ type canceler interface {
+ CancelRequest(*http.Request)
+ }
+ if cr, ok := t.base().(canceler); ok {
+ t.mu.Lock()
+ modReq := t.modReq[req]
+ delete(t.modReq, req)
+ t.mu.Unlock()
+ cr.CancelRequest(modReq)
+ }
+}
+
+func (t *Transport) base() http.RoundTripper {
+ if t.Base != nil {
+ return t.Base
+ }
+ return http.DefaultTransport
+}
+
+func (t *Transport) setModReq(orig, mod *http.Request) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.modReq == nil {
+ t.modReq = make(map[*http.Request]*http.Request)
+ }
+ if mod == nil {
+ delete(t.modReq, orig)
+ } else {
+ t.modReq[orig] = mod
+ }
+}
+
+// cloneRequest returns a clone of the provided *http.Request.
+// The clone is a shallow copy of the struct and its Header map.
+func cloneRequest(r *http.Request) *http.Request {
+ // shallow copy of the struct
+ r2 := new(http.Request)
+ *r2 = *r
+ // deep copy of the Header
+ r2.Header = make(http.Header, len(r.Header))
+ for k, s := range r.Header {
+ r2.Header[k] = append([]string(nil), s...)
+ }
+ return r2
+}
+
+type onEOFReader struct {
+ rc io.ReadCloser
+ fn func()
+}
+
+func (r *onEOFReader) Read(p []byte) (n int, err error) {
+ n, err = r.rc.Read(p)
+ if err == io.EOF {
+ r.runFunc()
+ }
+ return
+}
+
+func (r *onEOFReader) Close() error {
+ err := r.rc.Close()
+ r.runFunc()
+ return err
+}
+
+func (r *onEOFReader) runFunc() {
+ if fn := r.fn; fn != nil {
+ fn()
+ r.fn = nil
+ }
+}
+
+type errorTransport struct{ err error }
+
+func (t errorTransport) RoundTrip(*http.Request) (*http.Response, error) {
+ return nil, t.err
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go
new file mode 100644
index 000000000..efb8232ac
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/transport_test.go
@@ -0,0 +1,53 @@
+package oauth2
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+)
+
+type tokenSource struct{ token *Token }
+
+func (t *tokenSource) Token() (*Token, error) {
+ return t.token, nil
+}
+
+func TestTransportTokenSource(t *testing.T) {
+ ts := &tokenSource{
+ token: &Token{
+ AccessToken: "abc",
+ },
+ }
+ tr := &Transport{
+ Source: ts,
+ }
+ server := newMockServer(func(w http.ResponseWriter, r *http.Request) {
+ if r.Header.Get("Authorization") != "Bearer abc" {
+ t.Errorf("Transport doesn't set the Authorization header from the fetched token")
+ }
+ })
+ defer server.Close()
+ client := http.Client{Transport: tr}
+ client.Get(server.URL)
+}
+
+func TestTokenValidNoAccessToken(t *testing.T) {
+ token := &Token{}
+ if token.Valid() {
+ t.Errorf("Token should not be valid with no access token")
+ }
+}
+
+func TestExpiredWithExpiry(t *testing.T) {
+ token := &Token{
+ Expiry: time.Now().Add(-5 * time.Hour),
+ }
+ if token.Valid() {
+ t.Errorf("Token should not be valid if it expired in the past")
+ }
+}
+
+func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
+ return httptest.NewServer(http.HandlerFunc(handler))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go
new file mode 100644
index 000000000..4c3e71a99
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/vk/vk.go
@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 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 vk provides constants for using OAuth2 to access VK.com.
+package vk
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+)
+
+// Endpoint is VK's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+ AuthURL: "https://oauth.vk.com/authorize",
+ TokenURL: "https://oauth.vk.com/access_token",
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/go13.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/go13.go
new file mode 100644
index 000000000..c979f4390
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/go13.go
@@ -0,0 +1,37 @@
+// Copyright 2015 Google Inc. All Rights Reserved.
+//
+// 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.3
+
+package metadata
+
+import (
+ "net"
+ "time"
+)
+
+// This is a workaround for https://github.com/golang/oauth2/issues/70, where
+// net.Dialer.KeepAlive is unavailable on Go 1.2 (which App Engine as of
+// Jan 2015 still runs).
+//
+// TODO(bradfitz,jbd,adg): remove this once App Engine supports Go
+// 1.3+.
+func init() {
+ go13Dialer = func() *net.Dialer {
+ return &net.Dialer{
+ Timeout: 750 * time.Millisecond,
+ KeepAlive: 30 * time.Second,
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/metadata.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/metadata.go
new file mode 100644
index 000000000..eeacfc1a7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/metadata.go
@@ -0,0 +1,267 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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 metadata provides access to Google Compute Engine (GCE)
+// metadata and API service accounts.
+//
+// This package is a wrapper around the GCE metadata service,
+// as documented at https://developers.google.com/compute/docs/metadata.
+package metadata
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal"
+)
+
+type cachedValue struct {
+ k string
+ trim bool
+ mu sync.Mutex
+ v string
+}
+
+var (
+ projID = &cachedValue{k: "project/project-id", trim: true}
+ projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
+ instID = &cachedValue{k: "instance/id", trim: true}
+)
+
+var metaClient = &http.Client{
+ Transport: &internal.Transport{
+ Base: &http.Transport{
+ Dial: dialer().Dial,
+ ResponseHeaderTimeout: 750 * time.Millisecond,
+ },
+ },
+}
+
+// go13Dialer is nil until we're using Go 1.3+.
+// This is a workaround for https://github.com/golang/oauth2/issues/70, where
+// net.Dialer.KeepAlive is unavailable on Go 1.2 (which App Engine as of
+// Jan 2015 still runs).
+//
+// TODO(bradfitz,jbd,adg,dsymonds): remove this once App Engine supports Go
+// 1.3+ and go-app-builder also supports 1.3+, or when Go 1.2 is no longer an
+// option on App Engine.
+var go13Dialer func() *net.Dialer
+
+func dialer() *net.Dialer {
+ if fn := go13Dialer; fn != nil {
+ return fn()
+ }
+ return &net.Dialer{
+ Timeout: 750 * time.Millisecond,
+ }
+}
+
+// NotDefinedError is returned when requested metadata is not defined.
+//
+// The underlying string is the suffix after "/computeMetadata/v1/".
+//
+// This error is not returned if the value is defined to be the empty
+// string.
+type NotDefinedError string
+
+func (suffix NotDefinedError) Error() string {
+ return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
+}
+
+// Get returns a value from the metadata service.
+// The suffix is appended to "http://metadata/computeMetadata/v1/".
+//
+// If the requested metadata is not defined, the returned error will
+// be of type NotDefinedError.
+func Get(suffix string) (string, error) {
+ // Using 169.254.169.254 instead of "metadata" here because Go
+ // binaries built with the "netgo" tag and without cgo won't
+ // know the search suffix for "metadata" is
+ // ".google.internal", and this IP address is documented as
+ // being stable anyway.
+ url := "http://169.254.169.254/computeMetadata/v1/" + suffix
+ req, _ := http.NewRequest("GET", url, nil)
+ req.Header.Set("Metadata-Flavor", "Google")
+ res, err := metaClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer res.Body.Close()
+ if res.StatusCode == http.StatusNotFound {
+ return "", NotDefinedError(suffix)
+ }
+ if res.StatusCode != 200 {
+ return "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
+ }
+ all, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return "", err
+ }
+ return string(all), nil
+}
+
+func getTrimmed(suffix string) (s string, err error) {
+ s, err = Get(suffix)
+ s = strings.TrimSpace(s)
+ return
+}
+
+func (c *cachedValue) get() (v string, err error) {
+ defer c.mu.Unlock()
+ c.mu.Lock()
+ if c.v != "" {
+ return c.v, nil
+ }
+ if c.trim {
+ v, err = getTrimmed(c.k)
+ } else {
+ v, err = Get(c.k)
+ }
+ if err == nil {
+ c.v = v
+ }
+ return
+}
+
+var onGCE struct {
+ sync.Mutex
+ set bool
+ v bool
+}
+
+// OnGCE reports whether this process is running on Google Compute Engine.
+func OnGCE() bool {
+ defer onGCE.Unlock()
+ onGCE.Lock()
+ if onGCE.set {
+ return onGCE.v
+ }
+ onGCE.set = true
+
+ // We use the DNS name of the metadata service here instead of the IP address
+ // because we expect that to fail faster in the not-on-GCE case.
+ res, err := metaClient.Get("http://metadata.google.internal")
+ if err != nil {
+ return false
+ }
+ onGCE.v = res.Header.Get("Metadata-Flavor") == "Google"
+ return onGCE.v
+}
+
+// ProjectID returns the current instance's project ID string.
+func ProjectID() (string, error) { return projID.get() }
+
+// NumericProjectID returns the current instance's numeric project ID.
+func NumericProjectID() (string, error) { return projNum.get() }
+
+// InternalIP returns the instance's primary internal IP address.
+func InternalIP() (string, error) {
+ return getTrimmed("instance/network-interfaces/0/ip")
+}
+
+// ExternalIP returns the instance's primary external (public) IP address.
+func ExternalIP() (string, error) {
+ return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
+}
+
+// Hostname returns the instance's hostname. This will probably be of
+// the form "INSTANCENAME.c.PROJECT.internal" but that isn't
+// guaranteed.
+//
+// TODO: what is this defined to be? Docs say "The host name of the
+// instance."
+func Hostname() (string, error) {
+ return getTrimmed("network-interfaces/0/ip")
+}
+
+// InstanceTags returns the list of user-defined instance tags,
+// assigned when initially creating a GCE instance.
+func InstanceTags() ([]string, error) {
+ var s []string
+ j, err := Get("instance/tags")
+ if err != nil {
+ return nil, err
+ }
+ if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
+ return nil, err
+ }
+ return s, nil
+}
+
+// InstanceID returns the current VM's numeric instance ID.
+func InstanceID() (string, error) {
+ return instID.get()
+}
+
+// InstanceAttributes returns the list of user-defined attributes,
+// assigned when initially creating a GCE VM instance. The value of an
+// attribute can be obtained with InstanceAttributeValue.
+func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
+
+// ProjectAttributes returns the list of user-defined attributes
+// applying to the project as a whole, not just this VM. The value of
+// an attribute can be obtained with ProjectAttributeValue.
+func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
+
+func lines(suffix string) ([]string, error) {
+ j, err := Get(suffix)
+ if err != nil {
+ return nil, err
+ }
+ s := strings.Split(strings.TrimSpace(j), "\n")
+ for i := range s {
+ s[i] = strings.TrimSpace(s[i])
+ }
+ return s, nil
+}
+
+// InstanceAttributeValue returns the value of the provided VM
+// instance attribute.
+//
+// If the requested attribute is not defined, the returned error will
+// be of type NotDefinedError.
+//
+// InstanceAttributeValue may return ("", nil) if the attribute was
+// defined to be the empty string.
+func InstanceAttributeValue(attr string) (string, error) {
+ return Get("instance/attributes/" + attr)
+}
+
+// ProjectAttributeValue returns the value of the provided
+// project attribute.
+//
+// If the requested attribute is not defined, the returned error will
+// be of type NotDefinedError.
+//
+// ProjectAttributeValue may return ("", nil) if the attribute was
+// defined to be the empty string.
+func ProjectAttributeValue(attr string) (string, error) {
+ return Get("project/attributes/" + attr)
+}
+
+// Scopes returns the service account scopes for the given account.
+// The account may be empty or the string "default" to use the instance's
+// main account.
+func Scopes(serviceAccount string) ([]string, error) {
+ if serviceAccount == "" {
+ serviceAccount = "default"
+ }
+ return lines("instance/service-accounts/" + serviceAccount + "/scopes")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/cloud.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/cloud.go
new file mode 100644
index 000000000..68fc7f223
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/cloud.go
@@ -0,0 +1,128 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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 internal provides support for the cloud packages.
+//
+// Users should not import this package directly.
+package internal
+
+import (
+ "fmt"
+ "net/http"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+type contextKey struct{}
+
+func WithContext(parent context.Context, projID string, c *http.Client) context.Context {
+ if c == nil {
+ panic("nil *http.Client passed to WithContext")
+ }
+ if projID == "" {
+ panic("empty project ID passed to WithContext")
+ }
+ return context.WithValue(parent, contextKey{}, &cloudContext{
+ ProjectID: projID,
+ HTTPClient: c,
+ })
+}
+
+const userAgent = "gcloud-golang/0.1"
+
+type cloudContext struct {
+ ProjectID string
+ HTTPClient *http.Client
+
+ mu sync.Mutex // guards svc
+ svc map[string]interface{} // e.g. "storage" => *rawStorage.Service
+}
+
+// Service returns the result of the fill function if it's never been
+// called before for the given name (which is assumed to be an API
+// service name, like "datastore"). If it has already been cached, the fill
+// func is not run.
+// It's safe for concurrent use by multiple goroutines.
+func Service(ctx context.Context, name string, fill func(*http.Client) interface{}) interface{} {
+ return cc(ctx).service(name, fill)
+}
+
+func (c *cloudContext) service(name string, fill func(*http.Client) interface{}) interface{} {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ if c.svc == nil {
+ c.svc = make(map[string]interface{})
+ } else if v, ok := c.svc[name]; ok {
+ return v
+ }
+ v := fill(c.HTTPClient)
+ c.svc[name] = v
+ return v
+}
+
+// Transport is an http.RoundTripper that appends
+// Google Cloud client's user-agent to the original
+// request's user-agent header.
+type Transport struct {
+ // Base represents the actual http.RoundTripper
+ // the requests will be delegated to.
+ Base http.RoundTripper
+}
+
+// RoundTrip appends a user-agent to the existing user-agent
+// header and delegates the request to the base http.RoundTripper.
+func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
+ req = cloneRequest(req)
+ ua := req.Header.Get("User-Agent")
+ if ua == "" {
+ ua = userAgent
+ } else {
+ ua = fmt.Sprintf("%s %s", ua, userAgent)
+ }
+ req.Header.Set("User-Agent", ua)
+ return t.Base.RoundTrip(req)
+}
+
+// cloneRequest returns a clone of the provided *http.Request.
+// The clone is a shallow copy of the struct and its Header map.
+func cloneRequest(r *http.Request) *http.Request {
+ // shallow copy of the struct
+ r2 := new(http.Request)
+ *r2 = *r
+ // deep copy of the Header
+ r2.Header = make(http.Header)
+ for k, s := range r.Header {
+ r2.Header[k] = s
+ }
+ return r2
+}
+
+func ProjID(ctx context.Context) string {
+ return cc(ctx).ProjectID
+}
+
+func HTTPClient(ctx context.Context) *http.Client {
+ return cc(ctx).HTTPClient
+}
+
+// cc returns the internal *cloudContext (cc) state for a context.Context.
+// It panics if the user did it wrong.
+func cc(ctx context.Context) *cloudContext {
+ if c, ok := ctx.Value(contextKey{}).(*cloudContext); ok {
+ return c
+ }
+ panic("invalid context.Context type; it should be created with cloud.NewContext")
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go
new file mode 100644
index 000000000..3fb1083c0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.pb.go
@@ -0,0 +1,1633 @@
+// Code generated by protoc-gen-go.
+// source: datastore_v1.proto
+// DO NOT EDIT!
+
+/*
+Package datastore is a generated protocol buffer package.
+
+It is generated from these files:
+ datastore_v1.proto
+
+It has these top-level messages:
+ PartitionId
+ Key
+ Value
+ Property
+ Entity
+ EntityResult
+ Query
+ KindExpression
+ PropertyReference
+ PropertyExpression
+ PropertyOrder
+ Filter
+ CompositeFilter
+ PropertyFilter
+ GqlQuery
+ GqlQueryArg
+ QueryResultBatch
+ Mutation
+ MutationResult
+ ReadOptions
+ LookupRequest
+ LookupResponse
+ RunQueryRequest
+ RunQueryResponse
+ BeginTransactionRequest
+ BeginTransactionResponse
+ RollbackRequest
+ RollbackResponse
+ CommitRequest
+ CommitResponse
+ AllocateIdsRequest
+ AllocateIdsResponse
+*/
+package datastore
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+// Specifies what data the 'entity' field contains.
+// A ResultType is either implied (for example, in LookupResponse.found it
+// is always FULL) or specified by context (for example, in message
+// QueryResultBatch, field 'entity_result_type' specifies a ResultType
+// for all the values in field 'entity_result').
+type EntityResult_ResultType int32
+
+const (
+ EntityResult_FULL EntityResult_ResultType = 1
+ EntityResult_PROJECTION EntityResult_ResultType = 2
+ // The entity may have no key.
+ // A property value may have meaning 18.
+ EntityResult_KEY_ONLY EntityResult_ResultType = 3
+)
+
+var EntityResult_ResultType_name = map[int32]string{
+ 1: "FULL",
+ 2: "PROJECTION",
+ 3: "KEY_ONLY",
+}
+var EntityResult_ResultType_value = map[string]int32{
+ "FULL": 1,
+ "PROJECTION": 2,
+ "KEY_ONLY": 3,
+}
+
+func (x EntityResult_ResultType) Enum() *EntityResult_ResultType {
+ p := new(EntityResult_ResultType)
+ *p = x
+ return p
+}
+func (x EntityResult_ResultType) String() string {
+ return proto.EnumName(EntityResult_ResultType_name, int32(x))
+}
+func (x *EntityResult_ResultType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EntityResult_ResultType_value, data, "EntityResult_ResultType")
+ if err != nil {
+ return err
+ }
+ *x = EntityResult_ResultType(value)
+ return nil
+}
+
+type PropertyExpression_AggregationFunction int32
+
+const (
+ PropertyExpression_FIRST PropertyExpression_AggregationFunction = 1
+)
+
+var PropertyExpression_AggregationFunction_name = map[int32]string{
+ 1: "FIRST",
+}
+var PropertyExpression_AggregationFunction_value = map[string]int32{
+ "FIRST": 1,
+}
+
+func (x PropertyExpression_AggregationFunction) Enum() *PropertyExpression_AggregationFunction {
+ p := new(PropertyExpression_AggregationFunction)
+ *p = x
+ return p
+}
+func (x PropertyExpression_AggregationFunction) String() string {
+ return proto.EnumName(PropertyExpression_AggregationFunction_name, int32(x))
+}
+func (x *PropertyExpression_AggregationFunction) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PropertyExpression_AggregationFunction_value, data, "PropertyExpression_AggregationFunction")
+ if err != nil {
+ return err
+ }
+ *x = PropertyExpression_AggregationFunction(value)
+ return nil
+}
+
+type PropertyOrder_Direction int32
+
+const (
+ PropertyOrder_ASCENDING PropertyOrder_Direction = 1
+ PropertyOrder_DESCENDING PropertyOrder_Direction = 2
+)
+
+var PropertyOrder_Direction_name = map[int32]string{
+ 1: "ASCENDING",
+ 2: "DESCENDING",
+}
+var PropertyOrder_Direction_value = map[string]int32{
+ "ASCENDING": 1,
+ "DESCENDING": 2,
+}
+
+func (x PropertyOrder_Direction) Enum() *PropertyOrder_Direction {
+ p := new(PropertyOrder_Direction)
+ *p = x
+ return p
+}
+func (x PropertyOrder_Direction) String() string {
+ return proto.EnumName(PropertyOrder_Direction_name, int32(x))
+}
+func (x *PropertyOrder_Direction) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PropertyOrder_Direction_value, data, "PropertyOrder_Direction")
+ if err != nil {
+ return err
+ }
+ *x = PropertyOrder_Direction(value)
+ return nil
+}
+
+type CompositeFilter_Operator int32
+
+const (
+ CompositeFilter_AND CompositeFilter_Operator = 1
+)
+
+var CompositeFilter_Operator_name = map[int32]string{
+ 1: "AND",
+}
+var CompositeFilter_Operator_value = map[string]int32{
+ "AND": 1,
+}
+
+func (x CompositeFilter_Operator) Enum() *CompositeFilter_Operator {
+ p := new(CompositeFilter_Operator)
+ *p = x
+ return p
+}
+func (x CompositeFilter_Operator) String() string {
+ return proto.EnumName(CompositeFilter_Operator_name, int32(x))
+}
+func (x *CompositeFilter_Operator) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(CompositeFilter_Operator_value, data, "CompositeFilter_Operator")
+ if err != nil {
+ return err
+ }
+ *x = CompositeFilter_Operator(value)
+ return nil
+}
+
+type PropertyFilter_Operator int32
+
+const (
+ PropertyFilter_LESS_THAN PropertyFilter_Operator = 1
+ PropertyFilter_LESS_THAN_OR_EQUAL PropertyFilter_Operator = 2
+ PropertyFilter_GREATER_THAN PropertyFilter_Operator = 3
+ PropertyFilter_GREATER_THAN_OR_EQUAL PropertyFilter_Operator = 4
+ PropertyFilter_EQUAL PropertyFilter_Operator = 5
+ PropertyFilter_HAS_ANCESTOR PropertyFilter_Operator = 11
+)
+
+var PropertyFilter_Operator_name = map[int32]string{
+ 1: "LESS_THAN",
+ 2: "LESS_THAN_OR_EQUAL",
+ 3: "GREATER_THAN",
+ 4: "GREATER_THAN_OR_EQUAL",
+ 5: "EQUAL",
+ 11: "HAS_ANCESTOR",
+}
+var PropertyFilter_Operator_value = map[string]int32{
+ "LESS_THAN": 1,
+ "LESS_THAN_OR_EQUAL": 2,
+ "GREATER_THAN": 3,
+ "GREATER_THAN_OR_EQUAL": 4,
+ "EQUAL": 5,
+ "HAS_ANCESTOR": 11,
+}
+
+func (x PropertyFilter_Operator) Enum() *PropertyFilter_Operator {
+ p := new(PropertyFilter_Operator)
+ *p = x
+ return p
+}
+func (x PropertyFilter_Operator) String() string {
+ return proto.EnumName(PropertyFilter_Operator_name, int32(x))
+}
+func (x *PropertyFilter_Operator) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PropertyFilter_Operator_value, data, "PropertyFilter_Operator")
+ if err != nil {
+ return err
+ }
+ *x = PropertyFilter_Operator(value)
+ return nil
+}
+
+// The possible values for the 'more_results' field.
+type QueryResultBatch_MoreResultsType int32
+
+const (
+ QueryResultBatch_NOT_FINISHED QueryResultBatch_MoreResultsType = 1
+ QueryResultBatch_MORE_RESULTS_AFTER_LIMIT QueryResultBatch_MoreResultsType = 2
+ // results after the limit.
+ QueryResultBatch_NO_MORE_RESULTS QueryResultBatch_MoreResultsType = 3
+)
+
+var QueryResultBatch_MoreResultsType_name = map[int32]string{
+ 1: "NOT_FINISHED",
+ 2: "MORE_RESULTS_AFTER_LIMIT",
+ 3: "NO_MORE_RESULTS",
+}
+var QueryResultBatch_MoreResultsType_value = map[string]int32{
+ "NOT_FINISHED": 1,
+ "MORE_RESULTS_AFTER_LIMIT": 2,
+ "NO_MORE_RESULTS": 3,
+}
+
+func (x QueryResultBatch_MoreResultsType) Enum() *QueryResultBatch_MoreResultsType {
+ p := new(QueryResultBatch_MoreResultsType)
+ *p = x
+ return p
+}
+func (x QueryResultBatch_MoreResultsType) String() string {
+ return proto.EnumName(QueryResultBatch_MoreResultsType_name, int32(x))
+}
+func (x *QueryResultBatch_MoreResultsType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(QueryResultBatch_MoreResultsType_value, data, "QueryResultBatch_MoreResultsType")
+ if err != nil {
+ return err
+ }
+ *x = QueryResultBatch_MoreResultsType(value)
+ return nil
+}
+
+type ReadOptions_ReadConsistency int32
+
+const (
+ ReadOptions_DEFAULT ReadOptions_ReadConsistency = 0
+ ReadOptions_STRONG ReadOptions_ReadConsistency = 1
+ ReadOptions_EVENTUAL ReadOptions_ReadConsistency = 2
+)
+
+var ReadOptions_ReadConsistency_name = map[int32]string{
+ 0: "DEFAULT",
+ 1: "STRONG",
+ 2: "EVENTUAL",
+}
+var ReadOptions_ReadConsistency_value = map[string]int32{
+ "DEFAULT": 0,
+ "STRONG": 1,
+ "EVENTUAL": 2,
+}
+
+func (x ReadOptions_ReadConsistency) Enum() *ReadOptions_ReadConsistency {
+ p := new(ReadOptions_ReadConsistency)
+ *p = x
+ return p
+}
+func (x ReadOptions_ReadConsistency) String() string {
+ return proto.EnumName(ReadOptions_ReadConsistency_name, int32(x))
+}
+func (x *ReadOptions_ReadConsistency) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(ReadOptions_ReadConsistency_value, data, "ReadOptions_ReadConsistency")
+ if err != nil {
+ return err
+ }
+ *x = ReadOptions_ReadConsistency(value)
+ return nil
+}
+
+type BeginTransactionRequest_IsolationLevel int32
+
+const (
+ BeginTransactionRequest_SNAPSHOT BeginTransactionRequest_IsolationLevel = 0
+ // conflict if their mutations conflict. For example:
+ // Read(A),Write(B) may not conflict with Read(B),Write(A),
+ // but Read(B),Write(B) does conflict with Read(B),Write(B).
+ BeginTransactionRequest_SERIALIZABLE BeginTransactionRequest_IsolationLevel = 1
+)
+
+var BeginTransactionRequest_IsolationLevel_name = map[int32]string{
+ 0: "SNAPSHOT",
+ 1: "SERIALIZABLE",
+}
+var BeginTransactionRequest_IsolationLevel_value = map[string]int32{
+ "SNAPSHOT": 0,
+ "SERIALIZABLE": 1,
+}
+
+func (x BeginTransactionRequest_IsolationLevel) Enum() *BeginTransactionRequest_IsolationLevel {
+ p := new(BeginTransactionRequest_IsolationLevel)
+ *p = x
+ return p
+}
+func (x BeginTransactionRequest_IsolationLevel) String() string {
+ return proto.EnumName(BeginTransactionRequest_IsolationLevel_name, int32(x))
+}
+func (x *BeginTransactionRequest_IsolationLevel) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(BeginTransactionRequest_IsolationLevel_value, data, "BeginTransactionRequest_IsolationLevel")
+ if err != nil {
+ return err
+ }
+ *x = BeginTransactionRequest_IsolationLevel(value)
+ return nil
+}
+
+type CommitRequest_Mode int32
+
+const (
+ CommitRequest_TRANSACTIONAL CommitRequest_Mode = 1
+ CommitRequest_NON_TRANSACTIONAL CommitRequest_Mode = 2
+)
+
+var CommitRequest_Mode_name = map[int32]string{
+ 1: "TRANSACTIONAL",
+ 2: "NON_TRANSACTIONAL",
+}
+var CommitRequest_Mode_value = map[string]int32{
+ "TRANSACTIONAL": 1,
+ "NON_TRANSACTIONAL": 2,
+}
+
+func (x CommitRequest_Mode) Enum() *CommitRequest_Mode {
+ p := new(CommitRequest_Mode)
+ *p = x
+ return p
+}
+func (x CommitRequest_Mode) String() string {
+ return proto.EnumName(CommitRequest_Mode_name, int32(x))
+}
+func (x *CommitRequest_Mode) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(CommitRequest_Mode_value, data, "CommitRequest_Mode")
+ if err != nil {
+ return err
+ }
+ *x = CommitRequest_Mode(value)
+ return nil
+}
+
+// An identifier for a particular subset of entities.
+//
+// Entities are partitioned into various subsets, each used by different
+// datasets and different namespaces within a dataset and so forth.
+//
+// All input partition IDs are normalized before use.
+// A partition ID is normalized as follows:
+// If the partition ID is unset or is set to an empty partition ID, replace it
+// with the context partition ID.
+// Otherwise, if the partition ID has no dataset ID, assign it the context
+// partition ID's dataset ID.
+// Unless otherwise documented, the context partition ID has the dataset ID set
+// to the context dataset ID and no other partition dimension set.
+//
+// A partition ID is empty if all of its fields are unset.
+//
+// Partition dimension:
+// A dimension may be unset.
+// A dimension's value must never be "".
+// A dimension's value must match [A-Za-z\d\.\-_]{1,100}
+// If the value of any dimension matches regex "__.*__",
+// the partition is reserved/read-only.
+// A reserved/read-only partition ID is forbidden in certain documented contexts.
+//
+// Dataset ID:
+// A dataset id's value must never be "".
+// A dataset id's value must match
+// ([a-z\d\-]{1,100}~)?([a-z\d][a-z\d\-\.]{0,99}:)?([a-z\d][a-z\d\-]{0,99}
+type PartitionId struct {
+ // The dataset ID.
+ DatasetId *string `protobuf:"bytes,3,opt,name=dataset_id" json:"dataset_id,omitempty"`
+ // The namespace.
+ Namespace *string `protobuf:"bytes,4,opt,name=namespace" json:"namespace,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PartitionId) Reset() { *m = PartitionId{} }
+func (m *PartitionId) String() string { return proto.CompactTextString(m) }
+func (*PartitionId) ProtoMessage() {}
+
+func (m *PartitionId) GetDatasetId() string {
+ if m != nil && m.DatasetId != nil {
+ return *m.DatasetId
+ }
+ return ""
+}
+
+func (m *PartitionId) GetNamespace() string {
+ if m != nil && m.Namespace != nil {
+ return *m.Namespace
+ }
+ return ""
+}
+
+// A unique identifier for an entity.
+// If a key's partition id or any of its path kinds or names are
+// reserved/read-only, the key is reserved/read-only.
+// A reserved/read-only key is forbidden in certain documented contexts.
+type Key struct {
+ // Entities are partitioned into subsets, currently identified by a dataset
+ // (usually implicitly specified by the project) and namespace ID.
+ // Queries are scoped to a single partition.
+ PartitionId *PartitionId `protobuf:"bytes,1,opt,name=partition_id" json:"partition_id,omitempty"`
+ // The entity path.
+ // An entity path consists of one or more elements composed of a kind and a
+ // string or numerical identifier, which identify entities. The first
+ // element identifies a root entity , the second element identifies
+ // a child of the root entity, the third element a child of the
+ // second entity, and so forth. The entities identified by all prefixes of
+ // the path are called the element's ancestors .
+ // An entity path is always fully complete: ALL of the entity's ancestors
+ // are required to be in the path along with the entity identifier itself.
+ // The only exception is that in some documented cases, the identifier in the
+ // last path element (for the entity) itself may be omitted. A path can never
+ // be empty.
+ PathElement []*Key_PathElement `protobuf:"bytes,2,rep,name=path_element" json:"path_element,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Key) Reset() { *m = Key{} }
+func (m *Key) String() string { return proto.CompactTextString(m) }
+func (*Key) ProtoMessage() {}
+
+func (m *Key) GetPartitionId() *PartitionId {
+ if m != nil {
+ return m.PartitionId
+ }
+ return nil
+}
+
+func (m *Key) GetPathElement() []*Key_PathElement {
+ if m != nil {
+ return m.PathElement
+ }
+ return nil
+}
+
+// A (kind, ID/name) pair used to construct a key path.
+//
+// At most one of name or ID may be set.
+// If either is set, the element is complete.
+// If neither is set, the element is incomplete.
+type Key_PathElement struct {
+ // The kind of the entity.
+ // A kind matching regex "__.*__" is reserved/read-only.
+ // A kind must not contain more than 500 characters.
+ // Cannot be "".
+ Kind *string `protobuf:"bytes,1,req,name=kind" json:"kind,omitempty"`
+ // The ID of the entity.
+ // Never equal to zero. Values less than zero are discouraged and will not
+ // be supported in the future.
+ Id *int64 `protobuf:"varint,2,opt,name=id" json:"id,omitempty"`
+ // The name of the entity.
+ // A name matching regex "__.*__" is reserved/read-only.
+ // A name must not be more than 500 characters.
+ // Cannot be "".
+ Name *string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Key_PathElement) Reset() { *m = Key_PathElement{} }
+func (m *Key_PathElement) String() string { return proto.CompactTextString(m) }
+func (*Key_PathElement) ProtoMessage() {}
+
+func (m *Key_PathElement) GetKind() string {
+ if m != nil && m.Kind != nil {
+ return *m.Kind
+ }
+ return ""
+}
+
+func (m *Key_PathElement) GetId() int64 {
+ if m != nil && m.Id != nil {
+ return *m.Id
+ }
+ return 0
+}
+
+func (m *Key_PathElement) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+// A message that can hold any of the supported value types and associated
+// metadata.
+//
+// At most one of the Value fields may be set.
+// If none are set the value is "null".
+//
+type Value struct {
+ // A boolean value.
+ BooleanValue *bool `protobuf:"varint,1,opt,name=boolean_value" json:"boolean_value,omitempty"`
+ // An integer value.
+ IntegerValue *int64 `protobuf:"varint,2,opt,name=integer_value" json:"integer_value,omitempty"`
+ // A double value.
+ DoubleValue *float64 `protobuf:"fixed64,3,opt,name=double_value" json:"double_value,omitempty"`
+ // A timestamp value.
+ TimestampMicrosecondsValue *int64 `protobuf:"varint,4,opt,name=timestamp_microseconds_value" json:"timestamp_microseconds_value,omitempty"`
+ // A key value.
+ KeyValue *Key `protobuf:"bytes,5,opt,name=key_value" json:"key_value,omitempty"`
+ // A blob key value.
+ BlobKeyValue *string `protobuf:"bytes,16,opt,name=blob_key_value" json:"blob_key_value,omitempty"`
+ // A UTF-8 encoded string value.
+ StringValue *string `protobuf:"bytes,17,opt,name=string_value" json:"string_value,omitempty"`
+ // A blob value.
+ BlobValue []byte `protobuf:"bytes,18,opt,name=blob_value" json:"blob_value,omitempty"`
+ // An entity value.
+ // May have no key.
+ // May have a key with an incomplete key path.
+ // May have a reserved/read-only key.
+ EntityValue *Entity `protobuf:"bytes,6,opt,name=entity_value" json:"entity_value,omitempty"`
+ // A list value.
+ // Cannot contain another list value.
+ // Cannot also have a meaning and indexing set.
+ ListValue []*Value `protobuf:"bytes,7,rep,name=list_value" json:"list_value,omitempty"`
+ // The meaning field is reserved and should not be used.
+ Meaning *int32 `protobuf:"varint,14,opt,name=meaning" json:"meaning,omitempty"`
+ // If the value should be indexed.
+ //
+ // The indexed property may be set for a
+ // null value.
+ // When indexed is true, stringValue
+ // is limited to 500 characters and the blob value is limited to 500 bytes.
+ // Exception: If meaning is set to 2, string_value is limited to 2038
+ // characters regardless of indexed.
+ // When indexed is true, meaning 15 and 22 are not allowed, and meaning 16
+ // will be ignored on input (and will never be set on output).
+ // Input values by default have indexed set to
+ // true; however, you can explicitly set indexed to
+ // true if you want. (An output value never has
+ // indexed explicitly set to true.) If a value is
+ // itself an entity, it cannot have indexed set to
+ // true.
+ // Exception: An entity value with meaning 9, 20 or 21 may be indexed.
+ Indexed *bool `protobuf:"varint,15,opt,name=indexed,def=1" json:"indexed,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Value) Reset() { *m = Value{} }
+func (m *Value) String() string { return proto.CompactTextString(m) }
+func (*Value) ProtoMessage() {}
+
+const Default_Value_Indexed bool = true
+
+func (m *Value) GetBooleanValue() bool {
+ if m != nil && m.BooleanValue != nil {
+ return *m.BooleanValue
+ }
+ return false
+}
+
+func (m *Value) GetIntegerValue() int64 {
+ if m != nil && m.IntegerValue != nil {
+ return *m.IntegerValue
+ }
+ return 0
+}
+
+func (m *Value) GetDoubleValue() float64 {
+ if m != nil && m.DoubleValue != nil {
+ return *m.DoubleValue
+ }
+ return 0
+}
+
+func (m *Value) GetTimestampMicrosecondsValue() int64 {
+ if m != nil && m.TimestampMicrosecondsValue != nil {
+ return *m.TimestampMicrosecondsValue
+ }
+ return 0
+}
+
+func (m *Value) GetKeyValue() *Key {
+ if m != nil {
+ return m.KeyValue
+ }
+ return nil
+}
+
+func (m *Value) GetBlobKeyValue() string {
+ if m != nil && m.BlobKeyValue != nil {
+ return *m.BlobKeyValue
+ }
+ return ""
+}
+
+func (m *Value) GetStringValue() string {
+ if m != nil && m.StringValue != nil {
+ return *m.StringValue
+ }
+ return ""
+}
+
+func (m *Value) GetBlobValue() []byte {
+ if m != nil {
+ return m.BlobValue
+ }
+ return nil
+}
+
+func (m *Value) GetEntityValue() *Entity {
+ if m != nil {
+ return m.EntityValue
+ }
+ return nil
+}
+
+func (m *Value) GetListValue() []*Value {
+ if m != nil {
+ return m.ListValue
+ }
+ return nil
+}
+
+func (m *Value) GetMeaning() int32 {
+ if m != nil && m.Meaning != nil {
+ return *m.Meaning
+ }
+ return 0
+}
+
+func (m *Value) GetIndexed() bool {
+ if m != nil && m.Indexed != nil {
+ return *m.Indexed
+ }
+ return Default_Value_Indexed
+}
+
+// An entity property.
+type Property struct {
+ // The name of the property.
+ // A property name matching regex "__.*__" is reserved.
+ // A reserved property name is forbidden in certain documented contexts.
+ // The name must not contain more than 500 characters.
+ // Cannot be "".
+ Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+ // The value(s) of the property.
+ // Each value can have only one value property populated. For example,
+ // you cannot have a values list of { value: { integerValue: 22,
+ // stringValue: "a" } }, but you can have { value: { listValue:
+ // [ { integerValue: 22 }, { stringValue: "a" } ] }.
+ Value *Value `protobuf:"bytes,4,req,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Property) Reset() { *m = Property{} }
+func (m *Property) String() string { return proto.CompactTextString(m) }
+func (*Property) ProtoMessage() {}
+
+func (m *Property) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *Property) GetValue() *Value {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+// An entity.
+//
+// An entity is limited to 1 megabyte when stored. That roughly
+// corresponds to a limit of 1 megabyte for the serialized form of this
+// message.
+type Entity struct {
+ // The entity's key.
+ //
+ // An entity must have a key, unless otherwise documented (for example,
+ // an entity in Value.entityValue may have no key).
+ // An entity's kind is its key's path's last element's kind,
+ // or null if it has no key.
+ Key *Key `protobuf:"bytes,1,opt,name=key" json:"key,omitempty"`
+ // The entity's properties.
+ // Each property's name must be unique for its entity.
+ Property []*Property `protobuf:"bytes,2,rep,name=property" json:"property,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Entity) Reset() { *m = Entity{} }
+func (m *Entity) String() string { return proto.CompactTextString(m) }
+func (*Entity) ProtoMessage() {}
+
+func (m *Entity) GetKey() *Key {
+ if m != nil {
+ return m.Key
+ }
+ return nil
+}
+
+func (m *Entity) GetProperty() []*Property {
+ if m != nil {
+ return m.Property
+ }
+ return nil
+}
+
+// The result of fetching an entity from the datastore.
+type EntityResult struct {
+ // The resulting entity.
+ Entity *Entity `protobuf:"bytes,1,req,name=entity" json:"entity,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *EntityResult) Reset() { *m = EntityResult{} }
+func (m *EntityResult) String() string { return proto.CompactTextString(m) }
+func (*EntityResult) ProtoMessage() {}
+
+func (m *EntityResult) GetEntity() *Entity {
+ if m != nil {
+ return m.Entity
+ }
+ return nil
+}
+
+// A query.
+type Query struct {
+ // The projection to return. If not set the entire entity is returned.
+ Projection []*PropertyExpression `protobuf:"bytes,2,rep,name=projection" json:"projection,omitempty"`
+ // The kinds to query (if empty, returns entities from all kinds).
+ Kind []*KindExpression `protobuf:"bytes,3,rep,name=kind" json:"kind,omitempty"`
+ // The filter to apply (optional).
+ Filter *Filter `protobuf:"bytes,4,opt,name=filter" json:"filter,omitempty"`
+ // The order to apply to the query results (if empty, order is unspecified).
+ Order []*PropertyOrder `protobuf:"bytes,5,rep,name=order" json:"order,omitempty"`
+ // The properties to group by (if empty, no grouping is applied to the
+ // result set).
+ GroupBy []*PropertyReference `protobuf:"bytes,6,rep,name=group_by" json:"group_by,omitempty"`
+ // A starting point for the query results. Optional. Query cursors are
+ // returned in query result batches.
+ StartCursor []byte `protobuf:"bytes,7,opt,name=start_cursor" json:"start_cursor,omitempty"`
+ // An ending point for the query results. Optional. Query cursors are
+ // returned in query result batches.
+ EndCursor []byte `protobuf:"bytes,8,opt,name=end_cursor" json:"end_cursor,omitempty"`
+ // The number of results to skip. Applies before limit, but after all other
+ // constraints (optional, defaults to 0).
+ Offset *int32 `protobuf:"varint,10,opt,name=offset,def=0" json:"offset,omitempty"`
+ // The maximum number of results to return. Applies after all other
+ // constraints. Optional.
+ Limit *int32 `protobuf:"varint,11,opt,name=limit" json:"limit,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Query) Reset() { *m = Query{} }
+func (m *Query) String() string { return proto.CompactTextString(m) }
+func (*Query) ProtoMessage() {}
+
+const Default_Query_Offset int32 = 0
+
+func (m *Query) GetProjection() []*PropertyExpression {
+ if m != nil {
+ return m.Projection
+ }
+ return nil
+}
+
+func (m *Query) GetKind() []*KindExpression {
+ if m != nil {
+ return m.Kind
+ }
+ return nil
+}
+
+func (m *Query) GetFilter() *Filter {
+ if m != nil {
+ return m.Filter
+ }
+ return nil
+}
+
+func (m *Query) GetOrder() []*PropertyOrder {
+ if m != nil {
+ return m.Order
+ }
+ return nil
+}
+
+func (m *Query) GetGroupBy() []*PropertyReference {
+ if m != nil {
+ return m.GroupBy
+ }
+ return nil
+}
+
+func (m *Query) GetStartCursor() []byte {
+ if m != nil {
+ return m.StartCursor
+ }
+ return nil
+}
+
+func (m *Query) GetEndCursor() []byte {
+ if m != nil {
+ return m.EndCursor
+ }
+ return nil
+}
+
+func (m *Query) GetOffset() int32 {
+ if m != nil && m.Offset != nil {
+ return *m.Offset
+ }
+ return Default_Query_Offset
+}
+
+func (m *Query) GetLimit() int32 {
+ if m != nil && m.Limit != nil {
+ return *m.Limit
+ }
+ return 0
+}
+
+// A representation of a kind.
+type KindExpression struct {
+ // The name of the kind.
+ Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *KindExpression) Reset() { *m = KindExpression{} }
+func (m *KindExpression) String() string { return proto.CompactTextString(m) }
+func (*KindExpression) ProtoMessage() {}
+
+func (m *KindExpression) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+// A reference to a property relative to the kind expressions.
+// exactly.
+type PropertyReference struct {
+ // The name of the property.
+ Name *string `protobuf:"bytes,2,req,name=name" json:"name,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PropertyReference) Reset() { *m = PropertyReference{} }
+func (m *PropertyReference) String() string { return proto.CompactTextString(m) }
+func (*PropertyReference) ProtoMessage() {}
+
+func (m *PropertyReference) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+// A representation of a property in a projection.
+type PropertyExpression struct {
+ // The property to project.
+ Property *PropertyReference `protobuf:"bytes,1,req,name=property" json:"property,omitempty"`
+ // The aggregation function to apply to the property. Optional.
+ // Can only be used when grouping by at least one property. Must
+ // then be set on all properties in the projection that are not
+ // being grouped by.
+ AggregationFunction *PropertyExpression_AggregationFunction `protobuf:"varint,2,opt,name=aggregation_function,enum=datastore.PropertyExpression_AggregationFunction" json:"aggregation_function,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PropertyExpression) Reset() { *m = PropertyExpression{} }
+func (m *PropertyExpression) String() string { return proto.CompactTextString(m) }
+func (*PropertyExpression) ProtoMessage() {}
+
+func (m *PropertyExpression) GetProperty() *PropertyReference {
+ if m != nil {
+ return m.Property
+ }
+ return nil
+}
+
+func (m *PropertyExpression) GetAggregationFunction() PropertyExpression_AggregationFunction {
+ if m != nil && m.AggregationFunction != nil {
+ return *m.AggregationFunction
+ }
+ return PropertyExpression_FIRST
+}
+
+// The desired order for a specific property.
+type PropertyOrder struct {
+ // The property to order by.
+ Property *PropertyReference `protobuf:"bytes,1,req,name=property" json:"property,omitempty"`
+ // The direction to order by.
+ Direction *PropertyOrder_Direction `protobuf:"varint,2,opt,name=direction,enum=datastore.PropertyOrder_Direction,def=1" json:"direction,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PropertyOrder) Reset() { *m = PropertyOrder{} }
+func (m *PropertyOrder) String() string { return proto.CompactTextString(m) }
+func (*PropertyOrder) ProtoMessage() {}
+
+const Default_PropertyOrder_Direction PropertyOrder_Direction = PropertyOrder_ASCENDING
+
+func (m *PropertyOrder) GetProperty() *PropertyReference {
+ if m != nil {
+ return m.Property
+ }
+ return nil
+}
+
+func (m *PropertyOrder) GetDirection() PropertyOrder_Direction {
+ if m != nil && m.Direction != nil {
+ return *m.Direction
+ }
+ return Default_PropertyOrder_Direction
+}
+
+// A holder for any type of filter. Exactly one field should be specified.
+type Filter struct {
+ // A composite filter.
+ CompositeFilter *CompositeFilter `protobuf:"bytes,1,opt,name=composite_filter" json:"composite_filter,omitempty"`
+ // A filter on a property.
+ PropertyFilter *PropertyFilter `protobuf:"bytes,2,opt,name=property_filter" json:"property_filter,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Filter) Reset() { *m = Filter{} }
+func (m *Filter) String() string { return proto.CompactTextString(m) }
+func (*Filter) ProtoMessage() {}
+
+func (m *Filter) GetCompositeFilter() *CompositeFilter {
+ if m != nil {
+ return m.CompositeFilter
+ }
+ return nil
+}
+
+func (m *Filter) GetPropertyFilter() *PropertyFilter {
+ if m != nil {
+ return m.PropertyFilter
+ }
+ return nil
+}
+
+// A filter that merges the multiple other filters using the given operation.
+type CompositeFilter struct {
+ // The operator for combining multiple filters.
+ Operator *CompositeFilter_Operator `protobuf:"varint,1,req,name=operator,enum=datastore.CompositeFilter_Operator" json:"operator,omitempty"`
+ // The list of filters to combine.
+ // Must contain at least one filter.
+ Filter []*Filter `protobuf:"bytes,2,rep,name=filter" json:"filter,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *CompositeFilter) Reset() { *m = CompositeFilter{} }
+func (m *CompositeFilter) String() string { return proto.CompactTextString(m) }
+func (*CompositeFilter) ProtoMessage() {}
+
+func (m *CompositeFilter) GetOperator() CompositeFilter_Operator {
+ if m != nil && m.Operator != nil {
+ return *m.Operator
+ }
+ return CompositeFilter_AND
+}
+
+func (m *CompositeFilter) GetFilter() []*Filter {
+ if m != nil {
+ return m.Filter
+ }
+ return nil
+}
+
+// A filter on a specific property.
+type PropertyFilter struct {
+ // The property to filter by.
+ Property *PropertyReference `protobuf:"bytes,1,req,name=property" json:"property,omitempty"`
+ // The operator to filter by.
+ Operator *PropertyFilter_Operator `protobuf:"varint,2,req,name=operator,enum=datastore.PropertyFilter_Operator" json:"operator,omitempty"`
+ // The value to compare the property to.
+ Value *Value `protobuf:"bytes,3,req,name=value" json:"value,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *PropertyFilter) Reset() { *m = PropertyFilter{} }
+func (m *PropertyFilter) String() string { return proto.CompactTextString(m) }
+func (*PropertyFilter) ProtoMessage() {}
+
+func (m *PropertyFilter) GetProperty() *PropertyReference {
+ if m != nil {
+ return m.Property
+ }
+ return nil
+}
+
+func (m *PropertyFilter) GetOperator() PropertyFilter_Operator {
+ if m != nil && m.Operator != nil {
+ return *m.Operator
+ }
+ return PropertyFilter_LESS_THAN
+}
+
+func (m *PropertyFilter) GetValue() *Value {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+// A GQL query.
+type GqlQuery struct {
+ QueryString *string `protobuf:"bytes,1,req,name=query_string" json:"query_string,omitempty"`
+ // When false, the query string must not contain a literal.
+ AllowLiteral *bool `protobuf:"varint,2,opt,name=allow_literal,def=0" json:"allow_literal,omitempty"`
+ // A named argument must set field GqlQueryArg.name.
+ // No two named arguments may have the same name.
+ // For each non-reserved named binding site in the query string,
+ // there must be a named argument with that name,
+ // but not necessarily the inverse.
+ NameArg []*GqlQueryArg `protobuf:"bytes,3,rep,name=name_arg" json:"name_arg,omitempty"`
+ // Numbered binding site @1 references the first numbered argument,
+ // effectively using 1-based indexing, rather than the usual 0.
+ // A numbered argument must NOT set field GqlQueryArg.name.
+ // For each binding site numbered i in query_string,
+ // there must be an ith numbered argument.
+ // The inverse must also be true.
+ NumberArg []*GqlQueryArg `protobuf:"bytes,4,rep,name=number_arg" json:"number_arg,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GqlQuery) Reset() { *m = GqlQuery{} }
+func (m *GqlQuery) String() string { return proto.CompactTextString(m) }
+func (*GqlQuery) ProtoMessage() {}
+
+const Default_GqlQuery_AllowLiteral bool = false
+
+func (m *GqlQuery) GetQueryString() string {
+ if m != nil && m.QueryString != nil {
+ return *m.QueryString
+ }
+ return ""
+}
+
+func (m *GqlQuery) GetAllowLiteral() bool {
+ if m != nil && m.AllowLiteral != nil {
+ return *m.AllowLiteral
+ }
+ return Default_GqlQuery_AllowLiteral
+}
+
+func (m *GqlQuery) GetNameArg() []*GqlQueryArg {
+ if m != nil {
+ return m.NameArg
+ }
+ return nil
+}
+
+func (m *GqlQuery) GetNumberArg() []*GqlQueryArg {
+ if m != nil {
+ return m.NumberArg
+ }
+ return nil
+}
+
+// A binding argument for a GQL query.
+// Exactly one of fields value and cursor must be set.
+type GqlQueryArg struct {
+ // Must match regex "[A-Za-z_$][A-Za-z_$0-9]*".
+ // Must not match regex "__.*__".
+ // Must not be "".
+ Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ Value *Value `protobuf:"bytes,2,opt,name=value" json:"value,omitempty"`
+ Cursor []byte `protobuf:"bytes,3,opt,name=cursor" json:"cursor,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *GqlQueryArg) Reset() { *m = GqlQueryArg{} }
+func (m *GqlQueryArg) String() string { return proto.CompactTextString(m) }
+func (*GqlQueryArg) ProtoMessage() {}
+
+func (m *GqlQueryArg) GetName() string {
+ if m != nil && m.Name != nil {
+ return *m.Name
+ }
+ return ""
+}
+
+func (m *GqlQueryArg) GetValue() *Value {
+ if m != nil {
+ return m.Value
+ }
+ return nil
+}
+
+func (m *GqlQueryArg) GetCursor() []byte {
+ if m != nil {
+ return m.Cursor
+ }
+ return nil
+}
+
+// A batch of results produced by a query.
+type QueryResultBatch struct {
+ // The result type for every entity in entityResults.
+ EntityResultType *EntityResult_ResultType `protobuf:"varint,1,req,name=entity_result_type,enum=datastore.EntityResult_ResultType" json:"entity_result_type,omitempty"`
+ // The results for this batch.
+ EntityResult []*EntityResult `protobuf:"bytes,2,rep,name=entity_result" json:"entity_result,omitempty"`
+ // A cursor that points to the position after the last result in the batch.
+ // May be absent.
+ EndCursor []byte `protobuf:"bytes,4,opt,name=end_cursor" json:"end_cursor,omitempty"`
+ // The state of the query after the current batch.
+ MoreResults *QueryResultBatch_MoreResultsType `protobuf:"varint,5,req,name=more_results,enum=datastore.QueryResultBatch_MoreResultsType" json:"more_results,omitempty"`
+ // The number of results skipped because of Query.offset.
+ SkippedResults *int32 `protobuf:"varint,6,opt,name=skipped_results" json:"skipped_results,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *QueryResultBatch) Reset() { *m = QueryResultBatch{} }
+func (m *QueryResultBatch) String() string { return proto.CompactTextString(m) }
+func (*QueryResultBatch) ProtoMessage() {}
+
+func (m *QueryResultBatch) GetEntityResultType() EntityResult_ResultType {
+ if m != nil && m.EntityResultType != nil {
+ return *m.EntityResultType
+ }
+ return EntityResult_FULL
+}
+
+func (m *QueryResultBatch) GetEntityResult() []*EntityResult {
+ if m != nil {
+ return m.EntityResult
+ }
+ return nil
+}
+
+func (m *QueryResultBatch) GetEndCursor() []byte {
+ if m != nil {
+ return m.EndCursor
+ }
+ return nil
+}
+
+func (m *QueryResultBatch) GetMoreResults() QueryResultBatch_MoreResultsType {
+ if m != nil && m.MoreResults != nil {
+ return *m.MoreResults
+ }
+ return QueryResultBatch_NOT_FINISHED
+}
+
+func (m *QueryResultBatch) GetSkippedResults() int32 {
+ if m != nil && m.SkippedResults != nil {
+ return *m.SkippedResults
+ }
+ return 0
+}
+
+// A set of changes to apply.
+//
+// No entity in this message may have a reserved property name,
+// not even a property in an entity in a value.
+// No value in this message may have meaning 18,
+// not even a value in an entity in another value.
+//
+// If entities with duplicate keys are present, an arbitrary choice will
+// be made as to which is written.
+type Mutation struct {
+ // Entities to upsert.
+ // Each upserted entity's key must have a complete path and
+ // must not be reserved/read-only.
+ Upsert []*Entity `protobuf:"bytes,1,rep,name=upsert" json:"upsert,omitempty"`
+ // Entities to update.
+ // Each updated entity's key must have a complete path and
+ // must not be reserved/read-only.
+ Update []*Entity `protobuf:"bytes,2,rep,name=update" json:"update,omitempty"`
+ // Entities to insert.
+ // Each inserted entity's key must have a complete path and
+ // must not be reserved/read-only.
+ Insert []*Entity `protobuf:"bytes,3,rep,name=insert" json:"insert,omitempty"`
+ // Insert entities with a newly allocated ID.
+ // Each inserted entity's key must omit the final identifier in its path and
+ // must not be reserved/read-only.
+ InsertAutoId []*Entity `protobuf:"bytes,4,rep,name=insert_auto_id" json:"insert_auto_id,omitempty"`
+ // Keys of entities to delete.
+ // Each key must have a complete key path and must not be reserved/read-only.
+ Delete []*Key `protobuf:"bytes,5,rep,name=delete" json:"delete,omitempty"`
+ // Ignore a user specified read-only period. Optional.
+ Force *bool `protobuf:"varint,6,opt,name=force" json:"force,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Mutation) Reset() { *m = Mutation{} }
+func (m *Mutation) String() string { return proto.CompactTextString(m) }
+func (*Mutation) ProtoMessage() {}
+
+func (m *Mutation) GetUpsert() []*Entity {
+ if m != nil {
+ return m.Upsert
+ }
+ return nil
+}
+
+func (m *Mutation) GetUpdate() []*Entity {
+ if m != nil {
+ return m.Update
+ }
+ return nil
+}
+
+func (m *Mutation) GetInsert() []*Entity {
+ if m != nil {
+ return m.Insert
+ }
+ return nil
+}
+
+func (m *Mutation) GetInsertAutoId() []*Entity {
+ if m != nil {
+ return m.InsertAutoId
+ }
+ return nil
+}
+
+func (m *Mutation) GetDelete() []*Key {
+ if m != nil {
+ return m.Delete
+ }
+ return nil
+}
+
+func (m *Mutation) GetForce() bool {
+ if m != nil && m.Force != nil {
+ return *m.Force
+ }
+ return false
+}
+
+// The result of applying a mutation.
+type MutationResult struct {
+ // Number of index writes.
+ IndexUpdates *int32 `protobuf:"varint,1,req,name=index_updates" json:"index_updates,omitempty"`
+ // Keys for insertAutoId entities. One per entity from the
+ // request, in the same order.
+ InsertAutoIdKey []*Key `protobuf:"bytes,2,rep,name=insert_auto_id_key" json:"insert_auto_id_key,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *MutationResult) Reset() { *m = MutationResult{} }
+func (m *MutationResult) String() string { return proto.CompactTextString(m) }
+func (*MutationResult) ProtoMessage() {}
+
+func (m *MutationResult) GetIndexUpdates() int32 {
+ if m != nil && m.IndexUpdates != nil {
+ return *m.IndexUpdates
+ }
+ return 0
+}
+
+func (m *MutationResult) GetInsertAutoIdKey() []*Key {
+ if m != nil {
+ return m.InsertAutoIdKey
+ }
+ return nil
+}
+
+// Options shared by read requests.
+type ReadOptions struct {
+ // The read consistency to use.
+ // Cannot be set when transaction is set.
+ // Lookup and ancestor queries default to STRONG, global queries default to
+ // EVENTUAL and cannot be set to STRONG.
+ ReadConsistency *ReadOptions_ReadConsistency `protobuf:"varint,1,opt,name=read_consistency,enum=datastore.ReadOptions_ReadConsistency,def=0" json:"read_consistency,omitempty"`
+ // The transaction to use. Optional.
+ Transaction []byte `protobuf:"bytes,2,opt,name=transaction" json:"transaction,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ReadOptions) Reset() { *m = ReadOptions{} }
+func (m *ReadOptions) String() string { return proto.CompactTextString(m) }
+func (*ReadOptions) ProtoMessage() {}
+
+const Default_ReadOptions_ReadConsistency ReadOptions_ReadConsistency = ReadOptions_DEFAULT
+
+func (m *ReadOptions) GetReadConsistency() ReadOptions_ReadConsistency {
+ if m != nil && m.ReadConsistency != nil {
+ return *m.ReadConsistency
+ }
+ return Default_ReadOptions_ReadConsistency
+}
+
+func (m *ReadOptions) GetTransaction() []byte {
+ if m != nil {
+ return m.Transaction
+ }
+ return nil
+}
+
+// The request for Lookup.
+type LookupRequest struct {
+ // Options for this lookup request. Optional.
+ ReadOptions *ReadOptions `protobuf:"bytes,1,opt,name=read_options" json:"read_options,omitempty"`
+ // Keys of entities to look up from the datastore.
+ Key []*Key `protobuf:"bytes,3,rep,name=key" json:"key,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *LookupRequest) Reset() { *m = LookupRequest{} }
+func (m *LookupRequest) String() string { return proto.CompactTextString(m) }
+func (*LookupRequest) ProtoMessage() {}
+
+func (m *LookupRequest) GetReadOptions() *ReadOptions {
+ if m != nil {
+ return m.ReadOptions
+ }
+ return nil
+}
+
+func (m *LookupRequest) GetKey() []*Key {
+ if m != nil {
+ return m.Key
+ }
+ return nil
+}
+
+// The response for Lookup.
+type LookupResponse struct {
+ // Entities found as ResultType.FULL entities.
+ Found []*EntityResult `protobuf:"bytes,1,rep,name=found" json:"found,omitempty"`
+ // Entities not found as ResultType.KEY_ONLY entities.
+ Missing []*EntityResult `protobuf:"bytes,2,rep,name=missing" json:"missing,omitempty"`
+ // A list of keys that were not looked up due to resource constraints.
+ Deferred []*Key `protobuf:"bytes,3,rep,name=deferred" json:"deferred,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *LookupResponse) Reset() { *m = LookupResponse{} }
+func (m *LookupResponse) String() string { return proto.CompactTextString(m) }
+func (*LookupResponse) ProtoMessage() {}
+
+func (m *LookupResponse) GetFound() []*EntityResult {
+ if m != nil {
+ return m.Found
+ }
+ return nil
+}
+
+func (m *LookupResponse) GetMissing() []*EntityResult {
+ if m != nil {
+ return m.Missing
+ }
+ return nil
+}
+
+func (m *LookupResponse) GetDeferred() []*Key {
+ if m != nil {
+ return m.Deferred
+ }
+ return nil
+}
+
+// The request for RunQuery.
+type RunQueryRequest struct {
+ // The options for this query.
+ ReadOptions *ReadOptions `protobuf:"bytes,1,opt,name=read_options" json:"read_options,omitempty"`
+ // Entities are partitioned into subsets, identified by a dataset (usually
+ // implicitly specified by the project) and namespace ID. Queries are scoped
+ // to a single partition.
+ // This partition ID is normalized with the standard default context
+ // partition ID, but all other partition IDs in RunQueryRequest are
+ // normalized with this partition ID as the context partition ID.
+ PartitionId *PartitionId `protobuf:"bytes,2,opt,name=partition_id" json:"partition_id,omitempty"`
+ // The query to run.
+ // Either this field or field gql_query must be set, but not both.
+ Query *Query `protobuf:"bytes,3,opt,name=query" json:"query,omitempty"`
+ // The GQL query to run.
+ // Either this field or field query must be set, but not both.
+ GqlQuery *GqlQuery `protobuf:"bytes,7,opt,name=gql_query" json:"gql_query,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RunQueryRequest) Reset() { *m = RunQueryRequest{} }
+func (m *RunQueryRequest) String() string { return proto.CompactTextString(m) }
+func (*RunQueryRequest) ProtoMessage() {}
+
+func (m *RunQueryRequest) GetReadOptions() *ReadOptions {
+ if m != nil {
+ return m.ReadOptions
+ }
+ return nil
+}
+
+func (m *RunQueryRequest) GetPartitionId() *PartitionId {
+ if m != nil {
+ return m.PartitionId
+ }
+ return nil
+}
+
+func (m *RunQueryRequest) GetQuery() *Query {
+ if m != nil {
+ return m.Query
+ }
+ return nil
+}
+
+func (m *RunQueryRequest) GetGqlQuery() *GqlQuery {
+ if m != nil {
+ return m.GqlQuery
+ }
+ return nil
+}
+
+// The response for RunQuery.
+type RunQueryResponse struct {
+ // A batch of query results (always present).
+ Batch *QueryResultBatch `protobuf:"bytes,1,opt,name=batch" json:"batch,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RunQueryResponse) Reset() { *m = RunQueryResponse{} }
+func (m *RunQueryResponse) String() string { return proto.CompactTextString(m) }
+func (*RunQueryResponse) ProtoMessage() {}
+
+func (m *RunQueryResponse) GetBatch() *QueryResultBatch {
+ if m != nil {
+ return m.Batch
+ }
+ return nil
+}
+
+// The request for BeginTransaction.
+type BeginTransactionRequest struct {
+ // The transaction isolation level.
+ IsolationLevel *BeginTransactionRequest_IsolationLevel `protobuf:"varint,1,opt,name=isolation_level,enum=datastore.BeginTransactionRequest_IsolationLevel,def=0" json:"isolation_level,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *BeginTransactionRequest) Reset() { *m = BeginTransactionRequest{} }
+func (m *BeginTransactionRequest) String() string { return proto.CompactTextString(m) }
+func (*BeginTransactionRequest) ProtoMessage() {}
+
+const Default_BeginTransactionRequest_IsolationLevel BeginTransactionRequest_IsolationLevel = BeginTransactionRequest_SNAPSHOT
+
+func (m *BeginTransactionRequest) GetIsolationLevel() BeginTransactionRequest_IsolationLevel {
+ if m != nil && m.IsolationLevel != nil {
+ return *m.IsolationLevel
+ }
+ return Default_BeginTransactionRequest_IsolationLevel
+}
+
+// The response for BeginTransaction.
+type BeginTransactionResponse struct {
+ // The transaction identifier (always present).
+ Transaction []byte `protobuf:"bytes,1,opt,name=transaction" json:"transaction,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *BeginTransactionResponse) Reset() { *m = BeginTransactionResponse{} }
+func (m *BeginTransactionResponse) String() string { return proto.CompactTextString(m) }
+func (*BeginTransactionResponse) ProtoMessage() {}
+
+func (m *BeginTransactionResponse) GetTransaction() []byte {
+ if m != nil {
+ return m.Transaction
+ }
+ return nil
+}
+
+// The request for Rollback.
+type RollbackRequest struct {
+ // The transaction identifier, returned by a call to
+ // beginTransaction.
+ Transaction []byte `protobuf:"bytes,1,req,name=transaction" json:"transaction,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RollbackRequest) Reset() { *m = RollbackRequest{} }
+func (m *RollbackRequest) String() string { return proto.CompactTextString(m) }
+func (*RollbackRequest) ProtoMessage() {}
+
+func (m *RollbackRequest) GetTransaction() []byte {
+ if m != nil {
+ return m.Transaction
+ }
+ return nil
+}
+
+// The response for Rollback.
+type RollbackResponse struct {
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *RollbackResponse) Reset() { *m = RollbackResponse{} }
+func (m *RollbackResponse) String() string { return proto.CompactTextString(m) }
+func (*RollbackResponse) ProtoMessage() {}
+
+// The request for Commit.
+type CommitRequest struct {
+ // The transaction identifier, returned by a call to
+ // beginTransaction. Must be set when mode is TRANSACTIONAL.
+ Transaction []byte `protobuf:"bytes,1,opt,name=transaction" json:"transaction,omitempty"`
+ // The mutation to perform. Optional.
+ Mutation *Mutation `protobuf:"bytes,2,opt,name=mutation" json:"mutation,omitempty"`
+ // The type of commit to perform. Either TRANSACTIONAL or NON_TRANSACTIONAL.
+ Mode *CommitRequest_Mode `protobuf:"varint,5,opt,name=mode,enum=datastore.CommitRequest_Mode,def=1" json:"mode,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *CommitRequest) Reset() { *m = CommitRequest{} }
+func (m *CommitRequest) String() string { return proto.CompactTextString(m) }
+func (*CommitRequest) ProtoMessage() {}
+
+const Default_CommitRequest_Mode CommitRequest_Mode = CommitRequest_TRANSACTIONAL
+
+func (m *CommitRequest) GetTransaction() []byte {
+ if m != nil {
+ return m.Transaction
+ }
+ return nil
+}
+
+func (m *CommitRequest) GetMutation() *Mutation {
+ if m != nil {
+ return m.Mutation
+ }
+ return nil
+}
+
+func (m *CommitRequest) GetMode() CommitRequest_Mode {
+ if m != nil && m.Mode != nil {
+ return *m.Mode
+ }
+ return Default_CommitRequest_Mode
+}
+
+// The response for Commit.
+type CommitResponse struct {
+ // The result of performing the mutation (if any).
+ MutationResult *MutationResult `protobuf:"bytes,1,opt,name=mutation_result" json:"mutation_result,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *CommitResponse) Reset() { *m = CommitResponse{} }
+func (m *CommitResponse) String() string { return proto.CompactTextString(m) }
+func (*CommitResponse) ProtoMessage() {}
+
+func (m *CommitResponse) GetMutationResult() *MutationResult {
+ if m != nil {
+ return m.MutationResult
+ }
+ return nil
+}
+
+// The request for AllocateIds.
+type AllocateIdsRequest struct {
+ // A list of keys with incomplete key paths to allocate IDs for.
+ // No key may be reserved/read-only.
+ Key []*Key `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *AllocateIdsRequest) Reset() { *m = AllocateIdsRequest{} }
+func (m *AllocateIdsRequest) String() string { return proto.CompactTextString(m) }
+func (*AllocateIdsRequest) ProtoMessage() {}
+
+func (m *AllocateIdsRequest) GetKey() []*Key {
+ if m != nil {
+ return m.Key
+ }
+ return nil
+}
+
+// The response for AllocateIds.
+type AllocateIdsResponse struct {
+ // The keys specified in the request (in the same order), each with
+ // its key path completed with a newly allocated ID.
+ Key []*Key `protobuf:"bytes,1,rep,name=key" json:"key,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *AllocateIdsResponse) Reset() { *m = AllocateIdsResponse{} }
+func (m *AllocateIdsResponse) String() string { return proto.CompactTextString(m) }
+func (*AllocateIdsResponse) ProtoMessage() {}
+
+func (m *AllocateIdsResponse) GetKey() []*Key {
+ if m != nil {
+ return m.Key
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("datastore.EntityResult_ResultType", EntityResult_ResultType_name, EntityResult_ResultType_value)
+ proto.RegisterEnum("datastore.PropertyExpression_AggregationFunction", PropertyExpression_AggregationFunction_name, PropertyExpression_AggregationFunction_value)
+ proto.RegisterEnum("datastore.PropertyOrder_Direction", PropertyOrder_Direction_name, PropertyOrder_Direction_value)
+ proto.RegisterEnum("datastore.CompositeFilter_Operator", CompositeFilter_Operator_name, CompositeFilter_Operator_value)
+ proto.RegisterEnum("datastore.PropertyFilter_Operator", PropertyFilter_Operator_name, PropertyFilter_Operator_value)
+ proto.RegisterEnum("datastore.QueryResultBatch_MoreResultsType", QueryResultBatch_MoreResultsType_name, QueryResultBatch_MoreResultsType_value)
+ proto.RegisterEnum("datastore.ReadOptions_ReadConsistency", ReadOptions_ReadConsistency_name, ReadOptions_ReadConsistency_value)
+ proto.RegisterEnum("datastore.BeginTransactionRequest_IsolationLevel", BeginTransactionRequest_IsolationLevel_name, BeginTransactionRequest_IsolationLevel_value)
+ proto.RegisterEnum("datastore.CommitRequest_Mode", CommitRequest_Mode_name, CommitRequest_Mode_value)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto
new file mode 100644
index 000000000..36bb9df7b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/datastore/datastore_v1.proto
@@ -0,0 +1,594 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// The datastore v1 service proto definitions
+
+syntax = "proto2";
+
+package datastore;
+option java_package = "com.google.api.services.datastore";
+
+
+// An identifier for a particular subset of entities.
+//
+// Entities are partitioned into various subsets, each used by different
+// datasets and different namespaces within a dataset and so forth.
+//
+// All input partition IDs are normalized before use.
+// A partition ID is normalized as follows:
+// If the partition ID is unset or is set to an empty partition ID, replace it
+// with the context partition ID.
+// Otherwise, if the partition ID has no dataset ID, assign it the context
+// partition ID's dataset ID.
+// Unless otherwise documented, the context partition ID has the dataset ID set
+// to the context dataset ID and no other partition dimension set.
+//
+// A partition ID is empty if all of its fields are unset.
+//
+// Partition dimension:
+// A dimension may be unset.
+// A dimension's value must never be "".
+// A dimension's value must match [A-Za-z\d\.\-_]{1,100}
+// If the value of any dimension matches regex "__.*__",
+// the partition is reserved/read-only.
+// A reserved/read-only partition ID is forbidden in certain documented contexts.
+//
+// Dataset ID:
+// A dataset id's value must never be "".
+// A dataset id's value must match
+// ([a-z\d\-]{1,100}~)?([a-z\d][a-z\d\-\.]{0,99}:)?([a-z\d][a-z\d\-]{0,99}
+message PartitionId {
+ // The dataset ID.
+ optional string dataset_id = 3;
+ // The namespace.
+ optional string namespace = 4;
+}
+
+// A unique identifier for an entity.
+// If a key's partition id or any of its path kinds or names are
+// reserved/read-only, the key is reserved/read-only.
+// A reserved/read-only key is forbidden in certain documented contexts.
+message Key {
+ // Entities are partitioned into subsets, currently identified by a dataset
+ // (usually implicitly specified by the project) and namespace ID.
+ // Queries are scoped to a single partition.
+ optional PartitionId partition_id = 1;
+
+ // A (kind, ID/name) pair used to construct a key path.
+ //
+ // At most one of name or ID may be set.
+ // If either is set, the element is complete.
+ // If neither is set, the element is incomplete.
+ message PathElement {
+ // The kind of the entity.
+ // A kind matching regex "__.*__" is reserved/read-only.
+ // A kind must not contain more than 500 characters.
+ // Cannot be "".
+ required string kind = 1;
+ // The ID of the entity.
+ // Never equal to zero. Values less than zero are discouraged and will not
+ // be supported in the future.
+ optional int64 id = 2;
+ // The name of the entity.
+ // A name matching regex "__.*__" is reserved/read-only.
+ // A name must not be more than 500 characters.
+ // Cannot be "".
+ optional string name = 3;
+ }
+
+ // The entity path.
+ // An entity path consists of one or more elements composed of a kind and a
+ // string or numerical identifier, which identify entities. The first
+ // element identifies a root entity , the second element identifies
+ // a child of the root entity, the third element a child of the
+ // second entity, and so forth. The entities identified by all prefixes of
+ // the path are called the element's ancestors .
+ // An entity path is always fully complete: ALL of the entity's ancestors
+ // are required to be in the path along with the entity identifier itself.
+ // The only exception is that in some documented cases, the identifier in the
+ // last path element (for the entity) itself may be omitted. A path can never
+ // be empty.
+ repeated PathElement path_element = 2;
+}
+
+// A message that can hold any of the supported value types and associated
+// metadata.
+//
+// At most one of the Value fields may be set.
+// If none are set the value is "null".
+//
+message Value {
+ // A boolean value.
+ optional bool boolean_value = 1;
+ // An integer value.
+ optional int64 integer_value = 2;
+ // A double value.
+ optional double double_value = 3;
+ // A timestamp value.
+ optional int64 timestamp_microseconds_value = 4;
+ // A key value.
+ optional Key key_value = 5;
+ // A blob key value.
+ optional string blob_key_value = 16;
+ // A UTF-8 encoded string value.
+ optional string string_value = 17;
+ // A blob value.
+ optional bytes blob_value = 18;
+ // An entity value.
+ // May have no key.
+ // May have a key with an incomplete key path.
+ // May have a reserved/read-only key.
+ optional Entity entity_value = 6;
+ // A list value.
+ // Cannot contain another list value.
+ // Cannot also have a meaning and indexing set.
+ repeated Value list_value = 7;
+
+ // The meaning field is reserved and should not be used.
+ optional int32 meaning = 14;
+
+ // If the value should be indexed.
+ //
+ // The indexed property may be set for a
+ // null value.
+ // When indexed is true, stringValue
+ // is limited to 500 characters and the blob value is limited to 500 bytes.
+ // Exception: If meaning is set to 2, string_value is limited to 2038
+ // characters regardless of indexed.
+ // When indexed is true, meaning 15 and 22 are not allowed, and meaning 16
+ // will be ignored on input (and will never be set on output).
+ // Input values by default have indexed set to
+ // true; however, you can explicitly set indexed to
+ // true if you want. (An output value never has
+ // indexed explicitly set to true.) If a value is
+ // itself an entity, it cannot have indexed set to
+ // true.
+ // Exception: An entity value with meaning 9, 20 or 21 may be indexed.
+ optional bool indexed = 15 [default = true];
+}
+
+// An entity property.
+message Property {
+ // The name of the property.
+ // A property name matching regex "__.*__" is reserved.
+ // A reserved property name is forbidden in certain documented contexts.
+ // The name must not contain more than 500 characters.
+ // Cannot be "".
+ required string name = 1;
+
+ // The value(s) of the property.
+ // Each value can have only one value property populated. For example,
+ // you cannot have a values list of { value: { integerValue: 22,
+ // stringValue: "a" } }, but you can have { value: { listValue:
+ // [ { integerValue: 22 }, { stringValue: "a" } ] }.
+ required Value value = 4;
+}
+
+// An entity.
+//
+// An entity is limited to 1 megabyte when stored. That roughly
+// corresponds to a limit of 1 megabyte for the serialized form of this
+// message.
+message Entity {
+ // The entity's key.
+ //
+ // An entity must have a key, unless otherwise documented (for example,
+ // an entity in Value.entityValue may have no key).
+ // An entity's kind is its key's path's last element's kind,
+ // or null if it has no key.
+ optional Key key = 1;
+ // The entity's properties.
+ // Each property's name must be unique for its entity.
+ repeated Property property = 2;
+}
+
+// The result of fetching an entity from the datastore.
+message EntityResult {
+ // Specifies what data the 'entity' field contains.
+ // A ResultType is either implied (for example, in LookupResponse.found it
+ // is always FULL) or specified by context (for example, in message
+ // QueryResultBatch, field 'entity_result_type' specifies a ResultType
+ // for all the values in field 'entity_result').
+ enum ResultType {
+ FULL = 1; // The entire entity.
+ PROJECTION = 2; // A projected subset of properties.
+ // The entity may have no key.
+ // A property value may have meaning 18.
+ KEY_ONLY = 3; // Only the key.
+ }
+
+ // The resulting entity.
+ required Entity entity = 1;
+}
+
+// A query.
+message Query {
+ // The projection to return. If not set the entire entity is returned.
+ repeated PropertyExpression projection = 2;
+
+ // The kinds to query (if empty, returns entities from all kinds).
+ repeated KindExpression kind = 3;
+
+ // The filter to apply (optional).
+ optional Filter filter = 4;
+
+ // The order to apply to the query results (if empty, order is unspecified).
+ repeated PropertyOrder order = 5;
+
+ // The properties to group by (if empty, no grouping is applied to the
+ // result set).
+ repeated PropertyReference group_by = 6;
+
+ // A starting point for the query results. Optional. Query cursors are
+ // returned in query result batches.
+ optional bytes /* serialized QueryCursor */ start_cursor = 7;
+
+ // An ending point for the query results. Optional. Query cursors are
+ // returned in query result batches.
+ optional bytes /* serialized QueryCursor */ end_cursor = 8;
+
+ // The number of results to skip. Applies before limit, but after all other
+ // constraints (optional, defaults to 0).
+ optional int32 offset = 10 [default=0];
+
+ // The maximum number of results to return. Applies after all other
+ // constraints. Optional.
+ optional int32 limit = 11;
+}
+
+// A representation of a kind.
+message KindExpression {
+ // The name of the kind.
+ required string name = 1;
+}
+
+// A reference to a property relative to the kind expressions.
+// exactly.
+message PropertyReference {
+ // The name of the property.
+ required string name = 2;
+}
+
+// A representation of a property in a projection.
+message PropertyExpression {
+ enum AggregationFunction {
+ FIRST = 1;
+ }
+ // The property to project.
+ required PropertyReference property = 1;
+ // The aggregation function to apply to the property. Optional.
+ // Can only be used when grouping by at least one property. Must
+ // then be set on all properties in the projection that are not
+ // being grouped by.
+ optional AggregationFunction aggregation_function = 2;
+}
+
+// The desired order for a specific property.
+message PropertyOrder {
+ enum Direction {
+ ASCENDING = 1;
+ DESCENDING = 2;
+ }
+ // The property to order by.
+ required PropertyReference property = 1;
+ // The direction to order by.
+ optional Direction direction = 2 [default=ASCENDING];
+}
+
+// A holder for any type of filter. Exactly one field should be specified.
+message Filter {
+ // A composite filter.
+ optional CompositeFilter composite_filter = 1;
+ // A filter on a property.
+ optional PropertyFilter property_filter = 2;
+}
+
+// A filter that merges the multiple other filters using the given operation.
+message CompositeFilter {
+ enum Operator {
+ AND = 1;
+ }
+
+ // The operator for combining multiple filters.
+ required Operator operator = 1;
+ // The list of filters to combine.
+ // Must contain at least one filter.
+ repeated Filter filter = 2;
+}
+
+// A filter on a specific property.
+message PropertyFilter {
+ enum Operator {
+ LESS_THAN = 1;
+ LESS_THAN_OR_EQUAL = 2;
+ GREATER_THAN = 3;
+ GREATER_THAN_OR_EQUAL = 4;
+ EQUAL = 5;
+
+ HAS_ANCESTOR = 11;
+ }
+
+ // The property to filter by.
+ required PropertyReference property = 1;
+ // The operator to filter by.
+ required Operator operator = 2;
+ // The value to compare the property to.
+ required Value value = 3;
+}
+
+// A GQL query.
+message GqlQuery {
+ required string query_string = 1;
+ // When false, the query string must not contain a literal.
+ optional bool allow_literal = 2 [default = false];
+ // A named argument must set field GqlQueryArg.name.
+ // No two named arguments may have the same name.
+ // For each non-reserved named binding site in the query string,
+ // there must be a named argument with that name,
+ // but not necessarily the inverse.
+ repeated GqlQueryArg name_arg = 3;
+ // Numbered binding site @1 references the first numbered argument,
+ // effectively using 1-based indexing, rather than the usual 0.
+ // A numbered argument must NOT set field GqlQueryArg.name.
+ // For each binding site numbered i in query_string,
+ // there must be an ith numbered argument.
+ // The inverse must also be true.
+ repeated GqlQueryArg number_arg = 4;
+}
+
+// A binding argument for a GQL query.
+// Exactly one of fields value and cursor must be set.
+message GqlQueryArg {
+ // Must match regex "[A-Za-z_$][A-Za-z_$0-9]*".
+ // Must not match regex "__.*__".
+ // Must not be "".
+ optional string name = 1;
+ optional Value value = 2;
+ optional bytes cursor = 3;
+}
+
+// A batch of results produced by a query.
+message QueryResultBatch {
+ // The possible values for the 'more_results' field.
+ enum MoreResultsType {
+ NOT_FINISHED = 1; // There are additional batches to fetch from this query.
+ MORE_RESULTS_AFTER_LIMIT = 2; // The query is finished, but there are more
+ // results after the limit.
+ NO_MORE_RESULTS = 3; // The query has been exhausted.
+ }
+
+ // The result type for every entity in entityResults.
+ required EntityResult.ResultType entity_result_type = 1;
+ // The results for this batch.
+ repeated EntityResult entity_result = 2;
+
+ // A cursor that points to the position after the last result in the batch.
+ // May be absent.
+ optional bytes /* serialized QueryCursor */ end_cursor = 4;
+
+ // The state of the query after the current batch.
+ required MoreResultsType more_results = 5;
+
+ // The number of results skipped because of Query.offset.
+ optional int32 skipped_results = 6;
+}
+
+// A set of changes to apply.
+//
+// No entity in this message may have a reserved property name,
+// not even a property in an entity in a value.
+// No value in this message may have meaning 18,
+// not even a value in an entity in another value.
+//
+// If entities with duplicate keys are present, an arbitrary choice will
+// be made as to which is written.
+message Mutation {
+ // Entities to upsert.
+ // Each upserted entity's key must have a complete path and
+ // must not be reserved/read-only.
+ repeated Entity upsert = 1;
+ // Entities to update.
+ // Each updated entity's key must have a complete path and
+ // must not be reserved/read-only.
+ repeated Entity update = 2;
+ // Entities to insert.
+ // Each inserted entity's key must have a complete path and
+ // must not be reserved/read-only.
+ repeated Entity insert = 3;
+ // Insert entities with a newly allocated ID.
+ // Each inserted entity's key must omit the final identifier in its path and
+ // must not be reserved/read-only.
+ repeated Entity insert_auto_id = 4;
+ // Keys of entities to delete.
+ // Each key must have a complete key path and must not be reserved/read-only.
+ repeated Key delete = 5;
+ // Ignore a user specified read-only period. Optional.
+ optional bool force = 6;
+}
+
+// The result of applying a mutation.
+message MutationResult {
+ // Number of index writes.
+ required int32 index_updates = 1;
+ // Keys for insertAutoId entities. One per entity from the
+ // request, in the same order.
+ repeated Key insert_auto_id_key = 2;
+}
+
+// Options shared by read requests.
+message ReadOptions {
+ enum ReadConsistency {
+ DEFAULT = 0;
+ STRONG = 1;
+ EVENTUAL = 2;
+ }
+
+ // The read consistency to use.
+ // Cannot be set when transaction is set.
+ // Lookup and ancestor queries default to STRONG, global queries default to
+ // EVENTUAL and cannot be set to STRONG.
+ optional ReadConsistency read_consistency = 1 [default=DEFAULT];
+
+ // The transaction to use. Optional.
+ optional bytes /* serialized Transaction */ transaction = 2;
+}
+
+// The request for Lookup.
+message LookupRequest {
+
+ // Options for this lookup request. Optional.
+ optional ReadOptions read_options = 1;
+ // Keys of entities to look up from the datastore.
+ repeated Key key = 3;
+}
+
+// The response for Lookup.
+message LookupResponse {
+
+ // The order of results in these fields is undefined and has no relation to
+ // the order of the keys in the input.
+
+ // Entities found as ResultType.FULL entities.
+ repeated EntityResult found = 1;
+
+ // Entities not found as ResultType.KEY_ONLY entities.
+ repeated EntityResult missing = 2;
+
+ // A list of keys that were not looked up due to resource constraints.
+ repeated Key deferred = 3;
+}
+
+
+// The request for RunQuery.
+message RunQueryRequest {
+
+ // The options for this query.
+ optional ReadOptions read_options = 1;
+
+ // Entities are partitioned into subsets, identified by a dataset (usually
+ // implicitly specified by the project) and namespace ID. Queries are scoped
+ // to a single partition.
+ // This partition ID is normalized with the standard default context
+ // partition ID, but all other partition IDs in RunQueryRequest are
+ // normalized with this partition ID as the context partition ID.
+ optional PartitionId partition_id = 2;
+
+ // The query to run.
+ // Either this field or field gql_query must be set, but not both.
+ optional Query query = 3;
+ // The GQL query to run.
+ // Either this field or field query must be set, but not both.
+ optional GqlQuery gql_query = 7;
+}
+
+// The response for RunQuery.
+message RunQueryResponse {
+
+ // A batch of query results (always present).
+ optional QueryResultBatch batch = 1;
+
+}
+
+// The request for BeginTransaction.
+message BeginTransactionRequest {
+
+ enum IsolationLevel {
+ SNAPSHOT = 0; // Read from a consistent snapshot. Concurrent transactions
+ // conflict if their mutations conflict. For example:
+ // Read(A),Write(B) may not conflict with Read(B),Write(A),
+ // but Read(B),Write(B) does conflict with Read(B),Write(B).
+ SERIALIZABLE = 1; // Read from a consistent snapshot. Concurrent
+ // transactions conflict if they cannot be serialized.
+ // For example Read(A),Write(B) does conflict with
+ // Read(B),Write(A) but Read(A) may not conflict with
+ // Write(A).
+ }
+
+ // The transaction isolation level.
+ optional IsolationLevel isolation_level = 1 [default=SNAPSHOT];
+}
+
+// The response for BeginTransaction.
+message BeginTransactionResponse {
+
+ // The transaction identifier (always present).
+ optional bytes /* serialized Transaction */ transaction = 1;
+}
+
+// The request for Rollback.
+message RollbackRequest {
+
+ // The transaction identifier, returned by a call to
+ // beginTransaction.
+ required bytes /* serialized Transaction */ transaction = 1;
+}
+
+// The response for Rollback.
+message RollbackResponse {
+// Empty
+}
+
+// The request for Commit.
+message CommitRequest {
+
+ enum Mode {
+ TRANSACTIONAL = 1;
+ NON_TRANSACTIONAL = 2;
+ }
+
+ // The transaction identifier, returned by a call to
+ // beginTransaction. Must be set when mode is TRANSACTIONAL.
+ optional bytes /* serialized Transaction */ transaction = 1;
+ // The mutation to perform. Optional.
+ optional Mutation mutation = 2;
+ // The type of commit to perform. Either TRANSACTIONAL or NON_TRANSACTIONAL.
+ optional Mode mode = 5 [default=TRANSACTIONAL];
+}
+
+// The response for Commit.
+message CommitResponse {
+
+ // The result of performing the mutation (if any).
+ optional MutationResult mutation_result = 1;
+}
+
+// The request for AllocateIds.
+message AllocateIdsRequest {
+
+ // A list of keys with incomplete key paths to allocate IDs for.
+ // No key may be reserved/read-only.
+ repeated Key key = 1;
+}
+
+// The response for AllocateIds.
+message AllocateIdsResponse {
+
+ // The keys specified in the request (in the same order), each with
+ // its key path completed with a newly allocated ID.
+ repeated Key key = 1;
+}
+
+// Each rpc normalizes the partition IDs of the keys in its input entities,
+// and always returns entities with keys with normalized partition IDs.
+// (Note that applies to all entities, including entities in values.)
+service DatastoreService {
+ // Look up some entities by key.
+ rpc Lookup(LookupRequest) returns (LookupResponse) {
+ };
+ // Query for entities.
+ rpc RunQuery(RunQueryRequest) returns (RunQueryResponse) {
+ };
+ // Begin a new transaction.
+ rpc BeginTransaction(BeginTransactionRequest) returns (BeginTransactionResponse) {
+ };
+ // Commit a transaction, optionally creating, deleting or modifying some
+ // entities.
+ rpc Commit(CommitRequest) returns (CommitResponse) {
+ };
+ // Roll back a transaction.
+ rpc Rollback(RollbackRequest) returns (RollbackResponse) {
+ };
+ // Allocate IDs for incomplete keys (useful for referencing an entity before
+ // it is inserted).
+ rpc AllocateIds(AllocateIdsRequest) returns (AllocateIdsResponse) {
+ };
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go
new file mode 100644
index 000000000..53f0d17d7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/cloud/internal/testutil/context.go
@@ -0,0 +1,57 @@
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// 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 testutil contains helper functions for writing tests.
+package testutil
+
+import (
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google"
+ "google.golang.org/cloud"
+)
+
+const (
+ envProjID = "GCLOUD_TESTS_GOLANG_PROJECT_ID"
+ envPrivateKey = "GCLOUD_TESTS_GOLANG_KEY"
+)
+
+func Context(scopes ...string) context.Context {
+ key, projID := os.Getenv(envPrivateKey), os.Getenv(envProjID)
+ if key == "" || projID == "" {
+ log.Fatal("GCLOUD_TESTS_GOLANG_KEY and GCLOUD_TESTS_GOLANG_PROJECT_ID must be set. See CONTRIBUTING.md for details.")
+ }
+ jsonKey, err := ioutil.ReadFile(key)
+ if err != nil {
+ log.Fatalf("Cannot read the JSON key file, err: %v", err)
+ }
+ conf, err := google.JWTConfigFromJSON(jsonKey, scopes...)
+ if err != nil {
+ log.Fatal(err)
+ }
+ return cloud.NewContext(projID, conf.Client(oauth2.NoContext))
+}
+
+func NoAuthContext() context.Context {
+ projID := os.Getenv(envProjID)
+ if projID == "" {
+ log.Fatal("GCLOUD_TESTS_GOLANG_PROJECT_ID must be set. See CONTRIBUTING.md for details.")
+ }
+ return cloud.NewContext(projID, &http.Client{Transport: http.DefaultTransport})
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/CONTRIBUTING.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/CONTRIBUTING.md
new file mode 100644
index 000000000..b6decb6d8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/CONTRIBUTING.md
@@ -0,0 +1,27 @@
+# How to contribute
+
+We definitely welcome patches and contribution to grpc! Here is some guideline
+and information about how to do so.
+
+## Getting started
+
+### Legal requirements
+
+In order to protect both you and ourselves, you will need to sign the
+[Contributor License Agreement](https://cla.developers.google.com/clas).
+
+### Filing Issues
+When filing an issue, make sure to answer these five questions:
+
+1. What version of Go are you using (`go version`)?
+2. What operating system and processor architecture are you using?
+3. What did you do?
+4. What did you expect to see?
+5. What did you see instead?
+
+### Contributing code
+Please read the Contribution Guidelines before sending patches.
+
+We will not accept GitHub pull requests once Gerrit is setup (we will use Gerrit instead for code review).
+
+Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file.
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/LICENSE b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/LICENSE
new file mode 100644
index 000000000..f4988b450
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/LICENSE
@@ -0,0 +1,28 @@
+Copyright 2014, Google Inc.
+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/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/PATENTS b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/PATENTS
new file mode 100644
index 000000000..619f9dbfe
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of the GRPC project.
+
+Google 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,
+transfer and otherwise run, modify and propagate the contents of this
+implementation of GRPC, where such license applies only to those patent
+claims, both currently owned or controlled by Google and acquired in
+the future, licensable by Google that are necessarily infringed by this
+implementation of GRPC. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute or
+order or agree to the institution of patent litigation against any
+entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that this implementation of GRPC or any code incorporated within this
+implementation of GRPC constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation of GRPC
+shall terminate as of the date such litigation is filed.
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/README.md
new file mode 100644
index 000000000..caa40261f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/README.md
@@ -0,0 +1,23 @@
+#gRPC-Go
+
+[](https://travis-ci.org/grpc/grpc-go) [](https://godoc.org/google.golang.org/grpc)
+
+The Go implementation of [gRPC](https://github.com/grpc/grpc)
+
+Installation
+------------
+
+To install this package, you need to install Go 1.4 and setup your Go workspace on your computer. The simplest way to install the library is to run:
+
+```
+$ go get google.golang.org/grpc
+```
+
+Documentation
+-------------
+You can find more detailed documentation and examples in the [grpc-common repository](http://github.com/grpc/grpc-common).
+
+Status
+------
+Alpha - ready for early adopters.
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark.go
new file mode 100644
index 000000000..d3677428a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark.go
@@ -0,0 +1,203 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+/*
+Package benchmark implements the building blocks to setup end-to-end gRPC benchmarks.
+*/
+package benchmark
+
+import (
+ "io"
+ "math"
+ "net"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing"
+)
+
+func newPayload(t testpb.PayloadType, size int) *testpb.Payload {
+ if size < 0 {
+ grpclog.Fatalf("Requested a response with invalid length %d", size)
+ }
+ body := make([]byte, size)
+ switch t {
+ case testpb.PayloadType_COMPRESSABLE:
+ case testpb.PayloadType_UNCOMPRESSABLE:
+ grpclog.Fatalf("PayloadType UNCOMPRESSABLE is not supported")
+ default:
+ grpclog.Fatalf("Unsupported payload type: %d", t)
+ }
+ return &testpb.Payload{
+ Type: t.Enum(),
+ Body: body,
+ }
+}
+
+type testServer struct {
+}
+
+func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
+ return new(testpb.Empty), nil
+}
+
+func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
+ return &testpb.SimpleResponse{
+ Payload: newPayload(in.GetResponseType(), int(in.GetResponseSize())),
+ }, nil
+}
+
+func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testpb.TestService_StreamingOutputCallServer) error {
+ cs := args.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: newPayload(args.GetResponseType(), int(c.GetSize())),
+ }); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *testServer) StreamingInputCall(stream testpb.TestService_StreamingInputCallServer) error {
+ var sum int
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ return stream.SendAndClose(&testpb.StreamingInputCallResponse{
+ AggregatedPayloadSize: proto.Int32(int32(sum)),
+ })
+ }
+ if err != nil {
+ return err
+ }
+ p := in.GetPayload().GetBody()
+ sum += len(p)
+ }
+}
+
+func (s *testServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error {
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ cs := in.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: newPayload(in.GetResponseType(), int(c.GetSize())),
+ }); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServer) error {
+ var msgBuf []*testpb.StreamingOutputCallRequest
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ break
+ }
+ if err != nil {
+ return err
+ }
+ msgBuf = append(msgBuf, in)
+ }
+ for _, m := range msgBuf {
+ cs := m.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: newPayload(m.GetResponseType(), int(c.GetSize())),
+ }); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// StartServer starts a gRPC server serving a benchmark service. It returns its
+// listen address and a function to stop the server.
+func StartServer() (string, func()) {
+ lis, err := net.Listen("tcp", ":0")
+ if err != nil {
+ grpclog.Fatalf("Failed to listen: %v", err)
+ }
+ s := grpc.NewServer(grpc.MaxConcurrentStreams(math.MaxUint32))
+ testpb.RegisterTestServiceServer(s, &testServer{})
+ go s.Serve(lis)
+ return lis.Addr().String(), func() {
+ s.Stop()
+ }
+}
+
+// DoUnaryCall performs an unary RPC with given stub and request and response sizes.
+func DoUnaryCall(tc testpb.TestServiceClient, reqSize, respSize int) {
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, reqSize)
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: pl,
+ }
+ if _, err := tc.UnaryCall(context.Background(), req); err != nil {
+ grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
+ }
+}
+
+// NewClientConn creates a gRPC client connection to addr.
+func NewClientConn(addr string) *grpc.ClientConn {
+ conn, err := grpc.Dial(addr)
+ if err != nil {
+ grpclog.Fatalf("NewClientConn(%q) failed to create a ClientConn %v", addr, err)
+ }
+ return conn
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark_test.go
new file mode 100644
index 000000000..d79f8ce2c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/benchmark_test.go
@@ -0,0 +1,79 @@
+package benchmark
+
+import (
+ "os"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats"
+ testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing"
+)
+
+func run(b *testing.B, maxConcurrentCalls int, caller func(testpb.TestServiceClient)) {
+ s := stats.AddStats(b, 38)
+ b.StopTimer()
+ target, stopper := StartServer()
+ defer stopper()
+ conn := NewClientConn(target)
+ tc := testpb.NewTestServiceClient(conn)
+
+ // Warm up connection.
+ for i := 0; i < 10; i++ {
+ caller(tc)
+ }
+
+ ch := make(chan int, maxConcurrentCalls*4)
+ var (
+ mu sync.Mutex
+ wg sync.WaitGroup
+ )
+ wg.Add(maxConcurrentCalls)
+
+ // Distribute the b.N calls over maxConcurrentCalls workers.
+ for i := 0; i < maxConcurrentCalls; i++ {
+ go func() {
+ for _ = range ch {
+ start := time.Now()
+ caller(tc)
+ elapse := time.Since(start)
+ mu.Lock()
+ s.Add(elapse)
+ mu.Unlock()
+ }
+ wg.Done()
+ }()
+ }
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ ch <- i
+ }
+ b.StopTimer()
+ close(ch)
+ wg.Wait()
+ conn.Close()
+}
+
+func smallCaller(client testpb.TestServiceClient) {
+ DoUnaryCall(client, 1, 1)
+}
+
+func BenchmarkClientSmallc1(b *testing.B) {
+ run(b, 1, smallCaller)
+}
+
+func BenchmarkClientSmallc8(b *testing.B) {
+ run(b, 8, smallCaller)
+}
+
+func BenchmarkClientSmallc64(b *testing.B) {
+ run(b, 64, smallCaller)
+}
+
+func BenchmarkClientSmallc512(b *testing.B) {
+ run(b, 512, smallCaller)
+}
+
+func TestMain(m *testing.M) {
+ os.Exit(stats.RunTestMain(m))
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/client/main.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/client/main.go
new file mode 100644
index 000000000..a94c4501b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/client/main.go
@@ -0,0 +1,89 @@
+package main
+
+import (
+ "flag"
+ "math"
+ "net"
+ "net/http"
+ _ "net/http/pprof"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing"
+)
+
+var (
+ server = flag.String("server", "", "The server address")
+ maxConcurrentRPCs = flag.Int("max_concurrent_rpcs", 1, "The max number of concurrent RPCs")
+ duration = flag.Int("duration", math.MaxInt32, "The duration in seconds to run the benchmark client")
+)
+
+func caller(client testpb.TestServiceClient) {
+ benchmark.DoUnaryCall(client, 1, 1)
+}
+
+func closeLoop() {
+ s := stats.NewStats(256)
+ conn := benchmark.NewClientConn(*server)
+ tc := testpb.NewTestServiceClient(conn)
+ // Warm up connection.
+ for i := 0; i < 100; i++ {
+ caller(tc)
+ }
+ ch := make(chan int, *maxConcurrentRPCs*4)
+ var (
+ mu sync.Mutex
+ wg sync.WaitGroup
+ )
+ wg.Add(*maxConcurrentRPCs)
+ // Distribute RPCs over maxConcurrentCalls workers.
+ for i := 0; i < *maxConcurrentRPCs; i++ {
+ go func() {
+ for _ = range ch {
+ start := time.Now()
+ caller(tc)
+ elapse := time.Since(start)
+ mu.Lock()
+ s.Add(elapse)
+ mu.Unlock()
+ }
+ wg.Done()
+ }()
+ }
+ // Stop the client when time is up.
+ done := make(chan struct{})
+ go func() {
+ <-time.After(time.Duration(*duration) * time.Second)
+ close(done)
+ }()
+ ok := true
+ for ok {
+ select {
+ case ch <- 0:
+ case <-done:
+ ok = false
+ }
+ }
+ close(ch)
+ wg.Wait()
+ conn.Close()
+ grpclog.Println(s.String())
+}
+
+func main() {
+ flag.Parse()
+ go func() {
+ lis, err := net.Listen("tcp", ":0")
+ if err != nil {
+ grpclog.Fatalf("Failed to listen: %v", err)
+ }
+ grpclog.Println("Client profiling address: ", lis.Addr().String())
+ if err := http.Serve(lis, nil); err != nil {
+ grpclog.Fatalf("Failed to serve: %v", err)
+ }
+ }()
+ closeLoop()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go
new file mode 100644
index 000000000..754de67f9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.pb.go
@@ -0,0 +1,702 @@
+// Code generated by protoc-gen-go.
+// source: src/google.golang.org/grpc/test/grpc_testing/test.proto
+// DO NOT EDIT!
+
+/*
+Package grpc_testing is a generated protocol buffer package.
+
+It is generated from these files:
+ src/google.golang.org/grpc/test/grpc_testing/test.proto
+
+It has these top-level messages:
+ Empty
+ Payload
+ SimpleRequest
+ SimpleResponse
+ StreamingInputCallRequest
+ StreamingInputCallResponse
+ ResponseParameters
+ StreamingOutputCallRequest
+ StreamingOutputCallResponse
+*/
+package grpc_testing
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+import (
+ context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+// The type of payload that should be returned.
+type PayloadType int32
+
+const (
+ // Compressable text format.
+ PayloadType_COMPRESSABLE PayloadType = 0
+ // Uncompressable binary format.
+ PayloadType_UNCOMPRESSABLE PayloadType = 1
+ // Randomly chosen from all other formats defined in this enum.
+ PayloadType_RANDOM PayloadType = 2
+)
+
+var PayloadType_name = map[int32]string{
+ 0: "COMPRESSABLE",
+ 1: "UNCOMPRESSABLE",
+ 2: "RANDOM",
+}
+var PayloadType_value = map[string]int32{
+ "COMPRESSABLE": 0,
+ "UNCOMPRESSABLE": 1,
+ "RANDOM": 2,
+}
+
+func (x PayloadType) Enum() *PayloadType {
+ p := new(PayloadType)
+ *p = x
+ return p
+}
+func (x PayloadType) String() string {
+ return proto.EnumName(PayloadType_name, int32(x))
+}
+func (x *PayloadType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PayloadType_value, data, "PayloadType")
+ if err != nil {
+ return err
+ }
+ *x = PayloadType(value)
+ return nil
+}
+
+type Empty struct {
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+
+// A block of data, to simply increase gRPC message size.
+type Payload struct {
+ // The type of data in body.
+ Type *PayloadType `protobuf:"varint,1,opt,name=type,enum=grpc.testing.PayloadType" json:"type,omitempty"`
+ // Primary contents of payload.
+ Body []byte `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Payload) Reset() { *m = Payload{} }
+func (m *Payload) String() string { return proto.CompactTextString(m) }
+func (*Payload) ProtoMessage() {}
+
+func (m *Payload) GetType() PayloadType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *Payload) GetBody() []byte {
+ if m != nil {
+ return m.Body
+ }
+ return nil
+}
+
+// Unary request.
+type SimpleRequest struct {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ ResponseSize *int32 `protobuf:"varint,2,opt,name=response_size" json:"response_size,omitempty"`
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
+ // Whether SimpleResponse should include username.
+ FillUsername *bool `protobuf:"varint,4,opt,name=fill_username" json:"fill_username,omitempty"`
+ // Whether SimpleResponse should include OAuth scope.
+ FillOauthScope *bool `protobuf:"varint,5,opt,name=fill_oauth_scope" json:"fill_oauth_scope,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
+func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
+func (*SimpleRequest) ProtoMessage() {}
+
+func (m *SimpleRequest) GetResponseType() PayloadType {
+ if m != nil && m.ResponseType != nil {
+ return *m.ResponseType
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *SimpleRequest) GetResponseSize() int32 {
+ if m != nil && m.ResponseSize != nil {
+ return *m.ResponseSize
+ }
+ return 0
+}
+
+func (m *SimpleRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (m *SimpleRequest) GetFillUsername() bool {
+ if m != nil && m.FillUsername != nil {
+ return *m.FillUsername
+ }
+ return false
+}
+
+func (m *SimpleRequest) GetFillOauthScope() bool {
+ if m != nil && m.FillOauthScope != nil {
+ return *m.FillOauthScope
+ }
+ return false
+}
+
+// Unary response, as configured by the request.
+type SimpleResponse struct {
+ // Payload to increase message size.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ Username *string `protobuf:"bytes,2,opt,name=username" json:"username,omitempty"`
+ // OAuth scope.
+ OauthScope *string `protobuf:"bytes,3,opt,name=oauth_scope" json:"oauth_scope,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
+func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
+func (*SimpleResponse) ProtoMessage() {}
+
+func (m *SimpleResponse) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (m *SimpleResponse) GetUsername() string {
+ if m != nil && m.Username != nil {
+ return *m.Username
+ }
+ return ""
+}
+
+func (m *SimpleResponse) GetOauthScope() string {
+ if m != nil && m.OauthScope != nil {
+ return *m.OauthScope
+ }
+ return ""
+}
+
+// Client-streaming request.
+type StreamingInputCallRequest struct {
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
+func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallRequest) ProtoMessage() {}
+
+func (m *StreamingInputCallRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+// Client-streaming response.
+type StreamingInputCallResponse struct {
+ // Aggregated size of payloads received from the client.
+ AggregatedPayloadSize *int32 `protobuf:"varint,1,opt,name=aggregated_payload_size" json:"aggregated_payload_size,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
+func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallResponse) ProtoMessage() {}
+
+func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
+ if m != nil && m.AggregatedPayloadSize != nil {
+ return *m.AggregatedPayloadSize
+ }
+ return 0
+}
+
+// Configuration for a particular response.
+type ResponseParameters struct {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ Size *int32 `protobuf:"varint,1,opt,name=size" json:"size,omitempty"`
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ IntervalUs *int32 `protobuf:"varint,2,opt,name=interval_us" json:"interval_us,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
+func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
+func (*ResponseParameters) ProtoMessage() {}
+
+func (m *ResponseParameters) GetSize() int32 {
+ if m != nil && m.Size != nil {
+ return *m.Size
+ }
+ return 0
+}
+
+func (m *ResponseParameters) GetIntervalUs() int32 {
+ if m != nil && m.IntervalUs != nil {
+ return *m.IntervalUs
+ }
+ return 0
+}
+
+// Server-streaming request.
+type StreamingOutputCallRequest struct {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
+ // Configuration for each expected response message.
+ ResponseParameters []*ResponseParameters `protobuf:"bytes,2,rep,name=response_parameters" json:"response_parameters,omitempty"`
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
+func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallRequest) ProtoMessage() {}
+
+func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
+ if m != nil && m.ResponseType != nil {
+ return *m.ResponseType
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *StreamingOutputCallRequest) GetResponseParameters() []*ResponseParameters {
+ if m != nil {
+ return m.ResponseParameters
+ }
+ return nil
+}
+
+func (m *StreamingOutputCallRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+// Server-streaming response, as configured by the request and parameters.
+type StreamingOutputCallResponse struct {
+ // Payload to increase response size.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
+func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallResponse) ProtoMessage() {}
+
+func (m *StreamingOutputCallResponse) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
+}
+
+// Client API for TestService service
+
+type TestServiceClient interface {
+ // One empty request followed by one empty response.
+ EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)
+}
+
+type testServiceClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
+ return &testServiceClient{cc}
+}
+
+func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
+ out := new(Empty)
+ err := grpc.Invoke(ctx, "/grpc.testing.TestService/EmptyCall", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
+ out := new(SimpleResponse)
+ err := grpc.Invoke(ctx, "/grpc.testing.TestService/UnaryCall", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/grpc.testing.TestService/StreamingOutputCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceStreamingOutputCallClient{stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type TestService_StreamingOutputCallClient interface {
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceStreamingOutputCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[1], c.cc, "/grpc.testing.TestService/StreamingInputCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceStreamingInputCallClient{stream}
+ return x, nil
+}
+
+type TestService_StreamingInputCallClient interface {
+ Send(*StreamingInputCallRequest) error
+ CloseAndRecv() (*StreamingInputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceStreamingInputCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(StreamingInputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[2], c.cc, "/grpc.testing.TestService/FullDuplexCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceFullDuplexCallClient{stream}
+ return x, nil
+}
+
+type TestService_FullDuplexCallClient interface {
+ Send(*StreamingOutputCallRequest) error
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceFullDuplexCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[3], c.cc, "/grpc.testing.TestService/HalfDuplexCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceHalfDuplexCallClient{stream}
+ return x, nil
+}
+
+type TestService_HalfDuplexCallClient interface {
+ Send(*StreamingOutputCallRequest) error
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceHalfDuplexCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// Server API for TestService service
+
+type TestServiceServer interface {
+ // One empty request followed by one empty response.
+ EmptyCall(context.Context, *Empty) (*Empty, error)
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ StreamingInputCall(TestService_StreamingInputCallServer) error
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ FullDuplexCall(TestService_FullDuplexCallServer) error
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ HalfDuplexCall(TestService_HalfDuplexCallServer) error
+}
+
+func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
+ s.RegisterService(&_TestService_serviceDesc, srv)
+}
+
+func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(Empty)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(TestServiceServer).EmptyCall(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(SimpleRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(TestServiceServer).UnaryCall(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(StreamingOutputCallRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})
+}
+
+type TestService_StreamingOutputCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ grpc.ServerStream
+}
+
+type testServiceStreamingOutputCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})
+}
+
+type TestService_StreamingInputCallServer interface {
+ SendAndClose(*StreamingInputCallResponse) error
+ Recv() (*StreamingInputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceStreamingInputCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {
+ m := new(StreamingInputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})
+}
+
+type TestService_FullDuplexCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ Recv() (*StreamingOutputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceFullDuplexCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
+ m := new(StreamingOutputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})
+}
+
+type TestService_HalfDuplexCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ Recv() (*StreamingOutputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceHalfDuplexCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
+ m := new(StreamingOutputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+var _TestService_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "grpc.testing.TestService",
+ HandlerType: (*TestServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "EmptyCall",
+ Handler: _TestService_EmptyCall_Handler,
+ },
+ {
+ MethodName: "UnaryCall",
+ Handler: _TestService_UnaryCall_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "StreamingOutputCall",
+ Handler: _TestService_StreamingOutputCall_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "StreamingInputCall",
+ Handler: _TestService_StreamingInputCall_Handler,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "FullDuplexCall",
+ Handler: _TestService_FullDuplexCall_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "HalfDuplexCall",
+ Handler: _TestService_HalfDuplexCall_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto
new file mode 100644
index 000000000..b5bfe0537
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/grpc_testing/test.proto
@@ -0,0 +1,140 @@
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto2";
+
+package grpc.testing;
+
+message Empty {}
+
+// The type of payload that should be returned.
+enum PayloadType {
+ // Compressable text format.
+ COMPRESSABLE = 0;
+
+ // Uncompressable binary format.
+ UNCOMPRESSABLE = 1;
+
+ // Randomly chosen from all other formats defined in this enum.
+ RANDOM = 2;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+ // The type of data in body.
+ optional PayloadType type = 1;
+ // Primary contents of payload.
+ optional bytes body = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ optional PayloadType response_type = 1;
+
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 response_size = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+
+ // Whether SimpleResponse should include username.
+ optional bool fill_username = 4;
+
+ // Whether SimpleResponse should include OAuth scope.
+ optional bool fill_oauth_scope = 5;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+ // Payload to increase message size.
+ optional Payload payload = 1;
+
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ optional string username = 2;
+
+ // OAuth scope.
+ optional string oauth_scope = 3;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+ // Optional input payload sent along with the request.
+ optional Payload payload = 1;
+
+ // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+ // Aggregated size of payloads received from the client.
+ optional int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 size = 1;
+
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ optional int32 interval_us = 2;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ optional PayloadType response_type = 1;
+
+ // Configuration for each expected response message.
+ repeated ResponseParameters response_parameters = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+ // Payload to increase response size.
+ optional Payload payload = 1;
+}
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+ // One empty request followed by one empty response.
+ rpc EmptyCall(Empty) returns (Empty);
+
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ rpc StreamingOutputCall(StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ rpc StreamingInputCall(stream StreamingInputCallRequest)
+ returns (StreamingInputCallResponse);
+
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ rpc FullDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/server/main.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/server/main.go
new file mode 100644
index 000000000..142a38924
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/server/main.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+ "flag"
+ "math"
+ "net"
+ "net/http"
+ _ "net/http/pprof"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+)
+
+var (
+ duration = flag.Int("duration", math.MaxInt32, "The duration in seconds to run the benchmark server")
+)
+
+func main() {
+ flag.Parse()
+ go func() {
+ lis, err := net.Listen("tcp", ":0")
+ if err != nil {
+ grpclog.Fatalf("Failed to listen: %v", err)
+ }
+ grpclog.Println("Server profiling address: ", lis.Addr().String())
+ if err := http.Serve(lis, nil); err != nil {
+ grpclog.Fatalf("Failed to serve: %v", err)
+ }
+ }()
+ addr, stopper := benchmark.StartServer()
+ grpclog.Println("Server Address: ", addr)
+ <-time.After(time.Duration(*duration) * time.Second)
+ stopper()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/counter.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/counter.go
new file mode 100644
index 000000000..4389bae38
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/counter.go
@@ -0,0 +1,135 @@
+package stats
+
+import (
+ "sync"
+ "time"
+)
+
+var (
+ // TimeNow is used for testing.
+ TimeNow = time.Now
+)
+
+const (
+ hour = 0
+ tenminutes = 1
+ minute = 2
+)
+
+// Counter is a counter that keeps track of its recent values over a given
+// period of time, and with a given resolution. Use newCounter() to instantiate.
+type Counter struct {
+ mu sync.RWMutex
+ ts [3]*timeseries
+ lastUpdate time.Time
+}
+
+// newCounter returns a new Counter.
+func newCounter() *Counter {
+ now := TimeNow()
+ c := &Counter{}
+ c.ts[hour] = newTimeSeries(now, time.Hour, time.Minute)
+ c.ts[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
+ c.ts[minute] = newTimeSeries(now, time.Minute, time.Second)
+ return c
+}
+
+func (c *Counter) advance() time.Time {
+ now := TimeNow()
+ for _, ts := range c.ts {
+ ts.advanceTime(now)
+ }
+ return now
+}
+
+// Value returns the current value of the counter.
+func (c *Counter) Value() int64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.ts[minute].headValue()
+}
+
+// LastUpdate returns the last update time of the counter.
+func (c *Counter) LastUpdate() time.Time {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ return c.lastUpdate
+}
+
+// Set updates the current value of the counter.
+func (c *Counter) Set(value int64) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.lastUpdate = c.advance()
+ for _, ts := range c.ts {
+ ts.set(value)
+ }
+}
+
+// Incr increments the current value of the counter by 'delta'.
+func (c *Counter) Incr(delta int64) {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ c.lastUpdate = c.advance()
+ for _, ts := range c.ts {
+ ts.incr(delta)
+ }
+}
+
+// Delta1h returns the delta for the last hour.
+func (c *Counter) Delta1h() int64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ c.advance()
+ return c.ts[hour].delta()
+}
+
+// Delta10m returns the delta for the last 10 minutes.
+func (c *Counter) Delta10m() int64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ c.advance()
+ return c.ts[tenminutes].delta()
+}
+
+// Delta1m returns the delta for the last minute.
+func (c *Counter) Delta1m() int64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ c.advance()
+ return c.ts[minute].delta()
+}
+
+// Rate1h returns the rate of change of the counter in the last hour.
+func (c *Counter) Rate1h() float64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ c.advance()
+ return c.ts[hour].rate()
+}
+
+// Rate10m returns the rate of change of the counter in the last 10 minutes.
+func (c *Counter) Rate10m() float64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ c.advance()
+ return c.ts[tenminutes].rate()
+}
+
+// Rate1m returns the rate of change of the counter in the last minute.
+func (c *Counter) Rate1m() float64 {
+ c.mu.RLock()
+ defer c.mu.RUnlock()
+ c.advance()
+ return c.ts[minute].rate()
+}
+
+// Reset resets the counter to an empty state.
+func (c *Counter) Reset() {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ now := TimeNow()
+ for _, ts := range c.ts {
+ ts.reset(now)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/histogram.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/histogram.go
new file mode 100644
index 000000000..303811820
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/histogram.go
@@ -0,0 +1,255 @@
+package stats
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// HistogramValue is the value of Histogram objects.
+type HistogramValue struct {
+ // Count is the total number of values added to the histogram.
+ Count int64
+ // Sum is the sum of all the values added to the histogram.
+ Sum int64
+ // Min is the minimum of all the values added to the histogram.
+ Min int64
+ // Max is the maximum of all the values added to the histogram.
+ Max int64
+ // Buckets contains all the buckets of the histogram.
+ Buckets []HistogramBucket
+}
+
+// HistogramBucket is one histogram bucket.
+type HistogramBucket struct {
+ // LowBound is the lower bound of the bucket.
+ LowBound int64
+ // Count is the number of values in the bucket.
+ Count int64
+}
+
+// Print writes textual output of the histogram values.
+func (v HistogramValue) Print(w io.Writer) {
+ avg := float64(v.Sum) / float64(v.Count)
+ fmt.Fprintf(w, "Count: %d Min: %d Max: %d Avg: %.2f\n", v.Count, v.Min, v.Max, avg)
+ fmt.Fprintf(w, "%s\n", strings.Repeat("-", 60))
+ if v.Count <= 0 {
+ return
+ }
+
+ maxBucketDigitLen := len(strconv.FormatInt(v.Buckets[len(v.Buckets)-1].LowBound, 10))
+ if maxBucketDigitLen < 3 {
+ // For "inf".
+ maxBucketDigitLen = 3
+ }
+ maxCountDigitLen := len(strconv.FormatInt(v.Count, 10))
+ percentMulti := 100 / float64(v.Count)
+
+ accCount := int64(0)
+ for i, b := range v.Buckets {
+ fmt.Fprintf(w, "[%*d, ", maxBucketDigitLen, b.LowBound)
+ if i+1 < len(v.Buckets) {
+ fmt.Fprintf(w, "%*d)", maxBucketDigitLen, v.Buckets[i+1].LowBound)
+ } else {
+ fmt.Fprintf(w, "%*s)", maxBucketDigitLen, "inf")
+ }
+
+ accCount += b.Count
+ fmt.Fprintf(w, " %*d %5.1f%% %5.1f%%", maxCountDigitLen, b.Count, float64(b.Count)*percentMulti, float64(accCount)*percentMulti)
+
+ const barScale = 0.1
+ barLength := int(float64(b.Count)*percentMulti*barScale + 0.5)
+ fmt.Fprintf(w, " %s\n", strings.Repeat("#", barLength))
+ }
+}
+
+// String returns the textual output of the histogram values as string.
+func (v HistogramValue) String() string {
+ var b bytes.Buffer
+ v.Print(&b)
+ return b.String()
+}
+
+// A Histogram accumulates values in the form of a histogram. The type of the
+// values is int64, which is suitable for keeping track of things like RPC
+// latency in milliseconds. New histogram objects should be obtained via the
+// New() function.
+type Histogram struct {
+ opts HistogramOptions
+ buckets []bucketInternal
+ count *Counter
+ sum *Counter
+ tracker *Tracker
+}
+
+// HistogramOptions contains the parameters that define the histogram's buckets.
+type HistogramOptions struct {
+ // NumBuckets is the number of buckets.
+ NumBuckets int
+ // GrowthFactor is the growth factor of the buckets. A value of 0.1
+ // indicates that bucket N+1 will be 10% larger than bucket N.
+ GrowthFactor float64
+ // SmallestBucketSize is the size of the first bucket. Bucket sizes are
+ // rounded down to the nearest integer.
+ SmallestBucketSize float64
+ // MinValue is the lower bound of the first bucket.
+ MinValue int64
+}
+
+// bucketInternal is the internal representation of a bucket, which includes a
+// rate counter.
+type bucketInternal struct {
+ lowBound int64
+ count *Counter
+}
+
+// NewHistogram returns a pointer to a new Histogram object that was created
+// with the provided options.
+func NewHistogram(opts HistogramOptions) *Histogram {
+ if opts.NumBuckets == 0 {
+ opts.NumBuckets = 32
+ }
+ if opts.SmallestBucketSize == 0.0 {
+ opts.SmallestBucketSize = 1.0
+ }
+ h := Histogram{
+ opts: opts,
+ buckets: make([]bucketInternal, opts.NumBuckets),
+ count: newCounter(),
+ sum: newCounter(),
+ tracker: newTracker(),
+ }
+ low := opts.MinValue
+ delta := opts.SmallestBucketSize
+ for i := 0; i < opts.NumBuckets; i++ {
+ h.buckets[i].lowBound = low
+ h.buckets[i].count = newCounter()
+ low = low + int64(delta)
+ delta = delta * (1.0 + opts.GrowthFactor)
+ }
+ return &h
+}
+
+// Opts returns a copy of the options used to create the Histogram.
+func (h *Histogram) Opts() HistogramOptions {
+ return h.opts
+}
+
+// Add adds a value to the histogram.
+func (h *Histogram) Add(value int64) error {
+ bucket, err := h.findBucket(value)
+ if err != nil {
+ return err
+ }
+ h.buckets[bucket].count.Incr(1)
+ h.count.Incr(1)
+ h.sum.Incr(value)
+ h.tracker.Push(value)
+ return nil
+}
+
+// LastUpdate returns the time at which the object was last updated.
+func (h *Histogram) LastUpdate() time.Time {
+ return h.count.LastUpdate()
+}
+
+// Value returns the accumulated state of the histogram since it was created.
+func (h *Histogram) Value() HistogramValue {
+ b := make([]HistogramBucket, len(h.buckets))
+ for i, v := range h.buckets {
+ b[i] = HistogramBucket{
+ LowBound: v.lowBound,
+ Count: v.count.Value(),
+ }
+ }
+
+ v := HistogramValue{
+ Count: h.count.Value(),
+ Sum: h.sum.Value(),
+ Min: h.tracker.Min(),
+ Max: h.tracker.Max(),
+ Buckets: b,
+ }
+ return v
+}
+
+// Delta1h returns the change in the last hour.
+func (h *Histogram) Delta1h() HistogramValue {
+ b := make([]HistogramBucket, len(h.buckets))
+ for i, v := range h.buckets {
+ b[i] = HistogramBucket{
+ LowBound: v.lowBound,
+ Count: v.count.Delta1h(),
+ }
+ }
+
+ v := HistogramValue{
+ Count: h.count.Delta1h(),
+ Sum: h.sum.Delta1h(),
+ Min: h.tracker.Min1h(),
+ Max: h.tracker.Max1h(),
+ Buckets: b,
+ }
+ return v
+}
+
+// Delta10m returns the change in the last 10 minutes.
+func (h *Histogram) Delta10m() HistogramValue {
+ b := make([]HistogramBucket, len(h.buckets))
+ for i, v := range h.buckets {
+ b[i] = HistogramBucket{
+ LowBound: v.lowBound,
+ Count: v.count.Delta10m(),
+ }
+ }
+
+ v := HistogramValue{
+ Count: h.count.Delta10m(),
+ Sum: h.sum.Delta10m(),
+ Min: h.tracker.Min10m(),
+ Max: h.tracker.Max10m(),
+ Buckets: b,
+ }
+ return v
+}
+
+// Delta1m returns the change in the last 10 minutes.
+func (h *Histogram) Delta1m() HistogramValue {
+ b := make([]HistogramBucket, len(h.buckets))
+ for i, v := range h.buckets {
+ b[i] = HistogramBucket{
+ LowBound: v.lowBound,
+ Count: v.count.Delta1m(),
+ }
+ }
+
+ v := HistogramValue{
+ Count: h.count.Delta1m(),
+ Sum: h.sum.Delta1m(),
+ Min: h.tracker.Min1m(),
+ Max: h.tracker.Max1m(),
+ Buckets: b,
+ }
+ return v
+}
+
+// findBucket does a binary search to find in which bucket the value goes.
+func (h *Histogram) findBucket(value int64) (int, error) {
+ lastBucket := len(h.buckets) - 1
+ min, max := 0, lastBucket
+ for max >= min {
+ b := (min + max) / 2
+ if value >= h.buckets[b].lowBound && (b == lastBucket || value < h.buckets[b+1].lowBound) {
+ return b, nil
+ }
+ if value < h.buckets[b].lowBound {
+ max = b - 1
+ continue
+ }
+ min = b + 1
+ }
+ return 0, fmt.Errorf("no bucket for value: %f", value)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/stats.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/stats.go
new file mode 100644
index 000000000..4290ad77e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/stats.go
@@ -0,0 +1,116 @@
+package stats
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "math"
+ "time"
+)
+
+// Stats is a simple helper for gathering additional statistics like histogram
+// during benchmarks. This is not thread safe.
+type Stats struct {
+ numBuckets int
+ unit time.Duration
+ min, max int64
+ histogram *Histogram
+
+ durations durationSlice
+ dirty bool
+}
+
+type durationSlice []time.Duration
+
+// NewStats creates a new Stats instance. If numBuckets is not positive,
+// the default value (16) will be used.
+func NewStats(numBuckets int) *Stats {
+ if numBuckets <= 0 {
+ numBuckets = 16
+ }
+ return &Stats{
+ // Use one more bucket for the last unbounded bucket.
+ numBuckets: numBuckets + 1,
+ durations: make(durationSlice, 0, 100000),
+ }
+}
+
+// Add adds an elapsed time per operation to the stats.
+func (stats *Stats) Add(d time.Duration) {
+ stats.durations = append(stats.durations, d)
+ stats.dirty = true
+}
+
+// Clear resets the stats, removing all values.
+func (stats *Stats) Clear() {
+ stats.durations = stats.durations[:0]
+ stats.histogram = nil
+ stats.dirty = false
+}
+
+// maybeUpdate updates internal stat data if there was any newly added
+// stats since this was updated.
+func (stats *Stats) maybeUpdate() {
+ if !stats.dirty {
+ return
+ }
+
+ stats.min = math.MaxInt64
+ stats.max = 0
+ for _, d := range stats.durations {
+ if stats.min > int64(d) {
+ stats.min = int64(d)
+ }
+ if stats.max < int64(d) {
+ stats.max = int64(d)
+ }
+ }
+
+ // Use the largest unit that can represent the minimum time duration.
+ stats.unit = time.Nanosecond
+ for _, u := range []time.Duration{time.Microsecond, time.Millisecond, time.Second} {
+ if stats.min <= int64(u) {
+ break
+ }
+ stats.unit = u
+ }
+
+ // Adjust the min/max according to the new unit.
+ stats.min /= int64(stats.unit)
+ stats.max /= int64(stats.unit)
+ numBuckets := stats.numBuckets
+ if n := int(stats.max - stats.min + 1); n < numBuckets {
+ numBuckets = n
+ }
+ stats.histogram = NewHistogram(HistogramOptions{
+ NumBuckets: numBuckets,
+ // max(i.e., Nth lower bound) = min + (1 + growthFactor)^(numBuckets-2).
+ GrowthFactor: math.Pow(float64(stats.max-stats.min), 1/float64(stats.numBuckets-2)) - 1,
+ SmallestBucketSize: 1.0,
+ MinValue: stats.min})
+
+ for _, d := range stats.durations {
+ stats.histogram.Add(int64(d / stats.unit))
+ }
+
+ stats.dirty = false
+}
+
+// Print writes textual output of the Stats.
+func (stats *Stats) Print(w io.Writer) {
+ stats.maybeUpdate()
+
+ if stats.histogram == nil {
+ fmt.Fprint(w, "Histogram (empty)\n")
+ } else {
+ fmt.Fprintf(w, "Histogram (unit: %s)\n", fmt.Sprintf("%v", stats.unit)[1:])
+ stats.histogram.Value().Print(w)
+ }
+}
+
+// String returns the textual output of the Stats as string.
+func (stats *Stats) String() string {
+ var b bytes.Buffer
+ stats.Print(&b)
+ return b.String()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/timeseries.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/timeseries.go
new file mode 100644
index 000000000..2ba18a4d4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/timeseries.go
@@ -0,0 +1,154 @@
+package stats
+
+import (
+ "math"
+ "time"
+)
+
+// timeseries holds the history of a changing value over a predefined period of
+// time.
+type timeseries struct {
+ size int // The number of time slots. Equivalent to len(slots).
+ resolution time.Duration // The time resolution of each slot.
+ stepCount int64 // The number of intervals seen since creation.
+ head int // The position of the current time in slots.
+ time time.Time // The time at the beginning of the current time slot.
+ slots []int64 // A circular buffer of time slots.
+}
+
+// newTimeSeries returns a newly allocated timeseries that covers the requested
+// period with the given resolution.
+func newTimeSeries(initialTime time.Time, period, resolution time.Duration) *timeseries {
+ size := int(period.Nanoseconds()/resolution.Nanoseconds()) + 1
+ return ×eries{
+ size: size,
+ resolution: resolution,
+ stepCount: 1,
+ time: initialTime,
+ slots: make([]int64, size),
+ }
+}
+
+// advanceTimeWithFill moves the timeseries forward to time t and fills in any
+// slots that get skipped in the process with the given value. Values older than
+// the timeseries period are lost.
+func (ts *timeseries) advanceTimeWithFill(t time.Time, value int64) {
+ advanceTo := t.Truncate(ts.resolution)
+ if !advanceTo.After(ts.time) {
+ // This is shortcut for the most common case of a busy counter
+ // where updates come in many times per ts.resolution.
+ ts.time = advanceTo
+ return
+ }
+ steps := int(advanceTo.Sub(ts.time).Nanoseconds() / ts.resolution.Nanoseconds())
+ ts.stepCount += int64(steps)
+ if steps > ts.size {
+ steps = ts.size
+ }
+ for steps > 0 {
+ ts.head = (ts.head + 1) % ts.size
+ ts.slots[ts.head] = value
+ steps--
+ }
+ ts.time = advanceTo
+}
+
+// advanceTime moves the timeseries forward to time t and fills in any slots
+// that get skipped in the process with the head value. Values older than the
+// timeseries period are lost.
+func (ts *timeseries) advanceTime(t time.Time) {
+ ts.advanceTimeWithFill(t, ts.slots[ts.head])
+}
+
+// set sets the current value of the timeseries.
+func (ts *timeseries) set(value int64) {
+ ts.slots[ts.head] = value
+}
+
+// incr sets the current value of the timeseries.
+func (ts *timeseries) incr(delta int64) {
+ ts.slots[ts.head] += delta
+}
+
+// headValue returns the latest value from the timeseries.
+func (ts *timeseries) headValue() int64 {
+ return ts.slots[ts.head]
+}
+
+// headTime returns the time of the latest value from the timeseries.
+func (ts *timeseries) headTime() time.Time {
+ return ts.time
+}
+
+// tailValue returns the oldest value from the timeseries.
+func (ts *timeseries) tailValue() int64 {
+ if ts.stepCount < int64(ts.size) {
+ return 0
+ }
+ return ts.slots[(ts.head+1)%ts.size]
+}
+
+// tailTime returns the time of the oldest value from the timeseries.
+func (ts *timeseries) tailTime() time.Time {
+ size := int64(ts.size)
+ if ts.stepCount < size {
+ size = ts.stepCount
+ }
+ return ts.time.Add(-time.Duration(size-1) * ts.resolution)
+}
+
+// delta returns the difference between the newest and oldest values from the
+// timeseries.
+func (ts *timeseries) delta() int64 {
+ return ts.headValue() - ts.tailValue()
+}
+
+// rate returns the rate of change between the oldest and newest values from
+// the timeseries in units per second.
+func (ts *timeseries) rate() float64 {
+ deltaTime := ts.headTime().Sub(ts.tailTime()).Seconds()
+ if deltaTime == 0 {
+ return 0
+ }
+ return float64(ts.delta()) / deltaTime
+}
+
+// min returns the smallest value from the timeseries.
+func (ts *timeseries) min() int64 {
+ to := ts.size
+ if ts.stepCount < int64(ts.size) {
+ to = ts.head + 1
+ }
+ tail := (ts.head + 1) % ts.size
+ min := int64(math.MaxInt64)
+ for b := 0; b < to; b++ {
+ if b != tail && ts.slots[b] < min {
+ min = ts.slots[b]
+ }
+ }
+ return min
+}
+
+// max returns the largest value from the timeseries.
+func (ts *timeseries) max() int64 {
+ to := ts.size
+ if ts.stepCount < int64(ts.size) {
+ to = ts.head + 1
+ }
+ tail := (ts.head + 1) % ts.size
+ max := int64(math.MinInt64)
+ for b := 0; b < to; b++ {
+ if b != tail && ts.slots[b] > max {
+ max = ts.slots[b]
+ }
+ }
+ return max
+}
+
+// reset resets the timeseries to an empty state.
+func (ts *timeseries) reset(t time.Time) {
+ ts.head = 0
+ ts.time = t
+ ts.stepCount = 1
+ ts.slots = make([]int64, ts.size)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/tracker.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/tracker.go
new file mode 100644
index 000000000..802f72957
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/tracker.go
@@ -0,0 +1,159 @@
+package stats
+
+import (
+ "math"
+ "sync"
+ "time"
+)
+
+// Tracker is a min/max value tracker that keeps track of its min/max values
+// over a given period of time, and with a given resolution. The initial min
+// and max values are math.MaxInt64 and math.MinInt64 respectively.
+type Tracker struct {
+ mu sync.RWMutex
+ min, max int64 // All time min/max.
+ minTS, maxTS [3]*timeseries
+ lastUpdate time.Time
+}
+
+// newTracker returns a new Tracker.
+func newTracker() *Tracker {
+ now := TimeNow()
+ t := &Tracker{}
+ t.minTS[hour] = newTimeSeries(now, time.Hour, time.Minute)
+ t.minTS[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
+ t.minTS[minute] = newTimeSeries(now, time.Minute, time.Second)
+ t.maxTS[hour] = newTimeSeries(now, time.Hour, time.Minute)
+ t.maxTS[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
+ t.maxTS[minute] = newTimeSeries(now, time.Minute, time.Second)
+ t.init()
+ return t
+}
+
+func (t *Tracker) init() {
+ t.min = math.MaxInt64
+ t.max = math.MinInt64
+ for _, ts := range t.minTS {
+ ts.set(math.MaxInt64)
+ }
+ for _, ts := range t.maxTS {
+ ts.set(math.MinInt64)
+ }
+}
+
+func (t *Tracker) advance() time.Time {
+ now := TimeNow()
+ for _, ts := range t.minTS {
+ ts.advanceTimeWithFill(now, math.MaxInt64)
+ }
+ for _, ts := range t.maxTS {
+ ts.advanceTimeWithFill(now, math.MinInt64)
+ }
+ return now
+}
+
+// LastUpdate returns the last update time of the range.
+func (t *Tracker) LastUpdate() time.Time {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.lastUpdate
+}
+
+// Push adds a new value if it is a new minimum or maximum.
+func (t *Tracker) Push(value int64) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.lastUpdate = t.advance()
+ if t.min > value {
+ t.min = value
+ }
+ if t.max < value {
+ t.max = value
+ }
+ for _, ts := range t.minTS {
+ if ts.headValue() > value {
+ ts.set(value)
+ }
+ }
+ for _, ts := range t.maxTS {
+ if ts.headValue() < value {
+ ts.set(value)
+ }
+ }
+}
+
+// Min returns the minimum value of the tracker
+func (t *Tracker) Min() int64 {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.min
+}
+
+// Max returns the maximum value of the tracker.
+func (t *Tracker) Max() int64 {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.max
+}
+
+// Min1h returns the minimum value for the last hour.
+func (t *Tracker) Min1h() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.minTS[hour].min()
+}
+
+// Max1h returns the maximum value for the last hour.
+func (t *Tracker) Max1h() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.maxTS[hour].max()
+}
+
+// Min10m returns the minimum value for the last 10 minutes.
+func (t *Tracker) Min10m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.minTS[tenminutes].min()
+}
+
+// Max10m returns the maximum value for the last 10 minutes.
+func (t *Tracker) Max10m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.maxTS[tenminutes].max()
+}
+
+// Min1m returns the minimum value for the last 1 minute.
+func (t *Tracker) Min1m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.minTS[minute].min()
+}
+
+// Max1m returns the maximum value for the last 1 minute.
+func (t *Tracker) Max1m() int64 {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.advance()
+ return t.maxTS[minute].max()
+}
+
+// Reset resets the range to an empty state.
+func (t *Tracker) Reset() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ now := TimeNow()
+ for _, ts := range t.minTS {
+ ts.reset(now)
+ }
+ for _, ts := range t.maxTS {
+ ts.reset(now)
+ }
+ t.init()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/util.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/util.go
new file mode 100644
index 000000000..a9922f985
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/benchmark/stats/util.go
@@ -0,0 +1,191 @@
+package stats
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "sync"
+ "testing"
+)
+
+var (
+ curB *testing.B
+ curBenchName string
+ curStats map[string]*Stats
+
+ orgStdout *os.File
+ nextOutPos int
+
+ injectCond *sync.Cond
+ injectDone chan struct{}
+)
+
+// AddStats adds a new unnamed Stats instance to the current benchmark. You need
+// to run benchmarks by calling RunTestMain() to inject the stats to the
+// benchmark results. If numBuckets is not positive, the default value (16) will
+// be used. Please note that this calls b.ResetTimer() since it may be blocked
+// until the previous benchmark stats is printed out. So AddStats() should
+// typically be called at the very beginning of each benchmark function.
+func AddStats(b *testing.B, numBuckets int) *Stats {
+ return AddStatsWithName(b, "", numBuckets)
+}
+
+// AddStatsWithName adds a new named Stats instance to the current benchmark.
+// With this, you can add multiple stats in a single benchmark. You need
+// to run benchmarks by calling RunTestMain() to inject the stats to the
+// benchmark results. If numBuckets is not positive, the default value (16) will
+// be used. Please note that this calls b.ResetTimer() since it may be blocked
+// until the previous benchmark stats is printed out. So AddStatsWithName()
+// should typically be called at the very beginning of each benchmark function.
+func AddStatsWithName(b *testing.B, name string, numBuckets int) *Stats {
+ var benchName string
+ for i := 1; ; i++ {
+ pc, _, _, ok := runtime.Caller(i)
+ if !ok {
+ panic("benchmark function not found")
+ }
+ p := strings.Split(runtime.FuncForPC(pc).Name(), ".")
+ benchName = p[len(p)-1]
+ if strings.HasPrefix(benchName, "Benchmark") {
+ break
+ }
+ }
+ procs := runtime.GOMAXPROCS(-1)
+ if procs != 1 {
+ benchName = fmt.Sprintf("%s-%d", benchName, procs)
+ }
+
+ stats := NewStats(numBuckets)
+
+ if injectCond != nil {
+ // We need to wait until the previous benchmark stats is printed out.
+ injectCond.L.Lock()
+ for curB != nil && curBenchName != benchName {
+ injectCond.Wait()
+ }
+
+ curB = b
+ curBenchName = benchName
+ curStats[name] = stats
+
+ injectCond.L.Unlock()
+ }
+
+ b.ResetTimer()
+ return stats
+}
+
+// RunTestMain runs the tests with enabling injection of benchmark stats. It
+// returns an exit code to pass to os.Exit.
+func RunTestMain(m *testing.M) int {
+ startStatsInjector()
+ defer stopStatsInjector()
+ return m.Run()
+}
+
+// startStatsInjector starts stats injection to benchmark results.
+func startStatsInjector() {
+ orgStdout = os.Stdout
+ r, w, _ := os.Pipe()
+ os.Stdout = w
+ nextOutPos = 0
+
+ resetCurBenchStats()
+
+ injectCond = sync.NewCond(&sync.Mutex{})
+ injectDone = make(chan struct{})
+ go func() {
+ defer close(injectDone)
+
+ scanner := bufio.NewScanner(r)
+ scanner.Split(splitLines)
+ for scanner.Scan() {
+ injectStatsIfFinished(scanner.Text())
+ }
+ if err := scanner.Err(); err != nil {
+ panic(err)
+ }
+ }()
+}
+
+// stopStatsInjector stops stats injection and restores os.Stdout.
+func stopStatsInjector() {
+ os.Stdout.Close()
+ <-injectDone
+ injectCond = nil
+ os.Stdout = orgStdout
+}
+
+// splitLines is a split function for a bufio.Scanner that returns each line
+// of text, teeing texts to the original stdout even before each line ends.
+func splitLines(data []byte, eof bool) (advance int, token []byte, err error) {
+ if eof && len(data) == 0 {
+ return 0, nil, nil
+ }
+
+ if i := bytes.IndexByte(data, '\n'); i >= 0 {
+ orgStdout.Write(data[nextOutPos : i+1])
+ nextOutPos = 0
+ return i + 1, data[0:i], nil
+ }
+
+ orgStdout.Write(data[nextOutPos:])
+ nextOutPos = len(data)
+
+ if eof {
+ // This is a final, non-terminated line. Return it.
+ return len(data), data, nil
+ }
+
+ return 0, nil, nil
+}
+
+// injectStatsIfFinished prints out the stats if the current benchmark finishes.
+func injectStatsIfFinished(line string) {
+ injectCond.L.Lock()
+ defer injectCond.L.Unlock()
+
+ // We assume that the benchmark results start with the benchmark name.
+ if curB == nil || !strings.HasPrefix(line, curBenchName) {
+ return
+ }
+
+ if !curB.Failed() {
+ // Output all stats in alphabetical order.
+ names := make([]string, 0, len(curStats))
+ for name := range curStats {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ for _, name := range names {
+ stats := curStats[name]
+ // The output of stats starts with a header like "Histogram (unit: ms)"
+ // followed by statistical properties and the buckets. Add the stats name
+ // if it is a named stats and indent them as Go testing outputs.
+ lines := strings.Split(stats.String(), "\n")
+ if n := len(lines); n > 0 {
+ if name != "" {
+ name = ": " + name
+ }
+ fmt.Fprintf(orgStdout, "--- %s%s\n", lines[0], name)
+ for _, line := range lines[1 : n-1] {
+ fmt.Fprintf(orgStdout, "\t%s\n", line)
+ }
+ }
+ }
+ }
+
+ resetCurBenchStats()
+ injectCond.Signal()
+}
+
+// resetCurBenchStats resets the current benchmark stats.
+func resetCurBenchStats() {
+ curB = nil
+ curBenchName = ""
+ curStats = make(map[string]*Stats)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/call.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/call.go
new file mode 100644
index 000000000..db52e05a3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/call.go
@@ -0,0 +1,168 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+ "io"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
+)
+
+// recvResponse receives and parses an RPC response.
+// On error, it returns the error and indicates whether the call should be retried.
+//
+// TODO(zhaoq): Check whether the received message sequence is valid.
+func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error {
+ // Try to acquire header metadata from the server if there is any.
+ var err error
+ c.headerMD, err = stream.Header()
+ if err != nil {
+ return err
+ }
+ p := &parser{s: stream}
+ for {
+ if err = recv(p, codec, reply); err != nil {
+ if err == io.EOF {
+ break
+ }
+ return err
+ }
+ }
+ c.trailerMD = stream.Trailer()
+ return nil
+}
+
+// sendRequest writes out various information of an RPC such as Context and Message.
+func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
+ stream, err := t.NewStream(ctx, callHdr)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ if _, ok := err.(transport.ConnectionError); !ok {
+ t.CloseStream(stream, err)
+ }
+ }
+ }()
+ // TODO(zhaoq): Support compression.
+ outBuf, err := encode(codec, args, compressionNone)
+ if err != nil {
+ return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err)
+ }
+ err = t.Write(stream, outBuf, opts)
+ if err != nil {
+ return nil, err
+ }
+ // Sent successfully.
+ return stream, nil
+}
+
+// callInfo contains all related configuration and information about an RPC.
+type callInfo struct {
+ failFast bool
+ headerMD metadata.MD
+ trailerMD metadata.MD
+}
+
+// Invoke is called by the generated code. It sends the RPC request on the
+// wire and returns after response is received.
+func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) error {
+ var c callInfo
+ for _, o := range opts {
+ if err := o.before(&c); err != nil {
+ return toRPCErr(err)
+ }
+ }
+ defer func() {
+ for _, o := range opts {
+ o.after(&c)
+ }
+ }()
+ callHdr := &transport.CallHdr{
+ Host: cc.authority,
+ Method: method,
+ }
+ topts := &transport.Options{
+ Last: true,
+ Delay: false,
+ }
+ var (
+ ts int // track the transport sequence number
+ lastErr error // record the error that happened
+ )
+ for {
+ var (
+ err error
+ t transport.ClientTransport
+ stream *transport.Stream
+ )
+ // TODO(zhaoq): Need a formal spec of retry strategy for non-failfast rpcs.
+ if lastErr != nil && c.failFast {
+ return toRPCErr(lastErr)
+ }
+ t, ts, err = cc.wait(ctx, ts)
+ if err != nil {
+ if lastErr != nil {
+ // This was a retry; return the error from the last attempt.
+ return toRPCErr(lastErr)
+ }
+ return toRPCErr(err)
+ }
+ stream, err = sendRequest(ctx, cc.dopts.codec, callHdr, t, args, topts)
+ if err != nil {
+ if _, ok := err.(transport.ConnectionError); ok {
+ lastErr = err
+ continue
+ }
+ if lastErr != nil {
+ return toRPCErr(lastErr)
+ }
+ return toRPCErr(err)
+ }
+ // Receive the response
+ lastErr = recvResponse(cc.dopts.codec, t, &c, stream, reply)
+ if _, ok := lastErr.(transport.ConnectionError); ok {
+ continue
+ }
+ t.CloseStream(stream, lastErr)
+ if lastErr != nil {
+ return toRPCErr(lastErr)
+ }
+ return Errorf(stream.StatusCode(), stream.StatusDesc())
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go
new file mode 100644
index 000000000..2419a1e43
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/clientconn.go
@@ -0,0 +1,294 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+ "errors"
+ "net"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
+)
+
+var (
+ // ErrUnspecTarget indicates that the target address is unspecified.
+ ErrUnspecTarget = errors.New("grpc: target is unspecified")
+ // ErrClientConnClosing indicates that the operation is illegal because
+ // the session is closing.
+ ErrClientConnClosing = errors.New("grpc: the client connection is closing")
+ // ErrClientConnTimeout indicates that the connection could not be
+ // established or re-established within the specified timeout.
+ ErrClientConnTimeout = errors.New("grpc: timed out trying to connect")
+)
+
+// dialOptions configure a Dial call. dialOptions are set by the DialOption
+// values passed to Dial.
+type dialOptions struct {
+ codec Codec
+ copts transport.ConnectOptions
+}
+
+// DialOption configures how we set up the connection.
+type DialOption func(*dialOptions)
+
+// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
+func WithCodec(c Codec) DialOption {
+ return func(o *dialOptions) {
+ o.codec = c
+ }
+}
+
+// WithTransportCredentials returns a DialOption which configures a
+// connection level security credentials (e.g., TLS/SSL).
+func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption {
+ return func(o *dialOptions) {
+ o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
+ }
+}
+
+// WithPerRPCCredentials returns a DialOption which sets
+// credentials which will place auth state on each outbound RPC.
+func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
+ return func(o *dialOptions) {
+ o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
+ }
+}
+
+// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
+func WithTimeout(d time.Duration) DialOption {
+ return func(o *dialOptions) {
+ o.copts.Timeout = d
+ }
+}
+
+// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
+func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption {
+ return func(o *dialOptions) {
+ o.copts.Dialer = f
+ }
+}
+
+// Dial creates a client connection the given target.
+// TODO(zhaoq): Have an option to make Dial return immediately without waiting
+// for connection to complete.
+func Dial(target string, opts ...DialOption) (*ClientConn, error) {
+ if target == "" {
+ return nil, ErrUnspecTarget
+ }
+ cc := &ClientConn{
+ target: target,
+ }
+ for _, opt := range opts {
+ opt(&cc.dopts)
+ }
+ colonPos := strings.LastIndex(target, ":")
+ if colonPos == -1 {
+ colonPos = len(target)
+ }
+ cc.authority = target[:colonPos]
+ if cc.dopts.codec == nil {
+ // Set the default codec.
+ cc.dopts.codec = protoCodec{}
+ }
+ if err := cc.resetTransport(false); err != nil {
+ return nil, err
+ }
+ cc.shutdownChan = make(chan struct{})
+ // Start to monitor the error status of transport.
+ go cc.transportMonitor()
+ return cc, nil
+}
+
+// ClientConn represents a client connection to an RPC service.
+type ClientConn struct {
+ target string
+ authority string
+ dopts dialOptions
+ shutdownChan chan struct{}
+
+ mu sync.Mutex
+ // ready is closed and becomes nil when a new transport is up or failed
+ // due to timeout.
+ ready chan struct{}
+ // Indicates the ClientConn is under destruction.
+ closing bool
+ // Every time a new transport is created, this is incremented by 1. Used
+ // to avoid trying to recreate a transport while the new one is already
+ // under construction.
+ transportSeq int
+ transport transport.ClientTransport
+}
+
+func (cc *ClientConn) resetTransport(closeTransport bool) error {
+ var retries int
+ start := time.Now()
+ for {
+ cc.mu.Lock()
+ t := cc.transport
+ ts := cc.transportSeq
+ // Avoid wait() picking up a dying transport unnecessarily.
+ cc.transportSeq = 0
+ if cc.closing {
+ cc.mu.Unlock()
+ return ErrClientConnClosing
+ }
+ cc.mu.Unlock()
+ if closeTransport {
+ t.Close()
+ }
+ // Adjust timeout for the current try.
+ copts := cc.dopts.copts
+ if copts.Timeout < 0 {
+ cc.Close()
+ return ErrClientConnTimeout
+ }
+ if copts.Timeout > 0 {
+ copts.Timeout -= time.Since(start)
+ if copts.Timeout <= 0 {
+ cc.Close()
+ return ErrClientConnTimeout
+ }
+ }
+ newTransport, err := transport.NewClientTransport(cc.target, &copts)
+ if err != nil {
+ sleepTime := backoff(retries)
+ // Fail early before falling into sleep.
+ if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) {
+ cc.Close()
+ return ErrClientConnTimeout
+ }
+ closeTransport = false
+ time.Sleep(sleepTime)
+ retries++
+ grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target)
+ continue
+ }
+ cc.mu.Lock()
+ if cc.closing {
+ // cc.Close() has been invoked.
+ cc.mu.Unlock()
+ newTransport.Close()
+ return ErrClientConnClosing
+ }
+ cc.transport = newTransport
+ cc.transportSeq = ts + 1
+ if cc.ready != nil {
+ close(cc.ready)
+ cc.ready = nil
+ }
+ cc.mu.Unlock()
+ return nil
+ }
+}
+
+// Run in a goroutine to track the error in transport and create the
+// new transport if an error happens. It returns when the channel is closing.
+func (cc *ClientConn) transportMonitor() {
+ for {
+ select {
+ // shutdownChan is needed to detect the channel teardown when
+ // the ClientConn is idle (i.e., no RPC in flight).
+ case <-cc.shutdownChan:
+ return
+ case <-cc.transport.Error():
+ if err := cc.resetTransport(true); err != nil {
+ // The channel is closing.
+ grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err)
+ return
+ }
+ continue
+ }
+ }
+}
+
+// When wait returns, either the new transport is up or ClientConn is
+// closing. Used to avoid working on a dying transport. It updates and
+// returns the transport and its version when there is no error.
+func (cc *ClientConn) wait(ctx context.Context, ts int) (transport.ClientTransport, int, error) {
+ for {
+ cc.mu.Lock()
+ switch {
+ case cc.closing:
+ cc.mu.Unlock()
+ return nil, 0, ErrClientConnClosing
+ case ts < cc.transportSeq:
+ // Worked on a dying transport. Try the new one immediately.
+ defer cc.mu.Unlock()
+ return cc.transport, cc.transportSeq, nil
+ default:
+ ready := cc.ready
+ if ready == nil {
+ ready = make(chan struct{})
+ cc.ready = ready
+ }
+ cc.mu.Unlock()
+ select {
+ case <-ctx.Done():
+ return nil, 0, transport.ContextErr(ctx.Err())
+ // Wait until the new transport is ready or failed.
+ case <-ready:
+ }
+ }
+ }
+}
+
+// Close starts to tear down the ClientConn. Returns ErrClientConnClosing if
+// it has been closed (mostly due to dial time-out).
+// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
+// some edge cases (e.g., the caller opens and closes many ClientConn's in a
+// tight loop.
+func (cc *ClientConn) Close() error {
+ cc.mu.Lock()
+ defer cc.mu.Unlock()
+ if cc.closing {
+ return ErrClientConnClosing
+ }
+ cc.closing = true
+ if cc.ready != nil {
+ close(cc.ready)
+ cc.ready = nil
+ }
+ if cc.transport != nil {
+ cc.transport.Close()
+ }
+ if cc.shutdownChan != nil {
+ close(cc.shutdownChan)
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codegen.sh b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codegen.sh
new file mode 100644
index 000000000..4e7a9e75d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codegen.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# This script serves as an example to demonstrate how to generate the gRPC-Go
+# interface and the related messages from .proto file.
+#
+# It assumes the installation of i) Google proto buffer compiler at
+# https://github.com/google/protobuf (after v2.6.1) and ii) the Go codegen
+# plugin at https://github.com/golang/protobuf (after 2015-02-20). If you have
+# not, please install them first.
+#
+# We recommend running this script at $GOPATH or $GOPATH/src.
+#
+# If this is not what you need, feel free to make your own scripts. Again, this
+# script is for demonstration purpose.
+#
+proto=$1
+protoc --go_out=plugins=grpc:. $proto
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes/code_string.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes/code_string.go
new file mode 100644
index 000000000..e6762d084
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes/code_string.go
@@ -0,0 +1,16 @@
+// generated by stringer -type=Code; DO NOT EDIT
+
+package codes
+
+import "fmt"
+
+const _Code_name = "OKCanceledUnknownInvalidArgumentDeadlineExceededNotFoundAlreadyExistsPermissionDeniedResourceExhaustedFailedPreconditionAbortedOutOfRangeUnimplementedInternalUnavailableDataLossUnauthenticated"
+
+var _Code_index = [...]uint8{0, 2, 10, 17, 32, 48, 56, 69, 85, 102, 120, 127, 137, 150, 158, 169, 177, 192}
+
+func (i Code) String() string {
+ if i+1 >= Code(len(_Code_index)) {
+ return fmt.Sprintf("Code(%d)", i)
+ }
+ return _Code_name[_Code_index[i]:_Code_index[i+1]]
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes/codes.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes/codes.go
new file mode 100644
index 000000000..37c5b860b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes/codes.go
@@ -0,0 +1,159 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+// Package codes defines the canonical error codes used by gRPC. It is
+// consistent across various languages.
+package codes
+
+// A Code is an unsigned 32-bit error code as defined in the gRPC spec.
+type Code uint32
+
+//go:generate stringer -type=Code
+
+const (
+ // OK is returned on success.
+ OK Code = 0
+
+ // Canceled indicates the operation was cancelled (typically by the caller).
+ Canceled Code = 1
+
+ // Unknown error. An example of where this error may be returned is
+ // if a Status value received from another address space belongs to
+ // an error-space that is not known in this address space. Also
+ // errors raised by APIs that do not return enough error information
+ // may be converted to this error.
+ Unknown Code = 2
+
+ // InvalidArgument indicates client specified an invalid argument.
+ // Note that this differs from FailedPrecondition. It indicates arguments
+ // that are problematic regardless of the state of the system
+ // (e.g., a malformed file name).
+ InvalidArgument Code = 3
+
+ // DeadlineExceeded means operation expired before completion.
+ // For operations that change the state of the system, this error may be
+ // returned even if the operation has completed successfully. For
+ // example, a successful response from a server could have been delayed
+ // long enough for the deadline to expire.
+ DeadlineExceeded Code = 4
+
+ // NotFound means some requested entity (e.g., file or directory) was
+ // not found.
+ NotFound Code = 5
+
+ // AlreadyExists means an attempt to create an entity failed because one
+ // already exists.
+ AlreadyExists Code = 6
+
+ // PermissionDenied indicates the caller does not have permission to
+ // execute the specified operation. It must not be used for rejections
+ // caused by exhausting some resource (use ResourceExhausted
+ // instead for those errors). It must not be
+ // used if the caller cannot be identified (use Unauthenticated
+ // instead for those errors).
+ PermissionDenied Code = 7
+
+ // Unauthenticated indicates the request does not have valid
+ // authentication credentials for the operation.
+ Unauthenticated Code = 16
+
+ // ResourceExhausted indicates some resource has been exhausted, perhaps
+ // a per-user quota, or perhaps the entire file system is out of space.
+ ResourceExhausted Code = 8
+
+ // FailedPrecondition indicates operation was rejected because the
+ // system is not in a state required for the operation's execution.
+ // For example, directory to be deleted may be non-empty, an rmdir
+ // operation is applied to a non-directory, etc.
+ //
+ // A litmus test that may help a service implementor in deciding
+ // between FailedPrecondition, Aborted, and Unavailable:
+ // (a) Use Unavailable if the client can retry just the failing call.
+ // (b) Use Aborted if the client should retry at a higher-level
+ // (e.g., restarting a read-modify-write sequence).
+ // (c) Use FailedPrecondition if the client should not retry until
+ // the system state has been explicitly fixed. E.g., if an "rmdir"
+ // fails because the directory is non-empty, FailedPrecondition
+ // should be returned since the client should not retry unless
+ // they have first fixed up the directory by deleting files from it.
+ // (d) Use FailedPrecondition if the client performs conditional
+ // REST Get/Update/Delete on a resource and the resource on the
+ // server does not match the condition. E.g., conflicting
+ // read-modify-write on the same resource.
+ FailedPrecondition Code = 9
+
+ // Aborted indicates the operation was aborted, typically due to a
+ // concurrency issue like sequencer check failures, transaction aborts,
+ // etc.
+ //
+ // See litmus test above for deciding between FailedPrecondition,
+ // Aborted, and Unavailable.
+ Aborted Code = 10
+
+ // OutOfRange means operation was attempted past the valid range.
+ // E.g., seeking or reading past end of file.
+ //
+ // Unlike InvalidArgument, this error indicates a problem that may
+ // be fixed if the system state changes. For example, a 32-bit file
+ // system will generate InvalidArgument if asked to read at an
+ // offset that is not in the range [0,2^32-1], but it will generate
+ // OutOfRange if asked to read from an offset past the current
+ // file size.
+ //
+ // There is a fair bit of overlap between FailedPrecondition and
+ // OutOfRange. We recommend using OutOfRange (the more specific
+ // error) when it applies so that callers who are iterating through
+ // a space can easily look for an OutOfRange error to detect when
+ // they are done.
+ OutOfRange Code = 11
+
+ // Unimplemented indicates operation is not implemented or not
+ // supported/enabled in this service.
+ Unimplemented Code = 12
+
+ // Internal errors. Means some invariants expected by underlying
+ // system has been broken. If you see one of these errors,
+ // something is very broken.
+ Internal Code = 13
+
+ // Unavailable indicates the service is currently unavailable.
+ // This is a most likely a transient condition and may be corrected
+ // by retrying with a backoff.
+ //
+ // See litmus test above for deciding between FailedPrecondition,
+ // Aborted, and Unavailable.
+ Unavailable Code = 14
+
+ // DataLoss indicates unrecoverable data loss or corruption.
+ DataLoss Code = 15
+)
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials/credentials.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials/credentials.go
new file mode 100644
index 000000000..e88635e21
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials/credentials.go
@@ -0,0 +1,270 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+// Package credentials implements various credentials supported by gRPC library,
+// which encapsulate all the state needed by a client to authenticate with a
+// server and make various assertions, e.g., about the client's identity, role,
+// or whether it is authorized to make a particular call.
+package credentials
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/google"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/oauth2/jwt"
+)
+
+var (
+ // alpnProtoStr are the specified application level protocols for gRPC.
+ alpnProtoStr = []string{"h2-14", "h2-15", "h2-16"}
+)
+
+// Credentials defines the common interface all supported credentials must
+// implement.
+type Credentials interface {
+ // GetRequestMetadata gets the current request metadata, refreshing
+ // tokens if required. This should be called by the transport layer on
+ // each request, and the data should be populated in headers or other
+ // context. When supported by the underlying implementation, ctx can
+ // be used for timeout and cancellation.
+ // TODO(zhaoq): Define the set of the qualified keys instead of leaving
+ // it as an arbitrary string.
+ GetRequestMetadata(ctx context.Context) (map[string]string, error)
+}
+
+// ProtocolInfo provides information regarding the gRPC wire protocol version,
+// security protocol, security protocol version in use, etc.
+type ProtocolInfo struct {
+ // ProtocolVersion is the gRPC wire protocol version.
+ ProtocolVersion string
+ // SecurityProtocol is the security protocol in use.
+ SecurityProtocol string
+ // SecurityVersion is the security protocol version.
+ SecurityVersion string
+}
+
+// TransportAuthenticator defines the common interface for all the live gRPC wire
+// protocols and supported transport security protocols (e.g., TLS, SSL).
+type TransportAuthenticator interface {
+ // ClientHandshake does the authentication handshake specified by the corresponding
+ // authentication protocol on rawConn for clients.
+ ClientHandshake(addr string, rawConn net.Conn, timeout time.Duration) (net.Conn, error)
+ // ServerHandshake does the authentication handshake for servers.
+ ServerHandshake(rawConn net.Conn) (net.Conn, error)
+ // Info provides the ProtocolInfo of this TransportAuthenticator.
+ Info() ProtocolInfo
+ Credentials
+}
+
+// tlsCreds is the credentials required for authenticating a connection using TLS.
+type tlsCreds struct {
+ // TLS configuration
+ config tls.Config
+}
+
+func (c *tlsCreds) Info() ProtocolInfo {
+ return ProtocolInfo{
+ SecurityProtocol: "tls",
+ SecurityVersion: "1.2",
+ }
+}
+
+// GetRequestMetadata returns nil, nil since TLS credentials does not have
+// metadata.
+func (c *tlsCreds) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
+ return nil, nil
+}
+
+type timeoutError struct{}
+
+func (timeoutError) Error() string { return "credentials: Dial timed out" }
+func (timeoutError) Timeout() bool { return true }
+func (timeoutError) Temporary() bool { return true }
+
+func (c *tlsCreds) ClientHandshake(addr string, rawConn net.Conn, timeout time.Duration) (_ net.Conn, err error) {
+ // borrow some code from tls.DialWithDialer
+ var errChannel chan error
+ if timeout != 0 {
+ errChannel = make(chan error, 2)
+ time.AfterFunc(timeout, func() {
+ errChannel <- timeoutError{}
+ })
+ }
+ if c.config.ServerName == "" {
+ colonPos := strings.LastIndex(addr, ":")
+ if colonPos == -1 {
+ colonPos = len(addr)
+ }
+ c.config.ServerName = addr[:colonPos]
+ }
+ conn := tls.Client(rawConn, &c.config)
+ if timeout == 0 {
+ err = conn.Handshake()
+ } else {
+ go func() {
+ errChannel <- conn.Handshake()
+ }()
+ err = <-errChannel
+ }
+ if err != nil {
+ rawConn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, error) {
+ conn := tls.Server(rawConn, &c.config)
+ if err := conn.Handshake(); err != nil {
+ rawConn.Close()
+ return nil, err
+ }
+ return conn, nil
+}
+
+// NewTLS uses c to construct a TransportAuthenticator based on TLS.
+func NewTLS(c *tls.Config) TransportAuthenticator {
+ tc := &tlsCreds{*c}
+ tc.config.NextProtos = alpnProtoStr
+ return tc
+}
+
+// NewClientTLSFromCert constructs a TLS from the input certificate for client.
+func NewClientTLSFromCert(cp *x509.CertPool, serverName string) TransportAuthenticator {
+ return NewTLS(&tls.Config{ServerName: serverName, RootCAs: cp})
+}
+
+// NewClientTLSFromFile constructs a TLS from the input certificate file for client.
+func NewClientTLSFromFile(certFile, serverName string) (TransportAuthenticator, error) {
+ b, err := ioutil.ReadFile(certFile)
+ if err != nil {
+ return nil, err
+ }
+ cp := x509.NewCertPool()
+ if !cp.AppendCertsFromPEM(b) {
+ return nil, fmt.Errorf("credentials: failed to append certificates")
+ }
+ return NewTLS(&tls.Config{ServerName: serverName, RootCAs: cp}), nil
+}
+
+// NewServerTLSFromCert constructs a TLS from the input certificate for server.
+func NewServerTLSFromCert(cert *tls.Certificate) TransportAuthenticator {
+ return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
+}
+
+// NewServerTLSFromFile constructs a TLS from the input certificate file and key
+// file for server.
+func NewServerTLSFromFile(certFile, keyFile string) (TransportAuthenticator, error) {
+ cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return nil, err
+ }
+ return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
+}
+
+// TokenSource supplies credentials from an oauth2.TokenSource.
+type TokenSource struct {
+ oauth2.TokenSource
+}
+
+// GetRequestMetadata gets the request metadata as a map from a TokenSource.
+func (ts TokenSource) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
+ token, err := ts.Token()
+ if err != nil {
+ return nil, err
+ }
+ return map[string]string{
+ "authorization": token.TokenType + " " + token.AccessToken,
+ }, nil
+}
+
+// NewComputeEngine constructs the credentials that fetches access tokens from
+// Google Compute Engine (GCE)'s metadata server. It is only valid to use this
+// if your program is running on a GCE instance.
+// TODO(dsymonds): Deprecate and remove this.
+func NewComputeEngine() Credentials {
+ return TokenSource{google.ComputeTokenSource("")}
+}
+
+// serviceAccount represents credentials via JWT signing key.
+type serviceAccount struct {
+ config *jwt.Config
+}
+
+func (s serviceAccount) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
+ token, err := s.config.TokenSource(ctx).Token()
+ if err != nil {
+ return nil, err
+ }
+ return map[string]string{
+ "authorization": token.TokenType + " " + token.AccessToken,
+ }, nil
+}
+
+// NewServiceAccountFromKey constructs the credentials using the JSON key slice
+// from a Google Developers service account.
+func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (Credentials, error) {
+ config, err := google.JWTConfigFromJSON(jsonKey, scope...)
+ if err != nil {
+ return nil, err
+ }
+ return serviceAccount{config: config}, nil
+}
+
+// NewServiceAccountFromFile constructs the credentials using the JSON key file
+// of a Google Developers service account.
+func NewServiceAccountFromFile(keyFile string, scope ...string) (Credentials, error) {
+ jsonKey, err := ioutil.ReadFile(keyFile)
+ if err != nil {
+ return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
+ }
+ return NewServiceAccountFromKey(jsonKey, scope...)
+}
+
+// NewApplicationDefault returns "Application Default Credentials". For more
+// detail, see https://developers.google.com/accounts/docs/application-default-credentials.
+func NewApplicationDefault(ctx context.Context, scope ...string) (Credentials, error) {
+ t, err := google.DefaultTokenSource(ctx, scope...)
+ if err != nil {
+ return nil, err
+ }
+ return TokenSource{t}, nil
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/doc.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/doc.go
new file mode 100644
index 000000000..c0f54f759
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/doc.go
@@ -0,0 +1,6 @@
+/*
+Package grpc implements an RPC system called gRPC.
+
+See https://github.com/grpc/grpc for more information about gRPC.
+*/
+package grpc
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/README.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/README.md
new file mode 100644
index 000000000..02a43f197
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/README.md
@@ -0,0 +1,35 @@
+# Description
+The route guide server and client demonstrate how to use grpc go libraries to
+perform unary, client streaming, server streaming and full duplex RPCs.
+
+Please refer to [Getting Started Guide for Go] (https://github.com/grpc/grpc-common/blob/master/go/gotutorial.md) for more information.
+
+See the definition of the route guide service in proto/route_guide.proto.
+
+# Run the sample code
+To compile and run the server, assuming you are in the root of the route_guide
+folder, i.e., .../examples/route_guide/, simply:
+
+```sh
+$ go run server/server.go
+```
+
+Likewise, to run the client:
+
+```sh
+$ go run client/client.go
+```
+
+# Optional command line flags
+The server and client both take optional command line flags. For example, the
+client and server run without TLS by default. To enable TLS:
+
+```sh
+$ go run server/server.go -tls=true
+```
+
+and
+
+```sh
+$ go run client/client.go -tls=true
+```
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/client/client.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/client/client.go
new file mode 100644
index 000000000..3c9beb42f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/client/client.go
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * 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.
+ *
+ */
+
+// Package main implements a simple gRPC client that demonstrates how to use gRPC-Go libraries
+// to perform unary, client streaming, server streaming and full duplex RPCs.
+//
+// It interacts with the route guide service whose definition can be found in proto/route_guide.proto.
+package main
+
+import (
+ "flag"
+ "io"
+ "math/rand"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+)
+
+var (
+ tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
+ caFile = flag.String("ca_file", "testdata/ca.pem", "The file containning the CA root cert file")
+ serverAddr = flag.String("server_addr", "127.0.0.1:10000", "The server address in the format of host:port")
+ serverHostOverride = flag.String("server_host_override", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake")
+)
+
+// printFeature gets the feature for the given point.
+func printFeature(client pb.RouteGuideClient, point *pb.Point) {
+ grpclog.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude)
+ feature, err := client.GetFeature(context.Background(), point)
+ if err != nil {
+ grpclog.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err)
+ }
+ grpclog.Println(feature)
+}
+
+// printFeatures lists all the features within the given bounding Rectangle.
+func printFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) {
+ grpclog.Printf("Looking for features within %v", rect)
+ stream, err := client.ListFeatures(context.Background(), rect)
+ if err != nil {
+ grpclog.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
+ }
+ for {
+ feature, err := stream.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ grpclog.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
+ }
+ grpclog.Println(feature)
+ }
+}
+
+// runRecordRoute sends a sequence of points to server and expects to get a RouteSummary from server.
+func runRecordRoute(client pb.RouteGuideClient) {
+ // Create a random number of random points
+ r := rand.New(rand.NewSource(time.Now().UnixNano()))
+ pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
+ var points []*pb.Point
+ for i := 0; i < pointCount; i++ {
+ points = append(points, randomPoint(r))
+ }
+ grpclog.Printf("Traversing %d points.", len(points))
+ stream, err := client.RecordRoute(context.Background())
+ if err != nil {
+ grpclog.Fatalf("%v.RecordRoute(_) = _, %v", client, err)
+ }
+ for _, point := range points {
+ if err := stream.Send(point); err != nil {
+ grpclog.Fatalf("%v.Send(%v) = %v", stream, point, err)
+ }
+ }
+ reply, err := stream.CloseAndRecv()
+ if err != nil {
+ grpclog.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
+ }
+ grpclog.Printf("Route summary: %v", reply)
+}
+
+// runRouteChat receives a sequence of route notes, while sending notes for various locations.
+func runRouteChat(client pb.RouteGuideClient) {
+ notes := []*pb.RouteNote{
+ {&pb.Point{0, 1}, "First message"},
+ {&pb.Point{0, 2}, "Second message"},
+ {&pb.Point{0, 3}, "Third message"},
+ {&pb.Point{0, 1}, "Fourth message"},
+ {&pb.Point{0, 2}, "Fifth message"},
+ {&pb.Point{0, 3}, "Sixth message"},
+ }
+ stream, err := client.RouteChat(context.Background())
+ if err != nil {
+ grpclog.Fatalf("%v.RouteChat(_) = _, %v", client, err)
+ }
+ waitc := make(chan struct{})
+ go func() {
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ close(waitc)
+ return
+ }
+ if err != nil {
+ grpclog.Fatalf("Failed to receive a note : %v", err)
+ }
+ grpclog.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
+ }
+ }()
+ for _, note := range notes {
+ if err := stream.Send(note); err != nil {
+ grpclog.Fatalf("Failed to send a note: %v", err)
+ }
+ }
+ stream.CloseSend()
+ <-waitc
+}
+
+func randomPoint(r *rand.Rand) *pb.Point {
+ lat := (r.Int31n(180) - 90) * 1e7
+ long := (r.Int31n(360) - 180) * 1e7
+ return &pb.Point{lat, long}
+}
+
+func main() {
+ flag.Parse()
+ var opts []grpc.DialOption
+ if *tls {
+ var sn string
+ if *serverHostOverride != "" {
+ sn = *serverHostOverride
+ }
+ var creds credentials.TransportAuthenticator
+ if *caFile != "" {
+ var err error
+ creds, err = credentials.NewClientTLSFromFile(*caFile, sn)
+ if err != nil {
+ grpclog.Fatalf("Failed to create TLS credentials %v", err)
+ }
+ } else {
+ creds = credentials.NewClientTLSFromCert(nil, sn)
+ }
+ opts = append(opts, grpc.WithTransportCredentials(creds))
+ }
+ conn, err := grpc.Dial(*serverAddr, opts...)
+ if err != nil {
+ grpclog.Fatalf("fail to dial: %v", err)
+ }
+ defer conn.Close()
+ client := pb.NewRouteGuideClient(conn)
+
+ // Looking for a valid feature
+ printFeature(client, &pb.Point{409146138, -746188906})
+
+ // Feature missing.
+ printFeature(client, &pb.Point{0, 0})
+
+ // Looking for features between 40, -75 and 42, -73.
+ printFeatures(client, &pb.Rectangle{&pb.Point{400000000, -750000000}, &pb.Point{420000000, -730000000}})
+
+ // RecordRoute
+ runRecordRoute(client)
+
+ // RouteChat
+ runRouteChat(client)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto/route_guide.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto/route_guide.pb.go
new file mode 100644
index 000000000..a6f3ed146
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto/route_guide.pb.go
@@ -0,0 +1,425 @@
+// Code generated by protoc-gen-go.
+// source: route_guide.proto
+// DO NOT EDIT!
+
+/*
+Package proto is a generated protocol buffer package.
+
+It is generated from these files:
+ route_guide.proto
+
+It has these top-level messages:
+ Point
+ Rectangle
+ Feature
+ RouteNote
+ RouteSummary
+*/
+package proto
+
+import proto1 "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+
+import (
+ context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto1.Marshal
+
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+type Point struct {
+ Latitude int32 `protobuf:"varint,1,opt,name=latitude" json:"latitude,omitempty"`
+ Longitude int32 `protobuf:"varint,2,opt,name=longitude" json:"longitude,omitempty"`
+}
+
+func (m *Point) Reset() { *m = Point{} }
+func (m *Point) String() string { return proto1.CompactTextString(m) }
+func (*Point) ProtoMessage() {}
+
+// A latitude-longitude rectangle, represented as two diagonally opposite
+// points "lo" and "hi".
+type Rectangle struct {
+ // One corner of the rectangle.
+ Lo *Point `protobuf:"bytes,1,opt,name=lo" json:"lo,omitempty"`
+ // The other corner of the rectangle.
+ Hi *Point `protobuf:"bytes,2,opt,name=hi" json:"hi,omitempty"`
+}
+
+func (m *Rectangle) Reset() { *m = Rectangle{} }
+func (m *Rectangle) String() string { return proto1.CompactTextString(m) }
+func (*Rectangle) ProtoMessage() {}
+
+func (m *Rectangle) GetLo() *Point {
+ if m != nil {
+ return m.Lo
+ }
+ return nil
+}
+
+func (m *Rectangle) GetHi() *Point {
+ if m != nil {
+ return m.Hi
+ }
+ return nil
+}
+
+// A feature names something at a given point.
+//
+// If a feature could not be named, the name is empty.
+type Feature struct {
+ // The name of the feature.
+ Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+ // The point where the feature is detected.
+ Location *Point `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
+}
+
+func (m *Feature) Reset() { *m = Feature{} }
+func (m *Feature) String() string { return proto1.CompactTextString(m) }
+func (*Feature) ProtoMessage() {}
+
+func (m *Feature) GetLocation() *Point {
+ if m != nil {
+ return m.Location
+ }
+ return nil
+}
+
+// A RouteNote is a message sent while at a given point.
+type RouteNote struct {
+ // The location from which the message is sent.
+ Location *Point `protobuf:"bytes,1,opt,name=location" json:"location,omitempty"`
+ // The message to be sent.
+ Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
+}
+
+func (m *RouteNote) Reset() { *m = RouteNote{} }
+func (m *RouteNote) String() string { return proto1.CompactTextString(m) }
+func (*RouteNote) ProtoMessage() {}
+
+func (m *RouteNote) GetLocation() *Point {
+ if m != nil {
+ return m.Location
+ }
+ return nil
+}
+
+// A RouteSummary is received in response to a RecordRoute rpc.
+//
+// It contains the number of individual points received, the number of
+// detected features, and the total distance covered as the cumulative sum of
+// the distance between each point.
+type RouteSummary struct {
+ // The number of points received.
+ PointCount int32 `protobuf:"varint,1,opt,name=point_count" json:"point_count,omitempty"`
+ // The number of known features passed while traversing the route.
+ FeatureCount int32 `protobuf:"varint,2,opt,name=feature_count" json:"feature_count,omitempty"`
+ // The distance covered in metres.
+ Distance int32 `protobuf:"varint,3,opt,name=distance" json:"distance,omitempty"`
+ // The duration of the traversal in seconds.
+ ElapsedTime int32 `protobuf:"varint,4,opt,name=elapsed_time" json:"elapsed_time,omitempty"`
+}
+
+func (m *RouteSummary) Reset() { *m = RouteSummary{} }
+func (m *RouteSummary) String() string { return proto1.CompactTextString(m) }
+func (*RouteSummary) ProtoMessage() {}
+
+func init() {
+}
+
+// Client API for RouteGuide service
+
+type RouteGuideClient interface {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ //
+ // If no feature is found for the given point, a feature with an empty name
+ // should be returned.
+ GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error)
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error)
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error)
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error)
+}
+
+type routeGuideClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewRouteGuideClient(cc *grpc.ClientConn) RouteGuideClient {
+ return &routeGuideClient{cc}
+}
+
+func (c *routeGuideClient) GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error) {
+ out := new(Feature)
+ err := grpc.Invoke(ctx, "/proto.RouteGuide/GetFeature", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *routeGuideClient) ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[0], c.cc, "/proto.RouteGuide/ListFeatures", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &routeGuideListFeaturesClient{stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type RouteGuide_ListFeaturesClient interface {
+ Recv() (*Feature, error)
+ grpc.ClientStream
+}
+
+type routeGuideListFeaturesClient struct {
+ grpc.ClientStream
+}
+
+func (x *routeGuideListFeaturesClient) Recv() (*Feature, error) {
+ m := new(Feature)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *routeGuideClient) RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[1], c.cc, "/proto.RouteGuide/RecordRoute", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &routeGuideRecordRouteClient{stream}
+ return x, nil
+}
+
+type RouteGuide_RecordRouteClient interface {
+ Send(*Point) error
+ CloseAndRecv() (*RouteSummary, error)
+ grpc.ClientStream
+}
+
+type routeGuideRecordRouteClient struct {
+ grpc.ClientStream
+}
+
+func (x *routeGuideRecordRouteClient) Send(m *Point) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *routeGuideRecordRouteClient) CloseAndRecv() (*RouteSummary, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(RouteSummary)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[2], c.cc, "/proto.RouteGuide/RouteChat", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &routeGuideRouteChatClient{stream}
+ return x, nil
+}
+
+type RouteGuide_RouteChatClient interface {
+ Send(*RouteNote) error
+ Recv() (*RouteNote, error)
+ grpc.ClientStream
+}
+
+type routeGuideRouteChatClient struct {
+ grpc.ClientStream
+}
+
+func (x *routeGuideRouteChatClient) Send(m *RouteNote) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *routeGuideRouteChatClient) Recv() (*RouteNote, error) {
+ m := new(RouteNote)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// Server API for RouteGuide service
+
+type RouteGuideServer interface {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ //
+ // If no feature is found for the given point, a feature with an empty name
+ // should be returned.
+ GetFeature(context.Context, *Point) (*Feature, error)
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ ListFeatures(*Rectangle, RouteGuide_ListFeaturesServer) error
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ RecordRoute(RouteGuide_RecordRouteServer) error
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ RouteChat(RouteGuide_RouteChatServer) error
+}
+
+func RegisterRouteGuideServer(s *grpc.Server, srv RouteGuideServer) {
+ s.RegisterService(&_RouteGuide_serviceDesc, srv)
+}
+
+func _RouteGuide_GetFeature_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(Point)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(RouteGuideServer).GetFeature(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _RouteGuide_ListFeatures_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(Rectangle)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(RouteGuideServer).ListFeatures(m, &routeGuideListFeaturesServer{stream})
+}
+
+type RouteGuide_ListFeaturesServer interface {
+ Send(*Feature) error
+ grpc.ServerStream
+}
+
+type routeGuideListFeaturesServer struct {
+ grpc.ServerStream
+}
+
+func (x *routeGuideListFeaturesServer) Send(m *Feature) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _RouteGuide_RecordRoute_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(RouteGuideServer).RecordRoute(&routeGuideRecordRouteServer{stream})
+}
+
+type RouteGuide_RecordRouteServer interface {
+ SendAndClose(*RouteSummary) error
+ Recv() (*Point, error)
+ grpc.ServerStream
+}
+
+type routeGuideRecordRouteServer struct {
+ grpc.ServerStream
+}
+
+func (x *routeGuideRecordRouteServer) SendAndClose(m *RouteSummary) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *routeGuideRecordRouteServer) Recv() (*Point, error) {
+ m := new(Point)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _RouteGuide_RouteChat_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(RouteGuideServer).RouteChat(&routeGuideRouteChatServer{stream})
+}
+
+type RouteGuide_RouteChatServer interface {
+ Send(*RouteNote) error
+ Recv() (*RouteNote, error)
+ grpc.ServerStream
+}
+
+type routeGuideRouteChatServer struct {
+ grpc.ServerStream
+}
+
+func (x *routeGuideRouteChatServer) Send(m *RouteNote) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *routeGuideRouteChatServer) Recv() (*RouteNote, error) {
+ m := new(RouteNote)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+var _RouteGuide_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "proto.RouteGuide",
+ HandlerType: (*RouteGuideServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "GetFeature",
+ Handler: _RouteGuide_GetFeature_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "ListFeatures",
+ Handler: _RouteGuide_ListFeatures_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "RecordRoute",
+ Handler: _RouteGuide_RecordRoute_Handler,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "RouteChat",
+ Handler: _RouteGuide_RouteChat_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto/route_guide.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto/route_guide.proto
new file mode 100644
index 000000000..5ea4fcf5b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto/route_guide.proto
@@ -0,0 +1,121 @@
+// Copyright 2015, Google Inc.
+// 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.
+
+syntax = "proto3";
+
+package proto;
+
+// Interface exported by the server.
+service RouteGuide {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ //
+ // If no feature is found for the given point, a feature with an empty name
+ // should be returned.
+ rpc GetFeature(Point) returns (Feature) {}
+
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ rpc ListFeatures(Rectangle) returns (stream Feature) {}
+
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ rpc RecordRoute(stream Point) returns (RouteSummary) {}
+
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+}
+
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+message Point {
+ int32 latitude = 1;
+ int32 longitude = 2;
+}
+
+// A latitude-longitude rectangle, represented as two diagonally opposite
+// points "lo" and "hi".
+message Rectangle {
+ // One corner of the rectangle.
+ Point lo = 1;
+
+ // The other corner of the rectangle.
+ Point hi = 2;
+}
+
+// A feature names something at a given point.
+//
+// If a feature could not be named, the name is empty.
+message Feature {
+ // The name of the feature.
+ string name = 1;
+
+ // The point where the feature is detected.
+ Point location = 2;
+}
+
+// A RouteNote is a message sent while at a given point.
+message RouteNote {
+ // The location from which the message is sent.
+ Point location = 1;
+
+ // The message to be sent.
+ string message = 2;
+}
+
+// A RouteSummary is received in response to a RecordRoute rpc.
+//
+// It contains the number of individual points received, the number of
+// detected features, and the total distance covered as the cumulative sum of
+// the distance between each point.
+message RouteSummary {
+ // The number of points received.
+ int32 point_count = 1;
+
+ // The number of known features passed while traversing the route.
+ int32 feature_count = 2;
+
+ // The distance covered in metres.
+ int32 distance = 3;
+
+ // The duration of the traversal in seconds.
+ int32 elapsed_time = 4;
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/server/server.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/server/server.go
new file mode 100644
index 000000000..cd60ea240
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/server/server.go
@@ -0,0 +1,236 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * 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.
+ *
+ */
+
+// Package main implements a simple gRPC server that demonstrates how to use gRPC-Go libraries
+// to perform unary, client streaming, server streaming and full duplex RPCs.
+//
+// It implements the route guide service whose definition can be found in proto/route_guide.proto.
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math"
+ "net"
+ "time"
+
+ proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ pb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+)
+
+var (
+ tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
+ certFile = flag.String("cert_file", "testdata/server1.pem", "The TLS cert file")
+ keyFile = flag.String("key_file", "testdata/server1.key", "The TLS key file")
+ jsonDBFile = flag.String("json_db_file", "testdata/route_guide_db.json", "A json file containing a list of features")
+ port = flag.Int("port", 10000, "The server port")
+)
+
+type routeGuideServer struct {
+ savedFeatures []*pb.Feature
+ routeNotes map[string][]*pb.RouteNote
+}
+
+// GetFeature returns the feature at the given point.
+func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
+ for _, feature := range s.savedFeatures {
+ if proto.Equal(feature.Location, point) {
+ return feature, nil
+ }
+ }
+ // No feature was found, return an unnamed feature
+ return &pb.Feature{"", point}, nil
+}
+
+// ListFeatures lists all features comtained within the given bounding Rectangle.
+func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
+ for _, feature := range s.savedFeatures {
+ if inRange(feature.Location, rect) {
+ if err := stream.Send(feature); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// RecordRoute records a route composited of a sequence of points.
+//
+// It gets a stream of points, and responds with statistics about the "trip":
+// number of points, number of known features visited, total distance traveled, and
+// total time spent.
+func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
+ var pointCount, featureCount, distance int32
+ var lastPoint *pb.Point
+ startTime := time.Now()
+ for {
+ point, err := stream.Recv()
+ if err == io.EOF {
+ endTime := time.Now()
+ return stream.SendAndClose(&pb.RouteSummary{
+ PointCount: pointCount,
+ FeatureCount: featureCount,
+ Distance: distance,
+ ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
+ })
+ }
+ if err != nil {
+ return err
+ }
+ pointCount++
+ for _, feature := range s.savedFeatures {
+ if proto.Equal(feature.Location, point) {
+ featureCount++
+ }
+ }
+ if lastPoint != nil {
+ distance += calcDistance(lastPoint, point)
+ }
+ lastPoint = point
+ }
+}
+
+// RouteChat receives a stream of message/location pairs, and responds with a stream of all
+// previous messages at each of those locations.
+func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ key := serialize(in.Location)
+ if _, present := s.routeNotes[key]; !present {
+ s.routeNotes[key] = []*pb.RouteNote{in}
+ } else {
+ s.routeNotes[key] = append(s.routeNotes[key], in)
+ }
+ for _, note := range s.routeNotes[key] {
+ if err := stream.Send(note); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+// loadFeatures loads features from a JSON file.
+func (s *routeGuideServer) loadFeatures(filePath string) {
+ file, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ grpclog.Fatalf("Failed to load default features: %v", err)
+ }
+ if err := json.Unmarshal(file, &s.savedFeatures); err != nil {
+ grpclog.Fatalf("Failed to load default features: %v", err)
+ }
+}
+
+func toRadians(num float64) float64 {
+ return num * math.Pi / float64(180)
+}
+
+// calcDistance calculates the distance between two points using the "haversine" formula.
+// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
+func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
+ const CordFactor float64 = 1e7
+ const R float64 = float64(6371000) // metres
+ lat1 := float64(p1.Latitude) / CordFactor
+ lat2 := float64(p2.Latitude) / CordFactor
+ lng1 := float64(p1.Longitude) / CordFactor
+ lng2 := float64(p2.Longitude) / CordFactor
+ φ1 := toRadians(lat1)
+ φ2 := toRadians(lat2)
+ Δφ := toRadians(lat2 - lat1)
+ Δλ := toRadians(lng2 - lng1)
+
+ a := math.Sin(Δφ/2)*math.Sin(Δφ/2) +
+ math.Cos(φ1)*math.Cos(φ2)*
+ math.Sin(Δλ/2)*math.Sin(Δλ/2)
+ c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
+
+ distance := R * c
+ return int32(distance)
+}
+
+func inRange(point *pb.Point, rect *pb.Rectangle) bool {
+ left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
+ right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
+ top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
+ bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
+
+ if float64(point.Longitude) >= left &&
+ float64(point.Longitude) <= right &&
+ float64(point.Latitude) >= bottom &&
+ float64(point.Latitude) <= top {
+ return true
+ }
+ return false
+}
+
+func serialize(point *pb.Point) string {
+ return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
+}
+
+func newServer() *routeGuideServer {
+ s := new(routeGuideServer)
+ s.loadFeatures(*jsonDBFile)
+ s.routeNotes = make(map[string][]*pb.RouteNote)
+ return s
+}
+
+func main() {
+ flag.Parse()
+ lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
+ if err != nil {
+ grpclog.Fatalf("failed to listen: %v", err)
+ }
+ var opts []grpc.ServerOption
+ if *tls {
+ creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
+ if err != nil {
+ grpclog.Fatalf("Failed to generate credentials %v", err)
+ }
+ opts = []grpc.ServerOption{grpc.Creds(creds)}
+ }
+ grpcServer := grpc.NewServer(opts...)
+ pb.RegisterRouteGuideServer(grpcServer, newServer())
+ grpcServer.Serve(lis)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/ca.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/ca.pem
new file mode 100644
index 000000000..999da8977
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
+NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
+Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
+cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
+xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
+QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
+AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
+fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
+5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json
new file mode 100644
index 000000000..9d6a980ab
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/route_guide_db.json
@@ -0,0 +1,601 @@
+[{
+ "location": {
+ "latitude": 407838351,
+ "longitude": -746143763
+ },
+ "name": "Patriots Path, Mendham, NJ 07945, USA"
+}, {
+ "location": {
+ "latitude": 408122808,
+ "longitude": -743999179
+ },
+ "name": "101 New Jersey 10, Whippany, NJ 07981, USA"
+}, {
+ "location": {
+ "latitude": 413628156,
+ "longitude": -749015468
+ },
+ "name": "U.S. 6, Shohola, PA 18458, USA"
+}, {
+ "location": {
+ "latitude": 419999544,
+ "longitude": -740371136
+ },
+ "name": "5 Conners Road, Kingston, NY 12401, USA"
+}, {
+ "location": {
+ "latitude": 414008389,
+ "longitude": -743951297
+ },
+ "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
+}, {
+ "location": {
+ "latitude": 419611318,
+ "longitude": -746524769
+ },
+ "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
+}, {
+ "location": {
+ "latitude": 406109563,
+ "longitude": -742186778
+ },
+ "name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
+}, {
+ "location": {
+ "latitude": 416802456,
+ "longitude": -742370183
+ },
+ "name": "352 South Mountain Road, Wallkill, NY 12589, USA"
+}, {
+ "location": {
+ "latitude": 412950425,
+ "longitude": -741077389
+ },
+ "name": "Bailey Turn Road, Harriman, NY 10926, USA"
+}, {
+ "location": {
+ "latitude": 412144655,
+ "longitude": -743949739
+ },
+ "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
+}, {
+ "location": {
+ "latitude": 415736605,
+ "longitude": -742847522
+ },
+ "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
+}, {
+ "location": {
+ "latitude": 413843930,
+ "longitude": -740501726
+ },
+ "name": "162 Merrill Road, Highland Mills, NY 10930, USA"
+}, {
+ "location": {
+ "latitude": 410873075,
+ "longitude": -744459023
+ },
+ "name": "Clinton Road, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 412346009,
+ "longitude": -744026814
+ },
+ "name": "16 Old Brook Lane, Warwick, NY 10990, USA"
+}, {
+ "location": {
+ "latitude": 402948455,
+ "longitude": -747903913
+ },
+ "name": "3 Drake Lane, Pennington, NJ 08534, USA"
+}, {
+ "location": {
+ "latitude": 406337092,
+ "longitude": -740122226
+ },
+ "name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
+}, {
+ "location": {
+ "latitude": 406421967,
+ "longitude": -747727624
+ },
+ "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
+}, {
+ "location": {
+ "latitude": 416318082,
+ "longitude": -749677716
+ },
+ "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
+}, {
+ "location": {
+ "latitude": 415301720,
+ "longitude": -748416257
+ },
+ "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
+}, {
+ "location": {
+ "latitude": 402647019,
+ "longitude": -747071791
+ },
+ "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
+}, {
+ "location": {
+ "latitude": 412567807,
+ "longitude": -741058078
+ },
+ "name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
+}, {
+ "location": {
+ "latitude": 416855156,
+ "longitude": -744420597
+ },
+ "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
+}, {
+ "location": {
+ "latitude": 404663628,
+ "longitude": -744820157
+ },
+ "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
+}, {
+ "location": {
+ "latitude": 407113723,
+ "longitude": -749746483
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 402133926,
+ "longitude": -743613249
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400273442,
+ "longitude": -741220915
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411236786,
+ "longitude": -744070769
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411633782,
+ "longitude": -746784970
+ },
+ "name": "211-225 Plains Road, Augusta, NJ 07822, USA"
+}, {
+ "location": {
+ "latitude": 415830701,
+ "longitude": -742952812
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 413447164,
+ "longitude": -748712898
+ },
+ "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
+}, {
+ "location": {
+ "latitude": 405047245,
+ "longitude": -749800722
+ },
+ "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
+}, {
+ "location": {
+ "latitude": 418858923,
+ "longitude": -746156790
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 417951888,
+ "longitude": -748484944
+ },
+ "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
+}, {
+ "location": {
+ "latitude": 407033786,
+ "longitude": -743977337
+ },
+ "name": "26 East 3rd Street, New Providence, NJ 07974, USA"
+}, {
+ "location": {
+ "latitude": 417548014,
+ "longitude": -740075041
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410395868,
+ "longitude": -744972325
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404615353,
+ "longitude": -745129803
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406589790,
+ "longitude": -743560121
+ },
+ "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
+}, {
+ "location": {
+ "latitude": 414653148,
+ "longitude": -740477477
+ },
+ "name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
+}, {
+ "location": {
+ "latitude": 405957808,
+ "longitude": -743255336
+ },
+ "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
+}, {
+ "location": {
+ "latitude": 411733589,
+ "longitude": -741648093
+ },
+ "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
+}, {
+ "location": {
+ "latitude": 412676291,
+ "longitude": -742606606
+ },
+ "name": "1270 Lakes Road, Monroe, NY 10950, USA"
+}, {
+ "location": {
+ "latitude": 409224445,
+ "longitude": -748286738
+ },
+ "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
+}, {
+ "location": {
+ "latitude": 406523420,
+ "longitude": -742135517
+ },
+ "name": "652 Garden Street, Elizabeth, NJ 07202, USA"
+}, {
+ "location": {
+ "latitude": 401827388,
+ "longitude": -740294537
+ },
+ "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
+}, {
+ "location": {
+ "latitude": 410564152,
+ "longitude": -743685054
+ },
+ "name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 408472324,
+ "longitude": -740726046
+ },
+ "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
+}, {
+ "location": {
+ "latitude": 412452168,
+ "longitude": -740214052
+ },
+ "name": "5 White Oak Lane, Stony Point, NY 10980, USA"
+}, {
+ "location": {
+ "latitude": 409146138,
+ "longitude": -746188906
+ },
+ "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
+}, {
+ "location": {
+ "latitude": 404701380,
+ "longitude": -744781745
+ },
+ "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 409642566,
+ "longitude": -746017679
+ },
+ "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
+}, {
+ "location": {
+ "latitude": 408031728,
+ "longitude": -748645385
+ },
+ "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
+}, {
+ "location": {
+ "latitude": 413700272,
+ "longitude": -742135189
+ },
+ "name": "367 Prospect Road, Chester, NY 10918, USA"
+}, {
+ "location": {
+ "latitude": 404310607,
+ "longitude": -740282632
+ },
+ "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
+}, {
+ "location": {
+ "latitude": 409319800,
+ "longitude": -746201391
+ },
+ "name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
+}, {
+ "location": {
+ "latitude": 406685311,
+ "longitude": -742108603
+ },
+ "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
+}, {
+ "location": {
+ "latitude": 419018117,
+ "longitude": -749142781
+ },
+ "name": "43 Dreher Road, Roscoe, NY 12776, USA"
+}, {
+ "location": {
+ "latitude": 412856162,
+ "longitude": -745148837
+ },
+ "name": "Swan Street, Pine Island, NY 10969, USA"
+}, {
+ "location": {
+ "latitude": 416560744,
+ "longitude": -746721964
+ },
+ "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
+}, {
+ "location": {
+ "latitude": 405314270,
+ "longitude": -749836354
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414219548,
+ "longitude": -743327440
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415534177,
+ "longitude": -742900616
+ },
+ "name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
+}, {
+ "location": {
+ "latitude": 406898530,
+ "longitude": -749127080
+ },
+ "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
+}, {
+ "location": {
+ "latitude": 407586880,
+ "longitude": -741670168
+ },
+ "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
+}, {
+ "location": {
+ "latitude": 400106455,
+ "longitude": -742870190
+ },
+ "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
+}, {
+ "location": {
+ "latitude": 400066188,
+ "longitude": -746793294
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418803880,
+ "longitude": -744102673
+ },
+ "name": "40 Mountain Road, Napanoch, NY 12458, USA"
+}, {
+ "location": {
+ "latitude": 414204288,
+ "longitude": -747895140
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414777405,
+ "longitude": -740615601
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415464475,
+ "longitude": -747175374
+ },
+ "name": "48 North Road, Forestburgh, NY 12777, USA"
+}, {
+ "location": {
+ "latitude": 404062378,
+ "longitude": -746376177
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405688272,
+ "longitude": -749285130
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400342070,
+ "longitude": -748788996
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401809022,
+ "longitude": -744157964
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404226644,
+ "longitude": -740517141
+ },
+ "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
+}, {
+ "location": {
+ "latitude": 410322033,
+ "longitude": -747871659
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 407100674,
+ "longitude": -747742727
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418811433,
+ "longitude": -741718005
+ },
+ "name": "213 Bush Road, Stone Ridge, NY 12484, USA"
+}, {
+ "location": {
+ "latitude": 415034302,
+ "longitude": -743850945
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411349992,
+ "longitude": -743694161
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404839914,
+ "longitude": -744759616
+ },
+ "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 414638017,
+ "longitude": -745957854
+ },
+ "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
+}, {
+ "location": {
+ "latitude": 412127800,
+ "longitude": -740173578
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401263460,
+ "longitude": -747964303
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 412843391,
+ "longitude": -749086026
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418512773,
+ "longitude": -743067823
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404318328,
+ "longitude": -740835638
+ },
+ "name": "42-102 Main Street, Belford, NJ 07718, USA"
+}, {
+ "location": {
+ "latitude": 419020746,
+ "longitude": -741172328
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404080723,
+ "longitude": -746119569
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401012643,
+ "longitude": -744035134
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404306372,
+ "longitude": -741079661
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 403966326,
+ "longitude": -748519297
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405002031,
+ "longitude": -748407866
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 409532885,
+ "longitude": -742200683
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 416851321,
+ "longitude": -742674555
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406411633,
+ "longitude": -741722051
+ },
+ "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
+}, {
+ "location": {
+ "latitude": 413069058,
+ "longitude": -744597778
+ },
+ "name": "261 Van Sickle Road, Goshen, NY 10924, USA"
+}, {
+ "location": {
+ "latitude": 418465462,
+ "longitude": -746859398
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411733222,
+ "longitude": -744228360
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410248224,
+ "longitude": -747127767
+ },
+ "name": "3 Hasta Way, Newton, NJ 07860, USA"
+}]
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.key
new file mode 100644
index 000000000..ed00cd500
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
+bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
+BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
+AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
+ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
+omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
+ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
+9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
+MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
+IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
+USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
+VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
+RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.pem
new file mode 100644
index 000000000..8e582e571
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/examples/route_guide/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpc-auth-support.md b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpc-auth-support.md
new file mode 100644
index 000000000..36fe0bd0d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpc-auth-support.md
@@ -0,0 +1,41 @@
+# Authentication
+
+As outlined here gRPC supports a number of different mechanisms for asserting identity between an client and server. We'll present some code-samples here demonstrating how to provide TLS support encryption and identity assertions as well as passing OAuth2 tokens to services that support it.
+
+# Enabling TLS on a gRPC client
+
+```Go
+conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))
+```
+
+# Enabling TLS on a gRPC server
+
+```Go
+creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
+if err != nil {
+ log.Fatalf("Failed to generate credentials %v", err)
+}
+lis, err := net.Listen("tcp", ":0")
+server := grpc.NewServer(grpc.Creds(creds))
+...
+server.Serve(lis)
+```
+
+# Authenticating with Google
+
+## Google Compute Engine (GCE)
+
+```Go
+conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""), grpc.WithPerRPCCredentials(credentials.NewComputeEngine())))
+```
+
+## JWT
+
+```Go
+jwtCreds, err := credentials.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthScope)
+if err != nil {
+ log.Fatalf("Failed to create JWT credentials: %v", err)
+}
+conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""), grpc.WithPerRPCCredentials(jwtCreds)))
+```
+
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go
new file mode 100644
index 000000000..dbe8c90c2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog/logger.go
@@ -0,0 +1,125 @@
+/*
+ *
+ * Copyright 2015, Google Inc.
+ * 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.
+ *
+ */
+
+/*
+Package log defines logging for grpc.
+*/
+package grpclog
+
+import (
+ "log"
+ "os"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/glog"
+)
+
+var (
+ // GLogger is a Logger that uses glog. This is the default logger.
+ GLogger Logger = &glogger{}
+
+ // StdLogger is a Logger that uses golang's standard logger.
+ StdLogger Logger = log.New(os.Stderr, "", log.LstdFlags)
+
+ logger = GLogger
+)
+
+// Logger mimics golang's standard Logger as an interface.
+type Logger interface {
+ Fatal(args ...interface{})
+ Fatalf(format string, args ...interface{})
+ Fatalln(args ...interface{})
+ Print(args ...interface{})
+ Printf(format string, args ...interface{})
+ Println(args ...interface{})
+}
+
+// SetLogger sets the logger that is used in grpc.
+func SetLogger(l Logger) {
+ logger = l
+}
+
+// Fatal is equivalent to Print() followed by a call to os.Exit() with a non-zero exit code.
+func Fatal(args ...interface{}) {
+ logger.Fatal(args...)
+}
+
+// Fatal is equivalent to Printf() followed by a call to os.Exit() with a non-zero exit code.
+func Fatalf(format string, args ...interface{}) {
+ logger.Fatalf(format, args...)
+}
+
+// Fatal is equivalent to Println() followed by a call to os.Exit()) with a non-zero exit code.
+func Fatalln(args ...interface{}) {
+ logger.Fatalln(args...)
+}
+
+// Print prints to the logger. Arguments are handled in the manner of fmt.Print.
+func Print(args ...interface{}) {
+ logger.Print(args...)
+}
+
+// Printf prints to the logger. Arguments are handled in the manner of fmt.Printf.
+func Printf(format string, args ...interface{}) {
+ logger.Printf(format, args...)
+}
+
+// Println prints to the logger. Arguments are handled in the manner of fmt.Println.
+func Println(args ...interface{}) {
+ logger.Println(args...)
+}
+
+type glogger struct{}
+
+func (g *glogger) Fatal(args ...interface{}) {
+ glog.Fatal(args...)
+}
+
+func (g *glogger) Fatalf(format string, args ...interface{}) {
+ glog.Fatalf(format, args...)
+}
+
+func (g *glogger) Fatalln(args ...interface{}) {
+ glog.Fatalln(args...)
+}
+
+func (g *glogger) Print(args ...interface{}) {
+ glog.Info(args...)
+}
+
+func (g *glogger) Printf(format string, args ...interface{}) {
+ glog.Infof(format, args...)
+}
+
+func (g *glogger) Println(args ...interface{}) {
+ glog.Infoln(args...)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/client.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/client.go
new file mode 100644
index 000000000..8b00442a4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/client.go
@@ -0,0 +1,419 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package main
+
+import (
+ "flag"
+ "io"
+ "io/ioutil"
+ "net"
+ "strconv"
+ "strings"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+)
+
+var (
+ useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
+ caFile = flag.String("tls_ca_file", "testdata/ca.pem", "The file containning the CA root cert file")
+ serviceAccountKeyFile = flag.String("service_account_key_file", "", "Path to service account json key file")
+ oauthScope = flag.String("oauth_scope", "", "The scope for OAuth2 tokens")
+ defaultServiceAccount = flag.String("default_service_account", "", "Email of GCE default service account")
+ serverHost = flag.String("server_host", "127.0.0.1", "The server host name")
+ serverPort = flag.Int("server_port", 10000, "The server port number")
+ tlsServerName = flag.String("server_host_override", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake if it is not empty. Otherwise, --server_host is used.")
+ testCase = flag.String("test_case", "large_unary",
+ `Configure different test cases. Valid options are:
+ empty_unary : empty (zero bytes) request and response;
+ large_unary : single request and (large) response;
+ client_streaming : request streaming with single response;
+ server_streaming : single request with response streaming;
+ ping_pong : full-duplex streaming;
+ compute_engine_creds: large_unary with compute engine auth;
+ service_account_creds: large_unary with service account auth;
+ cancel_after_begin: cancellation after metadata has been sent but before payloads are sent;
+ cancel_after_first_response: cancellation after receiving 1st message from the server.`)
+)
+
+var (
+ reqSizes = []int{27182, 8, 1828, 45904}
+ respSizes = []int{31415, 9, 2653, 58979}
+ largeReqSize = 271828
+ largeRespSize = 314159
+)
+
+func newPayload(t testpb.PayloadType, size int) *testpb.Payload {
+ if size < 0 {
+ grpclog.Fatalf("Requested a response with invalid length %d", size)
+ }
+ body := make([]byte, size)
+ switch t {
+ case testpb.PayloadType_COMPRESSABLE:
+ case testpb.PayloadType_UNCOMPRESSABLE:
+ grpclog.Fatalf("PayloadType UNCOMPRESSABLE is not supported")
+ default:
+ grpclog.Fatalf("Unsupported payload type: %d", t)
+ }
+ return &testpb.Payload{
+ Type: t.Enum(),
+ Body: body,
+ }
+}
+
+func doEmptyUnaryCall(tc testpb.TestServiceClient) {
+ reply, err := tc.EmptyCall(context.Background(), &testpb.Empty{})
+ if err != nil {
+ grpclog.Fatal("/TestService/EmptyCall RPC failed: ", err)
+ }
+ if !proto.Equal(&testpb.Empty{}, reply) {
+ grpclog.Fatalf("/TestService/EmptyCall receives %v, want %v", reply, testpb.Empty{})
+ }
+ grpclog.Println("EmptyUnaryCall done")
+}
+
+func doLargeUnaryCall(tc testpb.TestServiceClient) {
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(largeRespSize)),
+ Payload: pl,
+ }
+ reply, err := tc.UnaryCall(context.Background(), req)
+ if err != nil {
+ grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
+ }
+ t := reply.GetPayload().GetType()
+ s := len(reply.GetPayload().GetBody())
+ if t != testpb.PayloadType_COMPRESSABLE || s != largeRespSize {
+ grpclog.Fatalf("Got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, largeRespSize)
+ }
+ grpclog.Println("LargeUnaryCall done")
+}
+
+func doClientStreaming(tc testpb.TestServiceClient) {
+ stream, err := tc.StreamingInputCall(context.Background())
+ if err != nil {
+ grpclog.Fatalf("%v.StreamingInputCall(_) = _, %v", tc, err)
+ }
+ var sum int
+ for _, s := range reqSizes {
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, s)
+ req := &testpb.StreamingInputCallRequest{
+ Payload: pl,
+ }
+ if err := stream.Send(req); err != nil {
+ grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
+ }
+ sum += s
+ grpclog.Printf("Sent a request of size %d, aggregated size %d", s, sum)
+
+ }
+ reply, err := stream.CloseAndRecv()
+ if err != nil {
+ grpclog.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
+ }
+ if reply.GetAggregatedPayloadSize() != int32(sum) {
+ grpclog.Fatalf("%v.CloseAndRecv().GetAggregatePayloadSize() = %v; want %v", stream, reply.GetAggregatedPayloadSize(), sum)
+ }
+ grpclog.Println("ClientStreaming done")
+}
+
+func doServerStreaming(tc testpb.TestServiceClient) {
+ respParam := make([]*testpb.ResponseParameters, len(respSizes))
+ for i, s := range respSizes {
+ respParam[i] = &testpb.ResponseParameters{
+ Size: proto.Int32(int32(s)),
+ }
+ }
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ }
+ stream, err := tc.StreamingOutputCall(context.Background(), req)
+ if err != nil {
+ grpclog.Fatalf("%v.StreamingOutputCall(_) = _, %v", tc, err)
+ }
+ var rpcStatus error
+ var respCnt int
+ var index int
+ for {
+ reply, err := stream.Recv()
+ if err != nil {
+ rpcStatus = err
+ break
+ }
+ t := reply.GetPayload().GetType()
+ if t != testpb.PayloadType_COMPRESSABLE {
+ grpclog.Fatalf("Got the reply of type %d, want %d", t, testpb.PayloadType_COMPRESSABLE)
+ }
+ size := len(reply.GetPayload().GetBody())
+ if size != int(respSizes[index]) {
+ grpclog.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
+ }
+ index++
+ respCnt++
+ }
+ if rpcStatus != io.EOF {
+ grpclog.Fatalf("Failed to finish the server streaming rpc: %v", err)
+ }
+ if respCnt != len(respSizes) {
+ grpclog.Fatalf("Got %d reply, want %d", len(respSizes), respCnt)
+ }
+ grpclog.Println("ServerStreaming done")
+}
+
+func doPingPong(tc testpb.TestServiceClient) {
+ stream, err := tc.FullDuplexCall(context.Background())
+ if err != nil {
+ grpclog.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
+ }
+ var index int
+ for index < len(reqSizes) {
+ respParam := []*testpb.ResponseParameters{
+ {
+ Size: proto.Int32(int32(respSizes[index])),
+ },
+ }
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, reqSizes[index])
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ Payload: pl,
+ }
+ if err := stream.Send(req); err != nil {
+ grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
+ }
+ reply, err := stream.Recv()
+ if err != nil {
+ grpclog.Fatalf("%v.Recv() = %v", stream, err)
+ }
+ t := reply.GetPayload().GetType()
+ if t != testpb.PayloadType_COMPRESSABLE {
+ grpclog.Fatalf("Got the reply of type %d, want %d", t, testpb.PayloadType_COMPRESSABLE)
+ }
+ size := len(reply.GetPayload().GetBody())
+ if size != int(respSizes[index]) {
+ grpclog.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
+ }
+ index++
+ }
+ if err := stream.CloseSend(); err != nil {
+ grpclog.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
+ }
+ if _, err := stream.Recv(); err != io.EOF {
+ grpclog.Fatalf("%v failed to complele the ping pong test: %v", stream, err)
+ }
+ grpclog.Println("Pingpong done")
+}
+
+func doComputeEngineCreds(tc testpb.TestServiceClient) {
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(largeRespSize)),
+ Payload: pl,
+ FillUsername: proto.Bool(true),
+ FillOauthScope: proto.Bool(true),
+ }
+ reply, err := tc.UnaryCall(context.Background(), req)
+ if err != nil {
+ grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
+ }
+ user := reply.GetUsername()
+ scope := reply.GetOauthScope()
+ if user != *defaultServiceAccount {
+ grpclog.Fatalf("Got user name %q, want %q.", user, *defaultServiceAccount)
+ }
+ if !strings.Contains(*oauthScope, scope) {
+ grpclog.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, *oauthScope)
+ }
+ grpclog.Println("ComputeEngineCreds done")
+}
+
+func getServiceAccountJSONKey() []byte {
+ jsonKey, err := ioutil.ReadFile(*serviceAccountKeyFile)
+ if err != nil {
+ grpclog.Fatalf("Failed to read the service account key file: %v", err)
+ }
+ return jsonKey
+}
+
+func doServiceAccountCreds(tc testpb.TestServiceClient) {
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(largeRespSize)),
+ Payload: pl,
+ FillUsername: proto.Bool(true),
+ FillOauthScope: proto.Bool(true),
+ }
+ reply, err := tc.UnaryCall(context.Background(), req)
+ if err != nil {
+ grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
+ }
+ jsonKey := getServiceAccountJSONKey()
+ user := reply.GetUsername()
+ scope := reply.GetOauthScope()
+ if !strings.Contains(string(jsonKey), user) {
+ grpclog.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
+ }
+ if !strings.Contains(*oauthScope, scope) {
+ grpclog.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, *oauthScope)
+ }
+ grpclog.Println("ServiceAccountCreds done")
+}
+
+var (
+ testMetadata = metadata.MD{
+ "key1": "value1",
+ "key2": "value2",
+ }
+)
+
+func doCancelAfterBegin(tc testpb.TestServiceClient) {
+ ctx, cancel := context.WithCancel(metadata.NewContext(context.Background(), testMetadata))
+ stream, err := tc.StreamingInputCall(ctx)
+ if err != nil {
+ grpclog.Fatalf("%v.StreamingInputCall(_) = _, %v", tc, err)
+ }
+ cancel()
+ _, err = stream.CloseAndRecv()
+ if grpc.Code(err) != codes.Canceled {
+ grpclog.Fatalf("%v.CloseAndRecv() got error code %d, want %d", stream, grpc.Code(err), codes.Canceled)
+ }
+ grpclog.Println("CancelAfterBegin done")
+}
+
+func doCancelAfterFirstResponse(tc testpb.TestServiceClient) {
+ ctx, cancel := context.WithCancel(context.Background())
+ stream, err := tc.FullDuplexCall(ctx)
+ if err != nil {
+ grpclog.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
+ }
+ respParam := []*testpb.ResponseParameters{
+ {
+ Size: proto.Int32(31415),
+ },
+ }
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, 27182)
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ Payload: pl,
+ }
+ if err := stream.Send(req); err != nil {
+ grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
+ }
+ if _, err := stream.Recv(); err != nil {
+ grpclog.Fatalf("%v.Recv() = %v", stream, err)
+ }
+ cancel()
+ if _, err := stream.Recv(); grpc.Code(err) != codes.Canceled {
+ grpclog.Fatalf("%v compleled with error code %d, want %d", stream, grpc.Code(err), codes.Canceled)
+ }
+ grpclog.Println("CancelAfterFirstResponse done")
+}
+
+func main() {
+ flag.Parse()
+ serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
+ var opts []grpc.DialOption
+ if *useTLS {
+ var sn string
+ if *tlsServerName != "" {
+ sn = *tlsServerName
+ }
+ var creds credentials.TransportAuthenticator
+ if *caFile != "" {
+ var err error
+ creds, err = credentials.NewClientTLSFromFile(*caFile, sn)
+ if err != nil {
+ grpclog.Fatalf("Failed to create TLS credentials %v", err)
+ }
+ } else {
+ creds = credentials.NewClientTLSFromCert(nil, sn)
+ }
+ opts = append(opts, grpc.WithTransportCredentials(creds))
+ if *testCase == "compute_engine_creds" {
+ opts = append(opts, grpc.WithPerRPCCredentials(credentials.NewComputeEngine()))
+ } else if *testCase == "service_account_creds" {
+ jwtCreds, err := credentials.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthScope)
+ if err != nil {
+ grpclog.Fatalf("Failed to create JWT credentials: %v", err)
+ }
+ opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
+ }
+ }
+ conn, err := grpc.Dial(serverAddr, opts...)
+ if err != nil {
+ grpclog.Fatalf("Fail to dial: %v", err)
+ }
+ defer conn.Close()
+ tc := testpb.NewTestServiceClient(conn)
+ switch *testCase {
+ case "empty_unary":
+ doEmptyUnaryCall(tc)
+ case "large_unary":
+ doLargeUnaryCall(tc)
+ case "client_streaming":
+ doClientStreaming(tc)
+ case "server_streaming":
+ doServerStreaming(tc)
+ case "ping_pong":
+ doPingPong(tc)
+ case "compute_engine_creds":
+ if !*useTLS {
+ grpclog.Fatalf("TLS is not enabled. TLS is required to execute compute_engine_creds test case.")
+ }
+ doComputeEngineCreds(tc)
+ case "service_account_creds":
+ if !*useTLS {
+ grpclog.Fatalf("TLS is not enabled. TLS is required to execute service_account_creds test case.")
+ }
+ doServiceAccountCreds(tc)
+ case "cancel_after_begin":
+ doCancelAfterBegin(tc)
+ case "cancel_after_first_response":
+ doCancelAfterFirstResponse(tc)
+ default:
+ grpclog.Fatal("Unsupported test case: ", *testCase)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/ca.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/ca.pem
new file mode 100644
index 000000000..999da8977
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
+NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
+Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
+cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
+xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
+QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
+AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
+fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
+5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.key
new file mode 100644
index 000000000..ed00cd500
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
+bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
+BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
+AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
+ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
+omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
+ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
+9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
+MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
+IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
+USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
+VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
+RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.pem
new file mode 100644
index 000000000..8e582e571
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/client/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go
new file mode 100644
index 000000000..754de67f9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.pb.go
@@ -0,0 +1,702 @@
+// Code generated by protoc-gen-go.
+// source: src/google.golang.org/grpc/test/grpc_testing/test.proto
+// DO NOT EDIT!
+
+/*
+Package grpc_testing is a generated protocol buffer package.
+
+It is generated from these files:
+ src/google.golang.org/grpc/test/grpc_testing/test.proto
+
+It has these top-level messages:
+ Empty
+ Payload
+ SimpleRequest
+ SimpleResponse
+ StreamingInputCallRequest
+ StreamingInputCallResponse
+ ResponseParameters
+ StreamingOutputCallRequest
+ StreamingOutputCallResponse
+*/
+package grpc_testing
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+import (
+ context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+// The type of payload that should be returned.
+type PayloadType int32
+
+const (
+ // Compressable text format.
+ PayloadType_COMPRESSABLE PayloadType = 0
+ // Uncompressable binary format.
+ PayloadType_UNCOMPRESSABLE PayloadType = 1
+ // Randomly chosen from all other formats defined in this enum.
+ PayloadType_RANDOM PayloadType = 2
+)
+
+var PayloadType_name = map[int32]string{
+ 0: "COMPRESSABLE",
+ 1: "UNCOMPRESSABLE",
+ 2: "RANDOM",
+}
+var PayloadType_value = map[string]int32{
+ "COMPRESSABLE": 0,
+ "UNCOMPRESSABLE": 1,
+ "RANDOM": 2,
+}
+
+func (x PayloadType) Enum() *PayloadType {
+ p := new(PayloadType)
+ *p = x
+ return p
+}
+func (x PayloadType) String() string {
+ return proto.EnumName(PayloadType_name, int32(x))
+}
+func (x *PayloadType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PayloadType_value, data, "PayloadType")
+ if err != nil {
+ return err
+ }
+ *x = PayloadType(value)
+ return nil
+}
+
+type Empty struct {
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+
+// A block of data, to simply increase gRPC message size.
+type Payload struct {
+ // The type of data in body.
+ Type *PayloadType `protobuf:"varint,1,opt,name=type,enum=grpc.testing.PayloadType" json:"type,omitempty"`
+ // Primary contents of payload.
+ Body []byte `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Payload) Reset() { *m = Payload{} }
+func (m *Payload) String() string { return proto.CompactTextString(m) }
+func (*Payload) ProtoMessage() {}
+
+func (m *Payload) GetType() PayloadType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *Payload) GetBody() []byte {
+ if m != nil {
+ return m.Body
+ }
+ return nil
+}
+
+// Unary request.
+type SimpleRequest struct {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ ResponseSize *int32 `protobuf:"varint,2,opt,name=response_size" json:"response_size,omitempty"`
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
+ // Whether SimpleResponse should include username.
+ FillUsername *bool `protobuf:"varint,4,opt,name=fill_username" json:"fill_username,omitempty"`
+ // Whether SimpleResponse should include OAuth scope.
+ FillOauthScope *bool `protobuf:"varint,5,opt,name=fill_oauth_scope" json:"fill_oauth_scope,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
+func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
+func (*SimpleRequest) ProtoMessage() {}
+
+func (m *SimpleRequest) GetResponseType() PayloadType {
+ if m != nil && m.ResponseType != nil {
+ return *m.ResponseType
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *SimpleRequest) GetResponseSize() int32 {
+ if m != nil && m.ResponseSize != nil {
+ return *m.ResponseSize
+ }
+ return 0
+}
+
+func (m *SimpleRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (m *SimpleRequest) GetFillUsername() bool {
+ if m != nil && m.FillUsername != nil {
+ return *m.FillUsername
+ }
+ return false
+}
+
+func (m *SimpleRequest) GetFillOauthScope() bool {
+ if m != nil && m.FillOauthScope != nil {
+ return *m.FillOauthScope
+ }
+ return false
+}
+
+// Unary response, as configured by the request.
+type SimpleResponse struct {
+ // Payload to increase message size.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ Username *string `protobuf:"bytes,2,opt,name=username" json:"username,omitempty"`
+ // OAuth scope.
+ OauthScope *string `protobuf:"bytes,3,opt,name=oauth_scope" json:"oauth_scope,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
+func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
+func (*SimpleResponse) ProtoMessage() {}
+
+func (m *SimpleResponse) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (m *SimpleResponse) GetUsername() string {
+ if m != nil && m.Username != nil {
+ return *m.Username
+ }
+ return ""
+}
+
+func (m *SimpleResponse) GetOauthScope() string {
+ if m != nil && m.OauthScope != nil {
+ return *m.OauthScope
+ }
+ return ""
+}
+
+// Client-streaming request.
+type StreamingInputCallRequest struct {
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
+func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallRequest) ProtoMessage() {}
+
+func (m *StreamingInputCallRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+// Client-streaming response.
+type StreamingInputCallResponse struct {
+ // Aggregated size of payloads received from the client.
+ AggregatedPayloadSize *int32 `protobuf:"varint,1,opt,name=aggregated_payload_size" json:"aggregated_payload_size,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
+func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallResponse) ProtoMessage() {}
+
+func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
+ if m != nil && m.AggregatedPayloadSize != nil {
+ return *m.AggregatedPayloadSize
+ }
+ return 0
+}
+
+// Configuration for a particular response.
+type ResponseParameters struct {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ Size *int32 `protobuf:"varint,1,opt,name=size" json:"size,omitempty"`
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ IntervalUs *int32 `protobuf:"varint,2,opt,name=interval_us" json:"interval_us,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
+func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
+func (*ResponseParameters) ProtoMessage() {}
+
+func (m *ResponseParameters) GetSize() int32 {
+ if m != nil && m.Size != nil {
+ return *m.Size
+ }
+ return 0
+}
+
+func (m *ResponseParameters) GetIntervalUs() int32 {
+ if m != nil && m.IntervalUs != nil {
+ return *m.IntervalUs
+ }
+ return 0
+}
+
+// Server-streaming request.
+type StreamingOutputCallRequest struct {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
+ // Configuration for each expected response message.
+ ResponseParameters []*ResponseParameters `protobuf:"bytes,2,rep,name=response_parameters" json:"response_parameters,omitempty"`
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
+func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallRequest) ProtoMessage() {}
+
+func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
+ if m != nil && m.ResponseType != nil {
+ return *m.ResponseType
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *StreamingOutputCallRequest) GetResponseParameters() []*ResponseParameters {
+ if m != nil {
+ return m.ResponseParameters
+ }
+ return nil
+}
+
+func (m *StreamingOutputCallRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+// Server-streaming response, as configured by the request and parameters.
+type StreamingOutputCallResponse struct {
+ // Payload to increase response size.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
+func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallResponse) ProtoMessage() {}
+
+func (m *StreamingOutputCallResponse) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
+}
+
+// Client API for TestService service
+
+type TestServiceClient interface {
+ // One empty request followed by one empty response.
+ EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)
+}
+
+type testServiceClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
+ return &testServiceClient{cc}
+}
+
+func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
+ out := new(Empty)
+ err := grpc.Invoke(ctx, "/grpc.testing.TestService/EmptyCall", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
+ out := new(SimpleResponse)
+ err := grpc.Invoke(ctx, "/grpc.testing.TestService/UnaryCall", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/grpc.testing.TestService/StreamingOutputCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceStreamingOutputCallClient{stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type TestService_StreamingOutputCallClient interface {
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceStreamingOutputCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[1], c.cc, "/grpc.testing.TestService/StreamingInputCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceStreamingInputCallClient{stream}
+ return x, nil
+}
+
+type TestService_StreamingInputCallClient interface {
+ Send(*StreamingInputCallRequest) error
+ CloseAndRecv() (*StreamingInputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceStreamingInputCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(StreamingInputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[2], c.cc, "/grpc.testing.TestService/FullDuplexCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceFullDuplexCallClient{stream}
+ return x, nil
+}
+
+type TestService_FullDuplexCallClient interface {
+ Send(*StreamingOutputCallRequest) error
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceFullDuplexCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[3], c.cc, "/grpc.testing.TestService/HalfDuplexCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceHalfDuplexCallClient{stream}
+ return x, nil
+}
+
+type TestService_HalfDuplexCallClient interface {
+ Send(*StreamingOutputCallRequest) error
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceHalfDuplexCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// Server API for TestService service
+
+type TestServiceServer interface {
+ // One empty request followed by one empty response.
+ EmptyCall(context.Context, *Empty) (*Empty, error)
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ StreamingInputCall(TestService_StreamingInputCallServer) error
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ FullDuplexCall(TestService_FullDuplexCallServer) error
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ HalfDuplexCall(TestService_HalfDuplexCallServer) error
+}
+
+func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
+ s.RegisterService(&_TestService_serviceDesc, srv)
+}
+
+func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(Empty)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(TestServiceServer).EmptyCall(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(SimpleRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(TestServiceServer).UnaryCall(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(StreamingOutputCallRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})
+}
+
+type TestService_StreamingOutputCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ grpc.ServerStream
+}
+
+type testServiceStreamingOutputCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})
+}
+
+type TestService_StreamingInputCallServer interface {
+ SendAndClose(*StreamingInputCallResponse) error
+ Recv() (*StreamingInputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceStreamingInputCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {
+ m := new(StreamingInputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})
+}
+
+type TestService_FullDuplexCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ Recv() (*StreamingOutputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceFullDuplexCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
+ m := new(StreamingOutputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})
+}
+
+type TestService_HalfDuplexCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ Recv() (*StreamingOutputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceHalfDuplexCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
+ m := new(StreamingOutputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+var _TestService_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "grpc.testing.TestService",
+ HandlerType: (*TestServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "EmptyCall",
+ Handler: _TestService_EmptyCall_Handler,
+ },
+ {
+ MethodName: "UnaryCall",
+ Handler: _TestService_UnaryCall_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "StreamingOutputCall",
+ Handler: _TestService_StreamingOutputCall_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "StreamingInputCall",
+ Handler: _TestService_StreamingInputCall_Handler,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "FullDuplexCall",
+ Handler: _TestService_FullDuplexCall_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "HalfDuplexCall",
+ Handler: _TestService_HalfDuplexCall_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.proto
new file mode 100644
index 000000000..b5bfe0537
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing/test.proto
@@ -0,0 +1,140 @@
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto2";
+
+package grpc.testing;
+
+message Empty {}
+
+// The type of payload that should be returned.
+enum PayloadType {
+ // Compressable text format.
+ COMPRESSABLE = 0;
+
+ // Uncompressable binary format.
+ UNCOMPRESSABLE = 1;
+
+ // Randomly chosen from all other formats defined in this enum.
+ RANDOM = 2;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+ // The type of data in body.
+ optional PayloadType type = 1;
+ // Primary contents of payload.
+ optional bytes body = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ optional PayloadType response_type = 1;
+
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 response_size = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+
+ // Whether SimpleResponse should include username.
+ optional bool fill_username = 4;
+
+ // Whether SimpleResponse should include OAuth scope.
+ optional bool fill_oauth_scope = 5;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+ // Payload to increase message size.
+ optional Payload payload = 1;
+
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ optional string username = 2;
+
+ // OAuth scope.
+ optional string oauth_scope = 3;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+ // Optional input payload sent along with the request.
+ optional Payload payload = 1;
+
+ // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+ // Aggregated size of payloads received from the client.
+ optional int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 size = 1;
+
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ optional int32 interval_us = 2;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ optional PayloadType response_type = 1;
+
+ // Configuration for each expected response message.
+ repeated ResponseParameters response_parameters = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+ // Payload to increase response size.
+ optional Payload payload = 1;
+}
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+ // One empty request followed by one empty response.
+ rpc EmptyCall(Empty) returns (Empty);
+
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ rpc StreamingOutputCall(StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ rpc StreamingInputCall(stream StreamingInputCallRequest)
+ returns (StreamingInputCallResponse);
+
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ rpc FullDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/server.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/server.go
new file mode 100644
index 000000000..b633716d1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/server.go
@@ -0,0 +1,209 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/grpc_testing"
+)
+
+var (
+ useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
+ certFile = flag.String("tls_cert_file", "testdata/server1.pem", "The TLS cert file")
+ keyFile = flag.String("tls_key_file", "testdata/server1.key", "The TLS key file")
+ port = flag.Int("port", 10000, "The server port")
+)
+
+type testServer struct {
+}
+
+func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
+ return new(testpb.Empty), nil
+}
+
+func newPayload(t testpb.PayloadType, size int32) (*testpb.Payload, error) {
+ if size < 0 {
+ return nil, fmt.Errorf("requested a response with invalid length %d", size)
+ }
+ body := make([]byte, size)
+ switch t {
+ case testpb.PayloadType_COMPRESSABLE:
+ case testpb.PayloadType_UNCOMPRESSABLE:
+ return nil, fmt.Errorf("payloadType UNCOMPRESSABLE is not supported")
+ default:
+ return nil, fmt.Errorf("unsupported payload type: %d", t)
+ }
+ return &testpb.Payload{
+ Type: t.Enum(),
+ Body: body,
+ }, nil
+}
+
+func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
+ pl, err := newPayload(in.GetResponseType(), in.GetResponseSize())
+ if err != nil {
+ return nil, err
+ }
+ return &testpb.SimpleResponse{
+ Payload: pl,
+ }, nil
+}
+
+func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testpb.TestService_StreamingOutputCallServer) error {
+ cs := args.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ pl, err := newPayload(args.GetResponseType(), c.GetSize())
+ if err != nil {
+ return err
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: pl,
+ }); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *testServer) StreamingInputCall(stream testpb.TestService_StreamingInputCallServer) error {
+ var sum int
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ return stream.SendAndClose(&testpb.StreamingInputCallResponse{
+ AggregatedPayloadSize: proto.Int32(int32(sum)),
+ })
+ }
+ if err != nil {
+ return err
+ }
+ p := in.GetPayload().GetBody()
+ sum += len(p)
+ }
+}
+
+func (s *testServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error {
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ cs := in.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ pl, err := newPayload(in.GetResponseType(), c.GetSize())
+ if err != nil {
+ return err
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: pl,
+ }); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServer) error {
+ var msgBuf []*testpb.StreamingOutputCallRequest
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ break
+ }
+ if err != nil {
+ return err
+ }
+ msgBuf = append(msgBuf, in)
+ }
+ for _, m := range msgBuf {
+ cs := m.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ pl, err := newPayload(m.GetResponseType(), c.GetSize())
+ if err != nil {
+ return err
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: pl,
+ }); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func main() {
+ flag.Parse()
+ p := strconv.Itoa(*port)
+ lis, err := net.Listen("tcp", ":"+p)
+ if err != nil {
+ grpclog.Fatalf("failed to listen: %v", err)
+ }
+ var opts []grpc.ServerOption
+ if *useTLS {
+ creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
+ if err != nil {
+ grpclog.Fatalf("Failed to generate credentials %v", err)
+ }
+ opts = []grpc.ServerOption{grpc.Creds(creds)}
+ }
+ server := grpc.NewServer(opts...)
+ testpb.RegisterTestServiceServer(server, &testServer{})
+ server.Serve(lis)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/ca.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/ca.pem
new file mode 100644
index 000000000..999da8977
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
+NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
+Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
+cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
+xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
+QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
+AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
+fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
+5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.key
new file mode 100644
index 000000000..ed00cd500
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
+bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
+BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
+AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
+ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
+omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
+ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
+9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
+MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
+IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
+USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
+VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
+RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.pem
new file mode 100644
index 000000000..8e582e571
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/interop/server/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata.go
new file mode 100644
index 000000000..5f06b69e5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata.go
@@ -0,0 +1,144 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+// Package metadata define the structure of the metadata supported by gRPC library.
+package metadata
+
+import (
+ "encoding/base64"
+ "fmt"
+ "strings"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+const (
+ binHdrSuffix = "-bin"
+)
+
+// grpc-http2 requires ASCII header key and value (more detail can be found in
+// "Requests" subsection in go/grpc-http2).
+func isASCII(s string) bool {
+ for _, c := range s {
+ if c > 127 {
+ return false
+ }
+ }
+ return true
+}
+
+// encodeKeyValue encodes key and value qualified for transmission via gRPC.
+// Transmitting binary headers violates HTTP/2 spec.
+// TODO(zhaoq): Maybe check if k is ASCII also.
+func encodeKeyValue(k, v string) (string, string) {
+ if isASCII(v) {
+ return k, v
+ }
+ key := k + binHdrSuffix
+ val := base64.StdEncoding.EncodeToString([]byte(v))
+ return key, string(val)
+}
+
+// DecodeKeyValue returns the original key and value corresponding to the
+// encoded data in k, v.
+func DecodeKeyValue(k, v string) (string, string, error) {
+ if !strings.HasSuffix(k, binHdrSuffix) {
+ return k, v, nil
+ }
+ key := k[:len(k)-len(binHdrSuffix)]
+ val, err := base64.StdEncoding.DecodeString(v)
+ if err != nil {
+ return "", "", err
+ }
+ return key, string(val), nil
+}
+
+// MD is a mapping from metadata keys to values. Users should use the following
+// two convenience functions New and Pairs to generate MD.
+type MD map[string]string
+
+// New creates a MD from given key-value map.
+func New(m map[string]string) MD {
+ md := MD{}
+ for k, v := range m {
+ key, val := encodeKeyValue(k, v)
+ md[key] = val
+ }
+ return md
+}
+
+// Pairs returns an MD formed by the mapping of key, value ...
+// Pairs panics if len(kv) is odd.
+func Pairs(kv ...string) MD {
+ if len(kv)%2 == 1 {
+ panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
+ }
+ md := MD{}
+ var k string
+ for i, s := range kv {
+ if i%2 == 0 {
+ k = s
+ continue
+ }
+ key, val := encodeKeyValue(k, s)
+ md[key] = val
+ }
+ return md
+}
+
+// Len returns the number of items in md.
+func (md MD) Len() int {
+ return len(md)
+}
+
+// Copy returns a copy of md.
+func (md MD) Copy() MD {
+ out := MD{}
+ for k, v := range md {
+ out[k] = v
+ }
+ return out
+}
+
+type mdKey struct{}
+
+// NewContext creates a new context with md attached.
+func NewContext(ctx context.Context, md MD) context.Context {
+ return context.WithValue(ctx, mdKey{}, md)
+}
+
+// FromContext returns the MD in ctx if it exists.
+func FromContext(ctx context.Context) (md MD, ok bool) {
+ md, ok = ctx.Value(mdKey{}).(MD)
+ return
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata_test.go
new file mode 100644
index 000000000..ef3a1b8c4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata/metadata_test.go
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package metadata
+
+import (
+ "reflect"
+ "testing"
+)
+
+const binaryValue = string(128)
+
+func TestDecodeKeyValue(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ kin string
+ vin string
+ // output
+ kout string
+ vout string
+ err error
+ }{
+ {"a", "abc", "a", "abc", nil},
+ {"key-bin", "Zm9vAGJhcg==", "key", "foo\x00bar", nil},
+ {"key-bin", "woA=", "key", binaryValue, nil},
+ } {
+ k, v, err := DecodeKeyValue(test.kin, test.vin)
+ if k != test.kout || !reflect.DeepEqual(v, test.vout) || !reflect.DeepEqual(err, test.err) {
+ t.Fatalf("DecodeKeyValue(%q, %q) = %q, %q, %v, want %q, %q, %v", test.kin, test.vin, k, v, err, test.kout, test.vout, test.err)
+ }
+ }
+}
+
+func TestPairsMD(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ kv []string
+ // output
+ md MD
+ }{
+ {[]string{}, MD{}},
+ {[]string{"k1", "v1", "k2", binaryValue}, New(map[string]string{
+ "k1": "v1",
+ "k2-bin": "woA=",
+ })},
+ } {
+ md := Pairs(test.kv...)
+ if !reflect.DeepEqual(md, test.md) {
+ t.Fatalf("Pairs(%v) = %v, want %v", test.kv, md, test.md)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go
new file mode 100644
index 000000000..c1324aa03
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/rpc_util.go
@@ -0,0 +1,306 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "math/rand"
+ "os"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
+)
+
+// Codec defines the interface gRPC uses to encode and decode messages.
+type Codec interface {
+ // Marshal returns the wire format of v.
+ Marshal(v interface{}) ([]byte, error)
+ // Unmarshal parses the wire format into v.
+ Unmarshal(data []byte, v interface{}) error
+ // String returns the name of the Codec implementation. The returned
+ // string will be used as part of content type in transmission.
+ String() string
+}
+
+// protoCodec is a Codec implemetation with protobuf. It is the default codec for gRPC.
+type protoCodec struct{}
+
+func (protoCodec) Marshal(v interface{}) ([]byte, error) {
+ return proto.Marshal(v.(proto.Message))
+}
+
+func (protoCodec) Unmarshal(data []byte, v interface{}) error {
+ return proto.Unmarshal(data, v.(proto.Message))
+}
+
+func (protoCodec) String() string {
+ return "proto"
+}
+
+// CallOption configures a Call before it starts or extracts information from
+// a Call after it completes.
+type CallOption interface {
+ // before is called before the call is sent to any server. If before
+ // returns a non-nil error, the RPC fails with that error.
+ before(*callInfo) error
+
+ // after is called after the call has completed. after cannot return an
+ // error, so any failures should be reported via output parameters.
+ after(*callInfo)
+}
+
+type beforeCall func(c *callInfo) error
+
+func (o beforeCall) before(c *callInfo) error { return o(c) }
+func (o beforeCall) after(c *callInfo) {}
+
+type afterCall func(c *callInfo)
+
+func (o afterCall) before(c *callInfo) error { return nil }
+func (o afterCall) after(c *callInfo) { o(c) }
+
+// Header returns a CallOptions that retrieves the header metadata
+// for a unary RPC.
+func Header(md *metadata.MD) CallOption {
+ return afterCall(func(c *callInfo) {
+ *md = c.headerMD
+ })
+}
+
+// Trailer returns a CallOptions that retrieves the trailer metadata
+// for a unary RPC.
+func Trailer(md *metadata.MD) CallOption {
+ return afterCall(func(c *callInfo) {
+ *md = c.trailerMD
+ })
+}
+
+// The format of the payload: compressed or not?
+type payloadFormat uint8
+
+const (
+ compressionNone payloadFormat = iota // no compression
+ compressionFlate
+ // More formats
+)
+
+// parser reads complelete gRPC messages from the underlying reader.
+type parser struct {
+ s io.Reader
+}
+
+// msgFixedHeader defines the header of a gRPC message (go/grpc-wirefmt).
+type msgFixedHeader struct {
+ T payloadFormat
+ Length uint32
+}
+
+// recvMsg is to read a complete gRPC message from the stream. It is blocking if
+// the message has not been complete yet. It returns the message and its type,
+// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
+// non-nil error is returned if something is wrong on reading.
+func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
+ var hdr msgFixedHeader
+ if err := binary.Read(p.s, binary.BigEndian, &hdr); err != nil {
+ return 0, nil, err
+ }
+ if hdr.Length == 0 {
+ return hdr.T, nil, nil
+ }
+ msg = make([]byte, int(hdr.Length))
+ if _, err := io.ReadFull(p.s, msg); err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return 0, nil, err
+ }
+ return hdr.T, msg, nil
+}
+
+// encode serializes msg and prepends the message header. If msg is nil, it
+// generates the message header of 0 message length.
+func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
+ var buf bytes.Buffer
+ // Write message fixed header.
+ buf.WriteByte(uint8(pf))
+ var b []byte
+ var length uint32
+ if msg != nil {
+ var err error
+ // TODO(zhaoq): optimize to reduce memory alloc and copying.
+ b, err = c.Marshal(msg)
+ if err != nil {
+ return nil, err
+ }
+ length = uint32(len(b))
+ }
+ var szHdr [4]byte
+ binary.BigEndian.PutUint32(szHdr[:], length)
+ buf.Write(szHdr[:])
+ buf.Write(b)
+ return buf.Bytes(), nil
+}
+
+func recv(p *parser, c Codec, m interface{}) error {
+ pf, d, err := p.recvMsg()
+ if err != nil {
+ return err
+ }
+ switch pf {
+ case compressionNone:
+ if err := c.Unmarshal(d, m); err != nil {
+ return Errorf(codes.Internal, "grpc: %v", err)
+ }
+ default:
+ return Errorf(codes.Internal, "gprc: compression is not supported yet.")
+ }
+ return nil
+}
+
+// rpcError defines the status from an RPC.
+type rpcError struct {
+ code codes.Code
+ desc string
+}
+
+func (e rpcError) Error() string {
+ return fmt.Sprintf("rpc error: code = %d desc = %q", e.code, e.desc)
+}
+
+// Code returns the error code for err if it was produced by the rpc system.
+// Otherwise, it returns codes.Unknown.
+func Code(err error) codes.Code {
+ if err == nil {
+ return codes.OK
+ }
+ if e, ok := err.(rpcError); ok {
+ return e.code
+ }
+ return codes.Unknown
+}
+
+// Errorf returns an error containing an error code and a description;
+// Errorf returns nil if c is OK.
+func Errorf(c codes.Code, format string, a ...interface{}) error {
+ if c == codes.OK {
+ return nil
+ }
+ return rpcError{
+ code: c,
+ desc: fmt.Sprintf(format, a...),
+ }
+}
+
+// toRPCErr converts an error into a rpcError.
+func toRPCErr(err error) error {
+ switch e := err.(type) {
+ case transport.StreamError:
+ return rpcError{
+ code: e.Code,
+ desc: e.Desc,
+ }
+ case transport.ConnectionError:
+ return rpcError{
+ code: codes.Internal,
+ desc: e.Desc,
+ }
+ }
+ return Errorf(codes.Unknown, "%v", err)
+}
+
+// convertCode converts a standard Go error into its canonical code. Note that
+// this is only used to translate the error returned by the server applications.
+func convertCode(err error) codes.Code {
+ switch err {
+ case nil:
+ return codes.OK
+ case io.EOF:
+ return codes.OutOfRange
+ case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
+ return codes.FailedPrecondition
+ case os.ErrInvalid:
+ return codes.InvalidArgument
+ case context.Canceled:
+ return codes.Canceled
+ case context.DeadlineExceeded:
+ return codes.DeadlineExceeded
+ }
+ switch {
+ case os.IsExist(err):
+ return codes.AlreadyExists
+ case os.IsNotExist(err):
+ return codes.NotFound
+ case os.IsPermission(err):
+ return codes.PermissionDenied
+ }
+ return codes.Unknown
+}
+
+const (
+ // how long to wait after the first failure before retrying
+ baseDelay = 1.0 * time.Second
+ // upper bound on backoff delay
+ maxDelay = 120 * time.Second
+ backoffFactor = 2.0 // backoff increases by this factor on each retry
+ backoffRange = 0.4 // backoff is randomized downwards by this factor
+)
+
+// backoff returns a value in [0, maxDelay] that increases exponentially with
+// retries, starting from baseDelay.
+func backoff(retries int) time.Duration {
+ backoff, max := float64(baseDelay), float64(maxDelay)
+ for backoff < max && retries > 0 {
+ backoff = backoff * backoffFactor
+ retries--
+ }
+ if backoff > max {
+ backoff = max
+ }
+
+ // Randomize backoff delays so that if a cluster of requests start at
+ // the same time, they won't operate in lockstep. We just subtract up
+ // to 40% so that we obey maxDelay.
+ backoff -= backoff * backoffRange * rand.Float64()
+ if backoff < 0 {
+ return 0
+ }
+ return time.Duration(backoff)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/rpc_util_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/rpc_util_test.go
new file mode 100644
index 000000000..372819774
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/rpc_util_test.go
@@ -0,0 +1,211 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+ "bytes"
+ "io"
+ "math"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ perfpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
+)
+
+func TestSimpleParsing(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ p []byte
+ // outputs
+ err error
+ b []byte
+ pt payloadFormat
+ }{
+ {nil, io.EOF, nil, compressionNone},
+ {[]byte{0, 0, 0, 0, 0}, nil, nil, compressionNone},
+ {[]byte{0, 0, 0, 0, 1, 'a'}, nil, []byte{'a'}, compressionNone},
+ {[]byte{1, 0}, io.ErrUnexpectedEOF, nil, compressionNone},
+ {[]byte{0, 0, 0, 0, 10, 'a'}, io.ErrUnexpectedEOF, nil, compressionNone},
+ } {
+ buf := bytes.NewReader(test.p)
+ parser := &parser{buf}
+ pt, b, err := parser.recvMsg()
+ if err != test.err || !bytes.Equal(b, test.b) || pt != test.pt {
+ t.Fatalf("parser{%v}.recvMsg() = %v, %v, %v\nwant %v, %v, %v", test.p, pt, b, err, test.pt, test.b, test.err)
+ }
+ }
+}
+
+func TestMultipleParsing(t *testing.T) {
+ // Set a byte stream consists of 3 messages with their headers.
+ p := []byte{0, 0, 0, 0, 1, 'a', 0, 0, 0, 0, 2, 'b', 'c', 0, 0, 0, 0, 1, 'd'}
+ b := bytes.NewReader(p)
+ parser := &parser{b}
+
+ wantRecvs := []struct {
+ pt payloadFormat
+ data []byte
+ }{
+ {compressionNone, []byte("a")},
+ {compressionNone, []byte("bc")},
+ {compressionNone, []byte("d")},
+ }
+ for i, want := range wantRecvs {
+ pt, data, err := parser.recvMsg()
+ if err != nil || pt != want.pt || !reflect.DeepEqual(data, want.data) {
+ t.Fatalf("after %d calls, parser{%v}.recvMsg() = %v, %v, %v\nwant %v, %v, ",
+ i, p, pt, data, err, want.pt, want.data)
+ }
+ }
+
+ pt, data, err := parser.recvMsg()
+ if err != io.EOF {
+ t.Fatalf("after %d recvMsgs calls, parser{%v}.recvMsg() = %v, %v, %v\nwant _, _, %v",
+ len(wantRecvs), p, pt, data, err, io.EOF)
+ }
+}
+
+func TestEncode(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ msg proto.Message
+ pt payloadFormat
+ // outputs
+ b []byte
+ err error
+ }{
+ {nil, compressionNone, []byte{0, 0, 0, 0, 0}, nil},
+ } {
+ b, err := encode(protoCodec{}, test.msg, test.pt)
+ if err != test.err || !bytes.Equal(b, test.b) {
+ t.Fatalf("encode(_, _, %d) = %v, %v\nwant %v, %v", test.pt, b, err, test.b, test.err)
+ }
+ }
+}
+
+func TestToRPCErr(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ errIn error
+ // outputs
+ errOut error
+ }{
+ {transport.StreamErrorf(codes.Unknown, ""), Errorf(codes.Unknown, "")},
+ {transport.ErrConnClosing, Errorf(codes.Internal, transport.ErrConnClosing.Desc)},
+ } {
+ err := toRPCErr(test.errIn)
+ if err != test.errOut {
+ t.Fatalf("toRPCErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
+ }
+ }
+}
+
+func TestContextErr(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ errIn error
+ // outputs
+ errOut transport.StreamError
+ }{
+ {context.DeadlineExceeded, transport.StreamErrorf(codes.DeadlineExceeded, "%v", context.DeadlineExceeded)},
+ {context.Canceled, transport.StreamErrorf(codes.Canceled, "%v", context.Canceled)},
+ } {
+ err := transport.ContextErr(test.errIn)
+ if err != test.errOut {
+ t.Fatalf("ContextErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
+ }
+ }
+}
+
+func TestBackoff(t *testing.T) {
+ for _, test := range []struct {
+ retries int
+ maxResult time.Duration
+ }{
+ {0, time.Second},
+ {1, time.Duration(1e9 * math.Pow(backoffFactor, 1))},
+ {2, time.Duration(1e9 * math.Pow(backoffFactor, 2))},
+ {3, time.Duration(1e9 * math.Pow(backoffFactor, 3))},
+ {4, time.Duration(1e9 * math.Pow(backoffFactor, 4))},
+ {int(math.Log2(float64(maxDelay)/float64(baseDelay))) + 1, maxDelay},
+ } {
+ delay := backoff(test.retries)
+ if delay < 0 || delay > test.maxResult {
+ t.Errorf("backoff(%d) = %v outside [0, %v]", test.retries, delay, test.maxResult)
+ }
+ }
+}
+
+// bmEncode benchmarks encoding a Protocol Buffer message containing mSize
+// bytes.
+func bmEncode(b *testing.B, mSize int) {
+ msg := &perfpb.Buffer{Body: make([]byte, mSize)}
+ encoded, _ := encode(protoCodec{}, msg, compressionNone)
+ encodedSz := int64(len(encoded))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ encode(protoCodec{}, msg, compressionNone)
+ }
+ b.SetBytes(encodedSz)
+}
+
+func BenchmarkEncode1B(b *testing.B) {
+ bmEncode(b, 1)
+}
+
+func BenchmarkEncode1KiB(b *testing.B) {
+ bmEncode(b, 1024)
+}
+
+func BenchmarkEncode8KiB(b *testing.B) {
+ bmEncode(b, 8*1024)
+}
+
+func BenchmarkEncode64KiB(b *testing.B) {
+ bmEncode(b, 64*1024)
+}
+
+func BenchmarkEncode512KiB(b *testing.B) {
+ bmEncode(b, 512*1024)
+}
+
+func BenchmarkEncode1MiB(b *testing.B) {
+ bmEncode(b, 1024*1024)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/server.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/server.go
new file mode 100644
index 000000000..7f31fa91c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/server.go
@@ -0,0 +1,420 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "reflect"
+ "strings"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
+)
+
+type methodHandler func(srv interface{}, ctx context.Context, codec Codec, buf []byte) (interface{}, error)
+
+// MethodDesc represents an RPC service's method specification.
+type MethodDesc struct {
+ MethodName string
+ Handler methodHandler
+}
+
+// ServiceDesc represents an RPC service's specification.
+type ServiceDesc struct {
+ ServiceName string
+ // The pointer to the service interface. Used to check whether the user
+ // provided implementation satisfies the interface requirements.
+ HandlerType interface{}
+ Methods []MethodDesc
+ Streams []StreamDesc
+}
+
+// service consists of the information of the server serving this service and
+// the methods in this service.
+type service struct {
+ server interface{} // the server for service methods
+ md map[string]*MethodDesc
+ sd map[string]*StreamDesc
+}
+
+// Server is a gRPC server to serve RPC requests.
+type Server struct {
+ opts options
+ mu sync.Mutex
+ lis map[net.Listener]bool
+ conns map[transport.ServerTransport]bool
+ m map[string]*service // service name -> service info
+}
+
+type options struct {
+ creds credentials.Credentials
+ codec Codec
+ maxConcurrentStreams uint32
+}
+
+// A ServerOption sets options.
+type ServerOption func(*options)
+
+// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
+func CustomCodec(codec Codec) ServerOption {
+ return func(o *options) {
+ o.codec = codec
+ }
+}
+
+// MaxConcurrentStreams returns a ServerOption that will apply a limit on the number
+// of concurrent streams to each ServerTransport.
+func MaxConcurrentStreams(n uint32) ServerOption {
+ return func(o *options) {
+ o.maxConcurrentStreams = n
+ }
+}
+
+// Creds returns a ServerOption that sets credentials for server connections.
+func Creds(c credentials.Credentials) ServerOption {
+ return func(o *options) {
+ o.creds = c
+ }
+}
+
+// NewServer creates a gRPC server which has no service registered and has not
+// started to accept requests yet.
+func NewServer(opt ...ServerOption) *Server {
+ var opts options
+ for _, o := range opt {
+ o(&opts)
+ }
+ if opts.codec == nil {
+ // Set the default codec.
+ opts.codec = protoCodec{}
+ }
+ return &Server{
+ lis: make(map[net.Listener]bool),
+ opts: opts,
+ conns: make(map[transport.ServerTransport]bool),
+ m: make(map[string]*service),
+ }
+}
+
+// RegisterService register a service and its implementation to the gRPC
+// server. Called from the IDL generated code. This must be called before
+// invoking Serve.
+func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ // Does some sanity checks.
+ if _, ok := s.m[sd.ServiceName]; ok {
+ grpclog.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
+ }
+ ht := reflect.TypeOf(sd.HandlerType).Elem()
+ st := reflect.TypeOf(ss)
+ if !st.Implements(ht) {
+ grpclog.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
+ }
+ srv := &service{
+ server: ss,
+ md: make(map[string]*MethodDesc),
+ sd: make(map[string]*StreamDesc),
+ }
+ for i := range sd.Methods {
+ d := &sd.Methods[i]
+ srv.md[d.MethodName] = d
+ }
+ for i := range sd.Streams {
+ d := &sd.Streams[i]
+ srv.sd[d.StreamName] = d
+ }
+ s.m[sd.ServiceName] = srv
+}
+
+var (
+ // ErrServerStopped indicates that the operation is now illegal because of
+ // the server being stopped.
+ ErrServerStopped = errors.New("grpc: the server has been stopped")
+)
+
+// Serve accepts incoming connections on the listener lis, creating a new
+// ServerTransport and service goroutine for each. The service goroutines
+// read gRPC request and then call the registered handlers to reply to them.
+// Service returns when lis.Accept fails.
+func (s *Server) Serve(lis net.Listener) error {
+ s.mu.Lock()
+ if s.lis == nil {
+ s.mu.Unlock()
+ return ErrServerStopped
+ }
+ s.lis[lis] = true
+ s.mu.Unlock()
+ defer func() {
+ lis.Close()
+ s.mu.Lock()
+ delete(s.lis, lis)
+ s.mu.Unlock()
+ }()
+ for {
+ c, err := lis.Accept()
+ if err != nil {
+ return err
+ }
+ if creds, ok := s.opts.creds.(credentials.TransportAuthenticator); ok {
+ c, err = creds.ServerHandshake(c)
+ if err != nil {
+ grpclog.Println("grpc: Server.Serve failed to complete security handshake.")
+ continue
+ }
+ }
+ s.mu.Lock()
+ if s.conns == nil {
+ s.mu.Unlock()
+ c.Close()
+ return nil
+ }
+ st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams)
+ if err != nil {
+ s.mu.Unlock()
+ c.Close()
+ grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
+ continue
+ }
+ s.conns[st] = true
+ s.mu.Unlock()
+
+ go func() {
+ st.HandleStreams(func(stream *transport.Stream) {
+ s.handleStream(st, stream)
+ })
+ s.mu.Lock()
+ delete(s.conns, st)
+ s.mu.Unlock()
+ }()
+ }
+}
+
+func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, pf payloadFormat, opts *transport.Options) error {
+ p, err := encode(s.opts.codec, msg, pf)
+ if err != nil {
+ // This typically indicates a fatal issue (e.g., memory
+ // corruption or hardware faults) the application program
+ // cannot handle.
+ //
+ // TODO(zhaoq): There exist other options also such as only closing the
+ // faulty stream locally and remotely (Other streams can keep going). Find
+ // the optimal option.
+ grpclog.Fatalf("grpc: Server failed to encode response %v", err)
+ }
+ return t.Write(stream, p, opts)
+}
+
+func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc) {
+ p := &parser{s: stream}
+ for {
+ pf, req, err := p.recvMsg()
+ if err == io.EOF {
+ // The entire stream is done (for unary RPC only).
+ return
+ }
+ if err != nil {
+ switch err := err.(type) {
+ case transport.ConnectionError:
+ // Nothing to do here.
+ case transport.StreamError:
+ if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
+ }
+ default:
+ panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
+ }
+ return
+ }
+ switch pf {
+ case compressionNone:
+ statusCode := codes.OK
+ statusDesc := ""
+ reply, appErr := md.Handler(srv.server, stream.Context(), s.opts.codec, req)
+ if appErr != nil {
+ if err, ok := appErr.(rpcError); ok {
+ statusCode = err.code
+ statusDesc = err.desc
+ } else {
+ statusCode = convertCode(appErr)
+ statusDesc = appErr.Error()
+ }
+ if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
+ grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
+ }
+ return
+ }
+ opts := &transport.Options{
+ Last: true,
+ Delay: false,
+ }
+ if err := s.sendResponse(t, stream, reply, compressionNone, opts); err != nil {
+ if _, ok := err.(transport.ConnectionError); ok {
+ return
+ }
+ if e, ok := err.(transport.StreamError); ok {
+ statusCode = e.Code
+ statusDesc = e.Desc
+ } else {
+ statusCode = codes.Unknown
+ statusDesc = err.Error()
+ }
+ }
+ t.WriteStatus(stream, statusCode, statusDesc)
+ default:
+ panic(fmt.Sprintf("payload format to be supported: %d", pf))
+ }
+ }
+}
+
+func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc) {
+ ss := &serverStream{
+ t: t,
+ s: stream,
+ p: &parser{s: stream},
+ codec: s.opts.codec,
+ }
+ if appErr := sd.Handler(srv.server, ss); appErr != nil {
+ if err, ok := appErr.(rpcError); ok {
+ ss.statusCode = err.code
+ ss.statusDesc = err.desc
+ } else {
+ ss.statusCode = convertCode(appErr)
+ ss.statusDesc = appErr.Error()
+ }
+ }
+ t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
+}
+
+func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
+ sm := stream.Method()
+ if sm != "" && sm[0] == '/' {
+ sm = sm[1:]
+ }
+ pos := strings.LastIndex(sm, "/")
+ if pos == -1 {
+ if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil {
+ grpclog.Printf("grpc: Server.handleStream failed to write status: %v", err)
+ }
+ return
+ }
+ service := sm[:pos]
+ method := sm[pos+1:]
+ srv, ok := s.m[service]
+ if !ok {
+ if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil {
+ grpclog.Printf("grpc: Server.handleStream failed to write status: %v", err)
+ }
+ return
+ }
+ // Unary RPC or Streaming RPC?
+ if md, ok := srv.md[method]; ok {
+ s.processUnaryRPC(t, stream, srv, md)
+ return
+ }
+ if sd, ok := srv.sd[method]; ok {
+ s.processStreamingRPC(t, stream, srv, sd)
+ return
+ }
+ if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil {
+ grpclog.Printf("grpc: Server.handleStream failed to write status: %v", err)
+ }
+}
+
+// Stop stops the gRPC server. Once Stop returns, the server stops accepting
+// connection requests and closes all the connected connections.
+func (s *Server) Stop() {
+ s.mu.Lock()
+ listeners := s.lis
+ s.lis = nil
+ cs := s.conns
+ s.conns = nil
+ s.mu.Unlock()
+ for lis := range listeners {
+ lis.Close()
+ }
+ for c := range cs {
+ c.Close()
+ }
+}
+
+// TestingCloseConns closes all exiting transports but keeps s.lis accepting new
+// connections. This is for test only now.
+func (s *Server) TestingCloseConns() {
+ s.mu.Lock()
+ for c := range s.conns {
+ c.Close()
+ }
+ s.conns = make(map[transport.ServerTransport]bool)
+ s.mu.Unlock()
+}
+
+// SendHeader sends header metadata. It may be called at most once from a unary
+// RPC handler. The ctx is the RPC handler's Context or one derived from it.
+func SendHeader(ctx context.Context, md metadata.MD) error {
+ if md.Len() == 0 {
+ return nil
+ }
+ stream, ok := transport.StreamFromContext(ctx)
+ if !ok {
+ return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
+ }
+ t := stream.ServerTransport()
+ if t == nil {
+ grpclog.Fatalf("grpc: SendHeader: %v has no ServerTransport to send header metadata.", stream)
+ }
+ return t.WriteHeader(stream, md)
+}
+
+// SetTrailer sets the trailer metadata that will be sent when an RPC returns.
+// It may be called at most once from a unary RPC handler. The ctx is the RPC
+// handler's Context or one derived from it.
+func SetTrailer(ctx context.Context, md metadata.MD) error {
+ if md.Len() == 0 {
+ return nil
+ }
+ stream, ok := transport.StreamFromContext(ctx)
+ if !ok {
+ return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
+ }
+ return stream.SetTrailer(md)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/stream.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/stream.go
new file mode 100644
index 000000000..c8ba648dd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/stream.go
@@ -0,0 +1,256 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc
+
+import (
+ "errors"
+ "io"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport"
+)
+
+type streamHandler func(srv interface{}, stream ServerStream) error
+
+// StreamDesc represents a streaming RPC service's method specification.
+type StreamDesc struct {
+ StreamName string
+ Handler streamHandler
+
+ // At least one of these is true.
+ ServerStreams bool
+ ClientStreams bool
+}
+
+// Stream defines the common interface a client or server stream has to satisfy.
+type Stream interface {
+ // Context returns the context for this stream.
+ Context() context.Context
+ // SendMsg blocks until it sends m, the stream is done or the stream
+ // breaks.
+ // On error, it aborts the stream and returns an RPC status on client
+ // side. On server side, it simply returns the error to the caller.
+ // SendMsg is called by generated code.
+ SendMsg(m interface{}) error
+ // RecvMsg blocks until it receives a message or the stream is
+ // done. On client side, it returns io.EOF when the stream is done. On
+ // any other error, it aborts the streama nd returns an RPC status. On
+ // server side, it simply returns the error to the caller.
+ RecvMsg(m interface{}) error
+}
+
+// ClientStream defines the interface a client stream has to satify.
+type ClientStream interface {
+ // Header returns the header metedata received from the server if there
+ // is any. It blocks if the metadata is not ready to read.
+ Header() (metadata.MD, error)
+ // Trailer returns the trailer metadata from the server. It must be called
+ // after stream.Recv() returns non-nil error (including io.EOF) for
+ // bi-directional streaming and server streaming or stream.CloseAndRecv()
+ // returns for client streaming in order to receive trailer metadata if
+ // present. Otherwise, it could returns an empty MD even though trailer
+ // is present.
+ Trailer() metadata.MD
+ // CloseSend closes the send direction of the stream. It closes the stream
+ // when non-nil error is met.
+ CloseSend() error
+ Stream
+}
+
+// NewClientStream creates a new Stream for the client side. This is called
+// by generated code.
+func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) {
+ // TODO(zhaoq): CallOption is omitted. Add support when it is needed.
+ callHdr := &transport.CallHdr{
+ Host: cc.authority,
+ Method: method,
+ }
+ t, _, err := cc.wait(ctx, 0)
+ if err != nil {
+ return nil, toRPCErr(err)
+ }
+ s, err := t.NewStream(ctx, callHdr)
+ if err != nil {
+ return nil, toRPCErr(err)
+ }
+ return &clientStream{
+ t: t,
+ s: s,
+ p: &parser{s: s},
+ desc: desc,
+ codec: cc.dopts.codec,
+ }, nil
+}
+
+// clientStream implements a client side Stream.
+type clientStream struct {
+ t transport.ClientTransport
+ s *transport.Stream
+ p *parser
+ desc *StreamDesc
+ codec Codec
+}
+
+func (cs *clientStream) Context() context.Context {
+ return cs.s.Context()
+}
+
+func (cs *clientStream) Header() (metadata.MD, error) {
+ m, err := cs.s.Header()
+ if err != nil {
+ if _, ok := err.(transport.ConnectionError); !ok {
+ cs.t.CloseStream(cs.s, err)
+ }
+ }
+ return m, err
+}
+
+func (cs *clientStream) Trailer() metadata.MD {
+ return cs.s.Trailer()
+}
+
+func (cs *clientStream) SendMsg(m interface{}) (err error) {
+ defer func() {
+ if err == nil || err == io.EOF {
+ return
+ }
+ if _, ok := err.(transport.ConnectionError); !ok {
+ cs.t.CloseStream(cs.s, err)
+ }
+ err = toRPCErr(err)
+ }()
+ out, err := encode(cs.codec, m, compressionNone)
+ if err != nil {
+ return transport.StreamErrorf(codes.Internal, "grpc: %v", err)
+ }
+ return cs.t.Write(cs.s, out, &transport.Options{Last: false})
+}
+
+func (cs *clientStream) RecvMsg(m interface{}) (err error) {
+ err = recv(cs.p, cs.codec, m)
+ if err == nil {
+ if !cs.desc.ClientStreams || cs.desc.ServerStreams {
+ return
+ }
+ // Special handling for client streaming rpc.
+ err = recv(cs.p, cs.codec, m)
+ cs.t.CloseStream(cs.s, err)
+ if err == nil {
+ return toRPCErr(errors.New("grpc: client streaming protocol violation: get , want "))
+ }
+ if err == io.EOF {
+ if cs.s.StatusCode() == codes.OK {
+ return nil
+ }
+ return Errorf(cs.s.StatusCode(), cs.s.StatusDesc())
+ }
+ return toRPCErr(err)
+ }
+ if _, ok := err.(transport.ConnectionError); !ok {
+ cs.t.CloseStream(cs.s, err)
+ }
+ if err == io.EOF {
+ if cs.s.StatusCode() == codes.OK {
+ // Returns io.EOF to indicate the end of the stream.
+ return
+ }
+ return Errorf(cs.s.StatusCode(), cs.s.StatusDesc())
+ }
+ return toRPCErr(err)
+}
+
+func (cs *clientStream) CloseSend() (err error) {
+ err = cs.t.Write(cs.s, nil, &transport.Options{Last: true})
+ if err == nil || err == io.EOF {
+ return
+ }
+ if _, ok := err.(transport.ConnectionError); !ok {
+ cs.t.CloseStream(cs.s, err)
+ }
+ err = toRPCErr(err)
+ return
+}
+
+// ServerStream defines the interface a server stream has to satisfy.
+type ServerStream interface {
+ // SendHeader sends the header metadata. It should not be called
+ // after SendProto. It fails if called multiple times or if
+ // called after SendProto.
+ SendHeader(metadata.MD) error
+ // SetTrailer sets the trailer metadata which will be sent with the
+ // RPC status.
+ SetTrailer(metadata.MD)
+ Stream
+}
+
+// serverStream implements a server side Stream.
+type serverStream struct {
+ t transport.ServerTransport
+ s *transport.Stream
+ p *parser
+ codec Codec
+ statusCode codes.Code
+ statusDesc string
+}
+
+func (ss *serverStream) Context() context.Context {
+ return ss.s.Context()
+}
+
+func (ss *serverStream) SendHeader(md metadata.MD) error {
+ return ss.t.WriteHeader(ss.s, md)
+}
+
+func (ss *serverStream) SetTrailer(md metadata.MD) {
+ if md.Len() == 0 {
+ return
+ }
+ ss.s.SetTrailer(md)
+ return
+}
+
+func (ss *serverStream) SendMsg(m interface{}) error {
+ out, err := encode(ss.codec, m, compressionNone)
+ if err != nil {
+ err = transport.StreamErrorf(codes.Internal, "grpc: %v", err)
+ return err
+ }
+ return ss.t.Write(ss.s, out, &transport.Options{Last: false})
+}
+
+func (ss *serverStream) RecvMsg(m interface{}) error {
+ return recv(ss.p, ss.codec, m)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go
new file mode 100644
index 000000000..8ad1a33aa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.pb.go
@@ -0,0 +1,42 @@
+// Code generated by protoc-gen-go.
+// source: perf.proto
+// DO NOT EDIT!
+
+/*
+Package codec_perf is a generated protocol buffer package.
+
+It is generated from these files:
+ perf.proto
+
+It has these top-level messages:
+ Buffer
+*/
+package codec_perf
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+// Buffer is a message that contains a body of bytes that is used to exercise
+// encoding and decoding overheads.
+type Buffer struct {
+ Body []byte `protobuf:"bytes,1,opt,name=body" json:"body,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Buffer) Reset() { *m = Buffer{} }
+func (m *Buffer) String() string { return proto.CompactTextString(m) }
+func (*Buffer) ProtoMessage() {}
+
+func (m *Buffer) GetBody() []byte {
+ if m != nil {
+ return m.Body
+ }
+ return nil
+}
+
+func init() {
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.proto
new file mode 100644
index 000000000..4cf561f7f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/codec_perf/perf.proto
@@ -0,0 +1,11 @@
+// Messages used for performance tests that may not reference grpc directly for
+// reasons of import cycles.
+syntax = "proto2";
+
+package codec.perf;
+
+// Buffer is a message that contains a body of bytes that is used to exercise
+// encoding and decoding overheads.
+message Buffer {
+ optional bytes body = 1;
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/end2end_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/end2end_test.go
new file mode 100644
index 000000000..568330417
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/end2end_test.go
@@ -0,0 +1,811 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package grpc_test
+
+import (
+ "fmt"
+ "io"
+ "math"
+ "net"
+ "reflect"
+ "runtime"
+ "sync"
+ "syscall"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+ testpb "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing"
+)
+
+var (
+ testMetadata = metadata.MD{
+ "key1": "value1",
+ "key2": "value2",
+ }
+)
+
+type testServer struct {
+}
+
+func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
+ if _, ok := metadata.FromContext(ctx); ok {
+ // For testing purpose, returns an error if there is attached metadata.
+ return nil, grpc.Errorf(codes.DataLoss, "got extra metadata")
+ }
+ return new(testpb.Empty), nil
+}
+
+func newPayload(t testpb.PayloadType, size int32) *testpb.Payload {
+ if size < 0 {
+ grpclog.Fatalf("Requested a response with invalid length %d", size)
+ }
+ body := make([]byte, size)
+ switch t {
+ case testpb.PayloadType_COMPRESSABLE:
+ case testpb.PayloadType_UNCOMPRESSABLE:
+ grpclog.Fatalf("PayloadType UNCOMPRESSABLE is not supported")
+ default:
+ grpclog.Fatalf("Unsupported payload type: %d", t)
+ }
+ return &testpb.Payload{
+ Type: t.Enum(),
+ Body: body,
+ }
+}
+
+func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
+ md, ok := metadata.FromContext(ctx)
+ if ok {
+ if err := grpc.SendHeader(ctx, md); err != nil {
+ grpclog.Fatalf("grpc.SendHeader(%v, %v) = %v, want %v", ctx, md, err, nil)
+ }
+ grpc.SetTrailer(ctx, md)
+ }
+ // Simulate some service delay.
+ time.Sleep(2 * time.Millisecond)
+ return &testpb.SimpleResponse{
+ Payload: newPayload(in.GetResponseType(), in.GetResponseSize()),
+ }, nil
+}
+
+func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testpb.TestService_StreamingOutputCallServer) error {
+ if _, ok := metadata.FromContext(stream.Context()); ok {
+ // For testing purpose, returns an error if there is attached metadata.
+ return grpc.Errorf(codes.DataLoss, "got extra metadata")
+ }
+ cs := args.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: newPayload(args.GetResponseType(), c.GetSize()),
+ }); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *testServer) StreamingInputCall(stream testpb.TestService_StreamingInputCallServer) error {
+ var sum int
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ return stream.SendAndClose(&testpb.StreamingInputCallResponse{
+ AggregatedPayloadSize: proto.Int32(int32(sum)),
+ })
+ }
+ if err != nil {
+ return err
+ }
+ p := in.GetPayload().GetBody()
+ sum += len(p)
+ }
+}
+
+func (s *testServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error {
+ md, ok := metadata.FromContext(stream.Context())
+ if ok {
+ if err := stream.SendHeader(md); err != nil {
+ grpclog.Fatalf("%v.SendHeader(%v) = %v, want %v", stream, md, err, nil)
+ }
+ stream.SetTrailer(md)
+ }
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ cs := in.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: newPayload(in.GetResponseType(), c.GetSize()),
+ }); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServer) error {
+ msgBuf := make([]*testpb.StreamingOutputCallRequest, 0)
+ for {
+ in, err := stream.Recv()
+ if err == io.EOF {
+ // read done.
+ break
+ }
+ if err != nil {
+ return err
+ }
+ msgBuf = append(msgBuf, in)
+ }
+ for _, m := range msgBuf {
+ cs := m.GetResponseParameters()
+ for _, c := range cs {
+ if us := c.GetIntervalUs(); us > 0 {
+ time.Sleep(time.Duration(us) * time.Microsecond)
+ }
+ if err := stream.Send(&testpb.StreamingOutputCallResponse{
+ Payload: newPayload(m.GetResponseType(), c.GetSize()),
+ }); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+const tlsDir = "testdata/"
+
+func TestDialTimeout(t *testing.T) {
+ conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTimeout(time.Millisecond))
+ if err == nil {
+ conn.Close()
+ }
+ if err != grpc.ErrClientConnTimeout {
+ t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout)
+ }
+}
+
+func TestTLSDialTimeout(t *testing.T) {
+ creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
+ if err != nil {
+ t.Fatalf("Failed to create credentials %v", err)
+ }
+ conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTransportCredentials(creds), grpc.WithTimeout(time.Millisecond))
+ if err == nil {
+ conn.Close()
+ }
+ if err != grpc.ErrClientConnTimeout {
+ t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout)
+ }
+}
+
+func TestReconnectTimeout(t *testing.T) {
+ lis, err := net.Listen("tcp", ":0")
+ if err != nil {
+ t.Fatalf("Failed to listen: %v", err)
+ }
+ _, port, err := net.SplitHostPort(lis.Addr().String())
+ if err != nil {
+ t.Fatalf("Failed to parse listener address: %v", err)
+ }
+ addr := "localhost:" + port
+ conn, err := grpc.Dial(addr, grpc.WithTimeout(5*time.Second))
+ if err != nil {
+ t.Fatalf("Failed to dial to the server %q: %v", addr, err)
+ }
+ // Close unaccepted connection (i.e., conn).
+ lis.Close()
+ tc := testpb.NewTestServiceClient(conn)
+ waitC := make(chan struct{})
+ go func() {
+ defer close(waitC)
+ argSize := 271828
+ respSize := 314159
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize)),
+ }
+ if _, err := tc.UnaryCall(context.Background(), req); err == nil {
+ t.Fatalf("TestService/UnaryCall(_, _) = _, , want _, non-nil")
+ }
+ }()
+ // Block untill reconnect times out.
+ <-waitC
+ if err := conn.Close(); err != grpc.ErrClientConnClosing {
+ t.Fatalf("%v.Close() = %v, want %v", conn, err, grpc.ErrClientConnClosing)
+ }
+}
+
+func unixDialer(addr string, timeout time.Duration) (net.Conn, error) {
+ return net.DialTimeout("unix", addr, timeout)
+}
+
+type env struct {
+ network string // The type of network such as tcp, unix, etc.
+ dialer func(addr string, timeout time.Duration) (net.Conn, error)
+ security string // The security protocol such as TLS, SSH, etc.
+}
+
+func listTestEnv() []env {
+ if runtime.GOOS == "windows" {
+ return []env{env{"tcp", nil, ""}, env{"tcp", nil, "tls"}}
+ }
+ return []env{env{"tcp", nil, ""}, env{"tcp", nil, "tls"}, env{"unix", unixDialer, ""}, env{"unix", unixDialer, "tls"}}
+}
+
+func setUp(maxStream uint32, e env) (s *grpc.Server, cc *grpc.ClientConn) {
+ sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream)}
+ la := ":0"
+ switch e.network {
+ case "unix":
+ la = "/tmp/testsock" + fmt.Sprintf("%d", time.Now())
+ syscall.Unlink(la)
+ }
+ lis, err := net.Listen(e.network, la)
+ if err != nil {
+ grpclog.Fatalf("Failed to listen: %v", err)
+ }
+ if e.security == "tls" {
+ creds, err := credentials.NewServerTLSFromFile(tlsDir+"server1.pem", tlsDir+"server1.key")
+ if err != nil {
+ grpclog.Fatalf("Failed to generate credentials %v", err)
+ }
+ sopts = append(sopts, grpc.Creds(creds))
+ }
+ s = grpc.NewServer(sopts...)
+ testpb.RegisterTestServiceServer(s, &testServer{})
+ go s.Serve(lis)
+ addr := la
+ switch e.network {
+ case "unix":
+ default:
+ _, port, err := net.SplitHostPort(lis.Addr().String())
+ if err != nil {
+ grpclog.Fatalf("Failed to parse listener address: %v", err)
+ }
+ addr = "localhost:" + port
+ }
+ if e.security == "tls" {
+ creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
+ if err != nil {
+ grpclog.Fatalf("Failed to create credentials %v", err)
+ }
+ cc, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer))
+ } else {
+ cc, err = grpc.Dial(addr, grpc.WithDialer(e.dialer))
+ }
+ if err != nil {
+ grpclog.Fatalf("Dial(%q) = %v", addr, err)
+ }
+ return
+}
+
+func tearDown(s *grpc.Server, cc *grpc.ClientConn) {
+ cc.Close()
+ s.Stop()
+}
+
+func TestTimeoutOnDeadServer(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testTimeoutOnDeadServer(t, e)
+ }
+}
+
+func testTimeoutOnDeadServer(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ s.Stop()
+ // Set -1 as the timeout to make sure if transportMonitor gets error
+ // notification in time the failure path of the 1st invoke of
+ // ClientConn.wait hits the deadline exceeded error.
+ ctx, _ := context.WithTimeout(context.Background(), -1)
+ if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); grpc.Code(err) != codes.DeadlineExceeded {
+ t.Fatalf("TestService/EmptyCall(%v, _) = _, error %v, want _, error code: %d", ctx, err, codes.DeadlineExceeded)
+ }
+ cc.Close()
+}
+
+func TestEmptyUnary(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testEmptyUnary(t, e)
+ }
+}
+
+func testEmptyUnary(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ reply, err := tc.EmptyCall(context.Background(), &testpb.Empty{})
+ if err != nil || !proto.Equal(&testpb.Empty{}, reply) {
+ t.Fatalf("TestService/EmptyCall(_, _) = %v, %v, want %v, ", reply, err, &testpb.Empty{})
+ }
+}
+
+func TestFailedEmptyUnary(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testFailedEmptyUnary(t, e)
+ }
+}
+
+func testFailedEmptyUnary(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ ctx := metadata.NewContext(context.Background(), testMetadata)
+ if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != grpc.Errorf(codes.DataLoss, "got extra metadata") {
+ t.Fatalf("TestService/EmptyCall(_, _) = _, %v, want _, %v", err, grpc.Errorf(codes.DataLoss, "got extra metadata"))
+ }
+}
+
+func TestLargeUnary(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testLargeUnary(t, e)
+ }
+}
+
+func testLargeUnary(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ argSize := 271828
+ respSize := 314159
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize)),
+ }
+ reply, err := tc.UnaryCall(context.Background(), req)
+ if err != nil {
+ t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
+ }
+ pt := reply.GetPayload().GetType()
+ ps := len(reply.GetPayload().GetBody())
+ if pt != testpb.PayloadType_COMPRESSABLE || ps != respSize {
+ t.Fatalf("Got the reply with type %d len %d; want %d, %d", pt, ps, testpb.PayloadType_COMPRESSABLE, respSize)
+ }
+}
+
+func TestMetadataUnaryRPC(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testMetadataUnaryRPC(t, e)
+ }
+}
+
+func testMetadataUnaryRPC(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ argSize := 2718
+ respSize := 314
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize)),
+ }
+ var header, trailer metadata.MD
+ ctx := metadata.NewContext(context.Background(), testMetadata)
+ _, err := tc.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer))
+ if err != nil {
+ t.Fatalf("TestService.UnaryCall(%v, _, _, _) = _, %v; want _, ", ctx, err)
+ }
+ if !reflect.DeepEqual(testMetadata, header) {
+ t.Fatalf("Received header metadata %v, want %v", header, testMetadata)
+ }
+ if !reflect.DeepEqual(testMetadata, trailer) {
+ t.Fatalf("Received trailer metadata %v, want %v", trailer, testMetadata)
+ }
+}
+
+func performOneRPC(t *testing.T, tc testpb.TestServiceClient, wg *sync.WaitGroup) {
+ argSize := 2718
+ respSize := 314
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize)),
+ }
+ reply, err := tc.UnaryCall(context.Background(), req)
+ if err != nil {
+ t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
+ }
+ pt := reply.GetPayload().GetType()
+ ps := len(reply.GetPayload().GetBody())
+ if pt != testpb.PayloadType_COMPRESSABLE || ps != respSize {
+ t.Fatalf("Got the reply with type %d len %d; want %d, %d", pt, ps, testpb.PayloadType_COMPRESSABLE, respSize)
+ }
+ wg.Done()
+}
+
+func TestRetry(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testRetry(t, e)
+ }
+}
+
+// This test mimics a user who sends 1000 RPCs concurrently on a faulty transport.
+// TODO(zhaoq): Refactor to make this clearer and add more cases to test racy
+// and error-prone paths.
+func testRetry(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ time.Sleep(1 * time.Second)
+ // The server shuts down the network connection to make a
+ // transport error which will be detected by the client side
+ // code.
+ s.TestingCloseConns()
+ wg.Done()
+ }()
+ // All these RPCs should succeed eventually.
+ for i := 0; i < 1000; i++ {
+ time.Sleep(2 * time.Millisecond)
+ wg.Add(1)
+ go performOneRPC(t, tc, &wg)
+ }
+ wg.Wait()
+}
+
+func TestRPCTimeout(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testRPCTimeout(t, e)
+ }
+}
+
+// TODO(zhaoq): Have a better test coverage of timeout and cancellation mechanism.
+func testRPCTimeout(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ argSize := 2718
+ respSize := 314
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize)),
+ }
+ // Performs 100 RPCs with various timeout values so that
+ // the RPCs could timeout on different stages of their lifetime. This
+ // is the best-effort to cover various cases when an rpc gets cancelled.
+ for i := 1; i <= 100; i++ {
+ ctx, _ := context.WithTimeout(context.Background(), time.Duration(i)*time.Microsecond)
+ reply, err := tc.UnaryCall(ctx, req)
+ if grpc.Code(err) != codes.DeadlineExceeded {
+ t.Fatalf(`TestService/UnaryCallv(_, _) = %v, %v; want , error code: %d`, reply, err, codes.DeadlineExceeded)
+ }
+ }
+}
+
+func TestCancel(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testCancel(t, e)
+ }
+}
+
+func testCancel(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ argSize := 2718
+ respSize := 314
+ req := &testpb.SimpleRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseSize: proto.Int32(int32(respSize)),
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize)),
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ time.AfterFunc(1*time.Millisecond, cancel)
+ reply, err := tc.UnaryCall(ctx, req)
+ if grpc.Code(err) != codes.Canceled {
+ t.Fatalf(`TestService/UnaryCall(_, _) = %v, %v; want , error code: %d`, reply, err, codes.Canceled)
+ }
+}
+
+// The following tests the gRPC streaming RPC implementations.
+// TODO(zhaoq): Have better coverage on error cases.
+var (
+ reqSizes = []int{27182, 8, 1828, 45904}
+ respSizes = []int{31415, 9, 2653, 58979}
+)
+
+func TestPingPong(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testPingPong(t, e)
+ }
+}
+
+func testPingPong(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ stream, err := tc.FullDuplexCall(context.Background())
+ if err != nil {
+ t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
+ }
+ var index int
+ for index < len(reqSizes) {
+ respParam := []*testpb.ResponseParameters{
+ {
+ Size: proto.Int32(int32(respSizes[index])),
+ },
+ }
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(reqSizes[index])),
+ }
+ if err := stream.Send(req); err != nil {
+ t.Fatalf("%v.Send(%v) = %v, want ", stream, req, err)
+ }
+ reply, err := stream.Recv()
+ if err != nil {
+ t.Fatalf("%v.Recv() = %v, want ", stream, err)
+ }
+ pt := reply.GetPayload().GetType()
+ if pt != testpb.PayloadType_COMPRESSABLE {
+ t.Fatalf("Got the reply of type %d, want %d", pt, testpb.PayloadType_COMPRESSABLE)
+ }
+ size := len(reply.GetPayload().GetBody())
+ if size != int(respSizes[index]) {
+ t.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
+ }
+ index++
+ }
+ if err := stream.CloseSend(); err != nil {
+ t.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
+ }
+ if _, err := stream.Recv(); err != io.EOF {
+ t.Fatalf("%v failed to complele the ping pong test: %v", stream, err)
+ }
+}
+
+func TestMetadataStreamingRPC(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testMetadataStreamingRPC(t, e)
+ }
+}
+
+func testMetadataStreamingRPC(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ ctx := metadata.NewContext(context.Background(), testMetadata)
+ stream, err := tc.FullDuplexCall(ctx)
+ if err != nil {
+ t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
+ }
+ go func() {
+ headerMD, err := stream.Header()
+ if err != nil || !reflect.DeepEqual(testMetadata, headerMD) {
+ t.Errorf("#1 %v.Header() = %v, %v, want %v, ", stream, headerMD, err, testMetadata)
+ }
+ // test the cached value.
+ headerMD, err = stream.Header()
+ if err != nil || !reflect.DeepEqual(testMetadata, headerMD) {
+ t.Errorf("#2 %v.Header() = %v, %v, want %v, ", stream, headerMD, err, testMetadata)
+ }
+ var index int
+ for index < len(reqSizes) {
+ respParam := []*testpb.ResponseParameters{
+ {
+ Size: proto.Int32(int32(respSizes[index])),
+ },
+ }
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ Payload: newPayload(testpb.PayloadType_COMPRESSABLE, int32(reqSizes[index])),
+ }
+ if err := stream.Send(req); err != nil {
+ t.Errorf("%v.Send(%v) = %v, want ", stream, req, err)
+ return
+ }
+ index++
+ }
+ // Tell the server we're done sending args.
+ stream.CloseSend()
+ }()
+ for {
+ if _, err := stream.Recv(); err != nil {
+ break
+ }
+ }
+ trailerMD := stream.Trailer()
+ if !reflect.DeepEqual(testMetadata, trailerMD) {
+ t.Fatalf("%v.Trailer() = %v, want %v", stream, trailerMD, testMetadata)
+ }
+}
+
+func TestServerStreaming(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testServerStreaming(t, e)
+ }
+}
+
+func testServerStreaming(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ respParam := make([]*testpb.ResponseParameters, len(respSizes))
+ for i, s := range respSizes {
+ respParam[i] = &testpb.ResponseParameters{
+ Size: proto.Int32(int32(s)),
+ }
+ }
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ }
+ stream, err := tc.StreamingOutputCall(context.Background(), req)
+ if err != nil {
+ t.Fatalf("%v.StreamingOutputCall(_) = _, %v, want ", tc, err)
+ }
+ var rpcStatus error
+ var respCnt int
+ var index int
+ for {
+ reply, err := stream.Recv()
+ if err != nil {
+ rpcStatus = err
+ break
+ }
+ pt := reply.GetPayload().GetType()
+ if pt != testpb.PayloadType_COMPRESSABLE {
+ t.Fatalf("Got the reply of type %d, want %d", pt, testpb.PayloadType_COMPRESSABLE)
+ }
+ size := len(reply.GetPayload().GetBody())
+ if size != int(respSizes[index]) {
+ t.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
+ }
+ index++
+ respCnt++
+ }
+ if rpcStatus != io.EOF {
+ t.Fatalf("Failed to finish the server streaming rpc: %v, want ", err)
+ }
+ if respCnt != len(respSizes) {
+ t.Fatalf("Got %d reply, want %d", len(respSizes), respCnt)
+ }
+}
+
+func TestFailedServerStreaming(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testFailedServerStreaming(t, e)
+ }
+}
+
+func testFailedServerStreaming(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ respParam := make([]*testpb.ResponseParameters, len(respSizes))
+ for i, s := range respSizes {
+ respParam[i] = &testpb.ResponseParameters{
+ Size: proto.Int32(int32(s)),
+ }
+ }
+ req := &testpb.StreamingOutputCallRequest{
+ ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+ ResponseParameters: respParam,
+ }
+ ctx := metadata.NewContext(context.Background(), testMetadata)
+ stream, err := tc.StreamingOutputCall(ctx, req)
+ if err != nil {
+ t.Fatalf("%v.StreamingOutputCall(_) = _, %v, want ", tc, err)
+ }
+ if _, err := stream.Recv(); err != grpc.Errorf(codes.DataLoss, "got extra metadata") {
+ t.Fatalf("%v.Recv() = _, %v, want _, %v", stream, err, grpc.Errorf(codes.DataLoss, "got extra metadata"))
+ }
+}
+
+func TestClientStreaming(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testClientStreaming(t, e)
+ }
+}
+
+func testClientStreaming(t *testing.T, e env) {
+ s, cc := setUp(math.MaxUint32, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ stream, err := tc.StreamingInputCall(context.Background())
+ if err != nil {
+ t.Fatalf("%v.StreamingInputCall(_) = _, %v, want ", tc, err)
+ }
+ var sum int
+ for _, s := range reqSizes {
+ pl := newPayload(testpb.PayloadType_COMPRESSABLE, int32(s))
+ req := &testpb.StreamingInputCallRequest{
+ Payload: pl,
+ }
+ if err := stream.Send(req); err != nil {
+ t.Fatalf("%v.Send(%v) = %v, want ", stream, req, err)
+ }
+ sum += s
+ }
+ reply, err := stream.CloseAndRecv()
+ if err != nil {
+ t.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
+ }
+ if reply.GetAggregatedPayloadSize() != int32(sum) {
+ t.Fatalf("%v.CloseAndRecv().GetAggregatePayloadSize() = %v; want %v", stream, reply.GetAggregatedPayloadSize(), sum)
+ }
+}
+
+func TestExceedMaxStreamsLimit(t *testing.T) {
+ for _, e := range listTestEnv() {
+ testExceedMaxStreamsLimit(t, e)
+ }
+}
+
+func testExceedMaxStreamsLimit(t *testing.T, e env) {
+ // Only allows 1 live stream per server transport.
+ s, cc := setUp(1, e)
+ tc := testpb.NewTestServiceClient(cc)
+ defer tearDown(s, cc)
+ var err error
+ for {
+ time.Sleep(2 * time.Millisecond)
+ _, err = tc.StreamingInputCall(context.Background())
+ // Loop until the settings of max concurrent streams is
+ // received by the client.
+ if err != nil {
+ break
+ }
+ }
+ if grpc.Code(err) != codes.Unavailable {
+ t.Fatalf("got %v, want error code %d", err, codes.Unavailable)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go
new file mode 100644
index 000000000..754de67f9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.pb.go
@@ -0,0 +1,702 @@
+// Code generated by protoc-gen-go.
+// source: src/google.golang.org/grpc/test/grpc_testing/test.proto
+// DO NOT EDIT!
+
+/*
+Package grpc_testing is a generated protocol buffer package.
+
+It is generated from these files:
+ src/google.golang.org/grpc/test/grpc_testing/test.proto
+
+It has these top-level messages:
+ Empty
+ Payload
+ SimpleRequest
+ SimpleResponse
+ StreamingInputCallRequest
+ StreamingInputCallResponse
+ ResponseParameters
+ StreamingOutputCallRequest
+ StreamingOutputCallResponse
+*/
+package grpc_testing
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/golang/protobuf/proto"
+import math "math"
+
+import (
+ context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+// The type of payload that should be returned.
+type PayloadType int32
+
+const (
+ // Compressable text format.
+ PayloadType_COMPRESSABLE PayloadType = 0
+ // Uncompressable binary format.
+ PayloadType_UNCOMPRESSABLE PayloadType = 1
+ // Randomly chosen from all other formats defined in this enum.
+ PayloadType_RANDOM PayloadType = 2
+)
+
+var PayloadType_name = map[int32]string{
+ 0: "COMPRESSABLE",
+ 1: "UNCOMPRESSABLE",
+ 2: "RANDOM",
+}
+var PayloadType_value = map[string]int32{
+ "COMPRESSABLE": 0,
+ "UNCOMPRESSABLE": 1,
+ "RANDOM": 2,
+}
+
+func (x PayloadType) Enum() *PayloadType {
+ p := new(PayloadType)
+ *p = x
+ return p
+}
+func (x PayloadType) String() string {
+ return proto.EnumName(PayloadType_name, int32(x))
+}
+func (x *PayloadType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(PayloadType_value, data, "PayloadType")
+ if err != nil {
+ return err
+ }
+ *x = PayloadType(value)
+ return nil
+}
+
+type Empty struct {
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Empty) Reset() { *m = Empty{} }
+func (m *Empty) String() string { return proto.CompactTextString(m) }
+func (*Empty) ProtoMessage() {}
+
+// A block of data, to simply increase gRPC message size.
+type Payload struct {
+ // The type of data in body.
+ Type *PayloadType `protobuf:"varint,1,opt,name=type,enum=grpc.testing.PayloadType" json:"type,omitempty"`
+ // Primary contents of payload.
+ Body []byte `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Payload) Reset() { *m = Payload{} }
+func (m *Payload) String() string { return proto.CompactTextString(m) }
+func (*Payload) ProtoMessage() {}
+
+func (m *Payload) GetType() PayloadType {
+ if m != nil && m.Type != nil {
+ return *m.Type
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *Payload) GetBody() []byte {
+ if m != nil {
+ return m.Body
+ }
+ return nil
+}
+
+// Unary request.
+type SimpleRequest struct {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ ResponseSize *int32 `protobuf:"varint,2,opt,name=response_size" json:"response_size,omitempty"`
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
+ // Whether SimpleResponse should include username.
+ FillUsername *bool `protobuf:"varint,4,opt,name=fill_username" json:"fill_username,omitempty"`
+ // Whether SimpleResponse should include OAuth scope.
+ FillOauthScope *bool `protobuf:"varint,5,opt,name=fill_oauth_scope" json:"fill_oauth_scope,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
+func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
+func (*SimpleRequest) ProtoMessage() {}
+
+func (m *SimpleRequest) GetResponseType() PayloadType {
+ if m != nil && m.ResponseType != nil {
+ return *m.ResponseType
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *SimpleRequest) GetResponseSize() int32 {
+ if m != nil && m.ResponseSize != nil {
+ return *m.ResponseSize
+ }
+ return 0
+}
+
+func (m *SimpleRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (m *SimpleRequest) GetFillUsername() bool {
+ if m != nil && m.FillUsername != nil {
+ return *m.FillUsername
+ }
+ return false
+}
+
+func (m *SimpleRequest) GetFillOauthScope() bool {
+ if m != nil && m.FillOauthScope != nil {
+ return *m.FillOauthScope
+ }
+ return false
+}
+
+// Unary response, as configured by the request.
+type SimpleResponse struct {
+ // Payload to increase message size.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ Username *string `protobuf:"bytes,2,opt,name=username" json:"username,omitempty"`
+ // OAuth scope.
+ OauthScope *string `protobuf:"bytes,3,opt,name=oauth_scope" json:"oauth_scope,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
+func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
+func (*SimpleResponse) ProtoMessage() {}
+
+func (m *SimpleResponse) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func (m *SimpleResponse) GetUsername() string {
+ if m != nil && m.Username != nil {
+ return *m.Username
+ }
+ return ""
+}
+
+func (m *SimpleResponse) GetOauthScope() string {
+ if m != nil && m.OauthScope != nil {
+ return *m.OauthScope
+ }
+ return ""
+}
+
+// Client-streaming request.
+type StreamingInputCallRequest struct {
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
+func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallRequest) ProtoMessage() {}
+
+func (m *StreamingInputCallRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+// Client-streaming response.
+type StreamingInputCallResponse struct {
+ // Aggregated size of payloads received from the client.
+ AggregatedPayloadSize *int32 `protobuf:"varint,1,opt,name=aggregated_payload_size" json:"aggregated_payload_size,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
+func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingInputCallResponse) ProtoMessage() {}
+
+func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
+ if m != nil && m.AggregatedPayloadSize != nil {
+ return *m.AggregatedPayloadSize
+ }
+ return 0
+}
+
+// Configuration for a particular response.
+type ResponseParameters struct {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ Size *int32 `protobuf:"varint,1,opt,name=size" json:"size,omitempty"`
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ IntervalUs *int32 `protobuf:"varint,2,opt,name=interval_us" json:"interval_us,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
+func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
+func (*ResponseParameters) ProtoMessage() {}
+
+func (m *ResponseParameters) GetSize() int32 {
+ if m != nil && m.Size != nil {
+ return *m.Size
+ }
+ return 0
+}
+
+func (m *ResponseParameters) GetIntervalUs() int32 {
+ if m != nil && m.IntervalUs != nil {
+ return *m.IntervalUs
+ }
+ return 0
+}
+
+// Server-streaming request.
+type StreamingOutputCallRequest struct {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
+ // Configuration for each expected response message.
+ ResponseParameters []*ResponseParameters `protobuf:"bytes,2,rep,name=response_parameters" json:"response_parameters,omitempty"`
+ // Optional input payload sent along with the request.
+ Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
+func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallRequest) ProtoMessage() {}
+
+func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
+ if m != nil && m.ResponseType != nil {
+ return *m.ResponseType
+ }
+ return PayloadType_COMPRESSABLE
+}
+
+func (m *StreamingOutputCallRequest) GetResponseParameters() []*ResponseParameters {
+ if m != nil {
+ return m.ResponseParameters
+ }
+ return nil
+}
+
+func (m *StreamingOutputCallRequest) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+// Server-streaming response, as configured by the request and parameters.
+type StreamingOutputCallResponse struct {
+ // Payload to increase response size.
+ Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
+func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
+func (*StreamingOutputCallResponse) ProtoMessage() {}
+
+func (m *StreamingOutputCallResponse) GetPayload() *Payload {
+ if m != nil {
+ return m.Payload
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
+}
+
+// Client API for TestService service
+
+type TestServiceClient interface {
+ // One empty request followed by one empty response.
+ EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)
+}
+
+type testServiceClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
+ return &testServiceClient{cc}
+}
+
+func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
+ out := new(Empty)
+ err := grpc.Invoke(ctx, "/grpc.testing.TestService/EmptyCall", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
+ out := new(SimpleResponse)
+ err := grpc.Invoke(ctx, "/grpc.testing.TestService/UnaryCall", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/grpc.testing.TestService/StreamingOutputCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceStreamingOutputCallClient{stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type TestService_StreamingOutputCallClient interface {
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceStreamingOutputCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[1], c.cc, "/grpc.testing.TestService/StreamingInputCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceStreamingInputCallClient{stream}
+ return x, nil
+}
+
+type TestService_StreamingInputCallClient interface {
+ Send(*StreamingInputCallRequest) error
+ CloseAndRecv() (*StreamingInputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceStreamingInputCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(StreamingInputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[2], c.cc, "/grpc.testing.TestService/FullDuplexCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceFullDuplexCallClient{stream}
+ return x, nil
+}
+
+type TestService_FullDuplexCallClient interface {
+ Send(*StreamingOutputCallRequest) error
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceFullDuplexCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {
+ stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[3], c.cc, "/grpc.testing.TestService/HalfDuplexCall", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &testServiceHalfDuplexCallClient{stream}
+ return x, nil
+}
+
+type TestService_HalfDuplexCallClient interface {
+ Send(*StreamingOutputCallRequest) error
+ Recv() (*StreamingOutputCallResponse, error)
+ grpc.ClientStream
+}
+
+type testServiceHalfDuplexCallClient struct {
+ grpc.ClientStream
+}
+
+func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
+ m := new(StreamingOutputCallResponse)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// Server API for TestService service
+
+type TestServiceServer interface {
+ // One empty request followed by one empty response.
+ EmptyCall(context.Context, *Empty) (*Empty, error)
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ StreamingInputCall(TestService_StreamingInputCallServer) error
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ FullDuplexCall(TestService_FullDuplexCallServer) error
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ HalfDuplexCall(TestService_HalfDuplexCallServer) error
+}
+
+func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
+ s.RegisterService(&_TestService_serviceDesc, srv)
+}
+
+func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(Empty)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(TestServiceServer).EmptyCall(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(SimpleRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(TestServiceServer).UnaryCall(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(StreamingOutputCallRequest)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})
+}
+
+type TestService_StreamingOutputCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ grpc.ServerStream
+}
+
+type testServiceStreamingOutputCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})
+}
+
+type TestService_StreamingInputCallServer interface {
+ SendAndClose(*StreamingInputCallResponse) error
+ Recv() (*StreamingInputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceStreamingInputCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {
+ m := new(StreamingInputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})
+}
+
+type TestService_FullDuplexCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ Recv() (*StreamingOutputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceFullDuplexCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
+ m := new(StreamingOutputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})
+}
+
+type TestService_HalfDuplexCallServer interface {
+ Send(*StreamingOutputCallResponse) error
+ Recv() (*StreamingOutputCallRequest, error)
+ grpc.ServerStream
+}
+
+type testServiceHalfDuplexCallServer struct {
+ grpc.ServerStream
+}
+
+func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
+ m := new(StreamingOutputCallRequest)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+var _TestService_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "grpc.testing.TestService",
+ HandlerType: (*TestServiceServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "EmptyCall",
+ Handler: _TestService_EmptyCall_Handler,
+ },
+ {
+ MethodName: "UnaryCall",
+ Handler: _TestService_UnaryCall_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "StreamingOutputCall",
+ Handler: _TestService_StreamingOutputCall_Handler,
+ ServerStreams: true,
+ },
+ {
+ StreamName: "StreamingInputCall",
+ Handler: _TestService_StreamingInputCall_Handler,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "FullDuplexCall",
+ Handler: _TestService_FullDuplexCall_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "HalfDuplexCall",
+ Handler: _TestService_HalfDuplexCall_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.proto b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.proto
new file mode 100644
index 000000000..b5bfe0537
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/grpc_testing/test.proto
@@ -0,0 +1,140 @@
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+syntax = "proto2";
+
+package grpc.testing;
+
+message Empty {}
+
+// The type of payload that should be returned.
+enum PayloadType {
+ // Compressable text format.
+ COMPRESSABLE = 0;
+
+ // Uncompressable binary format.
+ UNCOMPRESSABLE = 1;
+
+ // Randomly chosen from all other formats defined in this enum.
+ RANDOM = 2;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+ // The type of data in body.
+ optional PayloadType type = 1;
+ // Primary contents of payload.
+ optional bytes body = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ optional PayloadType response_type = 1;
+
+ // Desired payload size in the response from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 response_size = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+
+ // Whether SimpleResponse should include username.
+ optional bool fill_username = 4;
+
+ // Whether SimpleResponse should include OAuth scope.
+ optional bool fill_oauth_scope = 5;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+ // Payload to increase message size.
+ optional Payload payload = 1;
+
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ optional string username = 2;
+
+ // OAuth scope.
+ optional string oauth_scope = 3;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+ // Optional input payload sent along with the request.
+ optional Payload payload = 1;
+
+ // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+ // Aggregated size of payloads received from the client.
+ optional int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+ // Desired payload sizes in responses from the server.
+ // If response_type is COMPRESSABLE, this denotes the size before compression.
+ optional int32 size = 1;
+
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ optional int32 interval_us = 2;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ optional PayloadType response_type = 1;
+
+ // Configuration for each expected response message.
+ repeated ResponseParameters response_parameters = 2;
+
+ // Optional input payload sent along with the request.
+ optional Payload payload = 3;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+ // Payload to increase response size.
+ optional Payload payload = 1;
+}
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+ // One empty request followed by one empty response.
+ rpc EmptyCall(Empty) returns (Empty);
+
+ // One request followed by one response.
+ // The server returns the client payload as-is.
+ rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ rpc StreamingOutputCall(StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ rpc StreamingInputCall(stream StreamingInputCallRequest)
+ returns (StreamingInputCallResponse);
+
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ rpc FullDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/ca.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/ca.pem
new file mode 100644
index 000000000..999da8977
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
+NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
+Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
+cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
+xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
+QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
+AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
+fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
+5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.key
new file mode 100644
index 000000000..ed00cd500
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
+bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
+BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
+AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
+ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
+omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
+ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
+9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
+MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
+IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
+USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
+VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
+RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.pem
new file mode 100644
index 000000000..8e582e571
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/test/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/control.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/control.go
new file mode 100644
index 000000000..e47b5e115
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/control.go
@@ -0,0 +1,253 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package transport
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2"
+)
+
+const (
+ // The default value of flow control window size in HTTP2 spec.
+ defaultWindowSize = 65535
+ // The initial window size for flow control.
+ initialWindowSize = defaultWindowSize // for an RPC
+ initialConnWindowSize = defaultWindowSize * 16 // for a connection
+)
+
+// The following defines various control items which could flow through
+// the control buffer of transport. They represent different aspects of
+// control tasks, e.g., flow control, settings, streaming resetting, etc.
+type windowUpdate struct {
+ streamID uint32
+ increment uint32
+}
+
+func (windowUpdate) isItem() bool {
+ return true
+}
+
+type settings struct {
+ ack bool
+ setting []http2.Setting
+}
+
+func (settings) isItem() bool {
+ return true
+}
+
+type resetStream struct {
+ streamID uint32
+ code http2.ErrCode
+}
+
+func (resetStream) isItem() bool {
+ return true
+}
+
+type flushIO struct {
+}
+
+func (flushIO) isItem() bool {
+ return true
+}
+
+type ping struct {
+ ack bool
+}
+
+func (ping) isItem() bool {
+ return true
+}
+
+// quotaPool is a pool which accumulates the quota and sends it to acquire()
+// when it is available.
+type quotaPool struct {
+ c chan int
+
+ mu sync.Mutex
+ quota int
+}
+
+// newQuotaPool creates a quotaPool which has quota q available to consume.
+func newQuotaPool(q int) *quotaPool {
+ qb := "aPool{c: make(chan int, 1)}
+ qb.c <- q
+ return qb
+}
+
+// add adds n to the available quota and tries to send it on acquire.
+func (qb *quotaPool) add(n int) {
+ qb.mu.Lock()
+ defer qb.mu.Unlock()
+ qb.quota += n
+ if qb.quota <= 0 {
+ return
+ }
+ select {
+ case qb.c <- qb.quota:
+ qb.quota = 0
+ default:
+ }
+}
+
+// cancel cancels the pending quota sent on acquire, if any.
+func (qb *quotaPool) cancel() {
+ qb.mu.Lock()
+ defer qb.mu.Unlock()
+ select {
+ case n := <-qb.c:
+ qb.quota += n
+ default:
+ }
+}
+
+// reset cancels the pending quota sent on acquired, incremented by v and sends
+// it back on acquire.
+func (qb *quotaPool) reset(v int) {
+ qb.mu.Lock()
+ defer qb.mu.Unlock()
+ select {
+ case n := <-qb.c:
+ qb.quota += n
+ default:
+ }
+ qb.quota += v
+ if qb.quota <= 0 {
+ return
+ }
+ select {
+ case qb.c <- qb.quota:
+ qb.quota = 0
+ default:
+ }
+}
+
+// acquire returns the channel on which available quota amounts are sent.
+func (qb *quotaPool) acquire() <-chan int {
+ return qb.c
+}
+
+// inFlow deals with inbound flow control
+type inFlow struct {
+ // The inbound flow control limit for pending data.
+ limit uint32
+ // conn points to the shared connection-level inFlow that is shared
+ // by all streams on that conn. It is nil for the inFlow on the conn
+ // directly.
+ conn *inFlow
+
+ mu sync.Mutex
+ // pendingData is the overall data which have been received but not been
+ // consumed by applications.
+ pendingData uint32
+ // The amount of data the application has consumed but grpc has not sent
+ // window update for them. Used to reduce window update frequency.
+ pendingUpdate uint32
+}
+
+// onData is invoked when some data frame is received. It increments not only its
+// own pendingData but also that of the associated connection-level flow.
+func (f *inFlow) onData(n uint32) error {
+ if n == 0 {
+ return nil
+ }
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if f.pendingData+f.pendingUpdate+n > f.limit {
+ return fmt.Errorf("recieved %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate+n, f.limit)
+ }
+ if f.conn != nil {
+ if err := f.conn.onData(n); err != nil {
+ return ConnectionErrorf("%v", err)
+ }
+ }
+ f.pendingData += n
+ return nil
+}
+
+// connOnRead updates the connection level states when the application consumes data.
+func (f *inFlow) connOnRead(n uint32) uint32 {
+ if n == 0 || f.conn != nil {
+ return 0
+ }
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ f.pendingData -= n
+ f.pendingUpdate += n
+ if f.pendingUpdate >= f.limit/4 {
+ ret := f.pendingUpdate
+ f.pendingUpdate = 0
+ return ret
+ }
+ return 0
+}
+
+// onRead is invoked when the application reads the data. It returns the window updates
+// for both stream and connection level.
+func (f *inFlow) onRead(n uint32) (swu, cwu uint32) {
+ if n == 0 {
+ return
+ }
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ if f.pendingData == 0 {
+ // pendingData has been adjusted by restoreConn.
+ return
+ }
+ f.pendingData -= n
+ f.pendingUpdate += n
+ if f.pendingUpdate >= f.limit/4 {
+ swu = f.pendingUpdate
+ f.pendingUpdate = 0
+ }
+ cwu = f.conn.connOnRead(n)
+ return
+}
+
+// restoreConn is invoked when a stream is terminated. It removes its stake in
+// the connection-level flow and resets its own state.
+func (f *inFlow) restoreConn() uint32 {
+ if f.conn == nil {
+ return 0
+ }
+ f.mu.Lock()
+ defer f.mu.Unlock()
+ n := f.pendingData
+ f.pendingData = 0
+ f.pendingUpdate = 0
+ return f.conn.connOnRead(n)
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go
new file mode 100644
index 000000000..18b7df592
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_client.go
@@ -0,0 +1,753 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package transport
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "math"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+)
+
+// http2Client implements the ClientTransport interface with HTTP2.
+type http2Client struct {
+ target string // server name/addr
+ conn net.Conn // underlying communication channel
+ nextID uint32 // the next stream ID to be used
+
+ // writableChan synchronizes write access to the transport.
+ // A writer acquires the write lock by sending a value on writableChan
+ // and releases it by receiving from writableChan.
+ writableChan chan int
+ // shutdownChan is closed when Close is called.
+ // Blocking operations should select on shutdownChan to avoid
+ // blocking forever after Close.
+ // TODO(zhaoq): Maybe have a channel context?
+ shutdownChan chan struct{}
+ // errorChan is closed to notify the I/O error to the caller.
+ errorChan chan struct{}
+
+ framer *framer
+ hBuf *bytes.Buffer // the buffer for HPACK encoding
+ hEnc *hpack.Encoder // HPACK encoder
+
+ // controlBuf delivers all the control related tasks (e.g., window
+ // updates, reset streams, and various settings) to the controller.
+ controlBuf *recvBuffer
+ fc *inFlow
+ // sendQuotaPool provides flow control to outbound message.
+ sendQuotaPool *quotaPool
+
+ // The scheme used: https if TLS is on, http otherwise.
+ scheme string
+
+ authCreds []credentials.Credentials
+
+ mu sync.Mutex // guard the following variables
+ state transportState // the state of underlying connection
+ activeStreams map[uint32]*Stream
+ // The max number of concurrent streams
+ maxStreams uint32
+ // the per-stream outbound flow control window size set by the peer.
+ streamSendQuota uint32
+}
+
+// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
+// and starts to receive messages on it. Non-nil error returns if construction
+// fails.
+func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err error) {
+ if opts.Dialer == nil {
+ // Set the default Dialer.
+ opts.Dialer = func(addr string, timeout time.Duration) (net.Conn, error) {
+ return net.DialTimeout("tcp", addr, timeout)
+ }
+ }
+ scheme := "http"
+ startT := time.Now()
+ timeout := opts.Timeout
+ conn, connErr := opts.Dialer(addr, timeout)
+ if connErr != nil {
+ return nil, ConnectionErrorf("transport: %v", connErr)
+ }
+ for _, c := range opts.AuthOptions {
+ if ccreds, ok := c.(credentials.TransportAuthenticator); ok {
+ scheme = "https"
+ // TODO(zhaoq): Now the first TransportAuthenticator is used if there are
+ // multiple ones provided. Revisit this if it is not appropriate. Probably
+ // place the ClientTransport construction into a separate function to make
+ // things clear.
+ if timeout > 0 {
+ timeout -= time.Since(startT)
+ }
+ conn, connErr = ccreds.ClientHandshake(addr, conn, timeout)
+ break
+ }
+ }
+ if connErr != nil {
+ return nil, ConnectionErrorf("transport: %v", connErr)
+ }
+ defer func() {
+ if err != nil {
+ conn.Close()
+ }
+ }()
+ // Send connection preface to server.
+ n, err := conn.Write(clientPreface)
+ if err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ if n != len(clientPreface) {
+ return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
+ }
+ framer := newFramer(conn)
+ if initialWindowSize != defaultWindowSize {
+ err = framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
+ } else {
+ err = framer.writeSettings(true)
+ }
+ if err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ // Adjust the connection flow control window if needed.
+ if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
+ if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ }
+ var buf bytes.Buffer
+ t := &http2Client{
+ target: addr,
+ conn: conn,
+ // The client initiated stream id is odd starting from 1.
+ nextID: 1,
+ writableChan: make(chan int, 1),
+ shutdownChan: make(chan struct{}),
+ errorChan: make(chan struct{}),
+ framer: framer,
+ hBuf: &buf,
+ hEnc: hpack.NewEncoder(&buf),
+ controlBuf: newRecvBuffer(),
+ fc: &inFlow{limit: initialConnWindowSize},
+ sendQuotaPool: newQuotaPool(defaultWindowSize),
+ scheme: scheme,
+ state: reachable,
+ activeStreams: make(map[uint32]*Stream),
+ maxStreams: math.MaxUint32,
+ authCreds: opts.AuthOptions,
+ streamSendQuota: defaultWindowSize,
+ }
+ go t.controller()
+ t.writableChan <- 0
+ // Start the reader goroutine for incoming message. The threading model
+ // on receiving is that each transport has a dedicated goroutine which
+ // reads HTTP2 frame from network. Then it dispatches the frame to the
+ // corresponding stream entity.
+ go t.reader()
+ return t, nil
+}
+
+func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
+ fc := &inFlow{
+ limit: initialWindowSize,
+ conn: t.fc,
+ }
+ // TODO(zhaoq): Handle uint32 overflow of Stream.id.
+ s := &Stream{
+ id: t.nextID,
+ method: callHdr.Method,
+ buf: newRecvBuffer(),
+ fc: fc,
+ sendQuotaPool: newQuotaPool(int(t.streamSendQuota)),
+ headerChan: make(chan struct{}),
+ }
+ t.nextID += 2
+ s.windowHandler = func(n int) {
+ t.updateWindow(s, uint32(n))
+ }
+ // Make a stream be able to cancel the pending operations by itself.
+ s.ctx, s.cancel = context.WithCancel(ctx)
+ s.dec = &recvBufferReader{
+ ctx: s.ctx,
+ recv: s.buf,
+ }
+ return s
+}
+
+// NewStream creates a stream and register it into the transport as "active"
+// streams.
+func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
+ // Record the timeout value on the context.
+ var timeout time.Duration
+ if dl, ok := ctx.Deadline(); ok {
+ timeout = dl.Sub(time.Now())
+ if timeout <= 0 {
+ return nil, ContextErr(context.DeadlineExceeded)
+ }
+ }
+ authData := make(map[string]string)
+ for _, c := range t.authCreds {
+ data, err := c.GetRequestMetadata(ctx)
+ if err != nil {
+ return nil, StreamErrorf(codes.InvalidArgument, "transport: %v", err)
+ }
+ for k, v := range data {
+ authData[k] = v
+ }
+ }
+ if _, err := wait(ctx, t.shutdownChan, t.writableChan); err != nil {
+ return nil, err
+ }
+ t.mu.Lock()
+ if t.state != reachable {
+ t.mu.Unlock()
+ return nil, ErrConnClosing
+ }
+ if uint32(len(t.activeStreams)) >= t.maxStreams {
+ t.mu.Unlock()
+ t.writableChan <- 0
+ return nil, StreamErrorf(codes.Unavailable, "transport: failed to create new stream because the limit has been reached.")
+ }
+ s := t.newStream(ctx, callHdr)
+ t.activeStreams[s.id] = s
+ t.mu.Unlock()
+ // HPACK encodes various headers. Note that once WriteField(...) is
+ // called, the corresponding headers/continuation frame has to be sent
+ // because hpack.Encoder is stateful.
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":method", Value: "POST"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":scheme", Value: t.scheme})
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":path", Value: callHdr.Method})
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":authority", Value: callHdr.Host})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "te", Value: "trailers"})
+ if timeout > 0 {
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-timeout", Value: timeoutEncode(timeout)})
+ }
+ for k, v := range authData {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ }
+ var (
+ hasMD bool
+ endHeaders bool
+ )
+ if md, ok := metadata.FromContext(ctx); ok {
+ hasMD = true
+ for k, v := range md {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ }
+ }
+ first := true
+ // Sends the headers in a single batch even when they span multiple frames.
+ for !endHeaders {
+ size := t.hBuf.Len()
+ if size > http2MaxFrameLen {
+ size = http2MaxFrameLen
+ } else {
+ endHeaders = true
+ }
+ if first {
+ // Sends a HeadersFrame to server to start a new stream.
+ p := http2.HeadersFrameParam{
+ StreamID: s.id,
+ BlockFragment: t.hBuf.Next(size),
+ EndStream: false,
+ EndHeaders: endHeaders,
+ }
+ // Do a force flush for the buffered frames iff it is the last headers frame
+ // and there is header metadata to be sent. Otherwise, there is flushing until
+ // the corresponding data frame is written.
+ err = t.framer.writeHeaders(hasMD && endHeaders, p)
+ first = false
+ } else {
+ // Sends Continuation frames for the leftover headers.
+ err = t.framer.writeContinuation(hasMD && endHeaders, s.id, endHeaders, t.hBuf.Next(size))
+ }
+ if err != nil {
+ t.notifyError(err)
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ }
+ t.writableChan <- 0
+ return s, nil
+}
+
+// CloseStream clears the footprint of a stream when the stream is not needed any more.
+// This must not be executed in reader's goroutine.
+func (t *http2Client) CloseStream(s *Stream, err error) {
+ t.mu.Lock()
+ delete(t.activeStreams, s.id)
+ t.mu.Unlock()
+ s.mu.Lock()
+ if q := s.fc.restoreConn(); q > 0 {
+ t.controlBuf.put(&windowUpdate{0, q})
+ }
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ if !s.headerDone {
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ s.state = streamDone
+ s.mu.Unlock()
+ // In case stream sending and receiving are invoked in separate
+ // goroutines (e.g., bi-directional streaming), the caller needs
+ // to call cancel on the stream to interrupt the blocking on
+ // other goroutines.
+ s.cancel()
+ if _, ok := err.(StreamError); ok {
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeCancel})
+ }
+}
+
+// Close kicks off the shutdown process of the transport. This should be called
+// only once on a transport. Once it is called, the transport should not be
+// accessed any more.
+func (t *http2Client) Close() (err error) {
+ t.mu.Lock()
+ if t.state == closing {
+ t.mu.Unlock()
+ return errors.New("transport: Close() was already called")
+ }
+ t.state = closing
+ t.mu.Unlock()
+ close(t.shutdownChan)
+ err = t.conn.Close()
+ t.mu.Lock()
+ streams := t.activeStreams
+ t.activeStreams = nil
+ t.mu.Unlock()
+ // Notify all active streams.
+ for _, s := range streams {
+ s.mu.Lock()
+ if !s.headerDone {
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: ErrConnClosing})
+ }
+ return
+}
+
+// Write formats the data into HTTP2 data frame(s) and sends it out. The caller
+// should proceed only if Write returns nil.
+// TODO(zhaoq): opts.Delay is ignored in this implementation. Support it later
+// if it improves the performance.
+func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
+ r := bytes.NewBuffer(data)
+ for {
+ var p []byte
+ if r.Len() > 0 {
+ size := http2MaxFrameLen
+ s.sendQuotaPool.add(0)
+ // Wait until the stream has some quota to send the data.
+ sq, err := wait(s.ctx, t.shutdownChan, s.sendQuotaPool.acquire())
+ if err != nil {
+ return err
+ }
+ t.sendQuotaPool.add(0)
+ // Wait until the transport has some quota to send the data.
+ tq, err := wait(s.ctx, t.shutdownChan, t.sendQuotaPool.acquire())
+ if err != nil {
+ if _, ok := err.(StreamError); ok {
+ t.sendQuotaPool.cancel()
+ }
+ return err
+ }
+ if sq < size {
+ size = sq
+ }
+ if tq < size {
+ size = tq
+ }
+ p = r.Next(size)
+ ps := len(p)
+ if ps < sq {
+ // Overbooked stream quota. Return it back.
+ s.sendQuotaPool.add(sq - ps)
+ }
+ if ps < tq {
+ // Overbooked transport quota. Return it back.
+ t.sendQuotaPool.add(tq - ps)
+ }
+ }
+ var (
+ endStream bool
+ forceFlush bool
+ )
+ if opts.Last && r.Len() == 0 {
+ endStream = true
+ }
+ // Indicate there is a writer who is about to write a data frame.
+ t.framer.adjustNumWriters(1)
+ // Got some quota. Try to acquire writing privilege on the transport.
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ if t.framer.adjustNumWriters(-1) == 0 {
+ // This writer is the last one in this batch and has the
+ // responsibility to flush the buffered frames. It queues
+ // a flush request to controlBuf instead of flushing directly
+ // in order to avoid the race with other writing or flushing.
+ t.controlBuf.put(&flushIO{})
+ }
+ return err
+ }
+ if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 {
+ // Do a force flush iff this is last frame for the entire gRPC message
+ // and the caller is the only writer at this moment.
+ forceFlush = true
+ }
+ // If WriteData fails, all the pending streams will be handled
+ // by http2Client.Close(). No explicit CloseStream() needs to be
+ // invoked.
+ if err := t.framer.writeData(forceFlush, s.id, endStream, p); err != nil {
+ t.notifyError(err)
+ return ConnectionErrorf("transport: %v", err)
+ }
+ if t.framer.adjustNumWriters(-1) == 0 {
+ t.framer.flushWrite()
+ }
+ t.writableChan <- 0
+ if r.Len() == 0 {
+ break
+ }
+ }
+ if !opts.Last {
+ return nil
+ }
+ s.mu.Lock()
+ if s.state != streamDone {
+ if s.state == streamReadDone {
+ s.state = streamDone
+ } else {
+ s.state = streamWriteDone
+ }
+ }
+ s.mu.Unlock()
+ return nil
+}
+
+func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.activeStreams == nil {
+ // The transport is closing.
+ return nil, false
+ }
+ if s, ok := t.activeStreams[f.Header().StreamID]; ok {
+ return s, true
+ }
+ return nil, false
+}
+
+// updateWindow adjusts the inbound quota for the stream and the transport.
+// Window updates will deliver to the controller for sending when
+// the cumulative quota exceeds the corresponding threshold.
+func (t *http2Client) updateWindow(s *Stream, n uint32) {
+ swu, cwu := s.fc.onRead(n)
+ if swu > 0 {
+ t.controlBuf.put(&windowUpdate{s.id, swu})
+ }
+ if cwu > 0 {
+ t.controlBuf.put(&windowUpdate{0, cwu})
+ }
+}
+
+func (t *http2Client) handleData(f *http2.DataFrame) {
+ // Select the right stream to dispatch.
+ s, ok := t.getStream(f)
+ if !ok {
+ return
+ }
+ size := len(f.Data())
+ if err := s.fc.onData(uint32(size)); err != nil {
+ if _, ok := err.(ConnectionError); ok {
+ t.notifyError(err)
+ return
+ }
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ s.state = streamDone
+ s.statusCode = codes.Internal
+ s.statusDesc = err.Error()
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
+ return
+ }
+ // TODO(bradfitz, zhaoq): A copy is required here because there is no
+ // guarantee f.Data() is consumed before the arrival of next frame.
+ // Can this copy be eliminated?
+ data := make([]byte, size)
+ copy(data, f.Data())
+ s.write(recvMsg{data: data})
+}
+
+func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
+ s, ok := t.getStream(f)
+ if !ok {
+ return
+ }
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ s.state = streamDone
+ s.statusCode, ok = http2RSTErrConvTab[http2.ErrCode(f.ErrCode)]
+ if !ok {
+ grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode)
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+}
+
+func (t *http2Client) handleSettings(f *http2.SettingsFrame) {
+ if f.IsAck() {
+ return
+ }
+ f.ForeachSetting(func(s http2.Setting) error {
+ if v, ok := f.Value(s.ID); ok {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ switch s.ID {
+ case http2.SettingMaxConcurrentStreams:
+ t.maxStreams = v
+ case http2.SettingInitialWindowSize:
+ for _, s := range t.activeStreams {
+ // Adjust the sending quota for each s.
+ s.sendQuotaPool.reset(int(v - t.streamSendQuota))
+ }
+ t.streamSendQuota = v
+ }
+ }
+ return nil
+ })
+ t.controlBuf.put(&settings{ack: true})
+}
+
+func (t *http2Client) handlePing(f *http2.PingFrame) {
+ t.controlBuf.put(&ping{true})
+}
+
+func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
+ // TODO(zhaoq): GoAwayFrame handler to be implemented"
+}
+
+func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
+ id := f.Header().StreamID
+ incr := f.Increment
+ if id == 0 {
+ t.sendQuotaPool.add(int(incr))
+ return
+ }
+ if s, ok := t.getStream(f); ok {
+ s.sendQuotaPool.add(int(incr))
+ }
+}
+
+// operateHeader takes action on the decoded headers. It returns the current
+// stream if there are remaining headers on the wire (in the following
+// Continuation frame).
+func (t *http2Client) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool) (pendingStream *Stream) {
+ defer func() {
+ if pendingStream == nil {
+ hDec.state = decodeState{}
+ }
+ }()
+ endHeaders, err := hDec.decodeClientHTTP2Headers(frame)
+ if s == nil {
+ // s has been closed.
+ return nil
+ }
+ if err != nil {
+ s.write(recvMsg{err: err})
+ // Something wrong. Stops reading even when there is remaining.
+ return nil
+ }
+ if !endHeaders {
+ return s
+ }
+
+ s.mu.Lock()
+ if !s.headerDone {
+ if !endStream && len(hDec.state.mdata) > 0 {
+ s.header = hDec.state.mdata
+ }
+ close(s.headerChan)
+ s.headerDone = true
+ }
+ if !endStream || s.state == streamDone {
+ s.mu.Unlock()
+ return nil
+ }
+
+ if len(hDec.state.mdata) > 0 {
+ s.trailer = hDec.state.mdata
+ }
+ s.state = streamDone
+ s.statusCode = hDec.state.statusCode
+ s.statusDesc = hDec.state.statusDesc
+ s.mu.Unlock()
+
+ s.write(recvMsg{err: io.EOF})
+ return nil
+}
+
+// reader runs as a separate goroutine in charge of reading data from network
+// connection.
+//
+// TODO(zhaoq): currently one reader per transport. Investigate whether this is
+// optimal.
+// TODO(zhaoq): Check the validity of the incoming frame sequence.
+func (t *http2Client) reader() {
+ // Check the validity of server preface.
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ t.notifyError(err)
+ return
+ }
+ sf, ok := frame.(*http2.SettingsFrame)
+ if !ok {
+ t.notifyError(err)
+ return
+ }
+ t.handleSettings(sf)
+
+ hDec := newHPACKDecoder()
+ var curStream *Stream
+ // loop to keep reading incoming messages on this transport.
+ for {
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ t.notifyError(err)
+ return
+ }
+ switch frame := frame.(type) {
+ case *http2.HeadersFrame:
+ // operateHeaders has to be invoked regardless the value of curStream
+ // because the HPACK decoder needs to be updated using the received
+ // headers.
+ curStream, _ = t.getStream(frame)
+ endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
+ curStream = t.operateHeaders(hDec, curStream, frame, endStream)
+ case *http2.ContinuationFrame:
+ curStream = t.operateHeaders(hDec, curStream, frame, false)
+ case *http2.DataFrame:
+ t.handleData(frame)
+ case *http2.RSTStreamFrame:
+ t.handleRSTStream(frame)
+ case *http2.SettingsFrame:
+ t.handleSettings(frame)
+ case *http2.PingFrame:
+ t.handlePing(frame)
+ case *http2.GoAwayFrame:
+ t.handleGoAway(frame)
+ case *http2.WindowUpdateFrame:
+ t.handleWindowUpdate(frame)
+ default:
+ grpclog.Printf("transport: http2Client.reader got unhandled frame type %v.", frame)
+ }
+ }
+}
+
+// controller running in a separate goroutine takes charge of sending control
+// frames (e.g., window update, reset stream, setting, etc.) to the server.
+func (t *http2Client) controller() {
+ for {
+ select {
+ case i := <-t.controlBuf.get():
+ t.controlBuf.load()
+ select {
+ case <-t.writableChan:
+ switch i := i.(type) {
+ case *windowUpdate:
+ t.framer.writeWindowUpdate(true, i.streamID, i.increment)
+ case *settings:
+ if i.ack {
+ t.framer.writeSettingsAck(true)
+ } else {
+ t.framer.writeSettings(true, i.setting...)
+ }
+ case *resetStream:
+ t.framer.writeRSTStream(true, i.streamID, i.code)
+ case *flushIO:
+ t.framer.flushWrite()
+ case *ping:
+ // TODO(zhaoq): Ack with all-0 data now. will change to some
+ // meaningful content when this is actually in use.
+ t.framer.writePing(true, i.ack, [8]byte{})
+ default:
+ grpclog.Printf("transport: http2Client.controller got unexpected item type %v\n", i)
+ }
+ t.writableChan <- 0
+ continue
+ case <-t.shutdownChan:
+ return
+ }
+ case <-t.shutdownChan:
+ return
+ }
+ }
+}
+
+func (t *http2Client) Error() <-chan struct{} {
+ return t.errorChan
+}
+
+func (t *http2Client) notifyError(err error) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ // make sure t.errorChan is closed only once.
+ if t.state == reachable {
+ t.state = unreachable
+ close(t.errorChan)
+ grpclog.Printf("transport: http2Client.notifyError got notified that the client transport was broken %v.", err)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go
new file mode 100644
index 000000000..d39d713fa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http2_server.go
@@ -0,0 +1,670 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package transport
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "math"
+ "net"
+ "strconv"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+)
+
+// ErrIllegalHeaderWrite indicates that setting header is illegal because of
+// the stream's state.
+var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHeader was already called")
+
+// http2Server implements the ServerTransport interface with HTTP2.
+type http2Server struct {
+ conn net.Conn
+ maxStreamID uint32 // max stream ID ever seen
+ // writableChan synchronizes write access to the transport.
+ // A writer acquires the write lock by sending a value on writableChan
+ // and releases it by receiving from writableChan.
+ writableChan chan int
+ // shutdownChan is closed when Close is called.
+ // Blocking operations should select on shutdownChan to avoid
+ // blocking forever after Close.
+ shutdownChan chan struct{}
+ framer *framer
+ hBuf *bytes.Buffer // the buffer for HPACK encoding
+ hEnc *hpack.Encoder // HPACK encoder
+
+ // The max number of concurrent streams.
+ maxStreams uint32
+ // controlBuf delivers all the control related tasks (e.g., window
+ // updates, reset streams, and various settings) to the controller.
+ controlBuf *recvBuffer
+ fc *inFlow
+ // sendQuotaPool provides flow control to outbound message.
+ sendQuotaPool *quotaPool
+
+ mu sync.Mutex // guard the following
+ state transportState
+ activeStreams map[uint32]*Stream
+ // the per-stream outbound flow control window size set by the peer.
+ streamSendQuota uint32
+}
+
+// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
+// returned if something goes wrong.
+func newHTTP2Server(conn net.Conn, maxStreams uint32) (_ ServerTransport, err error) {
+ framer := newFramer(conn)
+ // Send initial settings as connection preface to client.
+ // TODO(zhaoq): Have a better way to signal "no limit" because 0 is
+ // permitted in the HTTP2 spec.
+ var settings []http2.Setting
+ // TODO(zhaoq): Have a better way to signal "no limit" because 0 is
+ // permitted in the HTTP2 spec.
+ if maxStreams == 0 {
+ maxStreams = math.MaxUint32
+ } else {
+ settings = append(settings, http2.Setting{http2.SettingMaxConcurrentStreams, maxStreams})
+ }
+ if initialWindowSize != defaultWindowSize {
+ settings = append(settings, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
+ }
+ if err := framer.writeSettings(true, settings...); err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ // Adjust the connection flow control window if needed.
+ if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
+ if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
+ return nil, ConnectionErrorf("transport: %v", err)
+ }
+ }
+ var buf bytes.Buffer
+ t := &http2Server{
+ conn: conn,
+ framer: framer,
+ hBuf: &buf,
+ hEnc: hpack.NewEncoder(&buf),
+ maxStreams: maxStreams,
+ controlBuf: newRecvBuffer(),
+ fc: &inFlow{limit: initialConnWindowSize},
+ sendQuotaPool: newQuotaPool(defaultWindowSize),
+ state: reachable,
+ writableChan: make(chan int, 1),
+ shutdownChan: make(chan struct{}),
+ activeStreams: make(map[uint32]*Stream),
+ streamSendQuota: defaultWindowSize,
+ }
+ go t.controller()
+ t.writableChan <- 0
+ return t, nil
+}
+
+// operateHeader takes action on the decoded headers. It returns the current
+// stream if there are remaining headers on the wire (in the following
+// Continuation frame).
+func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool, handle func(*Stream), wg *sync.WaitGroup) (pendingStream *Stream) {
+ defer func() {
+ if pendingStream == nil {
+ hDec.state = decodeState{}
+ }
+ }()
+ endHeaders, err := hDec.decodeServerHTTP2Headers(frame)
+ if s == nil {
+ // s has been closed.
+ return nil
+ }
+ if err != nil {
+ grpclog.Printf("transport: http2Server.operateHeader found %v", err)
+ if se, ok := err.(StreamError); ok {
+ t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
+ }
+ return nil
+ }
+ if endStream {
+ // s is just created by the caller. No lock needed.
+ s.state = streamReadDone
+ }
+ if !endHeaders {
+ return s
+ }
+ t.mu.Lock()
+ if t.state != reachable {
+ t.mu.Unlock()
+ return nil
+ }
+ if uint32(len(t.activeStreams)) >= t.maxStreams {
+ t.mu.Unlock()
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
+ return nil
+ }
+ s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
+ t.activeStreams[s.id] = s
+ t.mu.Unlock()
+ s.windowHandler = func(n int) {
+ t.updateWindow(s, uint32(n))
+ }
+ if hDec.state.timeoutSet {
+ s.ctx, s.cancel = context.WithTimeout(context.TODO(), hDec.state.timeout)
+ } else {
+ s.ctx, s.cancel = context.WithCancel(context.TODO())
+ }
+ // Cache the current stream to the context so that the server application
+ // can find out. Required when the server wants to send some metadata
+ // back to the client (unary call only).
+ s.ctx = newContextWithStream(s.ctx, s)
+ // Attach the received metadata to the context.
+ if len(hDec.state.mdata) > 0 {
+ s.ctx = metadata.NewContext(s.ctx, hDec.state.mdata)
+ }
+
+ s.dec = &recvBufferReader{
+ ctx: s.ctx,
+ recv: s.buf,
+ }
+ s.method = hDec.state.method
+
+ wg.Add(1)
+ go func() {
+ handle(s)
+ wg.Done()
+ }()
+ return nil
+}
+
+// HandleStreams receives incoming streams using the given handler. This is
+// typically run in a separate goroutine.
+func (t *http2Server) HandleStreams(handle func(*Stream)) {
+ // Check the validity of client preface.
+ preface := make([]byte, len(clientPreface))
+ if _, err := io.ReadFull(t.conn, preface); err != nil {
+ grpclog.Printf("transport: http2Server.HandleStreams failed to receive the preface from client: %v", err)
+ t.Close()
+ return
+ }
+ if !bytes.Equal(preface, clientPreface) {
+ grpclog.Printf("transport: http2Server.HandleStreams received bogus greeting from client: %q", preface)
+ t.Close()
+ return
+ }
+
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ grpclog.Printf("transport: http2Server.HandleStreams failed to read frame: %v", err)
+ t.Close()
+ return
+ }
+ sf, ok := frame.(*http2.SettingsFrame)
+ if !ok {
+ grpclog.Printf("transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
+ t.Close()
+ return
+ }
+ t.handleSettings(sf)
+
+ hDec := newHPACKDecoder()
+ var curStream *Stream
+ var wg sync.WaitGroup
+ defer wg.Wait()
+ for {
+ frame, err := t.framer.readFrame()
+ if err != nil {
+ t.Close()
+ return
+ }
+ switch frame := frame.(type) {
+ case *http2.HeadersFrame:
+ id := frame.Header().StreamID
+ if id%2 != 1 || id <= t.maxStreamID {
+ // illegal gRPC stream id.
+ grpclog.Println("transport: http2Server.HandleStreams received an illegal stream id: ", id)
+ t.Close()
+ break
+ }
+ t.maxStreamID = id
+ buf := newRecvBuffer()
+ fc := &inFlow{
+ limit: initialWindowSize,
+ conn: t.fc,
+ }
+ curStream = &Stream{
+ id: frame.Header().StreamID,
+ st: t,
+ buf: buf,
+ fc: fc,
+ }
+ endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
+ curStream = t.operateHeaders(hDec, curStream, frame, endStream, handle, &wg)
+ case *http2.ContinuationFrame:
+ curStream = t.operateHeaders(hDec, curStream, frame, false, handle, &wg)
+ case *http2.DataFrame:
+ t.handleData(frame)
+ case *http2.RSTStreamFrame:
+ t.handleRSTStream(frame)
+ case *http2.SettingsFrame:
+ t.handleSettings(frame)
+ case *http2.PingFrame:
+ t.handlePing(frame)
+ case *http2.WindowUpdateFrame:
+ t.handleWindowUpdate(frame)
+ case *http2.GoAwayFrame:
+ break
+ default:
+ grpclog.Printf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
+ }
+ }
+}
+
+func (t *http2Server) getStream(f http2.Frame) (*Stream, bool) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if t.activeStreams == nil {
+ // The transport is closing.
+ return nil, false
+ }
+ s, ok := t.activeStreams[f.Header().StreamID]
+ if !ok {
+ // The stream is already done.
+ return nil, false
+ }
+ return s, true
+}
+
+// updateWindow adjusts the inbound quota for the stream and the transport.
+// Window updates will deliver to the controller for sending when
+// the cumulative quota exceeds the corresponding threshold.
+func (t *http2Server) updateWindow(s *Stream, n uint32) {
+ swu, cwu := s.fc.onRead(n)
+ if swu > 0 {
+ t.controlBuf.put(&windowUpdate{s.id, swu})
+ }
+ if cwu > 0 {
+ t.controlBuf.put(&windowUpdate{0, cwu})
+ }
+}
+
+func (t *http2Server) handleData(f *http2.DataFrame) {
+ // Select the right stream to dispatch.
+ s, ok := t.getStream(f)
+ if !ok {
+ return
+ }
+ size := len(f.Data())
+ if err := s.fc.onData(uint32(size)); err != nil {
+ if _, ok := err.(ConnectionError); ok {
+ grpclog.Printf("transport: http2Server %v", err)
+ t.Close()
+ return
+ }
+ t.closeStream(s)
+ t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
+ return
+ }
+ // TODO(bradfitz, zhaoq): A copy is required here because there is no
+ // guarantee f.Data() is consumed before the arrival of next frame.
+ // Can this copy be eliminated?
+ data := make([]byte, size)
+ copy(data, f.Data())
+ s.write(recvMsg{data: data})
+ if f.Header().Flags.Has(http2.FlagDataEndStream) {
+ // Received the end of stream from the client.
+ s.mu.Lock()
+ if s.state != streamDone {
+ if s.state == streamWriteDone {
+ s.state = streamDone
+ } else {
+ s.state = streamReadDone
+ }
+ }
+ s.mu.Unlock()
+ s.write(recvMsg{err: io.EOF})
+ }
+}
+
+func (t *http2Server) handleRSTStream(f *http2.RSTStreamFrame) {
+ s, ok := t.getStream(f)
+ if !ok {
+ return
+ }
+ t.closeStream(s)
+}
+
+func (t *http2Server) handleSettings(f *http2.SettingsFrame) {
+ if f.IsAck() {
+ return
+ }
+ f.ForeachSetting(func(s http2.Setting) error {
+ if v, ok := f.Value(http2.SettingInitialWindowSize); ok {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ for _, s := range t.activeStreams {
+ s.sendQuotaPool.reset(int(v - t.streamSendQuota))
+ }
+ t.streamSendQuota = v
+ }
+ return nil
+ })
+ t.controlBuf.put(&settings{ack: true})
+}
+
+func (t *http2Server) handlePing(f *http2.PingFrame) {
+ t.controlBuf.put(&ping{true})
+}
+
+func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
+ id := f.Header().StreamID
+ incr := f.Increment
+ if id == 0 {
+ t.sendQuotaPool.add(int(incr))
+ return
+ }
+ if s, ok := t.getStream(f); ok {
+ s.sendQuotaPool.add(int(incr))
+ }
+}
+
+func (t *http2Server) writeHeaders(s *Stream, b *bytes.Buffer, endStream bool) error {
+ first := true
+ endHeaders := false
+ var err error
+ // Sends the headers in a single batch.
+ for !endHeaders {
+ size := t.hBuf.Len()
+ if size > http2MaxFrameLen {
+ size = http2MaxFrameLen
+ } else {
+ endHeaders = true
+ }
+ if first {
+ p := http2.HeadersFrameParam{
+ StreamID: s.id,
+ BlockFragment: b.Next(size),
+ EndStream: endStream,
+ EndHeaders: endHeaders,
+ }
+ err = t.framer.writeHeaders(endHeaders, p)
+ first = false
+ } else {
+ err = t.framer.writeContinuation(endHeaders, s.id, endHeaders, b.Next(size))
+ }
+ if err != nil {
+ t.Close()
+ return ConnectionErrorf("transport: %v", err)
+ }
+ }
+ return nil
+}
+
+// WriteHeader sends the header metedata md back to the client.
+func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
+ s.mu.Lock()
+ if s.headerOk || s.state == streamDone {
+ s.mu.Unlock()
+ return ErrIllegalHeaderWrite
+ }
+ s.headerOk = true
+ s.mu.Unlock()
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ return err
+ }
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ for k, v := range md {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ }
+ if err := t.writeHeaders(s, t.hBuf, false); err != nil {
+ return err
+ }
+ t.writableChan <- 0
+ return nil
+}
+
+// WriteStatus sends stream status to the client and terminates the stream.
+// There is no further I/O operations being able to perform on this stream.
+// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
+// OK is adopted.
+func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
+ s.mu.RLock()
+ if s.state == streamDone {
+ s.mu.RUnlock()
+ return nil
+ }
+ s.mu.RUnlock()
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ return err
+ }
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
+ t.hEnc.WriteField(
+ hpack.HeaderField{
+ Name: "grpc-status",
+ Value: strconv.Itoa(int(statusCode)),
+ })
+ t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-message", Value: statusDesc})
+ // Attach the trailer metadata.
+ for k, v := range s.trailer {
+ t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
+ }
+ if err := t.writeHeaders(s, t.hBuf, true); err != nil {
+ t.Close()
+ return err
+ }
+ t.closeStream(s)
+ t.writableChan <- 0
+ return nil
+}
+
+// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
+// is returns if it fails (e.g., framing error, transport error).
+func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
+ // TODO(zhaoq): Support multi-writers for a single stream.
+ var writeHeaderFrame bool
+ s.mu.Lock()
+ if !s.headerOk {
+ writeHeaderFrame = true
+ s.headerOk = true
+ }
+ s.mu.Unlock()
+ if writeHeaderFrame {
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ return err
+ }
+ t.hBuf.Reset()
+ t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
+ t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
+ p := http2.HeadersFrameParam{
+ StreamID: s.id,
+ BlockFragment: t.hBuf.Bytes(),
+ EndHeaders: true,
+ }
+ if err := t.framer.writeHeaders(false, p); err != nil {
+ t.Close()
+ return ConnectionErrorf("transport: %v", err)
+ }
+ t.writableChan <- 0
+ }
+ r := bytes.NewBuffer(data)
+ for {
+ if r.Len() == 0 {
+ return nil
+ }
+ size := http2MaxFrameLen
+ s.sendQuotaPool.add(0)
+ // Wait until the stream has some quota to send the data.
+ sq, err := wait(s.ctx, t.shutdownChan, s.sendQuotaPool.acquire())
+ if err != nil {
+ return err
+ }
+ t.sendQuotaPool.add(0)
+ // Wait until the transport has some quota to send the data.
+ tq, err := wait(s.ctx, t.shutdownChan, t.sendQuotaPool.acquire())
+ if err != nil {
+ if _, ok := err.(StreamError); ok {
+ t.sendQuotaPool.cancel()
+ }
+ return err
+ }
+ if sq < size {
+ size = sq
+ }
+ if tq < size {
+ size = tq
+ }
+ p := r.Next(size)
+ ps := len(p)
+ if ps < sq {
+ // Overbooked stream quota. Return it back.
+ s.sendQuotaPool.add(sq - ps)
+ }
+ if ps < tq {
+ // Overbooked transport quota. Return it back.
+ t.sendQuotaPool.add(tq - ps)
+ }
+ t.framer.adjustNumWriters(1)
+ // Got some quota. Try to acquire writing privilege on the
+ // transport.
+ if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
+ if t.framer.adjustNumWriters(-1) == 0 {
+ // This writer is the last one in this batch and has the
+ // responsibility to flush the buffered frames. It queues
+ // a flush request to controlBuf instead of flushing directly
+ // in order to avoid the race with other writing or flushing.
+ t.controlBuf.put(&flushIO{})
+ }
+ return err
+ }
+ var forceFlush bool
+ if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last {
+ forceFlush = true
+ }
+ if err := t.framer.writeData(forceFlush, s.id, false, p); err != nil {
+ t.Close()
+ return ConnectionErrorf("transport: %v", err)
+ }
+ if t.framer.adjustNumWriters(-1) == 0 {
+ t.framer.flushWrite()
+ }
+ t.writableChan <- 0
+ }
+
+}
+
+// controller running in a separate goroutine takes charge of sending control
+// frames (e.g., window update, reset stream, setting, etc.) to the server.
+func (t *http2Server) controller() {
+ for {
+ select {
+ case i := <-t.controlBuf.get():
+ t.controlBuf.load()
+ select {
+ case <-t.writableChan:
+ switch i := i.(type) {
+ case *windowUpdate:
+ t.framer.writeWindowUpdate(true, i.streamID, i.increment)
+ case *settings:
+ if i.ack {
+ t.framer.writeSettingsAck(true)
+ } else {
+ t.framer.writeSettings(true, i.setting...)
+ }
+ case *resetStream:
+ t.framer.writeRSTStream(true, i.streamID, i.code)
+ case *flushIO:
+ t.framer.flushWrite()
+ case *ping:
+ // TODO(zhaoq): Ack with all-0 data now. will change to some
+ // meaningful content when this is actually in use.
+ t.framer.writePing(true, i.ack, [8]byte{})
+ default:
+ grpclog.Printf("transport: http2Server.controller got unexpected item type %v\n", i)
+ }
+ t.writableChan <- 0
+ continue
+ case <-t.shutdownChan:
+ return
+ }
+ case <-t.shutdownChan:
+ return
+ }
+ }
+}
+
+// Close starts shutting down the http2Server transport.
+// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
+// could cause some resource issue. Revisit this later.
+func (t *http2Server) Close() (err error) {
+ t.mu.Lock()
+ if t.state == closing {
+ t.mu.Unlock()
+ return errors.New("transport: Close() was already called")
+ }
+ t.state = closing
+ streams := t.activeStreams
+ t.activeStreams = nil
+ t.mu.Unlock()
+ close(t.shutdownChan)
+ err = t.conn.Close()
+ // Notify all active streams.
+ for _, s := range streams {
+ s.write(recvMsg{err: ErrConnClosing})
+ }
+ return
+}
+
+// closeStream clears the footprint of a stream when the stream is not needed
+// any more.
+func (t *http2Server) closeStream(s *Stream) {
+ t.mu.Lock()
+ delete(t.activeStreams, s.id)
+ t.mu.Unlock()
+ if q := s.fc.restoreConn(); q > 0 {
+ t.controlBuf.put(&windowUpdate{0, q})
+ }
+ s.mu.Lock()
+ if s.state == streamDone {
+ s.mu.Unlock()
+ return
+ }
+ s.state = streamDone
+ s.mu.Unlock()
+ // In case stream sending and receiving are invoked in separate
+ // goroutines (e.g., bi-directional streaming), the caller needs
+ // to call cancel on the stream to interrupt the blocking on
+ // other goroutines.
+ s.cancel()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go
new file mode 100644
index 000000000..9fdd931ce
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util.go
@@ -0,0 +1,437 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package transport
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+ "sync/atomic"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2/hpack"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+)
+
+const (
+ // http2MaxFrameLen specifies the max length of a HTTP2 frame.
+ http2MaxFrameLen = 16384 // 16KB frame
+ // http://http2.github.io/http2-spec/#SettingValues
+ http2InitHeaderTableSize = 4096
+ // http2IOBufSize specifies the buffer size for sending frames.
+ http2IOBufSize = 32 * 1024
+)
+
+var (
+ clientPreface = []byte(http2.ClientPreface)
+)
+
+var http2RSTErrConvTab = map[http2.ErrCode]codes.Code{
+ http2.ErrCodeNo: codes.Internal,
+ http2.ErrCodeProtocol: codes.Internal,
+ http2.ErrCodeInternal: codes.Internal,
+ http2.ErrCodeFlowControl: codes.Internal,
+ http2.ErrCodeSettingsTimeout: codes.Internal,
+ http2.ErrCodeFrameSize: codes.Internal,
+ http2.ErrCodeRefusedStream: codes.Unavailable,
+ http2.ErrCodeCancel: codes.Canceled,
+ http2.ErrCodeCompression: codes.Internal,
+ http2.ErrCodeConnect: codes.Internal,
+ http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
+ http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
+}
+
+var statusCodeConvTab = map[codes.Code]http2.ErrCode{
+ codes.Internal: http2.ErrCodeInternal, // pick an arbitrary one which is matched.
+ codes.Canceled: http2.ErrCodeCancel,
+ codes.Unavailable: http2.ErrCodeRefusedStream,
+ codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm,
+ codes.PermissionDenied: http2.ErrCodeInadequateSecurity,
+}
+
+// Records the states during HPACK decoding. Must be reset once the
+// decoding of the entire headers are finished.
+type decodeState struct {
+ // statusCode caches the stream status received from the trailer
+ // the server sent. Client side only.
+ statusCode codes.Code
+ statusDesc string
+ // Server side only fields.
+ timeoutSet bool
+ timeout time.Duration
+ method string
+ // key-value metadata map from the peer.
+ mdata map[string]string
+}
+
+// An hpackDecoder decodes HTTP2 headers which may span multiple frames.
+type hpackDecoder struct {
+ h *hpack.Decoder
+ state decodeState
+ err error // The err when decoding
+}
+
+// A headerFrame is either a http2.HeaderFrame or http2.ContinuationFrame.
+type headerFrame interface {
+ Header() http2.FrameHeader
+ HeaderBlockFragment() []byte
+ HeadersEnded() bool
+}
+
+// isReservedHeader checks whether hdr belongs to HTTP2 headers
+// reserved by gRPC protocol. Any other headers are classified as the
+// user-specified metadata.
+func isReservedHeader(hdr string) bool {
+ if hdr[0] == ':' {
+ return true
+ }
+ switch hdr {
+ case "content-type",
+ "grpc-message-type",
+ "grpc-encoding",
+ "grpc-message",
+ "grpc-status",
+ "grpc-timeout",
+ "te",
+ "user-agent":
+ return true
+ default:
+ return false
+ }
+}
+
+func newHPACKDecoder() *hpackDecoder {
+ d := &hpackDecoder{}
+ d.h = hpack.NewDecoder(http2InitHeaderTableSize, func(f hpack.HeaderField) {
+ switch f.Name {
+ case "grpc-status":
+ code, err := strconv.Atoi(f.Value)
+ if err != nil {
+ d.err = StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err)
+ return
+ }
+ d.state.statusCode = codes.Code(code)
+ case "grpc-message":
+ d.state.statusDesc = f.Value
+ case "grpc-timeout":
+ d.state.timeoutSet = true
+ var err error
+ d.state.timeout, err = timeoutDecode(f.Value)
+ if err != nil {
+ d.err = StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err)
+ return
+ }
+ case ":path":
+ d.state.method = f.Value
+ default:
+ if !isReservedHeader(f.Name) {
+ if d.state.mdata == nil {
+ d.state.mdata = make(map[string]string)
+ }
+ k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
+ if err != nil {
+ grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
+ return
+ }
+ d.state.mdata[k] = v
+ }
+ }
+ })
+ return d
+}
+
+func (d *hpackDecoder) decodeClientHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
+ d.err = nil
+ _, err = d.h.Write(frame.HeaderBlockFragment())
+ if err != nil {
+ err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
+ }
+
+ if frame.HeadersEnded() {
+ if closeErr := d.h.Close(); closeErr != nil && err == nil {
+ err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
+ }
+ endHeaders = true
+ }
+
+ if err == nil && d.err != nil {
+ err = d.err
+ }
+ return
+}
+
+func (d *hpackDecoder) decodeServerHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
+ d.err = nil
+ _, err = d.h.Write(frame.HeaderBlockFragment())
+ if err != nil {
+ err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
+ }
+
+ if frame.HeadersEnded() {
+ if closeErr := d.h.Close(); closeErr != nil && err == nil {
+ err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
+ }
+ endHeaders = true
+ }
+
+ if err == nil && d.err != nil {
+ err = d.err
+ }
+ return
+}
+
+type timeoutUnit uint8
+
+const (
+ hour timeoutUnit = 'H'
+ minute timeoutUnit = 'M'
+ second timeoutUnit = 'S'
+ millisecond timeoutUnit = 'm'
+ microsecond timeoutUnit = 'u'
+ nanosecond timeoutUnit = 'n'
+)
+
+func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
+ switch u {
+ case hour:
+ return time.Hour, true
+ case minute:
+ return time.Minute, true
+ case second:
+ return time.Second, true
+ case millisecond:
+ return time.Millisecond, true
+ case microsecond:
+ return time.Microsecond, true
+ case nanosecond:
+ return time.Nanosecond, true
+ default:
+ }
+ return
+}
+
+const maxTimeoutValue int64 = 100000000 - 1
+
+// div does integer division and round-up the result. Note that this is
+// equivalent to (d+r-1)/r but has less chance to overflow.
+func div(d, r time.Duration) int64 {
+ if m := d % r; m > 0 {
+ return int64(d/r + 1)
+ }
+ return int64(d / r)
+}
+
+// TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
+func timeoutEncode(t time.Duration) string {
+ if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
+ return strconv.FormatInt(d, 10) + "n"
+ }
+ if d := div(t, time.Microsecond); d <= maxTimeoutValue {
+ return strconv.FormatInt(d, 10) + "u"
+ }
+ if d := div(t, time.Millisecond); d <= maxTimeoutValue {
+ return strconv.FormatInt(d, 10) + "m"
+ }
+ if d := div(t, time.Second); d <= maxTimeoutValue {
+ return strconv.FormatInt(d, 10) + "S"
+ }
+ if d := div(t, time.Minute); d <= maxTimeoutValue {
+ return strconv.FormatInt(d, 10) + "M"
+ }
+ // Note that maxTimeoutValue * time.Hour > MaxInt64.
+ return strconv.FormatInt(div(t, time.Hour), 10) + "H"
+}
+
+func timeoutDecode(s string) (time.Duration, error) {
+ size := len(s)
+ if size < 2 {
+ return 0, fmt.Errorf("transport: timeout string is too short: %q", s)
+ }
+ unit := timeoutUnit(s[size-1])
+ d, ok := timeoutUnitToDuration(unit)
+ if !ok {
+ return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s)
+ }
+ t, err := strconv.ParseInt(s[:size-1], 10, 64)
+ if err != nil {
+ return 0, err
+ }
+ return d * time.Duration(t), nil
+}
+
+type framer struct {
+ numWriters int32
+ reader io.Reader
+ writer *bufio.Writer
+ fr *http2.Framer
+}
+
+func newFramer(conn net.Conn) *framer {
+ f := &framer{
+ reader: conn,
+ writer: bufio.NewWriterSize(conn, http2IOBufSize),
+ }
+ f.fr = http2.NewFramer(f.writer, f.reader)
+ return f
+}
+
+func (f *framer) adjustNumWriters(i int32) int32 {
+ return atomic.AddInt32(&f.numWriters, i)
+}
+
+// The following writeXXX functions can only be called when the caller gets
+// unblocked from writableChan channel (i.e., owns the privilege to write).
+
+func (f *framer) writeContinuation(forceFlush bool, streamID uint32, endHeaders bool, headerBlockFragment []byte) error {
+ if err := f.fr.WriteContinuation(streamID, endHeaders, headerBlockFragment); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeData(forceFlush bool, streamID uint32, endStream bool, data []byte) error {
+ if err := f.fr.WriteData(streamID, endStream, data); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeGoAway(forceFlush bool, maxStreamID uint32, code http2.ErrCode, debugData []byte) error {
+ if err := f.fr.WriteGoAway(maxStreamID, code, debugData); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeHeaders(forceFlush bool, p http2.HeadersFrameParam) error {
+ if err := f.fr.WriteHeaders(p); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writePing(forceFlush, ack bool, data [8]byte) error {
+ if err := f.fr.WritePing(ack, data); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writePriority(forceFlush bool, streamID uint32, p http2.PriorityParam) error {
+ if err := f.fr.WritePriority(streamID, p); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writePushPromise(forceFlush bool, p http2.PushPromiseParam) error {
+ if err := f.fr.WritePushPromise(p); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeRSTStream(forceFlush bool, streamID uint32, code http2.ErrCode) error {
+ if err := f.fr.WriteRSTStream(streamID, code); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeSettings(forceFlush bool, settings ...http2.Setting) error {
+ if err := f.fr.WriteSettings(settings...); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeSettingsAck(forceFlush bool) error {
+ if err := f.fr.WriteSettingsAck(); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) writeWindowUpdate(forceFlush bool, streamID, incr uint32) error {
+ if err := f.fr.WriteWindowUpdate(streamID, incr); err != nil {
+ return err
+ }
+ if forceFlush {
+ return f.writer.Flush()
+ }
+ return nil
+}
+
+func (f *framer) flushWrite() error {
+ return f.writer.Flush()
+}
+
+func (f *framer) readFrame() (http2.Frame, error) {
+ return f.fr.ReadFrame()
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util_test.go
new file mode 100644
index 000000000..b5b18bf43
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/http_util_test.go
@@ -0,0 +1,87 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package transport
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+func TestTimeoutEncode(t *testing.T) {
+ for _, test := range []struct {
+ in string
+ out string
+ }{
+ {"12345678ns", "12345678n"},
+ {"123456789ns", "123457u"},
+ {"12345678us", "12345678u"},
+ {"123456789us", "123457m"},
+ {"12345678ms", "12345678m"},
+ {"123456789ms", "123457S"},
+ {"12345678s", "12345678S"},
+ {"123456789s", "2057614M"},
+ {"12345678m", "12345678M"},
+ {"123456789m", "2057614H"},
+ } {
+ d, err := time.ParseDuration(test.in)
+ if err != nil {
+ t.Fatalf("failed to parse duration string %s: %v", test.in, err)
+ }
+ out := timeoutEncode(d)
+ if out != test.out {
+ t.Fatalf("timeoutEncode(%s) = %s, want %s", test.in, out, test.out)
+ }
+ }
+}
+
+func TestTimeoutDecode(t *testing.T) {
+ for _, test := range []struct {
+ // input
+ s string
+ // output
+ d time.Duration
+ err error
+ }{
+ {"1234S", time.Second * 1234, nil},
+ {"1234x", 0, fmt.Errorf("transport: timeout unit is not recognized: %q", "1234x")},
+ {"1", 0, fmt.Errorf("transport: timeout string is too short: %q", "1")},
+ {"", 0, fmt.Errorf("transport: timeout string is too short: %q", "")},
+ } {
+ d, err := timeoutDecode(test.s)
+ if d != test.d || fmt.Sprint(err) != fmt.Sprint(test.err) {
+ t.Fatalf("timeoutDecode(%q) = %d, %v, want %d, %v", test.s, int64(d), err, int64(test.d), test.err)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/ca.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/ca.pem
new file mode 100644
index 000000000..999da8977
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/ca.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
+VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
+cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
+NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
+MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
+Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
+cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
+xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
+QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
+AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
+fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
+5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.key b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.key
new file mode 100644
index 000000000..ed00cd500
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
+bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
+BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
+AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
+ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
+omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
+ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
+9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
+MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
+IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
+USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
+VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
+RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.pem b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.pem
new file mode 100644
index 000000000..8e582e571
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/testdata/server1.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE-----
+MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
+MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
+dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
+MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
+BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
+c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
+JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
+RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
+3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
+b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
+KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
+wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
+aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go
new file mode 100644
index 000000000..86da2f4d9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/transport.go
@@ -0,0 +1,453 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+/*
+Package transport defines and implements message oriented communication channel
+to complete various transactions (e.g., an RPC).
+*/
+package transport
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/credentials"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/metadata"
+)
+
+// recvMsg represents the received msg from the transport. All transport
+// protocol specific info has been removed.
+type recvMsg struct {
+ data []byte
+ // nil: received some data
+ // io.EOF: stream is completed. data is nil.
+ // other non-nil error: transport failure. data is nil.
+ err error
+}
+
+func (recvMsg) isItem() bool {
+ return true
+}
+
+// All items in an out of a recvBuffer should be the same type.
+type item interface {
+ isItem() bool
+}
+
+// recvBuffer is an unbounded channel of item.
+type recvBuffer struct {
+ c chan item
+ mu sync.Mutex
+ backlog []item
+}
+
+func newRecvBuffer() *recvBuffer {
+ b := &recvBuffer{
+ c: make(chan item, 1),
+ }
+ return b
+}
+
+func (b *recvBuffer) put(r item) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ b.backlog = append(b.backlog, r)
+ select {
+ case b.c <- b.backlog[0]:
+ b.backlog = b.backlog[1:]
+ default:
+ }
+}
+
+func (b *recvBuffer) load() {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ if len(b.backlog) > 0 {
+ select {
+ case b.c <- b.backlog[0]:
+ b.backlog = b.backlog[1:]
+ default:
+ }
+ }
+}
+
+// get returns the channel that receives an item in the buffer.
+//
+// Upon receipt of an item, the caller should call load to send another
+// item onto the channel if there is any.
+func (b *recvBuffer) get() <-chan item {
+ return b.c
+}
+
+// recvBufferReader implements io.Reader interface to read the data from
+// recvBuffer.
+type recvBufferReader struct {
+ ctx context.Context
+ recv *recvBuffer
+ last *bytes.Reader // Stores the remaining data in the previous calls.
+ err error
+}
+
+// Read reads the next len(p) bytes from last. If last is drained, it tries to
+// read additional data from recv. It blocks if there no additional data available
+// in recv. If Read returns any non-nil error, it will continue to return that error.
+func (r *recvBufferReader) Read(p []byte) (n int, err error) {
+ if r.err != nil {
+ return 0, r.err
+ }
+ defer func() { r.err = err }()
+ if r.last != nil && r.last.Len() > 0 {
+ // Read remaining data left in last call.
+ return r.last.Read(p)
+ }
+ select {
+ case <-r.ctx.Done():
+ return 0, ContextErr(r.ctx.Err())
+ case i := <-r.recv.get():
+ r.recv.load()
+ m := i.(*recvMsg)
+ if m.err != nil {
+ return 0, m.err
+ }
+ r.last = bytes.NewReader(m.data)
+ return r.last.Read(p)
+ }
+}
+
+type streamState uint8
+
+const (
+ streamActive streamState = iota
+ streamWriteDone // EndStream sent
+ streamReadDone // EndStream received
+ streamDone // sendDone and recvDone or RSTStreamFrame is sent or received.
+)
+
+// Stream represents an RPC in the transport layer.
+type Stream struct {
+ id uint32
+ // nil for client side Stream.
+ st ServerTransport
+ // ctx is the associated context of the stream.
+ ctx context.Context
+ cancel context.CancelFunc
+ // method records the associated RPC method of the stream.
+ method string
+ buf *recvBuffer
+ dec io.Reader
+
+ fc *inFlow
+ recvQuota uint32
+ // The accumulated inbound quota pending for window update.
+ updateQuota uint32
+ // The handler to control the window update procedure for both this
+ // particular stream and the associated transport.
+ windowHandler func(int)
+
+ sendQuotaPool *quotaPool
+ // Close headerChan to indicate the end of reception of header metadata.
+ headerChan chan struct{}
+ // header caches the received header metadata.
+ header metadata.MD
+ // The key-value map of trailer metadata.
+ trailer metadata.MD
+
+ mu sync.RWMutex // guard the following
+ // headerOK becomes true from the first header is about to send.
+ headerOk bool
+ state streamState
+ // true iff headerChan is closed. Used to avoid closing headerChan
+ // multiple times.
+ headerDone bool
+ // the status received from the server.
+ statusCode codes.Code
+ statusDesc string
+}
+
+// Header acquires the key-value pairs of header metadata once it
+// is available. It blocks until i) the metadata is ready or ii) there is no
+// header metadata or iii) the stream is cancelled/expired.
+func (s *Stream) Header() (metadata.MD, error) {
+ select {
+ case <-s.ctx.Done():
+ return nil, ContextErr(s.ctx.Err())
+ case <-s.headerChan:
+ return s.header.Copy(), nil
+ }
+}
+
+// Trailer returns the cached trailer metedata. Note that if it is not called
+// after the entire stream is done, it could return an empty MD. Client
+// side only.
+func (s *Stream) Trailer() metadata.MD {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+ return s.trailer.Copy()
+}
+
+// ServerTransport returns the underlying ServerTransport for the stream.
+// The client side stream always returns nil.
+func (s *Stream) ServerTransport() ServerTransport {
+ return s.st
+}
+
+// Context returns the context of the stream.
+func (s *Stream) Context() context.Context {
+ return s.ctx
+}
+
+// Method returns the method for the stream.
+func (s *Stream) Method() string {
+ return s.method
+}
+
+// StatusCode returns statusCode received from the server.
+func (s *Stream) StatusCode() codes.Code {
+ return s.statusCode
+}
+
+// StatusDesc returns statusDesc received from the server.
+func (s *Stream) StatusDesc() string {
+ return s.statusDesc
+}
+
+// ErrIllegalTrailerSet indicates that the trailer has already been set or it
+// is too late to do so.
+var ErrIllegalTrailerSet = errors.New("transport: trailer has been set")
+
+// SetTrailer sets the trailer metadata which will be sent with the RPC status
+// by the server. This can only be called at most once. Server side only.
+func (s *Stream) SetTrailer(md metadata.MD) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.trailer != nil {
+ return ErrIllegalTrailerSet
+ }
+ s.trailer = md.Copy()
+ return nil
+}
+
+func (s *Stream) write(m recvMsg) {
+ s.buf.put(&m)
+}
+
+// Read reads all the data available for this Stream from the transport and
+// passes them into the decoder, which converts them into a gRPC message stream.
+// The error is io.EOF when the stream is done or another non-nil error if
+// the stream broke.
+func (s *Stream) Read(p []byte) (n int, err error) {
+ n, err = s.dec.Read(p)
+ if err != nil {
+ return
+ }
+ s.windowHandler(n)
+ return
+}
+
+type key int
+
+// The key to save transport.Stream in the context.
+const streamKey = key(0)
+
+// newContextWithStream creates a new context from ctx and attaches stream
+// to it.
+func newContextWithStream(ctx context.Context, stream *Stream) context.Context {
+ return context.WithValue(ctx, streamKey, stream)
+}
+
+// StreamFromContext returns the stream saved in ctx.
+func StreamFromContext(ctx context.Context) (s *Stream, ok bool) {
+ s, ok = ctx.Value(streamKey).(*Stream)
+ return
+}
+
+// state of transport
+type transportState int
+
+const (
+ reachable transportState = iota
+ unreachable
+ closing
+)
+
+// NewServerTransport creates a ServerTransport with conn or non-nil error
+// if it fails.
+func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32) (ServerTransport, error) {
+ return newHTTP2Server(conn, maxStreams)
+}
+
+// ConnectOptions covers all relevant options for dialing a server.
+type ConnectOptions struct {
+ Dialer func(string, time.Duration) (net.Conn, error)
+ AuthOptions []credentials.Credentials
+ Timeout time.Duration
+}
+
+// NewClientTransport establishes the transport with the required ConnectOptions
+// and returns it to the caller.
+func NewClientTransport(target string, opts *ConnectOptions) (ClientTransport, error) {
+ return newHTTP2Client(target, opts)
+}
+
+// Options provides additional hints and information for message
+// transmission.
+type Options struct {
+ // Indicate whether it is the last piece for this stream.
+ Last bool
+ // The hint to transport impl whether the data could be buffered for
+ // batching write. Transport impl can feel free to ignore it.
+ Delay bool
+}
+
+// CallHdr carries the information of a particular RPC.
+type CallHdr struct {
+ Host string // peer host
+ Method string // the operation to perform on the specified host
+}
+
+// ClientTransport is the common interface for all gRPC client side transport
+// implementations.
+type ClientTransport interface {
+ // Close tears down this transport. Once it returns, the transport
+ // should not be accessed any more. The caller must make sure this
+ // is called only once.
+ Close() error
+
+ // Write sends the data for the given stream. A nil stream indicates
+ // the write is to be performed on the transport as a whole.
+ Write(s *Stream, data []byte, opts *Options) error
+
+ // NewStream creates a Stream for an RPC.
+ NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error)
+
+ // CloseStream clears the footprint of a stream when the stream is
+ // not needed any more. The err indicates the error incurred when
+ // CloseStream is called. Must be called when a stream is finished
+ // unless the associated transport is closing.
+ CloseStream(stream *Stream, err error)
+
+ // Error returns a channel that is closed when some I/O error
+ // happens. Typically the caller should have a goroutine to monitor
+ // this in order to take action (e.g., close the current transport
+ // and create a new one) in error case. It should not return nil
+ // once the transport is initiated.
+ Error() <-chan struct{}
+}
+
+// ServerTransport is the common interface for all gRPC server side transport
+// implementations.
+type ServerTransport interface {
+ // WriteStatus sends the status of a stream to the client.
+ WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
+ // Write sends the data for the given stream.
+ Write(s *Stream, data []byte, opts *Options) error
+ // WriteHeader sends the header metedata for the given stream.
+ WriteHeader(s *Stream, md metadata.MD) error
+ // HandleStreams receives incoming streams using the given handler.
+ HandleStreams(func(*Stream))
+ // Close tears down the transport. Once it is called, the transport
+ // should not be accessed any more. All the pending streams and their
+ // handlers will be terminated asynchronously.
+ Close() error
+}
+
+// StreamErrorf creates an StreamError with the specified error code and description.
+func StreamErrorf(c codes.Code, format string, a ...interface{}) StreamError {
+ return StreamError{
+ Code: c,
+ Desc: fmt.Sprintf(format, a...),
+ }
+}
+
+// ConnectionErrorf creates an ConnectionError with the specified error description.
+func ConnectionErrorf(format string, a ...interface{}) ConnectionError {
+ return ConnectionError{
+ Desc: fmt.Sprintf(format, a...),
+ }
+}
+
+// ConnectionError is an error that results in the termination of the
+// entire connection and the retry of all the active streams.
+type ConnectionError struct {
+ Desc string
+}
+
+func (e ConnectionError) Error() string {
+ return fmt.Sprintf("connection error: desc = %q", e.Desc)
+}
+
+// Define some common ConnectionErrors.
+var ErrConnClosing = ConnectionError{Desc: "transport is closing"}
+
+// StreamError is an error that only affects one stream within a connection.
+type StreamError struct {
+ Code codes.Code
+ Desc string
+}
+
+func (e StreamError) Error() string {
+ return fmt.Sprintf("stream error: code = %d desc = %q", e.Code, e.Desc)
+}
+
+// ContextErr converts the error from context package into a StreamError.
+func ContextErr(err error) StreamError {
+ switch err {
+ case context.DeadlineExceeded:
+ return StreamErrorf(codes.DeadlineExceeded, "%v", err)
+ case context.Canceled:
+ return StreamErrorf(codes.Canceled, "%v", err)
+ }
+ panic(fmt.Sprintf("Unexpected error from context packet: %v", err))
+}
+
+// wait blocks until it can receive from ctx.Done, closing, or proceed.
+// If it receives from ctx.Done, it returns 0, the StreamError for ctx.Err.
+// If it receives from closing, it returns 0, ErrConnClosing.
+// If it receives from proceed, it returns the received integer, nil.
+func wait(ctx context.Context, closing <-chan struct{}, proceed <-chan int) (int, error) {
+ select {
+ case <-ctx.Done():
+ return 0, ContextErr(ctx.Err())
+ case <-closing:
+ return 0, ErrConnClosing
+ case i := <-proceed:
+ return i, nil
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/transport_test.go b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/transport_test.go
new file mode 100644
index 000000000..15e938945
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/transport/transport_test.go
@@ -0,0 +1,570 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * 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.
+ *
+ */
+
+package transport
+
+import (
+ "bytes"
+ "io"
+ "math"
+ "net"
+ "reflect"
+ "strconv"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/bradfitz/http2"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/grpclog"
+)
+
+type server struct {
+ lis net.Listener
+ port string
+ // channel to signal server is ready to serve.
+ readyChan chan bool
+ mu sync.Mutex
+ conns map[ServerTransport]bool
+}
+
+var (
+ expectedRequest = []byte("ping")
+ expectedResponse = []byte("pong")
+ expectedRequestLarge = make([]byte, initialWindowSize*2)
+ expectedResponseLarge = make([]byte, initialWindowSize*2)
+)
+
+type testStreamHandler struct {
+ t ServerTransport
+}
+
+type hType int
+
+const (
+ normal hType = iota
+ suspended
+ misbehaved
+)
+
+func (h *testStreamHandler) handleStream(s *Stream) {
+ req := expectedRequest
+ resp := expectedResponse
+ if s.Method() == "foo.Large" {
+ req = expectedRequestLarge
+ resp = expectedResponseLarge
+ }
+ p := make([]byte, len(req))
+ _, err := io.ReadFull(s, p)
+ if err != nil || !bytes.Equal(p, req) {
+ if err == ErrConnClosing {
+ return
+ }
+ grpclog.Fatalf("handleStream got error: %v, want ; result: %v, want %v", err, p, req)
+ }
+ // send a response back to the client.
+ h.t.Write(s, resp, &Options{})
+ // send the trailer to end the stream.
+ h.t.WriteStatus(s, codes.OK, "")
+}
+
+// handleStreamSuspension blocks until s.ctx is canceled.
+func (h *testStreamHandler) handleStreamSuspension(s *Stream) {
+ <-s.ctx.Done()
+}
+
+func (h *testStreamHandler) handleStreamMisbehave(s *Stream) {
+ conn, ok := s.ServerTransport().(*http2Server)
+ if !ok {
+ grpclog.Fatalf("Failed to convert %v to *http2Server", s.ServerTransport())
+ }
+ size := 1
+ if s.Method() == "foo.MaxFrame" {
+ size = http2MaxFrameLen
+ }
+ // Drain the client side stream flow control window.
+ var sent int
+ for sent <= initialWindowSize {
+ <-conn.writableChan
+ if err := conn.framer.writeData(true, s.id, false, make([]byte, size)); err != nil {
+ conn.writableChan <- 0
+ break
+ }
+ conn.writableChan <- 0
+ sent += size
+ }
+}
+
+// start starts server. Other goroutines should block on s.readyChan for futher operations.
+func (s *server) start(port int, maxStreams uint32, ht hType) {
+ var err error
+ if port == 0 {
+ s.lis, err = net.Listen("tcp", ":0")
+ } else {
+ s.lis, err = net.Listen("tcp", ":"+strconv.Itoa(port))
+ }
+ if err != nil {
+ grpclog.Fatalf("failed to listen: %v", err)
+ }
+ _, p, err := net.SplitHostPort(s.lis.Addr().String())
+ if err != nil {
+ grpclog.Fatalf("failed to parse listener address: %v", err)
+ }
+ s.port = p
+ s.conns = make(map[ServerTransport]bool)
+ if s.readyChan != nil {
+ close(s.readyChan)
+ }
+ for {
+ conn, err := s.lis.Accept()
+ if err != nil {
+ return
+ }
+ t, err := NewServerTransport("http2", conn, maxStreams)
+ if err != nil {
+ return
+ }
+ s.mu.Lock()
+ if s.conns == nil {
+ s.mu.Unlock()
+ t.Close()
+ return
+ }
+ s.conns[t] = true
+ s.mu.Unlock()
+ h := &testStreamHandler{t}
+ switch ht {
+ case suspended:
+ go t.HandleStreams(h.handleStreamSuspension)
+ case misbehaved:
+ go t.HandleStreams(h.handleStreamMisbehave)
+ default:
+ go t.HandleStreams(h.handleStream)
+ }
+ }
+}
+
+func (s *server) wait(t *testing.T, timeout time.Duration) {
+ select {
+ case <-s.readyChan:
+ case <-time.After(timeout):
+ t.Fatalf("Timed out after %v waiting for server to be ready", timeout)
+ }
+}
+
+func (s *server) stop() {
+ s.lis.Close()
+ s.mu.Lock()
+ for c := range s.conns {
+ c.Close()
+ }
+ s.conns = nil
+ s.mu.Unlock()
+}
+
+func setUp(t *testing.T, port int, maxStreams uint32, ht hType) (*server, ClientTransport) {
+ server := &server{readyChan: make(chan bool)}
+ go server.start(port, maxStreams, ht)
+ server.wait(t, 2*time.Second)
+ addr := "localhost:" + server.port
+ var (
+ ct ClientTransport
+ connErr error
+ )
+ ct, connErr = NewClientTransport(addr, &ConnectOptions{})
+ if connErr != nil {
+ t.Fatalf("failed to create transport: %v", connErr)
+ }
+ return server, ct
+}
+
+func TestClientSendAndReceive(t *testing.T) {
+ server, ct := setUp(t, 0, math.MaxUint32, normal)
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo.Small",
+ }
+ s1, err1 := ct.NewStream(context.Background(), callHdr)
+ if err1 != nil {
+ t.Fatalf("failed to open stream: %v", err1)
+ }
+ if s1.id != 1 {
+ t.Fatalf("wrong stream id: %d", s1.id)
+ }
+ s2, err2 := ct.NewStream(context.Background(), callHdr)
+ if err2 != nil {
+ t.Fatalf("failed to open stream: %v", err2)
+ }
+ if s2.id != 3 {
+ t.Fatalf("wrong stream id: %d", s2.id)
+ }
+ opts := Options{
+ Last: true,
+ Delay: false,
+ }
+ if err := ct.Write(s1, expectedRequest, &opts); err != nil {
+ t.Fatalf("failed to send data: %v", err)
+ }
+ p := make([]byte, len(expectedResponse))
+ _, recvErr := io.ReadFull(s1, p)
+ if recvErr != nil || !bytes.Equal(p, expectedResponse) {
+ t.Fatalf("Error: %v, want ; Result: %v, want %v", recvErr, p, expectedResponse)
+ }
+ _, recvErr = io.ReadFull(s1, p)
+ if recvErr != io.EOF {
+ t.Fatalf("Error: %v; want ", recvErr)
+ }
+ ct.Close()
+ server.stop()
+}
+
+func TestClientErrorNotify(t *testing.T) {
+ server, ct := setUp(t, 0, math.MaxUint32, normal)
+ go server.stop()
+ // ct.reader should detect the error and activate ct.Error().
+ <-ct.Error()
+ ct.Close()
+}
+
+func performOneRPC(ct ClientTransport) {
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo.Small",
+ }
+ s, err := ct.NewStream(context.Background(), callHdr)
+ if err != nil {
+ return
+ }
+ opts := Options{
+ Last: true,
+ Delay: false,
+ }
+ if err := ct.Write(s, expectedRequest, &opts); err == nil {
+ time.Sleep(5 * time.Millisecond)
+ // The following s.Recv()'s could error out because the
+ // underlying transport is gone.
+ //
+ // Read response
+ p := make([]byte, len(expectedResponse))
+ io.ReadFull(s, p)
+ // Read io.EOF
+ io.ReadFull(s, p)
+ }
+}
+
+func TestClientMix(t *testing.T) {
+ s, ct := setUp(t, 0, math.MaxUint32, normal)
+ go func(s *server) {
+ time.Sleep(5 * time.Second)
+ s.stop()
+ }(s)
+ go func(ct ClientTransport) {
+ <-ct.Error()
+ ct.Close()
+ }(ct)
+ for i := 0; i < 1000; i++ {
+ time.Sleep(10 * time.Millisecond)
+ go performOneRPC(ct)
+ }
+}
+
+func TestExceedMaxStreamsLimit(t *testing.T) {
+ server, ct := setUp(t, 0, 1, normal)
+ defer func() {
+ ct.Close()
+ server.stop()
+ }()
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo.Small",
+ }
+ // Creates the 1st stream and keep it alive.
+ _, err1 := ct.NewStream(context.Background(), callHdr)
+ if err1 != nil {
+ t.Fatalf("failed to open stream: %v", err1)
+ }
+ // Creates the 2nd stream. It has chance to succeed when the settings
+ // frame from the server has not received at the client.
+ s, err2 := ct.NewStream(context.Background(), callHdr)
+ if err2 != nil {
+ se, ok := err2.(StreamError)
+ if !ok {
+ t.Fatalf("Received unexpected error %v", err2)
+ }
+ if se.Code != codes.Unavailable {
+ t.Fatalf("Got error code: %d, want: %d", se.Code, codes.Unavailable)
+ }
+ return
+ }
+ // If the 2nd stream is created successfully, sends the request.
+ if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
+ t.Fatalf("failed to send data: %v", err)
+ }
+ // The 2nd stream was rejected by the server via a reset.
+ p := make([]byte, len(expectedResponse))
+ _, recvErr := io.ReadFull(s, p)
+ if recvErr != io.EOF || s.StatusCode() != codes.Unavailable {
+ t.Fatalf("Error: %v, StatusCode: %d; want , %d", recvErr, s.StatusCode(), codes.Unavailable)
+ }
+ // Server's setting has been received. From now on, new stream will be rejected instantly.
+ _, err3 := ct.NewStream(context.Background(), callHdr)
+ if err3 == nil {
+ t.Fatalf("Received unexpected , want an error with code %d", codes.Unavailable)
+ }
+ if se, ok := err3.(StreamError); !ok || se.Code != codes.Unavailable {
+ t.Fatalf("Got: %v, want a StreamError with error code %d", err3, codes.Unavailable)
+ }
+}
+
+func TestLargeMessage(t *testing.T) {
+ server, ct := setUp(t, 0, math.MaxUint32, normal)
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo.Large",
+ }
+ var wg sync.WaitGroup
+ for i := 0; i < 2; i++ {
+ wg.Add(1)
+ go func() {
+ s, err := ct.NewStream(context.Background(), callHdr)
+ if err != nil {
+ t.Fatalf("failed to open stream: %v", err)
+ }
+ if err := ct.Write(s, expectedRequestLarge, &Options{Last: true, Delay: false}); err != nil {
+ t.Fatalf("failed to send data: %v", err)
+ }
+ p := make([]byte, len(expectedResponseLarge))
+ _, recvErr := io.ReadFull(s, p)
+ if recvErr != nil || !bytes.Equal(p, expectedResponseLarge) {
+ t.Fatalf("Error: %v, want ; Result len: %d, want len %d", recvErr, len(p), len(expectedResponseLarge))
+ }
+ _, recvErr = io.ReadFull(s, p)
+ if recvErr != io.EOF {
+ t.Fatalf("Error: %v; want ", recvErr)
+ }
+ wg.Done()
+ }()
+ }
+ wg.Wait()
+ ct.Close()
+ server.stop()
+}
+
+func TestLargeMessageSuspension(t *testing.T) {
+ server, ct := setUp(t, 0, math.MaxUint32, suspended)
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo.Large",
+ }
+ // Set a long enough timeout for writing a large message out.
+ ctx, _ := context.WithTimeout(context.Background(), time.Second)
+ s, err := ct.NewStream(ctx, callHdr)
+ if err != nil {
+ t.Fatalf("failed to open stream: %v", err)
+ }
+ // Write should not be done successfully due to flow control.
+ err = ct.Write(s, expectedRequestLarge, &Options{Last: true, Delay: false})
+ expectedErr := StreamErrorf(codes.DeadlineExceeded, "%v", context.DeadlineExceeded)
+ if err == nil || err != expectedErr {
+ t.Fatalf("Write got %v, want %v", err, expectedErr)
+ }
+ ct.Close()
+ server.stop()
+}
+
+func TestServerWithMisbehavedClient(t *testing.T) {
+ server, ct := setUp(t, 0, math.MaxUint32, suspended)
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo",
+ }
+ var sc *http2Server
+ // Wait until the server transport is setup.
+ for {
+ server.mu.Lock()
+ if len(server.conns) == 0 {
+ server.mu.Unlock()
+ time.Sleep(time.Millisecond)
+ continue
+ }
+ for k := range server.conns {
+ var ok bool
+ sc, ok = k.(*http2Server)
+ if !ok {
+ t.Fatalf("Failed to convert %v to *http2Server", k)
+ }
+ }
+ server.mu.Unlock()
+ break
+ }
+ cc, ok := ct.(*http2Client)
+ if !ok {
+ t.Fatalf("Failed to convert %v to *http2Client", ct)
+ }
+ // Test server behavior for violation of stream flow control window size restriction.
+ s, err := ct.NewStream(context.Background(), callHdr)
+ if err != nil {
+ t.Fatalf("Failed to open stream: %v", err)
+ }
+ var sent int
+ // Drain the stream flow control window
+ <-cc.writableChan
+ if err = cc.framer.writeData(true, s.id, false, make([]byte, http2MaxFrameLen)); err != nil {
+ t.Fatalf("Failed to write data: %v", err)
+ }
+ cc.writableChan <- 0
+ sent += http2MaxFrameLen
+ // Wait until the server creates the corresponding stream and receive some data.
+ var ss *Stream
+ for {
+ time.Sleep(time.Millisecond)
+ sc.mu.Lock()
+ if len(sc.activeStreams) == 0 {
+ sc.mu.Unlock()
+ continue
+ }
+ ss = sc.activeStreams[s.id]
+ sc.mu.Unlock()
+ ss.fc.mu.Lock()
+ if ss.fc.pendingData > 0 {
+ ss.fc.mu.Unlock()
+ break
+ }
+ ss.fc.mu.Unlock()
+ }
+ if ss.fc.pendingData != http2MaxFrameLen || ss.fc.pendingUpdate != 0 || sc.fc.pendingData != http2MaxFrameLen || sc.fc.pendingUpdate != 0 {
+ t.Fatalf("Server mistakenly updates inbound flow control params: got %d, %d, %d, %d; want %d, %d, %d, %d", ss.fc.pendingData, ss.fc.pendingUpdate, sc.fc.pendingData, sc.fc.pendingUpdate, http2MaxFrameLen, 0, http2MaxFrameLen, 0)
+ }
+ // Keep sending until the server inbound window is drained for that stream.
+ for sent <= initialWindowSize {
+ <-cc.writableChan
+ if err = cc.framer.writeData(true, s.id, false, make([]byte, 1)); err != nil {
+ t.Fatalf("Failed to write data: %v", err)
+ }
+ cc.writableChan <- 0
+ sent++
+ }
+ // Server sent a resetStream for s already.
+ code := http2RSTErrConvTab[http2.ErrCodeFlowControl]
+ if _, err := io.ReadFull(s, make([]byte, 1)); err != io.EOF || s.statusCode != code {
+ t.Fatalf("%v got err %v with statusCode %d, want err with statusCode %d", s, err, s.statusCode, code)
+ }
+
+ if ss.fc.pendingData != 0 || ss.fc.pendingUpdate != 0 || sc.fc.pendingData != 0 || sc.fc.pendingUpdate != initialWindowSize {
+ t.Fatalf("Server mistakenly resets inbound flow control params: got %d, %d, %d, %d; want 0, 0, 0, %d", ss.fc.pendingData, ss.fc.pendingUpdate, sc.fc.pendingData, sc.fc.pendingUpdate, initialWindowSize)
+ }
+ ct.CloseStream(s, nil)
+ // Test server behavior for violation of connection flow control window size restriction.
+ //
+ // Keep creating new streams until the connection window is drained on the server and
+ // the server tears down the connection.
+ for {
+ s, err := ct.NewStream(context.Background(), callHdr)
+ if err != nil {
+ // The server tears down the connection.
+ break
+ }
+ <-cc.writableChan
+ cc.framer.writeData(true, s.id, true, make([]byte, http2MaxFrameLen))
+ cc.writableChan <- 0
+ }
+ ct.Close()
+ server.stop()
+}
+
+func TestClientWithMisbehavedServer(t *testing.T) {
+ server, ct := setUp(t, 0, math.MaxUint32, misbehaved)
+ callHdr := &CallHdr{
+ Host: "localhost",
+ Method: "foo",
+ }
+ conn, ok := ct.(*http2Client)
+ if !ok {
+ t.Fatalf("Failed to convert %v to *http2Client", ct)
+ }
+ // Test the logic for the violation of stream flow control window size restriction.
+ s, err := ct.NewStream(context.Background(), callHdr)
+ if err != nil {
+ t.Fatalf("Failed to open stream: %v", err)
+ }
+ if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
+ t.Fatalf("Failed to write: %v", err)
+ }
+ // Read without window update.
+ for {
+ p := make([]byte, http2MaxFrameLen)
+ if _, err = s.dec.Read(p); err != nil {
+ break
+ }
+ }
+ if s.fc.pendingData != initialWindowSize || s.fc.pendingUpdate != 0 || conn.fc.pendingData != initialWindowSize || conn.fc.pendingUpdate != 0 {
+ t.Fatalf("Client mistakenly updates inbound flow control params: got %d, %d, %d, %d; want %d, %d, %d, %d", s.fc.pendingData, s.fc.pendingUpdate, conn.fc.pendingData, conn.fc.pendingUpdate, initialWindowSize, 0, initialWindowSize, 0)
+ }
+ if err != io.EOF || s.statusCode != codes.Internal {
+ t.Fatalf("Got err %v and the status code %d, want and the code %d", err, s.statusCode, codes.Internal)
+ }
+ conn.CloseStream(s, err)
+ if s.fc.pendingData != 0 || s.fc.pendingUpdate != 0 || conn.fc.pendingData != 0 || conn.fc.pendingUpdate != initialWindowSize {
+ t.Fatalf("Client mistakenly resets inbound flow control params: got %d, %d, %d, %d; want 0, 0, 0, %d", s.fc.pendingData, s.fc.pendingUpdate, conn.fc.pendingData, conn.fc.pendingUpdate, initialWindowSize)
+ }
+ // Test the logic for the violation of the connection flow control window size restriction.
+ //
+ // Generate enough streams to drain the connection window.
+ callHdr = &CallHdr{
+ Host: "localhost",
+ Method: "foo.MaxFrame",
+ }
+ for i := 0; i < int(initialConnWindowSize/initialWindowSize+10); i++ {
+ s, err := ct.NewStream(context.Background(), callHdr)
+ if err != nil {
+ t.Fatalf("Failed to open stream: %v", err)
+ }
+ if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
+ break
+ }
+ }
+ // http2Client.errChan is closed due to connection flow control window size violation.
+ <-conn.Error()
+ ct.Close()
+ server.stop()
+}
+
+func TestStreamContext(t *testing.T) {
+ expectedStream := Stream{}
+ ctx := newContextWithStream(context.Background(), &expectedStream)
+ s, ok := StreamFromContext(ctx)
+ if !ok || !reflect.DeepEqual(expectedStream, *s) {
+ t.Fatalf("GetStreamFromContext(%v) = %v, %t, want: %v, true", ctx, *s, ok, expectedStream)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/README.md b/vendor/github.com/coreos/etcd/client/README.md
new file mode 100644
index 000000000..bb51bfeb1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/README.md
@@ -0,0 +1,110 @@
+# etcd/client
+
+etcd/client is the Go client library for etcd.
+
+[](https://godoc.org/github.com/coreos/etcd/client)
+
+## Install
+
+```bash
+go get github.com/coreos/etcd/client
+```
+
+## Usage
+
+```go
+package main
+
+import (
+ "log"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/client"
+)
+
+func main() {
+ cfg := client.Config{
+ Endpoints: []string{"http://127.0.0.1:2379"},
+ Transport: client.DefaultTransport,
+ // set timeout per request to fail fast when the target endpoint is unavailable
+ HeaderTimeoutPerRequest: time.Second,
+ }
+ c, err := client.New(cfg)
+ if err != nil {
+ log.Fatal(err)
+ }
+ kapi := client.NewKeysAPI(c)
+ // set "/foo" key with "bar" value
+ log.Print("Setting '/foo' key with 'bar' value")
+ resp, err := kapi.Set(context.Background(), "/foo", "bar", nil)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ // print common key info
+ log.Printf("Set is done. Metadata is %q\n", resp)
+ }
+ // get "/foo" key's value
+ log.Print("Getting '/foo' key value")
+ resp, err = kapi.Get(context.Background(), "/foo", nil)
+ if err != nil {
+ log.Fatal(err)
+ } else {
+ // print common key info
+ log.Printf("Get is done. Metadata is %q\n", resp)
+ // print value
+ log.Printf("%q key has %q value\n", resp.Node.Key, resp.Node.Value)
+ }
+}
+```
+
+## Error Handling
+
+etcd client might return three types of errors.
+
+- context error
+
+Each API call has its first parameter as `context`. A context can be canceled or have an attached deadline. If the context is canceled or reaches its deadline, the responding context error will be returned no matter what internal errors the API call has already encountered.
+
+- cluster error
+
+Each API call tries to send request to the cluster endpoints one by one until it successfully gets a response. If a requests to an endpoint fails, due to exceeding per request timeout or connection issues, the error will be added into a list of errors. If all possible endpoints fail, a cluster error that includes all encountered errors will be returned.
+
+- response error
+
+If the response gets from the cluster is invalid, a plain string error will be returned. For example, it might be a invalid JSON error.
+
+Here is the example code to handle client errors:
+
+```go
+cfg := client.Config{Endpoints: []string{"http://etcd1:2379,http://etcd2:2379,http://etcd3:2379"}}
+c, err := client.New(cfg)
+if err != nil {
+ log.Fatal(err)
+}
+
+kapi := client.NewKeysAPI(c)
+resp, err := kapi.Set(ctx, "test", "bar", nil)
+if err != nil {
+ if err == context.Canceled {
+ // ctx is canceled by another routine
+ } else if err == context.DeadlineExceeded {
+ // ctx is attached with a deadline and it exceeded
+ } else if cerr, ok := err.(*client.ClusterError); ok {
+ // process (cerr.Errors)
+ } else {
+ // bad cluster endpoints, which are not etcd servers
+ }
+}
+```
+
+
+## Caveat
+
+1. etcd/client prefers to use the same endpoint as long as the endpoint continues to work well. This saves socket resources, and improves efficiency for both client and server side. This preference doesn't remove consistency from the data consumed by the client because data replicated to each etcd member has already passed through the consensus process.
+
+2. etcd/client does round-robin rotation on other available endpoints if the preferred endpoint isn't functioning properly. For example, if the member that etcd/client connects to is hard killed, etcd/client will fail on the first attempt with the killed member, and succeed on the second attempt with another member. If it fails to talk to all available endpoints, it will return all errors happened.
+
+3. Default etcd/client cannot handle the case that the remote server is SIGSTOPed now. TCP keepalive mechanism doesn't help in this scenario because operating system may still send TCP keep-alive packets. Over time we'd like to improve this functionality, but solving this issue isn't high priority because a real-life case in which a server is stopped, but the connection is kept alive, hasn't been brought to our attention.
+
+4. etcd/client cannot detect whether the member in use is healthy when doing read requests. If the member is isolated from the cluster, etcd/client may retrieve outdated data. As a workaround, users could monitor experimental /health endpoint for member healthy information. We are improving it at [#3265](https://github.com/coreos/etcd/issues/3265).
diff --git a/vendor/github.com/coreos/etcd/client/auth_role.go b/vendor/github.com/coreos/etcd/client/auth_role.go
new file mode 100644
index 000000000..e771db7f4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/auth_role.go
@@ -0,0 +1,235 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "bytes"
+ "encoding/json"
+ "net/http"
+ "net/url"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+type Role struct {
+ Role string `json:"role"`
+ Permissions Permissions `json:"permissions"`
+ Grant *Permissions `json:"grant,omitempty"`
+ Revoke *Permissions `json:"revoke,omitempty"`
+}
+
+type Permissions struct {
+ KV rwPermission `json:"kv"`
+}
+
+type rwPermission struct {
+ Read []string `json:"read"`
+ Write []string `json:"write"`
+}
+
+type PermissionType int
+
+const (
+ ReadPermission PermissionType = iota
+ WritePermission
+ ReadWritePermission
+)
+
+// NewAuthRoleAPI constructs a new AuthRoleAPI that uses HTTP to
+// interact with etcd's role creation and modification features.
+func NewAuthRoleAPI(c Client) AuthRoleAPI {
+ return &httpAuthRoleAPI{
+ client: c,
+ }
+}
+
+type AuthRoleAPI interface {
+ // Add a role.
+ AddRole(ctx context.Context, role string) error
+
+ // Remove a role.
+ RemoveRole(ctx context.Context, role string) error
+
+ // Get role details.
+ GetRole(ctx context.Context, role string) (*Role, error)
+
+ // Grant a role some permission prefixes for the KV store.
+ GrantRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
+
+ // Revoke some some permission prefixes for a role on the KV store.
+ RevokeRoleKV(ctx context.Context, role string, prefixes []string, permType PermissionType) (*Role, error)
+
+ // List roles.
+ ListRoles(ctx context.Context) ([]string, error)
+}
+
+type httpAuthRoleAPI struct {
+ client httpClient
+}
+
+type authRoleAPIAction struct {
+ verb string
+ name string
+ role *Role
+}
+
+type authRoleAPIList struct{}
+
+func (list *authRoleAPIList) HTTPRequest(ep url.URL) *http.Request {
+ u := v2AuthURL(ep, "roles", "")
+ req, _ := http.NewRequest("GET", u.String(), nil)
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+func (l *authRoleAPIAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2AuthURL(ep, "roles", l.name)
+ if l.role == nil {
+ req, _ := http.NewRequest(l.verb, u.String(), nil)
+ return req
+ }
+ b, err := json.Marshal(l.role)
+ if err != nil {
+ panic(err)
+ }
+ body := bytes.NewReader(b)
+ req, _ := http.NewRequest(l.verb, u.String(), body)
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+func (r *httpAuthRoleAPI) ListRoles(ctx context.Context) ([]string, error) {
+ resp, body, err := r.client.Do(ctx, &authRoleAPIList{})
+ if err != nil {
+ return nil, err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
+ return nil, err
+ }
+ var userList struct {
+ Roles []string `json:"roles"`
+ }
+ err = json.Unmarshal(body, &userList)
+ if err != nil {
+ return nil, err
+ }
+ return userList.Roles, nil
+}
+
+func (r *httpAuthRoleAPI) AddRole(ctx context.Context, rolename string) error {
+ role := &Role{
+ Role: rolename,
+ }
+ return r.addRemoveRole(ctx, &authRoleAPIAction{
+ verb: "PUT",
+ name: rolename,
+ role: role,
+ })
+}
+
+func (r *httpAuthRoleAPI) RemoveRole(ctx context.Context, rolename string) error {
+ return r.addRemoveRole(ctx, &authRoleAPIAction{
+ verb: "DELETE",
+ name: rolename,
+ })
+}
+
+func (r *httpAuthRoleAPI) addRemoveRole(ctx context.Context, req *authRoleAPIAction) error {
+ resp, body, err := r.client.Do(ctx, req)
+ if err != nil {
+ return err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
+ var sec authError
+ err := json.Unmarshal(body, &sec)
+ if err != nil {
+ return err
+ }
+ return sec
+ }
+ return nil
+}
+
+func (r *httpAuthRoleAPI) GetRole(ctx context.Context, rolename string) (*Role, error) {
+ return r.modRole(ctx, &authRoleAPIAction{
+ verb: "GET",
+ name: rolename,
+ })
+}
+
+func buildRWPermission(prefixes []string, permType PermissionType) rwPermission {
+ var out rwPermission
+ switch permType {
+ case ReadPermission:
+ out.Read = prefixes
+ case WritePermission:
+ out.Write = prefixes
+ case ReadWritePermission:
+ out.Read = prefixes
+ out.Write = prefixes
+ }
+ return out
+}
+
+func (r *httpAuthRoleAPI) GrantRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
+ rwp := buildRWPermission(prefixes, permType)
+ role := &Role{
+ Role: rolename,
+ Grant: &Permissions{
+ KV: rwp,
+ },
+ }
+ return r.modRole(ctx, &authRoleAPIAction{
+ verb: "PUT",
+ name: rolename,
+ role: role,
+ })
+}
+
+func (r *httpAuthRoleAPI) RevokeRoleKV(ctx context.Context, rolename string, prefixes []string, permType PermissionType) (*Role, error) {
+ rwp := buildRWPermission(prefixes, permType)
+ role := &Role{
+ Role: rolename,
+ Revoke: &Permissions{
+ KV: rwp,
+ },
+ }
+ return r.modRole(ctx, &authRoleAPIAction{
+ verb: "PUT",
+ name: rolename,
+ role: role,
+ })
+}
+
+func (r *httpAuthRoleAPI) modRole(ctx context.Context, req *authRoleAPIAction) (*Role, error) {
+ resp, body, err := r.client.Do(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
+ var sec authError
+ err := json.Unmarshal(body, &sec)
+ if err != nil {
+ return nil, err
+ }
+ return nil, sec
+ }
+ var role Role
+ err = json.Unmarshal(body, &role)
+ if err != nil {
+ return nil, err
+ }
+ return &role, nil
+}
diff --git a/vendor/github.com/coreos/etcd/client/auth_user.go b/vendor/github.com/coreos/etcd/client/auth_user.go
new file mode 100644
index 000000000..9df1f294d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/auth_user.go
@@ -0,0 +1,297 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "bytes"
+ "encoding/json"
+ "net/http"
+ "net/url"
+ "path"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+var (
+ defaultV2AuthPrefix = "/v2/auth"
+)
+
+type User struct {
+ User string `json:"user"`
+ Password string `json:"password,omitempty"`
+ Roles []string `json:"roles"`
+ Grant []string `json:"grant,omitempty"`
+ Revoke []string `json:"revoke,omitempty"`
+}
+
+func v2AuthURL(ep url.URL, action string, name string) *url.URL {
+ if name != "" {
+ ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action, name)
+ return &ep
+ }
+ ep.Path = path.Join(ep.Path, defaultV2AuthPrefix, action)
+ return &ep
+}
+
+// NewAuthAPI constructs a new AuthAPI that uses HTTP to
+// interact with etcd's general auth features.
+func NewAuthAPI(c Client) AuthAPI {
+ return &httpAuthAPI{
+ client: c,
+ }
+}
+
+type AuthAPI interface {
+ // Enable auth.
+ Enable(ctx context.Context) error
+
+ // Disable auth.
+ Disable(ctx context.Context) error
+}
+
+type httpAuthAPI struct {
+ client httpClient
+}
+
+func (s *httpAuthAPI) Enable(ctx context.Context) error {
+ return s.enableDisable(ctx, &authAPIAction{"PUT"})
+}
+
+func (s *httpAuthAPI) Disable(ctx context.Context) error {
+ return s.enableDisable(ctx, &authAPIAction{"DELETE"})
+}
+
+func (s *httpAuthAPI) enableDisable(ctx context.Context, req httpAction) error {
+ resp, body, err := s.client.Do(ctx, req)
+ if err != nil {
+ return err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
+ var sec authError
+ err := json.Unmarshal(body, &sec)
+ if err != nil {
+ return err
+ }
+ return sec
+ }
+ return nil
+}
+
+type authAPIAction struct {
+ verb string
+}
+
+func (l *authAPIAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2AuthURL(ep, "enable", "")
+ req, _ := http.NewRequest(l.verb, u.String(), nil)
+ return req
+}
+
+type authError struct {
+ Message string `json:"message"`
+ Code int `json:"-"`
+}
+
+func (e authError) Error() string {
+ return e.Message
+}
+
+// NewAuthUserAPI constructs a new AuthUserAPI that uses HTTP to
+// interact with etcd's user creation and modification features.
+func NewAuthUserAPI(c Client) AuthUserAPI {
+ return &httpAuthUserAPI{
+ client: c,
+ }
+}
+
+type AuthUserAPI interface {
+ // Add a user.
+ AddUser(ctx context.Context, username string, password string) error
+
+ // Remove a user.
+ RemoveUser(ctx context.Context, username string) error
+
+ // Get user details.
+ GetUser(ctx context.Context, username string) (*User, error)
+
+ // Grant a user some permission roles.
+ GrantUser(ctx context.Context, username string, roles []string) (*User, error)
+
+ // Revoke some permission roles from a user.
+ RevokeUser(ctx context.Context, username string, roles []string) (*User, error)
+
+ // Change the user's password.
+ ChangePassword(ctx context.Context, username string, password string) (*User, error)
+
+ // List users.
+ ListUsers(ctx context.Context) ([]string, error)
+}
+
+type httpAuthUserAPI struct {
+ client httpClient
+}
+
+type authUserAPIAction struct {
+ verb string
+ username string
+ user *User
+}
+
+type authUserAPIList struct{}
+
+func (list *authUserAPIList) HTTPRequest(ep url.URL) *http.Request {
+ u := v2AuthURL(ep, "users", "")
+ req, _ := http.NewRequest("GET", u.String(), nil)
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+func (l *authUserAPIAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2AuthURL(ep, "users", l.username)
+ if l.user == nil {
+ req, _ := http.NewRequest(l.verb, u.String(), nil)
+ return req
+ }
+ b, err := json.Marshal(l.user)
+ if err != nil {
+ panic(err)
+ }
+ body := bytes.NewReader(b)
+ req, _ := http.NewRequest(l.verb, u.String(), body)
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+func (u *httpAuthUserAPI) ListUsers(ctx context.Context) ([]string, error) {
+ resp, body, err := u.client.Do(ctx, &authUserAPIList{})
+ if err != nil {
+ return nil, err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
+ var sec authError
+ err := json.Unmarshal(body, &sec)
+ if err != nil {
+ return nil, err
+ }
+ return nil, sec
+ }
+ var userList struct {
+ Users []string `json:"users"`
+ }
+ err = json.Unmarshal(body, &userList)
+ if err != nil {
+ return nil, err
+ }
+ return userList.Users, nil
+}
+
+func (u *httpAuthUserAPI) AddUser(ctx context.Context, username string, password string) error {
+ user := &User{
+ User: username,
+ Password: password,
+ }
+ return u.addRemoveUser(ctx, &authUserAPIAction{
+ verb: "PUT",
+ username: username,
+ user: user,
+ })
+}
+
+func (u *httpAuthUserAPI) RemoveUser(ctx context.Context, username string) error {
+ return u.addRemoveUser(ctx, &authUserAPIAction{
+ verb: "DELETE",
+ username: username,
+ })
+}
+
+func (u *httpAuthUserAPI) addRemoveUser(ctx context.Context, req *authUserAPIAction) error {
+ resp, body, err := u.client.Do(ctx, req)
+ if err != nil {
+ return err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK, http.StatusCreated); err != nil {
+ var sec authError
+ err := json.Unmarshal(body, &sec)
+ if err != nil {
+ return err
+ }
+ return sec
+ }
+ return nil
+}
+
+func (u *httpAuthUserAPI) GetUser(ctx context.Context, username string) (*User, error) {
+ return u.modUser(ctx, &authUserAPIAction{
+ verb: "GET",
+ username: username,
+ })
+}
+
+func (u *httpAuthUserAPI) GrantUser(ctx context.Context, username string, roles []string) (*User, error) {
+ user := &User{
+ User: username,
+ Grant: roles,
+ }
+ return u.modUser(ctx, &authUserAPIAction{
+ verb: "PUT",
+ username: username,
+ user: user,
+ })
+}
+
+func (u *httpAuthUserAPI) RevokeUser(ctx context.Context, username string, roles []string) (*User, error) {
+ user := &User{
+ User: username,
+ Revoke: roles,
+ }
+ return u.modUser(ctx, &authUserAPIAction{
+ verb: "PUT",
+ username: username,
+ user: user,
+ })
+}
+
+func (u *httpAuthUserAPI) ChangePassword(ctx context.Context, username string, password string) (*User, error) {
+ user := &User{
+ User: username,
+ Password: password,
+ }
+ return u.modUser(ctx, &authUserAPIAction{
+ verb: "PUT",
+ username: username,
+ user: user,
+ })
+}
+
+func (u *httpAuthUserAPI) modUser(ctx context.Context, req *authUserAPIAction) (*User, error) {
+ resp, body, err := u.client.Do(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
+ var sec authError
+ err := json.Unmarshal(body, &sec)
+ if err != nil {
+ return nil, err
+ }
+ return nil, sec
+ }
+ var user User
+ err = json.Unmarshal(body, &user)
+ if err != nil {
+ return nil, err
+ }
+ return &user, nil
+}
diff --git a/vendor/github.com/coreos/etcd/client/cancelreq.go b/vendor/github.com/coreos/etcd/client/cancelreq.go
new file mode 100644
index 000000000..fefdb40e4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/cancelreq.go
@@ -0,0 +1,20 @@
+// Copyright 2015 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.
+
+// borrowed from golang/net/context/ctxhttp/cancelreq.go
+
+// +build go1.5
+
+package client
+
+import "net/http"
+
+func requestCanceler(tr CancelableTransport, req *http.Request) func() {
+ ch := make(chan struct{})
+ req.Cancel = ch
+
+ return func() {
+ close(ch)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/cancelreq_go14.go b/vendor/github.com/coreos/etcd/client/cancelreq_go14.go
new file mode 100644
index 000000000..2bed38a41
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/cancelreq_go14.go
@@ -0,0 +1,17 @@
+// Copyright 2015 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.
+
+// borrowed from golang/net/context/ctxhttp/cancelreq_go14.go
+
+// +build !go1.5
+
+package client
+
+import "net/http"
+
+func requestCanceler(tr CancelableTransport, req *http.Request) func() {
+ return func() {
+ tr.CancelRequest(req)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/client.go b/vendor/github.com/coreos/etcd/client/client.go
new file mode 100644
index 000000000..da86a0bc7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/client.go
@@ -0,0 +1,514 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "net/http"
+ "net/url"
+ "reflect"
+ "sort"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+var (
+ ErrNoEndpoints = errors.New("client: no endpoints available")
+ ErrTooManyRedirects = errors.New("client: too many redirects")
+ ErrClusterUnavailable = errors.New("client: etcd cluster is unavailable or misconfigured")
+ errTooManyRedirectChecks = errors.New("client: too many redirect checks")
+)
+
+var DefaultRequestTimeout = 5 * time.Second
+
+var DefaultTransport CancelableTransport = &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ Dial: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 10 * time.Second,
+}
+
+type Config struct {
+ // Endpoints defines a set of URLs (schemes, hosts and ports only)
+ // that can be used to communicate with a logical etcd cluster. For
+ // example, a three-node cluster could be provided like so:
+ //
+ // Endpoints: []string{
+ // "http://node1.example.com:2379",
+ // "http://node2.example.com:2379",
+ // "http://node3.example.com:2379",
+ // }
+ //
+ // If multiple endpoints are provided, the Client will attempt to
+ // use them all in the event that one or more of them are unusable.
+ //
+ // If Client.Sync is ever called, the Client may cache an alternate
+ // set of endpoints to continue operation.
+ Endpoints []string
+
+ // Transport is used by the Client to drive HTTP requests. If not
+ // provided, DefaultTransport will be used.
+ Transport CancelableTransport
+
+ // CheckRedirect specifies the policy for handling HTTP redirects.
+ // If CheckRedirect is not nil, the Client calls it before
+ // following an HTTP redirect. The sole argument is the number of
+ // requests that have alrady been made. If CheckRedirect returns
+ // an error, Client.Do will not make any further requests and return
+ // the error back it to the caller.
+ //
+ // If CheckRedirect is nil, the Client uses its default policy,
+ // which is to stop after 10 consecutive requests.
+ CheckRedirect CheckRedirectFunc
+
+ // Username specifies the user credential to add as an authorization header
+ Username string
+
+ // Password is the password for the specified user to add as an authorization header
+ // to the request.
+ Password string
+
+ // HeaderTimeoutPerRequest specifies the time limit to wait for response
+ // header in a single request made by the Client. The timeout includes
+ // connection time, any redirects, and header wait time.
+ //
+ // For non-watch GET request, server returns the response body immediately.
+ // For PUT/POST/DELETE request, server will attempt to commit request
+ // before responding, which is expected to take `100ms + 2 * RTT`.
+ // For watch request, server returns the header immediately to notify Client
+ // watch start. But if server is behind some kind of proxy, the response
+ // header may be cached at proxy, and Client cannot rely on this behavior.
+ //
+ // One API call may send multiple requests to different etcd servers until it
+ // succeeds. Use context of the API to specify the overall timeout.
+ //
+ // A HeaderTimeoutPerRequest of zero means no timeout.
+ HeaderTimeoutPerRequest time.Duration
+}
+
+func (cfg *Config) transport() CancelableTransport {
+ if cfg.Transport == nil {
+ return DefaultTransport
+ }
+ return cfg.Transport
+}
+
+func (cfg *Config) checkRedirect() CheckRedirectFunc {
+ if cfg.CheckRedirect == nil {
+ return DefaultCheckRedirect
+ }
+ return cfg.CheckRedirect
+}
+
+// CancelableTransport mimics net/http.Transport, but requires that
+// the object also support request cancellation.
+type CancelableTransport interface {
+ http.RoundTripper
+ CancelRequest(req *http.Request)
+}
+
+type CheckRedirectFunc func(via int) error
+
+// DefaultCheckRedirect follows up to 10 redirects, but no more.
+var DefaultCheckRedirect CheckRedirectFunc = func(via int) error {
+ if via > 10 {
+ return ErrTooManyRedirects
+ }
+ return nil
+}
+
+type Client interface {
+ // Sync updates the internal cache of the etcd cluster's membership.
+ Sync(context.Context) error
+
+ // AutoSync periodically calls Sync() every given interval.
+ // The recommended sync interval is 10 seconds to 1 minute, which does
+ // not bring too much overhead to server and makes client catch up the
+ // cluster change in time.
+ //
+ // The example to use it:
+ //
+ // for {
+ // err := client.AutoSync(ctx, 10*time.Second)
+ // if err == context.DeadlineExceeded || err == context.Canceled {
+ // break
+ // }
+ // log.Print(err)
+ // }
+ AutoSync(context.Context, time.Duration) error
+
+ // Endpoints returns a copy of the current set of API endpoints used
+ // by Client to resolve HTTP requests. If Sync has ever been called,
+ // this may differ from the initial Endpoints provided in the Config.
+ Endpoints() []string
+
+ httpClient
+}
+
+func New(cfg Config) (Client, error) {
+ c := &httpClusterClient{
+ clientFactory: newHTTPClientFactory(cfg.transport(), cfg.checkRedirect(), cfg.HeaderTimeoutPerRequest),
+ rand: rand.New(rand.NewSource(int64(time.Now().Nanosecond()))),
+ }
+ if cfg.Username != "" {
+ c.credentials = &credentials{
+ username: cfg.Username,
+ password: cfg.Password,
+ }
+ }
+ if err := c.reset(cfg.Endpoints); err != nil {
+ return nil, err
+ }
+ return c, nil
+}
+
+type httpClient interface {
+ Do(context.Context, httpAction) (*http.Response, []byte, error)
+}
+
+func newHTTPClientFactory(tr CancelableTransport, cr CheckRedirectFunc, headerTimeout time.Duration) httpClientFactory {
+ return func(ep url.URL) httpClient {
+ return &redirectFollowingHTTPClient{
+ checkRedirect: cr,
+ client: &simpleHTTPClient{
+ transport: tr,
+ endpoint: ep,
+ headerTimeout: headerTimeout,
+ },
+ }
+ }
+}
+
+type credentials struct {
+ username string
+ password string
+}
+
+type httpClientFactory func(url.URL) httpClient
+
+type httpAction interface {
+ HTTPRequest(url.URL) *http.Request
+}
+
+type httpClusterClient struct {
+ clientFactory httpClientFactory
+ endpoints []url.URL
+ pinned int
+ credentials *credentials
+ sync.RWMutex
+ rand *rand.Rand
+}
+
+func (c *httpClusterClient) reset(eps []string) error {
+ if len(eps) == 0 {
+ return ErrNoEndpoints
+ }
+
+ neps := make([]url.URL, len(eps))
+ for i, ep := range eps {
+ u, err := url.Parse(ep)
+ if err != nil {
+ return err
+ }
+ neps[i] = *u
+ }
+
+ c.endpoints = shuffleEndpoints(c.rand, neps)
+ // TODO: pin old endpoint if possible, and rebalance when new endpoint appears
+ c.pinned = 0
+
+ return nil
+}
+
+func (c *httpClusterClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
+ action := act
+ c.RLock()
+ leps := len(c.endpoints)
+ eps := make([]url.URL, leps)
+ n := copy(eps, c.endpoints)
+ pinned := c.pinned
+
+ if c.credentials != nil {
+ action = &authedAction{
+ act: act,
+ credentials: *c.credentials,
+ }
+ }
+ c.RUnlock()
+
+ if leps == 0 {
+ return nil, nil, ErrNoEndpoints
+ }
+
+ if leps != n {
+ return nil, nil, errors.New("unable to pick endpoint: copy failed")
+ }
+
+ var resp *http.Response
+ var body []byte
+ var err error
+ cerr := &ClusterError{}
+
+ for i := pinned; i < leps+pinned; i++ {
+ k := i % leps
+ hc := c.clientFactory(eps[k])
+ resp, body, err = hc.Do(ctx, action)
+ if err != nil {
+ cerr.Errors = append(cerr.Errors, err)
+ // mask previous errors with context error, which is controlled by user
+ if err == context.Canceled || err == context.DeadlineExceeded {
+ return nil, nil, err
+ }
+ continue
+ }
+ if resp.StatusCode/100 == 5 {
+ switch resp.StatusCode {
+ case http.StatusInternalServerError, http.StatusServiceUnavailable:
+ // TODO: make sure this is a no leader response
+ cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s has no leader", eps[k].String()))
+ default:
+ cerr.Errors = append(cerr.Errors, fmt.Errorf("client: etcd member %s returns server error [%s]", eps[k].String(), http.StatusText(resp.StatusCode)))
+ }
+ continue
+ }
+ if k != pinned {
+ c.Lock()
+ c.pinned = k
+ c.Unlock()
+ }
+ return resp, body, nil
+ }
+
+ return nil, nil, cerr
+}
+
+func (c *httpClusterClient) Endpoints() []string {
+ c.RLock()
+ defer c.RUnlock()
+
+ eps := make([]string, len(c.endpoints))
+ for i, ep := range c.endpoints {
+ eps[i] = ep.String()
+ }
+
+ return eps
+}
+
+func (c *httpClusterClient) Sync(ctx context.Context) error {
+ mAPI := NewMembersAPI(c)
+ ms, err := mAPI.List(ctx)
+ if err != nil {
+ return err
+ }
+
+ c.Lock()
+ defer c.Unlock()
+
+ eps := make([]string, 0)
+ for _, m := range ms {
+ eps = append(eps, m.ClientURLs...)
+ }
+ sort.Sort(sort.StringSlice(eps))
+
+ ceps := make([]string, len(c.endpoints))
+ for i, cep := range c.endpoints {
+ ceps[i] = cep.String()
+ }
+ sort.Sort(sort.StringSlice(ceps))
+ // fast path if no change happens
+ // this helps client to pin the endpoint when no cluster change
+ if reflect.DeepEqual(eps, ceps) {
+ return nil
+ }
+
+ return c.reset(eps)
+}
+
+func (c *httpClusterClient) AutoSync(ctx context.Context, interval time.Duration) error {
+ ticker := time.NewTicker(interval)
+ defer ticker.Stop()
+ for {
+ err := c.Sync(ctx)
+ if err != nil {
+ return err
+ }
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-ticker.C:
+ }
+ }
+}
+
+type roundTripResponse struct {
+ resp *http.Response
+ err error
+}
+
+type simpleHTTPClient struct {
+ transport CancelableTransport
+ endpoint url.URL
+ headerTimeout time.Duration
+}
+
+func (c *simpleHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
+ req := act.HTTPRequest(c.endpoint)
+
+ if err := printcURL(req); err != nil {
+ return nil, nil, err
+ }
+
+ hctx, hcancel := context.WithCancel(ctx)
+ if c.headerTimeout > 0 {
+ hctx, hcancel = context.WithTimeout(ctx, c.headerTimeout)
+ }
+ defer hcancel()
+
+ reqcancel := requestCanceler(c.transport, req)
+
+ rtchan := make(chan roundTripResponse, 1)
+ go func() {
+ resp, err := c.transport.RoundTrip(req)
+ rtchan <- roundTripResponse{resp: resp, err: err}
+ close(rtchan)
+ }()
+
+ var resp *http.Response
+ var err error
+
+ select {
+ case rtresp := <-rtchan:
+ resp, err = rtresp.resp, rtresp.err
+ case <-hctx.Done():
+ // cancel and wait for request to actually exit before continuing
+ reqcancel()
+ rtresp := <-rtchan
+ resp = rtresp.resp
+ switch {
+ case ctx.Err() != nil:
+ err = ctx.Err()
+ case hctx.Err() != nil:
+ err = fmt.Errorf("client: endpoint %s exceeded header timeout", c.endpoint.String())
+ default:
+ panic("failed to get error from context")
+ }
+ }
+
+ // always check for resp nil-ness to deal with possible
+ // race conditions between channels above
+ defer func() {
+ if resp != nil {
+ resp.Body.Close()
+ }
+ }()
+
+ if err != nil {
+ return nil, nil, err
+ }
+
+ var body []byte
+ done := make(chan struct{})
+ go func() {
+ body, err = ioutil.ReadAll(resp.Body)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-ctx.Done():
+ resp.Body.Close()
+ <-done
+ return nil, nil, ctx.Err()
+ case <-done:
+ }
+
+ return resp, body, err
+}
+
+type authedAction struct {
+ act httpAction
+ credentials credentials
+}
+
+func (a *authedAction) HTTPRequest(url url.URL) *http.Request {
+ r := a.act.HTTPRequest(url)
+ r.SetBasicAuth(a.credentials.username, a.credentials.password)
+ return r
+}
+
+type redirectFollowingHTTPClient struct {
+ client httpClient
+ checkRedirect CheckRedirectFunc
+}
+
+func (r *redirectFollowingHTTPClient) Do(ctx context.Context, act httpAction) (*http.Response, []byte, error) {
+ next := act
+ for i := 0; i < 100; i++ {
+ if i > 0 {
+ if err := r.checkRedirect(i); err != nil {
+ return nil, nil, err
+ }
+ }
+ resp, body, err := r.client.Do(ctx, next)
+ if err != nil {
+ return nil, nil, err
+ }
+ if resp.StatusCode/100 == 3 {
+ hdr := resp.Header.Get("Location")
+ if hdr == "" {
+ return nil, nil, fmt.Errorf("Location header not set")
+ }
+ loc, err := url.Parse(hdr)
+ if err != nil {
+ return nil, nil, fmt.Errorf("Location header not valid URL: %s", hdr)
+ }
+ next = &redirectedHTTPAction{
+ action: act,
+ location: *loc,
+ }
+ continue
+ }
+ return resp, body, nil
+ }
+
+ return nil, nil, errTooManyRedirectChecks
+}
+
+type redirectedHTTPAction struct {
+ action httpAction
+ location url.URL
+}
+
+func (r *redirectedHTTPAction) HTTPRequest(ep url.URL) *http.Request {
+ orig := r.action.HTTPRequest(ep)
+ orig.URL = &r.location
+ return orig
+}
+
+func shuffleEndpoints(r *rand.Rand, eps []url.URL) []url.URL {
+ p := r.Perm(len(eps))
+ neps := make([]url.URL, len(eps))
+ for i, k := range p {
+ neps[i] = eps[k]
+ }
+ return neps
+}
diff --git a/vendor/github.com/coreos/etcd/client/client_test.go b/vendor/github.com/coreos/etcd/client/client_test.go
new file mode 100644
index 000000000..be526b17b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/client_test.go
@@ -0,0 +1,896 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "errors"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+type actionAssertingHTTPClient struct {
+ t *testing.T
+ num int
+ act httpAction
+
+ resp http.Response
+ body []byte
+ err error
+}
+
+func (a *actionAssertingHTTPClient) Do(_ context.Context, act httpAction) (*http.Response, []byte, error) {
+ if !reflect.DeepEqual(a.act, act) {
+ a.t.Errorf("#%d: unexpected httpAction: want=%#v got=%#v", a.num, a.act, act)
+ }
+
+ return &a.resp, a.body, a.err
+}
+
+type staticHTTPClient struct {
+ resp http.Response
+ body []byte
+ err error
+}
+
+func (s *staticHTTPClient) Do(context.Context, httpAction) (*http.Response, []byte, error) {
+ return &s.resp, s.body, s.err
+}
+
+type staticHTTPAction struct {
+ request http.Request
+}
+
+func (s *staticHTTPAction) HTTPRequest(url.URL) *http.Request {
+ return &s.request
+}
+
+type staticHTTPResponse struct {
+ resp http.Response
+ body []byte
+ err error
+}
+
+type multiStaticHTTPClient struct {
+ responses []staticHTTPResponse
+ cur int
+}
+
+func (s *multiStaticHTTPClient) Do(context.Context, httpAction) (*http.Response, []byte, error) {
+ r := s.responses[s.cur]
+ s.cur++
+ return &r.resp, r.body, r.err
+}
+
+func newStaticHTTPClientFactory(responses []staticHTTPResponse) httpClientFactory {
+ var cur int
+ return func(url.URL) httpClient {
+ r := responses[cur]
+ cur++
+ return &staticHTTPClient{resp: r.resp, body: r.body, err: r.err}
+ }
+}
+
+type fakeTransport struct {
+ respchan chan *http.Response
+ errchan chan error
+ startCancel chan struct{}
+ finishCancel chan struct{}
+}
+
+func newFakeTransport() *fakeTransport {
+ return &fakeTransport{
+ respchan: make(chan *http.Response, 1),
+ errchan: make(chan error, 1),
+ startCancel: make(chan struct{}, 1),
+ finishCancel: make(chan struct{}, 1),
+ }
+}
+
+func (t *fakeTransport) CancelRequest(*http.Request) {
+ t.startCancel <- struct{}{}
+}
+
+type fakeAction struct{}
+
+func (a *fakeAction) HTTPRequest(url.URL) *http.Request {
+ return &http.Request{}
+}
+
+func TestSimpleHTTPClientDoSuccess(t *testing.T) {
+ tr := newFakeTransport()
+ c := &simpleHTTPClient{transport: tr}
+
+ tr.respchan <- &http.Response{
+ StatusCode: http.StatusTeapot,
+ Body: ioutil.NopCloser(strings.NewReader("foo")),
+ }
+
+ resp, body, err := c.Do(context.Background(), &fakeAction{})
+ if err != nil {
+ t.Fatalf("incorrect error value: want=nil got=%v", err)
+ }
+
+ wantCode := http.StatusTeapot
+ if wantCode != resp.StatusCode {
+ t.Fatalf("invalid response code: want=%d got=%d", wantCode, resp.StatusCode)
+ }
+
+ wantBody := []byte("foo")
+ if !reflect.DeepEqual(wantBody, body) {
+ t.Fatalf("invalid response body: want=%q got=%q", wantBody, body)
+ }
+}
+
+func TestSimpleHTTPClientDoError(t *testing.T) {
+ tr := newFakeTransport()
+ c := &simpleHTTPClient{transport: tr}
+
+ tr.errchan <- errors.New("fixture")
+
+ _, _, err := c.Do(context.Background(), &fakeAction{})
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
+
+func TestSimpleHTTPClientDoCancelContext(t *testing.T) {
+ tr := newFakeTransport()
+ c := &simpleHTTPClient{transport: tr}
+
+ tr.startCancel <- struct{}{}
+ tr.finishCancel <- struct{}{}
+
+ _, _, err := c.Do(context.Background(), &fakeAction{})
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+}
+
+type checkableReadCloser struct {
+ io.ReadCloser
+ closed bool
+}
+
+func (c *checkableReadCloser) Close() error {
+ if !c.closed {
+ c.closed = true
+ return c.ReadCloser.Close()
+ }
+ return nil
+}
+
+func TestSimpleHTTPClientDoCancelContextResponseBodyClosed(t *testing.T) {
+ tr := newFakeTransport()
+ c := &simpleHTTPClient{transport: tr}
+
+ // create an already-cancelled context
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+
+ body := &checkableReadCloser{ReadCloser: ioutil.NopCloser(strings.NewReader("foo"))}
+ go func() {
+ // wait that simpleHTTPClient knows the context is already timed out,
+ // and calls CancelRequest
+ testutil.WaitSchedule()
+
+ // response is returned before cancel effects
+ tr.respchan <- &http.Response{Body: body}
+ }()
+
+ _, _, err := c.Do(ctx, &fakeAction{})
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+
+ if !body.closed {
+ t.Fatalf("expected closed body")
+ }
+}
+
+type blockingBody struct {
+ c chan struct{}
+}
+
+func (bb *blockingBody) Read(p []byte) (n int, err error) {
+ <-bb.c
+ return 0, errors.New("closed")
+}
+
+func (bb *blockingBody) Close() error {
+ close(bb.c)
+ return nil
+}
+
+func TestSimpleHTTPClientDoCancelContextResponseBodyClosedWithBlockingBody(t *testing.T) {
+ tr := newFakeTransport()
+ c := &simpleHTTPClient{transport: tr}
+
+ ctx, cancel := context.WithCancel(context.Background())
+ body := &checkableReadCloser{ReadCloser: &blockingBody{c: make(chan struct{})}}
+ go func() {
+ tr.respchan <- &http.Response{Body: body}
+ time.Sleep(2 * time.Millisecond)
+ // cancel after the body is received
+ cancel()
+ }()
+
+ _, _, err := c.Do(ctx, &fakeAction{})
+ if err != context.Canceled {
+ t.Fatalf("expected %+v, got %+v", context.Canceled, err)
+ }
+
+ if !body.closed {
+ t.Fatalf("expected closed body")
+ }
+}
+
+func TestSimpleHTTPClientDoCancelContextWaitForRoundTrip(t *testing.T) {
+ tr := newFakeTransport()
+ c := &simpleHTTPClient{transport: tr}
+
+ donechan := make(chan struct{})
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ c.Do(ctx, &fakeAction{})
+ close(donechan)
+ }()
+
+ // This should call CancelRequest and begin the cancellation process
+ cancel()
+
+ select {
+ case <-donechan:
+ t.Fatalf("simpleHTTPClient.Do should not have exited yet")
+ default:
+ }
+
+ tr.finishCancel <- struct{}{}
+
+ select {
+ case <-donechan:
+ //expected behavior
+ return
+ case <-time.After(time.Second):
+ t.Fatalf("simpleHTTPClient.Do did not exit within 1s")
+ }
+}
+
+func TestSimpleHTTPClientDoHeaderTimeout(t *testing.T) {
+ tr := newFakeTransport()
+ tr.finishCancel <- struct{}{}
+ c := &simpleHTTPClient{transport: tr, headerTimeout: time.Millisecond}
+
+ errc := make(chan error)
+ go func() {
+ _, _, err := c.Do(context.Background(), &fakeAction{})
+ errc <- err
+ }()
+
+ select {
+ case err := <-errc:
+ if err == nil {
+ t.Fatalf("expected non-nil error, got nil")
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("unexpected timeout when waitting for the test to finish")
+ }
+}
+
+func TestHTTPClusterClientDo(t *testing.T) {
+ fakeErr := errors.New("fake!")
+ fakeURL := url.URL{}
+ tests := []struct {
+ client *httpClusterClient
+ wantCode int
+ wantErr error
+ wantPinned int
+ }{
+ // first good response short-circuits Do
+ {
+ client: &httpClusterClient{
+ endpoints: []url.URL{fakeURL, fakeURL},
+ clientFactory: newStaticHTTPClientFactory(
+ []staticHTTPResponse{
+ {resp: http.Response{StatusCode: http.StatusTeapot}},
+ {err: fakeErr},
+ },
+ ),
+ rand: rand.New(rand.NewSource(0)),
+ },
+ wantCode: http.StatusTeapot,
+ },
+
+ // fall through to good endpoint if err is arbitrary
+ {
+ client: &httpClusterClient{
+ endpoints: []url.URL{fakeURL, fakeURL},
+ clientFactory: newStaticHTTPClientFactory(
+ []staticHTTPResponse{
+ {err: fakeErr},
+ {resp: http.Response{StatusCode: http.StatusTeapot}},
+ },
+ ),
+ rand: rand.New(rand.NewSource(0)),
+ },
+ wantCode: http.StatusTeapot,
+ wantPinned: 1,
+ },
+
+ // context.Canceled short-circuits Do
+ {
+ client: &httpClusterClient{
+ endpoints: []url.URL{fakeURL, fakeURL},
+ clientFactory: newStaticHTTPClientFactory(
+ []staticHTTPResponse{
+ {err: context.Canceled},
+ {resp: http.Response{StatusCode: http.StatusTeapot}},
+ },
+ ),
+ rand: rand.New(rand.NewSource(0)),
+ },
+ wantErr: context.Canceled,
+ },
+
+ // return err if there are no endpoints
+ {
+ client: &httpClusterClient{
+ endpoints: []url.URL{},
+ clientFactory: newHTTPClientFactory(nil, nil, 0),
+ rand: rand.New(rand.NewSource(0)),
+ },
+ wantErr: ErrNoEndpoints,
+ },
+
+ // return err if all endpoints return arbitrary errors
+ {
+ client: &httpClusterClient{
+ endpoints: []url.URL{fakeURL, fakeURL},
+ clientFactory: newStaticHTTPClientFactory(
+ []staticHTTPResponse{
+ {err: fakeErr},
+ {err: fakeErr},
+ },
+ ),
+ rand: rand.New(rand.NewSource(0)),
+ },
+ wantErr: &ClusterError{Errors: []error{fakeErr, fakeErr}},
+ },
+
+ // 500-level errors cause Do to fallthrough to next endpoint
+ {
+ client: &httpClusterClient{
+ endpoints: []url.URL{fakeURL, fakeURL},
+ clientFactory: newStaticHTTPClientFactory(
+ []staticHTTPResponse{
+ {resp: http.Response{StatusCode: http.StatusBadGateway}},
+ {resp: http.Response{StatusCode: http.StatusTeapot}},
+ },
+ ),
+ rand: rand.New(rand.NewSource(0)),
+ },
+ wantCode: http.StatusTeapot,
+ wantPinned: 1,
+ },
+ }
+
+ for i, tt := range tests {
+ resp, _, err := tt.client.Do(context.Background(), nil)
+ if !reflect.DeepEqual(tt.wantErr, err) {
+ t.Errorf("#%d: got err=%v, want=%v", i, err, tt.wantErr)
+ continue
+ }
+
+ if resp == nil {
+ if tt.wantCode != 0 {
+ t.Errorf("#%d: resp is nil, want=%d", i, tt.wantCode)
+ }
+ continue
+ }
+
+ if resp.StatusCode != tt.wantCode {
+ t.Errorf("#%d: resp code=%d, want=%d", i, resp.StatusCode, tt.wantCode)
+ continue
+ }
+
+ if tt.client.pinned != tt.wantPinned {
+ t.Errorf("#%d: pinned=%d, want=%d", i, tt.client.pinned, tt.wantPinned)
+ }
+ }
+}
+
+func TestHTTPClusterClientDoDeadlineExceedContext(t *testing.T) {
+ fakeURL := url.URL{}
+ tr := newFakeTransport()
+ tr.finishCancel <- struct{}{}
+ c := &httpClusterClient{
+ clientFactory: newHTTPClientFactory(tr, DefaultCheckRedirect, 0),
+ endpoints: []url.URL{fakeURL},
+ }
+
+ errc := make(chan error)
+ go func() {
+ ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
+ defer cancel()
+ _, _, err := c.Do(ctx, &fakeAction{})
+ errc <- err
+ }()
+
+ select {
+ case err := <-errc:
+ if err != context.DeadlineExceeded {
+ t.Errorf("err = %+v, want %+v", err, context.DeadlineExceeded)
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("unexpected timeout when waitting for request to deadline exceed")
+ }
+}
+
+func TestRedirectedHTTPAction(t *testing.T) {
+ act := &redirectedHTTPAction{
+ action: &staticHTTPAction{
+ request: http.Request{
+ Method: "DELETE",
+ URL: &url.URL{
+ Scheme: "https",
+ Host: "foo.example.com",
+ Path: "/ping",
+ },
+ },
+ },
+ location: url.URL{
+ Scheme: "https",
+ Host: "bar.example.com",
+ Path: "/pong",
+ },
+ }
+
+ want := &http.Request{
+ Method: "DELETE",
+ URL: &url.URL{
+ Scheme: "https",
+ Host: "bar.example.com",
+ Path: "/pong",
+ },
+ }
+ got := act.HTTPRequest(url.URL{Scheme: "http", Host: "baz.example.com", Path: "/pang"})
+
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("HTTPRequest is %#v, want %#v", want, got)
+ }
+}
+
+func TestRedirectFollowingHTTPClient(t *testing.T) {
+ tests := []struct {
+ checkRedirect CheckRedirectFunc
+ client httpClient
+ wantCode int
+ wantErr error
+ }{
+ // errors bubbled up
+ {
+ checkRedirect: func(int) error { return ErrTooManyRedirects },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ err: errors.New("fail!"),
+ },
+ },
+ },
+ wantErr: errors.New("fail!"),
+ },
+
+ // no need to follow redirect if none given
+ {
+ checkRedirect: func(int) error { return ErrTooManyRedirects },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+ },
+ },
+ wantCode: http.StatusTeapot,
+ },
+
+ // redirects if less than max
+ {
+ checkRedirect: func(via int) error {
+ if via >= 2 {
+ return ErrTooManyRedirects
+ }
+ return nil
+ },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{"http://example.com"}},
+ },
+ },
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+ },
+ },
+ wantCode: http.StatusTeapot,
+ },
+
+ // succeed after reaching max redirects
+ {
+ checkRedirect: func(via int) error {
+ if via >= 3 {
+ return ErrTooManyRedirects
+ }
+ return nil
+ },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{"http://example.com"}},
+ },
+ },
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{"http://example.com"}},
+ },
+ },
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+ },
+ },
+ wantCode: http.StatusTeapot,
+ },
+
+ // fail if too many redirects
+ {
+ checkRedirect: func(via int) error {
+ if via >= 2 {
+ return ErrTooManyRedirects
+ }
+ return nil
+ },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{"http://example.com"}},
+ },
+ },
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{"http://example.com"}},
+ },
+ },
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+ },
+ },
+ wantErr: ErrTooManyRedirects,
+ },
+
+ // fail if Location header not set
+ {
+ checkRedirect: func(int) error { return ErrTooManyRedirects },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ },
+ },
+ },
+ },
+ wantErr: errors.New("Location header not set"),
+ },
+
+ // fail if Location header is invalid
+ {
+ checkRedirect: func(int) error { return ErrTooManyRedirects },
+ client: &multiStaticHTTPClient{
+ responses: []staticHTTPResponse{
+ {
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{":"}},
+ },
+ },
+ },
+ },
+ wantErr: errors.New("Location header not valid URL: :"),
+ },
+
+ // fail if redirects checked way too many times
+ {
+ checkRedirect: func(int) error { return nil },
+ client: &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusTemporaryRedirect,
+ Header: http.Header{"Location": []string{"http://example.com"}},
+ },
+ },
+ wantErr: errTooManyRedirectChecks,
+ },
+ }
+
+ for i, tt := range tests {
+ client := &redirectFollowingHTTPClient{client: tt.client, checkRedirect: tt.checkRedirect}
+ resp, _, err := client.Do(context.Background(), nil)
+ if !reflect.DeepEqual(tt.wantErr, err) {
+ t.Errorf("#%d: got err=%v, want=%v", i, err, tt.wantErr)
+ continue
+ }
+
+ if resp == nil {
+ if tt.wantCode != 0 {
+ t.Errorf("#%d: resp is nil, want=%d", i, tt.wantCode)
+ }
+ continue
+ }
+
+ if resp.StatusCode != tt.wantCode {
+ t.Errorf("#%d: resp code=%d, want=%d", i, resp.StatusCode, tt.wantCode)
+ continue
+ }
+ }
+}
+
+func TestDefaultCheckRedirect(t *testing.T) {
+ tests := []struct {
+ num int
+ err error
+ }{
+ {0, nil},
+ {5, nil},
+ {10, nil},
+ {11, ErrTooManyRedirects},
+ {29, ErrTooManyRedirects},
+ }
+
+ for i, tt := range tests {
+ err := DefaultCheckRedirect(tt.num)
+ if !reflect.DeepEqual(tt.err, err) {
+ t.Errorf("#%d: want=%#v got=%#v", i, tt.err, err)
+ }
+ }
+}
+
+func TestHTTPClusterClientSync(t *testing.T) {
+ cf := newStaticHTTPClientFactory([]staticHTTPResponse{
+ {
+ resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}},
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ },
+ })
+
+ hc := &httpClusterClient{
+ clientFactory: cf,
+ rand: rand.New(rand.NewSource(0)),
+ }
+ err := hc.reset([]string{"http://127.0.0.1:2379"})
+ if err != nil {
+ t.Fatalf("unexpected error during setup: %#v", err)
+ }
+
+ want := []string{"http://127.0.0.1:2379"}
+ got := hc.Endpoints()
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("incorrect endpoints: want=%#v got=%#v", want, got)
+ }
+
+ err = hc.Sync(context.Background())
+ if err != nil {
+ t.Fatalf("unexpected error during Sync: %#v", err)
+ }
+
+ want = []string{"http://127.0.0.1:2379", "http://127.0.0.1:4001", "http://127.0.0.1:4002", "http://127.0.0.1:4003"}
+ got = hc.Endpoints()
+ sort.Sort(sort.StringSlice(got))
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("incorrect endpoints post-Sync: want=%#v got=%#v", want, got)
+ }
+
+ err = hc.reset([]string{"http://127.0.0.1:4009"})
+ if err != nil {
+ t.Fatalf("unexpected error during reset: %#v", err)
+ }
+
+ want = []string{"http://127.0.0.1:4009"}
+ got = hc.Endpoints()
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("incorrect endpoints post-reset: want=%#v got=%#v", want, got)
+ }
+}
+
+func TestHTTPClusterClientSyncFail(t *testing.T) {
+ cf := newStaticHTTPClientFactory([]staticHTTPResponse{
+ {err: errors.New("fail!")},
+ })
+
+ hc := &httpClusterClient{
+ clientFactory: cf,
+ rand: rand.New(rand.NewSource(0)),
+ }
+ err := hc.reset([]string{"http://127.0.0.1:2379"})
+ if err != nil {
+ t.Fatalf("unexpected error during setup: %#v", err)
+ }
+
+ want := []string{"http://127.0.0.1:2379"}
+ got := hc.Endpoints()
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("incorrect endpoints: want=%#v got=%#v", want, got)
+ }
+
+ err = hc.Sync(context.Background())
+ if err == nil {
+ t.Fatalf("got nil error during Sync")
+ }
+
+ got = hc.Endpoints()
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("incorrect endpoints after failed Sync: want=%#v got=%#v", want, got)
+ }
+}
+
+func TestHTTPClusterClientAutoSyncCancelContext(t *testing.T) {
+ cf := newStaticHTTPClientFactory([]staticHTTPResponse{
+ {
+ resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}},
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ },
+ })
+
+ hc := &httpClusterClient{
+ clientFactory: cf,
+ rand: rand.New(rand.NewSource(0)),
+ }
+ err := hc.reset([]string{"http://127.0.0.1:2379"})
+ if err != nil {
+ t.Fatalf("unexpected error during setup: %#v", err)
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+
+ err = hc.AutoSync(ctx, time.Hour)
+ if err != context.Canceled {
+ t.Fatalf("incorrect error value: want=%v got=%v", context.Canceled, err)
+ }
+}
+
+func TestHTTPClusterClientAutoSyncFail(t *testing.T) {
+ cf := newStaticHTTPClientFactory([]staticHTTPResponse{
+ {err: errors.New("fail!")},
+ })
+
+ hc := &httpClusterClient{
+ clientFactory: cf,
+ rand: rand.New(rand.NewSource(0)),
+ }
+ err := hc.reset([]string{"http://127.0.0.1:2379"})
+ if err != nil {
+ t.Fatalf("unexpected error during setup: %#v", err)
+ }
+
+ err = hc.AutoSync(context.Background(), time.Hour)
+ if err.Error() != ErrClusterUnavailable.Error() {
+ t.Fatalf("incorrect error value: want=%v got=%v", ErrClusterUnavailable, err)
+ }
+}
+
+// TestHTTPClusterClientSyncPinEndpoint tests that Sync() pins the endpoint when
+// it gets the exactly same member list as before.
+func TestHTTPClusterClientSyncPinEndpoint(t *testing.T) {
+ cf := newStaticHTTPClientFactory([]staticHTTPResponse{
+ {
+ resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}},
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ },
+ {
+ resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}},
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ },
+ {
+ resp: http.Response{StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}},
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ },
+ })
+
+ hc := &httpClusterClient{
+ clientFactory: cf,
+ rand: rand.New(rand.NewSource(0)),
+ }
+ err := hc.reset([]string{"http://127.0.0.1:4003", "http://127.0.0.1:2379", "http://127.0.0.1:4001", "http://127.0.0.1:4002"})
+ if err != nil {
+ t.Fatalf("unexpected error during setup: %#v", err)
+ }
+ pinnedEndpoint := hc.endpoints[hc.pinned]
+
+ for i := 0; i < 3; i++ {
+ err = hc.Sync(context.Background())
+ if err != nil {
+ t.Fatalf("#%d: unexpected error during Sync: %#v", i, err)
+ }
+
+ if g := hc.endpoints[hc.pinned]; g != pinnedEndpoint {
+ t.Errorf("#%d: pinned endpoint = %s, want %s", i, g, pinnedEndpoint)
+ }
+ }
+}
+
+func TestHTTPClusterClientResetFail(t *testing.T) {
+ tests := [][]string{
+ // need at least one endpoint
+ {},
+
+ // urls must be valid
+ {":"},
+ }
+
+ for i, tt := range tests {
+ hc := &httpClusterClient{rand: rand.New(rand.NewSource(0))}
+ err := hc.reset(tt)
+ if err == nil {
+ t.Errorf("#%d: expected non-nil error", i)
+ }
+ }
+}
+
+func TestHTTPClusterClientResetPinRandom(t *testing.T) {
+ round := 2000
+ pinNum := 0
+ for i := 0; i < round; i++ {
+ hc := &httpClusterClient{rand: rand.New(rand.NewSource(int64(i)))}
+ err := hc.reset([]string{"http://127.0.0.1:4001", "http://127.0.0.1:4002", "http://127.0.0.1:4003"})
+ if err != nil {
+ t.Fatalf("#%d: reset error (%v)", i, err)
+ }
+ if hc.endpoints[hc.pinned].String() == "http://127.0.0.1:4001" {
+ pinNum++
+ }
+ }
+
+ min := 1.0/3.0 - 0.05
+ max := 1.0/3.0 + 0.05
+ if ratio := float64(pinNum) / float64(round); ratio > max || ratio < min {
+ t.Errorf("pinned ratio = %v, want [%v, %v]", ratio, min, max)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/cluster_error.go b/vendor/github.com/coreos/etcd/client/cluster_error.go
new file mode 100644
index 000000000..957ed4624
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/cluster_error.go
@@ -0,0 +1,33 @@
+// Copyright 2015 CoreOS, 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 client
+
+import "fmt"
+
+type ClusterError struct {
+ Errors []error
+}
+
+func (ce *ClusterError) Error() string {
+ return ErrClusterUnavailable.Error()
+}
+
+func (ce *ClusterError) Detail() string {
+ s := ""
+ for i, e := range ce.Errors {
+ s += fmt.Sprintf("error #%d: %s\n", i, e)
+ }
+ return s
+}
diff --git a/vendor/github.com/coreos/etcd/client/curl.go b/vendor/github.com/coreos/etcd/client/curl.go
new file mode 100644
index 000000000..5a5a69a94
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/curl.go
@@ -0,0 +1,70 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "os"
+)
+
+var (
+ cURLDebug = false
+)
+
+func EnablecURLDebug() {
+ cURLDebug = true
+}
+
+func DisablecURLDebug() {
+ cURLDebug = false
+}
+
+// printcURL prints the cURL equivalent request to stderr.
+// It returns an error if the body of the request cannot
+// be read.
+// The caller MUST cancel the request if there is an error.
+func printcURL(req *http.Request) error {
+ if !cURLDebug {
+ return nil
+ }
+ var (
+ command string
+ b []byte
+ err error
+ )
+
+ if req.URL != nil {
+ command = fmt.Sprintf("curl -X %s %s", req.Method, req.URL.String())
+ }
+
+ if req.Body != nil {
+ b, err = ioutil.ReadAll(req.Body)
+ if err != nil {
+ return err
+ }
+ command += fmt.Sprintf(" -d %q", string(b))
+ }
+
+ fmt.Fprintf(os.Stderr, "cURL Command: %s\n", command)
+
+ // reset body
+ body := bytes.NewBuffer(b)
+ req.Body = ioutil.NopCloser(body)
+
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/client/discover.go b/vendor/github.com/coreos/etcd/client/discover.go
new file mode 100644
index 000000000..ae88659f4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/discover.go
@@ -0,0 +1,21 @@
+// Copyright 2015 CoreOS, 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 client
+
+// Discoverer is an interface that wraps the Discover method.
+type Discoverer interface {
+ // Discover looks up the etcd servers for the domain.
+ Discover(domain string) ([]string, error)
+}
diff --git a/vendor/github.com/coreos/etcd/client/doc.go b/vendor/github.com/coreos/etcd/client/doc.go
new file mode 100644
index 000000000..70111cace
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/doc.go
@@ -0,0 +1,71 @@
+// Copyright 2015 CoreOS, 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 client provides bindings for the etcd APIs.
+
+Create a Config and exchange it for a Client:
+
+ import (
+ "net/http"
+
+ "github.com/coreos/etcd/client"
+ "golang.org/x/net/context"
+ )
+
+ cfg := client.Config{
+ Endpoints: []string{"http://127.0.0.1:2379"},
+ Transport: DefaultTransport,
+ }
+
+ c, err := client.New(cfg)
+ if err != nil {
+ // handle error
+ }
+
+Create a KeysAPI using the Client, then use it to interact with etcd:
+
+ kAPI := client.NewKeysAPI(c)
+
+ // create a new key /foo with the value "bar"
+ _, err = kAPI.Create(context.Background(), "/foo", "bar")
+ if err != nil {
+ // handle error
+ }
+
+ // delete the newly created key only if the value is still "bar"
+ _, err = kAPI.Delete(context.Background(), "/foo", &DeleteOptions{PrevValue: "bar"})
+ if err != nil {
+ // handle error
+ }
+
+Use a custom context to set timeouts on your operations:
+
+ import "time"
+
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ // set a new key, ignoring it's previous state
+ _, err := kAPI.Set(ctx, "/ping", "pong", nil)
+ if err != nil {
+ if err == context.DeadlineExceeded {
+ // request took longer than 5s
+ } else {
+ // handle error
+ }
+ }
+
+*/
+package client
diff --git a/vendor/github.com/coreos/etcd/client/fake_transport_go14_test.go b/vendor/github.com/coreos/etcd/client/fake_transport_go14_test.go
new file mode 100644
index 000000000..4a99a7d37
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/fake_transport_go14_test.go
@@ -0,0 +1,41 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build !go1.5
+
+package client
+
+import (
+ "errors"
+ "net/http"
+)
+
+func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ select {
+ case resp := <-t.respchan:
+ return resp, nil
+ case err := <-t.errchan:
+ return nil, err
+ case <-t.startCancel:
+ select {
+ // this simulates that the request is finished before cancel effects
+ case resp := <-t.respchan:
+ return resp, nil
+ // wait on finishCancel to simulate taking some amount of
+ // time while calling CancelRequest
+ case <-t.finishCancel:
+ return nil, errors.New("cancelled")
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/fake_transport_test.go b/vendor/github.com/coreos/etcd/client/fake_transport_test.go
new file mode 100644
index 000000000..06761e266
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/fake_transport_test.go
@@ -0,0 +1,42 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build go1.5
+
+package client
+
+import (
+ "errors"
+ "net/http"
+)
+
+func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+ select {
+ case resp := <-t.respchan:
+ return resp, nil
+ case err := <-t.errchan:
+ return nil, err
+ case <-t.startCancel:
+ case <-req.Cancel:
+ }
+ select {
+ // this simulates that the request is finished before cancel effects
+ case resp := <-t.respchan:
+ return resp, nil
+ // wait on finishCancel to simulate taking some amount of
+ // time while calling CancelRequest
+ case <-t.finishCancel:
+ return nil, errors.New("cancelled")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/keys.generated.go b/vendor/github.com/coreos/etcd/client/keys.generated.go
new file mode 100644
index 000000000..9a575e4d7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/keys.generated.go
@@ -0,0 +1,921 @@
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED BY codecgen.
+// ************************************************************
+
+package client
+
+import (
+ "errors"
+ "fmt"
+ codec1978 "github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec"
+ "reflect"
+ "runtime"
+ time "time"
+)
+
+const (
+ codecSelferC_UTF81819 = 1
+ codecSelferC_RAW1819 = 0
+ codecSelferValueTypeArray1819 = 10
+ codecSelferValueTypeMap1819 = 9
+)
+
+var (
+ codecSelferBitsize1819 = uint8(reflect.TypeOf(uint(0)).Bits())
+ codecSelferOnlyMapOrArrayEncodeToStructErr1819 = errors.New(`only encoded map or array can be decoded into a struct`)
+)
+
+type codecSelfer1819 struct{}
+
+func init() {
+ if codec1978.GenVersion != 4 {
+ _, file, _, _ := runtime.Caller(0)
+ err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v",
+ 4, codec1978.GenVersion, file)
+ panic(err)
+ }
+ if false { // reference the types, but skip this branch at build/run time
+ var v0 time.Time
+ _ = v0
+ }
+}
+
+func (x *Response) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym1 := z.EncBinary()
+ _ = yym1
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ yysep2 := !z.EncBinary()
+ yy2arr2 := z.EncBasicHandle().StructToArray
+ var yyq2 [3]bool
+ _, _, _ = yysep2, yyq2, yy2arr2
+ const yyr2 bool = false
+ if yyr2 || yy2arr2 {
+ r.EncodeArrayStart(3)
+ } else {
+ var yynn2 int = 3
+ for _, b := range yyq2 {
+ if b {
+ yynn2++
+ }
+ }
+ r.EncodeMapStart(yynn2)
+ }
+ if yyr2 || yy2arr2 {
+ yym4 := z.EncBinary()
+ _ = yym4
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string(x.Action))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("action"))
+ yym5 := z.EncBinary()
+ _ = yym5
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string(x.Action))
+ }
+ }
+ if yyr2 || yy2arr2 {
+ if x.Node == nil {
+ r.EncodeNil()
+ } else {
+ x.Node.CodecEncodeSelf(e)
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("node"))
+ if x.Node == nil {
+ r.EncodeNil()
+ } else {
+ x.Node.CodecEncodeSelf(e)
+ }
+ }
+ if yyr2 || yy2arr2 {
+ if x.PrevNode == nil {
+ r.EncodeNil()
+ } else {
+ x.PrevNode.CodecEncodeSelf(e)
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("prevNode"))
+ if x.PrevNode == nil {
+ r.EncodeNil()
+ } else {
+ x.PrevNode.CodecEncodeSelf(e)
+ }
+ }
+ if yysep2 {
+ r.EncodeEnd()
+ }
+ }
+ }
+}
+
+func (x *Response) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym8 := z.DecBinary()
+ _ = yym8
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ if r.IsContainerType(codecSelferValueTypeMap1819) {
+ yyl9 := r.ReadMapStart()
+ if yyl9 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromMap(yyl9, d)
+ }
+ } else if r.IsContainerType(codecSelferValueTypeArray1819) {
+ yyl9 := r.ReadArrayStart()
+ if yyl9 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromArray(yyl9, d)
+ }
+ } else {
+ panic(codecSelferOnlyMapOrArrayEncodeToStructErr1819)
+ }
+ }
+}
+
+func (x *Response) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yys10Slc = z.DecScratchBuffer() // default slice to decode into
+ _ = yys10Slc
+ var yyhl10 bool = l >= 0
+ for yyj10 := 0; ; yyj10++ {
+ if yyhl10 {
+ if yyj10 >= l {
+ break
+ }
+ } else {
+ if r.CheckBreak() {
+ break
+ }
+ }
+ yys10Slc = r.DecodeBytes(yys10Slc, true, true)
+ yys10 := string(yys10Slc)
+ switch yys10 {
+ case "action":
+ if r.TryDecodeAsNil() {
+ x.Action = ""
+ } else {
+ x.Action = string(r.DecodeString())
+ }
+ case "node":
+ if r.TryDecodeAsNil() {
+ if x.Node != nil {
+ x.Node = nil
+ }
+ } else {
+ if x.Node == nil {
+ x.Node = new(Node)
+ }
+ x.Node.CodecDecodeSelf(d)
+ }
+ case "prevNode":
+ if r.TryDecodeAsNil() {
+ if x.PrevNode != nil {
+ x.PrevNode = nil
+ }
+ } else {
+ if x.PrevNode == nil {
+ x.PrevNode = new(Node)
+ }
+ x.PrevNode.CodecDecodeSelf(d)
+ }
+ default:
+ z.DecStructFieldNotFound(-1, yys10)
+ } // end switch yys10
+ } // end for yyj10
+ if !yyhl10 {
+ r.ReadEnd()
+ }
+}
+
+func (x *Response) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yyj14 int
+ var yyb14 bool
+ var yyhl14 bool = l >= 0
+ yyj14++
+ if yyhl14 {
+ yyb14 = yyj14 > l
+ } else {
+ yyb14 = r.CheckBreak()
+ }
+ if yyb14 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Action = ""
+ } else {
+ x.Action = string(r.DecodeString())
+ }
+ yyj14++
+ if yyhl14 {
+ yyb14 = yyj14 > l
+ } else {
+ yyb14 = r.CheckBreak()
+ }
+ if yyb14 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ if x.Node != nil {
+ x.Node = nil
+ }
+ } else {
+ if x.Node == nil {
+ x.Node = new(Node)
+ }
+ x.Node.CodecDecodeSelf(d)
+ }
+ yyj14++
+ if yyhl14 {
+ yyb14 = yyj14 > l
+ } else {
+ yyb14 = r.CheckBreak()
+ }
+ if yyb14 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ if x.PrevNode != nil {
+ x.PrevNode = nil
+ }
+ } else {
+ if x.PrevNode == nil {
+ x.PrevNode = new(Node)
+ }
+ x.PrevNode.CodecDecodeSelf(d)
+ }
+ for {
+ yyj14++
+ if yyhl14 {
+ yyb14 = yyj14 > l
+ } else {
+ yyb14 = r.CheckBreak()
+ }
+ if yyb14 {
+ break
+ }
+ z.DecStructFieldNotFound(yyj14-1, "")
+ }
+ r.ReadEnd()
+}
+
+func (x *Node) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym18 := z.EncBinary()
+ _ = yym18
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ yysep19 := !z.EncBinary()
+ yy2arr19 := z.EncBasicHandle().StructToArray
+ var yyq19 [8]bool
+ _, _, _ = yysep19, yyq19, yy2arr19
+ const yyr19 bool = false
+ yyq19[1] = x.Dir != false
+ yyq19[6] = x.Expiration != nil
+ yyq19[7] = x.TTL != 0
+ if yyr19 || yy2arr19 {
+ r.EncodeArrayStart(8)
+ } else {
+ var yynn19 int = 5
+ for _, b := range yyq19 {
+ if b {
+ yynn19++
+ }
+ }
+ r.EncodeMapStart(yynn19)
+ }
+ if yyr19 || yy2arr19 {
+ yym21 := z.EncBinary()
+ _ = yym21
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string(x.Key))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("key"))
+ yym22 := z.EncBinary()
+ _ = yym22
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string(x.Key))
+ }
+ }
+ if yyr19 || yy2arr19 {
+ if yyq19[1] {
+ yym24 := z.EncBinary()
+ _ = yym24
+ if false {
+ } else {
+ r.EncodeBool(bool(x.Dir))
+ }
+ } else {
+ r.EncodeBool(false)
+ }
+ } else {
+ if yyq19[1] {
+ r.EncodeString(codecSelferC_UTF81819, string("dir"))
+ yym25 := z.EncBinary()
+ _ = yym25
+ if false {
+ } else {
+ r.EncodeBool(bool(x.Dir))
+ }
+ }
+ }
+ if yyr19 || yy2arr19 {
+ yym27 := z.EncBinary()
+ _ = yym27
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string(x.Value))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("value"))
+ yym28 := z.EncBinary()
+ _ = yym28
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string(x.Value))
+ }
+ }
+ if yyr19 || yy2arr19 {
+ if x.Nodes == nil {
+ r.EncodeNil()
+ } else {
+ x.Nodes.CodecEncodeSelf(e)
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("nodes"))
+ if x.Nodes == nil {
+ r.EncodeNil()
+ } else {
+ x.Nodes.CodecEncodeSelf(e)
+ }
+ }
+ if yyr19 || yy2arr19 {
+ yym31 := z.EncBinary()
+ _ = yym31
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.CreatedIndex))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("createdIndex"))
+ yym32 := z.EncBinary()
+ _ = yym32
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.CreatedIndex))
+ }
+ }
+ if yyr19 || yy2arr19 {
+ yym34 := z.EncBinary()
+ _ = yym34
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.ModifiedIndex))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81819, string("modifiedIndex"))
+ yym35 := z.EncBinary()
+ _ = yym35
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.ModifiedIndex))
+ }
+ }
+ if yyr19 || yy2arr19 {
+ if yyq19[6] {
+ if x.Expiration == nil {
+ r.EncodeNil()
+ } else {
+ yym37 := z.EncBinary()
+ _ = yym37
+ if false {
+ } else if yym38 := z.TimeRtidIfBinc(); yym38 != 0 {
+ r.EncodeBuiltin(yym38, x.Expiration)
+ } else if z.HasExtensions() && z.EncExt(x.Expiration) {
+ } else if yym37 {
+ z.EncBinaryMarshal(x.Expiration)
+ } else if !yym37 && z.IsJSONHandle() {
+ z.EncJSONMarshal(x.Expiration)
+ } else {
+ z.EncFallback(x.Expiration)
+ }
+ }
+ } else {
+ r.EncodeNil()
+ }
+ } else {
+ if yyq19[6] {
+ r.EncodeString(codecSelferC_UTF81819, string("expiration"))
+ if x.Expiration == nil {
+ r.EncodeNil()
+ } else {
+ yym39 := z.EncBinary()
+ _ = yym39
+ if false {
+ } else if yym40 := z.TimeRtidIfBinc(); yym40 != 0 {
+ r.EncodeBuiltin(yym40, x.Expiration)
+ } else if z.HasExtensions() && z.EncExt(x.Expiration) {
+ } else if yym39 {
+ z.EncBinaryMarshal(x.Expiration)
+ } else if !yym39 && z.IsJSONHandle() {
+ z.EncJSONMarshal(x.Expiration)
+ } else {
+ z.EncFallback(x.Expiration)
+ }
+ }
+ }
+ }
+ if yyr19 || yy2arr19 {
+ if yyq19[7] {
+ yym42 := z.EncBinary()
+ _ = yym42
+ if false {
+ } else {
+ r.EncodeInt(int64(x.TTL))
+ }
+ } else {
+ r.EncodeInt(0)
+ }
+ } else {
+ if yyq19[7] {
+ r.EncodeString(codecSelferC_UTF81819, string("ttl"))
+ yym43 := z.EncBinary()
+ _ = yym43
+ if false {
+ } else {
+ r.EncodeInt(int64(x.TTL))
+ }
+ }
+ }
+ if yysep19 {
+ r.EncodeEnd()
+ }
+ }
+ }
+}
+
+func (x *Node) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym44 := z.DecBinary()
+ _ = yym44
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ if r.IsContainerType(codecSelferValueTypeMap1819) {
+ yyl45 := r.ReadMapStart()
+ if yyl45 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromMap(yyl45, d)
+ }
+ } else if r.IsContainerType(codecSelferValueTypeArray1819) {
+ yyl45 := r.ReadArrayStart()
+ if yyl45 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromArray(yyl45, d)
+ }
+ } else {
+ panic(codecSelferOnlyMapOrArrayEncodeToStructErr1819)
+ }
+ }
+}
+
+func (x *Node) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yys46Slc = z.DecScratchBuffer() // default slice to decode into
+ _ = yys46Slc
+ var yyhl46 bool = l >= 0
+ for yyj46 := 0; ; yyj46++ {
+ if yyhl46 {
+ if yyj46 >= l {
+ break
+ }
+ } else {
+ if r.CheckBreak() {
+ break
+ }
+ }
+ yys46Slc = r.DecodeBytes(yys46Slc, true, true)
+ yys46 := string(yys46Slc)
+ switch yys46 {
+ case "key":
+ if r.TryDecodeAsNil() {
+ x.Key = ""
+ } else {
+ x.Key = string(r.DecodeString())
+ }
+ case "dir":
+ if r.TryDecodeAsNil() {
+ x.Dir = false
+ } else {
+ x.Dir = bool(r.DecodeBool())
+ }
+ case "value":
+ if r.TryDecodeAsNil() {
+ x.Value = ""
+ } else {
+ x.Value = string(r.DecodeString())
+ }
+ case "nodes":
+ if r.TryDecodeAsNil() {
+ x.Nodes = nil
+ } else {
+ yyv50 := &x.Nodes
+ yyv50.CodecDecodeSelf(d)
+ }
+ case "createdIndex":
+ if r.TryDecodeAsNil() {
+ x.CreatedIndex = 0
+ } else {
+ x.CreatedIndex = uint64(r.DecodeUint(64))
+ }
+ case "modifiedIndex":
+ if r.TryDecodeAsNil() {
+ x.ModifiedIndex = 0
+ } else {
+ x.ModifiedIndex = uint64(r.DecodeUint(64))
+ }
+ case "expiration":
+ if r.TryDecodeAsNil() {
+ if x.Expiration != nil {
+ x.Expiration = nil
+ }
+ } else {
+ if x.Expiration == nil {
+ x.Expiration = new(time.Time)
+ }
+ yym54 := z.DecBinary()
+ _ = yym54
+ if false {
+ } else if yym55 := z.TimeRtidIfBinc(); yym55 != 0 {
+ r.DecodeBuiltin(yym55, x.Expiration)
+ } else if z.HasExtensions() && z.DecExt(x.Expiration) {
+ } else if yym54 {
+ z.DecBinaryUnmarshal(x.Expiration)
+ } else if !yym54 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(x.Expiration)
+ } else {
+ z.DecFallback(x.Expiration, false)
+ }
+ }
+ case "ttl":
+ if r.TryDecodeAsNil() {
+ x.TTL = 0
+ } else {
+ x.TTL = int64(r.DecodeInt(64))
+ }
+ default:
+ z.DecStructFieldNotFound(-1, yys46)
+ } // end switch yys46
+ } // end for yyj46
+ if !yyhl46 {
+ r.ReadEnd()
+ }
+}
+
+func (x *Node) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yyj57 int
+ var yyb57 bool
+ var yyhl57 bool = l >= 0
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Key = ""
+ } else {
+ x.Key = string(r.DecodeString())
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Dir = false
+ } else {
+ x.Dir = bool(r.DecodeBool())
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Value = ""
+ } else {
+ x.Value = string(r.DecodeString())
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Nodes = nil
+ } else {
+ yyv61 := &x.Nodes
+ yyv61.CodecDecodeSelf(d)
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.CreatedIndex = 0
+ } else {
+ x.CreatedIndex = uint64(r.DecodeUint(64))
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.ModifiedIndex = 0
+ } else {
+ x.ModifiedIndex = uint64(r.DecodeUint(64))
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ if x.Expiration != nil {
+ x.Expiration = nil
+ }
+ } else {
+ if x.Expiration == nil {
+ x.Expiration = new(time.Time)
+ }
+ yym65 := z.DecBinary()
+ _ = yym65
+ if false {
+ } else if yym66 := z.TimeRtidIfBinc(); yym66 != 0 {
+ r.DecodeBuiltin(yym66, x.Expiration)
+ } else if z.HasExtensions() && z.DecExt(x.Expiration) {
+ } else if yym65 {
+ z.DecBinaryUnmarshal(x.Expiration)
+ } else if !yym65 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(x.Expiration)
+ } else {
+ z.DecFallback(x.Expiration, false)
+ }
+ }
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.TTL = 0
+ } else {
+ x.TTL = int64(r.DecodeInt(64))
+ }
+ for {
+ yyj57++
+ if yyhl57 {
+ yyb57 = yyj57 > l
+ } else {
+ yyb57 = r.CheckBreak()
+ }
+ if yyb57 {
+ break
+ }
+ z.DecStructFieldNotFound(yyj57-1, "")
+ }
+ r.ReadEnd()
+}
+
+func (x Nodes) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym68 := z.EncBinary()
+ _ = yym68
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ h.encNodes((Nodes)(x), e)
+ }
+ }
+}
+
+func (x *Nodes) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym69 := z.DecBinary()
+ _ = yym69
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ h.decNodes((*Nodes)(x), d)
+ }
+}
+
+func (x codecSelfer1819) encNodes(v Nodes, e *codec1978.Encoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ r.EncodeArrayStart(len(v))
+ for _, yyv70 := range v {
+ if yyv70 == nil {
+ r.EncodeNil()
+ } else {
+ yyv70.CodecEncodeSelf(e)
+ }
+ }
+ r.EncodeEnd()
+}
+
+func (x codecSelfer1819) decNodes(v *Nodes, d *codec1978.Decoder) {
+ var h codecSelfer1819
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+
+ yyv71 := *v
+ yyh71, yyl71 := z.DecSliceHelperStart()
+
+ var yyrr71, yyrl71 int
+ var yyc71, yyrt71 bool
+ _, _, _ = yyc71, yyrt71, yyrl71
+ yyrr71 = yyl71
+
+ if yyv71 == nil {
+ if yyrl71, yyrt71 = z.DecInferLen(yyl71, z.DecBasicHandle().MaxInitLen, 8); yyrt71 {
+ yyrr71 = yyrl71
+ }
+ yyv71 = make(Nodes, yyrl71)
+ yyc71 = true
+ }
+
+ if yyl71 == 0 {
+ if len(yyv71) != 0 {
+ yyv71 = yyv71[:0]
+ yyc71 = true
+ }
+ } else if yyl71 > 0 {
+
+ if yyl71 > cap(yyv71) {
+ yyrl71, yyrt71 = z.DecInferLen(yyl71, z.DecBasicHandle().MaxInitLen, 8)
+ yyv71 = make([]*Node, yyrl71)
+ yyc71 = true
+
+ yyrr71 = len(yyv71)
+ } else if yyl71 != len(yyv71) {
+ yyv71 = yyv71[:yyl71]
+ yyc71 = true
+ }
+ yyj71 := 0
+ for ; yyj71 < yyrr71; yyj71++ {
+ if r.TryDecodeAsNil() {
+ if yyv71[yyj71] != nil {
+ *yyv71[yyj71] = Node{}
+ }
+ } else {
+ if yyv71[yyj71] == nil {
+ yyv71[yyj71] = new(Node)
+ }
+ yyw72 := yyv71[yyj71]
+ yyw72.CodecDecodeSelf(d)
+ }
+
+ }
+ if yyrt71 {
+ for ; yyj71 < yyl71; yyj71++ {
+ yyv71 = append(yyv71, nil)
+ if r.TryDecodeAsNil() {
+ if yyv71[yyj71] != nil {
+ *yyv71[yyj71] = Node{}
+ }
+ } else {
+ if yyv71[yyj71] == nil {
+ yyv71[yyj71] = new(Node)
+ }
+ yyw73 := yyv71[yyj71]
+ yyw73.CodecDecodeSelf(d)
+ }
+
+ }
+ }
+
+ } else {
+ for yyj71 := 0; !r.CheckBreak(); yyj71++ {
+ if yyj71 >= len(yyv71) {
+ yyv71 = append(yyv71, nil) // var yyz71 *Node
+ yyc71 = true
+ }
+
+ if yyj71 < len(yyv71) {
+ if r.TryDecodeAsNil() {
+ if yyv71[yyj71] != nil {
+ *yyv71[yyj71] = Node{}
+ }
+ } else {
+ if yyv71[yyj71] == nil {
+ yyv71[yyj71] = new(Node)
+ }
+ yyw74 := yyv71[yyj71]
+ yyw74.CodecDecodeSelf(d)
+ }
+
+ } else {
+ z.DecSwallow()
+ }
+
+ }
+ yyh71.End()
+ }
+ if yyc71 {
+ *v = yyv71
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/client/keys.go b/vendor/github.com/coreos/etcd/client/keys.go
new file mode 100644
index 000000000..67fa02d0e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/keys.go
@@ -0,0 +1,651 @@
+// Copyright 2015 CoreOS, 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 client
+
+//go:generate codecgen -d 1819 -r "Node|Response|Nodes" -o keys.generated.go keys.go
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/ugorji/go/codec"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/pkg/pathutil"
+)
+
+const (
+ ErrorCodeKeyNotFound = 100
+ ErrorCodeTestFailed = 101
+ ErrorCodeNotFile = 102
+ ErrorCodeNotDir = 104
+ ErrorCodeNodeExist = 105
+ ErrorCodeRootROnly = 107
+ ErrorCodeDirNotEmpty = 108
+ ErrorCodeUnauthorized = 110
+
+ ErrorCodePrevValueRequired = 201
+ ErrorCodeTTLNaN = 202
+ ErrorCodeIndexNaN = 203
+ ErrorCodeInvalidField = 209
+ ErrorCodeInvalidForm = 210
+
+ ErrorCodeRaftInternal = 300
+ ErrorCodeLeaderElect = 301
+
+ ErrorCodeWatcherCleared = 400
+ ErrorCodeEventIndexCleared = 401
+)
+
+type Error struct {
+ Code int `json:"errorCode"`
+ Message string `json:"message"`
+ Cause string `json:"cause"`
+ Index uint64 `json:"index"`
+}
+
+func (e Error) Error() string {
+ return fmt.Sprintf("%v: %v (%v) [%v]", e.Code, e.Message, e.Cause, e.Index)
+}
+
+var (
+ ErrInvalidJSON = errors.New("client: response is invalid json. The endpoint is probably not valid etcd cluster endpoint.")
+ ErrEmptyBody = errors.New("client: response body is empty")
+)
+
+// PrevExistType is used to define an existence condition when setting
+// or deleting Nodes.
+type PrevExistType string
+
+const (
+ PrevIgnore = PrevExistType("")
+ PrevExist = PrevExistType("true")
+ PrevNoExist = PrevExistType("false")
+)
+
+var (
+ defaultV2KeysPrefix = "/v2/keys"
+)
+
+// NewKeysAPI builds a KeysAPI that interacts with etcd's key-value
+// API over HTTP.
+func NewKeysAPI(c Client) KeysAPI {
+ return NewKeysAPIWithPrefix(c, defaultV2KeysPrefix)
+}
+
+// NewKeysAPIWithPrefix acts like NewKeysAPI, but allows the caller
+// to provide a custom base URL path. This should only be used in
+// very rare cases.
+func NewKeysAPIWithPrefix(c Client, p string) KeysAPI {
+ return &httpKeysAPI{
+ client: c,
+ prefix: p,
+ }
+}
+
+type KeysAPI interface {
+ // Get retrieves a set of Nodes from etcd
+ Get(ctx context.Context, key string, opts *GetOptions) (*Response, error)
+
+ // Set assigns a new value to a Node identified by a given key. The caller
+ // may define a set of conditions in the SetOptions. If SetOptions.Dir=true
+ // than value is ignored.
+ Set(ctx context.Context, key, value string, opts *SetOptions) (*Response, error)
+
+ // Delete removes a Node identified by the given key, optionally destroying
+ // all of its children as well. The caller may define a set of required
+ // conditions in an DeleteOptions object.
+ Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error)
+
+ // Create is an alias for Set w/ PrevExist=false
+ Create(ctx context.Context, key, value string) (*Response, error)
+
+ // CreateInOrder is used to atomically create in-order keys within the given directory.
+ CreateInOrder(ctx context.Context, dir, value string, opts *CreateInOrderOptions) (*Response, error)
+
+ // Update is an alias for Set w/ PrevExist=true
+ Update(ctx context.Context, key, value string) (*Response, error)
+
+ // Watcher builds a new Watcher targeted at a specific Node identified
+ // by the given key. The Watcher may be configured at creation time
+ // through a WatcherOptions object. The returned Watcher is designed
+ // to emit events that happen to a Node, and optionally to its children.
+ Watcher(key string, opts *WatcherOptions) Watcher
+}
+
+type WatcherOptions struct {
+ // AfterIndex defines the index after-which the Watcher should
+ // start emitting events. For example, if a value of 5 is
+ // provided, the first event will have an index >= 6.
+ //
+ // Setting AfterIndex to 0 (default) means that the Watcher
+ // should start watching for events starting at the current
+ // index, whatever that may be.
+ AfterIndex uint64
+
+ // Recursive specifies whether or not the Watcher should emit
+ // events that occur in children of the given keyspace. If set
+ // to false (default), events will be limited to those that
+ // occur for the exact key.
+ Recursive bool
+}
+
+type CreateInOrderOptions struct {
+ // TTL defines a period of time after-which the Node should
+ // expire and no longer exist. Values <= 0 are ignored. Given
+ // that the zero-value is ignored, TTL cannot be used to set
+ // a TTL of 0.
+ TTL time.Duration
+}
+
+type SetOptions struct {
+ // PrevValue specifies what the current value of the Node must
+ // be in order for the Set operation to succeed.
+ //
+ // Leaving this field empty means that the caller wishes to
+ // ignore the current value of the Node. This cannot be used
+ // to compare the Node's current value to an empty string.
+ //
+ // PrevValue is ignored if Dir=true
+ PrevValue string
+
+ // PrevIndex indicates what the current ModifiedIndex of the
+ // Node must be in order for the Set operation to succeed.
+ //
+ // If PrevIndex is set to 0 (default), no comparison is made.
+ PrevIndex uint64
+
+ // PrevExist specifies whether the Node must currently exist
+ // (PrevExist) or not (PrevNoExist). If the caller does not
+ // care about existence, set PrevExist to PrevIgnore, or simply
+ // leave it unset.
+ PrevExist PrevExistType
+
+ // TTL defines a period of time after-which the Node should
+ // expire and no longer exist. Values <= 0 are ignored. Given
+ // that the zero-value is ignored, TTL cannot be used to set
+ // a TTL of 0.
+ TTL time.Duration
+
+ // Dir specifies whether or not this Node should be created as a directory.
+ Dir bool
+}
+
+type GetOptions struct {
+ // Recursive defines whether or not all children of the Node
+ // should be returned.
+ Recursive bool
+
+ // Sort instructs the server whether or not to sort the Nodes.
+ // If true, the Nodes are sorted alphabetically by key in
+ // ascending order (A to z). If false (default), the Nodes will
+ // not be sorted and the ordering used should not be considered
+ // predictable.
+ Sort bool
+
+ // Quorum specifies whether it gets the latest committed value that
+ // has been applied in quorum of members, which ensures external
+ // consistency (or linearizability).
+ Quorum bool
+}
+
+type DeleteOptions struct {
+ // PrevValue specifies what the current value of the Node must
+ // be in order for the Delete operation to succeed.
+ //
+ // Leaving this field empty means that the caller wishes to
+ // ignore the current value of the Node. This cannot be used
+ // to compare the Node's current value to an empty string.
+ PrevValue string
+
+ // PrevIndex indicates what the current ModifiedIndex of the
+ // Node must be in order for the Delete operation to succeed.
+ //
+ // If PrevIndex is set to 0 (default), no comparison is made.
+ PrevIndex uint64
+
+ // Recursive defines whether or not all children of the Node
+ // should be deleted. If set to true, all children of the Node
+ // identified by the given key will be deleted. If left unset
+ // or explicitly set to false, only a single Node will be
+ // deleted.
+ Recursive bool
+
+ // Dir specifies whether or not this Node should be removed as a directory.
+ Dir bool
+}
+
+type Watcher interface {
+ // Next blocks until an etcd event occurs, then returns a Response
+ // represeting that event. The behavior of Next depends on the
+ // WatcherOptions used to construct the Watcher. Next is designed to
+ // be called repeatedly, each time blocking until a subsequent event
+ // is available.
+ //
+ // If the provided context is cancelled, Next will return a non-nil
+ // error. Any other failures encountered while waiting for the next
+ // event (connection issues, deserialization failures, etc) will
+ // also result in a non-nil error.
+ Next(context.Context) (*Response, error)
+}
+
+type Response struct {
+ // Action is the name of the operation that occurred. Possible values
+ // include get, set, delete, update, create, compareAndSwap,
+ // compareAndDelete and expire.
+ Action string `json:"action"`
+
+ // Node represents the state of the relevant etcd Node.
+ Node *Node `json:"node"`
+
+ // PrevNode represents the previous state of the Node. PrevNode is non-nil
+ // only if the Node existed before the action occurred and the action
+ // caused a change to the Node.
+ PrevNode *Node `json:"prevNode"`
+
+ // Index holds the cluster-level index at the time the Response was generated.
+ // This index is not tied to the Node(s) contained in this Response.
+ Index uint64 `json:"-"`
+}
+
+type Node struct {
+ // Key represents the unique location of this Node (e.g. "/foo/bar").
+ Key string `json:"key"`
+
+ // Dir reports whether node describes a directory.
+ Dir bool `json:"dir,omitempty"`
+
+ // Value is the current data stored on this Node. If this Node
+ // is a directory, Value will be empty.
+ Value string `json:"value"`
+
+ // Nodes holds the children of this Node, only if this Node is a directory.
+ // This slice of will be arbitrarily deep (children, grandchildren, great-
+ // grandchildren, etc.) if a recursive Get or Watch request were made.
+ Nodes Nodes `json:"nodes"`
+
+ // CreatedIndex is the etcd index at-which this Node was created.
+ CreatedIndex uint64 `json:"createdIndex"`
+
+ // ModifiedIndex is the etcd index at-which this Node was last modified.
+ ModifiedIndex uint64 `json:"modifiedIndex"`
+
+ // Expiration is the server side expiration time of the key.
+ Expiration *time.Time `json:"expiration,omitempty"`
+
+ // TTL is the time to live of the key in second.
+ TTL int64 `json:"ttl,omitempty"`
+}
+
+func (n *Node) String() string {
+ return fmt.Sprintf("{Key: %s, CreatedIndex: %d, ModifiedIndex: %d, TTL: %d}", n.Key, n.CreatedIndex, n.ModifiedIndex, n.TTL)
+}
+
+// TTLDuration returns the Node's TTL as a time.Duration object
+func (n *Node) TTLDuration() time.Duration {
+ return time.Duration(n.TTL) * time.Second
+}
+
+type Nodes []*Node
+
+// interfaces for sorting
+func (ns Nodes) Len() int { return len(ns) }
+func (ns Nodes) Less(i, j int) bool { return ns[i].Key < ns[j].Key }
+func (ns Nodes) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] }
+
+type httpKeysAPI struct {
+ client httpClient
+ prefix string
+}
+
+func (k *httpKeysAPI) Set(ctx context.Context, key, val string, opts *SetOptions) (*Response, error) {
+ act := &setAction{
+ Prefix: k.prefix,
+ Key: key,
+ Value: val,
+ }
+
+ if opts != nil {
+ act.PrevValue = opts.PrevValue
+ act.PrevIndex = opts.PrevIndex
+ act.PrevExist = opts.PrevExist
+ act.TTL = opts.TTL
+ act.Dir = opts.Dir
+ }
+
+ resp, body, err := k.client.Do(ctx, act)
+ if err != nil {
+ return nil, err
+ }
+
+ return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
+}
+
+func (k *httpKeysAPI) Create(ctx context.Context, key, val string) (*Response, error) {
+ return k.Set(ctx, key, val, &SetOptions{PrevExist: PrevNoExist})
+}
+
+func (k *httpKeysAPI) CreateInOrder(ctx context.Context, dir, val string, opts *CreateInOrderOptions) (*Response, error) {
+ act := &createInOrderAction{
+ Prefix: k.prefix,
+ Dir: dir,
+ Value: val,
+ }
+
+ if opts != nil {
+ act.TTL = opts.TTL
+ }
+
+ resp, body, err := k.client.Do(ctx, act)
+ if err != nil {
+ return nil, err
+ }
+
+ return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
+}
+
+func (k *httpKeysAPI) Update(ctx context.Context, key, val string) (*Response, error) {
+ return k.Set(ctx, key, val, &SetOptions{PrevExist: PrevExist})
+}
+
+func (k *httpKeysAPI) Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error) {
+ act := &deleteAction{
+ Prefix: k.prefix,
+ Key: key,
+ }
+
+ if opts != nil {
+ act.PrevValue = opts.PrevValue
+ act.PrevIndex = opts.PrevIndex
+ act.Dir = opts.Dir
+ act.Recursive = opts.Recursive
+ }
+
+ resp, body, err := k.client.Do(ctx, act)
+ if err != nil {
+ return nil, err
+ }
+
+ return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
+}
+
+func (k *httpKeysAPI) Get(ctx context.Context, key string, opts *GetOptions) (*Response, error) {
+ act := &getAction{
+ Prefix: k.prefix,
+ Key: key,
+ }
+
+ if opts != nil {
+ act.Recursive = opts.Recursive
+ act.Sorted = opts.Sort
+ act.Quorum = opts.Quorum
+ }
+
+ resp, body, err := k.client.Do(ctx, act)
+ if err != nil {
+ return nil, err
+ }
+
+ return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
+}
+
+func (k *httpKeysAPI) Watcher(key string, opts *WatcherOptions) Watcher {
+ act := waitAction{
+ Prefix: k.prefix,
+ Key: key,
+ }
+
+ if opts != nil {
+ act.Recursive = opts.Recursive
+ if opts.AfterIndex > 0 {
+ act.WaitIndex = opts.AfterIndex + 1
+ }
+ }
+
+ return &httpWatcher{
+ client: k.client,
+ nextWait: act,
+ }
+}
+
+type httpWatcher struct {
+ client httpClient
+ nextWait waitAction
+}
+
+func (hw *httpWatcher) Next(ctx context.Context) (*Response, error) {
+ for {
+ httpresp, body, err := hw.client.Do(ctx, &hw.nextWait)
+ if err != nil {
+ return nil, err
+ }
+
+ resp, err := unmarshalHTTPResponse(httpresp.StatusCode, httpresp.Header, body)
+ if err != nil {
+ if err == ErrEmptyBody {
+ continue
+ }
+ return nil, err
+ }
+
+ hw.nextWait.WaitIndex = resp.Node.ModifiedIndex + 1
+ return resp, nil
+ }
+}
+
+// v2KeysURL forms a URL representing the location of a key.
+// The endpoint argument represents the base URL of an etcd
+// server. The prefix is the path needed to route from the
+// provided endpoint's path to the root of the keys API
+// (typically "/v2/keys").
+func v2KeysURL(ep url.URL, prefix, key string) *url.URL {
+ // We concatenate all parts together manually. We cannot use
+ // path.Join because it does not reserve trailing slash.
+ // We call CanonicalURLPath to further cleanup the path.
+ if prefix != "" && prefix[0] != '/' {
+ prefix = "/" + prefix
+ }
+ if key != "" && key[0] != '/' {
+ key = "/" + key
+ }
+ ep.Path = pathutil.CanonicalURLPath(ep.Path + prefix + key)
+ return &ep
+}
+
+type getAction struct {
+ Prefix string
+ Key string
+ Recursive bool
+ Sorted bool
+ Quorum bool
+}
+
+func (g *getAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2KeysURL(ep, g.Prefix, g.Key)
+
+ params := u.Query()
+ params.Set("recursive", strconv.FormatBool(g.Recursive))
+ params.Set("sorted", strconv.FormatBool(g.Sorted))
+ params.Set("quorum", strconv.FormatBool(g.Quorum))
+ u.RawQuery = params.Encode()
+
+ req, _ := http.NewRequest("GET", u.String(), nil)
+ return req
+}
+
+type waitAction struct {
+ Prefix string
+ Key string
+ WaitIndex uint64
+ Recursive bool
+}
+
+func (w *waitAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2KeysURL(ep, w.Prefix, w.Key)
+
+ params := u.Query()
+ params.Set("wait", "true")
+ params.Set("waitIndex", strconv.FormatUint(w.WaitIndex, 10))
+ params.Set("recursive", strconv.FormatBool(w.Recursive))
+ u.RawQuery = params.Encode()
+
+ req, _ := http.NewRequest("GET", u.String(), nil)
+ return req
+}
+
+type setAction struct {
+ Prefix string
+ Key string
+ Value string
+ PrevValue string
+ PrevIndex uint64
+ PrevExist PrevExistType
+ TTL time.Duration
+ Dir bool
+}
+
+func (a *setAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2KeysURL(ep, a.Prefix, a.Key)
+
+ params := u.Query()
+ form := url.Values{}
+
+ // we're either creating a directory or setting a key
+ if a.Dir {
+ params.Set("dir", strconv.FormatBool(a.Dir))
+ } else {
+ // These options are only valid for setting a key
+ if a.PrevValue != "" {
+ params.Set("prevValue", a.PrevValue)
+ }
+ form.Add("value", a.Value)
+ }
+
+ // Options which apply to both setting a key and creating a dir
+ if a.PrevIndex != 0 {
+ params.Set("prevIndex", strconv.FormatUint(a.PrevIndex, 10))
+ }
+ if a.PrevExist != PrevIgnore {
+ params.Set("prevExist", string(a.PrevExist))
+ }
+ if a.TTL > 0 {
+ form.Add("ttl", strconv.FormatUint(uint64(a.TTL.Seconds()), 10))
+ }
+
+ u.RawQuery = params.Encode()
+ body := strings.NewReader(form.Encode())
+
+ req, _ := http.NewRequest("PUT", u.String(), body)
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ return req
+}
+
+type deleteAction struct {
+ Prefix string
+ Key string
+ PrevValue string
+ PrevIndex uint64
+ Dir bool
+ Recursive bool
+}
+
+func (a *deleteAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2KeysURL(ep, a.Prefix, a.Key)
+
+ params := u.Query()
+ if a.PrevValue != "" {
+ params.Set("prevValue", a.PrevValue)
+ }
+ if a.PrevIndex != 0 {
+ params.Set("prevIndex", strconv.FormatUint(a.PrevIndex, 10))
+ }
+ if a.Dir {
+ params.Set("dir", "true")
+ }
+ if a.Recursive {
+ params.Set("recursive", "true")
+ }
+ u.RawQuery = params.Encode()
+
+ req, _ := http.NewRequest("DELETE", u.String(), nil)
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+
+ return req
+}
+
+type createInOrderAction struct {
+ Prefix string
+ Dir string
+ Value string
+ TTL time.Duration
+}
+
+func (a *createInOrderAction) HTTPRequest(ep url.URL) *http.Request {
+ u := v2KeysURL(ep, a.Prefix, a.Dir)
+
+ form := url.Values{}
+ form.Add("value", a.Value)
+ if a.TTL > 0 {
+ form.Add("ttl", strconv.FormatUint(uint64(a.TTL.Seconds()), 10))
+ }
+ body := strings.NewReader(form.Encode())
+
+ req, _ := http.NewRequest("POST", u.String(), body)
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ return req
+}
+
+func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Response, err error) {
+ switch code {
+ case http.StatusOK, http.StatusCreated:
+ if len(body) == 0 {
+ return nil, ErrEmptyBody
+ }
+ res, err = unmarshalSuccessfulKeysResponse(header, body)
+ default:
+ err = unmarshalFailedKeysResponse(body)
+ }
+
+ return
+}
+
+func unmarshalSuccessfulKeysResponse(header http.Header, body []byte) (*Response, error) {
+ var res Response
+ err := codec.NewDecoderBytes(body, new(codec.JsonHandle)).Decode(&res)
+ if err != nil {
+ return nil, ErrInvalidJSON
+ }
+ if header.Get("X-Etcd-Index") != "" {
+ res.Index, err = strconv.ParseUint(header.Get("X-Etcd-Index"), 10, 64)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return &res, nil
+}
+
+func unmarshalFailedKeysResponse(body []byte) error {
+ var etcdErr Error
+ if err := json.Unmarshal(body, &etcdErr); err != nil {
+ return ErrInvalidJSON
+ }
+ return etcdErr
+}
diff --git a/vendor/github.com/coreos/etcd/client/keys_bench_test.go b/vendor/github.com/coreos/etcd/client/keys_bench_test.go
new file mode 100644
index 000000000..5b6541592
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/keys_bench_test.go
@@ -0,0 +1,87 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "encoding/json"
+ "net/http"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func createTestNode(size int) *Node {
+ return &Node{
+ Key: strings.Repeat("a", 30),
+ Value: strings.Repeat("a", size),
+ CreatedIndex: 123456,
+ ModifiedIndex: 123456,
+ TTL: 123456789,
+ }
+}
+
+func createTestNodeWithChildren(children, size int) *Node {
+ node := createTestNode(size)
+ for i := 0; i < children; i++ {
+ node.Nodes = append(node.Nodes, createTestNode(size))
+ }
+ return node
+}
+
+func createTestResponse(children, size int) *Response {
+ return &Response{
+ Action: "aaaaa",
+ Node: createTestNodeWithChildren(children, size),
+ PrevNode: nil,
+ }
+}
+
+func benchmarkResponseUnmarshalling(b *testing.B, children, size int) {
+ header := http.Header{}
+ header.Add("X-Etcd-Index", "123456")
+ response := createTestResponse(children, size)
+ body, err := json.Marshal(response)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ newResponse := new(Response)
+ for i := 0; i < b.N; i++ {
+ if newResponse, err = unmarshalSuccessfulKeysResponse(header, body); err != nil {
+ b.Errorf("error unmarshaling response (%v)", err)
+ }
+
+ }
+ if !reflect.DeepEqual(response.Node, newResponse.Node) {
+ b.Errorf("Unexpected difference in a parsed response: \n%+v\n%+v", response, newResponse)
+ }
+}
+
+func BenchmarkSmallResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 30, 20)
+}
+
+func BenchmarkManySmallResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 3000, 20)
+}
+
+func BenchmarkMediumResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 300, 200)
+}
+
+func BenchmarkLargeResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 3000, 2000)
+}
diff --git a/vendor/github.com/coreos/etcd/client/keys_test.go b/vendor/github.com/coreos/etcd/client/keys_test.go
new file mode 100644
index 000000000..cafe6bea4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/keys_test.go
@@ -0,0 +1,1407 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+func TestV2KeysURLHelper(t *testing.T) {
+ tests := []struct {
+ endpoint url.URL
+ prefix string
+ key string
+ want url.URL
+ }{
+ // key is empty, no problem
+ {
+ endpoint: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
+ prefix: "",
+ key: "",
+ want: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
+ },
+
+ // key is joined to path
+ {
+ endpoint: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"},
+ prefix: "",
+ key: "/foo/bar",
+ want: url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys/foo/bar"},
+ },
+
+ // key is joined to path when path is empty
+ {
+ endpoint: url.URL{Scheme: "http", Host: "example.com", Path: ""},
+ prefix: "",
+ key: "/foo/bar",
+ want: url.URL{Scheme: "http", Host: "example.com", Path: "/foo/bar"},
+ },
+
+ // Host field carries through with port
+ {
+ endpoint: url.URL{Scheme: "http", Host: "example.com:8080", Path: "/v2/keys"},
+ prefix: "",
+ key: "",
+ want: url.URL{Scheme: "http", Host: "example.com:8080", Path: "/v2/keys"},
+ },
+
+ // Scheme carries through
+ {
+ endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/v2/keys"},
+ prefix: "",
+ key: "",
+ want: url.URL{Scheme: "https", Host: "example.com", Path: "/v2/keys"},
+ },
+ // Prefix is applied
+ {
+ endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/foo"},
+ prefix: "/bar",
+ key: "/baz",
+ want: url.URL{Scheme: "https", Host: "example.com", Path: "/foo/bar/baz"},
+ },
+ // Prefix is joined to path
+ {
+ endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/foo"},
+ prefix: "/bar",
+ key: "",
+ want: url.URL{Scheme: "https", Host: "example.com", Path: "/foo/bar"},
+ },
+ // Keep trailing slash
+ {
+ endpoint: url.URL{Scheme: "https", Host: "example.com", Path: "/foo"},
+ prefix: "/bar",
+ key: "/baz/",
+ want: url.URL{Scheme: "https", Host: "example.com", Path: "/foo/bar/baz/"},
+ },
+ }
+
+ for i, tt := range tests {
+ got := v2KeysURL(tt.endpoint, tt.prefix, tt.key)
+ if tt.want != *got {
+ t.Errorf("#%d: want=%#v, got=%#v", i, tt.want, *got)
+ }
+ }
+}
+
+func TestGetAction(t *testing.T) {
+ ep := url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"}
+ baseWantURL := &url.URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/v2/keys/foo/bar",
+ }
+ wantHeader := http.Header{}
+
+ tests := []struct {
+ recursive bool
+ sorted bool
+ quorum bool
+ wantQuery string
+ }{
+ {
+ recursive: false,
+ sorted: false,
+ quorum: false,
+ wantQuery: "quorum=false&recursive=false&sorted=false",
+ },
+ {
+ recursive: true,
+ sorted: false,
+ quorum: false,
+ wantQuery: "quorum=false&recursive=true&sorted=false",
+ },
+ {
+ recursive: false,
+ sorted: true,
+ quorum: false,
+ wantQuery: "quorum=false&recursive=false&sorted=true",
+ },
+ {
+ recursive: true,
+ sorted: true,
+ quorum: false,
+ wantQuery: "quorum=false&recursive=true&sorted=true",
+ },
+ {
+ recursive: false,
+ sorted: false,
+ quorum: true,
+ wantQuery: "quorum=true&recursive=false&sorted=false",
+ },
+ }
+
+ for i, tt := range tests {
+ f := getAction{
+ Key: "/foo/bar",
+ Recursive: tt.recursive,
+ Sorted: tt.sorted,
+ Quorum: tt.quorum,
+ }
+ got := *f.HTTPRequest(ep)
+
+ wantURL := baseWantURL
+ wantURL.RawQuery = tt.wantQuery
+
+ err := assertRequest(got, "GET", wantURL, wantHeader, nil)
+ if err != nil {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+}
+
+func TestWaitAction(t *testing.T) {
+ ep := url.URL{Scheme: "http", Host: "example.com", Path: "/v2/keys"}
+ baseWantURL := &url.URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/v2/keys/foo/bar",
+ }
+ wantHeader := http.Header{}
+
+ tests := []struct {
+ waitIndex uint64
+ recursive bool
+ wantQuery string
+ }{
+ {
+ recursive: false,
+ waitIndex: uint64(0),
+ wantQuery: "recursive=false&wait=true&waitIndex=0",
+ },
+ {
+ recursive: false,
+ waitIndex: uint64(12),
+ wantQuery: "recursive=false&wait=true&waitIndex=12",
+ },
+ {
+ recursive: true,
+ waitIndex: uint64(12),
+ wantQuery: "recursive=true&wait=true&waitIndex=12",
+ },
+ }
+
+ for i, tt := range tests {
+ f := waitAction{
+ Key: "/foo/bar",
+ WaitIndex: tt.waitIndex,
+ Recursive: tt.recursive,
+ }
+ got := *f.HTTPRequest(ep)
+
+ wantURL := baseWantURL
+ wantURL.RawQuery = tt.wantQuery
+
+ err := assertRequest(got, "GET", wantURL, wantHeader, nil)
+ if err != nil {
+ t.Errorf("#%d: unexpected error: %#v", i, err)
+ }
+ }
+}
+
+func TestSetAction(t *testing.T) {
+ wantHeader := http.Header(map[string][]string{
+ "Content-Type": {"application/x-www-form-urlencoded"},
+ })
+
+ tests := []struct {
+ act setAction
+ wantURL string
+ wantBody string
+ }{
+ // default prefix
+ {
+ act: setAction{
+ Prefix: defaultV2KeysPrefix,
+ Key: "foo",
+ },
+ wantURL: "http://example.com/v2/keys/foo",
+ wantBody: "value=",
+ },
+
+ // non-default prefix
+ {
+ act: setAction{
+ Prefix: "/pfx",
+ Key: "foo",
+ },
+ wantURL: "http://example.com/pfx/foo",
+ wantBody: "value=",
+ },
+
+ // no prefix
+ {
+ act: setAction{
+ Key: "foo",
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "value=",
+ },
+
+ // Key with path separators
+ {
+ act: setAction{
+ Prefix: defaultV2KeysPrefix,
+ Key: "foo/bar/baz",
+ },
+ wantURL: "http://example.com/v2/keys/foo/bar/baz",
+ wantBody: "value=",
+ },
+
+ // Key with leading slash, Prefix with trailing slash
+ {
+ act: setAction{
+ Prefix: "/foo/",
+ Key: "/bar",
+ },
+ wantURL: "http://example.com/foo/bar",
+ wantBody: "value=",
+ },
+
+ // Key with trailing slash
+ {
+ act: setAction{
+ Key: "/foo/",
+ },
+ wantURL: "http://example.com/foo/",
+ wantBody: "value=",
+ },
+
+ // Value is set
+ {
+ act: setAction{
+ Key: "foo",
+ Value: "baz",
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "value=baz",
+ },
+
+ // PrevExist set, but still ignored
+ {
+ act: setAction{
+ Key: "foo",
+ PrevExist: PrevIgnore,
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "value=",
+ },
+
+ // PrevExist set to true
+ {
+ act: setAction{
+ Key: "foo",
+ PrevExist: PrevExist,
+ },
+ wantURL: "http://example.com/foo?prevExist=true",
+ wantBody: "value=",
+ },
+
+ // PrevExist set to false
+ {
+ act: setAction{
+ Key: "foo",
+ PrevExist: PrevNoExist,
+ },
+ wantURL: "http://example.com/foo?prevExist=false",
+ wantBody: "value=",
+ },
+
+ // PrevValue is urlencoded
+ {
+ act: setAction{
+ Key: "foo",
+ PrevValue: "bar baz",
+ },
+ wantURL: "http://example.com/foo?prevValue=bar+baz",
+ wantBody: "value=",
+ },
+
+ // PrevIndex is set
+ {
+ act: setAction{
+ Key: "foo",
+ PrevIndex: uint64(12),
+ },
+ wantURL: "http://example.com/foo?prevIndex=12",
+ wantBody: "value=",
+ },
+
+ // TTL is set
+ {
+ act: setAction{
+ Key: "foo",
+ TTL: 3 * time.Minute,
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "ttl=180&value=",
+ },
+ // Dir is set
+ {
+ act: setAction{
+ Key: "foo",
+ Dir: true,
+ },
+ wantURL: "http://example.com/foo?dir=true",
+ wantBody: "",
+ },
+ // Dir is set with a value
+ {
+ act: setAction{
+ Key: "foo",
+ Value: "bar",
+ Dir: true,
+ },
+ wantURL: "http://example.com/foo?dir=true",
+ wantBody: "",
+ },
+ // Dir is set with PrevExist set to true
+ {
+ act: setAction{
+ Key: "foo",
+ PrevExist: PrevExist,
+ Dir: true,
+ },
+ wantURL: "http://example.com/foo?dir=true&prevExist=true",
+ wantBody: "",
+ },
+ // Dir is set with PrevValue
+ {
+ act: setAction{
+ Key: "foo",
+ PrevValue: "bar",
+ Dir: true,
+ },
+ wantURL: "http://example.com/foo?dir=true",
+ wantBody: "",
+ },
+ }
+
+ for i, tt := range tests {
+ u, err := url.Parse(tt.wantURL)
+ if err != nil {
+ t.Errorf("#%d: unable to use wantURL fixture: %v", i, err)
+ }
+
+ got := tt.act.HTTPRequest(url.URL{Scheme: "http", Host: "example.com"})
+ if err := assertRequest(*got, "PUT", u, wantHeader, []byte(tt.wantBody)); err != nil {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+}
+
+func TestCreateInOrderAction(t *testing.T) {
+ wantHeader := http.Header(map[string][]string{
+ "Content-Type": {"application/x-www-form-urlencoded"},
+ })
+
+ tests := []struct {
+ act createInOrderAction
+ wantURL string
+ wantBody string
+ }{
+ // default prefix
+ {
+ act: createInOrderAction{
+ Prefix: defaultV2KeysPrefix,
+ Dir: "foo",
+ },
+ wantURL: "http://example.com/v2/keys/foo",
+ wantBody: "value=",
+ },
+
+ // non-default prefix
+ {
+ act: createInOrderAction{
+ Prefix: "/pfx",
+ Dir: "foo",
+ },
+ wantURL: "http://example.com/pfx/foo",
+ wantBody: "value=",
+ },
+
+ // no prefix
+ {
+ act: createInOrderAction{
+ Dir: "foo",
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "value=",
+ },
+
+ // Key with path separators
+ {
+ act: createInOrderAction{
+ Prefix: defaultV2KeysPrefix,
+ Dir: "foo/bar/baz",
+ },
+ wantURL: "http://example.com/v2/keys/foo/bar/baz",
+ wantBody: "value=",
+ },
+
+ // Key with leading slash, Prefix with trailing slash
+ {
+ act: createInOrderAction{
+ Prefix: "/foo/",
+ Dir: "/bar",
+ },
+ wantURL: "http://example.com/foo/bar",
+ wantBody: "value=",
+ },
+
+ // Key with trailing slash
+ {
+ act: createInOrderAction{
+ Dir: "/foo/",
+ },
+ wantURL: "http://example.com/foo/",
+ wantBody: "value=",
+ },
+
+ // Value is set
+ {
+ act: createInOrderAction{
+ Dir: "foo",
+ Value: "baz",
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "value=baz",
+ },
+ // TTL is set
+ {
+ act: createInOrderAction{
+ Dir: "foo",
+ TTL: 3 * time.Minute,
+ },
+ wantURL: "http://example.com/foo",
+ wantBody: "ttl=180&value=",
+ },
+ }
+
+ for i, tt := range tests {
+ u, err := url.Parse(tt.wantURL)
+ if err != nil {
+ t.Errorf("#%d: unable to use wantURL fixture: %v", i, err)
+ }
+
+ got := tt.act.HTTPRequest(url.URL{Scheme: "http", Host: "example.com"})
+ if err := assertRequest(*got, "POST", u, wantHeader, []byte(tt.wantBody)); err != nil {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+}
+
+func TestDeleteAction(t *testing.T) {
+ wantHeader := http.Header(map[string][]string{
+ "Content-Type": {"application/x-www-form-urlencoded"},
+ })
+
+ tests := []struct {
+ act deleteAction
+ wantURL string
+ }{
+ // default prefix
+ {
+ act: deleteAction{
+ Prefix: defaultV2KeysPrefix,
+ Key: "foo",
+ },
+ wantURL: "http://example.com/v2/keys/foo",
+ },
+
+ // non-default prefix
+ {
+ act: deleteAction{
+ Prefix: "/pfx",
+ Key: "foo",
+ },
+ wantURL: "http://example.com/pfx/foo",
+ },
+
+ // no prefix
+ {
+ act: deleteAction{
+ Key: "foo",
+ },
+ wantURL: "http://example.com/foo",
+ },
+
+ // Key with path separators
+ {
+ act: deleteAction{
+ Prefix: defaultV2KeysPrefix,
+ Key: "foo/bar/baz",
+ },
+ wantURL: "http://example.com/v2/keys/foo/bar/baz",
+ },
+
+ // Key with leading slash, Prefix with trailing slash
+ {
+ act: deleteAction{
+ Prefix: "/foo/",
+ Key: "/bar",
+ },
+ wantURL: "http://example.com/foo/bar",
+ },
+
+ // Key with trailing slash
+ {
+ act: deleteAction{
+ Key: "/foo/",
+ },
+ wantURL: "http://example.com/foo/",
+ },
+
+ // Recursive set to true
+ {
+ act: deleteAction{
+ Key: "foo",
+ Recursive: true,
+ },
+ wantURL: "http://example.com/foo?recursive=true",
+ },
+
+ // PrevValue is urlencoded
+ {
+ act: deleteAction{
+ Key: "foo",
+ PrevValue: "bar baz",
+ },
+ wantURL: "http://example.com/foo?prevValue=bar+baz",
+ },
+
+ // PrevIndex is set
+ {
+ act: deleteAction{
+ Key: "foo",
+ PrevIndex: uint64(12),
+ },
+ wantURL: "http://example.com/foo?prevIndex=12",
+ },
+ }
+
+ for i, tt := range tests {
+ u, err := url.Parse(tt.wantURL)
+ if err != nil {
+ t.Errorf("#%d: unable to use wantURL fixture: %v", i, err)
+ }
+
+ got := tt.act.HTTPRequest(url.URL{Scheme: "http", Host: "example.com"})
+ if err := assertRequest(*got, "DELETE", u, wantHeader, nil); err != nil {
+ t.Errorf("#%d: %v", i, err)
+ }
+ }
+}
+
+func assertRequest(got http.Request, wantMethod string, wantURL *url.URL, wantHeader http.Header, wantBody []byte) error {
+ if wantMethod != got.Method {
+ return fmt.Errorf("want.Method=%#v got.Method=%#v", wantMethod, got.Method)
+ }
+
+ if !reflect.DeepEqual(wantURL, got.URL) {
+ return fmt.Errorf("want.URL=%#v got.URL=%#v", wantURL, got.URL)
+ }
+
+ if !reflect.DeepEqual(wantHeader, got.Header) {
+ return fmt.Errorf("want.Header=%#v got.Header=%#v", wantHeader, got.Header)
+ }
+
+ if got.Body == nil {
+ if wantBody != nil {
+ return fmt.Errorf("want.Body=%v got.Body=%v", wantBody, got.Body)
+ }
+ } else {
+ if wantBody == nil {
+ return fmt.Errorf("want.Body=%v got.Body=%s", wantBody, got.Body)
+ } else {
+ gotBytes, err := ioutil.ReadAll(got.Body)
+ if err != nil {
+ return err
+ }
+
+ if !reflect.DeepEqual(wantBody, gotBytes) {
+ return fmt.Errorf("want.Body=%s got.Body=%s", wantBody, gotBytes)
+ }
+ }
+ }
+
+ return nil
+}
+
+func TestUnmarshalSuccessfulResponse(t *testing.T) {
+ var expiration time.Time
+ expiration.UnmarshalText([]byte("2015-04-07T04:40:23.044979686Z"))
+
+ tests := []struct {
+ hdr string
+ body string
+ wantRes *Response
+ wantErr bool
+ }{
+ // Neither PrevNode or Node
+ {
+ hdr: "1",
+ body: `{"action":"delete"}`,
+ wantRes: &Response{Action: "delete", Index: 1},
+ wantErr: false,
+ },
+
+ // PrevNode
+ {
+ hdr: "15",
+ body: `{"action":"delete", "prevNode": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
+ wantRes: &Response{
+ Action: "delete",
+ Index: 15,
+ Node: nil,
+ PrevNode: &Node{
+ Key: "/foo",
+ Value: "bar",
+ ModifiedIndex: 12,
+ CreatedIndex: 10,
+ },
+ },
+ wantErr: false,
+ },
+
+ // Node
+ {
+ hdr: "15",
+ body: `{"action":"get", "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10, "ttl": 10, "expiration": "2015-04-07T04:40:23.044979686Z"}}`,
+ wantRes: &Response{
+ Action: "get",
+ Index: 15,
+ Node: &Node{
+ Key: "/foo",
+ Value: "bar",
+ ModifiedIndex: 12,
+ CreatedIndex: 10,
+ TTL: 10,
+ Expiration: &expiration,
+ },
+ PrevNode: nil,
+ },
+ wantErr: false,
+ },
+
+ // Node Dir
+ {
+ hdr: "15",
+ body: `{"action":"get", "node": {"key": "/foo", "dir": true, "modifiedIndex": 12, "createdIndex": 10}}`,
+ wantRes: &Response{
+ Action: "get",
+ Index: 15,
+ Node: &Node{
+ Key: "/foo",
+ Dir: true,
+ ModifiedIndex: 12,
+ CreatedIndex: 10,
+ },
+ PrevNode: nil,
+ },
+ wantErr: false,
+ },
+
+ // PrevNode and Node
+ {
+ hdr: "15",
+ body: `{"action":"update", "prevNode": {"key": "/foo", "value": "baz", "modifiedIndex": 10, "createdIndex": 10}, "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
+ wantRes: &Response{
+ Action: "update",
+ Index: 15,
+ PrevNode: &Node{
+ Key: "/foo",
+ Value: "baz",
+ ModifiedIndex: 10,
+ CreatedIndex: 10,
+ },
+ Node: &Node{
+ Key: "/foo",
+ Value: "bar",
+ ModifiedIndex: 12,
+ CreatedIndex: 10,
+ },
+ },
+ wantErr: false,
+ },
+
+ // Garbage in body
+ {
+ hdr: "",
+ body: `garbage`,
+ wantRes: nil,
+ wantErr: true,
+ },
+
+ // non-integer index
+ {
+ hdr: "poo",
+ body: `{}`,
+ wantRes: nil,
+ wantErr: true,
+ },
+ }
+
+ for i, tt := range tests {
+ h := make(http.Header)
+ h.Add("X-Etcd-Index", tt.hdr)
+ res, err := unmarshalSuccessfulKeysResponse(h, []byte(tt.body))
+ if tt.wantErr != (err != nil) {
+ t.Errorf("#%d: wantErr=%t, err=%v", i, tt.wantErr, err)
+ }
+
+ if (res == nil) != (tt.wantRes == nil) {
+ t.Errorf("#%d: received res=%#v, but expected res=%#v", i, res, tt.wantRes)
+ continue
+ } else if tt.wantRes == nil {
+ // expected and successfully got nil response
+ continue
+ }
+
+ if res.Action != tt.wantRes.Action {
+ t.Errorf("#%d: Action=%s, expected %s", i, res.Action, tt.wantRes.Action)
+ }
+ if res.Index != tt.wantRes.Index {
+ t.Errorf("#%d: Index=%d, expected %d", i, res.Index, tt.wantRes.Index)
+ }
+ if !reflect.DeepEqual(res.Node, tt.wantRes.Node) {
+ t.Errorf("#%d: Node=%v, expected %v", i, res.Node, tt.wantRes.Node)
+ }
+ }
+}
+
+func TestUnmarshalFailedKeysResponse(t *testing.T) {
+ body := []byte(`{"errorCode":100,"message":"Key not found","cause":"/foo","index":18}`)
+
+ wantErr := Error{
+ Code: 100,
+ Message: "Key not found",
+ Cause: "/foo",
+ Index: uint64(18),
+ }
+
+ gotErr := unmarshalFailedKeysResponse(body)
+ if !reflect.DeepEqual(wantErr, gotErr) {
+ t.Errorf("unexpected error: want=%#v got=%#v", wantErr, gotErr)
+ }
+}
+
+func TestUnmarshalFailedKeysResponseBadJSON(t *testing.T) {
+ err := unmarshalFailedKeysResponse([]byte(`{"er`))
+ if err == nil {
+ t.Errorf("got nil error")
+ } else if _, ok := err.(Error); ok {
+ t.Errorf("error is of incorrect type *Error: %#v", err)
+ }
+}
+
+func TestHTTPWatcherNextWaitAction(t *testing.T) {
+ initAction := waitAction{
+ Prefix: "/pants",
+ Key: "/foo/bar",
+ Recursive: true,
+ WaitIndex: 19,
+ }
+
+ client := &actionAssertingHTTPClient{
+ t: t,
+ act: &initAction,
+ resp: http.Response{
+ StatusCode: http.StatusOK,
+ Header: http.Header{"X-Etcd-Index": []string{"42"}},
+ },
+ body: []byte(`{"action":"update","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":21,"createdIndex":19},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`),
+ }
+
+ wantResponse := &Response{
+ Action: "update",
+ Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(19), ModifiedIndex: uint64(21)},
+ PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)},
+ Index: uint64(42),
+ }
+
+ wantNextWait := waitAction{
+ Prefix: "/pants",
+ Key: "/foo/bar",
+ Recursive: true,
+ WaitIndex: 22,
+ }
+
+ watcher := &httpWatcher{
+ client: client,
+ nextWait: initAction,
+ }
+
+ resp, err := watcher.Next(context.Background())
+ if err != nil {
+ t.Errorf("non-nil error: %#v", err)
+ }
+
+ if !reflect.DeepEqual(wantResponse, resp) {
+ t.Errorf("received incorrect Response: want=%#v got=%#v", wantResponse, resp)
+ }
+
+ if !reflect.DeepEqual(wantNextWait, watcher.nextWait) {
+ t.Errorf("nextWait incorrect: want=%#v got=%#v", wantNextWait, watcher.nextWait)
+ }
+}
+
+func TestHTTPWatcherNextFail(t *testing.T) {
+ tests := []httpClient{
+ // generic HTTP client failure
+ &staticHTTPClient{
+ err: errors.New("fail!"),
+ },
+
+ // unusable status code
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+
+ // etcd Error response
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusNotFound,
+ },
+ body: []byte(`{"errorCode":100,"message":"Key not found","cause":"/foo","index":18}`),
+ },
+ }
+
+ for i, tt := range tests {
+ act := waitAction{
+ Prefix: "/pants",
+ Key: "/foo/bar",
+ Recursive: true,
+ WaitIndex: 19,
+ }
+
+ watcher := &httpWatcher{
+ client: tt,
+ nextWait: act,
+ }
+
+ resp, err := watcher.Next(context.Background())
+ if err == nil {
+ t.Errorf("#%d: expected non-nil error", i)
+ }
+ if resp != nil {
+ t.Errorf("#%d: expected nil Response, got %#v", i, resp)
+ }
+ if !reflect.DeepEqual(act, watcher.nextWait) {
+ t.Errorf("#%d: nextWait changed: want=%#v got=%#v", i, act, watcher.nextWait)
+ }
+ }
+}
+
+func TestHTTPKeysAPIWatcherAction(t *testing.T) {
+ tests := []struct {
+ key string
+ opts *WatcherOptions
+ want waitAction
+ }{
+ {
+ key: "/foo",
+ opts: nil,
+ want: waitAction{
+ Key: "/foo",
+ Recursive: false,
+ WaitIndex: 0,
+ },
+ },
+
+ {
+ key: "/foo",
+ opts: &WatcherOptions{
+ Recursive: false,
+ AfterIndex: 0,
+ },
+ want: waitAction{
+ Key: "/foo",
+ Recursive: false,
+ WaitIndex: 0,
+ },
+ },
+
+ {
+ key: "/foo",
+ opts: &WatcherOptions{
+ Recursive: true,
+ AfterIndex: 0,
+ },
+ want: waitAction{
+ Key: "/foo",
+ Recursive: true,
+ WaitIndex: 0,
+ },
+ },
+
+ {
+ key: "/foo",
+ opts: &WatcherOptions{
+ Recursive: false,
+ AfterIndex: 19,
+ },
+ want: waitAction{
+ Key: "/foo",
+ Recursive: false,
+ WaitIndex: 20,
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ kAPI := &httpKeysAPI{
+ client: &staticHTTPClient{err: errors.New("fail!")},
+ }
+
+ want := &httpWatcher{
+ client: &staticHTTPClient{err: errors.New("fail!")},
+ nextWait: tt.want,
+ }
+
+ got := kAPI.Watcher(tt.key, tt.opts)
+ if !reflect.DeepEqual(want, got) {
+ t.Errorf("#%d: incorrect watcher: want=%#v got=%#v", i, want, got)
+ }
+ }
+}
+
+func TestHTTPKeysAPISetAction(t *testing.T) {
+ tests := []struct {
+ key string
+ value string
+ opts *SetOptions
+ wantAction httpAction
+ }{
+ // nil SetOptions
+ {
+ key: "/foo",
+ value: "bar",
+ opts: nil,
+ wantAction: &setAction{
+ Key: "/foo",
+ Value: "bar",
+ PrevValue: "",
+ PrevIndex: 0,
+ PrevExist: PrevIgnore,
+ TTL: 0,
+ },
+ },
+ // empty SetOptions
+ {
+ key: "/foo",
+ value: "bar",
+ opts: &SetOptions{},
+ wantAction: &setAction{
+ Key: "/foo",
+ Value: "bar",
+ PrevValue: "",
+ PrevIndex: 0,
+ PrevExist: PrevIgnore,
+ TTL: 0,
+ },
+ },
+ // populated SetOptions
+ {
+ key: "/foo",
+ value: "bar",
+ opts: &SetOptions{
+ PrevValue: "baz",
+ PrevIndex: 13,
+ PrevExist: PrevExist,
+ TTL: time.Minute,
+ Dir: true,
+ },
+ wantAction: &setAction{
+ Key: "/foo",
+ Value: "bar",
+ PrevValue: "baz",
+ PrevIndex: 13,
+ PrevExist: PrevExist,
+ TTL: time.Minute,
+ Dir: true,
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
+ kAPI := httpKeysAPI{client: client}
+ kAPI.Set(context.Background(), tt.key, tt.value, tt.opts)
+ }
+}
+
+func TestHTTPKeysAPISetError(t *testing.T) {
+ tests := []httpClient{
+ // generic HTTP client failure
+ &staticHTTPClient{
+ err: errors.New("fail!"),
+ },
+
+ // unusable status code
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+
+ // etcd Error response
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusInternalServerError,
+ },
+ body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`),
+ },
+ }
+
+ for i, tt := range tests {
+ kAPI := httpKeysAPI{client: tt}
+ resp, err := kAPI.Set(context.Background(), "/foo", "bar", nil)
+ if err == nil {
+ t.Errorf("#%d: received nil error", i)
+ }
+ if resp != nil {
+ t.Errorf("#%d: received non-nil Response: %#v", i, resp)
+ }
+ }
+}
+
+func TestHTTPKeysAPISetResponse(t *testing.T) {
+ client := &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusOK,
+ Header: http.Header{"X-Etcd-Index": []string{"21"}},
+ },
+ body: []byte(`{"action":"set","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":21,"createdIndex":21},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`),
+ }
+
+ wantResponse := &Response{
+ Action: "set",
+ Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(21), ModifiedIndex: uint64(21)},
+ PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)},
+ Index: uint64(21),
+ }
+
+ kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
+ resp, err := kAPI.Set(context.Background(), "/foo/bar/baz", "snarf", nil)
+ if err != nil {
+ t.Errorf("non-nil error: %#v", err)
+ }
+ if !reflect.DeepEqual(wantResponse, resp) {
+ t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp)
+ }
+}
+
+func TestHTTPKeysAPIGetAction(t *testing.T) {
+ tests := []struct {
+ key string
+ opts *GetOptions
+ wantAction httpAction
+ }{
+ // nil GetOptions
+ {
+ key: "/foo",
+ opts: nil,
+ wantAction: &getAction{
+ Key: "/foo",
+ Sorted: false,
+ Recursive: false,
+ },
+ },
+ // empty GetOptions
+ {
+ key: "/foo",
+ opts: &GetOptions{},
+ wantAction: &getAction{
+ Key: "/foo",
+ Sorted: false,
+ Recursive: false,
+ },
+ },
+ // populated GetOptions
+ {
+ key: "/foo",
+ opts: &GetOptions{
+ Sort: true,
+ Recursive: true,
+ Quorum: true,
+ },
+ wantAction: &getAction{
+ Key: "/foo",
+ Sorted: true,
+ Recursive: true,
+ Quorum: true,
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
+ kAPI := httpKeysAPI{client: client}
+ kAPI.Get(context.Background(), tt.key, tt.opts)
+ }
+}
+
+func TestHTTPKeysAPIGetError(t *testing.T) {
+ tests := []httpClient{
+ // generic HTTP client failure
+ &staticHTTPClient{
+ err: errors.New("fail!"),
+ },
+
+ // unusable status code
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+
+ // etcd Error response
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusInternalServerError,
+ },
+ body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`),
+ },
+ }
+
+ for i, tt := range tests {
+ kAPI := httpKeysAPI{client: tt}
+ resp, err := kAPI.Get(context.Background(), "/foo", nil)
+ if err == nil {
+ t.Errorf("#%d: received nil error", i)
+ }
+ if resp != nil {
+ t.Errorf("#%d: received non-nil Response: %#v", i, resp)
+ }
+ }
+}
+
+func TestHTTPKeysAPIGetResponse(t *testing.T) {
+ client := &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusOK,
+ Header: http.Header{"X-Etcd-Index": []string{"42"}},
+ },
+ body: []byte(`{"action":"get","node":{"key":"/pants/foo/bar","modifiedIndex":25,"createdIndex":19,"nodes":[{"key":"/pants/foo/bar/baz","value":"snarf","createdIndex":21,"modifiedIndex":25}]}}`),
+ }
+
+ wantResponse := &Response{
+ Action: "get",
+ Node: &Node{
+ Key: "/pants/foo/bar",
+ Nodes: []*Node{
+ {Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: 21, ModifiedIndex: 25},
+ },
+ CreatedIndex: uint64(19),
+ ModifiedIndex: uint64(25),
+ },
+ Index: uint64(42),
+ }
+
+ kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
+ resp, err := kAPI.Get(context.Background(), "/foo/bar", &GetOptions{Recursive: true})
+ if err != nil {
+ t.Errorf("non-nil error: %#v", err)
+ }
+ if !reflect.DeepEqual(wantResponse, resp) {
+ t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp)
+ }
+}
+
+func TestHTTPKeysAPIDeleteAction(t *testing.T) {
+ tests := []struct {
+ key string
+ value string
+ opts *DeleteOptions
+ wantAction httpAction
+ }{
+ // nil DeleteOptions
+ {
+ key: "/foo",
+ opts: nil,
+ wantAction: &deleteAction{
+ Key: "/foo",
+ PrevValue: "",
+ PrevIndex: 0,
+ Recursive: false,
+ },
+ },
+ // empty DeleteOptions
+ {
+ key: "/foo",
+ opts: &DeleteOptions{},
+ wantAction: &deleteAction{
+ Key: "/foo",
+ PrevValue: "",
+ PrevIndex: 0,
+ Recursive: false,
+ },
+ },
+ // populated DeleteOptions
+ {
+ key: "/foo",
+ opts: &DeleteOptions{
+ PrevValue: "baz",
+ PrevIndex: 13,
+ Recursive: true,
+ },
+ wantAction: &deleteAction{
+ Key: "/foo",
+ PrevValue: "baz",
+ PrevIndex: 13,
+ Recursive: true,
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ client := &actionAssertingHTTPClient{t: t, num: i, act: tt.wantAction}
+ kAPI := httpKeysAPI{client: client}
+ kAPI.Delete(context.Background(), tt.key, tt.opts)
+ }
+}
+
+func TestHTTPKeysAPIDeleteError(t *testing.T) {
+ tests := []httpClient{
+ // generic HTTP client failure
+ &staticHTTPClient{
+ err: errors.New("fail!"),
+ },
+
+ // unusable status code
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusTeapot,
+ },
+ },
+
+ // etcd Error response
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusInternalServerError,
+ },
+ body: []byte(`{"errorCode":300,"message":"Raft internal error","cause":"/foo","index":18}`),
+ },
+ }
+
+ for i, tt := range tests {
+ kAPI := httpKeysAPI{client: tt}
+ resp, err := kAPI.Delete(context.Background(), "/foo", nil)
+ if err == nil {
+ t.Errorf("#%d: received nil error", i)
+ }
+ if resp != nil {
+ t.Errorf("#%d: received non-nil Response: %#v", i, resp)
+ }
+ }
+}
+
+func TestHTTPKeysAPIDeleteResponse(t *testing.T) {
+ client := &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusOK,
+ Header: http.Header{"X-Etcd-Index": []string{"22"}},
+ },
+ body: []byte(`{"action":"delete","node":{"key":"/pants/foo/bar/baz","value":"snarf","modifiedIndex":22,"createdIndex":19},"prevNode":{"key":"/pants/foo/bar/baz","value":"snazz","modifiedIndex":20,"createdIndex":19}}`),
+ }
+
+ wantResponse := &Response{
+ Action: "delete",
+ Node: &Node{Key: "/pants/foo/bar/baz", Value: "snarf", CreatedIndex: uint64(19), ModifiedIndex: uint64(22)},
+ PrevNode: &Node{Key: "/pants/foo/bar/baz", Value: "snazz", CreatedIndex: uint64(19), ModifiedIndex: uint64(20)},
+ Index: uint64(22),
+ }
+
+ kAPI := &httpKeysAPI{client: client, prefix: "/pants"}
+ resp, err := kAPI.Delete(context.Background(), "/foo/bar/baz", nil)
+ if err != nil {
+ t.Errorf("non-nil error: %#v", err)
+ }
+ if !reflect.DeepEqual(wantResponse, resp) {
+ t.Errorf("incorrect Response: want=%#v got=%#v", wantResponse, resp)
+ }
+}
+
+func TestHTTPKeysAPICreateAction(t *testing.T) {
+ act := &setAction{
+ Key: "/foo",
+ Value: "bar",
+ PrevExist: PrevNoExist,
+ PrevIndex: 0,
+ PrevValue: "",
+ TTL: 0,
+ }
+
+ kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
+ kAPI.Create(context.Background(), "/foo", "bar")
+}
+
+func TestHTTPKeysAPICreateInOrderAction(t *testing.T) {
+ act := &createInOrderAction{
+ Dir: "/foo",
+ Value: "bar",
+ TTL: 0,
+ }
+ kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
+ kAPI.CreateInOrder(context.Background(), "/foo", "bar", nil)
+}
+
+func TestHTTPKeysAPIUpdateAction(t *testing.T) {
+ act := &setAction{
+ Key: "/foo",
+ Value: "bar",
+ PrevExist: PrevExist,
+ PrevIndex: 0,
+ PrevValue: "",
+ TTL: 0,
+ }
+
+ kAPI := httpKeysAPI{client: &actionAssertingHTTPClient{t: t, act: act}}
+ kAPI.Update(context.Background(), "/foo", "bar")
+}
+
+func TestNodeTTLDuration(t *testing.T) {
+ tests := []struct {
+ node *Node
+ want time.Duration
+ }{
+ {
+ node: &Node{TTL: 0},
+ want: 0,
+ },
+ {
+ node: &Node{TTL: 97},
+ want: 97 * time.Second,
+ },
+ }
+
+ for i, tt := range tests {
+ got := tt.node.TTLDuration()
+ if tt.want != got {
+ t.Errorf("#%d: incorrect duration: want=%v got=%v", i, tt.want, got)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/members.go b/vendor/github.com/coreos/etcd/client/members.go
new file mode 100644
index 000000000..b6f33ed76
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/members.go
@@ -0,0 +1,272 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+ "path"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+var (
+ defaultV2MembersPrefix = "/v2/members"
+)
+
+type Member struct {
+ // ID is the unique identifier of this Member.
+ ID string `json:"id"`
+
+ // Name is a human-readable, non-unique identifier of this Member.
+ Name string `json:"name"`
+
+ // PeerURLs represents the HTTP(S) endpoints this Member uses to
+ // participate in etcd's consensus protocol.
+ PeerURLs []string `json:"peerURLs"`
+
+ // ClientURLs represents the HTTP(S) endpoints on which this Member
+ // serves it's client-facing APIs.
+ ClientURLs []string `json:"clientURLs"`
+}
+
+type memberCollection []Member
+
+func (c *memberCollection) UnmarshalJSON(data []byte) error {
+ d := struct {
+ Members []Member
+ }{}
+
+ if err := json.Unmarshal(data, &d); err != nil {
+ return err
+ }
+
+ if d.Members == nil {
+ *c = make([]Member, 0)
+ return nil
+ }
+
+ *c = d.Members
+ return nil
+}
+
+type memberCreateOrUpdateRequest struct {
+ PeerURLs types.URLs
+}
+
+func (m *memberCreateOrUpdateRequest) MarshalJSON() ([]byte, error) {
+ s := struct {
+ PeerURLs []string `json:"peerURLs"`
+ }{
+ PeerURLs: make([]string, len(m.PeerURLs)),
+ }
+
+ for i, u := range m.PeerURLs {
+ s.PeerURLs[i] = u.String()
+ }
+
+ return json.Marshal(&s)
+}
+
+// NewMembersAPI constructs a new MembersAPI that uses HTTP to
+// interact with etcd's membership API.
+func NewMembersAPI(c Client) MembersAPI {
+ return &httpMembersAPI{
+ client: c,
+ }
+}
+
+type MembersAPI interface {
+ // List enumerates the current cluster membership.
+ List(ctx context.Context) ([]Member, error)
+
+ // Add instructs etcd to accept a new Member into the cluster.
+ Add(ctx context.Context, peerURL string) (*Member, error)
+
+ // Remove demotes an existing Member out of the cluster.
+ Remove(ctx context.Context, mID string) error
+
+ // Update instructs etcd to update an existing Member in the cluster.
+ Update(ctx context.Context, mID string, peerURLs []string) error
+}
+
+type httpMembersAPI struct {
+ client httpClient
+}
+
+func (m *httpMembersAPI) List(ctx context.Context) ([]Member, error) {
+ req := &membersAPIActionList{}
+ resp, body, err := m.client.Do(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := assertStatusCode(resp.StatusCode, http.StatusOK); err != nil {
+ return nil, err
+ }
+
+ var mCollection memberCollection
+ if err := json.Unmarshal(body, &mCollection); err != nil {
+ return nil, err
+ }
+
+ return []Member(mCollection), nil
+}
+
+func (m *httpMembersAPI) Add(ctx context.Context, peerURL string) (*Member, error) {
+ urls, err := types.NewURLs([]string{peerURL})
+ if err != nil {
+ return nil, err
+ }
+
+ req := &membersAPIActionAdd{peerURLs: urls}
+ resp, body, err := m.client.Do(ctx, req)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := assertStatusCode(resp.StatusCode, http.StatusCreated, http.StatusConflict); err != nil {
+ return nil, err
+ }
+
+ if resp.StatusCode != http.StatusCreated {
+ var merr membersError
+ if err := json.Unmarshal(body, &merr); err != nil {
+ return nil, err
+ }
+ return nil, merr
+ }
+
+ var memb Member
+ if err := json.Unmarshal(body, &memb); err != nil {
+ return nil, err
+ }
+
+ return &memb, nil
+}
+
+func (m *httpMembersAPI) Update(ctx context.Context, memberID string, peerURLs []string) error {
+ urls, err := types.NewURLs(peerURLs)
+ if err != nil {
+ return err
+ }
+
+ req := &membersAPIActionUpdate{peerURLs: urls, memberID: memberID}
+ resp, body, err := m.client.Do(ctx, req)
+ if err != nil {
+ return err
+ }
+
+ if err := assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusNotFound, http.StatusConflict); err != nil {
+ return err
+ }
+
+ if resp.StatusCode != http.StatusNoContent {
+ var merr membersError
+ if err := json.Unmarshal(body, &merr); err != nil {
+ return err
+ }
+ return merr
+ }
+
+ return nil
+}
+
+func (m *httpMembersAPI) Remove(ctx context.Context, memberID string) error {
+ req := &membersAPIActionRemove{memberID: memberID}
+ resp, _, err := m.client.Do(ctx, req)
+ if err != nil {
+ return err
+ }
+
+ return assertStatusCode(resp.StatusCode, http.StatusNoContent, http.StatusGone)
+}
+
+type membersAPIActionList struct{}
+
+func (l *membersAPIActionList) HTTPRequest(ep url.URL) *http.Request {
+ u := v2MembersURL(ep)
+ req, _ := http.NewRequest("GET", u.String(), nil)
+ return req
+}
+
+type membersAPIActionRemove struct {
+ memberID string
+}
+
+func (d *membersAPIActionRemove) HTTPRequest(ep url.URL) *http.Request {
+ u := v2MembersURL(ep)
+ u.Path = path.Join(u.Path, d.memberID)
+ req, _ := http.NewRequest("DELETE", u.String(), nil)
+ return req
+}
+
+type membersAPIActionAdd struct {
+ peerURLs types.URLs
+}
+
+func (a *membersAPIActionAdd) HTTPRequest(ep url.URL) *http.Request {
+ u := v2MembersURL(ep)
+ m := memberCreateOrUpdateRequest{PeerURLs: a.peerURLs}
+ b, _ := json.Marshal(&m)
+ req, _ := http.NewRequest("POST", u.String(), bytes.NewReader(b))
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+type membersAPIActionUpdate struct {
+ memberID string
+ peerURLs types.URLs
+}
+
+func (a *membersAPIActionUpdate) HTTPRequest(ep url.URL) *http.Request {
+ u := v2MembersURL(ep)
+ m := memberCreateOrUpdateRequest{PeerURLs: a.peerURLs}
+ u.Path = path.Join(u.Path, a.memberID)
+ b, _ := json.Marshal(&m)
+ req, _ := http.NewRequest("PUT", u.String(), bytes.NewReader(b))
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+func assertStatusCode(got int, want ...int) (err error) {
+ for _, w := range want {
+ if w == got {
+ return nil
+ }
+ }
+ return fmt.Errorf("unexpected status code %d", got)
+}
+
+// v2MembersURL add the necessary path to the provided endpoint
+// to route requests to the default v2 members API.
+func v2MembersURL(ep url.URL) *url.URL {
+ ep.Path = path.Join(ep.Path, defaultV2MembersPrefix)
+ return &ep
+}
+
+type membersError struct {
+ Message string `json:"message"`
+ Code int `json:"-"`
+}
+
+func (e membersError) Error() string {
+ return e.Message
+}
diff --git a/vendor/github.com/coreos/etcd/client/members_test.go b/vendor/github.com/coreos/etcd/client/members_test.go
new file mode 100644
index 000000000..df7be1ccb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/members_test.go
@@ -0,0 +1,522 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "encoding/json"
+ "errors"
+ "net/http"
+ "net/url"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+func TestMembersAPIActionList(t *testing.T) {
+ ep := url.URL{Scheme: "http", Host: "example.com"}
+ act := &membersAPIActionList{}
+
+ wantURL := &url.URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/v2/members",
+ }
+
+ got := *act.HTTPRequest(ep)
+ err := assertRequest(got, "GET", wantURL, http.Header{}, nil)
+ if err != nil {
+ t.Error(err.Error())
+ }
+}
+
+func TestMembersAPIActionAdd(t *testing.T) {
+ ep := url.URL{Scheme: "http", Host: "example.com"}
+ act := &membersAPIActionAdd{
+ peerURLs: types.URLs([]url.URL{
+ {Scheme: "https", Host: "127.0.0.1:8081"},
+ {Scheme: "http", Host: "127.0.0.1:8080"},
+ }),
+ }
+
+ wantURL := &url.URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/v2/members",
+ }
+ wantHeader := http.Header{
+ "Content-Type": []string{"application/json"},
+ }
+ wantBody := []byte(`{"peerURLs":["https://127.0.0.1:8081","http://127.0.0.1:8080"]}`)
+
+ got := *act.HTTPRequest(ep)
+ err := assertRequest(got, "POST", wantURL, wantHeader, wantBody)
+ if err != nil {
+ t.Error(err.Error())
+ }
+}
+
+func TestMembersAPIActionUpdate(t *testing.T) {
+ ep := url.URL{Scheme: "http", Host: "example.com"}
+ act := &membersAPIActionUpdate{
+ memberID: "0xabcd",
+ peerURLs: types.URLs([]url.URL{
+ {Scheme: "https", Host: "127.0.0.1:8081"},
+ {Scheme: "http", Host: "127.0.0.1:8080"},
+ }),
+ }
+
+ wantURL := &url.URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/v2/members/0xabcd",
+ }
+ wantHeader := http.Header{
+ "Content-Type": []string{"application/json"},
+ }
+ wantBody := []byte(`{"peerURLs":["https://127.0.0.1:8081","http://127.0.0.1:8080"]}`)
+
+ got := *act.HTTPRequest(ep)
+ err := assertRequest(got, "PUT", wantURL, wantHeader, wantBody)
+ if err != nil {
+ t.Error(err.Error())
+ }
+}
+
+func TestMembersAPIActionRemove(t *testing.T) {
+ ep := url.URL{Scheme: "http", Host: "example.com"}
+ act := &membersAPIActionRemove{memberID: "XXX"}
+
+ wantURL := &url.URL{
+ Scheme: "http",
+ Host: "example.com",
+ Path: "/v2/members/XXX",
+ }
+
+ got := *act.HTTPRequest(ep)
+ err := assertRequest(got, "DELETE", wantURL, http.Header{}, nil)
+ if err != nil {
+ t.Error(err.Error())
+ }
+}
+
+func TestAssertStatusCode(t *testing.T) {
+ if err := assertStatusCode(404, 400); err == nil {
+ t.Errorf("assertStatusCode failed to detect conflict in 400 vs 404")
+ }
+
+ if err := assertStatusCode(404, 400, 404); err != nil {
+ t.Errorf("assertStatusCode found conflict in (404,400) vs 400: %v", err)
+ }
+}
+
+func TestV2MembersURL(t *testing.T) {
+ got := v2MembersURL(url.URL{
+ Scheme: "http",
+ Host: "foo.example.com:4002",
+ Path: "/pants",
+ })
+ want := &url.URL{
+ Scheme: "http",
+ Host: "foo.example.com:4002",
+ Path: "/pants/v2/members",
+ }
+
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("v2MembersURL got %#v, want %#v", got, want)
+ }
+}
+
+func TestMemberUnmarshal(t *testing.T) {
+ tests := []struct {
+ body []byte
+ wantMember Member
+ wantError bool
+ }{
+ // no URLs, just check ID & Name
+ {
+ body: []byte(`{"id": "c", "name": "dungarees"}`),
+ wantMember: Member{ID: "c", Name: "dungarees", PeerURLs: nil, ClientURLs: nil},
+ },
+
+ // both client and peer URLs
+ {
+ body: []byte(`{"peerURLs": ["http://127.0.0.1:2379"], "clientURLs": ["http://127.0.0.1:2379"]}`),
+ wantMember: Member{
+ PeerURLs: []string{
+ "http://127.0.0.1:2379",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:2379",
+ },
+ },
+ },
+
+ // multiple peer URLs
+ {
+ body: []byte(`{"peerURLs": ["http://127.0.0.1:2379", "https://example.com"]}`),
+ wantMember: Member{
+ PeerURLs: []string{
+ "http://127.0.0.1:2379",
+ "https://example.com",
+ },
+ ClientURLs: nil,
+ },
+ },
+
+ // multiple client URLs
+ {
+ body: []byte(`{"clientURLs": ["http://127.0.0.1:2379", "https://example.com"]}`),
+ wantMember: Member{
+ PeerURLs: nil,
+ ClientURLs: []string{
+ "http://127.0.0.1:2379",
+ "https://example.com",
+ },
+ },
+ },
+
+ // invalid JSON
+ {
+ body: []byte(`{"peerU`),
+ wantError: true,
+ },
+ }
+
+ for i, tt := range tests {
+ got := Member{}
+ err := json.Unmarshal(tt.body, &got)
+ if tt.wantError != (err != nil) {
+ t.Errorf("#%d: want error %t, got %v", i, tt.wantError, err)
+ continue
+ }
+
+ if !reflect.DeepEqual(tt.wantMember, got) {
+ t.Errorf("#%d: incorrect output: want=%#v, got=%#v", i, tt.wantMember, got)
+ }
+ }
+}
+
+func TestMemberCollectionUnmarshalFail(t *testing.T) {
+ mc := &memberCollection{}
+ if err := mc.UnmarshalJSON([]byte(`{`)); err == nil {
+ t.Errorf("got nil error")
+ }
+}
+
+func TestMemberCollectionUnmarshal(t *testing.T) {
+ tests := []struct {
+ body []byte
+ want memberCollection
+ }{
+ {
+ body: []byte(`{}`),
+ want: memberCollection([]Member{}),
+ },
+ {
+ body: []byte(`{"members":[]}`),
+ want: memberCollection([]Member{}),
+ },
+ {
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ want: memberCollection(
+ []Member{
+ {
+ ID: "2745e2525fce8fe",
+ Name: "node3",
+ PeerURLs: []string{
+ "http://127.0.0.1:7003",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:4003",
+ },
+ },
+ {
+ ID: "42134f434382925",
+ Name: "node1",
+ PeerURLs: []string{
+ "http://127.0.0.1:2380",
+ "http://127.0.0.1:7001",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:2379",
+ "http://127.0.0.1:4001",
+ },
+ },
+ {
+ ID: "94088180e21eb87b",
+ Name: "node2",
+ PeerURLs: []string{
+ "http://127.0.0.1:7002",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:4002",
+ },
+ },
+ },
+ ),
+ },
+ }
+
+ for i, tt := range tests {
+ var got memberCollection
+ err := json.Unmarshal(tt.body, &got)
+ if err != nil {
+ t.Errorf("#%d: unexpected error: %v", i, err)
+ continue
+ }
+
+ if !reflect.DeepEqual(tt.want, got) {
+ t.Errorf("#%d: incorrect output: want=%#v, got=%#v", i, tt.want, got)
+ }
+ }
+}
+
+func TestMemberCreateRequestMarshal(t *testing.T) {
+ req := memberCreateOrUpdateRequest{
+ PeerURLs: types.URLs([]url.URL{
+ {Scheme: "http", Host: "127.0.0.1:8081"},
+ {Scheme: "https", Host: "127.0.0.1:8080"},
+ }),
+ }
+ want := []byte(`{"peerURLs":["http://127.0.0.1:8081","https://127.0.0.1:8080"]}`)
+
+ got, err := json.Marshal(&req)
+ if err != nil {
+ t.Fatalf("Marshal returned unexpected err=%v", err)
+ }
+
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("Failed to marshal memberCreateRequest: want=%s, got=%s", want, got)
+ }
+}
+
+func TestHTTPMembersAPIAddSuccess(t *testing.T) {
+ wantAction := &membersAPIActionAdd{
+ peerURLs: types.URLs([]url.URL{
+ {Scheme: "http", Host: "127.0.0.1:7002"},
+ }),
+ }
+
+ mAPI := &httpMembersAPI{
+ client: &actionAssertingHTTPClient{
+ t: t,
+ act: wantAction,
+ resp: http.Response{
+ StatusCode: http.StatusCreated,
+ },
+ body: []byte(`{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"]}`),
+ },
+ }
+
+ wantResponseMember := &Member{
+ ID: "94088180e21eb87b",
+ PeerURLs: []string{"http://127.0.0.1:7002"},
+ }
+
+ m, err := mAPI.Add(context.Background(), "http://127.0.0.1:7002")
+ if err != nil {
+ t.Errorf("got non-nil err: %#v", err)
+ }
+ if !reflect.DeepEqual(wantResponseMember, m) {
+ t.Errorf("incorrect Member: want=%#v got=%#v", wantResponseMember, m)
+ }
+}
+
+func TestHTTPMembersAPIAddError(t *testing.T) {
+ okPeer := "http://example.com:2379"
+ tests := []struct {
+ peerURL string
+ client httpClient
+
+ // if wantErr == nil, assert that the returned error is non-nil
+ // if wantErr != nil, assert that the returned error matches
+ wantErr error
+ }{
+ // malformed peer URL
+ {
+ peerURL: ":",
+ },
+
+ // generic httpClient failure
+ {
+ peerURL: okPeer,
+ client: &staticHTTPClient{err: errors.New("fail!")},
+ },
+
+ // unrecognized HTTP status code
+ {
+ peerURL: okPeer,
+ client: &staticHTTPClient{
+ resp: http.Response{StatusCode: http.StatusTeapot},
+ },
+ },
+
+ // unmarshal body into membersError on StatusConflict
+ {
+ peerURL: okPeer,
+ client: &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusConflict,
+ },
+ body: []byte(`{"message":"fail!"}`),
+ },
+ wantErr: membersError{Message: "fail!"},
+ },
+
+ // fail to unmarshal body on StatusConflict
+ {
+ peerURL: okPeer,
+ client: &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusConflict,
+ },
+ body: []byte(`{"`),
+ },
+ },
+
+ // fail to unmarshal body on StatusCreated
+ {
+ peerURL: okPeer,
+ client: &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusCreated,
+ },
+ body: []byte(`{"id":"XX`),
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ mAPI := &httpMembersAPI{client: tt.client}
+ m, err := mAPI.Add(context.Background(), tt.peerURL)
+ if err == nil {
+ t.Errorf("#%d: got nil err", i)
+ }
+ if tt.wantErr != nil && !reflect.DeepEqual(tt.wantErr, err) {
+ t.Errorf("#%d: incorrect error: want=%#v got=%#v", i, tt.wantErr, err)
+ }
+ if m != nil {
+ t.Errorf("#%d: got non-nil Member", i)
+ }
+ }
+}
+
+func TestHTTPMembersAPIRemoveSuccess(t *testing.T) {
+ wantAction := &membersAPIActionRemove{
+ memberID: "94088180e21eb87b",
+ }
+
+ mAPI := &httpMembersAPI{
+ client: &actionAssertingHTTPClient{
+ t: t,
+ act: wantAction,
+ resp: http.Response{
+ StatusCode: http.StatusNoContent,
+ },
+ },
+ }
+
+ if err := mAPI.Remove(context.Background(), "94088180e21eb87b"); err != nil {
+ t.Errorf("got non-nil err: %#v", err)
+ }
+}
+
+func TestHTTPMembersAPIRemoveFail(t *testing.T) {
+ tests := []httpClient{
+ // generic error
+ &staticHTTPClient{
+ err: errors.New("fail!"),
+ },
+
+ // unexpected HTTP status code
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusInternalServerError,
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ mAPI := &httpMembersAPI{client: tt}
+ if err := mAPI.Remove(context.Background(), "94088180e21eb87b"); err == nil {
+ t.Errorf("#%d: got nil err", i)
+ }
+ }
+}
+
+func TestHTTPMembersAPIListSuccess(t *testing.T) {
+ wantAction := &membersAPIActionList{}
+ mAPI := &httpMembersAPI{
+ client: &actionAssertingHTTPClient{
+ t: t,
+ act: wantAction,
+ resp: http.Response{
+ StatusCode: http.StatusOK,
+ },
+ body: []byte(`{"members":[{"id":"94088180e21eb87b","name":"node2","peerURLs":["http://127.0.0.1:7002"],"clientURLs":["http://127.0.0.1:4002"]}]}`),
+ },
+ }
+
+ wantResponseMembers := []Member{
+ {
+ ID: "94088180e21eb87b",
+ Name: "node2",
+ PeerURLs: []string{"http://127.0.0.1:7002"},
+ ClientURLs: []string{"http://127.0.0.1:4002"},
+ },
+ }
+
+ m, err := mAPI.List(context.Background())
+ if err != nil {
+ t.Errorf("got non-nil err: %#v", err)
+ }
+ if !reflect.DeepEqual(wantResponseMembers, m) {
+ t.Errorf("incorrect Members: want=%#v got=%#v", wantResponseMembers, m)
+ }
+}
+
+func TestHTTPMembersAPIListError(t *testing.T) {
+ tests := []httpClient{
+ // generic httpClient failure
+ &staticHTTPClient{err: errors.New("fail!")},
+
+ // unrecognized HTTP status code
+ &staticHTTPClient{
+ resp: http.Response{StatusCode: http.StatusTeapot},
+ },
+
+ // fail to unmarshal body on StatusOK
+ &staticHTTPClient{
+ resp: http.Response{
+ StatusCode: http.StatusOK,
+ },
+ body: []byte(`[{"id":"XX`),
+ },
+ }
+
+ for i, tt := range tests {
+ mAPI := &httpMembersAPI{client: tt}
+ ms, err := mAPI.List(context.Background())
+ if err == nil {
+ t.Errorf("#%d: got nil err", i)
+ }
+ if ms != nil {
+ t.Errorf("#%d: got non-nil Member slice", i)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/client/srv.go b/vendor/github.com/coreos/etcd/client/srv.go
new file mode 100644
index 000000000..f74c1220b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/srv.go
@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "fmt"
+ "net"
+ "net/url"
+)
+
+var (
+ // indirection for testing
+ lookupSRV = net.LookupSRV
+)
+
+type srvDiscover struct{}
+
+// NewSRVDiscover constructs a new Dicoverer that uses the stdlib to lookup SRV records.
+func NewSRVDiscover() Discoverer {
+ return &srvDiscover{}
+}
+
+// Discover looks up the etcd servers for the domain.
+func (d *srvDiscover) Discover(domain string) ([]string, error) {
+ var urls []*url.URL
+
+ updateURLs := func(service, scheme string) error {
+ _, addrs, err := lookupSRV(service, "tcp", domain)
+ if err != nil {
+ return err
+ }
+ for _, srv := range addrs {
+ urls = append(urls, &url.URL{
+ Scheme: scheme,
+ Host: net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port)),
+ })
+ }
+ return nil
+ }
+
+ errHTTPS := updateURLs("etcd-server-ssl", "https")
+ errHTTP := updateURLs("etcd-server", "http")
+
+ if errHTTPS != nil && errHTTP != nil {
+ return nil, fmt.Errorf("dns lookup errors: %s and %s", errHTTPS, errHTTP)
+ }
+
+ endpoints := make([]string, len(urls))
+ for i := range urls {
+ endpoints[i] = urls[i].String()
+ }
+ return endpoints, nil
+}
diff --git a/vendor/github.com/coreos/etcd/client/srv_test.go b/vendor/github.com/coreos/etcd/client/srv_test.go
new file mode 100644
index 000000000..7121f454f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/client/srv_test.go
@@ -0,0 +1,102 @@
+// Copyright 2015 CoreOS, 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 client
+
+import (
+ "errors"
+ "net"
+ "reflect"
+ "testing"
+)
+
+func TestSRVDiscover(t *testing.T) {
+ defer func() { lookupSRV = net.LookupSRV }()
+
+ tests := []struct {
+ withSSL []*net.SRV
+ withoutSSL []*net.SRV
+ expected []string
+ }{
+ {
+ []*net.SRV{},
+ []*net.SRV{},
+ []string{},
+ },
+ {
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2480},
+ {Target: "10.0.0.2", Port: 2480},
+ {Target: "10.0.0.3", Port: 2480},
+ },
+ []*net.SRV{},
+ []string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480"},
+ },
+ {
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2480},
+ {Target: "10.0.0.2", Port: 2480},
+ {Target: "10.0.0.3", Port: 2480},
+ },
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 7001},
+ },
+ []string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480", "http://10.0.0.1:7001"},
+ },
+ {
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2480},
+ {Target: "10.0.0.2", Port: 2480},
+ {Target: "10.0.0.3", Port: 2480},
+ },
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 7001},
+ },
+ []string{"https://10.0.0.1:2480", "https://10.0.0.2:2480", "https://10.0.0.3:2480", "http://10.0.0.1:7001"},
+ },
+ {
+ []*net.SRV{
+ {Target: "a.example.com", Port: 2480},
+ {Target: "b.example.com", Port: 2480},
+ {Target: "c.example.com", Port: 2480},
+ },
+ []*net.SRV{},
+ []string{"https://a.example.com:2480", "https://b.example.com:2480", "https://c.example.com:2480"},
+ },
+ }
+
+ for i, tt := range tests {
+ lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
+ if service == "etcd-server-ssl" {
+ return "", tt.withSSL, nil
+ }
+ if service == "etcd-server" {
+ return "", tt.withoutSSL, nil
+ }
+ return "", nil, errors.New("Unknown service in mock")
+ }
+
+ d := NewSRVDiscover()
+
+ endpoints, err := d.Discover("example.com")
+ if err != nil {
+ t.Fatalf("%d: err: %#v", i, err)
+ }
+
+ if !reflect.DeepEqual(endpoints, tt.expected) {
+ t.Errorf("#%d: endpoints = %v, want %v", i, endpoints, tt.expected)
+ }
+
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/discovery/discovery.go b/vendor/github.com/coreos/etcd/discovery/discovery.go
new file mode 100644
index 000000000..0ad3d0c18
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/discovery/discovery.go
@@ -0,0 +1,355 @@
+// Copyright 2015 CoreOS, 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 discovery
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "net"
+ "net/http"
+ "net/url"
+ "path"
+ "sort"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/client"
+ "github.com/coreos/etcd/pkg/types"
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "discovery")
+
+ ErrInvalidURL = errors.New("discovery: invalid URL")
+ ErrBadSizeKey = errors.New("discovery: size key is bad")
+ ErrSizeNotFound = errors.New("discovery: size key not found")
+ ErrTokenNotFound = errors.New("discovery: token not found")
+ ErrDuplicateID = errors.New("discovery: found duplicate id")
+ ErrDuplicateName = errors.New("discovery: found duplicate name")
+ ErrFullCluster = errors.New("discovery: cluster is full")
+ ErrTooManyRetries = errors.New("discovery: too many retries")
+ ErrBadDiscoveryEndpoint = errors.New("discovery: bad discovery endpoint")
+)
+
+var (
+ // Number of retries discovery will attempt before giving up and erroring out.
+ nRetries = uint(math.MaxUint32)
+)
+
+// JoinCluster will connect to the discovery service at the given url, and
+// register the server represented by the given id and config to the cluster
+func JoinCluster(durl, dproxyurl string, id types.ID, config string) (string, error) {
+ d, err := newDiscovery(durl, dproxyurl, id)
+ if err != nil {
+ return "", err
+ }
+ return d.joinCluster(config)
+}
+
+// GetCluster will connect to the discovery service at the given url and
+// retrieve a string describing the cluster
+func GetCluster(durl, dproxyurl string) (string, error) {
+ d, err := newDiscovery(durl, dproxyurl, 0)
+ if err != nil {
+ return "", err
+ }
+ return d.getCluster()
+}
+
+type discovery struct {
+ cluster string
+ id types.ID
+ c client.KeysAPI
+ retries uint
+ url *url.URL
+
+ clock clockwork.Clock
+}
+
+// newProxyFunc builds a proxy function from the given string, which should
+// represent a URL that can be used as a proxy. It performs basic
+// sanitization of the URL and returns any error encountered.
+func newProxyFunc(proxy string) (func(*http.Request) (*url.URL, error), error) {
+ if proxy == "" {
+ return nil, nil
+ }
+ // Do a small amount of URL sanitization to help the user
+ // Derived from net/http.ProxyFromEnvironment
+ proxyURL, err := url.Parse(proxy)
+ if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
+ // proxy was bogus. Try prepending "http://" to it and
+ // see if that parses correctly. If not, we ignore the
+ // error and complain about the original one
+ var err2 error
+ proxyURL, err2 = url.Parse("http://" + proxy)
+ if err2 == nil {
+ err = nil
+ }
+ }
+ if err != nil {
+ return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
+ }
+
+ plog.Infof("using proxy %q", proxyURL.String())
+ return http.ProxyURL(proxyURL), nil
+}
+
+func newDiscovery(durl, dproxyurl string, id types.ID) (*discovery, error) {
+ u, err := url.Parse(durl)
+ if err != nil {
+ return nil, err
+ }
+ token := u.Path
+ u.Path = ""
+ pf, err := newProxyFunc(dproxyurl)
+ if err != nil {
+ return nil, err
+ }
+ cfg := client.Config{
+ Transport: &http.Transport{
+ Proxy: pf,
+ Dial: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ TLSHandshakeTimeout: 10 * time.Second,
+ // TODO: add ResponseHeaderTimeout back when watch on discovery service writes header early
+ },
+ Endpoints: []string{u.String()},
+ }
+ c, err := client.New(cfg)
+ if err != nil {
+ return nil, err
+ }
+ dc := client.NewKeysAPIWithPrefix(c, "")
+ return &discovery{
+ cluster: token,
+ c: dc,
+ id: id,
+ url: u,
+ clock: clockwork.NewRealClock(),
+ }, nil
+}
+
+func (d *discovery) joinCluster(config string) (string, error) {
+ // fast path: if the cluster is full, return the error
+ // do not need to register to the cluster in this case.
+ if _, _, _, err := d.checkCluster(); err != nil {
+ return "", err
+ }
+
+ if err := d.createSelf(config); err != nil {
+ // Fails, even on a timeout, if createSelf times out.
+ // TODO(barakmich): Retrying the same node might want to succeed here
+ // (ie, createSelf should be idempotent for discovery).
+ return "", err
+ }
+
+ nodes, size, index, err := d.checkCluster()
+ if err != nil {
+ return "", err
+ }
+
+ all, err := d.waitNodes(nodes, size, index)
+ if err != nil {
+ return "", err
+ }
+
+ return nodesToCluster(all, size)
+}
+
+func (d *discovery) getCluster() (string, error) {
+ nodes, size, index, err := d.checkCluster()
+ if err != nil {
+ if err == ErrFullCluster {
+ return nodesToCluster(nodes, size)
+ }
+ return "", err
+ }
+
+ all, err := d.waitNodes(nodes, size, index)
+ if err != nil {
+ return "", err
+ }
+ return nodesToCluster(all, size)
+}
+
+func (d *discovery) createSelf(contents string) error {
+ ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+ resp, err := d.c.Create(ctx, d.selfKey(), contents)
+ cancel()
+ if err != nil {
+ if eerr, ok := err.(client.Error); ok && eerr.Code == client.ErrorCodeNodeExist {
+ return ErrDuplicateID
+ }
+ return err
+ }
+
+ // ensure self appears on the server we connected to
+ w := d.c.Watcher(d.selfKey(), &client.WatcherOptions{AfterIndex: resp.Node.CreatedIndex - 1})
+ _, err = w.Next(context.Background())
+ return err
+}
+
+func (d *discovery) checkCluster() ([]*client.Node, int, uint64, error) {
+ configKey := path.Join("/", d.cluster, "_config")
+ ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+ // find cluster size
+ resp, err := d.c.Get(ctx, path.Join(configKey, "size"), nil)
+ cancel()
+ if err != nil {
+ if eerr, ok := err.(*client.Error); ok && eerr.Code == client.ErrorCodeKeyNotFound {
+ return nil, 0, 0, ErrSizeNotFound
+ }
+ if err == client.ErrInvalidJSON {
+ return nil, 0, 0, ErrBadDiscoveryEndpoint
+ }
+ if ce, ok := err.(*client.ClusterError); ok {
+ plog.Error(ce.Detail())
+ return d.checkClusterRetry()
+ }
+ return nil, 0, 0, err
+ }
+ size, err := strconv.Atoi(resp.Node.Value)
+ if err != nil {
+ return nil, 0, 0, ErrBadSizeKey
+ }
+
+ ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
+ resp, err = d.c.Get(ctx, d.cluster, nil)
+ cancel()
+ if err != nil {
+ if ce, ok := err.(*client.ClusterError); ok {
+ plog.Error(ce.Detail())
+ return d.checkClusterRetry()
+ }
+ return nil, 0, 0, err
+ }
+ nodes := make([]*client.Node, 0)
+ // append non-config keys to nodes
+ for _, n := range resp.Node.Nodes {
+ if !(path.Base(n.Key) == path.Base(configKey)) {
+ nodes = append(nodes, n)
+ }
+ }
+
+ snodes := sortableNodes{nodes}
+ sort.Sort(snodes)
+
+ // find self position
+ for i := range nodes {
+ if path.Base(nodes[i].Key) == path.Base(d.selfKey()) {
+ break
+ }
+ if i >= size-1 {
+ return nodes[:size], size, resp.Index, ErrFullCluster
+ }
+ }
+ return nodes, size, resp.Index, nil
+}
+
+func (d *discovery) logAndBackoffForRetry(step string) {
+ d.retries++
+ retryTime := time.Second * (0x1 << d.retries)
+ plog.Infof("%s: error connecting to %s, retrying in %s", step, d.url, retryTime)
+ d.clock.Sleep(retryTime)
+}
+
+func (d *discovery) checkClusterRetry() ([]*client.Node, int, uint64, error) {
+ if d.retries < nRetries {
+ d.logAndBackoffForRetry("cluster status check")
+ return d.checkCluster()
+ }
+ return nil, 0, 0, ErrTooManyRetries
+}
+
+func (d *discovery) waitNodesRetry() ([]*client.Node, error) {
+ if d.retries < nRetries {
+ d.logAndBackoffForRetry("waiting for other nodes")
+ nodes, n, index, err := d.checkCluster()
+ if err != nil {
+ return nil, err
+ }
+ return d.waitNodes(nodes, n, index)
+ }
+ return nil, ErrTooManyRetries
+}
+
+func (d *discovery) waitNodes(nodes []*client.Node, size int, index uint64) ([]*client.Node, error) {
+ if len(nodes) > size {
+ nodes = nodes[:size]
+ }
+ // watch from the next index
+ w := d.c.Watcher(d.cluster, &client.WatcherOptions{AfterIndex: index, Recursive: true})
+ all := make([]*client.Node, len(nodes))
+ copy(all, nodes)
+ for _, n := range all {
+ if path.Base(n.Key) == path.Base(d.selfKey()) {
+ plog.Noticef("found self %s in the cluster", path.Base(d.selfKey()))
+ } else {
+ plog.Noticef("found peer %s in the cluster", path.Base(n.Key))
+ }
+ }
+
+ // wait for others
+ for len(all) < size {
+ plog.Noticef("found %d peer(s), waiting for %d more", len(all), size-len(all))
+ resp, err := w.Next(context.Background())
+ if err != nil {
+ if ce, ok := err.(*client.ClusterError); ok {
+ plog.Error(ce.Detail())
+ return d.waitNodesRetry()
+ }
+ return nil, err
+ }
+ plog.Noticef("found peer %s in the cluster", path.Base(resp.Node.Key))
+ all = append(all, resp.Node)
+ }
+ plog.Noticef("found %d needed peer(s)", len(all))
+ return all, nil
+}
+
+func (d *discovery) selfKey() string {
+ return path.Join("/", d.cluster, d.id.String())
+}
+
+func nodesToCluster(ns []*client.Node, size int) (string, error) {
+ s := make([]string, len(ns))
+ for i, n := range ns {
+ s[i] = n.Value
+ }
+ us := strings.Join(s, ",")
+ m, err := types.NewURLsMap(us)
+ if err != nil {
+ return us, ErrInvalidURL
+ }
+ if m.Len() != size {
+ return us, ErrDuplicateName
+ }
+ return us, nil
+}
+
+type sortableNodes struct{ Nodes []*client.Node }
+
+func (ns sortableNodes) Len() int { return len(ns.Nodes) }
+func (ns sortableNodes) Less(i, j int) bool {
+ return ns.Nodes[i].CreatedIndex < ns.Nodes[j].CreatedIndex
+}
+func (ns sortableNodes) Swap(i, j int) { ns.Nodes[i], ns.Nodes[j] = ns.Nodes[j], ns.Nodes[i] }
diff --git a/vendor/github.com/coreos/etcd/discovery/discovery_test.go b/vendor/github.com/coreos/etcd/discovery/discovery_test.go
new file mode 100644
index 000000000..22402266f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/discovery/discovery_test.go
@@ -0,0 +1,558 @@
+// Copyright 2015 CoreOS, 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 discovery
+
+import (
+ "errors"
+ "math"
+ "math/rand"
+ "net/http"
+ "reflect"
+ "sort"
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+
+ "github.com/coreos/etcd/client"
+)
+
+const (
+ maxRetryInTest = 3
+)
+
+func TestNewProxyFuncUnset(t *testing.T) {
+ pf, err := newProxyFunc("")
+ if pf != nil {
+ t.Fatal("unexpected non-nil proxyFunc")
+ }
+ if err != nil {
+ t.Fatalf("unexpected non-nil err: %v", err)
+ }
+}
+
+func TestNewProxyFuncBad(t *testing.T) {
+ tests := []string{
+ "%%",
+ "http://foo.com/%1",
+ }
+ for i, in := range tests {
+ pf, err := newProxyFunc(in)
+ if pf != nil {
+ t.Errorf("#%d: unexpected non-nil proxyFunc", i)
+ }
+ if err == nil {
+ t.Errorf("#%d: unexpected nil err", i)
+ }
+ }
+}
+
+func TestNewProxyFunc(t *testing.T) {
+ tests := map[string]string{
+ "bar.com": "http://bar.com",
+ "http://disco.foo.bar": "http://disco.foo.bar",
+ }
+ for in, w := range tests {
+ pf, err := newProxyFunc(in)
+ if pf == nil {
+ t.Errorf("%s: unexpected nil proxyFunc", in)
+ continue
+ }
+ if err != nil {
+ t.Errorf("%s: unexpected non-nil err: %v", in, err)
+ continue
+ }
+ g, err := pf(&http.Request{})
+ if err != nil {
+ t.Errorf("%s: unexpected non-nil err: %v", in, err)
+ }
+ if g.String() != w {
+ t.Errorf("%s: proxyURL=%q, want %q", in, g, w)
+ }
+
+ }
+}
+
+func TestCheckCluster(t *testing.T) {
+ cluster := "/prefix/1000"
+ self := "/1000/1"
+
+ tests := []struct {
+ nodes []*client.Node
+ index uint64
+ werr error
+ wsize int
+ }{
+ {
+ // self is in the size range
+ []*client.Node{
+ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1},
+ {Key: "/1000/_config/"},
+ {Key: self, CreatedIndex: 2},
+ {Key: "/1000/2", CreatedIndex: 3},
+ {Key: "/1000/3", CreatedIndex: 4},
+ {Key: "/1000/4", CreatedIndex: 5},
+ },
+ 5,
+ nil,
+ 3,
+ },
+ {
+ // self is in the size range
+ []*client.Node{
+ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1},
+ {Key: "/1000/_config/"},
+ {Key: "/1000/2", CreatedIndex: 2},
+ {Key: "/1000/3", CreatedIndex: 3},
+ {Key: self, CreatedIndex: 4},
+ {Key: "/1000/4", CreatedIndex: 5},
+ },
+ 5,
+ nil,
+ 3,
+ },
+ {
+ // self is out of the size range
+ []*client.Node{
+ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1},
+ {Key: "/1000/_config/"},
+ {Key: "/1000/2", CreatedIndex: 2},
+ {Key: "/1000/3", CreatedIndex: 3},
+ {Key: "/1000/4", CreatedIndex: 4},
+ {Key: self, CreatedIndex: 5},
+ },
+ 5,
+ ErrFullCluster,
+ 3,
+ },
+ {
+ // self is not in the cluster
+ []*client.Node{
+ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1},
+ {Key: "/1000/_config/"},
+ {Key: "/1000/2", CreatedIndex: 2},
+ {Key: "/1000/3", CreatedIndex: 3},
+ },
+ 3,
+ nil,
+ 3,
+ },
+ {
+ []*client.Node{
+ {Key: "/1000/_config/size", Value: "3", CreatedIndex: 1},
+ {Key: "/1000/_config/"},
+ {Key: "/1000/2", CreatedIndex: 2},
+ {Key: "/1000/3", CreatedIndex: 3},
+ {Key: "/1000/4", CreatedIndex: 4},
+ },
+ 3,
+ ErrFullCluster,
+ 3,
+ },
+ {
+ // bad size key
+ []*client.Node{
+ {Key: "/1000/_config/size", Value: "bad", CreatedIndex: 1},
+ },
+ 0,
+ ErrBadSizeKey,
+ 0,
+ },
+ {
+ // no size key
+ []*client.Node{},
+ 0,
+ ErrSizeNotFound,
+ 0,
+ },
+ }
+
+ for i, tt := range tests {
+ rs := make([]*client.Response, 0)
+ if len(tt.nodes) > 0 {
+ rs = append(rs, &client.Response{Node: tt.nodes[0], Index: tt.index})
+ rs = append(rs, &client.Response{
+ Node: &client.Node{
+ Key: cluster,
+ Nodes: tt.nodes[1:],
+ },
+ Index: tt.index,
+ })
+ }
+ c := &clientWithResp{rs: rs}
+ dBase := discovery{cluster: cluster, id: 1, c: c}
+
+ cRetry := &clientWithRetry{failTimes: 3}
+ cRetry.rs = rs
+ fc := clockwork.NewFakeClock()
+ dRetry := discovery{cluster: cluster, id: 1, c: cRetry, clock: fc}
+
+ for _, d := range []discovery{dBase, dRetry} {
+ go func() {
+ for i := uint(1); i <= maxRetryInTest; i++ {
+ fc.BlockUntil(1)
+ fc.Advance(time.Second * (0x1 << i))
+ }
+ }()
+ ns, size, index, err := d.checkCluster()
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if reflect.DeepEqual(ns, tt.nodes) {
+ t.Errorf("#%d: nodes = %v, want %v", i, ns, tt.nodes)
+ }
+ if size != tt.wsize {
+ t.Errorf("#%d: size = %v, want %d", i, size, tt.wsize)
+ }
+ if index != tt.index {
+ t.Errorf("#%d: index = %v, want %d", i, index, tt.index)
+ }
+ }
+ }
+}
+
+func TestWaitNodes(t *testing.T) {
+ all := []*client.Node{
+ 0: {Key: "/1000/1", CreatedIndex: 2},
+ 1: {Key: "/1000/2", CreatedIndex: 3},
+ 2: {Key: "/1000/3", CreatedIndex: 4},
+ }
+
+ tests := []struct {
+ nodes []*client.Node
+ rs []*client.Response
+ }{
+ {
+ all,
+ []*client.Response{},
+ },
+ {
+ all[:1],
+ []*client.Response{
+ {Node: &client.Node{Key: "/1000/2", CreatedIndex: 3}},
+ {Node: &client.Node{Key: "/1000/3", CreatedIndex: 4}},
+ },
+ },
+ {
+ all[:2],
+ []*client.Response{
+ {Node: &client.Node{Key: "/1000/3", CreatedIndex: 4}},
+ },
+ },
+ {
+ append(all, &client.Node{Key: "/1000/4", CreatedIndex: 5}),
+ []*client.Response{
+ {Node: &client.Node{Key: "/1000/3", CreatedIndex: 4}},
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ // Basic case
+ c := &clientWithResp{rs: nil, w: &watcherWithResp{rs: tt.rs}}
+ dBase := &discovery{cluster: "1000", c: c}
+
+ // Retry case
+ retryScanResp := make([]*client.Response, 0)
+ if len(tt.nodes) > 0 {
+ retryScanResp = append(retryScanResp, &client.Response{
+ Node: &client.Node{
+ Key: "1000",
+ Value: strconv.Itoa(3),
+ },
+ })
+ retryScanResp = append(retryScanResp, &client.Response{
+ Node: &client.Node{
+ Nodes: tt.nodes,
+ },
+ })
+ }
+ cRetry := &clientWithResp{
+ rs: retryScanResp,
+ w: &watcherWithRetry{rs: tt.rs, failTimes: 2},
+ }
+ fc := clockwork.NewFakeClock()
+ dRetry := &discovery{
+ cluster: "1000",
+ c: cRetry,
+ clock: fc,
+ }
+
+ for _, d := range []*discovery{dBase, dRetry} {
+ go func() {
+ for i := uint(1); i <= maxRetryInTest; i++ {
+ fc.BlockUntil(1)
+ fc.Advance(time.Second * (0x1 << i))
+ }
+ }()
+ g, err := d.waitNodes(tt.nodes, 3, 0) // we do not care about index in this test
+ if err != nil {
+ t.Errorf("#%d: err = %v, want %v", i, err, nil)
+ }
+ if !reflect.DeepEqual(g, all) {
+ t.Errorf("#%d: all = %v, want %v", i, g, all)
+ }
+ }
+ }
+}
+
+func TestCreateSelf(t *testing.T) {
+ rs := []*client.Response{{Node: &client.Node{Key: "1000/1", CreatedIndex: 2}}}
+
+ w := &watcherWithResp{rs: rs}
+ errw := &watcherWithErr{err: errors.New("watch err")}
+
+ c := &clientWithResp{rs: rs, w: w}
+ errc := &clientWithErr{err: errors.New("create err"), w: w}
+ errdupc := &clientWithErr{err: client.Error{Code: client.ErrorCodeNodeExist}}
+ errwc := &clientWithResp{rs: rs, w: errw}
+
+ tests := []struct {
+ c client.KeysAPI
+ werr error
+ }{
+ // no error
+ {c, nil},
+ // client.create returns an error
+ {errc, errc.err},
+ // watcher.next retuens an error
+ {errwc, errw.err},
+ // parse key exist error to duplciate ID error
+ {errdupc, ErrDuplicateID},
+ }
+
+ for i, tt := range tests {
+ d := discovery{cluster: "1000", c: tt.c}
+ if err := d.createSelf(""); err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, nil)
+ }
+ }
+}
+
+func TestNodesToCluster(t *testing.T) {
+ tests := []struct {
+ nodes []*client.Node
+ size int
+ wcluster string
+ werr error
+ }{
+ {
+ []*client.Node{
+ 0: {Key: "/1000/1", Value: "1=http://1.1.1.1:2380", CreatedIndex: 1},
+ 1: {Key: "/1000/2", Value: "2=http://2.2.2.2:2380", CreatedIndex: 2},
+ 2: {Key: "/1000/3", Value: "3=http://3.3.3.3:2380", CreatedIndex: 3},
+ },
+ 3,
+ "1=http://1.1.1.1:2380,2=http://2.2.2.2:2380,3=http://3.3.3.3:2380",
+ nil,
+ },
+ {
+ []*client.Node{
+ 0: {Key: "/1000/1", Value: "1=http://1.1.1.1:2380", CreatedIndex: 1},
+ 1: {Key: "/1000/2", Value: "2=http://2.2.2.2:2380", CreatedIndex: 2},
+ 2: {Key: "/1000/3", Value: "2=http://3.3.3.3:2380", CreatedIndex: 3},
+ },
+ 3,
+ "1=http://1.1.1.1:2380,2=http://2.2.2.2:2380,2=http://3.3.3.3:2380",
+ ErrDuplicateName,
+ },
+ {
+ []*client.Node{
+ 0: {Key: "/1000/1", Value: "1=1.1.1.1:2380", CreatedIndex: 1},
+ 1: {Key: "/1000/2", Value: "2=http://2.2.2.2:2380", CreatedIndex: 2},
+ 2: {Key: "/1000/3", Value: "2=http://3.3.3.3:2380", CreatedIndex: 3},
+ },
+ 3,
+ "1=1.1.1.1:2380,2=http://2.2.2.2:2380,2=http://3.3.3.3:2380",
+ ErrInvalidURL,
+ },
+ }
+
+ for i, tt := range tests {
+ cluster, err := nodesToCluster(tt.nodes, tt.size)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if !reflect.DeepEqual(cluster, tt.wcluster) {
+ t.Errorf("#%d: cluster = %v, want %v", i, cluster, tt.wcluster)
+ }
+ }
+}
+
+func TestSortableNodes(t *testing.T) {
+ ns := []*client.Node{
+ 0: {CreatedIndex: 5},
+ 1: {CreatedIndex: 1},
+ 2: {CreatedIndex: 3},
+ 3: {CreatedIndex: 4},
+ }
+ // add some randomness
+ for i := 0; i < 10000; i++ {
+ ns = append(ns, &client.Node{CreatedIndex: uint64(rand.Int31())})
+ }
+ sns := sortableNodes{ns}
+ sort.Sort(sns)
+ cis := make([]int, 0)
+ for _, n := range sns.Nodes {
+ cis = append(cis, int(n.CreatedIndex))
+ }
+ if sort.IntsAreSorted(cis) != true {
+ t.Errorf("isSorted = %v, want %v", sort.IntsAreSorted(cis), true)
+ }
+ cis = make([]int, 0)
+ for _, n := range ns {
+ cis = append(cis, int(n.CreatedIndex))
+ }
+ if sort.IntsAreSorted(cis) != true {
+ t.Errorf("isSorted = %v, want %v", sort.IntsAreSorted(cis), true)
+ }
+}
+
+func TestRetryFailure(t *testing.T) {
+ nRetries = maxRetryInTest
+ defer func() { nRetries = math.MaxUint32 }()
+
+ cluster := "1000"
+ c := &clientWithRetry{failTimes: 4}
+ fc := clockwork.NewFakeClock()
+ d := discovery{
+ cluster: cluster,
+ id: 1,
+ c: c,
+ clock: fc,
+ }
+ go func() {
+ for i := uint(1); i <= maxRetryInTest; i++ {
+ fc.BlockUntil(1)
+ fc.Advance(time.Second * (0x1 << i))
+ }
+ }()
+ if _, _, _, err := d.checkCluster(); err != ErrTooManyRetries {
+ t.Errorf("err = %v, want %v", err, ErrTooManyRetries)
+ }
+}
+
+type clientWithResp struct {
+ rs []*client.Response
+ w client.Watcher
+ client.KeysAPI
+}
+
+func (c *clientWithResp) Create(ctx context.Context, key string, value string) (*client.Response, error) {
+ if len(c.rs) == 0 {
+ return &client.Response{}, nil
+ }
+ r := c.rs[0]
+ c.rs = c.rs[1:]
+ return r, nil
+}
+
+func (c *clientWithResp) Get(ctx context.Context, key string, opts *client.GetOptions) (*client.Response, error) {
+ if len(c.rs) == 0 {
+ return &client.Response{}, &client.Error{Code: client.ErrorCodeKeyNotFound}
+ }
+ r := c.rs[0]
+ c.rs = append(c.rs[1:], r)
+ return r, nil
+}
+
+func (c *clientWithResp) Watcher(key string, opts *client.WatcherOptions) client.Watcher {
+ return c.w
+}
+
+type clientWithErr struct {
+ err error
+ w client.Watcher
+ client.KeysAPI
+}
+
+func (c *clientWithErr) Create(ctx context.Context, key string, value string) (*client.Response, error) {
+ return &client.Response{}, c.err
+}
+
+func (c *clientWithErr) Get(ctx context.Context, key string, opts *client.GetOptions) (*client.Response, error) {
+ return &client.Response{}, c.err
+}
+
+func (c *clientWithErr) Watcher(key string, opts *client.WatcherOptions) client.Watcher {
+ return c.w
+}
+
+type watcherWithResp struct {
+ client.KeysAPI
+ rs []*client.Response
+}
+
+func (w *watcherWithResp) Next(context.Context) (*client.Response, error) {
+ if len(w.rs) == 0 {
+ return &client.Response{}, nil
+ }
+ r := w.rs[0]
+ w.rs = w.rs[1:]
+ return r, nil
+}
+
+type watcherWithErr struct {
+ err error
+}
+
+func (w *watcherWithErr) Next(context.Context) (*client.Response, error) {
+ return &client.Response{}, w.err
+}
+
+// clientWithRetry will timeout all requests up to failTimes
+type clientWithRetry struct {
+ clientWithResp
+ failCount int
+ failTimes int
+}
+
+func (c *clientWithRetry) Create(ctx context.Context, key string, value string) (*client.Response, error) {
+ if c.failCount < c.failTimes {
+ c.failCount++
+ return nil, &client.ClusterError{Errors: []error{context.DeadlineExceeded}}
+ }
+ return c.clientWithResp.Create(ctx, key, value)
+}
+
+func (c *clientWithRetry) Get(ctx context.Context, key string, opts *client.GetOptions) (*client.Response, error) {
+ if c.failCount < c.failTimes {
+ c.failCount++
+ return nil, &client.ClusterError{Errors: []error{context.DeadlineExceeded}}
+ }
+ return c.clientWithResp.Get(ctx, key, opts)
+}
+
+// watcherWithRetry will timeout all requests up to failTimes
+type watcherWithRetry struct {
+ rs []*client.Response
+ failCount int
+ failTimes int
+}
+
+func (w *watcherWithRetry) Next(context.Context) (*client.Response, error) {
+ if w.failCount < w.failTimes {
+ w.failCount++
+ return nil, &client.ClusterError{Errors: []error{context.DeadlineExceeded}}
+ }
+ if len(w.rs) == 0 {
+ return &client.Response{}, nil
+ }
+ r := w.rs[0]
+ w.rs = w.rs[1:]
+ return r, nil
+}
diff --git a/vendor/github.com/coreos/etcd/discovery/doc.go b/vendor/github.com/coreos/etcd/discovery/doc.go
new file mode 100644
index 000000000..18cfd78c1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/discovery/doc.go
@@ -0,0 +1,20 @@
+// Copyright 2015 CoreOS, 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 discovery provides an implementation of the cluster discovery that
+is used by etcd.
+
+*/
+package discovery
diff --git a/vendor/github.com/coreos/etcd/discovery/srv.go b/vendor/github.com/coreos/etcd/discovery/srv.go
new file mode 100644
index 000000000..f212c2feb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/discovery/srv.go
@@ -0,0 +1,97 @@
+// Copyright 2015 CoreOS, 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 discovery
+
+import (
+ "fmt"
+ "net"
+ "strings"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+var (
+ // indirection for testing
+ lookupSRV = net.LookupSRV
+ resolveTCPAddr = net.ResolveTCPAddr
+)
+
+// TODO(barakmich): Currently ignores priority and weight (as they don't make as much sense for a bootstrap)
+// Also doesn't do any lookups for the token (though it could)
+// Also sees each entry as a separate instance.
+func SRVGetCluster(name, dns string, defaultToken string, apurls types.URLs) (string, string, error) {
+ stringParts := make([]string, 0)
+ tempName := int(0)
+ tcpAPUrls := make([]string, 0)
+
+ // First, resolve the apurls
+ for _, url := range apurls {
+ tcpAddr, err := resolveTCPAddr("tcp", url.Host)
+ if err != nil {
+ plog.Errorf("couldn't resolve host %s during SRV discovery", url.Host)
+ return "", "", err
+ }
+ tcpAPUrls = append(tcpAPUrls, tcpAddr.String())
+ }
+
+ updateNodeMap := func(service, prefix string) error {
+ _, addrs, err := lookupSRV(service, "tcp", dns)
+ if err != nil {
+ return err
+ }
+ for _, srv := range addrs {
+ target := strings.TrimSuffix(srv.Target, ".")
+ host := net.JoinHostPort(target, fmt.Sprintf("%d", srv.Port))
+ tcpAddr, err := resolveTCPAddr("tcp", host)
+ if err != nil {
+ plog.Warningf("couldn't resolve host %s during SRV discovery", host)
+ continue
+ }
+ n := ""
+ for _, url := range tcpAPUrls {
+ if url == tcpAddr.String() {
+ n = name
+ }
+ }
+ if n == "" {
+ n = fmt.Sprintf("%d", tempName)
+ tempName += 1
+ }
+ stringParts = append(stringParts, fmt.Sprintf("%s=%s%s", n, prefix, host))
+ plog.Noticef("got bootstrap from DNS for %s at %s%s", service, prefix, host)
+ }
+ return nil
+ }
+
+ failCount := 0
+ err := updateNodeMap("etcd-server-ssl", "https://")
+ srvErr := make([]string, 2)
+ if err != nil {
+ srvErr[0] = fmt.Sprintf("error querying DNS SRV records for _etcd-server-ssl %s", err)
+ failCount += 1
+ }
+ err = updateNodeMap("etcd-server", "http://")
+ if err != nil {
+ srvErr[1] = fmt.Sprintf("error querying DNS SRV records for _etcd-server %s", err)
+ failCount += 1
+ }
+ if failCount == 2 {
+ plog.Warningf(srvErr[0])
+ plog.Warningf(srvErr[1])
+ plog.Errorf("SRV discovery failed: too many errors querying DNS SRV records")
+ return "", "", err
+ }
+ return strings.Join(stringParts, ","), defaultToken, nil
+}
diff --git a/vendor/github.com/coreos/etcd/discovery/srv_test.go b/vendor/github.com/coreos/etcd/discovery/srv_test.go
new file mode 100644
index 000000000..0d86791e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/discovery/srv_test.go
@@ -0,0 +1,129 @@
+// Copyright 2015 CoreOS, 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 discovery
+
+import (
+ "errors"
+ "net"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+func TestSRVGetCluster(t *testing.T) {
+ defer func() {
+ lookupSRV = net.LookupSRV
+ resolveTCPAddr = net.ResolveTCPAddr
+ }()
+
+ name := "dnsClusterTest"
+ tests := []struct {
+ withSSL []*net.SRV
+ withoutSSL []*net.SRV
+ urls []string
+ dns map[string]string
+
+ expected string
+ }{
+ {
+ []*net.SRV{},
+ []*net.SRV{},
+ nil,
+ nil,
+
+ "",
+ },
+ {
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2480},
+ {Target: "10.0.0.2", Port: 2480},
+ {Target: "10.0.0.3", Port: 2480},
+ },
+ []*net.SRV{},
+ nil,
+ nil,
+
+ "0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480",
+ },
+ {
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2480},
+ {Target: "10.0.0.2", Port: 2480},
+ {Target: "10.0.0.3", Port: 2480},
+ },
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2380},
+ },
+ nil,
+ nil,
+ "0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480,3=http://10.0.0.1:2380",
+ },
+ {
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2480},
+ {Target: "10.0.0.2", Port: 2480},
+ {Target: "10.0.0.3", Port: 2480},
+ },
+ []*net.SRV{
+ {Target: "10.0.0.1", Port: 2380},
+ },
+ []string{"https://10.0.0.1:2480"},
+ nil,
+ "dnsClusterTest=https://10.0.0.1:2480,0=https://10.0.0.2:2480,1=https://10.0.0.3:2480,2=http://10.0.0.1:2380",
+ },
+ // matching local member with resolved addr and return unresolved hostnames
+ {
+ []*net.SRV{
+ {Target: "1.example.com.", Port: 2480},
+ {Target: "2.example.com.", Port: 2480},
+ {Target: "3.example.com.", Port: 2480},
+ },
+ nil,
+ []string{"https://10.0.0.1:2480"},
+ map[string]string{"1.example.com:2480": "10.0.0.1:2480", "2.example.com:2480": "10.0.0.2:2480", "3.example.com:2480": "10.0.0.3:2480"},
+
+ "dnsClusterTest=https://1.example.com:2480,0=https://2.example.com:2480,1=https://3.example.com:2480",
+ },
+ }
+
+ for i, tt := range tests {
+ lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
+ if service == "etcd-server-ssl" {
+ return "", tt.withSSL, nil
+ }
+ if service == "etcd-server" {
+ return "", tt.withoutSSL, nil
+ }
+ return "", nil, errors.New("Unknown service in mock")
+ }
+ resolveTCPAddr = func(network, addr string) (*net.TCPAddr, error) {
+ if tt.dns == nil || tt.dns[addr] == "" {
+ return net.ResolveTCPAddr(network, addr)
+ }
+ return net.ResolveTCPAddr(network, tt.dns[addr])
+ }
+ urls := testutil.MustNewURLs(t, tt.urls)
+ str, token, err := SRVGetCluster(name, "example.com", "token", urls)
+ if err != nil {
+ t.Fatalf("%d: err: %#v", i, err)
+ }
+ if token != "token" {
+ t.Errorf("%d: token: %s", i, token)
+ }
+ if str != tt.expected {
+ t.Errorf("#%d: cluster = %s, want %s", i, str, tt.expected)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/error/error.go b/vendor/github.com/coreos/etcd/error/error.go
new file mode 100644
index 000000000..56facdc01
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/error/error.go
@@ -0,0 +1,159 @@
+// Copyright 2015 CoreOS, 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.
+
+// error package describes errors in etcd project.
+// When any change happens, Documentation/errorcode.md needs to be updated
+// correspondingly.
+package error
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+)
+
+var errors = map[int]string{
+ // command related errors
+ EcodeKeyNotFound: "Key not found",
+ EcodeTestFailed: "Compare failed", //test and set
+ EcodeNotFile: "Not a file",
+ ecodeNoMorePeer: "Reached the max number of peers in the cluster",
+ EcodeNotDir: "Not a directory",
+ EcodeNodeExist: "Key already exists", // create
+ ecodeKeyIsPreserved: "The prefix of given key is a keyword in etcd",
+ EcodeRootROnly: "Root is read only",
+ EcodeDirNotEmpty: "Directory not empty",
+ ecodeExistingPeerAddr: "Peer address has existed",
+ EcodeUnauthorized: "The request requires user authentication",
+
+ // Post form related errors
+ ecodeValueRequired: "Value is Required in POST form",
+ EcodePrevValueRequired: "PrevValue is Required in POST form",
+ EcodeTTLNaN: "The given TTL in POST form is not a number",
+ EcodeIndexNaN: "The given index in POST form is not a number",
+ ecodeValueOrTTLRequired: "Value or TTL is required in POST form",
+ ecodeTimeoutNaN: "The given timeout in POST form is not a number",
+ ecodeNameRequired: "Name is required in POST form",
+ ecodeIndexOrValueRequired: "Index or value is required",
+ ecodeIndexValueMutex: "Index and value cannot both be specified",
+ EcodeInvalidField: "Invalid field",
+ EcodeInvalidForm: "Invalid POST form",
+
+ // raft related errors
+ EcodeRaftInternal: "Raft Internal Error",
+ EcodeLeaderElect: "During Leader Election",
+
+ // etcd related errors
+ EcodeWatcherCleared: "watcher is cleared due to etcd recovery",
+ EcodeEventIndexCleared: "The event in requested index is outdated and cleared",
+ ecodeStandbyInternal: "Standby Internal Error",
+ ecodeInvalidActiveSize: "Invalid active size",
+ ecodeInvalidRemoveDelay: "Standby remove delay",
+
+ // client related errors
+ ecodeClientInternal: "Client Internal Error",
+}
+
+var errorStatus = map[int]int{
+ EcodeKeyNotFound: http.StatusNotFound,
+ EcodeNotFile: http.StatusForbidden,
+ EcodeDirNotEmpty: http.StatusForbidden,
+ EcodeUnauthorized: http.StatusUnauthorized,
+ EcodeTestFailed: http.StatusPreconditionFailed,
+ EcodeNodeExist: http.StatusPreconditionFailed,
+ EcodeRaftInternal: http.StatusInternalServerError,
+ EcodeLeaderElect: http.StatusInternalServerError,
+}
+
+const (
+ EcodeKeyNotFound = 100
+ EcodeTestFailed = 101
+ EcodeNotFile = 102
+ ecodeNoMorePeer = 103
+ EcodeNotDir = 104
+ EcodeNodeExist = 105
+ ecodeKeyIsPreserved = 106
+ EcodeRootROnly = 107
+ EcodeDirNotEmpty = 108
+ ecodeExistingPeerAddr = 109
+ EcodeUnauthorized = 110
+
+ ecodeValueRequired = 200
+ EcodePrevValueRequired = 201
+ EcodeTTLNaN = 202
+ EcodeIndexNaN = 203
+ ecodeValueOrTTLRequired = 204
+ ecodeTimeoutNaN = 205
+ ecodeNameRequired = 206
+ ecodeIndexOrValueRequired = 207
+ ecodeIndexValueMutex = 208
+ EcodeInvalidField = 209
+ EcodeInvalidForm = 210
+
+ EcodeRaftInternal = 300
+ EcodeLeaderElect = 301
+
+ EcodeWatcherCleared = 400
+ EcodeEventIndexCleared = 401
+ ecodeStandbyInternal = 402
+ ecodeInvalidActiveSize = 403
+ ecodeInvalidRemoveDelay = 404
+
+ ecodeClientInternal = 500
+)
+
+type Error struct {
+ ErrorCode int `json:"errorCode"`
+ Message string `json:"message"`
+ Cause string `json:"cause,omitempty"`
+ Index uint64 `json:"index"`
+}
+
+func NewRequestError(errorCode int, cause string) *Error {
+ return NewError(errorCode, cause, 0)
+}
+
+func NewError(errorCode int, cause string, index uint64) *Error {
+ return &Error{
+ ErrorCode: errorCode,
+ Message: errors[errorCode],
+ Cause: cause,
+ Index: index,
+ }
+}
+
+// Only for error interface
+func (e Error) Error() string {
+ return e.Message + " (" + e.Cause + ")"
+}
+
+func (e Error) toJsonString() string {
+ b, _ := json.Marshal(e)
+ return string(b)
+}
+
+func (e Error) StatusCode() int {
+ status, ok := errorStatus[e.ErrorCode]
+ if !ok {
+ status = http.StatusBadRequest
+ }
+ return status
+}
+
+func (e Error) WriteTo(w http.ResponseWriter) {
+ w.Header().Add("X-Etcd-Index", fmt.Sprint(e.Index))
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(e.StatusCode())
+ fmt.Fprintln(w, e.toJsonString())
+}
diff --git a/vendor/github.com/coreos/etcd/error/error_test.go b/vendor/github.com/coreos/etcd/error/error_test.go
new file mode 100644
index 000000000..9251f428b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/error/error_test.go
@@ -0,0 +1,50 @@
+// Copyright 2015 CoreOS, 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 error
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestErrorWriteTo(t *testing.T) {
+ for k := range errors {
+ err := NewError(k, "", 1)
+ rr := httptest.NewRecorder()
+ err.WriteTo(rr)
+
+ if err.StatusCode() != rr.Code {
+ t.Errorf("HTTP status code %d, want %d", rr.Code, err.StatusCode())
+ }
+
+ gbody := strings.TrimSuffix(rr.Body.String(), "\n")
+ if err.toJsonString() != gbody {
+ t.Errorf("HTTP body %q, want %q", gbody, err.toJsonString())
+ }
+
+ wheader := http.Header(map[string][]string{
+ "Content-Type": {"application/json"},
+ "X-Etcd-Index": {"1"},
+ })
+
+ if !reflect.DeepEqual(wheader, rr.HeaderMap) {
+ t.Errorf("HTTP headers %v, want %v", rr.HeaderMap, wheader)
+ }
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/error.go b/vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/error.go
new file mode 100644
index 000000000..5ad2269c1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/error.go
@@ -0,0 +1,27 @@
+// Copyright 2015 CoreOS, 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 v3rpc
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/storage"
+)
+
+var (
+ ErrEmptyKey = grpc.Errorf(codes.InvalidArgument, "key is not provided")
+ ErrCompacted = grpc.Errorf(codes.OutOfRange, storage.ErrCompacted.Error())
+ ErrFutureRev = grpc.Errorf(codes.OutOfRange, storage.ErrFutureRev.Error())
+)
diff --git a/vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/key.go b/vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/key.go
new file mode 100644
index 000000000..ae47aaeae
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/api/v3rpc/key.go
@@ -0,0 +1,163 @@
+// Copyright 2015 CoreOS, 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 v3rpc
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+ "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc/codes"
+ "github.com/coreos/etcd/etcdserver"
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/storage"
+)
+
+type handler struct {
+ server etcdserver.V3DemoServer
+}
+
+func New(s etcdserver.V3DemoServer) pb.EtcdServer {
+ return &handler{s}
+}
+
+func (h *handler) Range(ctx context.Context, r *pb.RangeRequest) (*pb.RangeResponse, error) {
+ if err := checkRangeRequest(r); err != nil {
+ return nil, err
+ }
+
+ resp, err := h.server.V3DemoDo(ctx, pb.InternalRaftRequest{Range: r})
+ if err != nil {
+ return nil, togRPCError(err)
+ }
+
+ return resp.(*pb.RangeResponse), err
+}
+
+func (h *handler) Put(ctx context.Context, r *pb.PutRequest) (*pb.PutResponse, error) {
+ if err := checkPutRequest(r); err != nil {
+ return nil, err
+ }
+
+ resp, err := h.server.V3DemoDo(ctx, pb.InternalRaftRequest{Put: r})
+ if err != nil {
+ return nil, togRPCError(err)
+ }
+
+ return resp.(*pb.PutResponse), err
+}
+
+func (h *handler) DeleteRange(ctx context.Context, r *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
+ if err := checkDeleteRequest(r); err != nil {
+ return nil, err
+ }
+
+ resp, err := h.server.V3DemoDo(ctx, pb.InternalRaftRequest{DeleteRange: r})
+ if err != nil {
+ return nil, togRPCError(err)
+ }
+
+ return resp.(*pb.DeleteRangeResponse), err
+}
+
+func (h *handler) Txn(ctx context.Context, r *pb.TxnRequest) (*pb.TxnResponse, error) {
+ if err := checkTxnRequest(r); err != nil {
+ return nil, err
+ }
+
+ resp, err := h.server.V3DemoDo(ctx, pb.InternalRaftRequest{Txn: r})
+ if err != nil {
+ return nil, togRPCError(err)
+ }
+
+ return resp.(*pb.TxnResponse), err
+}
+
+func (h *handler) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error) {
+ resp, err := h.server.V3DemoDo(ctx, pb.InternalRaftRequest{Compaction: r})
+ if err != nil {
+ return nil, togRPCError(err)
+ }
+
+ return resp.(*pb.CompactionResponse), nil
+}
+
+func checkRangeRequest(r *pb.RangeRequest) error {
+ if len(r.Key) == 0 {
+ return ErrEmptyKey
+ }
+ return nil
+}
+
+func checkPutRequest(r *pb.PutRequest) error {
+ if len(r.Key) == 0 {
+ return ErrEmptyKey
+ }
+ return nil
+}
+
+func checkDeleteRequest(r *pb.DeleteRangeRequest) error {
+ if len(r.Key) == 0 {
+ return ErrEmptyKey
+ }
+ return nil
+}
+
+func checkTxnRequest(r *pb.TxnRequest) error {
+ for _, c := range r.Compare {
+ if len(c.Key) == 0 {
+ return ErrEmptyKey
+ }
+ }
+
+ for _, u := range r.Success {
+ if err := checkRequestUnion(u); err != nil {
+ return err
+ }
+ }
+
+ for _, u := range r.Failure {
+ if err := checkRequestUnion(u); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func checkRequestUnion(u *pb.RequestUnion) error {
+ // TODO: ensure only one of the field is set.
+ switch {
+ case u.RequestRange != nil:
+ return checkRangeRequest(u.RequestRange)
+ case u.RequestPut != nil:
+ return checkPutRequest(u.RequestPut)
+ case u.RequestDeleteRange != nil:
+ return checkDeleteRequest(u.RequestDeleteRange)
+ default:
+ // empty union
+ return nil
+ }
+}
+
+func togRPCError(err error) error {
+ switch err {
+ case storage.ErrCompacted:
+ return ErrCompacted
+ case storage.ErrFutureRev:
+ return ErrFutureRev
+ // TODO: handle error from raft and timeout
+ default:
+ return grpc.Errorf(codes.Unknown, err.Error())
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/auth/auth.go b/vendor/github.com/coreos/etcd/etcdserver/auth/auth.go
new file mode 100644
index 000000000..7d27e74c2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/auth/auth.go
@@ -0,0 +1,636 @@
+// Copyright 2015 CoreOS, 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 auth
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "path"
+ "reflect"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ etcderr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/types"
+)
+
+const (
+ // StorePermsPrefix is the internal prefix of the storage layer dedicated to storing user data.
+ StorePermsPrefix = "/2"
+
+ // RootRoleName is the name of the ROOT role, with privileges to manage the cluster.
+ RootRoleName = "root"
+
+ // GuestRoleName is the name of the role that defines the privileges of an unauthenticated user.
+ GuestRoleName = "guest"
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "auth")
+)
+
+var rootRole = Role{
+ Role: RootRoleName,
+ Permissions: Permissions{
+ KV: RWPermission{
+ Read: []string{"*"},
+ Write: []string{"*"},
+ },
+ },
+}
+
+var guestRole = Role{
+ Role: GuestRoleName,
+ Permissions: Permissions{
+ KV: RWPermission{
+ Read: []string{"*"},
+ Write: []string{"*"},
+ },
+ },
+}
+
+type doer interface {
+ Do(context.Context, etcdserverpb.Request) (etcdserver.Response, error)
+}
+
+type Store interface {
+ AllUsers() ([]string, error)
+ GetUser(name string) (User, error)
+ CreateOrUpdateUser(user User) (out User, created bool, err error)
+ CreateUser(user User) (User, error)
+ DeleteUser(name string) error
+ UpdateUser(user User) (User, error)
+ AllRoles() ([]string, error)
+ GetRole(name string) (Role, error)
+ CreateRole(role Role) error
+ DeleteRole(name string) error
+ UpdateRole(role Role) (Role, error)
+ AuthEnabled() bool
+ EnableAuth() error
+ DisableAuth() error
+}
+
+type store struct {
+ server doer
+ timeout time.Duration
+ ensuredOnce bool
+ enabled *bool
+}
+
+type User struct {
+ User string `json:"user"`
+ Password string `json:"password,omitempty"`
+ Roles []string `json:"roles"`
+ Grant []string `json:"grant,omitempty"`
+ Revoke []string `json:"revoke,omitempty"`
+}
+
+type Role struct {
+ Role string `json:"role"`
+ Permissions Permissions `json:"permissions"`
+ Grant *Permissions `json:"grant,omitempty"`
+ Revoke *Permissions `json:"revoke,omitempty"`
+}
+
+type Permissions struct {
+ KV RWPermission `json:"kv"`
+}
+
+func (p *Permissions) IsEmpty() bool {
+ return p == nil || (len(p.KV.Read) == 0 && len(p.KV.Write) == 0)
+}
+
+type RWPermission struct {
+ Read []string `json:"read"`
+ Write []string `json:"write"`
+}
+
+type Error struct {
+ Status int
+ Errmsg string
+}
+
+func (ae Error) Error() string { return ae.Errmsg }
+func (ae Error) HTTPStatus() int { return ae.Status }
+
+func authErr(hs int, s string, v ...interface{}) Error {
+ return Error{Status: hs, Errmsg: fmt.Sprintf("auth: "+s, v...)}
+}
+
+func NewStore(server doer, timeout time.Duration) Store {
+ s := &store{
+ server: server,
+ timeout: timeout,
+ }
+ return s
+}
+
+func (s *store) AllUsers() ([]string, error) {
+ resp, err := s.requestResource("/users/", false)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return []string{}, nil
+ }
+ }
+ return nil, err
+ }
+ var nodes []string
+ for _, n := range resp.Event.Node.Nodes {
+ _, user := path.Split(n.Key)
+ nodes = append(nodes, user)
+ }
+ sort.Strings(nodes)
+ return nodes, nil
+}
+
+func (s *store) GetUser(name string) (User, error) {
+ resp, err := s.requestResource("/users/"+name, false)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return User{}, authErr(http.StatusNotFound, "User %s does not exist.", name)
+ }
+ }
+ return User{}, err
+ }
+ var u User
+ err = json.Unmarshal([]byte(*resp.Event.Node.Value), &u)
+ if err != nil {
+ return u, err
+ }
+ // Attach root role to root user.
+ if u.User == "root" {
+ u = attachRootRole(u)
+ }
+ return u, nil
+}
+
+// CreateOrUpdateUser should be only used for creating the new user or when you are not
+// sure if it is a create or update. (When only password is passed in, we are not sure
+// if it is a update or create)
+func (s *store) CreateOrUpdateUser(user User) (out User, created bool, err error) {
+ _, err = s.GetUser(user.User)
+ if err == nil {
+ out, err = s.UpdateUser(user)
+ return out, false, err
+ }
+ u, err := s.CreateUser(user)
+ return u, true, err
+}
+
+func (s *store) CreateUser(user User) (User, error) {
+ // Attach root role to root user.
+ if user.User == "root" {
+ user = attachRootRole(user)
+ }
+ u, err := s.createUserInternal(user)
+ if err == nil {
+ plog.Noticef("created user %s", user.User)
+ }
+ return u, err
+}
+
+func (s *store) createUserInternal(user User) (User, error) {
+ if user.Password == "" {
+ return user, authErr(http.StatusBadRequest, "Cannot create user %s with an empty password", user.User)
+ }
+ hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
+ if err != nil {
+ return user, err
+ }
+ user.Password = string(hash)
+
+ _, err = s.createResource("/users/"+user.User, user)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeNodeExist {
+ return user, authErr(http.StatusConflict, "User %s already exists.", user.User)
+ }
+ }
+ }
+ return user, err
+}
+
+func (s *store) DeleteUser(name string) error {
+ if s.AuthEnabled() && name == "root" {
+ return authErr(http.StatusForbidden, "Cannot delete root user while auth is enabled.")
+ }
+ _, err := s.deleteResource("/users/" + name)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return authErr(http.StatusNotFound, "User %s does not exist", name)
+ }
+ }
+ return err
+ }
+ plog.Noticef("deleted user %s", name)
+ return nil
+}
+
+func (s *store) UpdateUser(user User) (User, error) {
+ old, err := s.GetUser(user.User)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return user, authErr(http.StatusNotFound, "User %s doesn't exist.", user.User)
+ }
+ }
+ return old, err
+ }
+ newUser, err := old.merge(user)
+ if err != nil {
+ return old, err
+ }
+ if reflect.DeepEqual(old, newUser) {
+ return old, authErr(http.StatusBadRequest, "User not updated. Use grant/revoke/password to update the user.")
+ }
+ _, err = s.updateResource("/users/"+user.User, newUser)
+ if err == nil {
+ plog.Noticef("updated user %s", user.User)
+ }
+ return newUser, err
+}
+
+func (s *store) AllRoles() ([]string, error) {
+ nodes := []string{RootRoleName}
+ resp, err := s.requestResource("/roles/", false)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return nodes, nil
+ }
+ }
+ return nil, err
+ }
+ for _, n := range resp.Event.Node.Nodes {
+ _, role := path.Split(n.Key)
+ nodes = append(nodes, role)
+ }
+ sort.Strings(nodes)
+ return nodes, nil
+}
+
+func (s *store) GetRole(name string) (Role, error) {
+ if name == RootRoleName {
+ return rootRole, nil
+ }
+ resp, err := s.requestResource("/roles/"+name, false)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return Role{}, authErr(http.StatusNotFound, "Role %s does not exist.", name)
+ }
+ }
+ return Role{}, err
+ }
+ var r Role
+ err = json.Unmarshal([]byte(*resp.Event.Node.Value), &r)
+ if err != nil {
+ return r, err
+ }
+
+ return r, nil
+}
+
+func (s *store) CreateRole(role Role) error {
+ if role.Role == RootRoleName {
+ return authErr(http.StatusForbidden, "Cannot modify role %s: is root role.", role.Role)
+ }
+ _, err := s.createResource("/roles/"+role.Role, role)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeNodeExist {
+ return authErr(http.StatusConflict, "Role %s already exists.", role.Role)
+ }
+ }
+ }
+ if err == nil {
+ plog.Noticef("created new role %s", role.Role)
+ }
+ return err
+}
+
+func (s *store) DeleteRole(name string) error {
+ if name == RootRoleName {
+ return authErr(http.StatusForbidden, "Cannot modify role %s: is root role.", name)
+ }
+ _, err := s.deleteResource("/roles/" + name)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return authErr(http.StatusNotFound, "Role %s doesn't exist.", name)
+ }
+ }
+ }
+ if err == nil {
+ plog.Noticef("deleted role %s", name)
+ }
+ return err
+}
+
+func (s *store) UpdateRole(role Role) (Role, error) {
+ if role.Role == RootRoleName {
+ return Role{}, authErr(http.StatusForbidden, "Cannot modify role %s: is root role.", role.Role)
+ }
+ old, err := s.GetRole(role.Role)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ return role, authErr(http.StatusNotFound, "Role %s doesn't exist.", role.Role)
+ }
+ }
+ return old, err
+ }
+ newRole, err := old.merge(role)
+ if err != nil {
+ return old, err
+ }
+ if reflect.DeepEqual(old, newRole) {
+ return old, authErr(http.StatusBadRequest, "Role not updated. Use grant/revoke to update the role.")
+ }
+ _, err = s.updateResource("/roles/"+role.Role, newRole)
+ if err == nil {
+ plog.Noticef("updated role %s", role.Role)
+ }
+ return newRole, err
+}
+
+func (s *store) AuthEnabled() bool {
+ return s.detectAuth()
+}
+
+func (s *store) EnableAuth() error {
+ if s.AuthEnabled() {
+ return authErr(http.StatusConflict, "already enabled")
+ }
+ _, err := s.GetUser("root")
+ if err != nil {
+ return authErr(http.StatusConflict, "No root user available, please create one")
+ }
+ _, err = s.GetRole(GuestRoleName)
+ if err != nil {
+ plog.Printf("no guest role access found, creating default")
+ err := s.CreateRole(guestRole)
+ if err != nil {
+ plog.Errorf("error creating guest role. aborting auth enable.")
+ return err
+ }
+ }
+ err = s.enableAuth()
+ if err == nil {
+ b := true
+ s.enabled = &b
+ plog.Noticef("auth: enabled auth")
+ } else {
+ plog.Errorf("error enabling auth (%v)", err)
+ }
+ return err
+}
+
+func (s *store) DisableAuth() error {
+ if !s.AuthEnabled() {
+ return authErr(http.StatusConflict, "already disabled")
+ }
+ err := s.disableAuth()
+ if err == nil {
+ b := false
+ s.enabled = &b
+ plog.Noticef("auth: disabled auth")
+ } else {
+ plog.Errorf("error disabling auth (%v)", err)
+ }
+ return err
+}
+
+// merge applies the properties of the passed-in User to the User on which it
+// is called and returns a new User with these modifications applied. Think of
+// all Users as immutable sets of data. Merge allows you to perform the set
+// operations (desired grants and revokes) atomically
+func (u User) merge(n User) (User, error) {
+ var out User
+ if u.User != n.User {
+ return out, authErr(http.StatusConflict, "Merging user data with conflicting usernames: %s %s", u.User, n.User)
+ }
+ out.User = u.User
+ if n.Password != "" {
+ hash, err := bcrypt.GenerateFromPassword([]byte(n.Password), bcrypt.DefaultCost)
+ if err != nil {
+ return User{}, err
+ }
+ out.Password = string(hash)
+ } else {
+ out.Password = u.Password
+ }
+ currentRoles := types.NewUnsafeSet(u.Roles...)
+ for _, g := range n.Grant {
+ if currentRoles.Contains(g) {
+ plog.Noticef("granting duplicate role %s for user %s", g, n.User)
+ return User{}, authErr(http.StatusConflict, fmt.Sprintf("Granting duplicate role %s for user %s", g, n.User))
+ }
+ currentRoles.Add(g)
+ }
+ for _, r := range n.Revoke {
+ if !currentRoles.Contains(r) {
+ plog.Noticef("revoking ungranted role %s for user %s", r, n.User)
+ return User{}, authErr(http.StatusConflict, fmt.Sprintf("Revoking ungranted role %s for user %s", r, n.User))
+ }
+ currentRoles.Remove(r)
+ }
+ out.Roles = currentRoles.Values()
+ sort.Strings(out.Roles)
+ return out, nil
+}
+
+func (u User) CheckPassword(password string) bool {
+ err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
+ return err == nil
+}
+
+// merge for a role works the same as User above -- atomic Role application to
+// each of the substructures.
+func (r Role) merge(n Role) (Role, error) {
+ var out Role
+ var err error
+ if r.Role != n.Role {
+ return out, authErr(http.StatusConflict, "Merging role with conflicting names: %s %s", r.Role, n.Role)
+ }
+ out.Role = r.Role
+ out.Permissions, err = r.Permissions.Grant(n.Grant)
+ if err != nil {
+ return out, err
+ }
+ out.Permissions, err = out.Permissions.Revoke(n.Revoke)
+ if err != nil {
+ return out, err
+ }
+ return out, nil
+}
+
+func (r Role) HasKeyAccess(key string, write bool) bool {
+ if r.Role == RootRoleName {
+ return true
+ }
+ return r.Permissions.KV.HasAccess(key, write)
+}
+
+func (r Role) HasRecursiveAccess(key string, write bool) bool {
+ if r.Role == RootRoleName {
+ return true
+ }
+ return r.Permissions.KV.HasRecursiveAccess(key, write)
+}
+
+// Grant adds a set of permissions to the permission object on which it is called,
+// returning a new permission object.
+func (p Permissions) Grant(n *Permissions) (Permissions, error) {
+ var out Permissions
+ var err error
+ if n == nil {
+ return p, nil
+ }
+ out.KV, err = p.KV.Grant(n.KV)
+ return out, err
+}
+
+// Revoke removes a set of permissions to the permission object on which it is called,
+// returning a new permission object.
+func (p Permissions) Revoke(n *Permissions) (Permissions, error) {
+ var out Permissions
+ var err error
+ if n == nil {
+ return p, nil
+ }
+ out.KV, err = p.KV.Revoke(n.KV)
+ return out, err
+}
+
+// Grant adds a set of permissions to the permission object on which it is called,
+// returning a new permission object.
+func (rw RWPermission) Grant(n RWPermission) (RWPermission, error) {
+ var out RWPermission
+ currentRead := types.NewUnsafeSet(rw.Read...)
+ for _, r := range n.Read {
+ if currentRead.Contains(r) {
+ return out, authErr(http.StatusConflict, "Granting duplicate read permission %s", r)
+ }
+ currentRead.Add(r)
+ }
+ currentWrite := types.NewUnsafeSet(rw.Write...)
+ for _, w := range n.Write {
+ if currentWrite.Contains(w) {
+ return out, authErr(http.StatusConflict, "Granting duplicate write permission %s", w)
+ }
+ currentWrite.Add(w)
+ }
+ out.Read = currentRead.Values()
+ out.Write = currentWrite.Values()
+ sort.Strings(out.Read)
+ sort.Strings(out.Write)
+ return out, nil
+}
+
+// Revoke removes a set of permissions to the permission object on which it is called,
+// returning a new permission object.
+func (rw RWPermission) Revoke(n RWPermission) (RWPermission, error) {
+ var out RWPermission
+ currentRead := types.NewUnsafeSet(rw.Read...)
+ for _, r := range n.Read {
+ if !currentRead.Contains(r) {
+ plog.Noticef("revoking ungranted read permission %s", r)
+ continue
+ }
+ currentRead.Remove(r)
+ }
+ currentWrite := types.NewUnsafeSet(rw.Write...)
+ for _, w := range n.Write {
+ if !currentWrite.Contains(w) {
+ plog.Noticef("revoking ungranted write permission %s", w)
+ continue
+ }
+ currentWrite.Remove(w)
+ }
+ out.Read = currentRead.Values()
+ out.Write = currentWrite.Values()
+ sort.Strings(out.Read)
+ sort.Strings(out.Write)
+ return out, nil
+}
+
+func (rw RWPermission) HasAccess(key string, write bool) bool {
+ var list []string
+ if write {
+ list = rw.Write
+ } else {
+ list = rw.Read
+ }
+ for _, pat := range list {
+ match, err := simpleMatch(pat, key)
+ if err == nil && match {
+ return true
+ }
+ }
+ return false
+}
+
+func (rw RWPermission) HasRecursiveAccess(key string, write bool) bool {
+ list := rw.Read
+ if write {
+ list = rw.Write
+ }
+ for _, pat := range list {
+ match, err := prefixMatch(pat, key)
+ if err == nil && match {
+ return true
+ }
+ }
+ return false
+}
+
+func simpleMatch(pattern string, key string) (match bool, err error) {
+ if pattern[len(pattern)-1] == '*' {
+ return strings.HasPrefix(key, pattern[:len(pattern)-1]), nil
+ }
+ return key == pattern, nil
+}
+
+func prefixMatch(pattern string, key string) (match bool, err error) {
+ if pattern[len(pattern)-1] != '*' {
+ return false, nil
+ }
+ return strings.HasPrefix(key, pattern[:len(pattern)-1]), nil
+}
+
+func attachRootRole(u User) User {
+ inRoles := false
+ for _, r := range u.Roles {
+ if r == RootRoleName {
+ inRoles = true
+ break
+ }
+ }
+ if !inRoles {
+ u.Roles = append(u.Roles, RootRoleName)
+ }
+ return u
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/auth/auth_requests.go b/vendor/github.com/coreos/etcd/etcdserver/auth/auth_requests.go
new file mode 100644
index 000000000..d621ed0ef
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/auth/auth_requests.go
@@ -0,0 +1,168 @@
+// Copyright 2015 CoreOS, 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 auth
+
+import (
+ "encoding/json"
+ "path"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ etcderr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+)
+
+func (s *store) ensureAuthDirectories() error {
+ if s.ensuredOnce {
+ return nil
+ }
+ for _, res := range []string{StorePermsPrefix, StorePermsPrefix + "/users/", StorePermsPrefix + "/roles/"} {
+ ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
+ defer cancel()
+ pe := false
+ rr := etcdserverpb.Request{
+ Method: "PUT",
+ Path: res,
+ Dir: true,
+ PrevExist: &pe,
+ }
+ _, err := s.server.Do(ctx, rr)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeNodeExist {
+ continue
+ }
+ }
+ plog.Errorf("failed to create auth directories in the store (%v)", err)
+ return err
+ }
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
+ defer cancel()
+ pe := false
+ rr := etcdserverpb.Request{
+ Method: "PUT",
+ Path: StorePermsPrefix + "/enabled",
+ Val: "false",
+ PrevExist: &pe,
+ }
+ _, err := s.server.Do(ctx, rr)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeNodeExist {
+ s.ensuredOnce = true
+ return nil
+ }
+ }
+ return err
+ }
+ s.ensuredOnce = true
+ return nil
+}
+
+func (s *store) enableAuth() error {
+ _, err := s.updateResource("/enabled", true)
+ return err
+}
+func (s *store) disableAuth() error {
+ _, err := s.updateResource("/enabled", false)
+ return err
+}
+
+func (s *store) detectAuth() bool {
+ if s.server == nil {
+ return false
+ }
+ if s.enabled != nil {
+ return *s.enabled
+ }
+ value, err := s.requestResource("/enabled", false)
+ if err != nil {
+ if e, ok := err.(*etcderr.Error); ok {
+ if e.ErrorCode == etcderr.EcodeKeyNotFound {
+ b := false
+ s.enabled = &b
+ return false
+ }
+ }
+ plog.Errorf("failed to detect auth settings (%s)", err)
+ return false
+ }
+
+ var u bool
+ err = json.Unmarshal([]byte(*value.Event.Node.Value), &u)
+ if err != nil {
+ plog.Errorf("internal bookkeeping value for enabled isn't valid JSON (%v)", err)
+ return false
+ }
+ s.enabled = &u
+ return u
+}
+
+func (s *store) requestResource(res string, dir bool) (etcdserver.Response, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
+ defer cancel()
+ p := path.Join(StorePermsPrefix, res)
+ rr := etcdserverpb.Request{
+ Method: "GET",
+ Path: p,
+ Dir: dir,
+ }
+ return s.server.Do(ctx, rr)
+}
+
+func (s *store) updateResource(res string, value interface{}) (etcdserver.Response, error) {
+ return s.setResource(res, value, true)
+}
+func (s *store) createResource(res string, value interface{}) (etcdserver.Response, error) {
+ return s.setResource(res, value, false)
+}
+func (s *store) setResource(res string, value interface{}, prevexist bool) (etcdserver.Response, error) {
+ err := s.ensureAuthDirectories()
+ if err != nil {
+ return etcdserver.Response{}, err
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
+ defer cancel()
+ data, err := json.Marshal(value)
+ if err != nil {
+ return etcdserver.Response{}, err
+ }
+ p := path.Join(StorePermsPrefix, res)
+ rr := etcdserverpb.Request{
+ Method: "PUT",
+ Path: p,
+ Val: string(data),
+ PrevExist: &prevexist,
+ }
+ return s.server.Do(ctx, rr)
+}
+
+func (s *store) deleteResource(res string) (etcdserver.Response, error) {
+ err := s.ensureAuthDirectories()
+ if err != nil {
+ return etcdserver.Response{}, err
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
+ defer cancel()
+ pex := true
+ p := path.Join(StorePermsPrefix, res)
+ rr := etcdserverpb.Request{
+ Method: "DELETE",
+ Path: p,
+ PrevExist: &pex,
+ }
+ return s.server.Do(ctx, rr)
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/auth/auth_test.go b/vendor/github.com/coreos/etcd/etcdserver/auth/auth_test.go
new file mode 100644
index 000000000..1f4615af4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/auth/auth_test.go
@@ -0,0 +1,658 @@
+// Copyright 2015 CoreOS, 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 auth
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ etcderr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ etcdstore "github.com/coreos/etcd/store"
+)
+
+const testTimeout = time.Millisecond
+
+func TestMergeUser(t *testing.T) {
+ tbl := []struct {
+ input User
+ merge User
+ expect User
+ iserr bool
+ }{
+ {
+ User{User: "foo"},
+ User{User: "bar"},
+ User{},
+ true,
+ },
+ {
+ User{User: "foo"},
+ User{User: "foo"},
+ User{User: "foo", Roles: []string{}},
+ false,
+ },
+ {
+ User{User: "foo"},
+ User{User: "foo", Grant: []string{"role1"}},
+ User{User: "foo", Roles: []string{"role1"}},
+ false,
+ },
+ {
+ User{User: "foo", Roles: []string{"role1"}},
+ User{User: "foo", Grant: []string{"role1"}},
+ User{},
+ true,
+ },
+ {
+ User{User: "foo", Roles: []string{"role1"}},
+ User{User: "foo", Revoke: []string{"role2"}},
+ User{},
+ true,
+ },
+ {
+ User{User: "foo", Roles: []string{"role1"}},
+ User{User: "foo", Grant: []string{"role2"}},
+ User{User: "foo", Roles: []string{"role1", "role2"}},
+ false,
+ },
+ {
+ User{User: "foo"},
+ User{User: "foo", Password: "bar"},
+ User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"},
+ false,
+ },
+ }
+
+ for i, tt := range tbl {
+ out, err := tt.input.merge(tt.merge)
+ if err != nil && !tt.iserr {
+ t.Fatalf("Got unexpected error on item %d", i)
+ }
+ if !tt.iserr {
+ err := bcrypt.CompareHashAndPassword([]byte(out.Password), []byte(tt.merge.Password))
+ if err == nil {
+ tt.expect.Password = out.Password
+ }
+ if !reflect.DeepEqual(out, tt.expect) {
+ t.Errorf("Unequal merge expectation on item %d: got: %#v, expect: %#v", i, out, tt.expect)
+ }
+ }
+ }
+}
+
+func TestMergeRole(t *testing.T) {
+ tbl := []struct {
+ input Role
+ merge Role
+ expect Role
+ iserr bool
+ }{
+ {
+ Role{Role: "foo"},
+ Role{Role: "bar"},
+ Role{},
+ true,
+ },
+ {
+ Role{Role: "foo"},
+ Role{Role: "foo", Grant: &Permissions{KV: RWPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
+ Role{Role: "foo", Permissions: Permissions{KV: RWPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
+ false,
+ },
+ {
+ Role{Role: "foo", Permissions: Permissions{KV: RWPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
+ Role{Role: "foo", Revoke: &Permissions{KV: RWPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
+ Role{Role: "foo", Permissions: Permissions{KV: RWPermission{Read: []string{}, Write: []string{}}}},
+ false,
+ },
+ {
+ Role{Role: "foo", Permissions: Permissions{KV: RWPermission{Read: []string{"/bardir"}}}},
+ Role{Role: "foo", Revoke: &Permissions{KV: RWPermission{Read: []string{"/foodir"}}}},
+ Role{},
+ true,
+ },
+ }
+ for i, tt := range tbl {
+ out, err := tt.input.merge(tt.merge)
+ if err != nil && !tt.iserr {
+ t.Fatalf("Got unexpected error on item %d", i)
+ }
+ if !tt.iserr {
+ if !reflect.DeepEqual(out, tt.expect) {
+ t.Errorf("Unequal merge expectation on item %d: got: %#v, expect: %#v", i, out, tt.expect)
+ }
+ }
+ }
+}
+
+type testDoer struct {
+ get []etcdserver.Response
+ put []etcdserver.Response
+ getindex int
+ putindex int
+ explicitlyEnabled bool
+}
+
+func (td *testDoer) Do(_ context.Context, req etcdserverpb.Request) (etcdserver.Response, error) {
+ if td.explicitlyEnabled && (req.Path == StorePermsPrefix+"/enabled") {
+ t := "true"
+ return etcdserver.Response{
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/cat",
+ Value: &t,
+ },
+ },
+ }, nil
+ }
+ if req.Method == "GET" && td.get != nil {
+ res := td.get[td.getindex]
+ if res.Event == nil {
+ td.getindex++
+ return etcdserver.Response{}, &etcderr.Error{
+ ErrorCode: etcderr.EcodeKeyNotFound,
+ }
+ }
+ td.getindex++
+ return res, nil
+ }
+ if req.Method == "PUT" && td.put != nil {
+ res := td.put[td.putindex]
+ if res.Event == nil {
+ td.putindex++
+ return etcdserver.Response{}, &etcderr.Error{
+ ErrorCode: etcderr.EcodeNodeExist,
+ }
+ }
+ td.putindex++
+ return res, nil
+ }
+ return etcdserver.Response{}, nil
+}
+
+func TestAllUsers(t *testing.T) {
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Nodes: etcdstore.NodeExterns([]*etcdstore.NodeExtern{
+ {
+ Key: StorePermsPrefix + "/users/cat",
+ },
+ {
+ Key: StorePermsPrefix + "/users/dog",
+ },
+ }),
+ },
+ },
+ },
+ },
+ }
+ expected := []string{"cat", "dog"}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: false}
+ users, err := s.AllUsers()
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ if !reflect.DeepEqual(users, expected) {
+ t.Error("AllUsers doesn't match given store. Got", users, "expected", expected)
+ }
+}
+
+func TestGetAndDeleteUser(t *testing.T) {
+ data := `{"user": "cat", "roles" : ["animal"]}`
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/cat",
+ Value: &data,
+ },
+ },
+ },
+ },
+ explicitlyEnabled: true,
+ }
+ expected := User{User: "cat", Roles: []string{"animal"}}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: false}
+ out, err := s.GetUser("cat")
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ if !reflect.DeepEqual(out, expected) {
+ t.Error("GetUser doesn't match given store. Got", out, "expected", expected)
+ }
+ err = s.DeleteUser("cat")
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+}
+
+func TestAllRoles(t *testing.T) {
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Nodes: etcdstore.NodeExterns([]*etcdstore.NodeExtern{
+ {
+ Key: StorePermsPrefix + "/roles/animal",
+ },
+ {
+ Key: StorePermsPrefix + "/roles/human",
+ },
+ }),
+ },
+ },
+ },
+ },
+ explicitlyEnabled: true,
+ }
+ expected := []string{"animal", "human", "root"}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: false}
+ out, err := s.AllRoles()
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ if !reflect.DeepEqual(out, expected) {
+ t.Error("AllRoles doesn't match given store. Got", out, "expected", expected)
+ }
+}
+
+func TestGetAndDeleteRole(t *testing.T) {
+ data := `{"role": "animal"}`
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/roles/animal",
+ Value: &data,
+ },
+ },
+ },
+ },
+ explicitlyEnabled: true,
+ }
+ expected := Role{Role: "animal"}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: false}
+ out, err := s.GetRole("animal")
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ if !reflect.DeepEqual(out, expected) {
+ t.Error("GetRole doesn't match given store. Got", out, "expected", expected)
+ }
+ err = s.DeleteRole("animal")
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+}
+
+func TestEnsure(t *testing.T) {
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Set,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix,
+ Dir: true,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Set,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/",
+ Dir: true,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Set,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/roles/",
+ Dir: true,
+ },
+ },
+ },
+ },
+ }
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: false}
+ err := s.ensureAuthDirectories()
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+}
+
+func TestCreateAndUpdateUser(t *testing.T) {
+ olduser := `{"user": "cat", "roles" : ["animal"]}`
+ newuser := `{"user": "cat", "roles" : ["animal", "pet"]}`
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: nil,
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/cat",
+ Value: &olduser,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/cat",
+ Value: &olduser,
+ },
+ },
+ },
+ },
+ put: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Update,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/cat",
+ Value: &olduser,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Update,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/users/cat",
+ Value: &newuser,
+ },
+ },
+ },
+ },
+ explicitlyEnabled: true,
+ }
+ user := User{User: "cat", Password: "meow", Roles: []string{"animal"}}
+ update := User{User: "cat", Grant: []string{"pet"}}
+ expected := User{User: "cat", Roles: []string{"animal", "pet"}}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: true}
+ out, created, err := s.CreateOrUpdateUser(user)
+ if created == false {
+ t.Error("Should have created user, instead updated?")
+ }
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ out.Password = "meow"
+ if !reflect.DeepEqual(out, user) {
+ t.Error("UpdateUser doesn't match given update. Got", out, "expected", expected)
+ }
+ out, created, err = s.CreateOrUpdateUser(update)
+ if created == true {
+ t.Error("Should have updated user, instead created?")
+ }
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ if !reflect.DeepEqual(out, expected) {
+ t.Error("UpdateUser doesn't match given update. Got", out, "expected", expected)
+ }
+}
+
+func TestUpdateRole(t *testing.T) {
+ oldrole := `{"role": "animal", "permissions" : {"kv": {"read": ["/animal"], "write": []}}}`
+ newrole := `{"role": "animal", "permissions" : {"kv": {"read": ["/animal"], "write": ["/animal"]}}}`
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/roles/animal",
+ Value: &oldrole,
+ },
+ },
+ },
+ },
+ put: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Update,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/roles/animal",
+ Value: &newrole,
+ },
+ },
+ },
+ },
+ explicitlyEnabled: true,
+ }
+ update := Role{Role: "animal", Grant: &Permissions{KV: RWPermission{Read: []string{}, Write: []string{"/animal"}}}}
+ expected := Role{Role: "animal", Permissions: Permissions{KV: RWPermission{Read: []string{"/animal"}, Write: []string{"/animal"}}}}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: true}
+ out, err := s.UpdateRole(update)
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ if !reflect.DeepEqual(out, expected) {
+ t.Error("UpdateRole doesn't match given update. Got", out, "expected", expected)
+ }
+}
+
+func TestCreateRole(t *testing.T) {
+ role := `{"role": "animal", "permissions" : {"kv": {"read": ["/animal"], "write": []}}}`
+ d := &testDoer{
+ put: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Create,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/roles/animal",
+ Value: &role,
+ },
+ },
+ },
+ {
+ Event: nil,
+ },
+ },
+ explicitlyEnabled: true,
+ }
+ r := Role{Role: "animal", Permissions: Permissions{KV: RWPermission{Read: []string{"/animal"}, Write: []string{}}}}
+
+ s := store{server: d, timeout: testTimeout, ensuredOnce: true}
+ err := s.CreateRole(Role{Role: "root"})
+ if err == nil {
+ t.Error("Should error creating root role")
+ }
+ err = s.CreateRole(r)
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+ err = s.CreateRole(r)
+ if err == nil {
+ t.Error("Creating duplicate role, should error")
+ }
+}
+
+func TestEnableAuth(t *testing.T) {
+ rootUser := `{"user": "root", "password": ""}`
+ guestRole := `{"role": "guest", "permissions" : {"kv": {"read": ["*"], "write": ["*"]}}}`
+ trueval := "true"
+ falseval := "false"
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/enabled",
+ Value: &falseval,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/user/root",
+ Value: &rootUser,
+ },
+ },
+ },
+ {
+ Event: nil,
+ },
+ },
+ put: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Create,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/roles/guest",
+ Value: &guestRole,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Update,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/enabled",
+ Value: &trueval,
+ },
+ },
+ },
+ },
+ explicitlyEnabled: false,
+ }
+ s := store{server: d, timeout: testTimeout, ensuredOnce: true}
+ err := s.EnableAuth()
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+}
+func TestDisableAuth(t *testing.T) {
+ trueval := "true"
+ falseval := "false"
+ d := &testDoer{
+ get: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/enabled",
+ Value: &falseval,
+ },
+ },
+ },
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Get,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/enabled",
+ Value: &trueval,
+ },
+ },
+ },
+ },
+ put: []etcdserver.Response{
+ {
+ Event: &etcdstore.Event{
+ Action: etcdstore.Update,
+ Node: &etcdstore.NodeExtern{
+ Key: StorePermsPrefix + "/enabled",
+ Value: &falseval,
+ },
+ },
+ },
+ },
+ explicitlyEnabled: false,
+ }
+ s := store{server: d, timeout: testTimeout, ensuredOnce: true}
+ err := s.DisableAuth()
+ if err == nil {
+ t.Error("Expected error; already disabled")
+ }
+
+ // clear cache
+ s.enabled = nil
+ err = s.DisableAuth()
+ if err != nil {
+ t.Error("Unexpected error", err)
+ }
+}
+
+func TestSimpleMatch(t *testing.T) {
+ role := Role{Role: "foo", Permissions: Permissions{KV: RWPermission{Read: []string{"/foodir/*", "/fookey"}, Write: []string{"/bardir/*", "/barkey"}}}}
+ if !role.HasKeyAccess("/foodir/foo/bar", false) {
+ t.Fatal("role lacks expected access")
+ }
+ if !role.HasKeyAccess("/fookey", false) {
+ t.Fatal("role lacks expected access")
+ }
+ if !role.HasRecursiveAccess("/foodir/*", false) {
+ t.Fatal("role lacks expected access")
+ }
+ if !role.HasRecursiveAccess("/foodir/foo*", false) {
+ t.Fatal("role lacks expected access")
+ }
+ if !role.HasRecursiveAccess("/bardir/*", true) {
+ t.Fatal("role lacks expected access")
+ }
+ if !role.HasKeyAccess("/bardir/bar/foo", true) {
+ t.Fatal("role lacks expected access")
+ }
+ if !role.HasKeyAccess("/barkey", true) {
+ t.Fatal("role lacks expected access")
+ }
+
+ if role.HasKeyAccess("/bardir/bar/foo", false) {
+ t.Fatal("role has unexpected access")
+ }
+ if role.HasKeyAccess("/barkey", false) {
+ t.Fatal("role has unexpected access")
+ }
+ if role.HasKeyAccess("/foodir/foo/bar", true) {
+ t.Fatal("role has unexpected access")
+ }
+ if role.HasKeyAccess("/fookey", true) {
+ t.Fatal("role has unexpected access")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/cluster.go b/vendor/github.com/coreos/etcd/etcdserver/cluster.go
new file mode 100644
index 000000000..5dbcacba8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/cluster.go
@@ -0,0 +1,498 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "bytes"
+ "crypto/sha1"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "path"
+ "sort"
+ "strings"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/pkg/netutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/store"
+ "github.com/coreos/etcd/version"
+)
+
+const (
+ raftAttributesSuffix = "raftAttributes"
+ attributesSuffix = "attributes"
+)
+
+type Cluster interface {
+ // ID returns the cluster ID
+ ID() types.ID
+ // ClientURLs returns an aggregate set of all URLs on which this
+ // cluster is listening for client requests
+ ClientURLs() []string
+ // Members returns a slice of members sorted by their ID
+ Members() []*Member
+ // Member retrieves a particular member based on ID, or nil if the
+ // member does not exist in the cluster
+ Member(id types.ID) *Member
+ // IsIDRemoved checks whether the given ID has been removed from this
+ // cluster at some point in the past
+ IsIDRemoved(id types.ID) bool
+ // ClusterVersion is the cluster-wide minimum major.minor version.
+ Version() *semver.Version
+}
+
+// Cluster is a list of Members that belong to the same raft cluster
+type cluster struct {
+ id types.ID
+ token string
+ store store.Store
+
+ sync.Mutex // guards the fields below
+ version *semver.Version
+ members map[types.ID]*Member
+ // removed contains the ids of removed members in the cluster.
+ // removed id cannot be reused.
+ removed map[types.ID]bool
+}
+
+func newClusterFromURLsMap(token string, urlsmap types.URLsMap) (*cluster, error) {
+ c := newCluster(token)
+ for name, urls := range urlsmap {
+ m := NewMember(name, urls, token, nil)
+ if _, ok := c.members[m.ID]; ok {
+ return nil, fmt.Errorf("member exists with identical ID %v", m)
+ }
+ if uint64(m.ID) == raft.None {
+ return nil, fmt.Errorf("cannot use %x as member id", raft.None)
+ }
+ c.members[m.ID] = m
+ }
+ c.genID()
+ return c, nil
+}
+
+func newClusterFromMembers(token string, id types.ID, membs []*Member) *cluster {
+ c := newCluster(token)
+ c.id = id
+ for _, m := range membs {
+ c.members[m.ID] = m
+ }
+ return c
+}
+
+func newCluster(token string) *cluster {
+ return &cluster{
+ token: token,
+ members: make(map[types.ID]*Member),
+ removed: make(map[types.ID]bool),
+ }
+}
+
+func (c *cluster) ID() types.ID { return c.id }
+
+func (c *cluster) Members() []*Member {
+ c.Lock()
+ defer c.Unlock()
+ var ms MembersByID
+ for _, m := range c.members {
+ ms = append(ms, m.Clone())
+ }
+ sort.Sort(ms)
+ return []*Member(ms)
+}
+
+func (c *cluster) Member(id types.ID) *Member {
+ c.Lock()
+ defer c.Unlock()
+ return c.members[id].Clone()
+}
+
+// MemberByName returns a Member with the given name if exists.
+// If more than one member has the given name, it will panic.
+func (c *cluster) MemberByName(name string) *Member {
+ c.Lock()
+ defer c.Unlock()
+ var memb *Member
+ for _, m := range c.members {
+ if m.Name == name {
+ if memb != nil {
+ plog.Panicf("two members with the given name %q exist", name)
+ }
+ memb = m
+ }
+ }
+ return memb.Clone()
+}
+
+func (c *cluster) MemberIDs() []types.ID {
+ c.Lock()
+ defer c.Unlock()
+ var ids []types.ID
+ for _, m := range c.members {
+ ids = append(ids, m.ID)
+ }
+ sort.Sort(types.IDSlice(ids))
+ return ids
+}
+
+func (c *cluster) IsIDRemoved(id types.ID) bool {
+ c.Lock()
+ defer c.Unlock()
+ return c.removed[id]
+}
+
+// PeerURLs returns a list of all peer addresses.
+// The returned list is sorted in ascending lexicographical order.
+func (c *cluster) PeerURLs() []string {
+ c.Lock()
+ defer c.Unlock()
+ urls := make([]string, 0)
+ for _, p := range c.members {
+ for _, addr := range p.PeerURLs {
+ urls = append(urls, addr)
+ }
+ }
+ sort.Strings(urls)
+ return urls
+}
+
+// ClientURLs returns a list of all client addresses.
+// The returned list is sorted in ascending lexicographical order.
+func (c *cluster) ClientURLs() []string {
+ c.Lock()
+ defer c.Unlock()
+ urls := make([]string, 0)
+ for _, p := range c.members {
+ for _, url := range p.ClientURLs {
+ urls = append(urls, url)
+ }
+ }
+ sort.Strings(urls)
+ return urls
+}
+
+func (c *cluster) String() string {
+ c.Lock()
+ defer c.Unlock()
+ b := &bytes.Buffer{}
+ fmt.Fprintf(b, "{ClusterID:%s ", c.id)
+ var ms []string
+ for _, m := range c.members {
+ ms = append(ms, fmt.Sprintf("%+v", m))
+ }
+ fmt.Fprintf(b, "Members:[%s] ", strings.Join(ms, " "))
+ var ids []string
+ for id := range c.removed {
+ ids = append(ids, fmt.Sprintf("%s", id))
+ }
+ fmt.Fprintf(b, "RemovedMemberIDs:[%s]}", strings.Join(ids, " "))
+ return b.String()
+}
+
+func (c *cluster) genID() {
+ mIDs := c.MemberIDs()
+ b := make([]byte, 8*len(mIDs))
+ for i, id := range mIDs {
+ binary.BigEndian.PutUint64(b[8*i:], uint64(id))
+ }
+ hash := sha1.Sum(b)
+ c.id = types.ID(binary.BigEndian.Uint64(hash[:8]))
+}
+
+func (c *cluster) SetID(id types.ID) { c.id = id }
+
+func (c *cluster) SetStore(st store.Store) { c.store = st }
+
+func (c *cluster) Recover() {
+ c.members, c.removed = membersFromStore(c.store)
+ c.version = clusterVersionFromStore(c.store)
+ MustDetectDowngrade(c.version)
+
+ for _, m := range c.members {
+ plog.Infof("added member %s %v to cluster %s from store", m.ID, m.PeerURLs, c.id)
+ }
+ if c.version != nil {
+ plog.Infof("set the cluster version to %v from store", version.Cluster(c.version.String()))
+ }
+}
+
+// ValidateConfigurationChange takes a proposed ConfChange and
+// ensures that it is still valid.
+func (c *cluster) ValidateConfigurationChange(cc raftpb.ConfChange) error {
+ members, removed := membersFromStore(c.store)
+ id := types.ID(cc.NodeID)
+ if removed[id] {
+ return ErrIDRemoved
+ }
+ switch cc.Type {
+ case raftpb.ConfChangeAddNode:
+ if members[id] != nil {
+ return ErrIDExists
+ }
+ urls := make(map[string]bool)
+ for _, m := range members {
+ for _, u := range m.PeerURLs {
+ urls[u] = true
+ }
+ }
+ m := new(Member)
+ if err := json.Unmarshal(cc.Context, m); err != nil {
+ plog.Panicf("unmarshal member should never fail: %v", err)
+ }
+ for _, u := range m.PeerURLs {
+ if urls[u] {
+ return ErrPeerURLexists
+ }
+ }
+ case raftpb.ConfChangeRemoveNode:
+ if members[id] == nil {
+ return ErrIDNotFound
+ }
+ case raftpb.ConfChangeUpdateNode:
+ if members[id] == nil {
+ return ErrIDNotFound
+ }
+ urls := make(map[string]bool)
+ for _, m := range members {
+ if m.ID == id {
+ continue
+ }
+ for _, u := range m.PeerURLs {
+ urls[u] = true
+ }
+ }
+ m := new(Member)
+ if err := json.Unmarshal(cc.Context, m); err != nil {
+ plog.Panicf("unmarshal member should never fail: %v", err)
+ }
+ for _, u := range m.PeerURLs {
+ if urls[u] {
+ return ErrPeerURLexists
+ }
+ }
+ default:
+ plog.Panicf("ConfChange type should be either AddNode, RemoveNode or UpdateNode")
+ }
+ return nil
+}
+
+// AddMember adds a new Member into the cluster, and saves the given member's
+// raftAttributes into the store. The given member should have empty attributes.
+// A Member with a matching id must not exist.
+func (c *cluster) AddMember(m *Member) {
+ c.Lock()
+ defer c.Unlock()
+ b, err := json.Marshal(m.RaftAttributes)
+ if err != nil {
+ plog.Panicf("marshal raftAttributes should never fail: %v", err)
+ }
+ p := path.Join(memberStoreKey(m.ID), raftAttributesSuffix)
+ if _, err := c.store.Create(p, false, string(b), false, store.Permanent); err != nil {
+ plog.Panicf("create raftAttributes should never fail: %v", err)
+ }
+ c.members[m.ID] = m
+}
+
+// RemoveMember removes a member from the store.
+// The given id MUST exist, or the function panics.
+func (c *cluster) RemoveMember(id types.ID) {
+ c.Lock()
+ defer c.Unlock()
+ if _, err := c.store.Delete(memberStoreKey(id), true, true); err != nil {
+ plog.Panicf("delete member should never fail: %v", err)
+ }
+ delete(c.members, id)
+ if _, err := c.store.Create(removedMemberStoreKey(id), false, "", false, store.Permanent); err != nil {
+ plog.Panicf("create removedMember should never fail: %v", err)
+ }
+ c.removed[id] = true
+}
+
+func (c *cluster) UpdateAttributes(id types.ID, attr Attributes) bool {
+ c.Lock()
+ defer c.Unlock()
+ if m, ok := c.members[id]; ok {
+ m.Attributes = attr
+ return true
+ }
+ _, ok := c.removed[id]
+ if ok {
+ plog.Warningf("skipped updating attributes of removed member %s", id)
+ } else {
+ plog.Panicf("error updating attributes of unknown member %s", id)
+ }
+ // TODO: update store in this function
+ return false
+}
+
+func (c *cluster) UpdateRaftAttributes(id types.ID, raftAttr RaftAttributes) {
+ c.Lock()
+ defer c.Unlock()
+ b, err := json.Marshal(raftAttr)
+ if err != nil {
+ plog.Panicf("marshal raftAttributes should never fail: %v", err)
+ }
+ p := path.Join(memberStoreKey(id), raftAttributesSuffix)
+ if _, err := c.store.Update(p, string(b), store.Permanent); err != nil {
+ plog.Panicf("update raftAttributes should never fail: %v", err)
+ }
+ c.members[id].RaftAttributes = raftAttr
+}
+
+func (c *cluster) Version() *semver.Version {
+ c.Lock()
+ defer c.Unlock()
+ if c.version == nil {
+ return nil
+ }
+ return semver.Must(semver.NewVersion(c.version.String()))
+}
+
+func (c *cluster) SetVersion(ver *semver.Version) {
+ c.Lock()
+ defer c.Unlock()
+ if c.version != nil {
+ plog.Noticef("updated the cluster version from %v to %v", version.Cluster(c.version.String()), version.Cluster(ver.String()))
+ } else {
+ plog.Noticef("set the initial cluster version to %v", version.Cluster(ver.String()))
+ }
+ c.version = ver
+ MustDetectDowngrade(c.version)
+}
+
+func (c *cluster) isReadyToAddNewMember() bool {
+ nmembers := 1
+ nstarted := 0
+
+ for _, member := range c.members {
+ if member.IsStarted() {
+ nstarted++
+ }
+ nmembers++
+ }
+
+ if nstarted == 1 && nmembers == 2 {
+ // a case of adding a new node to 1-member cluster for restoring cluster data
+ // https://github.com/coreos/etcd/blob/master/Documentation/admin_guide.md#restoring-the-cluster
+
+ plog.Debugf("The number of started member is 1. This cluster can accept add member request.")
+ return true
+ }
+
+ nquorum := nmembers/2 + 1
+ if nstarted < nquorum {
+ plog.Warningf("Reject add member request: the number of started member (%d) will be less than the quorum number of the cluster (%d)", nstarted, nquorum)
+ return false
+ }
+
+ return true
+}
+
+func (c *cluster) isReadyToRemoveMember(id uint64) bool {
+ nmembers := 0
+ nstarted := 0
+
+ for _, member := range c.members {
+ if uint64(member.ID) == id {
+ continue
+ }
+
+ if member.IsStarted() {
+ nstarted++
+ }
+ nmembers++
+ }
+
+ nquorum := nmembers/2 + 1
+ if nstarted < nquorum {
+ plog.Warningf("Reject remove member request: the number of started member (%d) will be less than the quorum number of the cluster (%d)", nstarted, nquorum)
+ return false
+ }
+
+ return true
+}
+
+func membersFromStore(st store.Store) (map[types.ID]*Member, map[types.ID]bool) {
+ members := make(map[types.ID]*Member)
+ removed := make(map[types.ID]bool)
+ e, err := st.Get(storeMembersPrefix, true, true)
+ if err != nil {
+ if isKeyNotFound(err) {
+ return members, removed
+ }
+ plog.Panicf("get storeMembers should never fail: %v", err)
+ }
+ for _, n := range e.Node.Nodes {
+ var m *Member
+ m, err = nodeToMember(n)
+ if err != nil {
+ plog.Panicf("nodeToMember should never fail: %v", err)
+ }
+ members[m.ID] = m
+ }
+
+ e, err = st.Get(storeRemovedMembersPrefix, true, true)
+ if err != nil {
+ if isKeyNotFound(err) {
+ return members, removed
+ }
+ plog.Panicf("get storeRemovedMembers should never fail: %v", err)
+ }
+ for _, n := range e.Node.Nodes {
+ removed[mustParseMemberIDFromKey(n.Key)] = true
+ }
+ return members, removed
+}
+
+func clusterVersionFromStore(st store.Store) *semver.Version {
+ e, err := st.Get(path.Join(StoreClusterPrefix, "version"), false, false)
+ if err != nil {
+ if isKeyNotFound(err) {
+ return nil
+ }
+ plog.Panicf("unexpected error (%v) when getting cluster version from store", err)
+ }
+ return semver.Must(semver.NewVersion(*e.Node.Value))
+}
+
+// ValidateClusterAndAssignIDs validates the local cluster by matching the PeerURLs
+// with the existing cluster. If the validation succeeds, it assigns the IDs
+// from the existing cluster to the local cluster.
+// If the validation fails, an error will be returned.
+func ValidateClusterAndAssignIDs(local *cluster, existing *cluster) error {
+ ems := existing.Members()
+ lms := local.Members()
+ if len(ems) != len(lms) {
+ return fmt.Errorf("member count is unequal")
+ }
+ sort.Sort(MembersByPeerURLs(ems))
+ sort.Sort(MembersByPeerURLs(lms))
+
+ for i := range ems {
+ if !netutil.URLStringsEqual(ems[i].PeerURLs, lms[i].PeerURLs) {
+ return fmt.Errorf("unmatched member while checking PeerURLs")
+ }
+ lms[i].ID = ems[i].ID
+ }
+ local.members = make(map[types.ID]*Member)
+ for _, m := range lms {
+ local.members[m.ID] = m
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/cluster_test.go b/vendor/github.com/coreos/etcd/etcdserver/cluster_test.go
new file mode 100644
index 000000000..9c8550b22
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/cluster_test.go
@@ -0,0 +1,733 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "encoding/json"
+ "fmt"
+ "path"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/store"
+)
+
+func TestClusterMember(t *testing.T) {
+ membs := []*Member{
+ newTestMember(1, nil, "node1", nil),
+ newTestMember(2, nil, "node2", nil),
+ }
+ tests := []struct {
+ id types.ID
+ match bool
+ }{
+ {1, true},
+ {2, true},
+ {3, false},
+ }
+ for i, tt := range tests {
+ c := newTestCluster(membs)
+ m := c.Member(tt.id)
+ if g := m != nil; g != tt.match {
+ t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
+ }
+ if m != nil && m.ID != tt.id {
+ t.Errorf("#%d: id = %x, want %x", i, m.ID, tt.id)
+ }
+ }
+}
+
+func TestClusterMemberByName(t *testing.T) {
+ membs := []*Member{
+ newTestMember(1, nil, "node1", nil),
+ newTestMember(2, nil, "node2", nil),
+ }
+ tests := []struct {
+ name string
+ match bool
+ }{
+ {"node1", true},
+ {"node2", true},
+ {"node3", false},
+ }
+ for i, tt := range tests {
+ c := newTestCluster(membs)
+ m := c.MemberByName(tt.name)
+ if g := m != nil; g != tt.match {
+ t.Errorf("#%d: find member = %v, want %v", i, g, tt.match)
+ }
+ if m != nil && m.Name != tt.name {
+ t.Errorf("#%d: name = %v, want %v", i, m.Name, tt.name)
+ }
+ }
+}
+
+func TestClusterMemberIDs(t *testing.T) {
+ c := newTestCluster([]*Member{
+ newTestMember(1, nil, "", nil),
+ newTestMember(4, nil, "", nil),
+ newTestMember(100, nil, "", nil),
+ })
+ w := []types.ID{1, 4, 100}
+ g := c.MemberIDs()
+ if !reflect.DeepEqual(w, g) {
+ t.Errorf("IDs = %+v, want %+v", g, w)
+ }
+}
+
+func TestClusterPeerURLs(t *testing.T) {
+ tests := []struct {
+ mems []*Member
+ wurls []string
+ }{
+ // single peer with a single address
+ {
+ mems: []*Member{
+ newTestMember(1, []string{"http://192.0.2.1"}, "", nil),
+ },
+ wurls: []string{"http://192.0.2.1"},
+ },
+
+ // single peer with a single address with a port
+ {
+ mems: []*Member{
+ newTestMember(1, []string{"http://192.0.2.1:8001"}, "", nil),
+ },
+ wurls: []string{"http://192.0.2.1:8001"},
+ },
+
+ // several members explicitly unsorted
+ {
+ mems: []*Member{
+ newTestMember(2, []string{"http://192.0.2.3", "http://192.0.2.4"}, "", nil),
+ newTestMember(3, []string{"http://192.0.2.5", "http://192.0.2.6"}, "", nil),
+ newTestMember(1, []string{"http://192.0.2.1", "http://192.0.2.2"}, "", nil),
+ },
+ wurls: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
+ },
+
+ // no members
+ {
+ mems: []*Member{},
+ wurls: []string{},
+ },
+
+ // peer with no peer urls
+ {
+ mems: []*Member{
+ newTestMember(3, []string{}, "", nil),
+ },
+ wurls: []string{},
+ },
+ }
+
+ for i, tt := range tests {
+ c := newTestCluster(tt.mems)
+ urls := c.PeerURLs()
+ if !reflect.DeepEqual(urls, tt.wurls) {
+ t.Errorf("#%d: PeerURLs = %v, want %v", i, urls, tt.wurls)
+ }
+ }
+}
+
+func TestClusterClientURLs(t *testing.T) {
+ tests := []struct {
+ mems []*Member
+ wurls []string
+ }{
+ // single peer with a single address
+ {
+ mems: []*Member{
+ newTestMember(1, nil, "", []string{"http://192.0.2.1"}),
+ },
+ wurls: []string{"http://192.0.2.1"},
+ },
+
+ // single peer with a single address with a port
+ {
+ mems: []*Member{
+ newTestMember(1, nil, "", []string{"http://192.0.2.1:8001"}),
+ },
+ wurls: []string{"http://192.0.2.1:8001"},
+ },
+
+ // several members explicitly unsorted
+ {
+ mems: []*Member{
+ newTestMember(2, nil, "", []string{"http://192.0.2.3", "http://192.0.2.4"}),
+ newTestMember(3, nil, "", []string{"http://192.0.2.5", "http://192.0.2.6"}),
+ newTestMember(1, nil, "", []string{"http://192.0.2.1", "http://192.0.2.2"}),
+ },
+ wurls: []string{"http://192.0.2.1", "http://192.0.2.2", "http://192.0.2.3", "http://192.0.2.4", "http://192.0.2.5", "http://192.0.2.6"},
+ },
+
+ // no members
+ {
+ mems: []*Member{},
+ wurls: []string{},
+ },
+
+ // peer with no client urls
+ {
+ mems: []*Member{
+ newTestMember(3, nil, "", []string{}),
+ },
+ wurls: []string{},
+ },
+ }
+
+ for i, tt := range tests {
+ c := newTestCluster(tt.mems)
+ urls := c.ClientURLs()
+ if !reflect.DeepEqual(urls, tt.wurls) {
+ t.Errorf("#%d: ClientURLs = %v, want %v", i, urls, tt.wurls)
+ }
+ }
+}
+
+func TestClusterValidateAndAssignIDsBad(t *testing.T) {
+ tests := []struct {
+ clmembs []*Member
+ membs []*Member
+ }{
+ {
+ // unmatched length
+ []*Member{
+ newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
+ },
+ []*Member{},
+ },
+ {
+ // unmatched peer urls
+ []*Member{
+ newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
+ },
+ []*Member{
+ newTestMember(1, []string{"http://127.0.0.1:4001"}, "", nil),
+ },
+ },
+ {
+ // unmatched peer urls
+ []*Member{
+ newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
+ newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
+ },
+ []*Member{
+ newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
+ newTestMember(2, []string{"http://127.0.0.2:4001"}, "", nil),
+ },
+ },
+ }
+ for i, tt := range tests {
+ ecl := newTestCluster(tt.clmembs)
+ lcl := newTestCluster(tt.membs)
+ if err := ValidateClusterAndAssignIDs(lcl, ecl); err == nil {
+ t.Errorf("#%d: unexpected update success", i)
+ }
+ }
+}
+
+func TestClusterValidateAndAssignIDs(t *testing.T) {
+ tests := []struct {
+ clmembs []*Member
+ membs []*Member
+ wids []types.ID
+ }{
+ {
+ []*Member{
+ newTestMember(1, []string{"http://127.0.0.1:2379"}, "", nil),
+ newTestMember(2, []string{"http://127.0.0.2:2379"}, "", nil),
+ },
+ []*Member{
+ newTestMember(3, []string{"http://127.0.0.1:2379"}, "", nil),
+ newTestMember(4, []string{"http://127.0.0.2:2379"}, "", nil),
+ },
+ []types.ID{3, 4},
+ },
+ }
+ for i, tt := range tests {
+ lcl := newTestCluster(tt.clmembs)
+ ecl := newTestCluster(tt.membs)
+ if err := ValidateClusterAndAssignIDs(lcl, ecl); err != nil {
+ t.Errorf("#%d: unexpect update error: %v", i, err)
+ }
+ if !reflect.DeepEqual(lcl.MemberIDs(), tt.wids) {
+ t.Errorf("#%d: ids = %v, want %v", i, lcl.MemberIDs(), tt.wids)
+ }
+ }
+}
+
+func TestClusterValidateConfigurationChange(t *testing.T) {
+ cl := newCluster("")
+ cl.SetStore(store.New())
+ for i := 1; i <= 4; i++ {
+ attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", i)}}
+ cl.AddMember(&Member{ID: types.ID(i), RaftAttributes: attr})
+ }
+ cl.RemoveMember(4)
+
+ attr := RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 1)}}
+ ctx, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
+ ctx5, err := json.Marshal(&Member{ID: types.ID(5), RaftAttributes: attr})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 3)}}
+ ctx2to3, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ attr = RaftAttributes{PeerURLs: []string{fmt.Sprintf("http://127.0.0.1:%d", 5)}}
+ ctx2to5, err := json.Marshal(&Member{ID: types.ID(2), RaftAttributes: attr})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tests := []struct {
+ cc raftpb.ConfChange
+ werr error
+ }{
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: 3,
+ },
+ nil,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: 4,
+ },
+ ErrIDRemoved,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: 4,
+ },
+ ErrIDRemoved,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: 1,
+ },
+ ErrIDExists,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: 5,
+ Context: ctx,
+ },
+ ErrPeerURLexists,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: 5,
+ },
+ ErrIDNotFound,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: 5,
+ Context: ctx5,
+ },
+ nil,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode,
+ NodeID: 5,
+ Context: ctx,
+ },
+ ErrIDNotFound,
+ },
+ // try to change the peer url of 2 to the peer url of 3
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode,
+ NodeID: 2,
+ Context: ctx2to3,
+ },
+ ErrPeerURLexists,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode,
+ NodeID: 2,
+ Context: ctx2to5,
+ },
+ nil,
+ },
+ }
+ for i, tt := range tests {
+ err := cl.ValidateConfigurationChange(tt.cc)
+ if err != tt.werr {
+ t.Errorf("#%d: validateConfigurationChange error = %v, want %v", i, err, tt.werr)
+ }
+ }
+}
+
+func TestClusterGenID(t *testing.T) {
+ cs := newTestCluster([]*Member{
+ newTestMember(1, nil, "", nil),
+ newTestMember(2, nil, "", nil),
+ })
+
+ cs.genID()
+ if cs.ID() == 0 {
+ t.Fatalf("cluster.ID = %v, want not 0", cs.ID())
+ }
+ previd := cs.ID()
+
+ cs.SetStore(&storeRecorder{})
+ cs.AddMember(newTestMember(3, nil, "", nil))
+ cs.genID()
+ if cs.ID() == previd {
+ t.Fatalf("cluster.ID = %v, want not %v", cs.ID(), previd)
+ }
+}
+
+func TestNodeToMemberBad(t *testing.T) {
+ tests := []*store.NodeExtern{
+ {Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/strange"},
+ }},
+ {Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/raftAttributes", Value: stringp("garbage")},
+ }},
+ {Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
+ }},
+ {Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
+ {Key: "/1234/strange"},
+ }},
+ {Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
+ {Key: "/1234/attributes", Value: stringp("garbage")},
+ }},
+ {Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
+ {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
+ {Key: "/1234/strange"},
+ }},
+ }
+ for i, tt := range tests {
+ if _, err := nodeToMember(tt); err == nil {
+ t.Errorf("#%d: unexpected nil error", i)
+ }
+ }
+}
+
+func TestClusterAddMember(t *testing.T) {
+ st := &storeRecorder{}
+ c := newTestCluster(nil)
+ c.SetStore(st)
+ c.AddMember(newTestMember(1, nil, "node1", nil))
+
+ wactions := []testutil.Action{
+ {
+ Name: "Create",
+ Params: []interface{}{
+ path.Join(storeMembersPrefix, "1", "raftAttributes"),
+ false,
+ `{"peerURLs":null}`,
+ false,
+ store.Permanent,
+ },
+ },
+ }
+ if g := st.Action(); !reflect.DeepEqual(g, wactions) {
+ t.Errorf("actions = %v, want %v", g, wactions)
+ }
+}
+
+func TestClusterMembers(t *testing.T) {
+ cls := &cluster{
+ members: map[types.ID]*Member{
+ 1: {ID: 1},
+ 20: {ID: 20},
+ 100: {ID: 100},
+ 5: {ID: 5},
+ 50: {ID: 50},
+ },
+ }
+ w := []*Member{
+ {ID: 1},
+ {ID: 5},
+ {ID: 20},
+ {ID: 50},
+ {ID: 100},
+ }
+ if g := cls.Members(); !reflect.DeepEqual(g, w) {
+ t.Fatalf("Members()=%#v, want %#v", g, w)
+ }
+}
+
+func TestClusterRemoveMember(t *testing.T) {
+ st := &storeRecorder{}
+ c := newTestCluster(nil)
+ c.SetStore(st)
+ c.RemoveMember(1)
+
+ wactions := []testutil.Action{
+ {Name: "Delete", Params: []interface{}{memberStoreKey(1), true, true}},
+ {Name: "Create", Params: []interface{}{removedMemberStoreKey(1), false, "", false, store.Permanent}},
+ }
+ if !reflect.DeepEqual(st.Action(), wactions) {
+ t.Errorf("actions = %v, want %v", st.Action(), wactions)
+ }
+}
+
+func TestClusterUpdateAttributes(t *testing.T) {
+ name := "etcd"
+ clientURLs := []string{"http://127.0.0.1:4001"}
+ tests := []struct {
+ mems []*Member
+ removed map[types.ID]bool
+ wmems []*Member
+ }{
+ // update attributes of existing member
+ {
+ []*Member{
+ newTestMember(1, nil, "", nil),
+ },
+ nil,
+ []*Member{
+ newTestMember(1, nil, name, clientURLs),
+ },
+ },
+ // update attributes of removed member
+ {
+ nil,
+ map[types.ID]bool{types.ID(1): true},
+ nil,
+ },
+ }
+ for i, tt := range tests {
+ c := newTestCluster(tt.mems)
+ c.removed = tt.removed
+
+ c.UpdateAttributes(types.ID(1), Attributes{Name: name, ClientURLs: clientURLs})
+ if g := c.Members(); !reflect.DeepEqual(g, tt.wmems) {
+ t.Errorf("#%d: members = %+v, want %+v", i, g, tt.wmems)
+ }
+ }
+}
+
+func TestNodeToMember(t *testing.T) {
+ n := &store.NodeExtern{Key: "/1234", Nodes: []*store.NodeExtern{
+ {Key: "/1234/attributes", Value: stringp(`{"name":"node1","clientURLs":null}`)},
+ {Key: "/1234/raftAttributes", Value: stringp(`{"peerURLs":null}`)},
+ }}
+ wm := &Member{ID: 0x1234, RaftAttributes: RaftAttributes{}, Attributes: Attributes{Name: "node1"}}
+ m, err := nodeToMember(n)
+ if err != nil {
+ t.Fatalf("unexpected nodeToMember error: %v", err)
+ }
+ if !reflect.DeepEqual(m, wm) {
+ t.Errorf("member = %+v, want %+v", m, wm)
+ }
+}
+
+func newTestCluster(membs []*Member) *cluster {
+ c := &cluster{members: make(map[types.ID]*Member), removed: make(map[types.ID]bool)}
+ for _, m := range membs {
+ c.members[m.ID] = m
+ }
+ return c
+}
+
+func stringp(s string) *string { return &s }
+
+func TestIsReadyToAddNewMember(t *testing.T) {
+ tests := []struct {
+ members []*Member
+ want bool
+ }{
+ {
+ // 0/3 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "", nil),
+ newTestMember(2, nil, "", nil),
+ newTestMember(3, nil, "", nil),
+ },
+ false,
+ },
+ {
+ // 1/2 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "", nil),
+ },
+ false,
+ },
+ {
+ // 1/3 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "", nil),
+ newTestMember(3, nil, "", nil),
+ },
+ false,
+ },
+ {
+ // 1/1 members ready, should succeed (special case of 1-member cluster for recovery)
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ },
+ true,
+ },
+ {
+ // 2/3 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "", nil),
+ },
+ false,
+ },
+ {
+ // 3/3 members ready, should be fine to add one member and retain quorum
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "3", nil),
+ },
+ true,
+ },
+ {
+ // 3/4 members ready, should be fine to add one member and retain quorum
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "3", nil),
+ newTestMember(4, nil, "", nil),
+ },
+ true,
+ },
+ {
+ // empty cluster, it is impossible but should fail
+ []*Member{},
+ false,
+ },
+ }
+ for i, tt := range tests {
+ c := newTestCluster(tt.members)
+ if got := c.isReadyToAddNewMember(); got != tt.want {
+ t.Errorf("%d: isReadyToAddNewMember returned %t, want %t", i, got, tt.want)
+ }
+ }
+}
+
+func TestIsReadyToRemoveMember(t *testing.T) {
+ tests := []struct {
+ members []*Member
+ removeID uint64
+ want bool
+ }{
+ {
+ // 1/1 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ },
+ 1,
+ false,
+ },
+ {
+ // 0/3 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "", nil),
+ newTestMember(2, nil, "", nil),
+ newTestMember(3, nil, "", nil),
+ },
+ 1,
+ false,
+ },
+ {
+ // 1/2 members ready, should be fine to remove unstarted member
+ // (iReadyToRemoveMember() logic should return success, but operation itself would fail)
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "", nil),
+ },
+ 2,
+ true,
+ },
+ {
+ // 2/3 members ready, should fail
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "", nil),
+ },
+ 2,
+ false,
+ },
+ {
+ // 3/3 members ready, should be fine to remove one member and retain quorum
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "3", nil),
+ },
+ 3,
+ true,
+ },
+ {
+ // 3/4 members ready, should be fine to remove one member
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "3", nil),
+ newTestMember(4, nil, "", nil),
+ },
+ 3,
+ true,
+ },
+ {
+ // 3/4 members ready, should be fine to remove unstarted member
+ []*Member{
+ newTestMember(1, nil, "1", nil),
+ newTestMember(2, nil, "2", nil),
+ newTestMember(3, nil, "3", nil),
+ newTestMember(4, nil, "", nil),
+ },
+ 4,
+ true,
+ },
+ }
+ for i, tt := range tests {
+ c := newTestCluster(tt.members)
+ if got := c.isReadyToRemoveMember(tt.removeID); got != tt.want {
+ t.Errorf("%d: isReadyToAddNewMember returned %t, want %t", i, got, tt.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/cluster_util.go b/vendor/github.com/coreos/etcd/etcdserver/cluster_util.go
new file mode 100644
index 000000000..60ec32d0f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/cluster_util.go
@@ -0,0 +1,265 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "sort"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/version"
+)
+
+// isMemberBootstrapped tries to check if the given member has been bootstrapped
+// in the given cluster.
+func isMemberBootstrapped(cl *cluster, member string, tr *http.Transport) bool {
+ rcl, err := getClusterFromRemotePeers(getRemotePeerURLs(cl, member), time.Second, false, tr)
+ if err != nil {
+ return false
+ }
+ id := cl.MemberByName(member).ID
+ m := rcl.Member(id)
+ if m == nil {
+ return false
+ }
+ if len(m.ClientURLs) > 0 {
+ return true
+ }
+ return false
+}
+
+// GetClusterFromRemotePeers takes a set of URLs representing etcd peers, and
+// attempts to construct a Cluster by accessing the members endpoint on one of
+// these URLs. The first URL to provide a response is used. If no URLs provide
+// a response, or a Cluster cannot be successfully created from a received
+// response, an error is returned.
+// Each request has a 10-second timeout. Because the upper limit of TTL is 5s,
+// 10 second is enough for building connection and finishing request.
+func GetClusterFromRemotePeers(urls []string, tr *http.Transport) (*cluster, error) {
+ return getClusterFromRemotePeers(urls, 10*time.Second, true, tr)
+}
+
+// If logerr is true, it prints out more error messages.
+func getClusterFromRemotePeers(urls []string, timeout time.Duration, logerr bool, tr *http.Transport) (*cluster, error) {
+ cc := &http.Client{
+ Transport: tr,
+ Timeout: timeout,
+ }
+ for _, u := range urls {
+ resp, err := cc.Get(u + "/members")
+ if err != nil {
+ if logerr {
+ plog.Warningf("could not get cluster response from %s: %v", u, err)
+ }
+ continue
+ }
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ if logerr {
+ plog.Warningf("could not read the body of cluster response: %v", err)
+ }
+ continue
+ }
+ var membs []*Member
+ if err := json.Unmarshal(b, &membs); err != nil {
+ if logerr {
+ plog.Warningf("could not unmarshal cluster response: %v", err)
+ }
+ continue
+ }
+ id, err := types.IDFromString(resp.Header.Get("X-Etcd-Cluster-ID"))
+ if err != nil {
+ if logerr {
+ plog.Warningf("could not parse the cluster ID from cluster res: %v", err)
+ }
+ continue
+ }
+ return newClusterFromMembers("", id, membs), nil
+ }
+ return nil, fmt.Errorf("could not retrieve cluster information from the given urls")
+}
+
+// getRemotePeerURLs returns peer urls of remote members in the cluster. The
+// returned list is sorted in ascending lexicographical order.
+func getRemotePeerURLs(cl Cluster, local string) []string {
+ us := make([]string, 0)
+ for _, m := range cl.Members() {
+ if m.Name == local {
+ continue
+ }
+ us = append(us, m.PeerURLs...)
+ }
+ sort.Strings(us)
+ return us
+}
+
+// getVersions returns the versions of the members in the given cluster.
+// The key of the returned map is the member's ID. The value of the returned map
+// is the semver versions string, including server and cluster.
+// If it fails to get the version of a member, the key will be nil.
+func getVersions(cl Cluster, local types.ID, tr *http.Transport) map[string]*version.Versions {
+ members := cl.Members()
+ vers := make(map[string]*version.Versions)
+ for _, m := range members {
+ if m.ID == local {
+ cv := "not_decided"
+ if cl.Version() != nil {
+ cv = cl.Version().String()
+ }
+ vers[m.ID.String()] = &version.Versions{Server: version.Version, Cluster: cv}
+ continue
+ }
+ ver, err := getVersion(m, tr)
+ if err != nil {
+ plog.Warningf("cannot get the version of member %s (%v)", m.ID, err)
+ vers[m.ID.String()] = nil
+ } else {
+ vers[m.ID.String()] = ver
+ }
+ }
+ return vers
+}
+
+// decideClusterVersion decides the cluster version based on the versions map.
+// The returned version is the min server version in the map, or nil if the min
+// version in unknown.
+func decideClusterVersion(vers map[string]*version.Versions) *semver.Version {
+ var cv *semver.Version
+ lv := semver.Must(semver.NewVersion(version.Version))
+
+ for mid, ver := range vers {
+ if ver == nil {
+ return nil
+ }
+ v, err := semver.NewVersion(ver.Server)
+ if err != nil {
+ plog.Errorf("cannot understand the version of member %s (%v)", mid, err)
+ return nil
+ }
+ if lv.LessThan(*v) {
+ plog.Warningf("the local etcd version %s is not up-to-date", lv.String())
+ plog.Warningf("member %s has a higher version %s", mid, ver.Server)
+ }
+ if cv == nil {
+ cv = v
+ } else if v.LessThan(*cv) {
+ cv = v
+ }
+ }
+ return cv
+}
+
+// isCompatibleWithCluster return true if the local member has a compitable version with
+// the current running cluster.
+// The version is considered as compitable when at least one of the other members in the cluster has a
+// cluster version in the range of [MinClusterVersion, Version] and no known members has a cluster version
+// out of the range.
+// We set this rule since when the local member joins, another member might be offline.
+func isCompatibleWithCluster(cl Cluster, local types.ID, tr *http.Transport) bool {
+ vers := getVersions(cl, local, tr)
+ minV := semver.Must(semver.NewVersion(version.MinClusterVersion))
+ maxV := semver.Must(semver.NewVersion(version.Version))
+ maxV = &semver.Version{
+ Major: maxV.Major,
+ Minor: maxV.Minor,
+ }
+
+ return isCompatibleWithVers(vers, local, minV, maxV)
+}
+
+func isCompatibleWithVers(vers map[string]*version.Versions, local types.ID, minV, maxV *semver.Version) bool {
+ var ok bool
+ for id, v := range vers {
+ // ignore comparasion with local version
+ if id == local.String() {
+ continue
+ }
+ if v == nil {
+ continue
+ }
+ clusterv, err := semver.NewVersion(v.Cluster)
+ if err != nil {
+ plog.Errorf("cannot understand the cluster version of member %s (%v)", id, err)
+ continue
+ }
+ if clusterv.LessThan(*minV) {
+ plog.Warningf("the running cluster version(%v) is lower than the minimal cluster version(%v) supported", clusterv.String(), minV.String())
+ return false
+ }
+ if maxV.LessThan(*clusterv) {
+ plog.Warningf("the running cluster version(%v) is higher than the maximum cluster version(%v) supported", clusterv.String(), maxV.String())
+ return false
+ }
+ ok = true
+ }
+ return ok
+}
+
+// getVersion returns the Versions of the given member via its
+// peerURLs. Returns the last error if it fails to get the version.
+func getVersion(m *Member, tr *http.Transport) (*version.Versions, error) {
+ cc := &http.Client{
+ Transport: tr,
+ }
+ var (
+ err error
+ resp *http.Response
+ )
+
+ for _, u := range m.PeerURLs {
+ resp, err = cc.Get(u + "/version")
+ if err != nil {
+ plog.Warningf("failed to reach the peerURL(%s) of member %s (%v)", u, m.ID, err)
+ continue
+ }
+ // etcd 2.0 does not have version endpoint on peer url.
+ if resp.StatusCode == http.StatusNotFound {
+ resp.Body.Close()
+ return &version.Versions{
+ Server: "2.0.0",
+ Cluster: "2.0.0",
+ }, nil
+ }
+
+ var b []byte
+ b, err = ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ plog.Warningf("failed to read out the response body from the peerURL(%s) of member %s (%v)", u, m.ID, err)
+ continue
+ }
+ var vers version.Versions
+ if err := json.Unmarshal(b, &vers); err != nil {
+ plog.Warningf("failed to unmarshal the response body got from the peerURL(%s) of member %s (%v)", u, m.ID, err)
+ continue
+ }
+ return &vers, nil
+ }
+ return nil, err
+}
+
+func MustDetectDowngrade(cv *semver.Version) {
+ lv := semver.Must(semver.NewVersion(version.Version))
+ // only keep major.minor version for comparison against cluster version
+ lv = &semver.Version{Major: lv.Major, Minor: lv.Minor}
+ if cv != nil && lv.LessThan(*cv) {
+ plog.Fatalf("cluster cannot be downgraded (current version: %s is lower than determined cluster version: %s).", version.Version, version.Cluster(cv.String()))
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/cluster_util_test.go b/vendor/github.com/coreos/etcd/etcdserver/cluster_util_test.go
new file mode 100644
index 000000000..e602bddad
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/cluster_util_test.go
@@ -0,0 +1,131 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/version"
+)
+
+func TestDecideClusterVersion(t *testing.T) {
+ tests := []struct {
+ vers map[string]*version.Versions
+ wdver *semver.Version
+ }{
+ {
+ map[string]*version.Versions{"a": {Server: "2.0.0"}},
+ semver.Must(semver.NewVersion("2.0.0")),
+ },
+ // unknow
+ {
+ map[string]*version.Versions{"a": nil},
+ nil,
+ },
+ {
+ map[string]*version.Versions{"a": {Server: "2.0.0"}, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}},
+ semver.Must(semver.NewVersion("2.0.0")),
+ },
+ {
+ map[string]*version.Versions{"a": {Server: "2.1.0"}, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}},
+ semver.Must(semver.NewVersion("2.1.0")),
+ },
+ {
+ map[string]*version.Versions{"a": nil, "b": {Server: "2.1.0"}, "c": {Server: "2.1.0"}},
+ nil,
+ },
+ }
+
+ for i, tt := range tests {
+ dver := decideClusterVersion(tt.vers)
+ if !reflect.DeepEqual(dver, tt.wdver) {
+ t.Errorf("#%d: ver = %+v, want %+v", i, dver, tt.wdver)
+ }
+ }
+}
+
+func TestIsCompatibleWithVers(t *testing.T) {
+ tests := []struct {
+ vers map[string]*version.Versions
+ local types.ID
+ minV, maxV *semver.Version
+ wok bool
+ }{
+ // too low
+ {
+ map[string]*version.Versions{
+ "a": {Server: "2.0.0", Cluster: "not_decided"},
+ "b": {Server: "2.1.0", Cluster: "2.1.0"},
+ "c": {Server: "2.1.0", Cluster: "2.1.0"},
+ },
+ 0xa,
+ semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.0.0")),
+ false,
+ },
+ {
+ map[string]*version.Versions{
+ "a": {Server: "2.1.0", Cluster: "not_decided"},
+ "b": {Server: "2.1.0", Cluster: "2.1.0"},
+ "c": {Server: "2.1.0", Cluster: "2.1.0"},
+ },
+ 0xa,
+ semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")),
+ true,
+ },
+ // too high
+ {
+ map[string]*version.Versions{
+ "a": {Server: "2.2.0", Cluster: "not_decided"},
+ "b": {Server: "2.0.0", Cluster: "2.0.0"},
+ "c": {Server: "2.0.0", Cluster: "2.0.0"},
+ },
+ 0xa,
+ semver.Must(semver.NewVersion("2.1.0")), semver.Must(semver.NewVersion("2.2.0")),
+ false,
+ },
+ // cannot get b's version, expect ok
+ {
+ map[string]*version.Versions{
+ "a": {Server: "2.1.0", Cluster: "not_decided"},
+ "b": nil,
+ "c": {Server: "2.1.0", Cluster: "2.1.0"},
+ },
+ 0xa,
+ semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")),
+ true,
+ },
+ // cannot get b and c's version, expect not ok
+ {
+ map[string]*version.Versions{
+ "a": {Server: "2.1.0", Cluster: "not_decided"},
+ "b": nil,
+ "c": nil,
+ },
+ 0xa,
+ semver.Must(semver.NewVersion("2.0.0")), semver.Must(semver.NewVersion("2.1.0")),
+ false,
+ },
+ }
+
+ for i, tt := range tests {
+ ok := isCompatibleWithVers(tt.vers, tt.local, tt.minV, tt.maxV)
+ if ok != tt.wok {
+ t.Errorf("#%d: ok = %+v, want %+v", i, ok, tt.wok)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/config.go b/vendor/github.com/coreos/etcd/etcdserver/config.go
new file mode 100644
index 000000000..ff28e4cff
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/config.go
@@ -0,0 +1,183 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "fmt"
+ "path"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/pkg/netutil"
+ "github.com/coreos/etcd/pkg/transport"
+ "github.com/coreos/etcd/pkg/types"
+)
+
+// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
+type ServerConfig struct {
+ Name string
+ DiscoveryURL string
+ DiscoveryProxy string
+ ClientURLs types.URLs
+ PeerURLs types.URLs
+ DataDir string
+ // DedicatedWALDir config will make the etcd to write the WAL to the WALDir
+ // rather than the dataDir/member/wal.
+ DedicatedWALDir string
+ SnapCount uint64
+ MaxSnapFiles uint
+ MaxWALFiles uint
+ InitialPeerURLsMap types.URLsMap
+ InitialClusterToken string
+ NewCluster bool
+ ForceNewCluster bool
+ PeerTLSInfo transport.TLSInfo
+
+ TickMs uint
+ ElectionTicks int
+
+ V3demo bool
+
+ StrictReconfigCheck bool
+}
+
+// VerifyBootstrapConfig sanity-checks the initial config for bootstrap case
+// and returns an error for things that should never happen.
+func (c *ServerConfig) VerifyBootstrap() error {
+ if err := c.verifyLocalMember(true); err != nil {
+ return err
+ }
+ if checkDuplicateURL(c.InitialPeerURLsMap) {
+ return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
+ }
+ if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
+ return fmt.Errorf("initial cluster unset and no discovery URL found")
+ }
+ return nil
+}
+
+// VerifyJoinExisting sanity-checks the initial config for join existing cluster
+// case and returns an error for things that should never happen.
+func (c *ServerConfig) VerifyJoinExisting() error {
+ // no need for strict checking since the member have announced its
+ // peer urls to the cluster before starting and do not have to set
+ // it in the configuration again.
+ if err := c.verifyLocalMember(false); err != nil {
+ return err
+ }
+ if checkDuplicateURL(c.InitialPeerURLsMap) {
+ return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
+ }
+ if c.DiscoveryURL != "" {
+ return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
+ }
+ return nil
+}
+
+// verifyLocalMember verifies the configured member is in configured
+// cluster. If strict is set, it also verifies the configured member
+// has the same peer urls as configured advertised peer urls.
+func (c *ServerConfig) verifyLocalMember(strict bool) error {
+ urls := c.InitialPeerURLsMap[c.Name]
+ // Make sure the cluster at least contains the local server.
+ if urls == nil {
+ return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
+ }
+
+ // Advertised peer URLs must match those in the cluster peer list
+ apurls := c.PeerURLs.StringSlice()
+ sort.Strings(apurls)
+ urls.Sort()
+ if strict {
+ if !netutil.URLStringsEqual(apurls, urls.StringSlice()) {
+ umap := map[string]types.URLs{c.Name: c.PeerURLs}
+ return fmt.Errorf("--initial-cluster must include %s given --initial-advertise-peer-urls=%s", types.URLsMap(umap).String(), strings.Join(apurls, ","))
+ }
+ }
+ return nil
+}
+
+func (c *ServerConfig) MemberDir() string { return path.Join(c.DataDir, "member") }
+
+func (c *ServerConfig) WALDir() string {
+ if c.DedicatedWALDir != "" {
+ return c.DedicatedWALDir
+ }
+ return path.Join(c.MemberDir(), "wal")
+}
+
+func (c *ServerConfig) SnapDir() string { return path.Join(c.MemberDir(), "snap") }
+
+func (c *ServerConfig) StorageDir() string { return path.Join(c.MemberDir(), "storage") }
+
+func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }
+
+// ReqTimeout returns timeout for request to finish.
+func (c *ServerConfig) ReqTimeout() time.Duration {
+ // 5s for queue waiting, computation and disk IO delay
+ // + 2 * election timeout for possible leader election
+ return 5*time.Second + 2*time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond
+}
+
+func (c *ServerConfig) peerDialTimeout() time.Duration {
+ // 1s for queue wait and system delay
+ // + one RTT, which is smaller than 1/5 election timeout
+ return time.Second + time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond/5
+}
+
+func (c *ServerConfig) PrintWithInitial() { c.print(true) }
+
+func (c *ServerConfig) Print() { c.print(false) }
+
+func (c *ServerConfig) print(initial bool) {
+ plog.Infof("name = %s", c.Name)
+ if c.ForceNewCluster {
+ plog.Infof("force new cluster")
+ }
+ plog.Infof("data dir = %s", c.DataDir)
+ plog.Infof("member dir = %s", c.MemberDir())
+ if c.DedicatedWALDir != "" {
+ plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir)
+ }
+ plog.Infof("heartbeat = %dms", c.TickMs)
+ plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs))
+ plog.Infof("snapshot count = %d", c.SnapCount)
+ if len(c.DiscoveryURL) != 0 {
+ plog.Infof("discovery URL= %s", c.DiscoveryURL)
+ if len(c.DiscoveryProxy) != 0 {
+ plog.Infof("discovery proxy = %s", c.DiscoveryProxy)
+ }
+ }
+ plog.Infof("advertise client URLs = %s", c.ClientURLs)
+ if initial {
+ plog.Infof("initial advertise peer URLs = %s", c.PeerURLs)
+ plog.Infof("initial cluster = %s", c.InitialPeerURLsMap)
+ }
+}
+
+func checkDuplicateURL(urlsmap types.URLsMap) bool {
+ um := make(map[string]bool)
+ for _, urls := range urlsmap {
+ for _, url := range urls {
+ u := url.String()
+ if um[u] {
+ return true
+ }
+ um[u] = true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/config_test.go b/vendor/github.com/coreos/etcd/etcdserver/config_test.go
new file mode 100644
index 000000000..b04252dfc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/config_test.go
@@ -0,0 +1,194 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "net/url"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+func mustNewURLs(t *testing.T, urls []string) []url.URL {
+ if len(urls) == 0 {
+ return nil
+ }
+ u, err := types.NewURLs(urls)
+ if err != nil {
+ t.Fatalf("error creating new URLs from %q: %v", urls, err)
+ }
+ return u
+}
+
+func TestConfigVerifyBootstrapWithoutClusterAndDiscoveryURLFail(t *testing.T) {
+ c := &ServerConfig{
+ Name: "node1",
+ DiscoveryURL: "",
+ InitialPeerURLsMap: types.URLsMap{},
+ }
+ if err := c.VerifyBootstrap(); err == nil {
+ t.Errorf("err = nil, want not nil")
+ }
+}
+
+func TestConfigVerifyExistingWithDiscoveryURLFail(t *testing.T) {
+ cluster, err := types.NewURLsMap("node1=http://127.0.0.1:2380")
+ if err != nil {
+ t.Fatalf("NewCluster error: %v", err)
+ }
+ c := &ServerConfig{
+ Name: "node1",
+ DiscoveryURL: "http://127.0.0.1:2379/abcdefg",
+ PeerURLs: mustNewURLs(t, []string{"http://127.0.0.1:2380"}),
+ InitialPeerURLsMap: cluster,
+ NewCluster: false,
+ }
+ if err := c.VerifyJoinExisting(); err == nil {
+ t.Errorf("err = nil, want not nil")
+ }
+}
+
+func TestConfigVerifyLocalMember(t *testing.T) {
+ tests := []struct {
+ clusterSetting string
+ apurls []string
+ strict bool
+ shouldError bool
+ }{
+ {
+ // Node must exist in cluster
+ "",
+ nil,
+ true,
+
+ true,
+ },
+ {
+ // Initial cluster set
+ "node1=http://localhost:7001,node2=http://localhost:7002",
+ []string{"http://localhost:7001"},
+ true,
+
+ false,
+ },
+ {
+ // Default initial cluster
+ "node1=http://localhost:2380,node1=http://localhost:7001",
+ []string{"http://localhost:2380", "http://localhost:7001"},
+ true,
+
+ false,
+ },
+ {
+ // Advertised peer URLs must match those in cluster-state
+ "node1=http://localhost:7001",
+ []string{"http://localhost:12345"},
+ true,
+
+ true,
+ },
+ {
+ // Advertised peer URLs must match those in cluster-state
+ "node1=http://localhost:2380,node1=http://localhost:12345",
+ []string{"http://localhost:12345"},
+ true,
+
+ true,
+ },
+ {
+ // Advertised peer URLs must match those in cluster-state
+ "node1=http://localhost:2380",
+ []string{},
+ true,
+
+ true,
+ },
+ {
+ // do not care about the urls if strict is not set
+ "node1=http://localhost:2380",
+ []string{},
+ false,
+
+ false,
+ },
+ }
+
+ for i, tt := range tests {
+ cluster, err := types.NewURLsMap(tt.clusterSetting)
+ if err != nil {
+ t.Fatalf("#%d: Got unexpected error: %v", i, err)
+ }
+ cfg := ServerConfig{
+ Name: "node1",
+ InitialPeerURLsMap: cluster,
+ }
+ if tt.apurls != nil {
+ cfg.PeerURLs = mustNewURLs(t, tt.apurls)
+ }
+ err = cfg.verifyLocalMember(tt.strict)
+ if (err == nil) && tt.shouldError {
+ t.Errorf("#%d: Got no error where one was expected", i)
+ }
+ if (err != nil) && !tt.shouldError {
+ t.Errorf("#%d: Got unexpected error: %v", i, err)
+ }
+ }
+}
+
+func TestSnapDir(t *testing.T) {
+ tests := map[string]string{
+ "/": "/member/snap",
+ "/var/lib/etc": "/var/lib/etc/member/snap",
+ }
+ for dd, w := range tests {
+ cfg := ServerConfig{
+ DataDir: dd,
+ }
+ if g := cfg.SnapDir(); g != w {
+ t.Errorf("DataDir=%q: SnapDir()=%q, want=%q", dd, g, w)
+ }
+ }
+}
+
+func TestWALDir(t *testing.T) {
+ tests := map[string]string{
+ "/": "/member/wal",
+ "/var/lib/etc": "/var/lib/etc/member/wal",
+ }
+ for dd, w := range tests {
+ cfg := ServerConfig{
+ DataDir: dd,
+ }
+ if g := cfg.WALDir(); g != w {
+ t.Errorf("DataDir=%q: WALDir()=%q, want=%q", dd, g, w)
+ }
+ }
+}
+
+func TestShouldDiscover(t *testing.T) {
+ tests := map[string]bool{
+ "": false,
+ "foo": true,
+ "http://discovery.etcd.io/asdf": true,
+ }
+ for durl, w := range tests {
+ cfg := ServerConfig{
+ DiscoveryURL: durl,
+ }
+ if g := cfg.ShouldDiscover(); g != w {
+ t.Errorf("durl=%q: ShouldDiscover()=%t, want=%t", durl, g, w)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/errors.go b/vendor/github.com/coreos/etcd/etcdserver/errors.go
new file mode 100644
index 000000000..0f1c2043c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/errors.go
@@ -0,0 +1,55 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "errors"
+ "fmt"
+
+ etcdErr "github.com/coreos/etcd/error"
+)
+
+var (
+ ErrUnknownMethod = errors.New("etcdserver: unknown method")
+ ErrStopped = errors.New("etcdserver: server stopped")
+ ErrIDRemoved = errors.New("etcdserver: ID removed")
+ ErrIDExists = errors.New("etcdserver: ID exists")
+ ErrIDNotFound = errors.New("etcdserver: ID not found")
+ ErrPeerURLexists = errors.New("etcdserver: peerURL exists")
+ ErrCanceled = errors.New("etcdserver: request cancelled")
+ ErrTimeout = errors.New("etcdserver: request timed out")
+ ErrTimeoutDueToLeaderFail = errors.New("etcdserver: request timed out, possibly due to previous leader failure")
+ ErrTimeoutDueToConnectionLost = errors.New("etcdserver: request timed out, possibly due to connection lost")
+ ErrNotEnoughStartedMembers = errors.New("etcdserver: re-configuration failed due to not enough started members")
+)
+
+func isKeyNotFound(err error) bool {
+ e, ok := err.(*etcdErr.Error)
+ return ok && e.ErrorCode == etcdErr.EcodeKeyNotFound
+}
+
+type discoveryError struct {
+ op string
+ err error
+}
+
+func (e discoveryError) Error() string {
+ return fmt.Sprintf("failed to %s discovery cluster (%v)", e.op, e.err)
+}
+
+func IsDiscoveryError(err error) bool {
+ _, ok := err.(*discoveryError)
+ return ok
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/capability.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/capability.go
new file mode 100644
index 000000000..d69bea032
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/capability.go
@@ -0,0 +1,99 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "fmt"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+)
+
+type capability string
+
+const (
+ authCapability capability = "auth"
+)
+
+var (
+ // capabilityMap is a static map of version to capability map.
+ // the base capabilities is the set of capability 2.0 supports.
+ capabilityMaps = map[string]map[capability]bool{
+ "2.1.0": {authCapability: true},
+ "2.2.0": {authCapability: true},
+ }
+
+ enableMapMu sync.Mutex
+ // enabled points to a map in cpapbilityMaps
+ enabledMap map[capability]bool
+)
+
+// capabilityLoop checks the cluster version every 500ms and updates
+// the enabledCapability when the cluster version increased.
+// capabilityLoop MUST be ran in a goroutine before checking capability
+// or using capabilityHandler.
+func capabilityLoop(s *etcdserver.EtcdServer) {
+ stopped := s.StopNotify()
+
+ var pv *semver.Version
+ for {
+ if v := s.ClusterVersion(); v != pv {
+ if pv == nil {
+ pv = v
+ } else if v != nil && pv.LessThan(*v) {
+ pv = v
+ }
+ enableMapMu.Lock()
+ enabledMap = capabilityMaps[pv.String()]
+ enableMapMu.Unlock()
+ }
+
+ select {
+ case <-stopped:
+ return
+ case <-time.After(500 * time.Millisecond):
+ }
+ }
+}
+
+func isCapabilityEnabled(c capability) bool {
+ enableMapMu.Lock()
+ defer enableMapMu.Unlock()
+ if enabledMap == nil {
+ return false
+ }
+ return enabledMap[c]
+}
+
+func capabilityHandler(c capability, fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if !isCapabilityEnabled(c) {
+ notCapable(w, r, c)
+ return
+ }
+ fn(w, r)
+ }
+}
+
+func notCapable(w http.ResponseWriter, r *http.Request, c capability) {
+ herr := httptypes.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("Not capable of accessing %s feature during rolling upgrades.", c))
+ if err := herr.WriteTo(w); err != nil {
+ plog.Debugf("error writing HTTPError (%v) to %s", err, r.RemoteAddr)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client.go
new file mode 100644
index 000000000..d8082769a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client.go
@@ -0,0 +1,777 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "encoding/json"
+ "errors"
+ "expvar"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "path"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ etcdErr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/auth"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/store"
+ "github.com/coreos/etcd/version"
+)
+
+const (
+ authPrefix = "/v2/auth"
+ keysPrefix = "/v2/keys"
+ deprecatedMachinesPrefix = "/v2/machines"
+ membersPrefix = "/v2/members"
+ statsPrefix = "/v2/stats"
+ varsPath = "/debug/vars"
+ metricsPath = "/metrics"
+ healthPath = "/health"
+ versionPath = "/version"
+ configPath = "/config"
+)
+
+// NewClientHandler generates a muxed http.Handler with the given parameters to serve etcd client requests.
+func NewClientHandler(server *etcdserver.EtcdServer, timeout time.Duration) http.Handler {
+ go capabilityLoop(server)
+
+ sec := auth.NewStore(server, timeout)
+
+ kh := &keysHandler{
+ sec: sec,
+ server: server,
+ cluster: server.Cluster(),
+ timer: server,
+ timeout: timeout,
+ }
+
+ sh := &statsHandler{
+ stats: server,
+ }
+
+ mh := &membersHandler{
+ sec: sec,
+ server: server,
+ cluster: server.Cluster(),
+ timeout: timeout,
+ clock: clockwork.NewRealClock(),
+ }
+
+ dmh := &deprecatedMachinesHandler{
+ cluster: server.Cluster(),
+ }
+
+ sech := &authHandler{
+ sec: sec,
+ cluster: server.Cluster(),
+ }
+
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", http.NotFound)
+ mux.Handle(healthPath, healthHandler(server))
+ mux.HandleFunc(versionPath, versionHandler(server.Cluster(), serveVersion))
+ mux.Handle(keysPrefix, kh)
+ mux.Handle(keysPrefix+"/", kh)
+ mux.HandleFunc(statsPrefix+"/store", sh.serveStore)
+ mux.HandleFunc(statsPrefix+"/self", sh.serveSelf)
+ mux.HandleFunc(statsPrefix+"/leader", sh.serveLeader)
+ mux.HandleFunc(varsPath, serveVars)
+ mux.HandleFunc(configPath+"/local/log", logHandleFunc)
+ mux.Handle(metricsPath, prometheus.Handler())
+ mux.Handle(membersPrefix, mh)
+ mux.Handle(membersPrefix+"/", mh)
+ mux.Handle(deprecatedMachinesPrefix, dmh)
+ handleAuth(mux, sech)
+
+ return requestLogger(mux)
+}
+
+type keysHandler struct {
+ sec auth.Store
+ server etcdserver.Server
+ cluster etcdserver.Cluster
+ timer etcdserver.RaftTimer
+ timeout time.Duration
+}
+
+func (h *keysHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "HEAD", "GET", "PUT", "POST", "DELETE") {
+ return
+ }
+
+ w.Header().Set("X-Etcd-Cluster-ID", h.cluster.ID().String())
+
+ ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
+ defer cancel()
+ clock := clockwork.NewRealClock()
+ startTime := clock.Now()
+ rr, err := parseKeyRequest(r, clock)
+ if err != nil {
+ writeKeyError(w, err)
+ return
+ }
+ // The path must be valid at this point (we've parsed the request successfully).
+ if !hasKeyPrefixAccess(h.sec, r, r.URL.Path[len(keysPrefix):], rr.Recursive) {
+ writeKeyNoAuth(w)
+ return
+ }
+ if !rr.Wait {
+ reportRequestReceived(rr)
+ }
+ resp, err := h.server.Do(ctx, rr)
+ if err != nil {
+ err = trimErrorPrefix(err, etcdserver.StoreKeysPrefix)
+ writeKeyError(w, err)
+ reportRequestFailed(rr, err)
+ return
+ }
+ switch {
+ case resp.Event != nil:
+ if err := writeKeyEvent(w, resp.Event, h.timer); err != nil {
+ // Should never be reached
+ plog.Errorf("error writing event (%v)", err)
+ }
+ reportRequestCompleted(rr, resp, startTime)
+ case resp.Watcher != nil:
+ ctx, cancel := context.WithTimeout(context.Background(), defaultWatchTimeout)
+ defer cancel()
+ handleKeyWatch(ctx, w, resp.Watcher, rr.Stream, h.timer)
+ default:
+ writeKeyError(w, errors.New("received response with no Event/Watcher!"))
+ }
+}
+
+type deprecatedMachinesHandler struct {
+ cluster etcdserver.Cluster
+}
+
+func (h *deprecatedMachinesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET", "HEAD") {
+ return
+ }
+ endpoints := h.cluster.ClientURLs()
+ w.Write([]byte(strings.Join(endpoints, ", ")))
+}
+
+type membersHandler struct {
+ sec auth.Store
+ server etcdserver.Server
+ cluster etcdserver.Cluster
+ timeout time.Duration
+ clock clockwork.Clock
+}
+
+func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET", "POST", "DELETE", "PUT") {
+ return
+ }
+ if !hasWriteRootAccess(h.sec, r) {
+ writeNoAuth(w, r)
+ return
+ }
+ w.Header().Set("X-Etcd-Cluster-ID", h.cluster.ID().String())
+
+ ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
+ defer cancel()
+
+ switch r.Method {
+ case "GET":
+ switch trimPrefix(r.URL.Path, membersPrefix) {
+ case "":
+ mc := newMemberCollection(h.cluster.Members())
+ w.Header().Set("Content-Type", "application/json")
+ if err := json.NewEncoder(w).Encode(mc); err != nil {
+ plog.Warningf("failed to encode members response (%v)", err)
+ }
+ case "leader":
+ id := h.server.Leader()
+ if id == 0 {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusServiceUnavailable, "During election"))
+ return
+ }
+ m := newMember(h.cluster.Member(id))
+ w.Header().Set("Content-Type", "application/json")
+ if err := json.NewEncoder(w).Encode(m); err != nil {
+ plog.Warningf("failed to encode members response (%v)", err)
+ }
+ default:
+ writeError(w, r, httptypes.NewHTTPError(http.StatusNotFound, "Not found"))
+ }
+ case "POST":
+ req := httptypes.MemberCreateRequest{}
+ if ok := unmarshalRequest(r, &req, w); !ok {
+ return
+ }
+ now := h.clock.Now()
+ m := etcdserver.NewMember("", req.PeerURLs, "", &now)
+ err := h.server.AddMember(ctx, *m)
+ switch {
+ case err == etcdserver.ErrIDExists || err == etcdserver.ErrPeerURLexists:
+ writeError(w, r, httptypes.NewHTTPError(http.StatusConflict, err.Error()))
+ return
+ case err != nil:
+ plog.Errorf("error adding member %s (%v)", m.ID, err)
+ writeError(w, r, err)
+ return
+ }
+ res := newMember(m)
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ if err := json.NewEncoder(w).Encode(res); err != nil {
+ plog.Warningf("failed to encode members response (%v)", err)
+ }
+ case "DELETE":
+ id, ok := getID(r.URL.Path, w)
+ if !ok {
+ return
+ }
+ err := h.server.RemoveMember(ctx, uint64(id))
+ switch {
+ case err == etcdserver.ErrIDRemoved:
+ writeError(w, r, httptypes.NewHTTPError(http.StatusGone, fmt.Sprintf("Member permanently removed: %s", id)))
+ case err == etcdserver.ErrIDNotFound:
+ writeError(w, r, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id)))
+ case err != nil:
+ plog.Errorf("error removing member %s (%v)", id, err)
+ writeError(w, r, err)
+ default:
+ w.WriteHeader(http.StatusNoContent)
+ }
+ case "PUT":
+ id, ok := getID(r.URL.Path, w)
+ if !ok {
+ return
+ }
+ req := httptypes.MemberUpdateRequest{}
+ if ok := unmarshalRequest(r, &req, w); !ok {
+ return
+ }
+ m := etcdserver.Member{
+ ID: id,
+ RaftAttributes: etcdserver.RaftAttributes{PeerURLs: req.PeerURLs.StringSlice()},
+ }
+ err := h.server.UpdateMember(ctx, m)
+ switch {
+ case err == etcdserver.ErrPeerURLexists:
+ writeError(w, r, httptypes.NewHTTPError(http.StatusConflict, err.Error()))
+ case err == etcdserver.ErrIDNotFound:
+ writeError(w, r, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", id)))
+ case err != nil:
+ plog.Errorf("error updating member %s (%v)", m.ID, err)
+ writeError(w, r, err)
+ default:
+ w.WriteHeader(http.StatusNoContent)
+ }
+ }
+}
+
+type statsHandler struct {
+ stats stats.Stats
+}
+
+func (h *statsHandler) serveStore(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write(h.stats.StoreStats())
+}
+
+func (h *statsHandler) serveSelf(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write(h.stats.SelfStats())
+}
+
+func (h *statsHandler) serveLeader(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ stats := h.stats.LeaderStats()
+ if stats == nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusForbidden, "not current leader"))
+ return
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write(stats)
+}
+
+func serveVars(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+
+ w.Header().Set("Content-Type", "application/json; charset=utf-8")
+ fmt.Fprintf(w, "{\n")
+ first := true
+ expvar.Do(func(kv expvar.KeyValue) {
+ if !first {
+ fmt.Fprintf(w, ",\n")
+ }
+ first = false
+ fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
+ })
+ fmt.Fprintf(w, "\n}\n")
+}
+
+// TODO: change etcdserver to raft interface when we have it.
+// add test for healthHeadler when we have the interface ready.
+func healthHandler(server *etcdserver.EtcdServer) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+
+ if uint64(server.Leader()) == raft.None {
+ http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
+ return
+ }
+
+ // wait for raft's progress
+ index := server.Index()
+ for i := 0; i < 3; i++ {
+ time.Sleep(250 * time.Millisecond)
+ if server.Index() > index {
+ w.WriteHeader(http.StatusOK)
+ w.Write([]byte(`{"health": "true"}`))
+ return
+ }
+ }
+
+ http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
+ return
+ }
+}
+
+func versionHandler(c etcdserver.Cluster, fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ v := c.Version()
+ if v != nil {
+ fn(w, r, v.String())
+ } else {
+ fn(w, r, "not_decided")
+ }
+ }
+}
+
+func serveVersion(w http.ResponseWriter, r *http.Request, clusterV string) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ vs := version.Versions{
+ Server: version.Version,
+ Cluster: clusterV,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ b, err := json.Marshal(&vs)
+ if err != nil {
+ plog.Panicf("cannot marshal versions to json (%v)", err)
+ }
+ w.Write(b)
+}
+
+func logHandleFunc(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "PUT") {
+ return
+ }
+
+ in := struct{ Level string }{}
+
+ d := json.NewDecoder(r.Body)
+ if err := d.Decode(&in); err != nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid json body"))
+ return
+ }
+
+ logl, err := capnslog.ParseLevel(strings.ToUpper(in.Level))
+ if err != nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid log level "+in.Level))
+ return
+ }
+
+ plog.Noticef("globalLogLevel set to %q", logl.String())
+ capnslog.SetGlobalLogLevel(logl)
+ w.WriteHeader(http.StatusNoContent)
+}
+
+// parseKeyRequest converts a received http.Request on keysPrefix to
+// a server Request, performing validation of supplied fields as appropriate.
+// If any validation fails, an empty Request and non-nil error is returned.
+func parseKeyRequest(r *http.Request, clock clockwork.Clock) (etcdserverpb.Request, error) {
+ emptyReq := etcdserverpb.Request{}
+
+ err := r.ParseForm()
+ if err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidForm,
+ err.Error(),
+ )
+ }
+
+ if !strings.HasPrefix(r.URL.Path, keysPrefix) {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidForm,
+ "incorrect key prefix",
+ )
+ }
+ p := path.Join(etcdserver.StoreKeysPrefix, r.URL.Path[len(keysPrefix):])
+
+ var pIdx, wIdx uint64
+ if pIdx, err = getUint64(r.Form, "prevIndex"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeIndexNaN,
+ `invalid value for "prevIndex"`,
+ )
+ }
+ if wIdx, err = getUint64(r.Form, "waitIndex"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeIndexNaN,
+ `invalid value for "waitIndex"`,
+ )
+ }
+
+ var rec, sort, wait, dir, quorum, stream bool
+ if rec, err = getBool(r.Form, "recursive"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `invalid value for "recursive"`,
+ )
+ }
+ if sort, err = getBool(r.Form, "sorted"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `invalid value for "sorted"`,
+ )
+ }
+ if wait, err = getBool(r.Form, "wait"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `invalid value for "wait"`,
+ )
+ }
+ // TODO(jonboulle): define what parameters dir is/isn't compatible with?
+ if dir, err = getBool(r.Form, "dir"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `invalid value for "dir"`,
+ )
+ }
+ if quorum, err = getBool(r.Form, "quorum"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `invalid value for "quorum"`,
+ )
+ }
+ if stream, err = getBool(r.Form, "stream"); err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `invalid value for "stream"`,
+ )
+ }
+
+ if wait && r.Method != "GET" {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ `"wait" can only be used with GET requests`,
+ )
+ }
+
+ pV := r.FormValue("prevValue")
+ if _, ok := r.Form["prevValue"]; ok && pV == "" {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodePrevValueRequired,
+ `"prevValue" cannot be empty`,
+ )
+ }
+
+ // TTL is nullable, so leave it null if not specified
+ // or an empty string
+ var ttl *uint64
+ if len(r.FormValue("ttl")) > 0 {
+ i, err := getUint64(r.Form, "ttl")
+ if err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeTTLNaN,
+ `invalid value for "ttl"`,
+ )
+ }
+ ttl = &i
+ }
+
+ // prevExist is nullable, so leave it null if not specified
+ var pe *bool
+ if _, ok := r.Form["prevExist"]; ok {
+ bv, err := getBool(r.Form, "prevExist")
+ if err != nil {
+ return emptyReq, etcdErr.NewRequestError(
+ etcdErr.EcodeInvalidField,
+ "invalid value for prevExist",
+ )
+ }
+ pe = &bv
+ }
+
+ rr := etcdserverpb.Request{
+ Method: r.Method,
+ Path: p,
+ Val: r.FormValue("value"),
+ Dir: dir,
+ PrevValue: pV,
+ PrevIndex: pIdx,
+ PrevExist: pe,
+ Wait: wait,
+ Since: wIdx,
+ Recursive: rec,
+ Sorted: sort,
+ Quorum: quorum,
+ Stream: stream,
+ }
+
+ if pe != nil {
+ rr.PrevExist = pe
+ }
+
+ // Null TTL is equivalent to unset Expiration
+ if ttl != nil {
+ expr := time.Duration(*ttl) * time.Second
+ rr.Expiration = clock.Now().Add(expr).UnixNano()
+ }
+
+ return rr, nil
+}
+
+// writeKeyEvent trims the prefix of key path in a single Event under
+// StoreKeysPrefix, serializes it and writes the resulting JSON to the given
+// ResponseWriter, along with the appropriate headers.
+func writeKeyEvent(w http.ResponseWriter, ev *store.Event, rt etcdserver.RaftTimer) error {
+ if ev == nil {
+ return errors.New("cannot write empty Event!")
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Header().Set("X-Etcd-Index", fmt.Sprint(ev.EtcdIndex))
+ w.Header().Set("X-Raft-Index", fmt.Sprint(rt.Index()))
+ w.Header().Set("X-Raft-Term", fmt.Sprint(rt.Term()))
+
+ if ev.IsCreated() {
+ w.WriteHeader(http.StatusCreated)
+ }
+
+ ev = trimEventPrefix(ev, etcdserver.StoreKeysPrefix)
+ return json.NewEncoder(w).Encode(ev)
+}
+
+func writeKeyNoAuth(w http.ResponseWriter) {
+ e := etcdErr.NewError(etcdErr.EcodeUnauthorized, "Insufficient credentials", 0)
+ e.WriteTo(w)
+}
+
+// writeKeyError logs and writes the given Error to the ResponseWriter.
+// If Error is not an etcdErr, the error will be converted to an etcd error.
+func writeKeyError(w http.ResponseWriter, err error) {
+ if err == nil {
+ return
+ }
+ switch e := err.(type) {
+ case *etcdErr.Error:
+ e.WriteTo(w)
+ default:
+ switch err {
+ case etcdserver.ErrTimeoutDueToLeaderFail, etcdserver.ErrTimeoutDueToConnectionLost:
+ plog.Error(err)
+ default:
+ plog.Errorf("got unexpected response error (%v)", err)
+ }
+ ee := etcdErr.NewError(etcdErr.EcodeRaftInternal, err.Error(), 0)
+ ee.WriteTo(w)
+ }
+}
+
+func handleKeyWatch(ctx context.Context, w http.ResponseWriter, wa store.Watcher, stream bool, rt etcdserver.RaftTimer) {
+ defer wa.Remove()
+ ech := wa.EventChan()
+ var nch <-chan bool
+ if x, ok := w.(http.CloseNotifier); ok {
+ nch = x.CloseNotify()
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ w.Header().Set("X-Etcd-Index", fmt.Sprint(wa.StartIndex()))
+ w.Header().Set("X-Raft-Index", fmt.Sprint(rt.Index()))
+ w.Header().Set("X-Raft-Term", fmt.Sprint(rt.Term()))
+ w.WriteHeader(http.StatusOK)
+
+ // Ensure headers are flushed early, in case of long polling
+ w.(http.Flusher).Flush()
+
+ for {
+ select {
+ case <-nch:
+ // Client closed connection. Nothing to do.
+ return
+ case <-ctx.Done():
+ // Timed out. net/http will close the connection for us, so nothing to do.
+ return
+ case ev, ok := <-ech:
+ if !ok {
+ // If the channel is closed this may be an indication of
+ // that notifications are much more than we are able to
+ // send to the client in time. Then we simply end streaming.
+ return
+ }
+ ev = trimEventPrefix(ev, etcdserver.StoreKeysPrefix)
+ if err := json.NewEncoder(w).Encode(ev); err != nil {
+ // Should never be reached
+ plog.Warningf("error writing event (%v)", err)
+ return
+ }
+ if !stream {
+ return
+ }
+ w.(http.Flusher).Flush()
+ }
+ }
+}
+
+func trimEventPrefix(ev *store.Event, prefix string) *store.Event {
+ if ev == nil {
+ return nil
+ }
+ // Since the *Event may reference one in the store history
+ // history, we must copy it before modifying
+ e := ev.Clone()
+ e.Node = trimNodeExternPrefix(e.Node, prefix)
+ e.PrevNode = trimNodeExternPrefix(e.PrevNode, prefix)
+ return e
+}
+
+func trimNodeExternPrefix(n *store.NodeExtern, prefix string) *store.NodeExtern {
+ if n == nil {
+ return nil
+ }
+ n.Key = strings.TrimPrefix(n.Key, prefix)
+ for _, nn := range n.Nodes {
+ nn = trimNodeExternPrefix(nn, prefix)
+ }
+ return n
+}
+
+func trimErrorPrefix(err error, prefix string) error {
+ if e, ok := err.(*etcdErr.Error); ok {
+ e.Cause = strings.TrimPrefix(e.Cause, prefix)
+ }
+ return err
+}
+
+func unmarshalRequest(r *http.Request, req json.Unmarshaler, w http.ResponseWriter) bool {
+ ctype := r.Header.Get("Content-Type")
+ if ctype != "application/json" {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusUnsupportedMediaType, fmt.Sprintf("Bad Content-Type %s, accept application/json", ctype)))
+ return false
+ }
+ b, err := ioutil.ReadAll(r.Body)
+ if err != nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, err.Error()))
+ return false
+ }
+ if err := req.UnmarshalJSON(b); err != nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, err.Error()))
+ return false
+ }
+ return true
+}
+
+func getID(p string, w http.ResponseWriter) (types.ID, bool) {
+ idStr := trimPrefix(p, membersPrefix)
+ if idStr == "" {
+ http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+ return 0, false
+ }
+ id, err := types.IDFromString(idStr)
+ if err != nil {
+ writeError(w, nil, httptypes.NewHTTPError(http.StatusNotFound, fmt.Sprintf("No such member: %s", idStr)))
+ return 0, false
+ }
+ return id, true
+}
+
+// getUint64 extracts a uint64 by the given key from a Form. If the key does
+// not exist in the form, 0 is returned. If the key exists but the value is
+// badly formed, an error is returned. If multiple values are present only the
+// first is considered.
+func getUint64(form url.Values, key string) (i uint64, err error) {
+ if vals, ok := form[key]; ok {
+ i, err = strconv.ParseUint(vals[0], 10, 64)
+ }
+ return
+}
+
+// getBool extracts a bool by the given key from a Form. If the key does not
+// exist in the form, false is returned. If the key exists but the value is
+// badly formed, an error is returned. If multiple values are present only the
+// first is considered.
+func getBool(form url.Values, key string) (b bool, err error) {
+ if vals, ok := form[key]; ok {
+ b, err = strconv.ParseBool(vals[0])
+ }
+ return
+}
+
+// trimPrefix removes a given prefix and any slash following the prefix
+// e.g.: trimPrefix("foo", "foo") == trimPrefix("foo/", "foo") == ""
+func trimPrefix(p, prefix string) (s string) {
+ s = strings.TrimPrefix(p, prefix)
+ s = strings.TrimPrefix(s, "/")
+ return
+}
+
+func newMemberCollection(ms []*etcdserver.Member) *httptypes.MemberCollection {
+ c := httptypes.MemberCollection(make([]httptypes.Member, len(ms)))
+
+ for i, m := range ms {
+ c[i] = newMember(m)
+ }
+
+ return &c
+}
+
+func newMember(m *etcdserver.Member) httptypes.Member {
+ tm := httptypes.Member{
+ ID: m.ID.String(),
+ Name: m.Name,
+ PeerURLs: make([]string, len(m.PeerURLs)),
+ ClientURLs: make([]string, len(m.ClientURLs)),
+ }
+
+ copy(tm.PeerURLs, m.PeerURLs)
+ copy(tm.ClientURLs, m.ClientURLs)
+
+ return tm
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_auth.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_auth.go
new file mode 100644
index 000000000..0637afa13
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_auth.go
@@ -0,0 +1,506 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "encoding/json"
+ "net/http"
+ "path"
+ "strings"
+
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/auth"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+)
+
+type authHandler struct {
+ sec auth.Store
+ cluster etcdserver.Cluster
+}
+
+func hasWriteRootAccess(sec auth.Store, r *http.Request) bool {
+ if r.Method == "GET" || r.Method == "HEAD" {
+ return true
+ }
+ return hasRootAccess(sec, r)
+}
+
+func hasRootAccess(sec auth.Store, r *http.Request) bool {
+ if sec == nil {
+ // No store means no auth available, eg, tests.
+ return true
+ }
+ if !sec.AuthEnabled() {
+ return true
+ }
+ username, password, ok := r.BasicAuth()
+ if !ok {
+ return false
+ }
+ rootUser, err := sec.GetUser(username)
+ if err != nil {
+ return false
+ }
+ ok = rootUser.CheckPassword(password)
+ if !ok {
+ plog.Warningf("auth: wrong password for user %s", username)
+ return false
+ }
+ for _, role := range rootUser.Roles {
+ if role == auth.RootRoleName {
+ return true
+ }
+ }
+ plog.Warningf("auth: user %s does not have the %s role for resource %s.", username, auth.RootRoleName, r.URL.Path)
+ return false
+}
+
+func hasKeyPrefixAccess(sec auth.Store, r *http.Request, key string, recursive bool) bool {
+ if sec == nil {
+ // No store means no auth available, eg, tests.
+ return true
+ }
+ if !sec.AuthEnabled() {
+ return true
+ }
+ if r.Header.Get("Authorization") == "" {
+ plog.Warningf("auth: no authorization provided, checking guest access")
+ return hasGuestAccess(sec, r, key)
+ }
+ username, password, ok := r.BasicAuth()
+ if !ok {
+ plog.Warningf("auth: malformed basic auth encoding")
+ return false
+ }
+ user, err := sec.GetUser(username)
+ if err != nil {
+ plog.Warningf("auth: no such user: %s.", username)
+ return false
+ }
+ authAsUser := user.CheckPassword(password)
+ if !authAsUser {
+ plog.Warningf("auth: incorrect password for user: %s.", username)
+ return false
+ }
+ writeAccess := r.Method != "GET" && r.Method != "HEAD"
+ for _, roleName := range user.Roles {
+ role, err := sec.GetRole(roleName)
+ if err != nil {
+ continue
+ }
+ if recursive {
+ if role.HasRecursiveAccess(key, writeAccess) {
+ return true
+ }
+ } else if role.HasKeyAccess(key, writeAccess) {
+ return true
+ }
+ }
+ plog.Warningf("auth: invalid access for user %s on key %s.", username, key)
+ return false
+}
+
+func hasGuestAccess(sec auth.Store, r *http.Request, key string) bool {
+ writeAccess := r.Method != "GET" && r.Method != "HEAD"
+ role, err := sec.GetRole(auth.GuestRoleName)
+ if err != nil {
+ return false
+ }
+ if role.HasKeyAccess(key, writeAccess) {
+ return true
+ }
+ plog.Warningf("auth: invalid access for unauthenticated user on resource %s.", key)
+ return false
+}
+
+func writeNoAuth(w http.ResponseWriter, r *http.Request) {
+ herr := httptypes.NewHTTPError(http.StatusUnauthorized, "Insufficient credentials")
+ if err := herr.WriteTo(w); err != nil {
+ plog.Debugf("error writing HTTPError (%v) to %s", err, r.RemoteAddr)
+ }
+}
+
+func handleAuth(mux *http.ServeMux, sh *authHandler) {
+ mux.HandleFunc(authPrefix+"/roles", capabilityHandler(authCapability, sh.baseRoles))
+ mux.HandleFunc(authPrefix+"/roles/", capabilityHandler(authCapability, sh.handleRoles))
+ mux.HandleFunc(authPrefix+"/users", capabilityHandler(authCapability, sh.baseUsers))
+ mux.HandleFunc(authPrefix+"/users/", capabilityHandler(authCapability, sh.handleUsers))
+ mux.HandleFunc(authPrefix+"/enable", capabilityHandler(authCapability, sh.enableDisable))
+}
+
+func (sh *authHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ if !hasRootAccess(sh.sec, r) {
+ writeNoAuth(w, r)
+ return
+ }
+
+ w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
+ w.Header().Set("Content-Type", "application/json")
+
+ roles, err := sh.sec.AllRoles()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ if roles == nil {
+ roles = make([]string, 0)
+ }
+
+ err = r.ParseForm()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+
+ var rolesCollections struct {
+ Roles []auth.Role `json:"roles"`
+ }
+ for _, roleName := range roles {
+ var role auth.Role
+ role, err = sh.sec.GetRole(roleName)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ rolesCollections.Roles = append(rolesCollections.Roles, role)
+ }
+ err = json.NewEncoder(w).Encode(rolesCollections)
+
+ if err != nil {
+ plog.Warningf("baseRoles error encoding on %s", r.URL)
+ writeError(w, r, err)
+ return
+ }
+}
+
+func (sh *authHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
+ subpath := path.Clean(r.URL.Path[len(authPrefix):])
+ // Split "/roles/rolename/command".
+ // First item is an empty string, second is "roles"
+ pieces := strings.Split(subpath, "/")
+ if len(pieces) == 2 {
+ sh.baseRoles(w, r)
+ return
+ }
+ if len(pieces) != 3 {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path"))
+ return
+ }
+ sh.forRole(w, r, pieces[2])
+}
+
+func (sh *authHandler) forRole(w http.ResponseWriter, r *http.Request, role string) {
+ if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
+ return
+ }
+ if !hasRootAccess(sh.sec, r) {
+ writeNoAuth(w, r)
+ return
+ }
+ w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
+ w.Header().Set("Content-Type", "application/json")
+
+ switch r.Method {
+ case "GET":
+ data, err := sh.sec.GetRole(role)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ err = json.NewEncoder(w).Encode(data)
+ if err != nil {
+ plog.Warningf("forRole error encoding on %s", r.URL)
+ return
+ }
+ return
+ case "PUT":
+ var in auth.Role
+ err := json.NewDecoder(r.Body).Decode(&in)
+ if err != nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
+ return
+ }
+ if in.Role != role {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Role JSON name does not match the name in the URL"))
+ return
+ }
+
+ var out auth.Role
+
+ // create
+ if in.Grant.IsEmpty() && in.Revoke.IsEmpty() {
+ err = sh.sec.CreateRole(in)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ w.WriteHeader(http.StatusCreated)
+ out = in
+ } else {
+ if !in.Permissions.IsEmpty() {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Role JSON contains both permissions and grant/revoke"))
+ return
+ }
+ out, err = sh.sec.UpdateRole(in)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ w.WriteHeader(http.StatusOK)
+ }
+
+ err = json.NewEncoder(w).Encode(out)
+ if err != nil {
+ plog.Warningf("forRole error encoding on %s", r.URL)
+ return
+ }
+ return
+ case "DELETE":
+ err := sh.sec.DeleteRole(role)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ }
+}
+
+type userWithRoles struct {
+ User string `json:"user"`
+ Roles []auth.Role `json:"roles,omitempty"`
+}
+
+func (sh *authHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ if !hasRootAccess(sh.sec, r) {
+ writeNoAuth(w, r)
+ return
+ }
+ w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
+ w.Header().Set("Content-Type", "application/json")
+
+ users, err := sh.sec.AllUsers()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ if users == nil {
+ users = make([]string, 0)
+ }
+
+ err = r.ParseForm()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+
+ var usersCollections struct {
+ Users []userWithRoles `json:"users"`
+ }
+ for _, userName := range users {
+ var user auth.User
+ user, err = sh.sec.GetUser(userName)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+
+ uwr := userWithRoles{User: user.User}
+ for _, roleName := range user.Roles {
+ var role auth.Role
+ role, err = sh.sec.GetRole(roleName)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ uwr.Roles = append(uwr.Roles, role)
+ }
+
+ usersCollections.Users = append(usersCollections.Users, uwr)
+ }
+ err = json.NewEncoder(w).Encode(usersCollections)
+
+ if err != nil {
+ plog.Warningf("baseUsers error encoding on %s", r.URL)
+ writeError(w, r, err)
+ return
+ }
+}
+
+func (sh *authHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
+ subpath := path.Clean(r.URL.Path[len(authPrefix):])
+ // Split "/users/username".
+ // First item is an empty string, second is "users"
+ pieces := strings.Split(subpath, "/")
+ if len(pieces) == 2 {
+ sh.baseUsers(w, r)
+ return
+ }
+ if len(pieces) != 3 {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path"))
+ return
+ }
+ sh.forUser(w, r, pieces[2])
+}
+
+func (sh *authHandler) forUser(w http.ResponseWriter, r *http.Request, user string) {
+ if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
+ return
+ }
+ if !hasRootAccess(sh.sec, r) {
+ writeNoAuth(w, r)
+ return
+ }
+ w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
+ w.Header().Set("Content-Type", "application/json")
+
+ switch r.Method {
+ case "GET":
+ u, err := sh.sec.GetUser(user)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+
+ err = r.ParseForm()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+
+ uwr := userWithRoles{User: u.User}
+ for _, roleName := range u.Roles {
+ var role auth.Role
+ role, err = sh.sec.GetRole(roleName)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ uwr.Roles = append(uwr.Roles, role)
+ }
+ err = json.NewEncoder(w).Encode(uwr)
+
+ if err != nil {
+ plog.Warningf("forUser error encoding on %s", r.URL)
+ return
+ }
+ return
+ case "PUT":
+ var u auth.User
+ err := json.NewDecoder(r.Body).Decode(&u)
+ if err != nil {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
+ return
+ }
+ if u.User != user {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "User JSON name does not match the name in the URL"))
+ return
+ }
+
+ var (
+ out auth.User
+ created bool
+ )
+
+ if len(u.Grant) == 0 && len(u.Revoke) == 0 {
+ // create or update
+ if len(u.Roles) != 0 {
+ out, err = sh.sec.CreateUser(u)
+ } else {
+ // if user passes in both password and roles, we are unsure about his/her
+ // intention.
+ out, created, err = sh.sec.CreateOrUpdateUser(u)
+ }
+
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ } else {
+ // update case
+ if len(u.Roles) != 0 {
+ writeError(w, r, httptypes.NewHTTPError(http.StatusBadRequest, "User JSON contains both roles and grant/revoke"))
+ return
+ }
+ out, err = sh.sec.UpdateUser(u)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ }
+
+ if created {
+ w.WriteHeader(http.StatusCreated)
+ } else {
+ w.WriteHeader(http.StatusOK)
+ }
+
+ out.Password = ""
+
+ err = json.NewEncoder(w).Encode(out)
+ if err != nil {
+ plog.Warningf("forUser error encoding on %s", r.URL)
+ return
+ }
+ return
+ case "DELETE":
+ err := sh.sec.DeleteUser(user)
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ }
+}
+
+type enabled struct {
+ Enabled bool `json:"enabled"`
+}
+
+func (sh *authHandler) enableDisable(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
+ return
+ }
+ if !hasWriteRootAccess(sh.sec, r) {
+ writeNoAuth(w, r)
+ return
+ }
+ w.Header().Set("X-Etcd-Cluster-ID", sh.cluster.ID().String())
+ w.Header().Set("Content-Type", "application/json")
+ isEnabled := sh.sec.AuthEnabled()
+ switch r.Method {
+ case "GET":
+ jsonDict := enabled{isEnabled}
+ err := json.NewEncoder(w).Encode(jsonDict)
+ if err != nil {
+ plog.Warningf("error encoding auth state on %s", r.URL)
+ }
+ case "PUT":
+ err := sh.sec.EnableAuth()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ case "DELETE":
+ err := sh.sec.DisableAuth()
+ if err != nil {
+ writeError(w, r, err)
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_auth_test.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_auth_test.go
new file mode 100644
index 000000000..97bea77e6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_auth_test.go
@@ -0,0 +1,625 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "errors"
+ "net/http"
+ "net/http/httptest"
+ "path"
+ "strings"
+ "testing"
+
+ "github.com/coreos/etcd/etcdserver/auth"
+)
+
+const goodPassword = "$2a$10$VYdJecHfm6WNodzv8XhmYeIG4n2SsQefdo5V2t6xIq/aWDHNqSUQW"
+
+func mustJSONRequest(t *testing.T, method string, p string, body string) *http.Request {
+ req, err := http.NewRequest(method, path.Join(authPrefix, p), strings.NewReader(body))
+ if err != nil {
+ t.Fatalf("Error making JSON request: %s %s %s\n", method, p, body)
+ }
+ req.Header.Set("Content-Type", "application/json")
+ return req
+}
+
+type mockAuthStore struct {
+ users map[string]*auth.User
+ roles map[string]*auth.Role
+ err error
+ enabled bool
+}
+
+func (s *mockAuthStore) AllUsers() ([]string, error) { return []string{"alice", "bob", "root"}, s.err }
+func (s *mockAuthStore) GetUser(name string) (auth.User, error) {
+ u, ok := s.users[name]
+ if !ok {
+ return auth.User{}, s.err
+ }
+ return *u, s.err
+}
+func (s *mockAuthStore) CreateOrUpdateUser(user auth.User) (out auth.User, created bool, err error) {
+ if s.users == nil {
+ u, err := s.CreateUser(user)
+ return u, true, err
+ }
+ u, err := s.UpdateUser(user)
+ return u, false, err
+}
+func (s *mockAuthStore) CreateUser(user auth.User) (auth.User, error) { return user, s.err }
+func (s *mockAuthStore) DeleteUser(name string) error { return s.err }
+func (s *mockAuthStore) UpdateUser(user auth.User) (auth.User, error) {
+ return *s.users[user.User], s.err
+}
+func (s *mockAuthStore) AllRoles() ([]string, error) {
+ return []string{"awesome", "guest", "root"}, s.err
+}
+func (s *mockAuthStore) GetRole(name string) (auth.Role, error) { return *s.roles[name], s.err }
+func (s *mockAuthStore) CreateRole(role auth.Role) error { return s.err }
+func (s *mockAuthStore) DeleteRole(name string) error { return s.err }
+func (s *mockAuthStore) UpdateRole(role auth.Role) (auth.Role, error) {
+ return *s.roles[role.Role], s.err
+}
+func (s *mockAuthStore) AuthEnabled() bool { return s.enabled }
+func (s *mockAuthStore) EnableAuth() error { return s.err }
+func (s *mockAuthStore) DisableAuth() error { return s.err }
+
+func TestAuthFlow(t *testing.T) {
+ enableMapMu.Lock()
+ enabledMap = make(map[capability]bool)
+ enabledMap[authCapability] = true
+ enableMapMu.Unlock()
+ var testCases = []struct {
+ req *http.Request
+ store mockAuthStore
+
+ wcode int
+ wbody string
+ }{
+ {
+ req: mustJSONRequest(t, "PUT", "users/alice", `{{{{{{{`),
+ store: mockAuthStore{},
+ wcode: http.StatusBadRequest,
+ wbody: `{"message":"Invalid JSON in request body."}`,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`),
+ store: mockAuthStore{enabled: true},
+ wcode: http.StatusUnauthorized,
+ wbody: `{"message":"Insufficient credentials"}`,
+ },
+ // Users
+ {
+ req: mustJSONRequest(t, "GET", "users", ""),
+ store: mockAuthStore{
+ users: map[string]*auth.User{
+ "alice": {
+ User: "alice",
+ Roles: []string{"alicerole", "guest"},
+ Password: "wheeee",
+ },
+ "bob": {
+ User: "bob",
+ Roles: []string{"guest"},
+ Password: "wheeee",
+ },
+ "root": {
+ User: "root",
+ Roles: []string{"root"},
+ Password: "wheeee",
+ },
+ },
+ roles: map[string]*auth.Role{
+ "alicerole": {
+ Role: "alicerole",
+ },
+ "guest": {
+ Role: "guest",
+ },
+ "root": {
+ Role: "root",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"users":[` +
+ `{"user":"alice","roles":[` +
+ `{"role":"alicerole","permissions":{"kv":{"read":null,"write":null}}},` +
+ `{"role":"guest","permissions":{"kv":{"read":null,"write":null}}}` +
+ `]},` +
+ `{"user":"bob","roles":[{"role":"guest","permissions":{"kv":{"read":null,"write":null}}}]},` +
+ `{"user":"root","roles":[{"role":"root","permissions":{"kv":{"read":null,"write":null}}}]}]}`,
+ },
+ {
+ req: mustJSONRequest(t, "GET", "users/alice", ""),
+ store: mockAuthStore{
+ users: map[string]*auth.User{
+ "alice": {
+ User: "alice",
+ Roles: []string{"alicerole"},
+ Password: "wheeee",
+ },
+ },
+ roles: map[string]*auth.Role{
+ "alicerole": {
+ Role: "alicerole",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"user":"alice","roles":[{"role":"alicerole","permissions":{"kv":{"read":null,"write":null}}}]}`,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`),
+ store: mockAuthStore{},
+ wcode: http.StatusCreated,
+ wbody: `{"user":"alice","roles":null}`,
+ },
+ {
+ req: mustJSONRequest(t, "DELETE", "users/alice", ``),
+ store: mockAuthStore{},
+ wcode: http.StatusOK,
+ wbody: ``,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "password": "goodpassword"}`),
+ store: mockAuthStore{
+ users: map[string]*auth.User{
+ "alice": {
+ User: "alice",
+ Roles: []string{"alicerole", "guest"},
+ Password: "wheeee",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"user":"alice","roles":["alicerole","guest"]}`,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "users/alice", `{"user": "alice", "grant": ["alicerole"]}`),
+ store: mockAuthStore{
+ users: map[string]*auth.User{
+ "alice": {
+ User: "alice",
+ Roles: []string{"alicerole", "guest"},
+ Password: "wheeee",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"user":"alice","roles":["alicerole","guest"]}`,
+ },
+ {
+ req: mustJSONRequest(t, "GET", "users/alice", ``),
+ store: mockAuthStore{
+ users: map[string]*auth.User{},
+ err: auth.Error{Status: http.StatusNotFound, Errmsg: "auth: User alice doesn't exist."},
+ },
+ wcode: http.StatusNotFound,
+ wbody: `{"message":"auth: User alice doesn't exist."}`,
+ },
+ {
+ req: mustJSONRequest(t, "GET", "roles/manager", ""),
+ store: mockAuthStore{
+ roles: map[string]*auth.Role{
+ "manager": {
+ Role: "manager",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"role":"manager","permissions":{"kv":{"read":null,"write":null}}}`,
+ },
+ {
+ req: mustJSONRequest(t, "DELETE", "roles/manager", ``),
+ store: mockAuthStore{},
+ wcode: http.StatusOK,
+ wbody: ``,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "roles/manager", `{"role":"manager","permissions":{"kv":{"read":[],"write":[]}}}`),
+ store: mockAuthStore{},
+ wcode: http.StatusCreated,
+ wbody: `{"role":"manager","permissions":{"kv":{"read":[],"write":[]}}}`,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "roles/manager", `{"role":"manager","revoke":{"kv":{"read":["foo"],"write":[]}}}`),
+ store: mockAuthStore{
+ roles: map[string]*auth.Role{
+ "manager": {
+ Role: "manager",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"role":"manager","permissions":{"kv":{"read":null,"write":null}}}`,
+ },
+ {
+ req: mustJSONRequest(t, "GET", "roles", ""),
+ store: mockAuthStore{
+ roles: map[string]*auth.Role{
+ "awesome": {
+ Role: "awesome",
+ },
+ "guest": {
+ Role: "guest",
+ },
+ "root": {
+ Role: "root",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: `{"roles":[{"role":"awesome","permissions":{"kv":{"read":null,"write":null}}},` +
+ `{"role":"guest","permissions":{"kv":{"read":null,"write":null}}},` +
+ `{"role":"root","permissions":{"kv":{"read":null,"write":null}}}]}`,
+ },
+ {
+ req: mustJSONRequest(t, "GET", "enable", ""),
+ store: mockAuthStore{
+ enabled: true,
+ },
+ wcode: http.StatusOK,
+ wbody: `{"enabled":true}`,
+ },
+ {
+ req: mustJSONRequest(t, "PUT", "enable", ""),
+ store: mockAuthStore{
+ enabled: false,
+ },
+ wcode: http.StatusOK,
+ wbody: ``,
+ },
+ {
+ req: (func() *http.Request {
+ req := mustJSONRequest(t, "DELETE", "enable", "")
+ req.SetBasicAuth("root", "good")
+ return req
+ })(),
+ store: mockAuthStore{
+ enabled: true,
+ users: map[string]*auth.User{
+ "root": {
+ User: "root",
+ Password: goodPassword,
+ Roles: []string{"root"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "root": {
+ Role: "root",
+ },
+ },
+ },
+ wcode: http.StatusOK,
+ wbody: ``,
+ },
+ {
+ req: (func() *http.Request {
+ req := mustJSONRequest(t, "DELETE", "enable", "")
+ req.SetBasicAuth("root", "bad")
+ return req
+ })(),
+ store: mockAuthStore{
+ enabled: true,
+ users: map[string]*auth.User{
+ "root": {
+ User: "root",
+ Password: goodPassword,
+ Roles: []string{"root"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "root": {
+ Role: "guest",
+ },
+ },
+ },
+ wcode: http.StatusUnauthorized,
+ wbody: `{"message":"Insufficient credentials"}`,
+ },
+ }
+
+ for i, tt := range testCases {
+ mux := http.NewServeMux()
+ h := &authHandler{
+ sec: &tt.store,
+ cluster: &fakeCluster{id: 1},
+ }
+ handleAuth(mux, h)
+ rw := httptest.NewRecorder()
+ mux.ServeHTTP(rw, tt.req)
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ g := rw.Body.String()
+ g = strings.TrimSpace(g)
+ if g != tt.wbody {
+ t.Errorf("#%d: got body=%s, want %s", i, g, tt.wbody)
+ }
+ }
+}
+
+func mustAuthRequest(method, username, password string) *http.Request {
+ req, err := http.NewRequest(method, "path", strings.NewReader(""))
+ if err != nil {
+ panic("Cannot make auth request: " + err.Error())
+ }
+ req.SetBasicAuth(username, password)
+ return req
+}
+
+func TestPrefixAccess(t *testing.T) {
+ var table = []struct {
+ key string
+ req *http.Request
+ store *mockAuthStore
+ hasRoot bool
+ hasKeyPrefixAccess bool
+ hasRecursiveAccess bool
+ }{
+ {
+ key: "/foo",
+ req: mustAuthRequest("GET", "root", "good"),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "root": {
+ User: "root",
+ Password: goodPassword,
+ Roles: []string{"root"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "root": {
+ Role: "root",
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: true,
+ hasKeyPrefixAccess: true,
+ hasRecursiveAccess: true,
+ },
+ {
+ key: "/foo",
+ req: mustAuthRequest("GET", "user", "good"),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "user": {
+ User: "user",
+ Password: goodPassword,
+ Roles: []string{"foorole"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "foorole": {
+ Role: "foorole",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo"},
+ Write: []string{"/foo"},
+ },
+ },
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: true,
+ hasRecursiveAccess: false,
+ },
+ {
+ key: "/foo",
+ req: mustAuthRequest("GET", "user", "good"),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "user": {
+ User: "user",
+ Password: goodPassword,
+ Roles: []string{"foorole"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "foorole": {
+ Role: "foorole",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo*"},
+ Write: []string{"/foo*"},
+ },
+ },
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: true,
+ hasRecursiveAccess: true,
+ },
+ {
+ key: "/foo",
+ req: mustAuthRequest("GET", "user", "bad"),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "user": {
+ User: "user",
+ Password: goodPassword,
+ Roles: []string{"foorole"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "foorole": {
+ Role: "foorole",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo*"},
+ Write: []string{"/foo*"},
+ },
+ },
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: false,
+ hasRecursiveAccess: false,
+ },
+ {
+ key: "/foo",
+ req: mustAuthRequest("GET", "user", "good"),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{},
+ err: errors.New("Not the user"),
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: false,
+ hasRecursiveAccess: false,
+ },
+ {
+ key: "/foo",
+ req: mustJSONRequest(t, "GET", "somepath", ""),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "user": {
+ User: "user",
+ Password: goodPassword,
+ Roles: []string{"foorole"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "guest": {
+ Role: "guest",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo*"},
+ Write: []string{"/foo*"},
+ },
+ },
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: true,
+ hasRecursiveAccess: true,
+ },
+ {
+ key: "/bar",
+ req: mustJSONRequest(t, "GET", "somepath", ""),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "user": {
+ User: "user",
+ Password: goodPassword,
+ Roles: []string{"foorole"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "guest": {
+ Role: "guest",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo*"},
+ Write: []string{"/foo*"},
+ },
+ },
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: false,
+ hasRecursiveAccess: false,
+ },
+ // check access for multiple roles
+ {
+ key: "/foo",
+ req: mustAuthRequest("GET", "user", "good"),
+ store: &mockAuthStore{
+ users: map[string]*auth.User{
+ "user": {
+ User: "user",
+ Password: goodPassword,
+ Roles: []string{"role1", "role2"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "role1": {
+ Role: "role1",
+ },
+ "role2": {
+ Role: "role2",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo"},
+ Write: []string{"/foo"},
+ },
+ },
+ },
+ },
+ enabled: true,
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: true,
+ hasRecursiveAccess: false,
+ },
+ {
+ key: "/foo",
+ req: (func() *http.Request {
+ req := mustJSONRequest(t, "GET", "somepath", "")
+ req.Header.Set("Authorization", "malformedencoding")
+ return req
+ })(),
+ store: &mockAuthStore{
+ enabled: true,
+ users: map[string]*auth.User{
+ "root": {
+ User: "root",
+ Password: goodPassword,
+ Roles: []string{"root"},
+ },
+ },
+ roles: map[string]*auth.Role{
+ "guest": {
+ Role: "guest",
+ Permissions: auth.Permissions{
+ KV: auth.RWPermission{
+ Read: []string{"/foo*"},
+ Write: []string{"/foo*"},
+ },
+ },
+ },
+ },
+ },
+ hasRoot: false,
+ hasKeyPrefixAccess: false,
+ hasRecursiveAccess: false,
+ },
+ }
+
+ for i, tt := range table {
+ if tt.hasRoot != hasRootAccess(tt.store, tt.req) {
+ t.Errorf("#%d: hasRoot doesn't match (expected %v)", i, tt.hasRoot)
+ }
+ if tt.hasKeyPrefixAccess != hasKeyPrefixAccess(tt.store, tt.req, tt.key, false) {
+ t.Errorf("#%d: hasKeyPrefixAccess doesn't match (expected %v)", i, tt.hasRoot)
+ }
+ if tt.hasRecursiveAccess != hasKeyPrefixAccess(tt.store, tt.req, tt.key, true) {
+ t.Errorf("#%d: hasRecursiveAccess doesn't match (expected %v)", i, tt.hasRoot)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_test.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_test.go
new file mode 100644
index 000000000..2d3e2c1b6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/client_test.go
@@ -0,0 +1,1972 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "path"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ etcdErr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/store"
+ "github.com/coreos/etcd/version"
+)
+
+func mustMarshalEvent(t *testing.T, ev *store.Event) string {
+ b := new(bytes.Buffer)
+ if err := json.NewEncoder(b).Encode(ev); err != nil {
+ t.Fatalf("error marshalling event %#v: %v", ev, err)
+ }
+ return b.String()
+}
+
+// mustNewForm takes a set of Values and constructs a PUT *http.Request,
+// with a URL constructed from appending the given path to the standard keysPrefix
+func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
+ u := testutil.MustNewURL(t, path.Join(keysPrefix, p))
+ req, err := http.NewRequest("PUT", u.String(), strings.NewReader(vals.Encode()))
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ if err != nil {
+ t.Fatalf("error creating new request: %v", err)
+ }
+ return req
+}
+
+// mustNewPostForm takes a set of Values and constructs a POST *http.Request,
+// with a URL constructed from appending the given path to the standard keysPrefix
+func mustNewPostForm(t *testing.T, p string, vals url.Values) *http.Request {
+ u := testutil.MustNewURL(t, path.Join(keysPrefix, p))
+ req, err := http.NewRequest("POST", u.String(), strings.NewReader(vals.Encode()))
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ if err != nil {
+ t.Fatalf("error creating new request: %v", err)
+ }
+ return req
+}
+
+// mustNewRequest takes a path, appends it to the standard keysPrefix, and constructs
+// a GET *http.Request referencing the resulting URL
+func mustNewRequest(t *testing.T, p string) *http.Request {
+ return mustNewMethodRequest(t, "GET", p)
+}
+
+func mustNewMethodRequest(t *testing.T, m, p string) *http.Request {
+ return &http.Request{
+ Method: m,
+ URL: testutil.MustNewURL(t, path.Join(keysPrefix, p)),
+ }
+}
+
+type serverRecorder struct {
+ actions []action
+}
+
+func (s *serverRecorder) Start() {}
+func (s *serverRecorder) Stop() {}
+func (s *serverRecorder) Leader() types.ID { return types.ID(1) }
+func (s *serverRecorder) ID() types.ID { return types.ID(1) }
+func (s *serverRecorder) Do(_ context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
+ s.actions = append(s.actions, action{name: "Do", params: []interface{}{r}})
+ return etcdserver.Response{}, nil
+}
+func (s *serverRecorder) Process(_ context.Context, m raftpb.Message) error {
+ s.actions = append(s.actions, action{name: "Process", params: []interface{}{m}})
+ return nil
+}
+func (s *serverRecorder) AddMember(_ context.Context, m etcdserver.Member) error {
+ s.actions = append(s.actions, action{name: "AddMember", params: []interface{}{m}})
+ return nil
+}
+func (s *serverRecorder) RemoveMember(_ context.Context, id uint64) error {
+ s.actions = append(s.actions, action{name: "RemoveMember", params: []interface{}{id}})
+ return nil
+}
+
+func (s *serverRecorder) UpdateMember(_ context.Context, m etcdserver.Member) error {
+ s.actions = append(s.actions, action{name: "UpdateMember", params: []interface{}{m}})
+ return nil
+}
+
+func (s *serverRecorder) ClusterVersion() *semver.Version { return nil }
+
+type action struct {
+ name string
+ params []interface{}
+}
+
+// flushingRecorder provides a channel to allow users to block until the Recorder is Flushed()
+type flushingRecorder struct {
+ *httptest.ResponseRecorder
+ ch chan struct{}
+}
+
+func (fr *flushingRecorder) Flush() {
+ fr.ResponseRecorder.Flush()
+ fr.ch <- struct{}{}
+}
+
+// resServer implements the etcd.Server interface for testing.
+// It returns the given responsefrom any Do calls, and nil error
+type resServer struct {
+ res etcdserver.Response
+}
+
+func (rs *resServer) Start() {}
+func (rs *resServer) Stop() {}
+func (rs *resServer) ID() types.ID { return types.ID(1) }
+func (rs *resServer) Leader() types.ID { return types.ID(1) }
+func (rs *resServer) Do(_ context.Context, _ etcdserverpb.Request) (etcdserver.Response, error) {
+ return rs.res, nil
+}
+func (rs *resServer) Process(_ context.Context, _ raftpb.Message) error { return nil }
+func (rs *resServer) AddMember(_ context.Context, _ etcdserver.Member) error { return nil }
+func (rs *resServer) RemoveMember(_ context.Context, _ uint64) error { return nil }
+func (rs *resServer) UpdateMember(_ context.Context, _ etcdserver.Member) error { return nil }
+func (rs *resServer) ClusterVersion() *semver.Version { return nil }
+
+func boolp(b bool) *bool { return &b }
+
+type dummyRaftTimer struct{}
+
+func (drt dummyRaftTimer) Index() uint64 { return uint64(100) }
+func (drt dummyRaftTimer) Term() uint64 { return uint64(5) }
+
+type dummyWatcher struct {
+ echan chan *store.Event
+ sidx uint64
+}
+
+func (w *dummyWatcher) EventChan() chan *store.Event {
+ return w.echan
+}
+func (w *dummyWatcher) StartIndex() uint64 { return w.sidx }
+func (w *dummyWatcher) Remove() {}
+
+func TestBadParseRequest(t *testing.T) {
+ tests := []struct {
+ in *http.Request
+ wcode int
+ }{
+ {
+ // parseForm failure
+ &http.Request{
+ Body: nil,
+ Method: "PUT",
+ },
+ etcdErr.EcodeInvalidForm,
+ },
+ {
+ // bad key prefix
+ &http.Request{
+ URL: testutil.MustNewURL(t, "/badprefix/"),
+ },
+ etcdErr.EcodeInvalidForm,
+ },
+ // bad values for prevIndex, waitIndex, ttl
+ {
+ mustNewForm(t, "foo", url.Values{"prevIndex": []string{"garbage"}}),
+ etcdErr.EcodeIndexNaN,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"prevIndex": []string{"1.5"}}),
+ etcdErr.EcodeIndexNaN,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"prevIndex": []string{"-1"}}),
+ etcdErr.EcodeIndexNaN,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"waitIndex": []string{"garbage"}}),
+ etcdErr.EcodeIndexNaN,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"waitIndex": []string{"??"}}),
+ etcdErr.EcodeIndexNaN,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"ttl": []string{"-1"}}),
+ etcdErr.EcodeTTLNaN,
+ },
+ // bad values for recursive, sorted, wait, prevExist, dir, stream
+ {
+ mustNewForm(t, "foo", url.Values{"recursive": []string{"hahaha"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"recursive": []string{"1234"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"recursive": []string{"?"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"sorted": []string{"?"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"sorted": []string{"x"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"wait": []string{"?!"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"wait": []string{"yes"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"prevExist": []string{"yes"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"prevExist": []string{"#2"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"dir": []string{"no"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"dir": []string{"file"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"quorum": []string{"no"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"quorum": []string{"file"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"stream": []string{"zzz"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"stream": []string{"something"}}),
+ etcdErr.EcodeInvalidField,
+ },
+ // prevValue cannot be empty
+ {
+ mustNewForm(t, "foo", url.Values{"prevValue": []string{""}}),
+ etcdErr.EcodePrevValueRequired,
+ },
+ // wait is only valid with GET requests
+ {
+ mustNewMethodRequest(t, "HEAD", "foo?wait=true"),
+ etcdErr.EcodeInvalidField,
+ },
+ // query values are considered
+ {
+ mustNewRequest(t, "foo?prevExist=wrong"),
+ etcdErr.EcodeInvalidField,
+ },
+ {
+ mustNewRequest(t, "foo?ttl=wrong"),
+ etcdErr.EcodeTTLNaN,
+ },
+ // but body takes precedence if both are specified
+ {
+ mustNewForm(
+ t,
+ "foo?ttl=12",
+ url.Values{"ttl": []string{"garbage"}},
+ ),
+ etcdErr.EcodeTTLNaN,
+ },
+ {
+ mustNewForm(
+ t,
+ "foo?prevExist=false",
+ url.Values{"prevExist": []string{"yes"}},
+ ),
+ etcdErr.EcodeInvalidField,
+ },
+ }
+ for i, tt := range tests {
+ got, err := parseKeyRequest(tt.in, clockwork.NewFakeClock())
+ if err == nil {
+ t.Errorf("#%d: unexpected nil error!", i)
+ continue
+ }
+ ee, ok := err.(*etcdErr.Error)
+ if !ok {
+ t.Errorf("#%d: err is not etcd.Error!", i)
+ continue
+ }
+ if ee.ErrorCode != tt.wcode {
+ t.Errorf("#%d: code=%d, want %v", i, ee.ErrorCode, tt.wcode)
+ t.Logf("cause: %#v", ee.Cause)
+ }
+ if !reflect.DeepEqual(got, etcdserverpb.Request{}) {
+ t.Errorf("#%d: unexpected non-empty Request: %#v", i, got)
+ }
+ }
+}
+
+func TestGoodParseRequest(t *testing.T) {
+ fc := clockwork.NewFakeClock()
+ fc.Advance(1111)
+ tests := []struct {
+ in *http.Request
+ w etcdserverpb.Request
+ }{
+ {
+ // good prefix, all other values default
+ mustNewRequest(t, "foo"),
+ etcdserverpb.Request{
+ Method: "GET",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // value specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"value": []string{"some_value"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ Val: "some_value",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // prevIndex specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"prevIndex": []string{"98765"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ PrevIndex: 98765,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // recursive specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"recursive": []string{"true"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ Recursive: true,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // sorted specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"sorted": []string{"true"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ Sorted: true,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // quorum specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"quorum": []string{"true"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ Quorum: true,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // wait specified
+ mustNewRequest(t, "foo?wait=true"),
+ etcdserverpb.Request{
+ Method: "GET",
+ Wait: true,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // empty TTL specified
+ mustNewRequest(t, "foo?ttl="),
+ etcdserverpb.Request{
+ Method: "GET",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ Expiration: 0,
+ },
+ },
+ {
+ // non-empty TTL specified
+ mustNewRequest(t, "foo?ttl=5678"),
+ etcdserverpb.Request{
+ Method: "GET",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ Expiration: fc.Now().Add(5678 * time.Second).UnixNano(),
+ },
+ },
+ {
+ // zero TTL specified
+ mustNewRequest(t, "foo?ttl=0"),
+ etcdserverpb.Request{
+ Method: "GET",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ Expiration: fc.Now().UnixNano(),
+ },
+ },
+ {
+ // dir specified
+ mustNewRequest(t, "foo?dir=true"),
+ etcdserverpb.Request{
+ Method: "GET",
+ Dir: true,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // dir specified negatively
+ mustNewRequest(t, "foo?dir=false"),
+ etcdserverpb.Request{
+ Method: "GET",
+ Dir: false,
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // prevExist should be non-null if specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"prevExist": []string{"true"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ PrevExist: boolp(true),
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ {
+ // prevExist should be non-null if specified
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{"prevExist": []string{"false"}},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ PrevExist: boolp(false),
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ // mix various fields
+ {
+ mustNewForm(
+ t,
+ "foo",
+ url.Values{
+ "value": []string{"some value"},
+ "prevExist": []string{"true"},
+ "prevValue": []string{"previous value"},
+ },
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ PrevExist: boolp(true),
+ PrevValue: "previous value",
+ Val: "some value",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ // query parameters should be used if given
+ {
+ mustNewForm(
+ t,
+ "foo?prevValue=woof",
+ url.Values{},
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ PrevValue: "woof",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ // but form values should take precedence over query parameters
+ {
+ mustNewForm(
+ t,
+ "foo?prevValue=woof",
+ url.Values{
+ "prevValue": []string{"miaow"},
+ },
+ ),
+ etcdserverpb.Request{
+ Method: "PUT",
+ PrevValue: "miaow",
+ Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ got, err := parseKeyRequest(tt.in, fc)
+ if err != nil {
+ t.Errorf("#%d: err = %v, want %v", i, err, nil)
+ }
+ if !reflect.DeepEqual(got, tt.w) {
+ t.Errorf("#%d: request=%#v, want %#v", i, got, tt.w)
+ }
+ }
+}
+
+func TestServeMembers(t *testing.T) {
+ memb1 := etcdserver.Member{ID: 12, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
+ memb2 := etcdserver.Member{ID: 13, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
+ cluster := &fakeCluster{
+ id: 1,
+ members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
+ }
+ h := &membersHandler{
+ server: &serverRecorder{},
+ clock: clockwork.NewFakeClock(),
+ cluster: cluster,
+ }
+
+ wmc := string(`{"members":[{"id":"c","name":"","peerURLs":[],"clientURLs":["http://localhost:8080"]},{"id":"d","name":"","peerURLs":[],"clientURLs":["http://localhost:8081"]}]}`)
+
+ tests := []struct {
+ path string
+ wcode int
+ wct string
+ wbody string
+ }{
+ {membersPrefix, http.StatusOK, "application/json", wmc + "\n"},
+ {membersPrefix + "/", http.StatusOK, "application/json", wmc + "\n"},
+ {path.Join(membersPrefix, "100"), http.StatusNotFound, "application/json", `{"message":"Not found"}`},
+ {path.Join(membersPrefix, "foobar"), http.StatusNotFound, "application/json", `{"message":"Not found"}`},
+ }
+
+ for i, tt := range tests {
+ req, err := http.NewRequest("GET", testutil.MustNewURL(t, tt.path).String(), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rw := httptest.NewRecorder()
+ h.ServeHTTP(rw, req)
+
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
+ t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
+ }
+ if rw.Body.String() != tt.wbody {
+ t.Errorf("#%d: body = %q, want %q", i, rw.Body.String(), tt.wbody)
+ }
+ }
+}
+
+// TODO: consolidate **ALL** fake server implementations and add no leader test case.
+func TestServeLeader(t *testing.T) {
+ memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
+ memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
+ cluster := &fakeCluster{
+ id: 1,
+ members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
+ }
+ h := &membersHandler{
+ server: &serverRecorder{},
+ clock: clockwork.NewFakeClock(),
+ cluster: cluster,
+ }
+
+ wmc := string(`{"id":"1","name":"","peerURLs":[],"clientURLs":["http://localhost:8080"]}`)
+
+ tests := []struct {
+ path string
+ wcode int
+ wct string
+ wbody string
+ }{
+ {membersPrefix + "leader", http.StatusOK, "application/json", wmc + "\n"},
+ // TODO: add no leader case
+ }
+
+ for i, tt := range tests {
+ req, err := http.NewRequest("GET", testutil.MustNewURL(t, tt.path).String(), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rw := httptest.NewRecorder()
+ h.ServeHTTP(rw, req)
+
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
+ t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
+ }
+ if rw.Body.String() != tt.wbody {
+ t.Errorf("#%d: body = %q, want %q", i, rw.Body.String(), tt.wbody)
+ }
+ }
+}
+
+func TestServeMembersCreate(t *testing.T) {
+ u := testutil.MustNewURL(t, membersPrefix)
+ b := []byte(`{"peerURLs":["http://127.0.0.1:1"]}`)
+ req, err := http.NewRequest("POST", u.String(), bytes.NewReader(b))
+ if err != nil {
+ t.Fatal(err)
+ }
+ req.Header.Set("Content-Type", "application/json")
+ s := &serverRecorder{}
+ h := &membersHandler{
+ server: s,
+ clock: clockwork.NewFakeClock(),
+ cluster: &fakeCluster{id: 1},
+ }
+ rw := httptest.NewRecorder()
+
+ h.ServeHTTP(rw, req)
+
+ wcode := http.StatusCreated
+ if rw.Code != wcode {
+ t.Errorf("code=%d, want %d", rw.Code, wcode)
+ }
+
+ wct := "application/json"
+ if gct := rw.Header().Get("Content-Type"); gct != wct {
+ t.Errorf("content-type = %s, want %s", gct, wct)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("cid = %s, want %s", gcid, wcid)
+ }
+
+ wb := `{"id":"2a86a83729b330d5","name":"","peerURLs":["http://127.0.0.1:1"],"clientURLs":[]}` + "\n"
+ g := rw.Body.String()
+ if g != wb {
+ t.Errorf("got body=%q, want %q", g, wb)
+ }
+
+ wm := etcdserver.Member{
+ ID: 3064321551348478165,
+ RaftAttributes: etcdserver.RaftAttributes{
+ PeerURLs: []string{"http://127.0.0.1:1"},
+ },
+ }
+
+ wactions := []action{{name: "AddMember", params: []interface{}{wm}}}
+ if !reflect.DeepEqual(s.actions, wactions) {
+ t.Errorf("actions = %+v, want %+v", s.actions, wactions)
+ }
+}
+
+func TestServeMembersDelete(t *testing.T) {
+ req := &http.Request{
+ Method: "DELETE",
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "BEEF")),
+ }
+ s := &serverRecorder{}
+ h := &membersHandler{
+ server: s,
+ cluster: &fakeCluster{id: 1},
+ }
+ rw := httptest.NewRecorder()
+
+ h.ServeHTTP(rw, req)
+
+ wcode := http.StatusNoContent
+ if rw.Code != wcode {
+ t.Errorf("code=%d, want %d", rw.Code, wcode)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("cid = %s, want %s", gcid, wcid)
+ }
+ g := rw.Body.String()
+ if g != "" {
+ t.Errorf("got body=%q, want %q", g, "")
+ }
+ wactions := []action{{name: "RemoveMember", params: []interface{}{uint64(0xBEEF)}}}
+ if !reflect.DeepEqual(s.actions, wactions) {
+ t.Errorf("actions = %+v, want %+v", s.actions, wactions)
+ }
+}
+
+func TestServeMembersUpdate(t *testing.T) {
+ u := testutil.MustNewURL(t, path.Join(membersPrefix, "1"))
+ b := []byte(`{"peerURLs":["http://127.0.0.1:1"]}`)
+ req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(b))
+ if err != nil {
+ t.Fatal(err)
+ }
+ req.Header.Set("Content-Type", "application/json")
+ s := &serverRecorder{}
+ h := &membersHandler{
+ server: s,
+ clock: clockwork.NewFakeClock(),
+ cluster: &fakeCluster{id: 1},
+ }
+ rw := httptest.NewRecorder()
+
+ h.ServeHTTP(rw, req)
+
+ wcode := http.StatusNoContent
+ if rw.Code != wcode {
+ t.Errorf("code=%d, want %d", rw.Code, wcode)
+ }
+
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("cid = %s, want %s", gcid, wcid)
+ }
+
+ wm := etcdserver.Member{
+ ID: 1,
+ RaftAttributes: etcdserver.RaftAttributes{
+ PeerURLs: []string{"http://127.0.0.1:1"},
+ },
+ }
+
+ wactions := []action{{name: "UpdateMember", params: []interface{}{wm}}}
+ if !reflect.DeepEqual(s.actions, wactions) {
+ t.Errorf("actions = %+v, want %+v", s.actions, wactions)
+ }
+}
+
+func TestServeMembersFail(t *testing.T) {
+ tests := []struct {
+ req *http.Request
+ server etcdserver.Server
+
+ wcode int
+ }{
+ {
+ // bad method
+ &http.Request{
+ Method: "CONNECT",
+ },
+ &resServer{},
+
+ http.StatusMethodNotAllowed,
+ },
+ {
+ // bad method
+ &http.Request{
+ Method: "TRACE",
+ },
+ &resServer{},
+
+ http.StatusMethodNotAllowed,
+ },
+ {
+ // parse body error
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "POST",
+ Body: ioutil.NopCloser(strings.NewReader("bad json")),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &resServer{},
+
+ http.StatusBadRequest,
+ },
+ {
+ // bad content type
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "POST",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/bad"}},
+ },
+ &errServer{},
+
+ http.StatusUnsupportedMediaType,
+ },
+ {
+ // bad url
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "POST",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://a"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{},
+
+ http.StatusBadRequest,
+ },
+ {
+ // etcdserver.AddMember error
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "POST",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{
+ errors.New("Error while adding a member"),
+ },
+
+ http.StatusInternalServerError,
+ },
+ {
+ // etcdserver.AddMember error
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "POST",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{
+ etcdserver.ErrIDExists,
+ },
+
+ http.StatusConflict,
+ },
+ {
+ // etcdserver.AddMember error
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "POST",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{
+ etcdserver.ErrPeerURLexists,
+ },
+
+ http.StatusConflict,
+ },
+ {
+ // etcdserver.RemoveMember error with arbitrary server error
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "1")),
+ Method: "DELETE",
+ },
+ &errServer{
+ errors.New("Error while removing member"),
+ },
+
+ http.StatusInternalServerError,
+ },
+ {
+ // etcdserver.RemoveMember error with previously removed ID
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "DELETE",
+ },
+ &errServer{
+ etcdserver.ErrIDRemoved,
+ },
+
+ http.StatusGone,
+ },
+ {
+ // etcdserver.RemoveMember error with nonexistent ID
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "DELETE",
+ },
+ &errServer{
+ etcdserver.ErrIDNotFound,
+ },
+
+ http.StatusNotFound,
+ },
+ {
+ // etcdserver.RemoveMember error with badly formed ID
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "bad_id")),
+ Method: "DELETE",
+ },
+ nil,
+
+ http.StatusNotFound,
+ },
+ {
+ // etcdserver.RemoveMember with no ID
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "DELETE",
+ },
+ nil,
+
+ http.StatusMethodNotAllowed,
+ },
+ {
+ // parse body error
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "PUT",
+ Body: ioutil.NopCloser(strings.NewReader("bad json")),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &resServer{},
+
+ http.StatusBadRequest,
+ },
+ {
+ // bad content type
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "PUT",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/bad"}},
+ },
+ &errServer{},
+
+ http.StatusUnsupportedMediaType,
+ },
+ {
+ // bad url
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "PUT",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://a"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{},
+
+ http.StatusBadRequest,
+ },
+ {
+ // etcdserver.UpdateMember error
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "PUT",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{
+ errors.New("blah"),
+ },
+
+ http.StatusInternalServerError,
+ },
+ {
+ // etcdserver.UpdateMember error
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "PUT",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{
+ etcdserver.ErrPeerURLexists,
+ },
+
+ http.StatusConflict,
+ },
+ {
+ // etcdserver.UpdateMember error
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
+ Method: "PUT",
+ Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
+ Header: map[string][]string{"Content-Type": {"application/json"}},
+ },
+ &errServer{
+ etcdserver.ErrIDNotFound,
+ },
+
+ http.StatusNotFound,
+ },
+ {
+ // etcdserver.UpdateMember error with badly formed ID
+ &http.Request{
+ URL: testutil.MustNewURL(t, path.Join(membersPrefix, "bad_id")),
+ Method: "PUT",
+ },
+ nil,
+
+ http.StatusNotFound,
+ },
+ {
+ // etcdserver.UpdateMember with no ID
+ &http.Request{
+ URL: testutil.MustNewURL(t, membersPrefix),
+ Method: "PUT",
+ },
+ nil,
+
+ http.StatusMethodNotAllowed,
+ },
+ }
+ for i, tt := range tests {
+ h := &membersHandler{
+ server: tt.server,
+ cluster: &fakeCluster{id: 1},
+ clock: clockwork.NewFakeClock(),
+ }
+ rw := httptest.NewRecorder()
+ h.ServeHTTP(rw, tt.req)
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ if rw.Code != http.StatusMethodNotAllowed {
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
+ }
+ }
+ }
+}
+
+func TestWriteEvent(t *testing.T) {
+ // nil event should not panic
+ rec := httptest.NewRecorder()
+ writeKeyEvent(rec, nil, dummyRaftTimer{})
+ h := rec.Header()
+ if len(h) > 0 {
+ t.Fatalf("unexpected non-empty headers: %#v", h)
+ }
+ b := rec.Body.String()
+ if len(b) > 0 {
+ t.Fatalf("unexpected non-empty body: %q", b)
+ }
+
+ tests := []struct {
+ ev *store.Event
+ idx string
+ // TODO(jonboulle): check body as well as just status code
+ code int
+ err error
+ }{
+ // standard case, standard 200 response
+ {
+ &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ PrevNode: &store.NodeExtern{},
+ },
+ "0",
+ http.StatusOK,
+ nil,
+ },
+ // check new nodes return StatusCreated
+ {
+ &store.Event{
+ Action: store.Create,
+ Node: &store.NodeExtern{},
+ PrevNode: &store.NodeExtern{},
+ },
+ "0",
+ http.StatusCreated,
+ nil,
+ },
+ }
+
+ for i, tt := range tests {
+ rw := httptest.NewRecorder()
+ writeKeyEvent(rw, tt.ev, dummyRaftTimer{})
+ if gct := rw.Header().Get("Content-Type"); gct != "application/json" {
+ t.Errorf("case %d: bad Content-Type: got %q, want application/json", i, gct)
+ }
+ if gri := rw.Header().Get("X-Raft-Index"); gri != "100" {
+ t.Errorf("case %d: bad X-Raft-Index header: got %s, want %s", i, gri, "100")
+ }
+ if grt := rw.Header().Get("X-Raft-Term"); grt != "5" {
+ t.Errorf("case %d: bad X-Raft-Term header: got %s, want %s", i, grt, "5")
+ }
+ if gei := rw.Header().Get("X-Etcd-Index"); gei != tt.idx {
+ t.Errorf("case %d: bad X-Etcd-Index header: got %s, want %s", i, gei, tt.idx)
+ }
+ if rw.Code != tt.code {
+ t.Errorf("case %d: bad response code: got %d, want %v", i, rw.Code, tt.code)
+ }
+
+ }
+}
+
+func TestV2DeprecatedMachinesEndpoint(t *testing.T) {
+ tests := []struct {
+ method string
+ wcode int
+ }{
+ {"GET", http.StatusOK},
+ {"HEAD", http.StatusOK},
+ {"POST", http.StatusMethodNotAllowed},
+ }
+
+ m := &deprecatedMachinesHandler{cluster: &fakeCluster{}}
+ s := httptest.NewServer(m)
+ defer s.Close()
+
+ for _, tt := range tests {
+ req, err := http.NewRequest(tt.method, s.URL+deprecatedMachinesPrefix, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if resp.StatusCode != tt.wcode {
+ t.Errorf("StatusCode = %d, expected %d", resp.StatusCode, tt.wcode)
+ }
+ }
+}
+
+func TestServeMachines(t *testing.T) {
+ cluster := &fakeCluster{
+ clientURLs: []string{"http://localhost:8080", "http://localhost:8081", "http://localhost:8082"},
+ }
+ writer := httptest.NewRecorder()
+ req, err := http.NewRequest("GET", "", nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ h := &deprecatedMachinesHandler{cluster: cluster}
+ h.ServeHTTP(writer, req)
+ w := "http://localhost:8080, http://localhost:8081, http://localhost:8082"
+ if g := writer.Body.String(); g != w {
+ t.Errorf("body = %s, want %s", g, w)
+ }
+ if writer.Code != http.StatusOK {
+ t.Errorf("code = %d, want %d", writer.Code, http.StatusOK)
+ }
+}
+
+func TestGetID(t *testing.T) {
+ tests := []struct {
+ path string
+
+ wok bool
+ wid types.ID
+ wcode int
+ }{
+ {
+ "123",
+ true, 0x123, http.StatusOK,
+ },
+ {
+ "bad_id",
+ false, 0, http.StatusNotFound,
+ },
+ {
+ "",
+ false, 0, http.StatusMethodNotAllowed,
+ },
+ }
+
+ for i, tt := range tests {
+ w := httptest.NewRecorder()
+ id, ok := getID(tt.path, w)
+ if id != tt.wid {
+ t.Errorf("#%d: id = %d, want %d", i, id, tt.wid)
+ }
+ if ok != tt.wok {
+ t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
+ }
+ if w.Code != tt.wcode {
+ t.Errorf("#%d code = %d, want %d", i, w.Code, tt.wcode)
+ }
+ }
+}
+
+type dummyStats struct {
+ data []byte
+}
+
+func (ds *dummyStats) SelfStats() []byte { return ds.data }
+func (ds *dummyStats) LeaderStats() []byte { return ds.data }
+func (ds *dummyStats) StoreStats() []byte { return ds.data }
+func (ds *dummyStats) UpdateRecvApp(_ types.ID, _ int64) {}
+
+func TestServeSelfStats(t *testing.T) {
+ wb := []byte("some statistics")
+ w := string(wb)
+ sh := &statsHandler{
+ stats: &dummyStats{data: wb},
+ }
+ rw := httptest.NewRecorder()
+ sh.serveSelf(rw, &http.Request{Method: "GET"})
+ if rw.Code != http.StatusOK {
+ t.Errorf("code = %d, want %d", rw.Code, http.StatusOK)
+ }
+ wct := "application/json"
+ if gct := rw.Header().Get("Content-Type"); gct != wct {
+ t.Errorf("Content-Type = %q, want %q", gct, wct)
+ }
+ if g := rw.Body.String(); g != w {
+ t.Errorf("body = %s, want %s", g, w)
+ }
+}
+
+func TestSelfServeStatsBad(t *testing.T) {
+ for _, m := range []string{"PUT", "POST", "DELETE"} {
+ sh := &statsHandler{}
+ rw := httptest.NewRecorder()
+ sh.serveSelf(
+ rw,
+ &http.Request{
+ Method: m,
+ },
+ )
+ if rw.Code != http.StatusMethodNotAllowed {
+ t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
+ }
+ }
+}
+
+func TestLeaderServeStatsBad(t *testing.T) {
+ for _, m := range []string{"PUT", "POST", "DELETE"} {
+ sh := &statsHandler{}
+ rw := httptest.NewRecorder()
+ sh.serveLeader(
+ rw,
+ &http.Request{
+ Method: m,
+ },
+ )
+ if rw.Code != http.StatusMethodNotAllowed {
+ t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
+ }
+ }
+}
+
+func TestServeLeaderStats(t *testing.T) {
+ wb := []byte("some statistics")
+ w := string(wb)
+ sh := &statsHandler{
+ stats: &dummyStats{data: wb},
+ }
+ rw := httptest.NewRecorder()
+ sh.serveLeader(rw, &http.Request{Method: "GET"})
+ if rw.Code != http.StatusOK {
+ t.Errorf("code = %d, want %d", rw.Code, http.StatusOK)
+ }
+ wct := "application/json"
+ if gct := rw.Header().Get("Content-Type"); gct != wct {
+ t.Errorf("Content-Type = %q, want %q", gct, wct)
+ }
+ if g := rw.Body.String(); g != w {
+ t.Errorf("body = %s, want %s", g, w)
+ }
+}
+
+func TestServeStoreStats(t *testing.T) {
+ wb := []byte("some statistics")
+ w := string(wb)
+ sh := &statsHandler{
+ stats: &dummyStats{data: wb},
+ }
+ rw := httptest.NewRecorder()
+ sh.serveStore(rw, &http.Request{Method: "GET"})
+ if rw.Code != http.StatusOK {
+ t.Errorf("code = %d, want %d", rw.Code, http.StatusOK)
+ }
+ wct := "application/json"
+ if gct := rw.Header().Get("Content-Type"); gct != wct {
+ t.Errorf("Content-Type = %q, want %q", gct, wct)
+ }
+ if g := rw.Body.String(); g != w {
+ t.Errorf("body = %s, want %s", g, w)
+ }
+
+}
+
+func TestServeVersion(t *testing.T) {
+ req, err := http.NewRequest("GET", "", nil)
+ if err != nil {
+ t.Fatalf("error creating request: %v", err)
+ }
+ rw := httptest.NewRecorder()
+ serveVersion(rw, req, "2.1.0")
+ if rw.Code != http.StatusOK {
+ t.Errorf("code=%d, want %d", rw.Code, http.StatusOK)
+ }
+ vs := version.Versions{
+ Server: version.Version,
+ Cluster: "2.1.0",
+ }
+ w, err := json.Marshal(&vs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if g := rw.Body.String(); g != string(w) {
+ t.Fatalf("body = %q, want %q", g, string(w))
+ }
+ if ct := rw.HeaderMap.Get("Content-Type"); ct != "application/json" {
+ t.Errorf("contet-type header = %s, want %s", ct, "application/json")
+ }
+}
+
+func TestServeVersionFails(t *testing.T) {
+ for _, m := range []string{
+ "CONNECT", "TRACE", "PUT", "POST", "HEAD",
+ } {
+ req, err := http.NewRequest(m, "", nil)
+ if err != nil {
+ t.Fatalf("error creating request: %v", err)
+ }
+ rw := httptest.NewRecorder()
+ serveVersion(rw, req, "2.1.0")
+ if rw.Code != http.StatusMethodNotAllowed {
+ t.Errorf("method %s: code=%d, want %d", m, rw.Code, http.StatusMethodNotAllowed)
+ }
+ }
+}
+
+func TestBadServeKeys(t *testing.T) {
+ testBadCases := []struct {
+ req *http.Request
+ server etcdserver.Server
+
+ wcode int
+ wbody string
+ }{
+ {
+ // bad method
+ &http.Request{
+ Method: "CONNECT",
+ },
+ &resServer{},
+
+ http.StatusMethodNotAllowed,
+ "Method Not Allowed",
+ },
+ {
+ // bad method
+ &http.Request{
+ Method: "TRACE",
+ },
+ &resServer{},
+
+ http.StatusMethodNotAllowed,
+ "Method Not Allowed",
+ },
+ {
+ // parseRequest error
+ &http.Request{
+ Body: nil,
+ Method: "PUT",
+ },
+ &resServer{},
+
+ http.StatusBadRequest,
+ `{"errorCode":210,"message":"Invalid POST form","cause":"missing form body","index":0}`,
+ },
+ {
+ // etcdserver.Server error
+ mustNewRequest(t, "foo"),
+ &errServer{
+ errors.New("Internal Server Error"),
+ },
+
+ http.StatusInternalServerError,
+ `{"errorCode":300,"message":"Raft Internal Error","cause":"Internal Server Error","index":0}`,
+ },
+ {
+ // etcdserver.Server etcd error
+ mustNewRequest(t, "foo"),
+ &errServer{
+ etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/1/pant", 0),
+ },
+
+ http.StatusNotFound,
+ `{"errorCode":100,"message":"Key not found","cause":"/pant","index":0}`,
+ },
+ {
+ // non-event/watcher response from etcdserver.Server
+ mustNewRequest(t, "foo"),
+ &resServer{
+ etcdserver.Response{},
+ },
+
+ http.StatusInternalServerError,
+ `{"errorCode":300,"message":"Raft Internal Error","cause":"received response with no Event/Watcher!","index":0}`,
+ },
+ }
+ for i, tt := range testBadCases {
+ h := &keysHandler{
+ timeout: 0, // context times out immediately
+ server: tt.server,
+ cluster: &fakeCluster{id: 1},
+ }
+ rw := httptest.NewRecorder()
+ h.ServeHTTP(rw, tt.req)
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ if rw.Code != http.StatusMethodNotAllowed {
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
+ }
+ }
+ if g := strings.TrimSuffix(rw.Body.String(), "\n"); g != tt.wbody {
+ t.Errorf("#%d: body = %s, want %s", i, g, tt.wbody)
+ }
+ }
+}
+
+func TestServeKeysGood(t *testing.T) {
+ tests := []struct {
+ req *http.Request
+ wcode int
+ }{
+ {
+ mustNewMethodRequest(t, "HEAD", "foo"),
+ http.StatusOK,
+ },
+ {
+ mustNewMethodRequest(t, "GET", "foo"),
+ http.StatusOK,
+ },
+ {
+ mustNewForm(t, "foo", url.Values{"value": []string{"bar"}}),
+ http.StatusOK,
+ },
+ {
+ mustNewMethodRequest(t, "DELETE", "foo"),
+ http.StatusOK,
+ },
+ {
+ mustNewPostForm(t, "foo", url.Values{"value": []string{"bar"}}),
+ http.StatusOK,
+ },
+ }
+ server := &resServer{
+ etcdserver.Response{
+ Event: &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ },
+ },
+ }
+ for i, tt := range tests {
+ h := &keysHandler{
+ timeout: time.Hour,
+ server: server,
+ timer: &dummyRaftTimer{},
+ cluster: &fakeCluster{id: 1},
+ }
+ rw := httptest.NewRecorder()
+ h.ServeHTTP(rw, tt.req)
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ }
+}
+
+func TestServeKeysEvent(t *testing.T) {
+ req := mustNewRequest(t, "foo")
+ server := &resServer{
+ etcdserver.Response{
+ Event: &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ },
+ },
+ }
+ h := &keysHandler{
+ timeout: time.Hour,
+ server: server,
+ cluster: &fakeCluster{id: 1},
+ timer: &dummyRaftTimer{},
+ }
+ rw := httptest.NewRecorder()
+
+ h.ServeHTTP(rw, req)
+
+ wcode := http.StatusOK
+ wbody := mustMarshalEvent(
+ t,
+ &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ },
+ )
+
+ if rw.Code != wcode {
+ t.Errorf("got code=%d, want %d", rw.Code, wcode)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("cid = %s, want %s", gcid, wcid)
+ }
+ g := rw.Body.String()
+ if g != wbody {
+ t.Errorf("got body=%#v, want %#v", g, wbody)
+ }
+}
+
+func TestServeKeysWatch(t *testing.T) {
+ req := mustNewRequest(t, "/foo/bar")
+ ec := make(chan *store.Event)
+ dw := &dummyWatcher{
+ echan: ec,
+ }
+ server := &resServer{
+ etcdserver.Response{
+ Watcher: dw,
+ },
+ }
+ h := &keysHandler{
+ timeout: time.Hour,
+ server: server,
+ cluster: &fakeCluster{id: 1},
+ timer: &dummyRaftTimer{},
+ }
+ go func() {
+ ec <- &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ }
+ }()
+ rw := httptest.NewRecorder()
+
+ h.ServeHTTP(rw, req)
+
+ wcode := http.StatusOK
+ wbody := mustMarshalEvent(
+ t,
+ &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ },
+ )
+
+ if rw.Code != wcode {
+ t.Errorf("got code=%d, want %d", rw.Code, wcode)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := h.cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("cid = %s, want %s", gcid, wcid)
+ }
+ g := rw.Body.String()
+ if g != wbody {
+ t.Errorf("got body=%#v, want %#v", g, wbody)
+ }
+}
+
+type recordingCloseNotifier struct {
+ *httptest.ResponseRecorder
+ cn chan bool
+}
+
+func (rcn *recordingCloseNotifier) CloseNotify() <-chan bool {
+ return rcn.cn
+}
+
+func TestHandleWatch(t *testing.T) {
+ defaultRwRr := func() (http.ResponseWriter, *httptest.ResponseRecorder) {
+ r := httptest.NewRecorder()
+ return r, r
+ }
+ noopEv := func(chan *store.Event) {}
+
+ tests := []struct {
+ getCtx func() context.Context
+ getRwRr func() (http.ResponseWriter, *httptest.ResponseRecorder)
+ doToChan func(chan *store.Event)
+
+ wbody string
+ }{
+ {
+ // Normal case: one event
+ context.Background,
+ defaultRwRr,
+ func(ch chan *store.Event) {
+ ch <- &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ }
+ },
+
+ mustMarshalEvent(
+ t,
+ &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ },
+ ),
+ },
+ {
+ // Channel is closed, no event
+ context.Background,
+ defaultRwRr,
+ func(ch chan *store.Event) {
+ close(ch)
+ },
+
+ "",
+ },
+ {
+ // Simulate a timed-out context
+ func() context.Context {
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+ return ctx
+ },
+ defaultRwRr,
+ noopEv,
+
+ "",
+ },
+ {
+ // Close-notifying request
+ context.Background,
+ func() (http.ResponseWriter, *httptest.ResponseRecorder) {
+ rw := &recordingCloseNotifier{
+ ResponseRecorder: httptest.NewRecorder(),
+ cn: make(chan bool, 1),
+ }
+ rw.cn <- true
+ return rw, rw.ResponseRecorder
+ },
+ noopEv,
+
+ "",
+ },
+ }
+
+ for i, tt := range tests {
+ rw, rr := tt.getRwRr()
+ wa := &dummyWatcher{
+ echan: make(chan *store.Event, 1),
+ sidx: 10,
+ }
+ tt.doToChan(wa.echan)
+
+ handleKeyWatch(tt.getCtx(), rw, wa, false, dummyRaftTimer{})
+
+ wcode := http.StatusOK
+ wct := "application/json"
+ wei := "10"
+ wri := "100"
+ wrt := "5"
+
+ if rr.Code != wcode {
+ t.Errorf("#%d: got code=%d, want %d", i, rr.Code, wcode)
+ }
+ h := rr.Header()
+ if ct := h.Get("Content-Type"); ct != wct {
+ t.Errorf("#%d: Content-Type=%q, want %q", i, ct, wct)
+ }
+ if ei := h.Get("X-Etcd-Index"); ei != wei {
+ t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, ei, wei)
+ }
+ if ri := h.Get("X-Raft-Index"); ri != wri {
+ t.Errorf("#%d: X-Raft-Index=%q, want %q", i, ri, wri)
+ }
+ if rt := h.Get("X-Raft-Term"); rt != wrt {
+ t.Errorf("#%d: X-Raft-Term=%q, want %q", i, rt, wrt)
+ }
+ g := rr.Body.String()
+ if g != tt.wbody {
+ t.Errorf("#%d: got body=%#v, want %#v", i, g, tt.wbody)
+ }
+ }
+}
+
+func TestHandleWatchStreaming(t *testing.T) {
+ rw := &flushingRecorder{
+ httptest.NewRecorder(),
+ make(chan struct{}, 1),
+ }
+ wa := &dummyWatcher{
+ echan: make(chan *store.Event),
+ }
+
+ // Launch the streaming handler in the background with a cancellable context
+ ctx, cancel := context.WithCancel(context.Background())
+ done := make(chan struct{})
+ go func() {
+ handleKeyWatch(ctx, rw, wa, true, dummyRaftTimer{})
+ close(done)
+ }()
+
+ // Expect one Flush for the headers etc.
+ select {
+ case <-rw.ch:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for flush")
+ }
+
+ // Expect headers but no body
+ wcode := http.StatusOK
+ wct := "application/json"
+ wbody := ""
+
+ if rw.Code != wcode {
+ t.Errorf("got code=%d, want %d", rw.Code, wcode)
+ }
+ h := rw.Header()
+ if ct := h.Get("Content-Type"); ct != wct {
+ t.Errorf("Content-Type=%q, want %q", ct, wct)
+ }
+ g := rw.Body.String()
+ if g != wbody {
+ t.Errorf("got body=%#v, want %#v", g, wbody)
+ }
+
+ // Now send the first event
+ select {
+ case wa.echan <- &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ }:
+ case <-time.After(time.Second):
+ t.Fatal("timed out waiting for send")
+ }
+
+ // Wait for it to be flushed...
+ select {
+ case <-rw.ch:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for flush")
+ }
+
+ // And check the body is as expected
+ wbody = mustMarshalEvent(
+ t,
+ &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ },
+ )
+ g = rw.Body.String()
+ if g != wbody {
+ t.Errorf("got body=%#v, want %#v", g, wbody)
+ }
+
+ // Rinse and repeat
+ select {
+ case wa.echan <- &store.Event{
+ Action: store.Get,
+ Node: &store.NodeExtern{},
+ }:
+ case <-time.After(time.Second):
+ t.Fatal("timed out waiting for send")
+ }
+
+ select {
+ case <-rw.ch:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for flush")
+ }
+
+ // This time, we expect to see both events
+ wbody = wbody + wbody
+ g = rw.Body.String()
+ if g != wbody {
+ t.Errorf("got body=%#v, want %#v", g, wbody)
+ }
+
+ // Finally, time out the connection and ensure the serving goroutine returns
+ cancel()
+
+ select {
+ case <-done:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for done")
+ }
+}
+
+func TestTrimEventPrefix(t *testing.T) {
+ pre := "/abc"
+ tests := []struct {
+ ev *store.Event
+ wev *store.Event
+ }{
+ {
+ nil,
+ nil,
+ },
+ {
+ &store.Event{},
+ &store.Event{},
+ },
+ {
+ &store.Event{Node: &store.NodeExtern{Key: "/abc/def"}},
+ &store.Event{Node: &store.NodeExtern{Key: "/def"}},
+ },
+ {
+ &store.Event{PrevNode: &store.NodeExtern{Key: "/abc/ghi"}},
+ &store.Event{PrevNode: &store.NodeExtern{Key: "/ghi"}},
+ },
+ {
+ &store.Event{
+ Node: &store.NodeExtern{Key: "/abc/def"},
+ PrevNode: &store.NodeExtern{Key: "/abc/ghi"},
+ },
+ &store.Event{
+ Node: &store.NodeExtern{Key: "/def"},
+ PrevNode: &store.NodeExtern{Key: "/ghi"},
+ },
+ },
+ }
+ for i, tt := range tests {
+ ev := trimEventPrefix(tt.ev, pre)
+ if !reflect.DeepEqual(ev, tt.wev) {
+ t.Errorf("#%d: event = %+v, want %+v", i, ev, tt.wev)
+ }
+ }
+}
+
+func TestTrimNodeExternPrefix(t *testing.T) {
+ pre := "/abc"
+ tests := []struct {
+ n *store.NodeExtern
+ wn *store.NodeExtern
+ }{
+ {
+ nil,
+ nil,
+ },
+ {
+ &store.NodeExtern{Key: "/abc/def"},
+ &store.NodeExtern{Key: "/def"},
+ },
+ {
+ &store.NodeExtern{
+ Key: "/abc/def",
+ Nodes: []*store.NodeExtern{
+ {Key: "/abc/def/1"},
+ {Key: "/abc/def/2"},
+ },
+ },
+ &store.NodeExtern{
+ Key: "/def",
+ Nodes: []*store.NodeExtern{
+ {Key: "/def/1"},
+ {Key: "/def/2"},
+ },
+ },
+ },
+ }
+ for i, tt := range tests {
+ n := trimNodeExternPrefix(tt.n, pre)
+ if !reflect.DeepEqual(n, tt.wn) {
+ t.Errorf("#%d: node = %+v, want %+v", i, n, tt.wn)
+ }
+ }
+}
+
+func TestTrimPrefix(t *testing.T) {
+ tests := []struct {
+ in string
+ prefix string
+ w string
+ }{
+ {"/v2/members", "/v2/members", ""},
+ {"/v2/members/", "/v2/members", ""},
+ {"/v2/members/foo", "/v2/members", "foo"},
+ }
+ for i, tt := range tests {
+ if g := trimPrefix(tt.in, tt.prefix); g != tt.w {
+ t.Errorf("#%d: trimPrefix = %q, want %q", i, g, tt.w)
+ }
+ }
+}
+
+func TestNewMemberCollection(t *testing.T) {
+ fixture := []*etcdserver.Member{
+ {
+ ID: 12,
+ Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080", "http://localhost:8081"}},
+ RaftAttributes: etcdserver.RaftAttributes{PeerURLs: []string{"http://localhost:8082", "http://localhost:8083"}},
+ },
+ {
+ ID: 13,
+ Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:9090", "http://localhost:9091"}},
+ RaftAttributes: etcdserver.RaftAttributes{PeerURLs: []string{"http://localhost:9092", "http://localhost:9093"}},
+ },
+ }
+ got := newMemberCollection(fixture)
+
+ want := httptypes.MemberCollection([]httptypes.Member{
+ {
+ ID: "c",
+ ClientURLs: []string{"http://localhost:8080", "http://localhost:8081"},
+ PeerURLs: []string{"http://localhost:8082", "http://localhost:8083"},
+ },
+ {
+ ID: "d",
+ ClientURLs: []string{"http://localhost:9090", "http://localhost:9091"},
+ PeerURLs: []string{"http://localhost:9092", "http://localhost:9093"},
+ },
+ })
+
+ if !reflect.DeepEqual(&want, got) {
+ t.Fatalf("newMemberCollection failure: want=%#v, got=%#v", &want, got)
+ }
+}
+
+func TestNewMember(t *testing.T) {
+ fixture := &etcdserver.Member{
+ ID: 12,
+ Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080", "http://localhost:8081"}},
+ RaftAttributes: etcdserver.RaftAttributes{PeerURLs: []string{"http://localhost:8082", "http://localhost:8083"}},
+ }
+ got := newMember(fixture)
+
+ want := httptypes.Member{
+ ID: "c",
+ ClientURLs: []string{"http://localhost:8080", "http://localhost:8081"},
+ PeerURLs: []string{"http://localhost:8082", "http://localhost:8083"},
+ }
+
+ if !reflect.DeepEqual(want, got) {
+ t.Fatalf("newMember failure: want=%#v, got=%#v", want, got)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/http.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/http.go
new file mode 100644
index 000000000..eb82fc4a0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/http.go
@@ -0,0 +1,93 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "errors"
+ "math"
+ "net/http"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ etcdErr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/auth"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+)
+
+const (
+ // time to wait for a Watch request
+ defaultWatchTimeout = time.Duration(math.MaxInt64)
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdhttp")
+ errClosed = errors.New("etcdhttp: client closed connection")
+)
+
+// writeError logs and writes the given Error to the ResponseWriter
+// If Error is an etcdErr, it is rendered to the ResponseWriter
+// Otherwise, it is assumed to be an InternalServerError
+func writeError(w http.ResponseWriter, r *http.Request, err error) {
+ if err == nil {
+ return
+ }
+ switch e := err.(type) {
+ case *etcdErr.Error:
+ e.WriteTo(w)
+ case *httptypes.HTTPError:
+ if et := e.WriteTo(w); et != nil {
+ plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr)
+ }
+ case auth.Error:
+ herr := httptypes.NewHTTPError(e.HTTPStatus(), e.Error())
+ if et := herr.WriteTo(w); et != nil {
+ plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr)
+ }
+ default:
+ switch err {
+ case etcdserver.ErrTimeoutDueToLeaderFail, etcdserver.ErrTimeoutDueToConnectionLost:
+ plog.Error(err)
+ default:
+ plog.Errorf("got unexpected response error (%v)", err)
+ }
+ herr := httptypes.NewHTTPError(http.StatusInternalServerError, "Internal Server Error")
+ if et := herr.WriteTo(w); et != nil {
+ plog.Debugf("error writing HTTPError (%v) to %s", et, r.RemoteAddr)
+ }
+ }
+}
+
+// allowMethod verifies that the given method is one of the allowed methods,
+// and if not, it writes an error to w. A boolean is returned indicating
+// whether or not the method is allowed.
+func allowMethod(w http.ResponseWriter, m string, ms ...string) bool {
+ for _, meth := range ms {
+ if m == meth {
+ return true
+ }
+ }
+ w.Header().Set("Allow", strings.Join(ms, ","))
+ http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+ return false
+}
+
+func requestLogger(handler http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ plog.Debugf("[%s] %s remote:%s", r.Method, r.RequestURI, r.RemoteAddr)
+ handler.ServeHTTP(w, r)
+ })
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/http_test.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/http_test.go
new file mode 100644
index 000000000..bd2b096f1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/http_test.go
@@ -0,0 +1,192 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "errors"
+ "net/http"
+ "net/http/httptest"
+ "sort"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ etcdErr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+type fakeCluster struct {
+ id uint64
+ clientURLs []string
+ members map[uint64]*etcdserver.Member
+}
+
+func (c *fakeCluster) ID() types.ID { return types.ID(c.id) }
+func (c *fakeCluster) ClientURLs() []string { return c.clientURLs }
+func (c *fakeCluster) Members() []*etcdserver.Member {
+ var ms etcdserver.MembersByID
+ for _, m := range c.members {
+ ms = append(ms, m)
+ }
+ sort.Sort(ms)
+ return []*etcdserver.Member(ms)
+}
+func (c *fakeCluster) Member(id types.ID) *etcdserver.Member { return c.members[uint64(id)] }
+func (c *fakeCluster) IsIDRemoved(id types.ID) bool { return false }
+func (c *fakeCluster) Version() *semver.Version { return nil }
+
+// errServer implements the etcd.Server interface for testing.
+// It returns the given error from any Do/Process/AddMember/RemoveMember calls.
+type errServer struct {
+ err error
+}
+
+func (fs *errServer) Start() {}
+func (fs *errServer) Stop() {}
+func (fs *errServer) ID() types.ID { return types.ID(1) }
+func (fs *errServer) Leader() types.ID { return types.ID(1) }
+func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
+ return etcdserver.Response{}, fs.err
+}
+func (fs *errServer) Process(ctx context.Context, m raftpb.Message) error {
+ return fs.err
+}
+func (fs *errServer) AddMember(ctx context.Context, m etcdserver.Member) error {
+ return fs.err
+}
+func (fs *errServer) RemoveMember(ctx context.Context, id uint64) error {
+ return fs.err
+}
+func (fs *errServer) UpdateMember(ctx context.Context, m etcdserver.Member) error {
+ return fs.err
+}
+
+func (fs *errServer) ClusterVersion() *semver.Version { return nil }
+
+func TestWriteError(t *testing.T) {
+ // nil error should not panic
+ rec := httptest.NewRecorder()
+ r := new(http.Request)
+ writeError(rec, r, nil)
+ h := rec.Header()
+ if len(h) > 0 {
+ t.Fatalf("unexpected non-empty headers: %#v", h)
+ }
+ b := rec.Body.String()
+ if len(b) > 0 {
+ t.Fatalf("unexpected non-empty body: %q", b)
+ }
+
+ tests := []struct {
+ err error
+ wcode int
+ wi string
+ }{
+ {
+ etcdErr.NewError(etcdErr.EcodeKeyNotFound, "/foo/bar", 123),
+ http.StatusNotFound,
+ "123",
+ },
+ {
+ etcdErr.NewError(etcdErr.EcodeTestFailed, "/foo/bar", 456),
+ http.StatusPreconditionFailed,
+ "456",
+ },
+ {
+ err: errors.New("something went wrong"),
+ wcode: http.StatusInternalServerError,
+ },
+ }
+
+ for i, tt := range tests {
+ rw := httptest.NewRecorder()
+ writeError(rw, r, tt.err)
+ if code := rw.Code; code != tt.wcode {
+ t.Errorf("#%d: code=%d, want %d", i, code, tt.wcode)
+ }
+ if idx := rw.Header().Get("X-Etcd-Index"); idx != tt.wi {
+ t.Errorf("#%d: X-Etcd-Index=%q, want %q", i, idx, tt.wi)
+ }
+ }
+}
+
+func TestAllowMethod(t *testing.T) {
+ tests := []struct {
+ m string
+ ms []string
+ w bool
+ wh string
+ }{
+ // Accepted methods
+ {
+ m: "GET",
+ ms: []string{"GET", "POST", "PUT"},
+ w: true,
+ },
+ {
+ m: "POST",
+ ms: []string{"POST"},
+ w: true,
+ },
+ // Made-up methods no good
+ {
+ m: "FAKE",
+ ms: []string{"GET", "POST", "PUT"},
+ w: false,
+ wh: "GET,POST,PUT",
+ },
+ // Empty methods no good
+ {
+ m: "",
+ ms: []string{"GET", "POST"},
+ w: false,
+ wh: "GET,POST",
+ },
+ // Empty accepted methods no good
+ {
+ m: "GET",
+ ms: []string{""},
+ w: false,
+ wh: "",
+ },
+ // No methods accepted
+ {
+ m: "GET",
+ ms: []string{},
+ w: false,
+ wh: "",
+ },
+ }
+
+ for i, tt := range tests {
+ rw := httptest.NewRecorder()
+ g := allowMethod(rw, tt.m, tt.ms...)
+ if g != tt.w {
+ t.Errorf("#%d: got allowMethod()=%t, want %t", i, g, tt.w)
+ }
+ if !tt.w {
+ if rw.Code != http.StatusMethodNotAllowed {
+ t.Errorf("#%d: code=%d, want %d", i, rw.Code, http.StatusMethodNotAllowed)
+ }
+ gh := rw.Header().Get("Allow")
+ if gh != tt.wh {
+ t.Errorf("#%d: Allow header=%q, want %q", i, gh, tt.wh)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/doc.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/doc.go
new file mode 100644
index 000000000..fa0158020
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/doc.go
@@ -0,0 +1,19 @@
+// Copyright 2015 CoreOS, 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 httptypes defines how etcd's HTTP API entities are serialized to and deserialized from JSON.
+*/
+
+package httptypes
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/errors.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/errors.go
new file mode 100644
index 000000000..15d16ff67
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/errors.go
@@ -0,0 +1,56 @@
+// Copyright 2015 CoreOS, 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 httptypes
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver/etcdhttp", "httptypes")
+)
+
+type HTTPError struct {
+ Message string `json:"message"`
+ // HTTP return code
+ Code int `json:"-"`
+}
+
+func (e HTTPError) Error() string {
+ return e.Message
+}
+
+func (e HTTPError) WriteTo(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(e.Code)
+ b, err := json.Marshal(e)
+ if err != nil {
+ plog.Panicf("marshal HTTPError should never fail (%v)", err)
+ }
+ if _, err := w.Write(b); err != nil {
+ return err
+ }
+ return nil
+}
+
+func NewHTTPError(code int, m string) *HTTPError {
+ return &HTTPError{
+ Message: m,
+ Code: code,
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/errors_test.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/errors_test.go
new file mode 100644
index 000000000..6ef1b3edd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/errors_test.go
@@ -0,0 +1,49 @@
+// Copyright 2015 CoreOS, 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 httptypes
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "testing"
+)
+
+func TestHTTPErrorWriteTo(t *testing.T) {
+ err := NewHTTPError(http.StatusBadRequest, "what a bad request you made!")
+ rr := httptest.NewRecorder()
+ if e := err.WriteTo(rr); e != nil {
+ t.Fatalf("HTTPError.WriteTo error (%v)", e)
+ }
+
+ wcode := http.StatusBadRequest
+ wheader := http.Header(map[string][]string{
+ "Content-Type": {"application/json"},
+ })
+ wbody := `{"message":"what a bad request you made!"}`
+
+ if wcode != rr.Code {
+ t.Errorf("HTTP status code %d, want %d", rr.Code, wcode)
+ }
+
+ if !reflect.DeepEqual(wheader, rr.HeaderMap) {
+ t.Errorf("HTTP headers %v, want %v", rr.HeaderMap, wheader)
+ }
+
+ gbody := rr.Body.String()
+ if wbody != gbody {
+ t.Errorf("HTTP body %q, want %q", gbody, wbody)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/member.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/member.go
new file mode 100644
index 000000000..30ecbb539
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/member.go
@@ -0,0 +1,67 @@
+// Copyright 2015 CoreOS, 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 httptypes
+
+import (
+ "encoding/json"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+type Member struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ PeerURLs []string `json:"peerURLs"`
+ ClientURLs []string `json:"clientURLs"`
+}
+
+type MemberCreateRequest struct {
+ PeerURLs types.URLs
+}
+
+type MemberUpdateRequest struct {
+ MemberCreateRequest
+}
+
+func (m *MemberCreateRequest) UnmarshalJSON(data []byte) error {
+ s := struct {
+ PeerURLs []string `json:"peerURLs"`
+ }{}
+
+ err := json.Unmarshal(data, &s)
+ if err != nil {
+ return err
+ }
+
+ urls, err := types.NewURLs(s.PeerURLs)
+ if err != nil {
+ return err
+ }
+
+ m.PeerURLs = urls
+ return nil
+}
+
+type MemberCollection []Member
+
+func (c *MemberCollection) MarshalJSON() ([]byte, error) {
+ d := struct {
+ Members []Member `json:"members"`
+ }{
+ Members: []Member(*c),
+ }
+
+ return json.Marshal(d)
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/member_test.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/member_test.go
new file mode 100644
index 000000000..4ea4320c3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/httptypes/member_test.go
@@ -0,0 +1,135 @@
+// Copyright 2015 CoreOS, 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 httptypes
+
+import (
+ "encoding/json"
+ "net/url"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+func TestMemberUnmarshal(t *testing.T) {
+ tests := []struct {
+ body []byte
+ wantMember Member
+ wantError bool
+ }{
+ // no URLs, just check ID & Name
+ {
+ body: []byte(`{"id": "c", "name": "dungarees"}`),
+ wantMember: Member{ID: "c", Name: "dungarees", PeerURLs: nil, ClientURLs: nil},
+ },
+
+ // both client and peer URLs
+ {
+ body: []byte(`{"peerURLs": ["http://127.0.0.1:2379"], "clientURLs": ["http://127.0.0.1:2379"]}`),
+ wantMember: Member{
+ PeerURLs: []string{
+ "http://127.0.0.1:2379",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:2379",
+ },
+ },
+ },
+
+ // multiple peer URLs
+ {
+ body: []byte(`{"peerURLs": ["http://127.0.0.1:2379", "https://example.com"]}`),
+ wantMember: Member{
+ PeerURLs: []string{
+ "http://127.0.0.1:2379",
+ "https://example.com",
+ },
+ ClientURLs: nil,
+ },
+ },
+
+ // multiple client URLs
+ {
+ body: []byte(`{"clientURLs": ["http://127.0.0.1:2379", "https://example.com"]}`),
+ wantMember: Member{
+ PeerURLs: nil,
+ ClientURLs: []string{
+ "http://127.0.0.1:2379",
+ "https://example.com",
+ },
+ },
+ },
+
+ // invalid JSON
+ {
+ body: []byte(`{"peerU`),
+ wantError: true,
+ },
+ }
+
+ for i, tt := range tests {
+ got := Member{}
+ err := json.Unmarshal(tt.body, &got)
+ if tt.wantError != (err != nil) {
+ t.Errorf("#%d: want error %t, got %v", i, tt.wantError, err)
+ continue
+ }
+
+ if !reflect.DeepEqual(tt.wantMember, got) {
+ t.Errorf("#%d: incorrect output: want=%#v, got=%#v", i, tt.wantMember, got)
+ }
+ }
+}
+
+func TestMemberCreateRequestUnmarshal(t *testing.T) {
+ body := []byte(`{"peerURLs": ["http://127.0.0.1:8081", "https://127.0.0.1:8080"]}`)
+ want := MemberCreateRequest{
+ PeerURLs: types.URLs([]url.URL{
+ {Scheme: "http", Host: "127.0.0.1:8081"},
+ {Scheme: "https", Host: "127.0.0.1:8080"},
+ }),
+ }
+
+ var req MemberCreateRequest
+ if err := json.Unmarshal(body, &req); err != nil {
+ t.Fatalf("Unmarshal returned unexpected err=%v", err)
+ }
+
+ if !reflect.DeepEqual(want, req) {
+ t.Fatalf("Failed to unmarshal MemberCreateRequest: want=%#v, got=%#v", want, req)
+ }
+}
+
+func TestMemberCreateRequestUnmarshalFail(t *testing.T) {
+ tests := [][]byte{
+ // invalid JSON
+ []byte(``),
+ []byte(`{`),
+
+ // spot-check validation done in types.NewURLs
+ []byte(`{"peerURLs": "foo"}`),
+ []byte(`{"peerURLs": ["."]}`),
+ []byte(`{"peerURLs": []}`),
+ []byte(`{"peerURLs": ["http://127.0.0.1:2379/foo"]}`),
+ []byte(`{"peerURLs": ["http://127.0.0.1"]}`),
+ }
+
+ for i, tt := range tests {
+ var req MemberCreateRequest
+ if err := json.Unmarshal(tt, &req); err == nil {
+ t.Errorf("#%d: expected err, got nil", i)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/metrics.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/metrics.go
new file mode 100644
index 000000000..f037d3c33
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/metrics.go
@@ -0,0 +1,96 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "strconv"
+ "time"
+
+ "net/http"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ etcdErr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+ "github.com/coreos/etcd/etcdserver/etcdserverpb"
+)
+
+var (
+ incomingEvents = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "http",
+ Name: "received_total",
+ Help: "Counter of requests received into the system (successfully parsed and authd).",
+ }, []string{"method"})
+
+ failedEvents = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "http",
+ Name: "failed_total",
+ Help: "Counter of handle failures of requests (non-watches), by method (GET/PUT etc.) and code (400, 500 etc.).",
+ }, []string{"method", "code"})
+
+ successfulEventsHandlingTime = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Namespace: "etcd",
+ Subsystem: "http",
+ Name: "successful_duration_second",
+ Help: "Bucketed histogram of processing time (s) of successfully handled requests (non-watches), by method (GET/PUT etc.).",
+ Buckets: prometheus.ExponentialBuckets(0.0005, 2, 13),
+ }, []string{"method"})
+)
+
+func init() {
+ prometheus.MustRegister(incomingEvents)
+ prometheus.MustRegister(failedEvents)
+ prometheus.MustRegister(successfulEventsHandlingTime)
+}
+
+func reportRequestReceived(request etcdserverpb.Request) {
+ incomingEvents.WithLabelValues(methodFromRequest(request)).Inc()
+}
+
+func reportRequestCompleted(request etcdserverpb.Request, response etcdserver.Response, startTime time.Time) {
+ method := methodFromRequest(request)
+ successfulEventsHandlingTime.WithLabelValues(method).Observe(time.Since(startTime).Seconds())
+}
+
+func reportRequestFailed(request etcdserverpb.Request, err error) {
+ method := methodFromRequest(request)
+ failedEvents.WithLabelValues(method, strconv.Itoa(codeFromError(err))).Inc()
+}
+
+func methodFromRequest(request etcdserverpb.Request) string {
+ if request.Method == "GET" && request.Quorum {
+ return "QGET"
+ }
+ return request.Method
+}
+
+func codeFromError(err error) int {
+ if err == nil {
+ return http.StatusInternalServerError
+ }
+ switch e := err.(type) {
+ case *etcdErr.Error:
+ return (*etcdErr.Error)(e).StatusCode()
+ case *httptypes.HTTPError:
+ return (*httptypes.HTTPError)(e).Code
+ default:
+ return http.StatusInternalServerError
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/peer.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/peer.go
new file mode 100644
index 000000000..756b6baf7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/peer.go
@@ -0,0 +1,63 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/rafthttp"
+)
+
+const (
+ peerMembersPrefix = "/members"
+)
+
+// NewPeerHandler generates an http.Handler to handle etcd peer (raft) requests.
+func NewPeerHandler(cluster etcdserver.Cluster, raftHandler http.Handler) http.Handler {
+ mh := &peerMembersHandler{
+ cluster: cluster,
+ }
+
+ mux := http.NewServeMux()
+ mux.HandleFunc("/", http.NotFound)
+ mux.Handle(rafthttp.RaftPrefix, raftHandler)
+ mux.Handle(rafthttp.RaftPrefix+"/", raftHandler)
+ mux.Handle(peerMembersPrefix, mh)
+ mux.HandleFunc(versionPath, versionHandler(cluster, serveVersion))
+ return mux
+}
+
+type peerMembersHandler struct {
+ cluster etcdserver.Cluster
+}
+
+func (h *peerMembersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if !allowMethod(w, r.Method, "GET") {
+ return
+ }
+ w.Header().Set("X-Etcd-Cluster-ID", h.cluster.ID().String())
+
+ if r.URL.Path != peerMembersPrefix {
+ http.Error(w, "bad path", http.StatusBadRequest)
+ return
+ }
+ ms := h.cluster.Members()
+ w.Header().Set("Content-Type", "application/json")
+ if err := json.NewEncoder(w).Encode(ms); err != nil {
+ plog.Warningf("failed to encode members response (%v)", err)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/peer_test.go b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/peer_test.go
new file mode 100644
index 000000000..68b2f0e70
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdhttp/peer_test.go
@@ -0,0 +1,134 @@
+// Copyright 2015 CoreOS, 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 etcdhttp
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "path"
+ "testing"
+
+ "github.com/coreos/etcd/etcdserver"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/rafthttp"
+)
+
+// TestNewPeerHandler tests that NewPeerHandler returns a handler that
+// handles raft-prefix requests well.
+func TestNewPeerHandlerOnRaftPrefix(t *testing.T) {
+ h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("test data"))
+ })
+ ph := NewPeerHandler(&fakeCluster{}, h)
+ srv := httptest.NewServer(ph)
+ defer srv.Close()
+
+ tests := []string{
+ rafthttp.RaftPrefix,
+ rafthttp.RaftPrefix + "/hello",
+ }
+ for i, tt := range tests {
+ resp, err := http.Get(srv.URL + tt)
+ if err != nil {
+ t.Fatalf("unexpected http.Get error: %v", err)
+ }
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatalf("unexpected ioutil.ReadAll error: %v", err)
+ }
+ if w := "test data"; string(body) != w {
+ t.Errorf("#%d: body = %s, want %s", i, body, w)
+ }
+ }
+}
+
+func TestServeMembersFails(t *testing.T) {
+ tests := []struct {
+ method string
+ wcode int
+ }{
+ {
+ "POST",
+ http.StatusMethodNotAllowed,
+ },
+ {
+ "DELETE",
+ http.StatusMethodNotAllowed,
+ },
+ {
+ "BAD",
+ http.StatusMethodNotAllowed,
+ },
+ }
+ for i, tt := range tests {
+ rw := httptest.NewRecorder()
+ h := &peerMembersHandler{cluster: nil}
+ h.ServeHTTP(rw, &http.Request{Method: tt.method})
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ }
+}
+
+func TestServeMembersGet(t *testing.T) {
+ memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
+ memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
+ cluster := &fakeCluster{
+ id: 1,
+ members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
+ }
+ h := &peerMembersHandler{cluster: cluster}
+ msb, err := json.Marshal([]etcdserver.Member{memb1, memb2})
+ if err != nil {
+ t.Fatal(err)
+ }
+ wms := string(msb) + "\n"
+
+ tests := []struct {
+ path string
+ wcode int
+ wct string
+ wbody string
+ }{
+ {peerMembersPrefix, http.StatusOK, "application/json", wms},
+ {path.Join(peerMembersPrefix, "bad"), http.StatusBadRequest, "text/plain; charset=utf-8", "bad path\n"},
+ }
+
+ for i, tt := range tests {
+ req, err := http.NewRequest("GET", testutil.MustNewURL(t, tt.path).String(), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rw := httptest.NewRecorder()
+ h.ServeHTTP(rw, req)
+
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
+ t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
+ }
+ if rw.Body.String() != tt.wbody {
+ t.Errorf("#%d: body = %s, want %s", i, rw.Body.String(), tt.wbody)
+ }
+ gcid := rw.Header().Get("X-Etcd-Cluster-ID")
+ wcid := cluster.ID().String()
+ if gcid != wcid {
+ t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.pb.go b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.pb.go
new file mode 100644
index 000000000..519e4865c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.pb.go
@@ -0,0 +1,804 @@
+// Code generated by protoc-gen-gogo.
+// source: etcdserver.proto
+// DO NOT EDIT!
+
+/*
+ Package etcdserverpb is a generated protocol buffer package.
+
+ It is generated from these files:
+ etcdserver.proto
+ raft_internal.proto
+ rpc.proto
+
+ It has these top-level messages:
+ Request
+ Metadata
+*/
+package etcdserverpb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+import math "math"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type Request struct {
+ ID uint64 `protobuf:"varint,1,opt" json:"ID"`
+ Method string `protobuf:"bytes,2,opt" json:"Method"`
+ Path string `protobuf:"bytes,3,opt" json:"Path"`
+ Val string `protobuf:"bytes,4,opt" json:"Val"`
+ Dir bool `protobuf:"varint,5,opt" json:"Dir"`
+ PrevValue string `protobuf:"bytes,6,opt" json:"PrevValue"`
+ PrevIndex uint64 `protobuf:"varint,7,opt" json:"PrevIndex"`
+ PrevExist *bool `protobuf:"varint,8,opt" json:"PrevExist,omitempty"`
+ Expiration int64 `protobuf:"varint,9,opt" json:"Expiration"`
+ Wait bool `protobuf:"varint,10,opt" json:"Wait"`
+ Since uint64 `protobuf:"varint,11,opt" json:"Since"`
+ Recursive bool `protobuf:"varint,12,opt" json:"Recursive"`
+ Sorted bool `protobuf:"varint,13,opt" json:"Sorted"`
+ Quorum bool `protobuf:"varint,14,opt" json:"Quorum"`
+ Time int64 `protobuf:"varint,15,opt" json:"Time"`
+ Stream bool `protobuf:"varint,16,opt" json:"Stream"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Request) Reset() { *m = Request{} }
+func (m *Request) String() string { return proto.CompactTextString(m) }
+func (*Request) ProtoMessage() {}
+
+type Metadata struct {
+ NodeID uint64 `protobuf:"varint,1,opt" json:"NodeID"`
+ ClusterID uint64 `protobuf:"varint,2,opt" json:"ClusterID"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Metadata) Reset() { *m = Metadata{} }
+func (m *Metadata) String() string { return proto.CompactTextString(m) }
+func (*Metadata) ProtoMessage() {}
+
+func (m *Request) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Request) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.ID))
+ data[i] = 0x12
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(len(m.Method)))
+ i += copy(data[i:], m.Method)
+ data[i] = 0x1a
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(len(m.Path)))
+ i += copy(data[i:], m.Path)
+ data[i] = 0x22
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(len(m.Val)))
+ i += copy(data[i:], m.Val)
+ data[i] = 0x28
+ i++
+ if m.Dir {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ data[i] = 0x32
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(len(m.PrevValue)))
+ i += copy(data[i:], m.PrevValue)
+ data[i] = 0x38
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.PrevIndex))
+ if m.PrevExist != nil {
+ data[i] = 0x40
+ i++
+ if *m.PrevExist {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ }
+ data[i] = 0x48
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.Expiration))
+ data[i] = 0x50
+ i++
+ if m.Wait {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ data[i] = 0x58
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.Since))
+ data[i] = 0x60
+ i++
+ if m.Recursive {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ data[i] = 0x68
+ i++
+ if m.Sorted {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ data[i] = 0x70
+ i++
+ if m.Quorum {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ data[i] = 0x78
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.Time))
+ data[i] = 0x80
+ i++
+ data[i] = 0x1
+ i++
+ if m.Stream {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *Metadata) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Metadata) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.NodeID))
+ data[i] = 0x10
+ i++
+ i = encodeVarintEtcdserver(data, i, uint64(m.ClusterID))
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func encodeFixed64Etcdserver(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Etcdserver(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintEtcdserver(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *Request) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovEtcdserver(uint64(m.ID))
+ l = len(m.Method)
+ n += 1 + l + sovEtcdserver(uint64(l))
+ l = len(m.Path)
+ n += 1 + l + sovEtcdserver(uint64(l))
+ l = len(m.Val)
+ n += 1 + l + sovEtcdserver(uint64(l))
+ n += 2
+ l = len(m.PrevValue)
+ n += 1 + l + sovEtcdserver(uint64(l))
+ n += 1 + sovEtcdserver(uint64(m.PrevIndex))
+ if m.PrevExist != nil {
+ n += 2
+ }
+ n += 1 + sovEtcdserver(uint64(m.Expiration))
+ n += 2
+ n += 1 + sovEtcdserver(uint64(m.Since))
+ n += 2
+ n += 2
+ n += 2
+ n += 1 + sovEtcdserver(uint64(m.Time))
+ n += 3
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *Metadata) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovEtcdserver(uint64(m.NodeID))
+ n += 1 + sovEtcdserver(uint64(m.ClusterID))
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func sovEtcdserver(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozEtcdserver(x uint64) (n int) {
+ return sovEtcdserver(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Request) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+ }
+ m.ID = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ID |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthEtcdserver
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Method = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthEtcdserver
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Path = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Val", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthEtcdserver
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Val = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 5:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Dir", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Dir = bool(v != 0)
+ case 6:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field PrevValue", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthEtcdserver
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.PrevValue = string(data[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 7:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field PrevIndex", wireType)
+ }
+ m.PrevIndex = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.PrevIndex |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 8:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field PrevExist", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ b := bool(v != 0)
+ m.PrevExist = &b
+ case 9:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Expiration", wireType)
+ }
+ m.Expiration = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Expiration |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 10:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Wait", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Wait = bool(v != 0)
+ case 11:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Since", wireType)
+ }
+ m.Since = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Since |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 12:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Recursive", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Recursive = bool(v != 0)
+ case 13:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Sorted", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Sorted = bool(v != 0)
+ case 14:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Quorum", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Quorum = bool(v != 0)
+ case 15:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Time", wireType)
+ }
+ m.Time = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Time |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 16:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Stream", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Stream = bool(v != 0)
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipEtcdserver(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthEtcdserver
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *Metadata) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType)
+ }
+ m.NodeID = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.NodeID |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ClusterID", wireType)
+ }
+ m.ClusterID = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ClusterID |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipEtcdserver(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthEtcdserver
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipEtcdserver(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthEtcdserver
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipEtcdserver(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthEtcdserver = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.proto b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.proto
new file mode 100644
index 000000000..024c3037f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/etcdserver.proto
@@ -0,0 +1,33 @@
+syntax = "proto2";
+package etcdserverpb;
+
+import "gogoproto/gogo.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.goproto_getters_all) = false;
+
+message Request {
+ optional uint64 ID = 1 [(gogoproto.nullable) = false];
+ optional string Method = 2 [(gogoproto.nullable) = false];
+ optional string Path = 3 [(gogoproto.nullable) = false];
+ optional string Val = 4 [(gogoproto.nullable) = false];
+ optional bool Dir = 5 [(gogoproto.nullable) = false];
+ optional string PrevValue = 6 [(gogoproto.nullable) = false];
+ optional uint64 PrevIndex = 7 [(gogoproto.nullable) = false];
+ optional bool PrevExist = 8 [(gogoproto.nullable) = true];
+ optional int64 Expiration = 9 [(gogoproto.nullable) = false];
+ optional bool Wait = 10 [(gogoproto.nullable) = false];
+ optional uint64 Since = 11 [(gogoproto.nullable) = false];
+ optional bool Recursive = 12 [(gogoproto.nullable) = false];
+ optional bool Sorted = 13 [(gogoproto.nullable) = false];
+ optional bool Quorum = 14 [(gogoproto.nullable) = false];
+ optional int64 Time = 15 [(gogoproto.nullable) = false];
+ optional bool Stream = 16 [(gogoproto.nullable) = false];
+}
+
+message Metadata {
+ optional uint64 NodeID = 1 [(gogoproto.nullable) = false];
+ optional uint64 ClusterID = 2 [(gogoproto.nullable) = false];
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.pb.go b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.pb.go
new file mode 100644
index 000000000..25773a69b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.pb.go
@@ -0,0 +1,595 @@
+// Code generated by protoc-gen-gogo.
+// source: raft_internal.proto
+// DO NOT EDIT!
+
+package etcdserverpb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+
+// An InternalRaftRequest is the union of all requests which can be
+// sent via raft.
+type InternalRaftRequest struct {
+ ID uint64 `protobuf:"varint,1,opt,proto3" json:"ID,omitempty"`
+ V2 *Request `protobuf:"bytes,2,opt,name=v2" json:"v2,omitempty"`
+ Range *RangeRequest `protobuf:"bytes,3,opt,name=range" json:"range,omitempty"`
+ Put *PutRequest `protobuf:"bytes,4,opt,name=put" json:"put,omitempty"`
+ DeleteRange *DeleteRangeRequest `protobuf:"bytes,5,opt,name=delete_range" json:"delete_range,omitempty"`
+ Txn *TxnRequest `protobuf:"bytes,6,opt,name=txn" json:"txn,omitempty"`
+ Compaction *CompactionRequest `protobuf:"bytes,7,opt,name=compaction" json:"compaction,omitempty"`
+}
+
+func (m *InternalRaftRequest) Reset() { *m = InternalRaftRequest{} }
+func (m *InternalRaftRequest) String() string { return proto.CompactTextString(m) }
+func (*InternalRaftRequest) ProtoMessage() {}
+
+type EmptyResponse struct {
+}
+
+func (m *EmptyResponse) Reset() { *m = EmptyResponse{} }
+func (m *EmptyResponse) String() string { return proto.CompactTextString(m) }
+func (*EmptyResponse) ProtoMessage() {}
+
+func (m *InternalRaftRequest) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *InternalRaftRequest) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.ID != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.ID))
+ }
+ if m.V2 != nil {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.V2.Size()))
+ n1, err := m.V2.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n1
+ }
+ if m.Range != nil {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.Range.Size()))
+ n2, err := m.Range.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n2
+ }
+ if m.Put != nil {
+ data[i] = 0x22
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.Put.Size()))
+ n3, err := m.Put.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n3
+ }
+ if m.DeleteRange != nil {
+ data[i] = 0x2a
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.DeleteRange.Size()))
+ n4, err := m.DeleteRange.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n4
+ }
+ if m.Txn != nil {
+ data[i] = 0x32
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.Txn.Size()))
+ n5, err := m.Txn.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n5
+ }
+ if m.Compaction != nil {
+ data[i] = 0x3a
+ i++
+ i = encodeVarintRaftInternal(data, i, uint64(m.Compaction.Size()))
+ n6, err := m.Compaction.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n6
+ }
+ return i, nil
+}
+
+func (m *EmptyResponse) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *EmptyResponse) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ return i, nil
+}
+
+func encodeFixed64RaftInternal(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32RaftInternal(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintRaftInternal(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *InternalRaftRequest) Size() (n int) {
+ var l int
+ _ = l
+ if m.ID != 0 {
+ n += 1 + sovRaftInternal(uint64(m.ID))
+ }
+ if m.V2 != nil {
+ l = m.V2.Size()
+ n += 1 + l + sovRaftInternal(uint64(l))
+ }
+ if m.Range != nil {
+ l = m.Range.Size()
+ n += 1 + l + sovRaftInternal(uint64(l))
+ }
+ if m.Put != nil {
+ l = m.Put.Size()
+ n += 1 + l + sovRaftInternal(uint64(l))
+ }
+ if m.DeleteRange != nil {
+ l = m.DeleteRange.Size()
+ n += 1 + l + sovRaftInternal(uint64(l))
+ }
+ if m.Txn != nil {
+ l = m.Txn.Size()
+ n += 1 + l + sovRaftInternal(uint64(l))
+ }
+ if m.Compaction != nil {
+ l = m.Compaction.Size()
+ n += 1 + l + sovRaftInternal(uint64(l))
+ }
+ return n
+}
+
+func (m *EmptyResponse) Size() (n int) {
+ var l int
+ _ = l
+ return n
+}
+
+func sovRaftInternal(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozRaftInternal(x uint64) (n int) {
+ return sovRaftInternal(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *InternalRaftRequest) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+ }
+ m.ID = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ID |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field V2", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.V2 == nil {
+ m.V2 = &Request{}
+ }
+ if err := m.V2.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Range", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Range == nil {
+ m.Range = &RangeRequest{}
+ }
+ if err := m.Range.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Put", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Put == nil {
+ m.Put = &PutRequest{}
+ }
+ if err := m.Put.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 5:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field DeleteRange", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.DeleteRange == nil {
+ m.DeleteRange = &DeleteRangeRequest{}
+ }
+ if err := m.DeleteRange.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 6:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Txn", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Txn == nil {
+ m.Txn = &TxnRequest{}
+ }
+ if err := m.Txn.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 7:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Compaction", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Compaction == nil {
+ m.Compaction = &CompactionRequest{}
+ }
+ if err := m.Compaction.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaftInternal(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *EmptyResponse) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ switch fieldNum {
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaftInternal(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaftInternal
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipRaftInternal(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthRaftInternal
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipRaftInternal(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthRaftInternal = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.proto b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.proto
new file mode 100644
index 000000000..6b9adc6c7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/raft_internal.proto
@@ -0,0 +1,26 @@
+syntax = "proto3";
+package etcdserverpb;
+
+import "gogoproto/gogo.proto";
+import "etcdserver.proto";
+import "rpc.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.goproto_getters_all) = false;
+
+// An InternalRaftRequest is the union of all requests which can be
+// sent via raft.
+message InternalRaftRequest {
+ uint64 ID = 1;
+ Request v2 = 2;
+ RangeRequest range = 3;
+ PutRequest put = 4;
+ DeleteRangeRequest delete_range = 5;
+ TxnRequest txn = 6;
+ CompactionRequest compaction = 7;
+}
+
+message EmptyResponse {
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.go b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.go
new file mode 100644
index 000000000..2055a2a97
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.pb.go
@@ -0,0 +1,3024 @@
+// Code generated by protoc-gen-gogo.
+// source: rpc.proto
+// DO NOT EDIT!
+
+package etcdserverpb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+import storagepb "github.com/coreos/etcd/storage/storagepb"
+
+import (
+ context "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ grpc "github.com/coreos/etcd/Godeps/_workspace/src/google.golang.org/grpc"
+)
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+
+type Compare_CompareResult int32
+
+const (
+ Compare_EQUAL Compare_CompareResult = 0
+ Compare_GREATER Compare_CompareResult = 1
+ Compare_LESS Compare_CompareResult = 2
+)
+
+var Compare_CompareResult_name = map[int32]string{
+ 0: "EQUAL",
+ 1: "GREATER",
+ 2: "LESS",
+}
+var Compare_CompareResult_value = map[string]int32{
+ "EQUAL": 0,
+ "GREATER": 1,
+ "LESS": 2,
+}
+
+func (x Compare_CompareResult) String() string {
+ return proto.EnumName(Compare_CompareResult_name, int32(x))
+}
+
+type Compare_CompareTarget int32
+
+const (
+ Compare_VERSION Compare_CompareTarget = 0
+ Compare_CREATE Compare_CompareTarget = 1
+ Compare_MOD Compare_CompareTarget = 2
+ Compare_VALUE Compare_CompareTarget = 3
+)
+
+var Compare_CompareTarget_name = map[int32]string{
+ 0: "VERSION",
+ 1: "CREATE",
+ 2: "MOD",
+ 3: "VALUE",
+}
+var Compare_CompareTarget_value = map[string]int32{
+ "VERSION": 0,
+ "CREATE": 1,
+ "MOD": 2,
+ "VALUE": 3,
+}
+
+func (x Compare_CompareTarget) String() string {
+ return proto.EnumName(Compare_CompareTarget_name, int32(x))
+}
+
+type ResponseHeader struct {
+ ClusterId uint64 `protobuf:"varint,1,opt,name=cluster_id,proto3" json:"cluster_id,omitempty"`
+ MemberId uint64 `protobuf:"varint,2,opt,name=member_id,proto3" json:"member_id,omitempty"`
+ // revision of the store when the request was applied.
+ Revision int64 `protobuf:"varint,3,opt,name=revision,proto3" json:"revision,omitempty"`
+ // term of raft when the request was applied.
+ RaftTerm uint64 `protobuf:"varint,4,opt,name=raft_term,proto3" json:"raft_term,omitempty"`
+}
+
+func (m *ResponseHeader) Reset() { *m = ResponseHeader{} }
+func (m *ResponseHeader) String() string { return proto.CompactTextString(m) }
+func (*ResponseHeader) ProtoMessage() {}
+
+type RangeRequest struct {
+ // if the range_end is not given, the request returns the key.
+ Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ // if the range_end is given, it gets the keys in range [key, range_end).
+ RangeEnd []byte `protobuf:"bytes,2,opt,name=range_end,proto3" json:"range_end,omitempty"`
+ // limit the number of keys returned.
+ Limit int64 `protobuf:"varint,3,opt,name=limit,proto3" json:"limit,omitempty"`
+ // range over the store at the given revision.
+ // if revision is less or equal to zero, range over the newest store.
+ // if the revision has been compacted, ErrCompaction will be returned in
+ // response.
+ Revision int64 `protobuf:"varint,4,opt,name=revision,proto3" json:"revision,omitempty"`
+}
+
+func (m *RangeRequest) Reset() { *m = RangeRequest{} }
+func (m *RangeRequest) String() string { return proto.CompactTextString(m) }
+func (*RangeRequest) ProtoMessage() {}
+
+type RangeResponse struct {
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
+ Kvs []*storagepb.KeyValue `protobuf:"bytes,2,rep,name=kvs" json:"kvs,omitempty"`
+ // more indicates if there are more keys to return in the requested range.
+ More bool `protobuf:"varint,3,opt,name=more,proto3" json:"more,omitempty"`
+}
+
+func (m *RangeResponse) Reset() { *m = RangeResponse{} }
+func (m *RangeResponse) String() string { return proto.CompactTextString(m) }
+func (*RangeResponse) ProtoMessage() {}
+
+func (m *RangeResponse) GetHeader() *ResponseHeader {
+ if m != nil {
+ return m.Header
+ }
+ return nil
+}
+
+func (m *RangeResponse) GetKvs() []*storagepb.KeyValue {
+ if m != nil {
+ return m.Kvs
+ }
+ return nil
+}
+
+type PutRequest struct {
+ Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (m *PutRequest) Reset() { *m = PutRequest{} }
+func (m *PutRequest) String() string { return proto.CompactTextString(m) }
+func (*PutRequest) ProtoMessage() {}
+
+type PutResponse struct {
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
+}
+
+func (m *PutResponse) Reset() { *m = PutResponse{} }
+func (m *PutResponse) String() string { return proto.CompactTextString(m) }
+func (*PutResponse) ProtoMessage() {}
+
+func (m *PutResponse) GetHeader() *ResponseHeader {
+ if m != nil {
+ return m.Header
+ }
+ return nil
+}
+
+type DeleteRangeRequest struct {
+ // if the range_end is not given, the request deletes the key.
+ Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ // if the range_end is given, it deletes the keys in range [key, range_end).
+ RangeEnd []byte `protobuf:"bytes,2,opt,name=range_end,proto3" json:"range_end,omitempty"`
+}
+
+func (m *DeleteRangeRequest) Reset() { *m = DeleteRangeRequest{} }
+func (m *DeleteRangeRequest) String() string { return proto.CompactTextString(m) }
+func (*DeleteRangeRequest) ProtoMessage() {}
+
+type DeleteRangeResponse struct {
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
+}
+
+func (m *DeleteRangeResponse) Reset() { *m = DeleteRangeResponse{} }
+func (m *DeleteRangeResponse) String() string { return proto.CompactTextString(m) }
+func (*DeleteRangeResponse) ProtoMessage() {}
+
+func (m *DeleteRangeResponse) GetHeader() *ResponseHeader {
+ if m != nil {
+ return m.Header
+ }
+ return nil
+}
+
+type RequestUnion struct {
+ RequestRange *RangeRequest `protobuf:"bytes,1,opt,name=request_range" json:"request_range,omitempty"`
+ RequestPut *PutRequest `protobuf:"bytes,2,opt,name=request_put" json:"request_put,omitempty"`
+ RequestDeleteRange *DeleteRangeRequest `protobuf:"bytes,3,opt,name=request_delete_range" json:"request_delete_range,omitempty"`
+}
+
+func (m *RequestUnion) Reset() { *m = RequestUnion{} }
+func (m *RequestUnion) String() string { return proto.CompactTextString(m) }
+func (*RequestUnion) ProtoMessage() {}
+
+func (m *RequestUnion) GetRequestRange() *RangeRequest {
+ if m != nil {
+ return m.RequestRange
+ }
+ return nil
+}
+
+func (m *RequestUnion) GetRequestPut() *PutRequest {
+ if m != nil {
+ return m.RequestPut
+ }
+ return nil
+}
+
+func (m *RequestUnion) GetRequestDeleteRange() *DeleteRangeRequest {
+ if m != nil {
+ return m.RequestDeleteRange
+ }
+ return nil
+}
+
+type ResponseUnion struct {
+ ResponseRange *RangeResponse `protobuf:"bytes,1,opt,name=response_range" json:"response_range,omitempty"`
+ ResponsePut *PutResponse `protobuf:"bytes,2,opt,name=response_put" json:"response_put,omitempty"`
+ ResponseDeleteRange *DeleteRangeResponse `protobuf:"bytes,3,opt,name=response_delete_range" json:"response_delete_range,omitempty"`
+}
+
+func (m *ResponseUnion) Reset() { *m = ResponseUnion{} }
+func (m *ResponseUnion) String() string { return proto.CompactTextString(m) }
+func (*ResponseUnion) ProtoMessage() {}
+
+func (m *ResponseUnion) GetResponseRange() *RangeResponse {
+ if m != nil {
+ return m.ResponseRange
+ }
+ return nil
+}
+
+func (m *ResponseUnion) GetResponsePut() *PutResponse {
+ if m != nil {
+ return m.ResponsePut
+ }
+ return nil
+}
+
+func (m *ResponseUnion) GetResponseDeleteRange() *DeleteRangeResponse {
+ if m != nil {
+ return m.ResponseDeleteRange
+ }
+ return nil
+}
+
+type Compare struct {
+ Result Compare_CompareResult `protobuf:"varint,1,opt,name=result,proto3,enum=etcdserverpb.Compare_CompareResult" json:"result,omitempty"`
+ Target Compare_CompareTarget `protobuf:"varint,2,opt,name=target,proto3,enum=etcdserverpb.Compare_CompareTarget" json:"target,omitempty"`
+ // key path
+ Key []byte `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
+ // version of the given key
+ Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
+ // create revision of the given key
+ CreateRevision int64 `protobuf:"varint,5,opt,name=create_revision,proto3" json:"create_revision,omitempty"`
+ // last modified revision of the given key
+ ModRevision int64 `protobuf:"varint,6,opt,name=mod_revision,proto3" json:"mod_revision,omitempty"`
+ // value of the given key
+ Value []byte `protobuf:"bytes,7,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (m *Compare) Reset() { *m = Compare{} }
+func (m *Compare) String() string { return proto.CompactTextString(m) }
+func (*Compare) ProtoMessage() {}
+
+// From google paxosdb paper:
+// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
+// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
+// and consists of three components:
+// 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check
+// for the absence or presence of a value, or compare with a given value. Two different tests in the guard
+// may apply to the same or different entries in the database. All tests in the guard are applied and
+// MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise
+// it executes f op (see item 3 below).
+// 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or
+// lookup operation, and applies to a single database entry. Two different operations in the list may apply
+// to the same or different entries in the database. These operations are executed
+// if guard evaluates to
+// true.
+// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
+type TxnRequest struct {
+ Compare []*Compare `protobuf:"bytes,1,rep,name=compare" json:"compare,omitempty"`
+ Success []*RequestUnion `protobuf:"bytes,2,rep,name=success" json:"success,omitempty"`
+ Failure []*RequestUnion `protobuf:"bytes,3,rep,name=failure" json:"failure,omitempty"`
+}
+
+func (m *TxnRequest) Reset() { *m = TxnRequest{} }
+func (m *TxnRequest) String() string { return proto.CompactTextString(m) }
+func (*TxnRequest) ProtoMessage() {}
+
+func (m *TxnRequest) GetCompare() []*Compare {
+ if m != nil {
+ return m.Compare
+ }
+ return nil
+}
+
+func (m *TxnRequest) GetSuccess() []*RequestUnion {
+ if m != nil {
+ return m.Success
+ }
+ return nil
+}
+
+func (m *TxnRequest) GetFailure() []*RequestUnion {
+ if m != nil {
+ return m.Failure
+ }
+ return nil
+}
+
+type TxnResponse struct {
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
+ Succeeded bool `protobuf:"varint,2,opt,name=succeeded,proto3" json:"succeeded,omitempty"`
+ Responses []*ResponseUnion `protobuf:"bytes,3,rep,name=responses" json:"responses,omitempty"`
+}
+
+func (m *TxnResponse) Reset() { *m = TxnResponse{} }
+func (m *TxnResponse) String() string { return proto.CompactTextString(m) }
+func (*TxnResponse) ProtoMessage() {}
+
+func (m *TxnResponse) GetHeader() *ResponseHeader {
+ if m != nil {
+ return m.Header
+ }
+ return nil
+}
+
+func (m *TxnResponse) GetResponses() []*ResponseUnion {
+ if m != nil {
+ return m.Responses
+ }
+ return nil
+}
+
+// Compaction compacts the kv store upto the given revision (including).
+// It removes the old versions of a key. It keeps the newest version of
+// the key even if its latest modification revision is smaller than the given
+// revision.
+type CompactionRequest struct {
+ Revision int64 `protobuf:"varint,1,opt,name=revision,proto3" json:"revision,omitempty"`
+}
+
+func (m *CompactionRequest) Reset() { *m = CompactionRequest{} }
+func (m *CompactionRequest) String() string { return proto.CompactTextString(m) }
+func (*CompactionRequest) ProtoMessage() {}
+
+type CompactionResponse struct {
+ Header *ResponseHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
+}
+
+func (m *CompactionResponse) Reset() { *m = CompactionResponse{} }
+func (m *CompactionResponse) String() string { return proto.CompactTextString(m) }
+func (*CompactionResponse) ProtoMessage() {}
+
+func (m *CompactionResponse) GetHeader() *ResponseHeader {
+ if m != nil {
+ return m.Header
+ }
+ return nil
+}
+
+func init() {
+ proto.RegisterEnum("etcdserverpb.Compare_CompareResult", Compare_CompareResult_name, Compare_CompareResult_value)
+ proto.RegisterEnum("etcdserverpb.Compare_CompareTarget", Compare_CompareTarget_name, Compare_CompareTarget_value)
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// Client API for Etcd service
+
+type EtcdClient interface {
+ // Range gets the keys in the range from the store.
+ Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error)
+ // Put puts the given key into the store.
+ // A put request increases the revision of the store,
+ // and generates one event in the event history.
+ Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error)
+ // Delete deletes the given range from the store.
+ // A delete request increase the revision of the store,
+ // and generates one event in the event history.
+ DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error)
+ // Txn processes all the requests in one transaction.
+ // A txn request increases the revision of the store,
+ // and generates events with the same revision in the event history.
+ Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error)
+ // Compact compacts the event history in etcd. User should compact the
+ // event history periodically, or it will grow infinitely.
+ Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error)
+}
+
+type etcdClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewEtcdClient(cc *grpc.ClientConn) EtcdClient {
+ return &etcdClient{cc}
+}
+
+func (c *etcdClient) Range(ctx context.Context, in *RangeRequest, opts ...grpc.CallOption) (*RangeResponse, error) {
+ out := new(RangeResponse)
+ err := grpc.Invoke(ctx, "/etcdserverpb.etcd/Range", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *etcdClient) Put(ctx context.Context, in *PutRequest, opts ...grpc.CallOption) (*PutResponse, error) {
+ out := new(PutResponse)
+ err := grpc.Invoke(ctx, "/etcdserverpb.etcd/Put", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *etcdClient) DeleteRange(ctx context.Context, in *DeleteRangeRequest, opts ...grpc.CallOption) (*DeleteRangeResponse, error) {
+ out := new(DeleteRangeResponse)
+ err := grpc.Invoke(ctx, "/etcdserverpb.etcd/DeleteRange", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *etcdClient) Txn(ctx context.Context, in *TxnRequest, opts ...grpc.CallOption) (*TxnResponse, error) {
+ out := new(TxnResponse)
+ err := grpc.Invoke(ctx, "/etcdserverpb.etcd/Txn", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *etcdClient) Compact(ctx context.Context, in *CompactionRequest, opts ...grpc.CallOption) (*CompactionResponse, error) {
+ out := new(CompactionResponse)
+ err := grpc.Invoke(ctx, "/etcdserverpb.etcd/Compact", in, out, c.cc, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// Server API for Etcd service
+
+type EtcdServer interface {
+ // Range gets the keys in the range from the store.
+ Range(context.Context, *RangeRequest) (*RangeResponse, error)
+ // Put puts the given key into the store.
+ // A put request increases the revision of the store,
+ // and generates one event in the event history.
+ Put(context.Context, *PutRequest) (*PutResponse, error)
+ // Delete deletes the given range from the store.
+ // A delete request increase the revision of the store,
+ // and generates one event in the event history.
+ DeleteRange(context.Context, *DeleteRangeRequest) (*DeleteRangeResponse, error)
+ // Txn processes all the requests in one transaction.
+ // A txn request increases the revision of the store,
+ // and generates events with the same revision in the event history.
+ Txn(context.Context, *TxnRequest) (*TxnResponse, error)
+ // Compact compacts the event history in etcd. User should compact the
+ // event history periodically, or it will grow infinitely.
+ Compact(context.Context, *CompactionRequest) (*CompactionResponse, error)
+}
+
+func RegisterEtcdServer(s *grpc.Server, srv EtcdServer) {
+ s.RegisterService(&_Etcd_serviceDesc, srv)
+}
+
+func _Etcd_Range_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(RangeRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(EtcdServer).Range(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _Etcd_Put_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(PutRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(EtcdServer).Put(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _Etcd_DeleteRange_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(DeleteRangeRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(EtcdServer).DeleteRange(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _Etcd_Txn_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(TxnRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(EtcdServer).Txn(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func _Etcd_Compact_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
+ in := new(CompactionRequest)
+ if err := codec.Unmarshal(buf, in); err != nil {
+ return nil, err
+ }
+ out, err := srv.(EtcdServer).Compact(ctx, in)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+var _Etcd_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "etcdserverpb.etcd",
+ HandlerType: (*EtcdServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "Range",
+ Handler: _Etcd_Range_Handler,
+ },
+ {
+ MethodName: "Put",
+ Handler: _Etcd_Put_Handler,
+ },
+ {
+ MethodName: "DeleteRange",
+ Handler: _Etcd_DeleteRange_Handler,
+ },
+ {
+ MethodName: "Txn",
+ Handler: _Etcd_Txn_Handler,
+ },
+ {
+ MethodName: "Compact",
+ Handler: _Etcd_Compact_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+}
+
+func (m *ResponseHeader) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *ResponseHeader) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.ClusterId != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.ClusterId))
+ }
+ if m.MemberId != 0 {
+ data[i] = 0x10
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.MemberId))
+ }
+ if m.Revision != 0 {
+ data[i] = 0x18
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Revision))
+ }
+ if m.RaftTerm != 0 {
+ data[i] = 0x20
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.RaftTerm))
+ }
+ return i, nil
+}
+
+func (m *RangeRequest) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *RangeRequest) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Key != nil {
+ if len(m.Key) > 0 {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.Key)))
+ i += copy(data[i:], m.Key)
+ }
+ }
+ if m.RangeEnd != nil {
+ if len(m.RangeEnd) > 0 {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.RangeEnd)))
+ i += copy(data[i:], m.RangeEnd)
+ }
+ }
+ if m.Limit != 0 {
+ data[i] = 0x18
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Limit))
+ }
+ if m.Revision != 0 {
+ data[i] = 0x20
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Revision))
+ }
+ return i, nil
+}
+
+func (m *RangeResponse) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *RangeResponse) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Header != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Header.Size()))
+ n1, err := m.Header.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n1
+ }
+ if len(m.Kvs) > 0 {
+ for _, msg := range m.Kvs {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ if m.More {
+ data[i] = 0x18
+ i++
+ if m.More {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ }
+ return i, nil
+}
+
+func (m *PutRequest) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *PutRequest) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Key != nil {
+ if len(m.Key) > 0 {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.Key)))
+ i += copy(data[i:], m.Key)
+ }
+ }
+ if m.Value != nil {
+ if len(m.Value) > 0 {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.Value)))
+ i += copy(data[i:], m.Value)
+ }
+ }
+ return i, nil
+}
+
+func (m *PutResponse) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *PutResponse) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Header != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Header.Size()))
+ n2, err := m.Header.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n2
+ }
+ return i, nil
+}
+
+func (m *DeleteRangeRequest) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *DeleteRangeRequest) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Key != nil {
+ if len(m.Key) > 0 {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.Key)))
+ i += copy(data[i:], m.Key)
+ }
+ }
+ if m.RangeEnd != nil {
+ if len(m.RangeEnd) > 0 {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.RangeEnd)))
+ i += copy(data[i:], m.RangeEnd)
+ }
+ }
+ return i, nil
+}
+
+func (m *DeleteRangeResponse) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *DeleteRangeResponse) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Header != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Header.Size()))
+ n3, err := m.Header.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n3
+ }
+ return i, nil
+}
+
+func (m *RequestUnion) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *RequestUnion) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.RequestRange != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.RequestRange.Size()))
+ n4, err := m.RequestRange.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n4
+ }
+ if m.RequestPut != nil {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.RequestPut.Size()))
+ n5, err := m.RequestPut.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n5
+ }
+ if m.RequestDeleteRange != nil {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.RequestDeleteRange.Size()))
+ n6, err := m.RequestDeleteRange.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n6
+ }
+ return i, nil
+}
+
+func (m *ResponseUnion) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *ResponseUnion) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.ResponseRange != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.ResponseRange.Size()))
+ n7, err := m.ResponseRange.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n7
+ }
+ if m.ResponsePut != nil {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.ResponsePut.Size()))
+ n8, err := m.ResponsePut.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n8
+ }
+ if m.ResponseDeleteRange != nil {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.ResponseDeleteRange.Size()))
+ n9, err := m.ResponseDeleteRange.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n9
+ }
+ return i, nil
+}
+
+func (m *Compare) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Compare) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Result != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Result))
+ }
+ if m.Target != 0 {
+ data[i] = 0x10
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Target))
+ }
+ if m.Key != nil {
+ if len(m.Key) > 0 {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.Key)))
+ i += copy(data[i:], m.Key)
+ }
+ }
+ if m.Version != 0 {
+ data[i] = 0x20
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Version))
+ }
+ if m.CreateRevision != 0 {
+ data[i] = 0x28
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.CreateRevision))
+ }
+ if m.ModRevision != 0 {
+ data[i] = 0x30
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.ModRevision))
+ }
+ if m.Value != nil {
+ if len(m.Value) > 0 {
+ data[i] = 0x3a
+ i++
+ i = encodeVarintRpc(data, i, uint64(len(m.Value)))
+ i += copy(data[i:], m.Value)
+ }
+ }
+ return i, nil
+}
+
+func (m *TxnRequest) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *TxnRequest) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if len(m.Compare) > 0 {
+ for _, msg := range m.Compare {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ if len(m.Success) > 0 {
+ for _, msg := range m.Success {
+ data[i] = 0x12
+ i++
+ i = encodeVarintRpc(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ if len(m.Failure) > 0 {
+ for _, msg := range m.Failure {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRpc(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ return i, nil
+}
+
+func (m *TxnResponse) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *TxnResponse) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Header != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Header.Size()))
+ n10, err := m.Header.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n10
+ }
+ if m.Succeeded {
+ data[i] = 0x10
+ i++
+ if m.Succeeded {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ }
+ if len(m.Responses) > 0 {
+ for _, msg := range m.Responses {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRpc(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ return i, nil
+}
+
+func (m *CompactionRequest) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *CompactionRequest) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Revision != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Revision))
+ }
+ return i, nil
+}
+
+func (m *CompactionResponse) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *CompactionResponse) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Header != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRpc(data, i, uint64(m.Header.Size()))
+ n11, err := m.Header.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n11
+ }
+ return i, nil
+}
+
+func encodeFixed64Rpc(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Rpc(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintRpc(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *ResponseHeader) Size() (n int) {
+ var l int
+ _ = l
+ if m.ClusterId != 0 {
+ n += 1 + sovRpc(uint64(m.ClusterId))
+ }
+ if m.MemberId != 0 {
+ n += 1 + sovRpc(uint64(m.MemberId))
+ }
+ if m.Revision != 0 {
+ n += 1 + sovRpc(uint64(m.Revision))
+ }
+ if m.RaftTerm != 0 {
+ n += 1 + sovRpc(uint64(m.RaftTerm))
+ }
+ return n
+}
+
+func (m *RangeRequest) Size() (n int) {
+ var l int
+ _ = l
+ if m.Key != nil {
+ l = len(m.Key)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if m.RangeEnd != nil {
+ l = len(m.RangeEnd)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if m.Limit != 0 {
+ n += 1 + sovRpc(uint64(m.Limit))
+ }
+ if m.Revision != 0 {
+ n += 1 + sovRpc(uint64(m.Revision))
+ }
+ return n
+}
+
+func (m *RangeResponse) Size() (n int) {
+ var l int
+ _ = l
+ if m.Header != nil {
+ l = m.Header.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ if len(m.Kvs) > 0 {
+ for _, e := range m.Kvs {
+ l = e.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if m.More {
+ n += 2
+ }
+ return n
+}
+
+func (m *PutRequest) Size() (n int) {
+ var l int
+ _ = l
+ if m.Key != nil {
+ l = len(m.Key)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if m.Value != nil {
+ l = len(m.Value)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *PutResponse) Size() (n int) {
+ var l int
+ _ = l
+ if m.Header != nil {
+ l = m.Header.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ return n
+}
+
+func (m *DeleteRangeRequest) Size() (n int) {
+ var l int
+ _ = l
+ if m.Key != nil {
+ l = len(m.Key)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if m.RangeEnd != nil {
+ l = len(m.RangeEnd)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *DeleteRangeResponse) Size() (n int) {
+ var l int
+ _ = l
+ if m.Header != nil {
+ l = m.Header.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ return n
+}
+
+func (m *RequestUnion) Size() (n int) {
+ var l int
+ _ = l
+ if m.RequestRange != nil {
+ l = m.RequestRange.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ if m.RequestPut != nil {
+ l = m.RequestPut.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ if m.RequestDeleteRange != nil {
+ l = m.RequestDeleteRange.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ return n
+}
+
+func (m *ResponseUnion) Size() (n int) {
+ var l int
+ _ = l
+ if m.ResponseRange != nil {
+ l = m.ResponseRange.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ if m.ResponsePut != nil {
+ l = m.ResponsePut.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ if m.ResponseDeleteRange != nil {
+ l = m.ResponseDeleteRange.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ return n
+}
+
+func (m *Compare) Size() (n int) {
+ var l int
+ _ = l
+ if m.Result != 0 {
+ n += 1 + sovRpc(uint64(m.Result))
+ }
+ if m.Target != 0 {
+ n += 1 + sovRpc(uint64(m.Target))
+ }
+ if m.Key != nil {
+ l = len(m.Key)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if m.Version != 0 {
+ n += 1 + sovRpc(uint64(m.Version))
+ }
+ if m.CreateRevision != 0 {
+ n += 1 + sovRpc(uint64(m.CreateRevision))
+ }
+ if m.ModRevision != 0 {
+ n += 1 + sovRpc(uint64(m.ModRevision))
+ }
+ if m.Value != nil {
+ l = len(m.Value)
+ if l > 0 {
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *TxnRequest) Size() (n int) {
+ var l int
+ _ = l
+ if len(m.Compare) > 0 {
+ for _, e := range m.Compare {
+ l = e.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if len(m.Success) > 0 {
+ for _, e := range m.Success {
+ l = e.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ if len(m.Failure) > 0 {
+ for _, e := range m.Failure {
+ l = e.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *TxnResponse) Size() (n int) {
+ var l int
+ _ = l
+ if m.Header != nil {
+ l = m.Header.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ if m.Succeeded {
+ n += 2
+ }
+ if len(m.Responses) > 0 {
+ for _, e := range m.Responses {
+ l = e.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *CompactionRequest) Size() (n int) {
+ var l int
+ _ = l
+ if m.Revision != 0 {
+ n += 1 + sovRpc(uint64(m.Revision))
+ }
+ return n
+}
+
+func (m *CompactionResponse) Size() (n int) {
+ var l int
+ _ = l
+ if m.Header != nil {
+ l = m.Header.Size()
+ n += 1 + l + sovRpc(uint64(l))
+ }
+ return n
+}
+
+func sovRpc(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozRpc(x uint64) (n int) {
+ return sovRpc(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *ResponseHeader) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType)
+ }
+ m.ClusterId = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ClusterId |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType)
+ }
+ m.MemberId = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.MemberId |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
+ }
+ m.Revision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Revision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RaftTerm", wireType)
+ }
+ m.RaftTerm = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.RaftTerm |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *RangeRequest) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Key = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.RangeEnd = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType)
+ }
+ m.Limit = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Limit |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
+ }
+ m.Revision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Revision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *RangeResponse) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Header == nil {
+ m.Header = &ResponseHeader{}
+ }
+ if err := m.Header.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Kvs", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Kvs = append(m.Kvs, &storagepb.KeyValue{})
+ if err := m.Kvs[len(m.Kvs)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field More", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.More = bool(v != 0)
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *PutRequest) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Key = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Value = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *PutResponse) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Header == nil {
+ m.Header = &ResponseHeader{}
+ }
+ if err := m.Header.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *DeleteRangeRequest) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Key = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RangeEnd", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.RangeEnd = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *DeleteRangeResponse) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Header == nil {
+ m.Header = &ResponseHeader{}
+ }
+ if err := m.Header.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *RequestUnion) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RequestRange", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.RequestRange == nil {
+ m.RequestRange = &RangeRequest{}
+ }
+ if err := m.RequestRange.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RequestPut", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.RequestPut == nil {
+ m.RequestPut = &PutRequest{}
+ }
+ if err := m.RequestPut.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RequestDeleteRange", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.RequestDeleteRange == nil {
+ m.RequestDeleteRange = &DeleteRangeRequest{}
+ }
+ if err := m.RequestDeleteRange.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *ResponseUnion) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ResponseRange", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.ResponseRange == nil {
+ m.ResponseRange = &RangeResponse{}
+ }
+ if err := m.ResponseRange.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ResponsePut", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.ResponsePut == nil {
+ m.ResponsePut = &PutResponse{}
+ }
+ if err := m.ResponsePut.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ResponseDeleteRange", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.ResponseDeleteRange == nil {
+ m.ResponseDeleteRange = &DeleteRangeResponse{}
+ }
+ if err := m.ResponseDeleteRange.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *Compare) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType)
+ }
+ m.Result = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Result |= (Compare_CompareResult(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType)
+ }
+ m.Target = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Target |= (Compare_CompareTarget(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Key = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+ }
+ m.Version = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Version |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 5:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
+ }
+ m.CreateRevision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.CreateRevision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 6:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
+ }
+ m.ModRevision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ModRevision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 7:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Value = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *TxnRequest) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Compare", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Compare = append(m.Compare, &Compare{})
+ if err := m.Compare[len(m.Compare)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Success = append(m.Success, &RequestUnion{})
+ if err := m.Success[len(m.Success)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Failure", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Failure = append(m.Failure, &RequestUnion{})
+ if err := m.Failure[len(m.Failure)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *TxnResponse) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Header == nil {
+ m.Header = &ResponseHeader{}
+ }
+ if err := m.Header.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Succeeded", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Succeeded = bool(v != 0)
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Responses", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Responses = append(m.Responses, &ResponseUnion{})
+ if err := m.Responses[len(m.Responses)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *CompactionRequest) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Revision", wireType)
+ }
+ m.Revision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Revision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *CompactionResponse) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRpc
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Header == nil {
+ m.Header = &ResponseHeader{}
+ }
+ if err := m.Header.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRpc(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRpc
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipRpc(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthRpc
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipRpc(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthRpc = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.proto b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.proto
new file mode 100644
index 000000000..c91d1299d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/etcdserverpb/rpc.proto
@@ -0,0 +1,171 @@
+syntax = "proto3";
+package etcdserverpb;
+
+import "gogoproto/gogo.proto";
+import "etcd/storage/storagepb/kv.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+
+// Interface exported by the server.
+service etcd {
+ // Range gets the keys in the range from the store.
+ rpc Range(RangeRequest) returns (RangeResponse) {}
+
+ // Put puts the given key into the store.
+ // A put request increases the revision of the store,
+ // and generates one event in the event history.
+ rpc Put(PutRequest) returns (PutResponse) {}
+
+ // Delete deletes the given range from the store.
+ // A delete request increase the revision of the store,
+ // and generates one event in the event history.
+ rpc DeleteRange(DeleteRangeRequest) returns (DeleteRangeResponse) {}
+
+ // Txn processes all the requests in one transaction.
+ // A txn request increases the revision of the store,
+ // and generates events with the same revision in the event history.
+ rpc Txn(TxnRequest) returns (TxnResponse) {}
+
+ // Compact compacts the event history in etcd. User should compact the
+ // event history periodically, or it will grow infinitely.
+ rpc Compact(CompactionRequest) returns (CompactionResponse) {}
+}
+
+message ResponseHeader {
+ uint64 cluster_id = 1;
+ uint64 member_id = 2;
+ // revision of the store when the request was applied.
+ int64 revision = 3;
+ // term of raft when the request was applied.
+ uint64 raft_term = 4;
+}
+
+message RangeRequest {
+ // if the range_end is not given, the request returns the key.
+ bytes key = 1;
+ // if the range_end is given, it gets the keys in range [key, range_end).
+ bytes range_end = 2;
+ // limit the number of keys returned.
+ int64 limit = 3;
+ // range over the store at the given revision.
+ // if revision is less or equal to zero, range over the newest store.
+ // if the revision has been compacted, ErrCompaction will be returned in
+ // response.
+ int64 revision = 4;
+}
+
+message RangeResponse {
+ ResponseHeader header = 1;
+ repeated storagepb.KeyValue kvs = 2;
+ // more indicates if there are more keys to return in the requested range.
+ bool more = 3;
+}
+
+message PutRequest {
+ bytes key = 1;
+ bytes value = 2;
+}
+
+message PutResponse {
+ ResponseHeader header = 1;
+}
+
+message DeleteRangeRequest {
+ // if the range_end is not given, the request deletes the key.
+ bytes key = 1;
+ // if the range_end is given, it deletes the keys in range [key, range_end).
+ bytes range_end = 2;
+}
+
+message DeleteRangeResponse {
+ ResponseHeader header = 1;
+}
+
+message RequestUnion {
+ oneof request {
+ RangeRequest request_range = 1;
+ PutRequest request_put = 2;
+ DeleteRangeRequest request_delete_range = 3;
+ }
+}
+
+message ResponseUnion {
+ oneof response {
+ RangeResponse response_range = 1;
+ PutResponse response_put = 2;
+ DeleteRangeResponse response_delete_range = 3;
+ }
+}
+
+message Compare {
+ enum CompareResult {
+ EQUAL = 0;
+ GREATER = 1;
+ LESS = 2;
+ }
+ enum CompareTarget {
+ VERSION = 0;
+ CREATE = 1;
+ MOD = 2;
+ VALUE= 3;
+ }
+ CompareResult result = 1;
+ CompareTarget target = 2;
+ // key path
+ bytes key = 3;
+ oneof target_union {
+ // version of the given key
+ int64 version = 4;
+ // create revision of the given key
+ int64 create_revision = 5;
+ // last modified revision of the given key
+ int64 mod_revision = 6;
+ // value of the given key
+ bytes value = 7;
+ }
+}
+
+// If the comparisons succeed, then the success requests will be processed in order,
+// and the response will contain their respective responses in order.
+// If the comparisons fail, then the failure requests will be processed in order,
+// and the response will contain their respective responses in order.
+
+// From google paxosdb paper:
+// Our implementation hinges around a powerful primitive which we call MultiOp. All other database
+// operations except for iteration are implemented as a single call to MultiOp. A MultiOp is applied atomically
+// and consists of three components:
+// 1. A list of tests called guard. Each test in guard checks a single entry in the database. It may check
+// for the absence or presence of a value, or compare with a given value. Two different tests in the guard
+// may apply to the same or different entries in the database. All tests in the guard are applied and
+// MultiOp returns the results. If all tests are true, MultiOp executes t op (see item 2 below), otherwise
+// it executes f op (see item 3 below).
+// 2. A list of database operations called t op. Each operation in the list is either an insert, delete, or
+// lookup operation, and applies to a single database entry. Two different operations in the list may apply
+// to the same or different entries in the database. These operations are executed
+// if guard evaluates to
+// true.
+// 3. A list of database operations called f op. Like t op, but executed if guard evaluates to false.
+message TxnRequest {
+ repeated Compare compare = 1;
+ repeated RequestUnion success = 2;
+ repeated RequestUnion failure = 3;
+}
+
+message TxnResponse {
+ ResponseHeader header = 1;
+ bool succeeded = 2;
+ repeated ResponseUnion responses = 3;
+}
+
+// Compaction compacts the kv store upto the given revision (including).
+// It removes the old versions of a key. It keeps the newest version of
+// the key even if its latest modification revision is smaller than the given
+// revision.
+message CompactionRequest {
+ int64 revision = 1;
+}
+
+message CompactionResponse {
+ ResponseHeader header = 1;
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/member.go b/vendor/github.com/coreos/etcd/etcdserver/member.go
new file mode 100644
index 000000000..6e9fe6bc3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/member.go
@@ -0,0 +1,174 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "crypto/sha1"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "math/rand"
+ "path"
+ "sort"
+ "time"
+
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/store"
+)
+
+var (
+ storeMembersPrefix = path.Join(StoreClusterPrefix, "members")
+ storeRemovedMembersPrefix = path.Join(StoreClusterPrefix, "removed_members")
+)
+
+// RaftAttributes represents the raft related attributes of an etcd member.
+type RaftAttributes struct {
+ // TODO(philips): ensure these are URLs
+ PeerURLs []string `json:"peerURLs"`
+}
+
+// Attributes represents all the non-raft related attributes of an etcd member.
+type Attributes struct {
+ Name string `json:"name,omitempty"`
+ ClientURLs []string `json:"clientURLs,omitempty"`
+}
+
+type Member struct {
+ ID types.ID `json:"id"`
+ RaftAttributes
+ Attributes
+}
+
+// NewMember creates a Member without an ID and generates one based on the
+// name, peer URLs. This is used for bootstrapping/adding new member.
+func NewMember(name string, peerURLs types.URLs, clusterName string, now *time.Time) *Member {
+ m := &Member{
+ RaftAttributes: RaftAttributes{PeerURLs: peerURLs.StringSlice()},
+ Attributes: Attributes{Name: name},
+ }
+
+ var b []byte
+ sort.Strings(m.PeerURLs)
+ for _, p := range m.PeerURLs {
+ b = append(b, []byte(p)...)
+ }
+
+ b = append(b, []byte(clusterName)...)
+ if now != nil {
+ b = append(b, []byte(fmt.Sprintf("%d", now.Unix()))...)
+ }
+
+ hash := sha1.Sum(b)
+ m.ID = types.ID(binary.BigEndian.Uint64(hash[:8]))
+ return m
+}
+
+// PickPeerURL chooses a random address from a given Member's PeerURLs.
+// It will panic if there is no PeerURLs available in Member.
+func (m *Member) PickPeerURL() string {
+ if len(m.PeerURLs) == 0 {
+ plog.Panicf("member should always have some peer url")
+ }
+ return m.PeerURLs[rand.Intn(len(m.PeerURLs))]
+}
+
+func (m *Member) Clone() *Member {
+ if m == nil {
+ return nil
+ }
+ mm := &Member{
+ ID: m.ID,
+ Attributes: Attributes{
+ Name: m.Name,
+ },
+ }
+ if m.PeerURLs != nil {
+ mm.PeerURLs = make([]string, len(m.PeerURLs))
+ copy(mm.PeerURLs, m.PeerURLs)
+ }
+ if m.ClientURLs != nil {
+ mm.ClientURLs = make([]string, len(m.ClientURLs))
+ copy(mm.ClientURLs, m.ClientURLs)
+ }
+ return mm
+}
+
+func (m *Member) IsStarted() bool {
+ return len(m.Name) != 0
+}
+
+func memberStoreKey(id types.ID) string {
+ return path.Join(storeMembersPrefix, id.String())
+}
+
+func MemberAttributesStorePath(id types.ID) string {
+ return path.Join(memberStoreKey(id), attributesSuffix)
+}
+
+func mustParseMemberIDFromKey(key string) types.ID {
+ id, err := types.IDFromString(path.Base(key))
+ if err != nil {
+ plog.Panicf("unexpected parse member id error: %v", err)
+ }
+ return id
+}
+
+func removedMemberStoreKey(id types.ID) string {
+ return path.Join(storeRemovedMembersPrefix, id.String())
+}
+
+// nodeToMember builds member from a key value node.
+// the child nodes of the given node MUST be sorted by key.
+func nodeToMember(n *store.NodeExtern) (*Member, error) {
+ m := &Member{ID: mustParseMemberIDFromKey(n.Key)}
+ attrs := make(map[string][]byte)
+ raftAttrKey := path.Join(n.Key, raftAttributesSuffix)
+ attrKey := path.Join(n.Key, attributesSuffix)
+ for _, nn := range n.Nodes {
+ if nn.Key != raftAttrKey && nn.Key != attrKey {
+ return nil, fmt.Errorf("unknown key %q", nn.Key)
+ }
+ attrs[nn.Key] = []byte(*nn.Value)
+ }
+ if data := attrs[raftAttrKey]; data != nil {
+ if err := json.Unmarshal(data, &m.RaftAttributes); err != nil {
+ return nil, fmt.Errorf("unmarshal raftAttributes error: %v", err)
+ }
+ } else {
+ return nil, fmt.Errorf("raftAttributes key doesn't exist")
+ }
+ if data := attrs[attrKey]; data != nil {
+ if err := json.Unmarshal(data, &m.Attributes); err != nil {
+ return m, fmt.Errorf("unmarshal attributes error: %v", err)
+ }
+ }
+ return m, nil
+}
+
+// implement sort by ID interface
+type MembersByID []*Member
+
+func (ms MembersByID) Len() int { return len(ms) }
+func (ms MembersByID) Less(i, j int) bool { return ms[i].ID < ms[j].ID }
+func (ms MembersByID) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
+
+// implement sort by peer urls interface
+type MembersByPeerURLs []*Member
+
+func (ms MembersByPeerURLs) Len() int { return len(ms) }
+func (ms MembersByPeerURLs) Less(i, j int) bool {
+ return ms[i].PeerURLs[0] < ms[j].PeerURLs[0]
+}
+func (ms MembersByPeerURLs) Swap(i, j int) { ms[i], ms[j] = ms[j], ms[i] }
diff --git a/vendor/github.com/coreos/etcd/etcdserver/member_test.go b/vendor/github.com/coreos/etcd/etcdserver/member_test.go
new file mode 100644
index 000000000..2159282a2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/member_test.go
@@ -0,0 +1,115 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "net/url"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+func timeParse(value string) *time.Time {
+ t, err := time.Parse(time.RFC3339, value)
+ if err != nil {
+ panic(err)
+ }
+ return &t
+}
+
+func TestMemberTime(t *testing.T) {
+ tests := []struct {
+ mem *Member
+ id types.ID
+ }{
+ {NewMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, "", nil), 14544069596553697298},
+ // Same ID, different name (names shouldn't matter)
+ {NewMember("memfoo", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, "", nil), 14544069596553697298},
+ // Same ID, different Time
+ {NewMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, "", timeParse("1984-12-23T15:04:05Z")), 2448790162483548276},
+ // Different cluster name
+ {NewMember("mcm1", []url.URL{{Scheme: "http", Host: "10.0.0.8:2379"}}, "etcd", timeParse("1984-12-23T15:04:05Z")), 6973882743191604649},
+ {NewMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}}, "", timeParse("1984-12-23T15:04:05Z")), 1466075294948436910},
+ // Order shouldn't matter
+ {NewMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "10.0.0.2:2379"}}, "", nil), 16552244735972308939},
+ {NewMember("mem1", []url.URL{{Scheme: "http", Host: "10.0.0.2:2379"}, {Scheme: "http", Host: "10.0.0.1:2379"}}, "", nil), 16552244735972308939},
+ }
+ for i, tt := range tests {
+ if tt.mem.ID != tt.id {
+ t.Errorf("#%d: mem.ID = %v, want %v", i, tt.mem.ID, tt.id)
+ }
+ }
+}
+
+func TestMemberPick(t *testing.T) {
+ tests := []struct {
+ memb *Member
+ urls map[string]bool
+ }{
+ {
+ newTestMember(1, []string{"abc", "def", "ghi", "jkl", "mno", "pqr", "stu"}, "", nil),
+ map[string]bool{
+ "abc": true,
+ "def": true,
+ "ghi": true,
+ "jkl": true,
+ "mno": true,
+ "pqr": true,
+ "stu": true,
+ },
+ },
+ {
+ newTestMember(2, []string{"xyz"}, "", nil),
+ map[string]bool{"xyz": true},
+ },
+ }
+ for i, tt := range tests {
+ for j := 0; j < 1000; j++ {
+ a := tt.memb.PickPeerURL()
+ if !tt.urls[a] {
+ t.Errorf("#%d: returned ID %q not in expected range!", i, a)
+ break
+ }
+ }
+ }
+}
+
+func TestMemberClone(t *testing.T) {
+ tests := []*Member{
+ newTestMember(1, nil, "abc", nil),
+ newTestMember(1, []string{"http://a"}, "abc", nil),
+ newTestMember(1, nil, "abc", []string{"http://b"}),
+ newTestMember(1, []string{"http://a"}, "abc", []string{"http://b"}),
+ }
+ for i, tt := range tests {
+ nm := tt.Clone()
+ if nm == tt {
+ t.Errorf("#%d: the pointers are the same, and clone doesn't happen", i)
+ }
+ if !reflect.DeepEqual(nm, tt) {
+ t.Errorf("#%d: member = %+v, want %+v", i, nm, tt)
+ }
+ }
+}
+
+func newTestMember(id uint64, peerURLs []string, name string, clientURLs []string) *Member {
+ return &Member{
+ ID: types.ID(id),
+ RaftAttributes: RaftAttributes{PeerURLs: peerURLs},
+ Attributes: Attributes{Name: name, ClientURLs: clientURLs},
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/metrics.go b/vendor/github.com/coreos/etcd/etcdserver/metrics.go
new file mode 100644
index 000000000..0544f3f1a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/metrics.go
@@ -0,0 +1,86 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ "github.com/coreos/etcd/pkg/runtime"
+)
+
+var (
+ // TODO: with label in v3?
+ proposeDurations = prometheus.NewSummary(prometheus.SummaryOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "proposal_durations_milliseconds",
+ Help: "The latency distributions of committing proposal.",
+ })
+ proposePending = prometheus.NewGauge(prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "pending_proposal_total",
+ Help: "The total number of pending proposals.",
+ })
+ // This is number of proposal failed in client's view.
+ // The proposal might be later got committed in raft.
+ proposeFailed = prometheus.NewCounter(prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "proposal_failed_total",
+ Help: "The total number of failed proposals.",
+ })
+
+ fileDescriptorUsed = prometheus.NewGauge(prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "server",
+ Name: "file_descriptors_used_total",
+ Help: "The total number of file descriptors used.",
+ })
+)
+
+func init() {
+ prometheus.MustRegister(proposeDurations)
+ prometheus.MustRegister(proposePending)
+ prometheus.MustRegister(proposeFailed)
+ prometheus.MustRegister(fileDescriptorUsed)
+}
+
+func monitorFileDescriptor(done <-chan struct{}) {
+ ticker := time.NewTicker(5 * time.Second)
+ defer ticker.Stop()
+ for {
+ used, err := runtime.FDUsage()
+ if err != nil {
+ plog.Errorf("cannot monitor file descriptor usage (%v)", err)
+ return
+ }
+ fileDescriptorUsed.Set(float64(used))
+ limit, err := runtime.FDLimit()
+ if err != nil {
+ plog.Errorf("cannot monitor file descriptor usage (%v)", err)
+ return
+ }
+ if used >= limit/5*4 {
+ plog.Warningf("80%% of the file descriptor limit is used [used = %d, limit = %d]", used, limit)
+ }
+ select {
+ case <-ticker.C:
+ case <-done:
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/raft.go b/vendor/github.com/coreos/etcd/etcdserver/raft.go
new file mode 100644
index 000000000..4532fad13
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/raft.go
@@ -0,0 +1,450 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "encoding/json"
+ "expvar"
+ "os"
+ "sort"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/rafthttp"
+ "github.com/coreos/etcd/wal"
+ "github.com/coreos/etcd/wal/walpb"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+)
+
+const (
+ // Number of entries for slow follower to catch-up after compacting
+ // the raft storage entries.
+ // We expect the follower has a millisecond level latency with the leader.
+ // The max throughput is around 10K. Keep a 5K entries is enough for helping
+ // follower to catch up.
+ numberOfCatchUpEntries = 5000
+
+ // The max throughput of etcd will not exceed 100MB/s (100K * 1KB value).
+ // Assuming the RTT is around 10ms, 1MB max size is large enough.
+ maxSizePerMsg = 1 * 1024 * 1024
+ // Never overflow the rafthttp buffer, which is 4096.
+ // TODO: a better const?
+ maxInflightMsgs = 4096 / 8
+)
+
+var (
+ // indirection for expvar func interface
+ // expvar panics when publishing duplicate name
+ // expvar does not support remove a registered name
+ // so only register a func that calls raftStatus
+ // and change raftStatus as we need.
+ raftStatus func() raft.Status
+)
+
+func init() {
+ raft.SetLogger(capnslog.NewPackageLogger("github.com/coreos/etcd", "raft"))
+ expvar.Publish("raft.status", expvar.Func(func() interface{} { return raftStatus() }))
+}
+
+type RaftTimer interface {
+ Index() uint64
+ Term() uint64
+}
+
+// apply contains entries, snapshot be applied.
+// After applied all the items, the application needs
+// to send notification to done chan.
+type apply struct {
+ entries []raftpb.Entry
+ snapshot raftpb.Snapshot
+ done chan struct{}
+}
+
+type raftNode struct {
+ // Cache of the latest raft index and raft term the server has seen.
+ // These three unit64 fields must be the first elements to keep 64-bit
+ // alignment for atomic access to the fields.
+ index uint64
+ term uint64
+ lead uint64
+
+ mu sync.Mutex
+ // last lead elected time
+ lt time.Time
+
+ raft.Node
+
+ // a chan to send out apply
+ applyc chan apply
+
+ // TODO: remove the etcdserver related logic from raftNode
+ // TODO: add a state machine interface to apply the commit entries
+ // and do snapshot/recover
+ s *EtcdServer
+
+ // utility
+ ticker <-chan time.Time
+ raftStorage *raftStorage
+ storage Storage
+ // transport specifies the transport to send and receive msgs to members.
+ // Sending messages MUST NOT block. It is okay to drop messages, since
+ // clients should timeout and reissue their messages.
+ // If transport is nil, server will panic.
+ transport rafthttp.Transporter
+
+ stopped chan struct{}
+ done chan struct{}
+}
+
+// start prepares and starts raftNode in a new goroutine. It is no longer safe
+// to modify the fields after it has been started.
+// TODO: Ideally raftNode should get rid of the passed in server structure.
+func (r *raftNode) start(s *EtcdServer) {
+ r.s = s
+ r.raftStorage.raftStarted = true
+ r.applyc = make(chan apply)
+ r.stopped = make(chan struct{})
+ r.done = make(chan struct{})
+
+ go func() {
+ var syncC <-chan time.Time
+
+ defer r.onStop()
+ for {
+ select {
+ case <-r.ticker:
+ r.Tick()
+ case rd := <-r.Ready():
+ if rd.SoftState != nil {
+ if lead := atomic.LoadUint64(&r.lead); rd.SoftState.Lead != raft.None && lead != rd.SoftState.Lead {
+ r.mu.Lock()
+ r.lt = time.Now()
+ r.mu.Unlock()
+ }
+ atomic.StoreUint64(&r.lead, rd.SoftState.Lead)
+ if rd.RaftState == raft.StateLeader {
+ syncC = r.s.SyncTicker
+ // TODO: remove the nil checking
+ // current test utility does not provide the stats
+ if r.s.stats != nil {
+ r.s.stats.BecomeLeader()
+ }
+ } else {
+ syncC = nil
+ }
+ }
+
+ apply := apply{
+ entries: rd.CommittedEntries,
+ snapshot: rd.Snapshot,
+ done: make(chan struct{}),
+ }
+
+ select {
+ case r.applyc <- apply:
+ case <-r.stopped:
+ return
+ }
+
+ if !raft.IsEmptySnap(rd.Snapshot) {
+ if err := r.storage.SaveSnap(rd.Snapshot); err != nil {
+ plog.Fatalf("raft save snapshot error: %v", err)
+ }
+ r.raftStorage.ApplySnapshot(rd.Snapshot)
+ plog.Infof("raft applied incoming snapshot at index %d", rd.Snapshot.Metadata.Index)
+ }
+ if err := r.storage.Save(rd.HardState, rd.Entries); err != nil {
+ plog.Fatalf("raft save state and entries error: %v", err)
+ }
+ r.raftStorage.Append(rd.Entries)
+
+ r.s.send(rd.Messages)
+
+ select {
+ case <-apply.done:
+ case <-r.stopped:
+ return
+ }
+ r.Advance()
+ case <-syncC:
+ r.s.sync(r.s.cfg.ReqTimeout())
+ case <-r.stopped:
+ return
+ }
+ }
+ }()
+}
+
+func (r *raftNode) apply() chan apply {
+ return r.applyc
+}
+
+func (r *raftNode) leadElectedTime() time.Time {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ return r.lt
+}
+
+func (r *raftNode) stop() {
+ r.stopped <- struct{}{}
+ <-r.done
+}
+
+func (r *raftNode) onStop() {
+ r.Stop()
+ r.transport.Stop()
+ if err := r.storage.Close(); err != nil {
+ plog.Panicf("raft close storage error: %v", err)
+ }
+ close(r.done)
+}
+
+// for testing
+func (r *raftNode) pauseSending() {
+ p := r.transport.(rafthttp.Pausable)
+ p.Pause()
+}
+
+func (r *raftNode) resumeSending() {
+ p := r.transport.(rafthttp.Pausable)
+ p.Resume()
+}
+
+// advanceTicksForElection advances ticks to the node for fast election.
+// This reduces the time to wait for first leader election if bootstrapping the whole
+// cluster, while leaving at least 1 heartbeat for possible existing leader
+// to contact it.
+func advanceTicksForElection(n raft.Node, electionTicks int) {
+ for i := 0; i < electionTicks-1; i++ {
+ n.Tick()
+ }
+}
+
+func startNode(cfg *ServerConfig, cl *cluster, ids []types.ID) (id types.ID, n raft.Node, s *raftStorage, w *wal.WAL) {
+ var err error
+ member := cl.MemberByName(cfg.Name)
+ metadata := pbutil.MustMarshal(
+ &pb.Metadata{
+ NodeID: uint64(member.ID),
+ ClusterID: uint64(cl.ID()),
+ },
+ )
+ if err := os.MkdirAll(cfg.SnapDir(), privateDirMode); err != nil {
+ plog.Fatalf("create snapshot directory error: %v", err)
+ }
+ if w, err = wal.Create(cfg.WALDir(), metadata); err != nil {
+ plog.Fatalf("create wal error: %v", err)
+ }
+ peers := make([]raft.Peer, len(ids))
+ for i, id := range ids {
+ ctx, err := json.Marshal((*cl).Member(id))
+ if err != nil {
+ plog.Panicf("marshal member should never fail: %v", err)
+ }
+ peers[i] = raft.Peer{ID: uint64(id), Context: ctx}
+ }
+ id = member.ID
+ plog.Infof("starting member %s in cluster %s", id, cl.ID())
+ s = newRaftStorage()
+ c := &raft.Config{
+ ID: uint64(id),
+ ElectionTick: cfg.ElectionTicks,
+ HeartbeatTick: 1,
+ Storage: s,
+ MaxSizePerMsg: maxSizePerMsg,
+ MaxInflightMsgs: maxInflightMsgs,
+ }
+ n = raft.StartNode(c, peers)
+ raftStatus = n.Status
+ advanceTicksForElection(n, c.ElectionTick)
+ return
+}
+
+func restartNode(cfg *ServerConfig, snapshot *raftpb.Snapshot) (types.ID, *cluster, raft.Node, *raftStorage, *wal.WAL) {
+ var walsnap walpb.Snapshot
+ if snapshot != nil {
+ walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
+ }
+ w, id, cid, st, ents := readWAL(cfg.WALDir(), walsnap)
+
+ plog.Infof("restarting member %s in cluster %s at commit index %d", id, cid, st.Commit)
+ cl := newCluster("")
+ cl.SetID(cid)
+ s := newRaftStorage()
+ if snapshot != nil {
+ s.ApplySnapshot(*snapshot)
+ }
+ s.SetHardState(st)
+ s.Append(ents)
+ c := &raft.Config{
+ ID: uint64(id),
+ ElectionTick: cfg.ElectionTicks,
+ HeartbeatTick: 1,
+ Storage: s,
+ MaxSizePerMsg: maxSizePerMsg,
+ MaxInflightMsgs: maxInflightMsgs,
+ }
+ n := raft.RestartNode(c)
+ raftStatus = n.Status
+ advanceTicksForElection(n, c.ElectionTick)
+ return id, cl, n, s, w
+}
+
+func restartAsStandaloneNode(cfg *ServerConfig, snapshot *raftpb.Snapshot) (types.ID, *cluster, raft.Node, *raftStorage, *wal.WAL) {
+ var walsnap walpb.Snapshot
+ if snapshot != nil {
+ walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
+ }
+ w, id, cid, st, ents := readWAL(cfg.WALDir(), walsnap)
+
+ // discard the previously uncommitted entries
+ for i, ent := range ents {
+ if ent.Index > st.Commit {
+ plog.Infof("discarding %d uncommitted WAL entries ", len(ents)-i)
+ ents = ents[:i]
+ break
+ }
+ }
+
+ // force append the configuration change entries
+ toAppEnts := createConfigChangeEnts(getIDs(snapshot, ents), uint64(id), st.Term, st.Commit)
+ ents = append(ents, toAppEnts...)
+
+ // force commit newly appended entries
+ err := w.Save(raftpb.HardState{}, toAppEnts)
+ if err != nil {
+ plog.Fatalf("%v", err)
+ }
+ if len(ents) != 0 {
+ st.Commit = ents[len(ents)-1].Index
+ }
+
+ plog.Printf("forcing restart of member %s in cluster %s at commit index %d", id, cid, st.Commit)
+ cl := newCluster("")
+ cl.SetID(cid)
+ s := newRaftStorage()
+ if snapshot != nil {
+ s.ApplySnapshot(*snapshot)
+ }
+ s.SetHardState(st)
+ s.Append(ents)
+ c := &raft.Config{
+ ID: uint64(id),
+ ElectionTick: cfg.ElectionTicks,
+ HeartbeatTick: 1,
+ Storage: s,
+ MaxSizePerMsg: maxSizePerMsg,
+ MaxInflightMsgs: maxInflightMsgs,
+ }
+ n := raft.RestartNode(c)
+ raftStatus = n.Status
+ return id, cl, n, s, w
+}
+
+// getIDs returns an ordered set of IDs included in the given snapshot and
+// the entries. The given snapshot/entries can contain two kinds of
+// ID-related entry:
+// - ConfChangeAddNode, in which case the contained ID will be added into the set.
+// - ConfChangeAddRemove, in which case the contained ID will be removed from the set.
+func getIDs(snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64 {
+ ids := make(map[uint64]bool)
+ if snap != nil {
+ for _, id := range snap.Metadata.ConfState.Nodes {
+ ids[id] = true
+ }
+ }
+ for _, e := range ents {
+ if e.Type != raftpb.EntryConfChange {
+ continue
+ }
+ var cc raftpb.ConfChange
+ pbutil.MustUnmarshal(&cc, e.Data)
+ switch cc.Type {
+ case raftpb.ConfChangeAddNode:
+ ids[cc.NodeID] = true
+ case raftpb.ConfChangeRemoveNode:
+ delete(ids, cc.NodeID)
+ case raftpb.ConfChangeUpdateNode:
+ // do nothing
+ default:
+ plog.Panicf("ConfChange Type should be either ConfChangeAddNode or ConfChangeRemoveNode!")
+ }
+ }
+ sids := make(types.Uint64Slice, 0)
+ for id := range ids {
+ sids = append(sids, id)
+ }
+ sort.Sort(sids)
+ return []uint64(sids)
+}
+
+// createConfigChangeEnts creates a series of Raft entries (i.e.
+// EntryConfChange) to remove the set of given IDs from the cluster. The ID
+// `self` is _not_ removed, even if present in the set.
+// If `self` is not inside the given ids, it creates a Raft entry to add a
+// default member with the given `self`.
+func createConfigChangeEnts(ids []uint64, self uint64, term, index uint64) []raftpb.Entry {
+ ents := make([]raftpb.Entry, 0)
+ next := index + 1
+ found := false
+ for _, id := range ids {
+ if id == self {
+ found = true
+ continue
+ }
+ cc := &raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: id,
+ }
+ e := raftpb.Entry{
+ Type: raftpb.EntryConfChange,
+ Data: pbutil.MustMarshal(cc),
+ Term: term,
+ Index: next,
+ }
+ ents = append(ents, e)
+ next++
+ }
+ if !found {
+ m := Member{
+ ID: types.ID(self),
+ RaftAttributes: RaftAttributes{PeerURLs: []string{"http://localhost:7001", "http://localhost:2380"}},
+ }
+ ctx, err := json.Marshal(m)
+ if err != nil {
+ plog.Panicf("marshal member should never fail: %v", err)
+ }
+ cc := &raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: self,
+ Context: ctx,
+ }
+ e := raftpb.Entry{
+ Type: raftpb.EntryConfChange,
+ Data: pbutil.MustMarshal(cc),
+ Term: term,
+ Index: next,
+ }
+ ents = append(ents, e)
+ }
+ return ents
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/raft_storage.go b/vendor/github.com/coreos/etcd/etcdserver/raft_storage.go
new file mode 100644
index 000000000..b546793df
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/raft_storage.go
@@ -0,0 +1,64 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+type raftStorage struct {
+ *raft.MemoryStorage
+ // snapStore is the place to request snapshot when v3demo is enabled.
+ // If snapStore is nil, it uses the snapshot saved in MemoryStorage.
+ snapStore *snapshotStore
+ // raftStarted indicates whether raft starts to function. If not, it cannot
+ // request snapshot, and should get snapshot from MemoryStorage.
+ raftStarted bool
+}
+
+func newRaftStorage() *raftStorage {
+ return &raftStorage{
+ MemoryStorage: raft.NewMemoryStorage(),
+ }
+}
+
+func (rs *raftStorage) reqsnap() <-chan struct{} {
+ if rs.snapStore == nil {
+ return nil
+ }
+ return rs.snapStore.reqsnapc
+}
+
+func (rs *raftStorage) raftsnap() chan<- raftpb.Snapshot {
+ if rs.snapStore == nil {
+ return nil
+ }
+ return rs.snapStore.raftsnapc
+}
+
+// Snapshot returns raft snapshot. If snapStore is nil or raft is not started, this method
+// returns snapshot saved in MemoryStorage. Otherwise, this method
+// returns snapshot from snapStore.
+func (rs *raftStorage) Snapshot() (raftpb.Snapshot, error) {
+ if rs.snapStore == nil || !rs.raftStarted {
+ return rs.MemoryStorage.Snapshot()
+ }
+ snap, err := rs.snapStore.getSnap()
+ if err != nil {
+ return raftpb.Snapshot{}, err
+ }
+ return snap.raft(), nil
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/raft_test.go b/vendor/github.com/coreos/etcd/etcdserver/raft_test.go
new file mode 100644
index 000000000..336e85e88
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/raft_test.go
@@ -0,0 +1,173 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "encoding/json"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestGetIDs(t *testing.T) {
+ addcc := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 2}
+ addEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(addcc)}
+ removecc := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
+ removeEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc)}
+ normalEntry := raftpb.Entry{Type: raftpb.EntryNormal}
+ updatecc := &raftpb.ConfChange{Type: raftpb.ConfChangeUpdateNode, NodeID: 2}
+ updateEntry := raftpb.Entry{Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(updatecc)}
+
+ tests := []struct {
+ confState *raftpb.ConfState
+ ents []raftpb.Entry
+
+ widSet []uint64
+ }{
+ {nil, []raftpb.Entry{}, []uint64{}},
+ {&raftpb.ConfState{Nodes: []uint64{1}},
+ []raftpb.Entry{}, []uint64{1}},
+ {&raftpb.ConfState{Nodes: []uint64{1}},
+ []raftpb.Entry{addEntry}, []uint64{1, 2}},
+ {&raftpb.ConfState{Nodes: []uint64{1}},
+ []raftpb.Entry{addEntry, removeEntry}, []uint64{1}},
+ {&raftpb.ConfState{Nodes: []uint64{1}},
+ []raftpb.Entry{addEntry, normalEntry}, []uint64{1, 2}},
+ {&raftpb.ConfState{Nodes: []uint64{1}},
+ []raftpb.Entry{addEntry, normalEntry, updateEntry}, []uint64{1, 2}},
+ {&raftpb.ConfState{Nodes: []uint64{1}},
+ []raftpb.Entry{addEntry, removeEntry, normalEntry}, []uint64{1}},
+ }
+
+ for i, tt := range tests {
+ var snap raftpb.Snapshot
+ if tt.confState != nil {
+ snap.Metadata.ConfState = *tt.confState
+ }
+ idSet := getIDs(&snap, tt.ents)
+ if !reflect.DeepEqual(idSet, tt.widSet) {
+ t.Errorf("#%d: idset = %#v, want %#v", i, idSet, tt.widSet)
+ }
+ }
+}
+
+func TestCreateConfigChangeEnts(t *testing.T) {
+ m := Member{
+ ID: types.ID(1),
+ RaftAttributes: RaftAttributes{PeerURLs: []string{"http://localhost:7001", "http://localhost:2380"}},
+ }
+ ctx, err := json.Marshal(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ addcc1 := &raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1, Context: ctx}
+ removecc2 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 2}
+ removecc3 := &raftpb.ConfChange{Type: raftpb.ConfChangeRemoveNode, NodeID: 3}
+ tests := []struct {
+ ids []uint64
+ self uint64
+ term, index uint64
+
+ wents []raftpb.Entry
+ }{
+ {
+ []uint64{1},
+ 1,
+ 1, 1,
+
+ []raftpb.Entry{},
+ },
+ {
+ []uint64{1, 2},
+ 1,
+ 1, 1,
+
+ []raftpb.Entry{{Term: 1, Index: 2, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)}},
+ },
+ {
+ []uint64{1, 2},
+ 1,
+ 2, 2,
+
+ []raftpb.Entry{{Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)}},
+ },
+ {
+ []uint64{1, 2, 3},
+ 1,
+ 2, 2,
+
+ []raftpb.Entry{
+ {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)},
+ {Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
+ },
+ },
+ {
+ []uint64{2, 3},
+ 2,
+ 2, 2,
+
+ []raftpb.Entry{
+ {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
+ },
+ },
+ {
+ []uint64{2, 3},
+ 1,
+ 2, 2,
+
+ []raftpb.Entry{
+ {Term: 2, Index: 3, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc2)},
+ {Term: 2, Index: 4, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(removecc3)},
+ {Term: 2, Index: 5, Type: raftpb.EntryConfChange, Data: pbutil.MustMarshal(addcc1)},
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ gents := createConfigChangeEnts(tt.ids, tt.self, tt.term, tt.index)
+ if !reflect.DeepEqual(gents, tt.wents) {
+ t.Errorf("#%d: ents = %v, want %v", i, gents, tt.wents)
+ }
+ }
+}
+
+func TestStopRaftWhenWaitingForApplyDone(t *testing.T) {
+ n := newReadyNode()
+ r := raftNode{
+ Node: n,
+ storage: &storageRecorder{},
+ raftStorage: newRaftStorage(),
+ transport: &nopTransporter{},
+ }
+ r.start(&EtcdServer{r: r})
+ n.readyc <- raft.Ready{}
+ select {
+ case <-r.applyc:
+ case <-time.After(time.Second):
+ t.Fatalf("failed to receive apply struct")
+ }
+
+ r.stopped <- struct{}{}
+ select {
+ case <-r.done:
+ case <-time.After(time.Second):
+ t.Fatalf("failed to stop raft loop")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/server.go b/vendor/github.com/coreos/etcd/etcdserver/server.go
new file mode 100644
index 000000000..1d408a637
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/server.go
@@ -0,0 +1,1166 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "encoding/json"
+ "errors"
+ "expvar"
+ "fmt"
+ "log"
+ "math/rand"
+ "net/http"
+ "os"
+ "path"
+ "regexp"
+ "sync/atomic"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/discovery"
+ "github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/fileutil"
+ "github.com/coreos/etcd/pkg/idutil"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/runtime"
+ "github.com/coreos/etcd/pkg/timeutil"
+ "github.com/coreos/etcd/pkg/transport"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/pkg/wait"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/rafthttp"
+ "github.com/coreos/etcd/snap"
+ dstorage "github.com/coreos/etcd/storage"
+ "github.com/coreos/etcd/store"
+ "github.com/coreos/etcd/version"
+ "github.com/coreos/etcd/wal"
+)
+
+const (
+ // owner can make/remove files inside the directory
+ privateDirMode = 0700
+
+ DefaultSnapCount = 10000
+
+ StoreClusterPrefix = "/0"
+ StoreKeysPrefix = "/1"
+
+ purgeFileInterval = 30 * time.Second
+ monitorVersionInterval = 5 * time.Second
+
+ databaseFilename = "db"
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "etcdserver")
+
+ storeMemberAttributeRegexp = regexp.MustCompile(path.Join(storeMembersPrefix, "[[:xdigit:]]{1,16}", attributesSuffix))
+)
+
+func init() {
+ rand.Seed(time.Now().UnixNano())
+
+ expvar.Publish(
+ "file_descriptor_limit",
+ expvar.Func(
+ func() interface{} {
+ n, _ := runtime.FDLimit()
+ return n
+ },
+ ),
+ )
+}
+
+type Response struct {
+ Event *store.Event
+ Watcher store.Watcher
+ err error
+}
+
+type Server interface {
+ // Start performs any initialization of the Server necessary for it to
+ // begin serving requests. It must be called before Do or Process.
+ // Start must be non-blocking; any long-running server functionality
+ // should be implemented in goroutines.
+ Start()
+ // Stop terminates the Server and performs any necessary finalization.
+ // Do and Process cannot be called after Stop has been invoked.
+ Stop()
+ // ID returns the ID of the Server.
+ ID() types.ID
+ // Leader returns the ID of the leader Server.
+ Leader() types.ID
+ // Do takes a request and attempts to fulfill it, returning a Response.
+ Do(ctx context.Context, r pb.Request) (Response, error)
+ // Process takes a raft message and applies it to the server's raft state
+ // machine, respecting any timeout of the given context.
+ Process(ctx context.Context, m raftpb.Message) error
+ // AddMember attempts to add a member into the cluster. It will return
+ // ErrIDRemoved if member ID is removed from the cluster, or return
+ // ErrIDExists if member ID exists in the cluster.
+ AddMember(ctx context.Context, memb Member) error
+ // RemoveMember attempts to remove a member from the cluster. It will
+ // return ErrIDRemoved if member ID is removed from the cluster, or return
+ // ErrIDNotFound if member ID is not in the cluster.
+ RemoveMember(ctx context.Context, id uint64) error
+
+ // UpdateMember attempts to update an existing member in the cluster. It will
+ // return ErrIDNotFound if the member ID does not exist.
+ UpdateMember(ctx context.Context, updateMemb Member) error
+
+ // ClusterVersion is the cluster-wide minimum major.minor version.
+ // Cluster version is set to the min version that a etcd member is
+ // compatible with when first bootstrap.
+ //
+ // ClusterVersion is nil until the cluster is bootstrapped (has a quorum).
+ //
+ // During a rolling upgrades, the ClusterVersion will be updated
+ // automatically after a sync. (5 second by default)
+ //
+ // The API/raft component can utilize ClusterVersion to determine if
+ // it can accept a client request or a raft RPC.
+ // NOTE: ClusterVersion might be nil when etcd 2.1 works with etcd 2.0 and
+ // the leader is etcd 2.0. etcd 2.0 leader will not update clusterVersion since
+ // this feature is introduced post 2.0.
+ ClusterVersion() *semver.Version
+}
+
+// EtcdServer is the production implementation of the Server interface
+type EtcdServer struct {
+ // r must be the first element to keep 64-bit alignment for atomic
+ // access to fields
+ r raftNode
+
+ cfg *ServerConfig
+ snapCount uint64
+
+ w wait.Wait
+ stop chan struct{}
+ done chan struct{}
+ errorc chan error
+ id types.ID
+ attributes Attributes
+
+ cluster *cluster
+
+ store store.Store
+ kv dstorage.KV
+
+ stats *stats.ServerStats
+ lstats *stats.LeaderStats
+
+ SyncTicker <-chan time.Time
+
+ // versionTr used to send requests for peer version
+ versionTr *http.Transport
+ reqIDGen *idutil.Generator
+
+ // forceVersionC is used to force the version monitor loop
+ // to detect the cluster version immediately.
+ forceVersionC chan struct{}
+}
+
+// NewServer creates a new EtcdServer from the supplied configuration. The
+// configuration is considered static for the lifetime of the EtcdServer.
+func NewServer(cfg *ServerConfig) (*EtcdServer, error) {
+ st := store.New(StoreClusterPrefix, StoreKeysPrefix)
+ var w *wal.WAL
+ var n raft.Node
+ var s *raftStorage
+ var id types.ID
+ var cl *cluster
+
+ if !cfg.V3demo && fileutil.Exist(path.Join(cfg.StorageDir(), databaseFilename)) {
+ return nil, errors.New("experimental-v3demo cannot be disabled once it is enabled")
+ }
+
+ // Run the migrations.
+ dataVer, err := version.DetectDataDir(cfg.DataDir)
+ if err != nil {
+ return nil, err
+ }
+ if err := upgradeDataDir(cfg.DataDir, cfg.Name, dataVer); err != nil {
+ return nil, err
+ }
+
+ err = os.MkdirAll(cfg.MemberDir(), privateDirMode)
+ if err != nil && err != os.ErrExist {
+ return nil, err
+ }
+
+ haveWAL := wal.Exist(cfg.WALDir())
+ ss := snap.New(cfg.SnapDir())
+
+ pt, err := transport.NewTransport(cfg.PeerTLSInfo, cfg.peerDialTimeout())
+ if err != nil {
+ return nil, err
+ }
+ var remotes []*Member
+ switch {
+ case !haveWAL && !cfg.NewCluster:
+ if err := cfg.VerifyJoinExisting(); err != nil {
+ return nil, err
+ }
+ cl, err = newClusterFromURLsMap(cfg.InitialClusterToken, cfg.InitialPeerURLsMap)
+ if err != nil {
+ return nil, err
+ }
+ existingCluster, err := GetClusterFromRemotePeers(getRemotePeerURLs(cl, cfg.Name), pt)
+ if err != nil {
+ return nil, fmt.Errorf("cannot fetch cluster info from peer urls: %v", err)
+ }
+ if err := ValidateClusterAndAssignIDs(cl, existingCluster); err != nil {
+ return nil, fmt.Errorf("error validating peerURLs %s: %v", existingCluster, err)
+ }
+ if !isCompatibleWithCluster(cl, cl.MemberByName(cfg.Name).ID, pt) {
+ return nil, fmt.Errorf("incomptible with current running cluster")
+ }
+
+ remotes = existingCluster.Members()
+ cl.SetID(existingCluster.id)
+ cl.SetStore(st)
+ cfg.Print()
+ id, n, s, w = startNode(cfg, cl, nil)
+ case !haveWAL && cfg.NewCluster:
+ if err := cfg.VerifyBootstrap(); err != nil {
+ return nil, err
+ }
+ cl, err = newClusterFromURLsMap(cfg.InitialClusterToken, cfg.InitialPeerURLsMap)
+ if err != nil {
+ return nil, err
+ }
+ m := cl.MemberByName(cfg.Name)
+ if isMemberBootstrapped(cl, cfg.Name, pt) {
+ return nil, fmt.Errorf("member %s has already been bootstrapped", m.ID)
+ }
+ if cfg.ShouldDiscover() {
+ var str string
+ var err error
+ str, err = discovery.JoinCluster(cfg.DiscoveryURL, cfg.DiscoveryProxy, m.ID, cfg.InitialPeerURLsMap.String())
+ if err != nil {
+ return nil, &discoveryError{op: "join", err: err}
+ }
+ urlsmap, err := types.NewURLsMap(str)
+ if err != nil {
+ return nil, err
+ }
+ if checkDuplicateURL(urlsmap) {
+ return nil, fmt.Errorf("discovery cluster %s has duplicate url", urlsmap)
+ }
+ if cl, err = newClusterFromURLsMap(cfg.InitialClusterToken, urlsmap); err != nil {
+ return nil, err
+ }
+ }
+ cl.SetStore(st)
+ cfg.PrintWithInitial()
+ id, n, s, w = startNode(cfg, cl, cl.MemberIDs())
+ case haveWAL:
+ if err := fileutil.IsDirWriteable(cfg.DataDir); err != nil {
+ return nil, fmt.Errorf("cannot write to data directory: %v", err)
+ }
+
+ if err := fileutil.IsDirWriteable(cfg.MemberDir()); err != nil {
+ return nil, fmt.Errorf("cannot write to member directory: %v", err)
+ }
+
+ if err := fileutil.IsDirWriteable(cfg.WALDir()); err != nil {
+ return nil, fmt.Errorf("cannot write to WAL directory: %v", err)
+ }
+
+ if cfg.ShouldDiscover() {
+ plog.Warningf("discovery token ignored since a cluster has already been initialized. Valid log found at %q", cfg.WALDir())
+ }
+ var snapshot *raftpb.Snapshot
+ var err error
+ snapshot, err = ss.Load()
+ if err != nil && err != snap.ErrNoSnapshot {
+ return nil, err
+ }
+ if snapshot != nil {
+ if err := st.Recovery(snapshot.Data); err != nil {
+ plog.Panicf("recovered store from snapshot error: %v", err)
+ }
+ plog.Infof("recovered store from snapshot at index %d", snapshot.Metadata.Index)
+ }
+ cfg.Print()
+ if !cfg.ForceNewCluster {
+ id, cl, n, s, w = restartNode(cfg, snapshot)
+ } else {
+ id, cl, n, s, w = restartAsStandaloneNode(cfg, snapshot)
+ }
+ cl.SetStore(st)
+ cl.Recover()
+ default:
+ return nil, fmt.Errorf("unsupported bootstrap config")
+ }
+
+ sstats := &stats.ServerStats{
+ Name: cfg.Name,
+ ID: id.String(),
+ }
+ sstats.Initialize()
+ lstats := stats.NewLeaderStats(id.String())
+
+ srv := &EtcdServer{
+ cfg: cfg,
+ snapCount: cfg.SnapCount,
+ errorc: make(chan error, 1),
+ store: st,
+ r: raftNode{
+ Node: n,
+ ticker: time.Tick(time.Duration(cfg.TickMs) * time.Millisecond),
+ raftStorage: s,
+ storage: NewStorage(w, ss),
+ },
+ id: id,
+ attributes: Attributes{Name: cfg.Name, ClientURLs: cfg.ClientURLs.StringSlice()},
+ cluster: cl,
+ stats: sstats,
+ lstats: lstats,
+ SyncTicker: time.Tick(500 * time.Millisecond),
+ versionTr: pt,
+ reqIDGen: idutil.NewGenerator(uint8(id), time.Now()),
+ forceVersionC: make(chan struct{}),
+ }
+
+ if cfg.V3demo {
+ err = os.MkdirAll(cfg.StorageDir(), privateDirMode)
+ if err != nil && err != os.ErrExist {
+ return nil, err
+ }
+ srv.kv = dstorage.New(path.Join(cfg.StorageDir(), databaseFilename))
+ if err := srv.kv.Restore(); err != nil {
+ plog.Fatalf("v3 storage restore error: %v", err)
+ }
+ s.snapStore = newSnapshotStore(cfg.StorageDir(), srv.kv)
+ }
+
+ // TODO: move transport initialization near the definition of remote
+ tr := &rafthttp.Transport{
+ TLSInfo: cfg.PeerTLSInfo,
+ DialTimeout: cfg.peerDialTimeout(),
+ ID: id,
+ ClusterID: cl.ID(),
+ Raft: srv,
+ SnapSaver: s.snapStore,
+ ServerStats: sstats,
+ LeaderStats: lstats,
+ ErrorC: srv.errorc,
+ V3demo: cfg.V3demo,
+ }
+ if err := tr.Start(); err != nil {
+ return nil, err
+ }
+ // add all remotes into transport
+ for _, m := range remotes {
+ if m.ID != id {
+ tr.AddRemote(m.ID, m.PeerURLs)
+ }
+ }
+ for _, m := range cl.Members() {
+ if m.ID != id {
+ tr.AddPeer(m.ID, m.PeerURLs)
+ }
+ }
+ srv.r.transport = tr
+
+ if cfg.V3demo {
+ s.snapStore.tr = tr
+ }
+
+ return srv, nil
+}
+
+// Start prepares and starts server in a new goroutine. It is no longer safe to
+// modify a server's fields after it has been sent to Start.
+// It also starts a goroutine to publish its server information.
+func (s *EtcdServer) Start() {
+ s.start()
+ go s.publish(s.cfg.ReqTimeout())
+ go s.purgeFile()
+ go monitorFileDescriptor(s.done)
+ go s.monitorVersions()
+}
+
+// start prepares and starts server in a new goroutine. It is no longer safe to
+// modify a server's fields after it has been sent to Start.
+// This function is just used for testing.
+func (s *EtcdServer) start() {
+ if s.snapCount == 0 {
+ plog.Infof("set snapshot count to default %d", DefaultSnapCount)
+ s.snapCount = DefaultSnapCount
+ }
+ s.w = wait.New()
+ s.done = make(chan struct{})
+ s.stop = make(chan struct{})
+ if s.ClusterVersion() != nil {
+ plog.Infof("starting server... [version: %v, cluster version: %v]", version.Version, version.Cluster(s.ClusterVersion().String()))
+ } else {
+ plog.Infof("starting server... [version: %v, cluster version: to_be_decided]", version.Version)
+ }
+ // TODO: if this is an empty log, writes all peer infos
+ // into the first entry
+ go s.run()
+}
+
+func (s *EtcdServer) purgeFile() {
+ var serrc, werrc <-chan error
+ if s.cfg.MaxSnapFiles > 0 {
+ serrc = fileutil.PurgeFile(s.cfg.SnapDir(), "snap", s.cfg.MaxSnapFiles, purgeFileInterval, s.done)
+ }
+ if s.cfg.MaxWALFiles > 0 {
+ werrc = fileutil.PurgeFile(s.cfg.WALDir(), "wal", s.cfg.MaxWALFiles, purgeFileInterval, s.done)
+ }
+ select {
+ case e := <-werrc:
+ plog.Fatalf("failed to purge wal file %v", e)
+ case e := <-serrc:
+ plog.Fatalf("failed to purge snap file %v", e)
+ case <-s.done:
+ return
+ }
+}
+
+func (s *EtcdServer) ID() types.ID { return s.id }
+
+func (s *EtcdServer) Cluster() Cluster { return s.cluster }
+
+func (s *EtcdServer) RaftHandler() http.Handler { return s.r.transport.Handler() }
+
+func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error {
+ if s.cluster.IsIDRemoved(types.ID(m.From)) {
+ plog.Warningf("reject message from removed member %s", types.ID(m.From).String())
+ return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message from removed member")
+ }
+ if m.Type == raftpb.MsgApp {
+ s.stats.RecvAppendReq(types.ID(m.From).String(), m.Size())
+ }
+ return s.r.Step(ctx, m)
+}
+
+func (s *EtcdServer) IsIDRemoved(id uint64) bool { return s.cluster.IsIDRemoved(types.ID(id)) }
+
+func (s *EtcdServer) ReportUnreachable(id uint64) { s.r.ReportUnreachable(id) }
+
+// ReportSnapshot reports snapshot sent status to the raft state machine,
+// and clears the used snapshot from the snapshot store.
+func (s *EtcdServer) ReportSnapshot(id uint64, status raft.SnapshotStatus) {
+ s.r.ReportSnapshot(id, status)
+ if s.cfg.V3demo {
+ s.r.raftStorage.snapStore.clearUsedSnap()
+ }
+}
+
+func (s *EtcdServer) run() {
+ snap, err := s.r.raftStorage.Snapshot()
+ if err != nil {
+ plog.Panicf("get snapshot from raft storage error: %v", err)
+ }
+ confState := snap.Metadata.ConfState
+ snapi := snap.Metadata.Index
+ appliedi := snapi
+ s.r.start(s)
+ defer func() {
+ s.r.stop()
+ close(s.done)
+ }()
+
+ var shouldstop bool
+ for {
+ select {
+ case apply := <-s.r.apply():
+ // apply snapshot
+ if !raft.IsEmptySnap(apply.snapshot) {
+ if apply.snapshot.Metadata.Index <= appliedi {
+ plog.Panicf("snapshot index [%d] should > appliedi[%d] + 1",
+ apply.snapshot.Metadata.Index, appliedi)
+ }
+
+ if err := s.store.Recovery(apply.snapshot.Data); err != nil {
+ plog.Panicf("recovery store error: %v", err)
+ }
+ s.cluster.Recover()
+
+ // recover raft transport
+ s.r.transport.RemoveAllPeers()
+ for _, m := range s.cluster.Members() {
+ if m.ID == s.ID() {
+ continue
+ }
+ s.r.transport.AddPeer(m.ID, m.PeerURLs)
+ }
+
+ appliedi = apply.snapshot.Metadata.Index
+ snapi = appliedi
+ confState = apply.snapshot.Metadata.ConfState
+ plog.Infof("recovered from incoming snapshot at index %d", snapi)
+ }
+
+ // apply entries
+ if len(apply.entries) != 0 {
+ firsti := apply.entries[0].Index
+ if firsti > appliedi+1 {
+ plog.Panicf("first index of committed entry[%d] should <= appliedi[%d] + 1", firsti, appliedi)
+ }
+ var ents []raftpb.Entry
+ if appliedi+1-firsti < uint64(len(apply.entries)) {
+ ents = apply.entries[appliedi+1-firsti:]
+ }
+ if appliedi, shouldstop = s.apply(ents, &confState); shouldstop {
+ go s.stopWithDelay(10*100*time.Millisecond, fmt.Errorf("the member has been permanently removed from the cluster"))
+ }
+ }
+
+ // wait for the raft routine to finish the disk writes before triggering a
+ // snapshot. or applied index might be greater than the last index in raft
+ // storage, since the raft routine might be slower than apply routine.
+ apply.done <- struct{}{}
+
+ // trigger snapshot
+ if appliedi-snapi > s.snapCount {
+ plog.Infof("start to snapshot (applied: %d, lastsnap: %d)", appliedi, snapi)
+ s.snapshot(appliedi, confState)
+ snapi = appliedi
+ }
+ case <-s.r.raftStorage.reqsnap():
+ s.r.raftStorage.raftsnap() <- s.createRaftSnapshot(appliedi, confState)
+ plog.Infof("requested snapshot created at %d", snapi)
+ case err := <-s.errorc:
+ plog.Errorf("%s", err)
+ plog.Infof("the data-dir used by this member must be removed.")
+ return
+ case <-s.stop:
+ return
+ }
+ }
+}
+
+// Stop stops the server gracefully, and shuts down the running goroutine.
+// Stop should be called after a Start(s), otherwise it will block forever.
+func (s *EtcdServer) Stop() {
+ select {
+ case s.stop <- struct{}{}:
+ case <-s.done:
+ return
+ }
+ <-s.done
+}
+
+func (s *EtcdServer) stopWithDelay(d time.Duration, err error) {
+ time.Sleep(d)
+ select {
+ case s.errorc <- err:
+ default:
+ }
+}
+
+// StopNotify returns a channel that receives a empty struct
+// when the server is stopped.
+func (s *EtcdServer) StopNotify() <-chan struct{} { return s.done }
+
+// Do interprets r and performs an operation on s.store according to r.Method
+// and other fields. If r.Method is "POST", "PUT", "DELETE", or a "GET" with
+// Quorum == true, r will be sent through consensus before performing its
+// respective operation. Do will block until an action is performed or there is
+// an error.
+func (s *EtcdServer) Do(ctx context.Context, r pb.Request) (Response, error) {
+ r.ID = s.reqIDGen.Next()
+ if r.Method == "GET" && r.Quorum {
+ r.Method = "QGET"
+ }
+ switch r.Method {
+ case "POST", "PUT", "DELETE", "QGET":
+ data, err := r.Marshal()
+ if err != nil {
+ return Response{}, err
+ }
+ ch := s.w.Register(r.ID)
+
+ // TODO: benchmark the cost of time.Now()
+ // might be sampling?
+ start := time.Now()
+ s.r.Propose(ctx, data)
+
+ proposePending.Inc()
+ defer proposePending.Dec()
+
+ select {
+ case x := <-ch:
+ proposeDurations.Observe(float64(time.Since(start).Nanoseconds() / int64(time.Millisecond)))
+ resp := x.(Response)
+ return resp, resp.err
+ case <-ctx.Done():
+ proposeFailed.Inc()
+ s.w.Trigger(r.ID, nil) // GC wait
+ return Response{}, s.parseProposeCtxErr(ctx.Err(), start)
+ case <-s.done:
+ return Response{}, ErrStopped
+ }
+ case "GET":
+ switch {
+ case r.Wait:
+ wc, err := s.store.Watch(r.Path, r.Recursive, r.Stream, r.Since)
+ if err != nil {
+ return Response{}, err
+ }
+ return Response{Watcher: wc}, nil
+ default:
+ ev, err := s.store.Get(r.Path, r.Recursive, r.Sorted)
+ if err != nil {
+ return Response{}, err
+ }
+ return Response{Event: ev}, nil
+ }
+ case "HEAD":
+ ev, err := s.store.Get(r.Path, r.Recursive, r.Sorted)
+ if err != nil {
+ return Response{}, err
+ }
+ return Response{Event: ev}, nil
+ default:
+ return Response{}, ErrUnknownMethod
+ }
+}
+
+func (s *EtcdServer) SelfStats() []byte { return s.stats.JSON() }
+
+func (s *EtcdServer) LeaderStats() []byte {
+ lead := atomic.LoadUint64(&s.r.lead)
+ if lead != uint64(s.id) {
+ return nil
+ }
+ return s.lstats.JSON()
+}
+
+func (s *EtcdServer) StoreStats() []byte { return s.store.JsonStats() }
+
+func (s *EtcdServer) AddMember(ctx context.Context, memb Member) error {
+ if s.cfg.StrictReconfigCheck && !s.cluster.isReadyToAddNewMember() {
+ // If s.cfg.StrictReconfigCheck is false, it means the option -strict-reconfig-check isn't passed to etcd.
+ // In such a case adding a new member is allowed unconditionally
+ return ErrNotEnoughStartedMembers
+ }
+
+ // TODO: move Member to protobuf type
+ b, err := json.Marshal(memb)
+ if err != nil {
+ return err
+ }
+ cc := raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: uint64(memb.ID),
+ Context: b,
+ }
+ return s.configure(ctx, cc)
+}
+
+func (s *EtcdServer) RemoveMember(ctx context.Context, id uint64) error {
+ if s.cfg.StrictReconfigCheck && !s.cluster.isReadyToRemoveMember(id) {
+ // If s.cfg.StrictReconfigCheck is false, it means the option -strict-reconfig-check isn't passed to etcd.
+ // In such a case removing a member is allowed unconditionally
+ return ErrNotEnoughStartedMembers
+ }
+
+ cc := raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: id,
+ }
+ return s.configure(ctx, cc)
+}
+
+func (s *EtcdServer) UpdateMember(ctx context.Context, memb Member) error {
+ b, err := json.Marshal(memb)
+ if err != nil {
+ return err
+ }
+ cc := raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode,
+ NodeID: uint64(memb.ID),
+ Context: b,
+ }
+ return s.configure(ctx, cc)
+}
+
+// Implement the RaftTimer interface
+func (s *EtcdServer) Index() uint64 { return atomic.LoadUint64(&s.r.index) }
+
+func (s *EtcdServer) Term() uint64 { return atomic.LoadUint64(&s.r.term) }
+
+// Only for testing purpose
+// TODO: add Raft server interface to expose raft related info:
+// Index, Term, Lead, Committed, Applied, LastIndex, etc.
+func (s *EtcdServer) Lead() uint64 { return atomic.LoadUint64(&s.r.lead) }
+
+func (s *EtcdServer) Leader() types.ID { return types.ID(s.Lead()) }
+
+// configure sends a configuration change through consensus and
+// then waits for it to be applied to the server. It
+// will block until the change is performed or there is an error.
+func (s *EtcdServer) configure(ctx context.Context, cc raftpb.ConfChange) error {
+ cc.ID = s.reqIDGen.Next()
+ ch := s.w.Register(cc.ID)
+ start := time.Now()
+ if err := s.r.ProposeConfChange(ctx, cc); err != nil {
+ s.w.Trigger(cc.ID, nil)
+ return err
+ }
+ select {
+ case x := <-ch:
+ if err, ok := x.(error); ok {
+ return err
+ }
+ if x != nil {
+ plog.Panicf("return type should always be error")
+ }
+ return nil
+ case <-ctx.Done():
+ s.w.Trigger(cc.ID, nil) // GC wait
+ return s.parseProposeCtxErr(ctx.Err(), start)
+ case <-s.done:
+ return ErrStopped
+ }
+}
+
+// sync proposes a SYNC request and is non-blocking.
+// This makes no guarantee that the request will be proposed or performed.
+// The request will be cancelled after the given timeout.
+func (s *EtcdServer) sync(timeout time.Duration) {
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ req := pb.Request{
+ Method: "SYNC",
+ ID: s.reqIDGen.Next(),
+ Time: time.Now().UnixNano(),
+ }
+ data := pbutil.MustMarshal(&req)
+ // There is no promise that node has leader when do SYNC request,
+ // so it uses goroutine to propose.
+ go func() {
+ s.r.Propose(ctx, data)
+ cancel()
+ }()
+}
+
+// publish registers server information into the cluster. The information
+// is the JSON representation of this server's member struct, updated with the
+// static clientURLs of the server.
+// The function keeps attempting to register until it succeeds,
+// or its server is stopped.
+func (s *EtcdServer) publish(timeout time.Duration) {
+ b, err := json.Marshal(s.attributes)
+ if err != nil {
+ plog.Panicf("json marshal error: %v", err)
+ return
+ }
+ req := pb.Request{
+ Method: "PUT",
+ Path: MemberAttributesStorePath(s.id),
+ Val: string(b),
+ }
+
+ for {
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ _, err := s.Do(ctx, req)
+ cancel()
+ switch err {
+ case nil:
+ plog.Infof("published %+v to cluster %s", s.attributes, s.cluster.ID())
+ return
+ case ErrStopped:
+ plog.Infof("aborting publish because server is stopped")
+ return
+ default:
+ plog.Errorf("publish error: %v", err)
+ }
+ }
+}
+
+func (s *EtcdServer) send(ms []raftpb.Message) {
+ for i := range ms {
+ if s.cluster.IsIDRemoved(types.ID(ms[i].To)) {
+ ms[i].To = 0
+ }
+ }
+ s.r.transport.Send(ms)
+}
+
+// apply takes entries received from Raft (after it has been committed) and
+// applies them to the current state of the EtcdServer.
+// The given entries should not be empty.
+func (s *EtcdServer) apply(es []raftpb.Entry, confState *raftpb.ConfState) (uint64, bool) {
+ var applied uint64
+ var shouldstop bool
+ var err error
+ for i := range es {
+ e := es[i]
+ switch e.Type {
+ case raftpb.EntryNormal:
+ // raft state machine may generate noop entry when leader confirmation.
+ // skip it in advance to avoid some potential bug in the future
+ if len(e.Data) == 0 {
+ select {
+ case s.forceVersionC <- struct{}{}:
+ default:
+ }
+ break
+ }
+
+ var raftReq pb.InternalRaftRequest
+ if !pbutil.MaybeUnmarshal(&raftReq, e.Data) { // backward compatible
+ var r pb.Request
+ pbutil.MustUnmarshal(&r, e.Data)
+ s.w.Trigger(r.ID, s.applyRequest(r))
+ } else {
+ switch {
+ case raftReq.V2 != nil:
+ req := raftReq.V2
+ s.w.Trigger(req.ID, s.applyRequest(*req))
+ default:
+ s.w.Trigger(raftReq.ID, s.applyV3Request(&raftReq))
+ }
+ }
+ case raftpb.EntryConfChange:
+ var cc raftpb.ConfChange
+ pbutil.MustUnmarshal(&cc, e.Data)
+ shouldstop, err = s.applyConfChange(cc, confState)
+ s.w.Trigger(cc.ID, err)
+ default:
+ plog.Panicf("entry type should be either EntryNormal or EntryConfChange")
+ }
+ atomic.StoreUint64(&s.r.index, e.Index)
+ atomic.StoreUint64(&s.r.term, e.Term)
+ applied = e.Index
+ }
+ return applied, shouldstop
+}
+
+// applyRequest interprets r as a call to store.X and returns a Response interpreted
+// from store.Event
+func (s *EtcdServer) applyRequest(r pb.Request) Response {
+ f := func(ev *store.Event, err error) Response {
+ return Response{Event: ev, err: err}
+ }
+ expr := timeutil.UnixNanoToTime(r.Expiration)
+ switch r.Method {
+ case "POST":
+ return f(s.store.Create(r.Path, r.Dir, r.Val, true, expr))
+ case "PUT":
+ exists, existsSet := pbutil.GetBool(r.PrevExist)
+ switch {
+ case existsSet:
+ if exists {
+ if r.PrevIndex == 0 && r.PrevValue == "" {
+ return f(s.store.Update(r.Path, r.Val, expr))
+ } else {
+ return f(s.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, expr))
+ }
+ }
+ return f(s.store.Create(r.Path, r.Dir, r.Val, false, expr))
+ case r.PrevIndex > 0 || r.PrevValue != "":
+ return f(s.store.CompareAndSwap(r.Path, r.PrevValue, r.PrevIndex, r.Val, expr))
+ default:
+ // TODO (yicheng): cluster should be the owner of cluster prefix store
+ // we should not modify cluster store here.
+ if storeMemberAttributeRegexp.MatchString(r.Path) {
+ id := mustParseMemberIDFromKey(path.Dir(r.Path))
+ var attr Attributes
+ if err := json.Unmarshal([]byte(r.Val), &attr); err != nil {
+ plog.Panicf("unmarshal %s should never fail: %v", r.Val, err)
+ }
+ ok := s.cluster.UpdateAttributes(id, attr)
+ if !ok {
+ return Response{}
+ }
+ }
+ if r.Path == path.Join(StoreClusterPrefix, "version") {
+ s.cluster.SetVersion(semver.Must(semver.NewVersion(r.Val)))
+ }
+ return f(s.store.Set(r.Path, r.Dir, r.Val, expr))
+ }
+ case "DELETE":
+ switch {
+ case r.PrevIndex > 0 || r.PrevValue != "":
+ return f(s.store.CompareAndDelete(r.Path, r.PrevValue, r.PrevIndex))
+ default:
+ return f(s.store.Delete(r.Path, r.Dir, r.Recursive))
+ }
+ case "QGET":
+ return f(s.store.Get(r.Path, r.Recursive, r.Sorted))
+ case "SYNC":
+ s.store.DeleteExpiredKeys(time.Unix(0, r.Time))
+ return Response{}
+ default:
+ // This should never be reached, but just in case:
+ return Response{err: ErrUnknownMethod}
+ }
+}
+
+// applyConfChange applies a ConfChange to the server. It is only
+// invoked with a ConfChange that has already passed through Raft
+func (s *EtcdServer) applyConfChange(cc raftpb.ConfChange, confState *raftpb.ConfState) (bool, error) {
+ if err := s.cluster.ValidateConfigurationChange(cc); err != nil {
+ cc.NodeID = raft.None
+ s.r.ApplyConfChange(cc)
+ return false, err
+ }
+ *confState = *s.r.ApplyConfChange(cc)
+ switch cc.Type {
+ case raftpb.ConfChangeAddNode:
+ m := new(Member)
+ if err := json.Unmarshal(cc.Context, m); err != nil {
+ plog.Panicf("unmarshal member should never fail: %v", err)
+ }
+ if cc.NodeID != uint64(m.ID) {
+ plog.Panicf("nodeID should always be equal to member ID")
+ }
+ s.cluster.AddMember(m)
+ if m.ID == s.id {
+ plog.Noticef("added local member %s %v to cluster %s", m.ID, m.PeerURLs, s.cluster.ID())
+ } else {
+ s.r.transport.AddPeer(m.ID, m.PeerURLs)
+ plog.Noticef("added member %s %v to cluster %s", m.ID, m.PeerURLs, s.cluster.ID())
+ }
+ case raftpb.ConfChangeRemoveNode:
+ id := types.ID(cc.NodeID)
+ s.cluster.RemoveMember(id)
+ if id == s.id {
+ return true, nil
+ } else {
+ s.r.transport.RemovePeer(id)
+ plog.Noticef("removed member %s from cluster %s", id, s.cluster.ID())
+ }
+ case raftpb.ConfChangeUpdateNode:
+ m := new(Member)
+ if err := json.Unmarshal(cc.Context, m); err != nil {
+ plog.Panicf("unmarshal member should never fail: %v", err)
+ }
+ if cc.NodeID != uint64(m.ID) {
+ plog.Panicf("nodeID should always be equal to member ID")
+ }
+ s.cluster.UpdateRaftAttributes(m.ID, m.RaftAttributes)
+ if m.ID == s.id {
+ plog.Noticef("update local member %s %v in cluster %s", m.ID, m.PeerURLs, s.cluster.ID())
+ } else {
+ s.r.transport.UpdatePeer(m.ID, m.PeerURLs)
+ plog.Noticef("update member %s %v in cluster %s", m.ID, m.PeerURLs, s.cluster.ID())
+ }
+ }
+ return false, nil
+}
+
+// createRaftSnapshot creates a raft snapshot that includes the state of store for v2 api.
+func (s *EtcdServer) createRaftSnapshot(snapi uint64, confState raftpb.ConfState) raftpb.Snapshot {
+ snapt, err := s.r.raftStorage.Term(snapi)
+ if err != nil {
+ log.Panicf("get term should never fail: %v", err)
+ }
+
+ clone := s.store.Clone()
+ d, err := clone.SaveNoCopy()
+ if err != nil {
+ plog.Panicf("store save should never fail: %v", err)
+ }
+
+ return raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{
+ Index: snapi,
+ Term: snapt,
+ ConfState: confState,
+ },
+ Data: d,
+ }
+}
+
+// TODO: non-blocking snapshot
+func (s *EtcdServer) snapshot(snapi uint64, confState raftpb.ConfState) {
+ clone := s.store.Clone()
+
+ go func() {
+ d, err := clone.SaveNoCopy()
+ // TODO: current store will never fail to do a snapshot
+ // what should we do if the store might fail?
+ if err != nil {
+ plog.Panicf("store save should never fail: %v", err)
+ }
+ snap, err := s.r.raftStorage.CreateSnapshot(snapi, &confState, d)
+ if err != nil {
+ // the snapshot was done asynchronously with the progress of raft.
+ // raft might have already got a newer snapshot.
+ if err == raft.ErrSnapOutOfDate {
+ return
+ }
+ plog.Panicf("unexpected create snapshot error %v", err)
+ }
+ if err := s.r.storage.SaveSnap(snap); err != nil {
+ plog.Fatalf("save snapshot error: %v", err)
+ }
+ plog.Infof("saved snapshot at index %d", snap.Metadata.Index)
+
+ // keep some in memory log entries for slow followers.
+ compacti := uint64(1)
+ if snapi > numberOfCatchUpEntries {
+ compacti = snapi - numberOfCatchUpEntries
+ }
+ err = s.r.raftStorage.Compact(compacti)
+ if err != nil {
+ // the compaction was done asynchronously with the progress of raft.
+ // raft log might already been compact.
+ if err == raft.ErrCompacted {
+ return
+ }
+ plog.Panicf("unexpected compaction error %v", err)
+ }
+ plog.Infof("compacted raft log at %d", compacti)
+ if s.cfg.V3demo && s.r.raftStorage.snapStore.closeSnapBefore(compacti) {
+ plog.Infof("closed snapshot stored due to compaction at %d", compacti)
+ }
+ }()
+}
+
+func (s *EtcdServer) PauseSending() { s.r.pauseSending() }
+
+func (s *EtcdServer) ResumeSending() { s.r.resumeSending() }
+
+func (s *EtcdServer) ClusterVersion() *semver.Version {
+ if s.cluster == nil {
+ return nil
+ }
+ return s.cluster.Version()
+}
+
+// monitorVersions checks the member's version every monitorVersion interval.
+// It updates the cluster version if all members agrees on a higher one.
+// It prints out log if there is a member with a higher version than the
+// local version.
+func (s *EtcdServer) monitorVersions() {
+ for {
+ select {
+ case <-s.forceVersionC:
+ case <-time.After(monitorVersionInterval):
+ case <-s.done:
+ return
+ }
+
+ if s.Leader() != s.ID() {
+ continue
+ }
+
+ v := decideClusterVersion(getVersions(s.cluster, s.id, s.versionTr))
+ if v != nil {
+ // only keep major.minor version for comparasion
+ v = &semver.Version{
+ Major: v.Major,
+ Minor: v.Minor,
+ }
+ }
+
+ // if the current version is nil:
+ // 1. use the decided version if possible
+ // 2. or use the min cluster version
+ if s.cluster.Version() == nil {
+ if v != nil {
+ go s.updateClusterVersion(v.String())
+ } else {
+ go s.updateClusterVersion(version.MinClusterVersion)
+ }
+ continue
+ }
+
+ // update cluster version only if the decided version is greater than
+ // the current cluster version
+ if v != nil && s.cluster.Version().LessThan(*v) {
+ go s.updateClusterVersion(v.String())
+ }
+ }
+}
+
+func (s *EtcdServer) updateClusterVersion(ver string) {
+ if s.cluster.Version() == nil {
+ plog.Infof("setting up the initial cluster version to %s", version.Cluster(ver))
+ } else {
+ plog.Infof("updating the cluster version from %s to %s", version.Cluster(s.cluster.Version().String()), version.Cluster(ver))
+ }
+ req := pb.Request{
+ Method: "PUT",
+ Path: path.Join(StoreClusterPrefix, "version"),
+ Val: ver,
+ }
+ ctx, cancel := context.WithTimeout(context.Background(), s.cfg.ReqTimeout())
+ _, err := s.Do(ctx, req)
+ cancel()
+ switch err {
+ case nil:
+ return
+ case ErrStopped:
+ plog.Infof("aborting update cluster version because server is stopped")
+ return
+ default:
+ plog.Errorf("error updating cluster version (%v)", err)
+ }
+}
+
+func (s *EtcdServer) parseProposeCtxErr(err error, start time.Time) error {
+ switch err {
+ case context.Canceled:
+ return ErrCanceled
+ case context.DeadlineExceeded:
+ curLeadElected := s.r.leadElectedTime()
+ prevLeadLost := curLeadElected.Add(-2 * time.Duration(s.cfg.ElectionTicks) * time.Duration(s.cfg.TickMs) * time.Millisecond)
+ if start.After(prevLeadLost) && start.Before(curLeadElected) {
+ return ErrTimeoutDueToLeaderFail
+ }
+
+ lead := types.ID(atomic.LoadUint64(&s.r.lead))
+ switch lead {
+ case types.ID(raft.None):
+ // TODO: return error to specify it happens because the cluster does not have leader now
+ case s.ID():
+ if !isConnectedToQuorumSince(s.r.transport, start, s.ID(), s.cluster.Members()) {
+ return ErrTimeoutDueToConnectionLost
+ }
+ default:
+ if !isConnectedSince(s.r.transport, start, lead) {
+ return ErrTimeoutDueToConnectionLost
+ }
+ }
+
+ return ErrTimeout
+ default:
+ return err
+ }
+}
+
+// isConnectedToQuorumSince checks whether the local member is connected to the
+// quorum of the cluster since the given time.
+func isConnectedToQuorumSince(transport rafthttp.Transporter, since time.Time, self types.ID, members []*Member) bool {
+ var connectedNum int
+ for _, m := range members {
+ if m.ID == self || isConnectedSince(transport, since, m.ID) {
+ connectedNum++
+ }
+ }
+ return connectedNum >= (len(members)+1)/2
+}
+
+// isConnectedSince checks whether the local member is connected to the
+// remote member since the given time.
+func isConnectedSince(transport rafthttp.Transporter, since time.Time, remote types.ID) bool {
+ t := transport.ActiveSince(remote)
+ return !t.IsZero() && t.Before(since)
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/server_test.go b/vendor/github.com/coreos/etcd/etcdserver/server_test.go
new file mode 100644
index 000000000..fc20f25ce
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/server_test.go
@@ -0,0 +1,1485 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "path"
+ "reflect"
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/idutil"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/store"
+)
+
+// TestDoLocalAction tests requests which do not need to go through raft to be applied,
+// and are served through local data.
+func TestDoLocalAction(t *testing.T) {
+ tests := []struct {
+ req pb.Request
+
+ wresp Response
+ werr error
+ wactions []testutil.Action
+ }{
+ {
+ pb.Request{Method: "GET", ID: 1, Wait: true},
+ Response{Watcher: &nopWatcher{}}, nil, []testutil.Action{{Name: "Watch"}},
+ },
+ {
+ pb.Request{Method: "GET", ID: 1},
+ Response{Event: &store.Event{}}, nil,
+ []testutil.Action{
+ {
+ Name: "Get",
+ Params: []interface{}{"", false, false},
+ },
+ },
+ },
+ {
+ pb.Request{Method: "HEAD", ID: 1},
+ Response{Event: &store.Event{}}, nil,
+ []testutil.Action{
+ {
+ Name: "Get",
+ Params: []interface{}{"", false, false},
+ },
+ },
+ },
+ {
+ pb.Request{Method: "BADMETHOD", ID: 1},
+ Response{}, ErrUnknownMethod, []testutil.Action{},
+ },
+ }
+ for i, tt := range tests {
+ st := &storeRecorder{}
+ srv := &EtcdServer{
+ store: st,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ resp, err := srv.Do(context.TODO(), tt.req)
+
+ if err != tt.werr {
+ t.Fatalf("#%d: err = %+v, want %+v", i, err, tt.werr)
+ }
+ if !reflect.DeepEqual(resp, tt.wresp) {
+ t.Errorf("#%d: resp = %+v, want %+v", i, resp, tt.wresp)
+ }
+ gaction := st.Action()
+ if !reflect.DeepEqual(gaction, tt.wactions) {
+ t.Errorf("#%d: action = %+v, want %+v", i, gaction, tt.wactions)
+ }
+ }
+}
+
+// TestDoBadLocalAction tests server requests which do not need to go through consensus,
+// and return errors when they fetch from local data.
+func TestDoBadLocalAction(t *testing.T) {
+ storeErr := fmt.Errorf("bah")
+ tests := []struct {
+ req pb.Request
+
+ wactions []testutil.Action
+ }{
+ {
+ pb.Request{Method: "GET", ID: 1, Wait: true},
+ []testutil.Action{{Name: "Watch"}},
+ },
+ {
+ pb.Request{Method: "GET", ID: 1},
+ []testutil.Action{
+ {
+ Name: "Get",
+ Params: []interface{}{"", false, false},
+ },
+ },
+ },
+ {
+ pb.Request{Method: "HEAD", ID: 1},
+ []testutil.Action{
+ {
+ Name: "Get",
+ Params: []interface{}{"", false, false},
+ },
+ },
+ },
+ }
+ for i, tt := range tests {
+ st := &errStoreRecorder{err: storeErr}
+ srv := &EtcdServer{
+ store: st,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ resp, err := srv.Do(context.Background(), tt.req)
+
+ if err != storeErr {
+ t.Fatalf("#%d: err = %+v, want %+v", i, err, storeErr)
+ }
+ if !reflect.DeepEqual(resp, Response{}) {
+ t.Errorf("#%d: resp = %+v, want %+v", i, resp, Response{})
+ }
+ gaction := st.Action()
+ if !reflect.DeepEqual(gaction, tt.wactions) {
+ t.Errorf("#%d: action = %+v, want %+v", i, gaction, tt.wactions)
+ }
+ }
+}
+
+func TestApplyRequest(t *testing.T) {
+ tests := []struct {
+ req pb.Request
+
+ wresp Response
+ wactions []testutil.Action
+ }{
+ // POST ==> Create
+ {
+ pb.Request{Method: "POST", ID: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Create",
+ Params: []interface{}{"", false, "", true, time.Time{}},
+ },
+ },
+ },
+ // POST ==> Create, with expiration
+ {
+ pb.Request{Method: "POST", ID: 1, Expiration: 1337},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Create",
+ Params: []interface{}{"", false, "", true, time.Unix(0, 1337)},
+ },
+ },
+ },
+ // POST ==> Create, with dir
+ {
+ pb.Request{Method: "POST", ID: 1, Dir: true},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Create",
+ Params: []interface{}{"", true, "", true, time.Time{}},
+ },
+ },
+ },
+ // PUT ==> Set
+ {
+ pb.Request{Method: "PUT", ID: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Set",
+ Params: []interface{}{"", false, "", time.Time{}},
+ },
+ },
+ },
+ // PUT ==> Set, with dir
+ {
+ pb.Request{Method: "PUT", ID: 1, Dir: true},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Set",
+ Params: []interface{}{"", true, "", time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevExist=true ==> Update
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevExist: pbutil.Boolp(true)},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Update",
+ Params: []interface{}{"", "", time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevExist=false ==> Create
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevExist: pbutil.Boolp(false)},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Create",
+ Params: []interface{}{"", false, "", false, time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevExist=true *and* PrevIndex set ==> CompareAndSwap
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevExist: pbutil.Boolp(true), PrevIndex: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndSwap",
+ Params: []interface{}{"", "", uint64(1), "", time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevExist=false *and* PrevIndex set ==> Create
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevExist: pbutil.Boolp(false), PrevIndex: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Create",
+ Params: []interface{}{"", false, "", false, time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevIndex set ==> CompareAndSwap
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevIndex: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndSwap",
+ Params: []interface{}{"", "", uint64(1), "", time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevValue set ==> CompareAndSwap
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevValue: "bar"},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndSwap",
+ Params: []interface{}{"", "bar", uint64(0), "", time.Time{}},
+ },
+ },
+ },
+ // PUT with PrevIndex and PrevValue set ==> CompareAndSwap
+ {
+ pb.Request{Method: "PUT", ID: 1, PrevIndex: 1, PrevValue: "bar"},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndSwap",
+ Params: []interface{}{"", "bar", uint64(1), "", time.Time{}},
+ },
+ },
+ },
+ // DELETE ==> Delete
+ {
+ pb.Request{Method: "DELETE", ID: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Delete",
+ Params: []interface{}{"", false, false},
+ },
+ },
+ },
+ // DELETE with PrevIndex set ==> CompareAndDelete
+ {
+ pb.Request{Method: "DELETE", ID: 1, PrevIndex: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndDelete",
+ Params: []interface{}{"", "", uint64(1)},
+ },
+ },
+ },
+ // DELETE with PrevValue set ==> CompareAndDelete
+ {
+ pb.Request{Method: "DELETE", ID: 1, PrevValue: "bar"},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndDelete",
+ Params: []interface{}{"", "bar", uint64(0)},
+ },
+ },
+ },
+ // DELETE with PrevIndex *and* PrevValue set ==> CompareAndDelete
+ {
+ pb.Request{Method: "DELETE", ID: 1, PrevIndex: 5, PrevValue: "bar"},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "CompareAndDelete",
+ Params: []interface{}{"", "bar", uint64(5)},
+ },
+ },
+ },
+ // QGET ==> Get
+ {
+ pb.Request{Method: "QGET", ID: 1},
+ Response{Event: &store.Event{}},
+ []testutil.Action{
+ {
+ Name: "Get",
+ Params: []interface{}{"", false, false},
+ },
+ },
+ },
+ // SYNC ==> DeleteExpiredKeys
+ {
+ pb.Request{Method: "SYNC", ID: 1},
+ Response{},
+ []testutil.Action{
+ {
+ Name: "DeleteExpiredKeys",
+ Params: []interface{}{time.Unix(0, 0)},
+ },
+ },
+ },
+ {
+ pb.Request{Method: "SYNC", ID: 1, Time: 12345},
+ Response{},
+ []testutil.Action{
+ {
+ Name: "DeleteExpiredKeys",
+ Params: []interface{}{time.Unix(0, 12345)},
+ },
+ },
+ },
+ // Unknown method - error
+ {
+ pb.Request{Method: "BADMETHOD", ID: 1},
+ Response{err: ErrUnknownMethod},
+ []testutil.Action{},
+ },
+ }
+
+ for i, tt := range tests {
+ st := &storeRecorder{}
+ srv := &EtcdServer{store: st}
+ resp := srv.applyRequest(tt.req)
+
+ if !reflect.DeepEqual(resp, tt.wresp) {
+ t.Errorf("#%d: resp = %+v, want %+v", i, resp, tt.wresp)
+ }
+ gaction := st.Action()
+ if !reflect.DeepEqual(gaction, tt.wactions) {
+ t.Errorf("#%d: action = %#v, want %#v", i, gaction, tt.wactions)
+ }
+ }
+}
+
+func TestApplyRequestOnAdminMemberAttributes(t *testing.T) {
+ cl := newTestCluster([]*Member{{ID: 1}})
+ srv := &EtcdServer{
+ store: &storeRecorder{},
+ cluster: cl,
+ }
+ req := pb.Request{
+ Method: "PUT",
+ ID: 1,
+ Path: path.Join(storeMembersPrefix, strconv.FormatUint(1, 16), attributesSuffix),
+ Val: `{"Name":"abc","ClientURLs":["http://127.0.0.1:2379"]}`,
+ }
+ srv.applyRequest(req)
+ w := Attributes{Name: "abc", ClientURLs: []string{"http://127.0.0.1:2379"}}
+ if g := cl.Member(1).Attributes; !reflect.DeepEqual(g, w) {
+ t.Errorf("attributes = %v, want %v", g, w)
+ }
+}
+
+func TestApplyConfChangeError(t *testing.T) {
+ cl := newCluster("")
+ cl.SetStore(store.New())
+ for i := 1; i <= 4; i++ {
+ cl.AddMember(&Member{ID: types.ID(i)})
+ }
+ cl.RemoveMember(4)
+
+ tests := []struct {
+ cc raftpb.ConfChange
+ werr error
+ }{
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: 4,
+ },
+ ErrIDRemoved,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeUpdateNode,
+ NodeID: 4,
+ },
+ ErrIDRemoved,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeAddNode,
+ NodeID: 1,
+ },
+ ErrIDExists,
+ },
+ {
+ raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: 5,
+ },
+ ErrIDNotFound,
+ },
+ }
+ for i, tt := range tests {
+ n := &nodeRecorder{}
+ srv := &EtcdServer{
+ r: raftNode{Node: n},
+ cluster: cl,
+ cfg: &ServerConfig{},
+ }
+ _, err := srv.applyConfChange(tt.cc, nil)
+ if err != tt.werr {
+ t.Errorf("#%d: applyConfChange error = %v, want %v", i, err, tt.werr)
+ }
+ cc := raftpb.ConfChange{Type: tt.cc.Type, NodeID: raft.None}
+ w := []testutil.Action{
+ {
+ Name: "ApplyConfChange",
+ Params: []interface{}{cc},
+ },
+ }
+ if g := n.Action(); !reflect.DeepEqual(g, w) {
+ t.Errorf("#%d: action = %+v, want %+v", i, g, w)
+ }
+ }
+}
+
+func TestApplyConfChangeShouldStop(t *testing.T) {
+ cl := newCluster("")
+ cl.SetStore(store.New())
+ for i := 1; i <= 3; i++ {
+ cl.AddMember(&Member{ID: types.ID(i)})
+ }
+ srv := &EtcdServer{
+ id: 1,
+ r: raftNode{
+ Node: &nodeRecorder{},
+ transport: &nopTransporter{},
+ },
+ cluster: cl,
+ }
+ cc := raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: 2,
+ }
+ // remove non-local member
+ shouldStop, err := srv.applyConfChange(cc, &raftpb.ConfState{})
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ if shouldStop != false {
+ t.Errorf("shouldStop = %t, want %t", shouldStop, false)
+ }
+
+ // remove local member
+ cc.NodeID = 1
+ shouldStop, err = srv.applyConfChange(cc, &raftpb.ConfState{})
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ if shouldStop != true {
+ t.Errorf("shouldStop = %t, want %t", shouldStop, true)
+ }
+}
+
+func TestDoProposal(t *testing.T) {
+ tests := []pb.Request{
+ {Method: "POST", ID: 1},
+ {Method: "PUT", ID: 1},
+ {Method: "DELETE", ID: 1},
+ {Method: "GET", ID: 1, Quorum: true},
+ }
+ for i, tt := range tests {
+ st := &storeRecorder{}
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{
+ Node: newNodeCommitter(),
+ storage: &storageRecorder{},
+ raftStorage: newRaftStorage(),
+ transport: &nopTransporter{},
+ },
+ store: st,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ srv.start()
+ resp, err := srv.Do(context.Background(), tt)
+ srv.Stop()
+
+ action := st.Action()
+ if len(action) != 1 {
+ t.Errorf("#%d: len(action) = %d, want 1", i, len(action))
+ }
+ if err != nil {
+ t.Fatalf("#%d: err = %v, want nil", i, err)
+ }
+ wresp := Response{Event: &store.Event{}}
+ if !reflect.DeepEqual(resp, wresp) {
+ t.Errorf("#%d: resp = %v, want %v", i, resp, wresp)
+ }
+ }
+}
+
+func TestDoProposalCancelled(t *testing.T) {
+ wait := &waitRecorder{}
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{Node: &nodeRecorder{}},
+ w: wait,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+ _, err := srv.Do(ctx, pb.Request{Method: "PUT"})
+
+ if err != ErrCanceled {
+ t.Fatalf("err = %v, want %v", err, ErrCanceled)
+ }
+ w := []testutil.Action{{Name: "Register"}, {Name: "Trigger"}}
+ if !reflect.DeepEqual(wait.action, w) {
+ t.Errorf("wait.action = %+v, want %+v", wait.action, w)
+ }
+}
+
+func TestDoProposalTimeout(t *testing.T) {
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{Node: &nodeRecorder{}},
+ w: &waitRecorder{},
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ ctx, _ := context.WithTimeout(context.Background(), 0)
+ _, err := srv.Do(ctx, pb.Request{Method: "PUT"})
+ if err != ErrTimeout {
+ t.Fatalf("err = %v, want %v", err, ErrTimeout)
+ }
+}
+
+func TestDoProposalStopped(t *testing.T) {
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{Node: &nodeRecorder{}},
+ w: &waitRecorder{},
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ srv.done = make(chan struct{})
+ close(srv.done)
+ _, err := srv.Do(context.Background(), pb.Request{Method: "PUT", ID: 1})
+ if err != ErrStopped {
+ t.Errorf("err = %v, want %v", err, ErrStopped)
+ }
+}
+
+// TestSync tests sync 1. is nonblocking 2. proposes SYNC request.
+func TestSync(t *testing.T) {
+ n := &nodeRecorder{}
+ srv := &EtcdServer{
+ r: raftNode{Node: n},
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ // check that sync is non-blocking
+ done := make(chan struct{})
+ go func() {
+ srv.sync(10 * time.Second)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ case <-time.After(time.Second):
+ t.Fatal("sync should be non-blocking but did not return after 1s!")
+ }
+
+ testutil.WaitSchedule()
+
+ action := n.Action()
+ if len(action) != 1 {
+ t.Fatalf("len(action) = %d, want 1", len(action))
+ }
+ if action[0].Name != "Propose" {
+ t.Fatalf("action = %s, want Propose", action[0].Name)
+ }
+ data := action[0].Params[0].([]byte)
+ var r pb.Request
+ if err := r.Unmarshal(data); err != nil {
+ t.Fatalf("unmarshal request error: %v", err)
+ }
+ if r.Method != "SYNC" {
+ t.Errorf("method = %s, want SYNC", r.Method)
+ }
+}
+
+// TestSyncTimeout tests the case that sync 1. is non-blocking 2. cancel request
+// after timeout
+func TestSyncTimeout(t *testing.T) {
+ n := &nodeProposalBlockerRecorder{}
+ srv := &EtcdServer{
+ r: raftNode{Node: n},
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ // check that sync is non-blocking
+ done := make(chan struct{})
+ go func() {
+ srv.sync(0)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ case <-time.After(time.Second):
+ t.Fatal("sync should be non-blocking but did not return after 1s!")
+ }
+
+ // give time for goroutine in sync to cancel
+ testutil.WaitSchedule()
+ w := []testutil.Action{{Name: "Propose blocked"}}
+ if g := n.Action(); !reflect.DeepEqual(g, w) {
+ t.Errorf("action = %v, want %v", g, w)
+ }
+}
+
+// TODO: TestNoSyncWhenNoLeader
+
+// TestSyncTrigger tests that the server proposes a SYNC request when its sync timer ticks
+func TestSyncTrigger(t *testing.T) {
+ n := newReadyNode()
+ st := make(chan time.Time, 1)
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{
+ Node: n,
+ raftStorage: newRaftStorage(),
+ transport: &nopTransporter{},
+ storage: &storageRecorder{},
+ },
+ store: &storeRecorder{},
+ SyncTicker: st,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ srv.start()
+ defer srv.Stop()
+ // trigger the server to become a leader and accept sync requests
+ n.readyc <- raft.Ready{
+ SoftState: &raft.SoftState{
+ RaftState: raft.StateLeader,
+ },
+ }
+ // trigger a sync request
+ st <- time.Time{}
+ testutil.WaitSchedule()
+
+ action := n.Action()
+ if len(action) != 1 {
+ t.Fatalf("len(action) = %d, want 1", len(action))
+ }
+ if action[0].Name != "Propose" {
+ t.Fatalf("action = %s, want Propose", action[0].Name)
+ }
+ data := action[0].Params[0].([]byte)
+ var req pb.Request
+ if err := req.Unmarshal(data); err != nil {
+ t.Fatalf("error unmarshalling data: %v", err)
+ }
+ if req.Method != "SYNC" {
+ t.Fatalf("unexpected proposed request: %#v", req.Method)
+ }
+}
+
+func TestCreateRaftSnapshot(t *testing.T) {
+ s := newRaftStorage()
+ s.Append([]raftpb.Entry{{Index: 1, Term: 1}})
+ st := &storeRecorder{}
+ srv := &EtcdServer{
+ r: raftNode{
+ raftStorage: s,
+ },
+ store: st,
+ }
+
+ snap := srv.createRaftSnapshot(1, raftpb.ConfState{Nodes: []uint64{1}})
+ wdata, err := st.Save()
+ if err != nil {
+ t.Fatal(err)
+ }
+ wsnap := raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{
+ Index: 1,
+ Term: 1,
+ ConfState: raftpb.ConfState{Nodes: []uint64{1}},
+ },
+ Data: wdata,
+ }
+ if !reflect.DeepEqual(snap, wsnap) {
+ t.Errorf("snap = %+v, want %+v", snap, wsnap)
+ }
+
+ gaction := st.Action()
+ // the third action is store.Save used in testing
+ if len(gaction) != 3 {
+ t.Fatalf("len(action) = %d, want 3", len(gaction))
+ }
+ if !reflect.DeepEqual(gaction[0], testutil.Action{Name: "Clone"}) {
+ t.Errorf("action = %s, want Clone", gaction[0])
+ }
+ if !reflect.DeepEqual(gaction[1], testutil.Action{Name: "SaveNoCopy"}) {
+ t.Errorf("action = %s, want SaveNoCopy", gaction[1])
+ }
+
+}
+
+// snapshot should snapshot the store and cut the persistent
+func TestSnapshot(t *testing.T) {
+ s := newRaftStorage()
+ s.Append([]raftpb.Entry{{Index: 1}})
+ st := &storeRecorder{}
+ p := &storageRecorder{}
+ srv := &EtcdServer{
+ cfg: &ServerConfig{},
+ r: raftNode{
+ Node: &nodeRecorder{},
+ raftStorage: s,
+ storage: p,
+ },
+ store: st,
+ }
+ srv.snapshot(1, raftpb.ConfState{Nodes: []uint64{1}})
+ testutil.WaitSchedule()
+ gaction := st.Action()
+ if len(gaction) != 2 {
+ t.Fatalf("len(action) = %d, want 1", len(gaction))
+ }
+ if !reflect.DeepEqual(gaction[0], testutil.Action{Name: "Clone"}) {
+ t.Errorf("action = %s, want Clone", gaction[0])
+ }
+ if !reflect.DeepEqual(gaction[1], testutil.Action{Name: "SaveNoCopy"}) {
+ t.Errorf("action = %s, want SaveNoCopy", gaction[1])
+ }
+ gaction = p.Action()
+ if len(gaction) != 1 {
+ t.Fatalf("len(action) = %d, want 1", len(gaction))
+ }
+ if !reflect.DeepEqual(gaction[0], testutil.Action{Name: "SaveSnap"}) {
+ t.Errorf("action = %s, want SaveSnap", gaction[0])
+ }
+}
+
+// Applied > SnapCount should trigger a SaveSnap event
+func TestTriggerSnap(t *testing.T) {
+ snapc := 10
+ st := &storeRecorder{}
+ p := &storageRecorder{}
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ snapCount: uint64(snapc),
+ r: raftNode{
+ Node: newNodeCommitter(),
+ raftStorage: newRaftStorage(),
+ storage: p,
+ transport: &nopTransporter{},
+ },
+ store: st,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ srv.start()
+ for i := 0; i < snapc+1; i++ {
+ srv.Do(context.Background(), pb.Request{Method: "PUT"})
+ }
+ srv.Stop()
+ // wait for snapshot goroutine to finish
+ testutil.WaitSchedule()
+
+ gaction := p.Action()
+ // each operation is recorded as a Save
+ // (SnapCount+1) * Puts + SaveSnap = (SnapCount+1) * Save + SaveSnap
+ wcnt := 2 + snapc
+ if len(gaction) != wcnt {
+ t.Fatalf("len(action) = %d, want %d", len(gaction), wcnt)
+ }
+ if !reflect.DeepEqual(gaction[wcnt-1], testutil.Action{Name: "SaveSnap"}) {
+ t.Errorf("action = %s, want SaveSnap", gaction[wcnt-1])
+ }
+}
+
+// TestRecvSnapshot tests when it receives a snapshot from raft leader,
+// it should trigger storage.SaveSnap and also store.Recover.
+func TestRecvSnapshot(t *testing.T) {
+ n := newReadyNode()
+ st := &storeRecorder{}
+ p := &storageRecorder{}
+ cl := newCluster("abc")
+ cl.SetStore(store.New())
+ s := &EtcdServer{
+ r: raftNode{
+ Node: n,
+ transport: &nopTransporter{},
+ storage: p,
+ raftStorage: newRaftStorage(),
+ },
+ store: st,
+ cluster: cl,
+ }
+
+ s.start()
+ n.readyc <- raft.Ready{Snapshot: raftpb.Snapshot{Metadata: raftpb.SnapshotMetadata{Index: 1}}}
+ // make goroutines move forward to receive snapshot
+ testutil.WaitSchedule()
+ s.Stop()
+
+ wactions := []testutil.Action{{Name: "Recovery"}}
+ if g := st.Action(); !reflect.DeepEqual(g, wactions) {
+ t.Errorf("store action = %v, want %v", g, wactions)
+ }
+ wactions = []testutil.Action{{Name: "SaveSnap"}, {Name: "Save"}}
+ if g := p.Action(); !reflect.DeepEqual(g, wactions) {
+ t.Errorf("storage action = %v, want %v", g, wactions)
+ }
+}
+
+// TestApplySnapshotAndCommittedEntries tests that server applies snapshot
+// first and then committed entries.
+func TestApplySnapshotAndCommittedEntries(t *testing.T) {
+ n := newReadyNode()
+ st := &storeRecorder{}
+ cl := newCluster("abc")
+ cl.SetStore(store.New())
+ storage := newRaftStorage()
+ s := &EtcdServer{
+ r: raftNode{
+ Node: n,
+ storage: &storageRecorder{},
+ raftStorage: storage,
+ transport: &nopTransporter{},
+ },
+ store: st,
+ cluster: cl,
+ }
+
+ s.start()
+ req := &pb.Request{Method: "QGET"}
+ n.readyc <- raft.Ready{
+ Snapshot: raftpb.Snapshot{Metadata: raftpb.SnapshotMetadata{Index: 1}},
+ CommittedEntries: []raftpb.Entry{
+ {Index: 2, Data: pbutil.MustMarshal(req)},
+ },
+ }
+ // make goroutines move forward to receive snapshot
+ testutil.WaitSchedule()
+ s.Stop()
+
+ actions := st.Action()
+ if len(actions) != 2 {
+ t.Fatalf("len(action) = %d, want 2", len(actions))
+ }
+ if actions[0].Name != "Recovery" {
+ t.Errorf("actions[0] = %s, want %s", actions[0].Name, "Recovery")
+ }
+ if actions[1].Name != "Get" {
+ t.Errorf("actions[1] = %s, want %s", actions[1].Name, "Get")
+ }
+}
+
+// TestAddMember tests AddMember can propose and perform node addition.
+func TestAddMember(t *testing.T) {
+ n := newNodeConfChangeCommitterRecorder()
+ n.readyc <- raft.Ready{
+ SoftState: &raft.SoftState{RaftState: raft.StateLeader},
+ }
+ cl := newTestCluster(nil)
+ st := store.New()
+ cl.SetStore(st)
+ s := &EtcdServer{
+ r: raftNode{
+ Node: n,
+ raftStorage: newRaftStorage(),
+ storage: &storageRecorder{},
+ transport: &nopTransporter{},
+ },
+ cfg: &ServerConfig{},
+ store: st,
+ cluster: cl,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ s.start()
+ m := Member{ID: 1234, RaftAttributes: RaftAttributes{PeerURLs: []string{"foo"}}}
+ err := s.AddMember(context.TODO(), m)
+ gaction := n.Action()
+ s.Stop()
+
+ if err != nil {
+ t.Fatalf("AddMember error: %v", err)
+ }
+ wactions := []testutil.Action{{Name: "ProposeConfChange:ConfChangeAddNode"}, {Name: "ApplyConfChange:ConfChangeAddNode"}}
+ if !reflect.DeepEqual(gaction, wactions) {
+ t.Errorf("action = %v, want %v", gaction, wactions)
+ }
+ if cl.Member(1234) == nil {
+ t.Errorf("member with id 1234 is not added")
+ }
+}
+
+// TestRemoveMember tests RemoveMember can propose and perform node removal.
+func TestRemoveMember(t *testing.T) {
+ n := newNodeConfChangeCommitterRecorder()
+ n.readyc <- raft.Ready{
+ SoftState: &raft.SoftState{RaftState: raft.StateLeader},
+ }
+ cl := newTestCluster(nil)
+ st := store.New()
+ cl.SetStore(store.New())
+ cl.AddMember(&Member{ID: 1234})
+ s := &EtcdServer{
+ r: raftNode{
+ Node: n,
+ raftStorage: newRaftStorage(),
+ storage: &storageRecorder{},
+ transport: &nopTransporter{},
+ },
+ cfg: &ServerConfig{},
+ store: st,
+ cluster: cl,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ s.start()
+ err := s.RemoveMember(context.TODO(), 1234)
+ gaction := n.Action()
+ s.Stop()
+
+ if err != nil {
+ t.Fatalf("RemoveMember error: %v", err)
+ }
+ wactions := []testutil.Action{{Name: "ProposeConfChange:ConfChangeRemoveNode"}, {Name: "ApplyConfChange:ConfChangeRemoveNode"}}
+ if !reflect.DeepEqual(gaction, wactions) {
+ t.Errorf("action = %v, want %v", gaction, wactions)
+ }
+ if cl.Member(1234) != nil {
+ t.Errorf("member with id 1234 is not removed")
+ }
+}
+
+// TestUpdateMember tests RemoveMember can propose and perform node update.
+func TestUpdateMember(t *testing.T) {
+ n := newNodeConfChangeCommitterRecorder()
+ n.readyc <- raft.Ready{
+ SoftState: &raft.SoftState{RaftState: raft.StateLeader},
+ }
+ cl := newTestCluster(nil)
+ st := store.New()
+ cl.SetStore(st)
+ cl.AddMember(&Member{ID: 1234})
+ s := &EtcdServer{
+ r: raftNode{
+ Node: n,
+ raftStorage: newRaftStorage(),
+ storage: &storageRecorder{},
+ transport: &nopTransporter{},
+ },
+ store: st,
+ cluster: cl,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ s.start()
+ wm := Member{ID: 1234, RaftAttributes: RaftAttributes{PeerURLs: []string{"http://127.0.0.1:1"}}}
+ err := s.UpdateMember(context.TODO(), wm)
+ gaction := n.Action()
+ s.Stop()
+
+ if err != nil {
+ t.Fatalf("UpdateMember error: %v", err)
+ }
+ wactions := []testutil.Action{{Name: "ProposeConfChange:ConfChangeUpdateNode"}, {Name: "ApplyConfChange:ConfChangeUpdateNode"}}
+ if !reflect.DeepEqual(gaction, wactions) {
+ t.Errorf("action = %v, want %v", gaction, wactions)
+ }
+ if !reflect.DeepEqual(cl.Member(1234), &wm) {
+ t.Errorf("member = %v, want %v", cl.Member(1234), &wm)
+ }
+}
+
+// TODO: test server could stop itself when being removed
+
+func TestPublish(t *testing.T) {
+ n := &nodeRecorder{}
+ ch := make(chan interface{}, 1)
+ // simulate that request has gone through consensus
+ ch <- Response{}
+ w := &waitWithResponse{ch: ch}
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ id: 1,
+ r: raftNode{Node: n},
+ attributes: Attributes{Name: "node1", ClientURLs: []string{"http://a", "http://b"}},
+ cluster: &cluster{},
+ w: w,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ srv.publish(time.Hour)
+
+ action := n.Action()
+ if len(action) != 1 {
+ t.Fatalf("len(action) = %d, want 1", len(action))
+ }
+ if action[0].Name != "Propose" {
+ t.Fatalf("action = %s, want Propose", action[0].Name)
+ }
+ data := action[0].Params[0].([]byte)
+ var r pb.Request
+ if err := r.Unmarshal(data); err != nil {
+ t.Fatalf("unmarshal request error: %v", err)
+ }
+ if r.Method != "PUT" {
+ t.Errorf("method = %s, want PUT", r.Method)
+ }
+ wm := Member{ID: 1, Attributes: Attributes{Name: "node1", ClientURLs: []string{"http://a", "http://b"}}}
+ if wpath := path.Join(memberStoreKey(wm.ID), attributesSuffix); r.Path != wpath {
+ t.Errorf("path = %s, want %s", r.Path, wpath)
+ }
+ var gattr Attributes
+ if err := json.Unmarshal([]byte(r.Val), &gattr); err != nil {
+ t.Fatalf("unmarshal val error: %v", err)
+ }
+ if !reflect.DeepEqual(gattr, wm.Attributes) {
+ t.Errorf("member = %v, want %v", gattr, wm.Attributes)
+ }
+}
+
+// TestPublishStopped tests that publish will be stopped if server is stopped.
+func TestPublishStopped(t *testing.T) {
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{
+ Node: &nodeRecorder{},
+ transport: &nopTransporter{},
+ },
+ cluster: &cluster{},
+ w: &waitRecorder{},
+ done: make(chan struct{}),
+ stop: make(chan struct{}),
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ close(srv.done)
+ srv.publish(time.Hour)
+}
+
+// TestPublishRetry tests that publish will keep retry until success.
+func TestPublishRetry(t *testing.T) {
+ n := &nodeRecorder{}
+ srv := &EtcdServer{
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{Node: n},
+ w: &waitRecorder{},
+ done: make(chan struct{}),
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ time.AfterFunc(500*time.Microsecond, func() { close(srv.done) })
+ srv.publish(10 * time.Nanosecond)
+
+ action := n.Action()
+ // multiple Proposes
+ if cnt := len(action); cnt < 2 {
+ t.Errorf("len(action) = %d, want >= 2", cnt)
+ }
+}
+
+func TestUpdateVersion(t *testing.T) {
+ n := &nodeRecorder{}
+ ch := make(chan interface{}, 1)
+ // simulate that request has gone through consensus
+ ch <- Response{}
+ w := &waitWithResponse{ch: ch}
+ srv := &EtcdServer{
+ id: 1,
+ cfg: &ServerConfig{TickMs: 1},
+ r: raftNode{Node: n},
+ attributes: Attributes{Name: "node1", ClientURLs: []string{"http://node1.com"}},
+ cluster: &cluster{},
+ w: w,
+ reqIDGen: idutil.NewGenerator(0, time.Time{}),
+ }
+ srv.updateClusterVersion("2.0.0")
+
+ action := n.Action()
+ if len(action) != 1 {
+ t.Fatalf("len(action) = %d, want 1", len(action))
+ }
+ if action[0].Name != "Propose" {
+ t.Fatalf("action = %s, want Propose", action[0].Name)
+ }
+ data := action[0].Params[0].([]byte)
+ var r pb.Request
+ if err := r.Unmarshal(data); err != nil {
+ t.Fatalf("unmarshal request error: %v", err)
+ }
+ if r.Method != "PUT" {
+ t.Errorf("method = %s, want PUT", r.Method)
+ }
+ if wpath := path.Join(StoreClusterPrefix, "version"); r.Path != wpath {
+ t.Errorf("path = %s, want %s", r.Path, wpath)
+ }
+ if r.Val != "2.0.0" {
+ t.Errorf("val = %s, want %s", r.Val, "2.0.0")
+ }
+}
+
+func TestStopNotify(t *testing.T) {
+ s := &EtcdServer{
+ stop: make(chan struct{}),
+ done: make(chan struct{}),
+ }
+ go func() {
+ <-s.stop
+ close(s.done)
+ }()
+
+ notifier := s.StopNotify()
+ select {
+ case <-notifier:
+ t.Fatalf("received unexpected stop notification")
+ default:
+ }
+ s.Stop()
+ select {
+ case <-notifier:
+ default:
+ t.Fatalf("cannot receive stop notification")
+ }
+}
+
+func TestGetOtherPeerURLs(t *testing.T) {
+ tests := []struct {
+ membs []*Member
+ self string
+ wurls []string
+ }{
+ {
+ []*Member{
+ newTestMember(1, []string{"http://10.0.0.1"}, "a", nil),
+ },
+ "a",
+ []string{},
+ },
+ {
+ []*Member{
+ newTestMember(1, []string{"http://10.0.0.1"}, "a", nil),
+ newTestMember(2, []string{"http://10.0.0.2"}, "b", nil),
+ newTestMember(3, []string{"http://10.0.0.3"}, "c", nil),
+ },
+ "a",
+ []string{"http://10.0.0.2", "http://10.0.0.3"},
+ },
+ {
+ []*Member{
+ newTestMember(1, []string{"http://10.0.0.1"}, "a", nil),
+ newTestMember(3, []string{"http://10.0.0.3"}, "c", nil),
+ newTestMember(2, []string{"http://10.0.0.2"}, "b", nil),
+ },
+ "a",
+ []string{"http://10.0.0.2", "http://10.0.0.3"},
+ },
+ }
+ for i, tt := range tests {
+ cl := newClusterFromMembers("", types.ID(0), tt.membs)
+ urls := getRemotePeerURLs(cl, tt.self)
+ if !reflect.DeepEqual(urls, tt.wurls) {
+ t.Errorf("#%d: urls = %+v, want %+v", i, urls, tt.wurls)
+ }
+ }
+}
+
+// storeRecorder records all the methods it receives.
+// storeRecorder DOES NOT work as a actual store.
+// It always returns invalid empty response and no error.
+type storeRecorder struct{ testutil.Recorder }
+
+func (s *storeRecorder) Version() int { return 0 }
+func (s *storeRecorder) Index() uint64 { return 0 }
+func (s *storeRecorder) Get(path string, recursive, sorted bool) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "Get",
+ Params: []interface{}{path, recursive, sorted},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) Set(path string, dir bool, val string, expr time.Time) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "Set",
+ Params: []interface{}{path, dir, val, expr},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) Update(path, val string, expr time.Time) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "Update",
+ Params: []interface{}{path, val, expr},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) Create(path string, dir bool, val string, uniq bool, exp time.Time) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "Create",
+ Params: []interface{}{path, dir, val, uniq, exp},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) CompareAndSwap(path, prevVal string, prevIdx uint64, val string, expr time.Time) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "CompareAndSwap",
+ Params: []interface{}{path, prevVal, prevIdx, val, expr},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) Delete(path string, dir, recursive bool) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "Delete",
+ Params: []interface{}{path, dir, recursive},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) CompareAndDelete(path, prevVal string, prevIdx uint64) (*store.Event, error) {
+ s.Record(testutil.Action{
+ Name: "CompareAndDelete",
+ Params: []interface{}{path, prevVal, prevIdx},
+ })
+ return &store.Event{}, nil
+}
+func (s *storeRecorder) Watch(_ string, _, _ bool, _ uint64) (store.Watcher, error) {
+ s.Record(testutil.Action{Name: "Watch"})
+ return &nopWatcher{}, nil
+}
+func (s *storeRecorder) Save() ([]byte, error) {
+ s.Record(testutil.Action{Name: "Save"})
+ return nil, nil
+}
+func (s *storeRecorder) Recovery(b []byte) error {
+ s.Record(testutil.Action{Name: "Recovery"})
+ return nil
+}
+
+func (s *storeRecorder) SaveNoCopy() ([]byte, error) {
+ s.Record(testutil.Action{Name: "SaveNoCopy"})
+ return nil, nil
+}
+
+func (s *storeRecorder) Clone() store.Store {
+ s.Record(testutil.Action{Name: "Clone"})
+ return s
+}
+
+func (s *storeRecorder) JsonStats() []byte { return nil }
+func (s *storeRecorder) DeleteExpiredKeys(cutoff time.Time) {
+ s.Record(testutil.Action{
+ Name: "DeleteExpiredKeys",
+ Params: []interface{}{cutoff},
+ })
+}
+
+type nopWatcher struct{}
+
+func (w *nopWatcher) EventChan() chan *store.Event { return nil }
+func (w *nopWatcher) StartIndex() uint64 { return 0 }
+func (w *nopWatcher) Remove() {}
+
+// errStoreRecorder is a storeRecorder, but returns the given error on
+// Get, Watch methods.
+type errStoreRecorder struct {
+ storeRecorder
+ err error
+}
+
+func (s *errStoreRecorder) Get(path string, recursive, sorted bool) (*store.Event, error) {
+ s.storeRecorder.Get(path, recursive, sorted)
+ return nil, s.err
+}
+func (s *errStoreRecorder) Watch(path string, recursive, sorted bool, index uint64) (store.Watcher, error) {
+ s.storeRecorder.Watch(path, recursive, sorted, index)
+ return nil, s.err
+}
+
+type waitRecorder struct {
+ action []testutil.Action
+}
+
+func (w *waitRecorder) Register(id uint64) <-chan interface{} {
+ w.action = append(w.action, testutil.Action{Name: "Register"})
+ return nil
+}
+func (w *waitRecorder) Trigger(id uint64, x interface{}) {
+ w.action = append(w.action, testutil.Action{Name: "Trigger"})
+}
+
+type waitWithResponse struct {
+ ch <-chan interface{}
+}
+
+func (w *waitWithResponse) Register(id uint64) <-chan interface{} {
+ return w.ch
+}
+func (w *waitWithResponse) Trigger(id uint64, x interface{}) {}
+
+type storageRecorder struct{ testutil.Recorder }
+
+func (p *storageRecorder) Save(st raftpb.HardState, ents []raftpb.Entry) error {
+ p.Record(testutil.Action{Name: "Save"})
+ return nil
+}
+func (p *storageRecorder) SaveSnap(st raftpb.Snapshot) error {
+ if !raft.IsEmptySnap(st) {
+ p.Record(testutil.Action{Name: "SaveSnap"})
+ }
+ return nil
+}
+func (p *storageRecorder) Close() error { return nil }
+
+type nodeRecorder struct{ testutil.Recorder }
+
+func (n *nodeRecorder) Tick() { n.Record(testutil.Action{Name: "Tick"}) }
+func (n *nodeRecorder) Campaign(ctx context.Context) error {
+ n.Record(testutil.Action{Name: "Campaign"})
+ return nil
+}
+func (n *nodeRecorder) Propose(ctx context.Context, data []byte) error {
+ n.Record(testutil.Action{Name: "Propose", Params: []interface{}{data}})
+ return nil
+}
+func (n *nodeRecorder) ProposeConfChange(ctx context.Context, conf raftpb.ConfChange) error {
+ n.Record(testutil.Action{Name: "ProposeConfChange"})
+ return nil
+}
+func (n *nodeRecorder) Step(ctx context.Context, msg raftpb.Message) error {
+ n.Record(testutil.Action{Name: "Step"})
+ return nil
+}
+func (n *nodeRecorder) Status() raft.Status { return raft.Status{} }
+func (n *nodeRecorder) Ready() <-chan raft.Ready { return nil }
+func (n *nodeRecorder) Advance() {}
+func (n *nodeRecorder) ApplyConfChange(conf raftpb.ConfChange) *raftpb.ConfState {
+ n.Record(testutil.Action{Name: "ApplyConfChange", Params: []interface{}{conf}})
+ return &raftpb.ConfState{}
+}
+
+func (n *nodeRecorder) Stop() {
+ n.Record(testutil.Action{Name: "Stop"})
+}
+
+func (n *nodeRecorder) ReportUnreachable(id uint64) {}
+
+func (n *nodeRecorder) ReportSnapshot(id uint64, status raft.SnapshotStatus) {}
+
+func (n *nodeRecorder) Compact(index uint64, nodes []uint64, d []byte) {
+ n.Record(testutil.Action{Name: "Compact"})
+}
+
+type nodeProposalBlockerRecorder struct {
+ nodeRecorder
+}
+
+func (n *nodeProposalBlockerRecorder) Propose(ctx context.Context, data []byte) error {
+ <-ctx.Done()
+ n.Record(testutil.Action{Name: "Propose blocked"})
+ return nil
+}
+
+type nodeConfChangeCommitterRecorder struct {
+ nodeRecorder
+ readyc chan raft.Ready
+ index uint64
+}
+
+func newNodeConfChangeCommitterRecorder() *nodeConfChangeCommitterRecorder {
+ readyc := make(chan raft.Ready, 1)
+ return &nodeConfChangeCommitterRecorder{readyc: readyc}
+}
+func (n *nodeConfChangeCommitterRecorder) ProposeConfChange(ctx context.Context, conf raftpb.ConfChange) error {
+ data, err := conf.Marshal()
+ if err != nil {
+ return err
+ }
+ n.index++
+ n.Record(testutil.Action{Name: "ProposeConfChange:" + conf.Type.String()})
+ n.readyc <- raft.Ready{CommittedEntries: []raftpb.Entry{{Index: n.index, Type: raftpb.EntryConfChange, Data: data}}}
+ return nil
+}
+func (n *nodeConfChangeCommitterRecorder) Ready() <-chan raft.Ready {
+ return n.readyc
+}
+func (n *nodeConfChangeCommitterRecorder) ApplyConfChange(conf raftpb.ConfChange) *raftpb.ConfState {
+ n.Record(testutil.Action{Name: "ApplyConfChange:" + conf.Type.String()})
+ return &raftpb.ConfState{}
+}
+
+// nodeCommitter commits proposed data immediately.
+type nodeCommitter struct {
+ nodeRecorder
+ readyc chan raft.Ready
+ index uint64
+}
+
+func newNodeCommitter() *nodeCommitter {
+ readyc := make(chan raft.Ready, 1)
+ return &nodeCommitter{readyc: readyc}
+}
+func (n *nodeCommitter) Propose(ctx context.Context, data []byte) error {
+ n.index++
+ ents := []raftpb.Entry{{Index: n.index, Data: data}}
+ n.readyc <- raft.Ready{
+ Entries: ents,
+ CommittedEntries: ents,
+ }
+ return nil
+}
+func (n *nodeCommitter) Ready() <-chan raft.Ready {
+ return n.readyc
+}
+
+type readyNode struct {
+ nodeRecorder
+ readyc chan raft.Ready
+}
+
+func newReadyNode() *readyNode {
+ readyc := make(chan raft.Ready, 1)
+ return &readyNode{readyc: readyc}
+}
+func (n *readyNode) Ready() <-chan raft.Ready { return n.readyc }
+
+type nopTransporter struct{}
+
+func (s *nopTransporter) Start() error { return nil }
+func (s *nopTransporter) Handler() http.Handler { return nil }
+func (s *nopTransporter) Send(m []raftpb.Message) {}
+func (s *nopTransporter) AddRemote(id types.ID, us []string) {}
+func (s *nopTransporter) AddPeer(id types.ID, us []string) {}
+func (s *nopTransporter) RemovePeer(id types.ID) {}
+func (s *nopTransporter) RemoveAllPeers() {}
+func (s *nopTransporter) UpdatePeer(id types.ID, us []string) {}
+func (s *nopTransporter) ActiveSince(id types.ID) time.Time { return time.Time{} }
+func (s *nopTransporter) SnapshotReady(rc io.ReadCloser, index uint64) {}
+func (s *nopTransporter) Stop() {}
+func (s *nopTransporter) Pause() {}
+func (s *nopTransporter) Resume() {}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/snapshot_store.go b/vendor/github.com/coreos/etcd/etcdserver/snapshot_store.go
new file mode 100644
index 000000000..573f0e7ff
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/snapshot_store.go
@@ -0,0 +1,260 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/pkg/fileutil"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/rafthttp"
+ dstorage "github.com/coreos/etcd/storage"
+)
+
+// clearUnusedSnapshotInterval specifies the time interval to wait
+// before clearing unused snapshot.
+// The newly created snapshot should be retrieved within one heartbeat
+// interval because raft state machine retries to send snapshot
+// to slow follower when receiving MsgHeartbeatResp from the follower.
+// Set it as 5s to match the upper limit of heartbeat interval.
+const clearUnusedSnapshotInterval = 5 * time.Second
+
+type snapshot struct {
+ r raftpb.Snapshot
+
+ io.ReadCloser // used to read out v3 snapshot
+
+ done chan struct{}
+}
+
+func newSnapshot(r raftpb.Snapshot, kv dstorage.Snapshot) *snapshot {
+ done := make(chan struct{})
+ pr, pw := io.Pipe()
+ go func() {
+ _, err := kv.WriteTo(pw)
+ pw.CloseWithError(err)
+ kv.Close()
+ close(done)
+ }()
+ return &snapshot{
+ r: r,
+ ReadCloser: pr,
+ done: done,
+ }
+}
+
+func (s *snapshot) raft() raftpb.Snapshot { return s.r }
+
+func (s *snapshot) isClosed() bool {
+ select {
+ case <-s.done:
+ return true
+ default:
+ return false
+ }
+}
+
+// TODO: remove snapshotStore. getSnap part could be put into memoryStorage,
+// while SaveFrom could be put into another struct, or even put into dstorage package.
+type snapshotStore struct {
+ // dir to save snapshot data
+ dir string
+ kv dstorage.KV
+ tr rafthttp.Transporter
+
+ // send empty to reqsnapc to notify the channel receiver to send back latest
+ // snapshot to snapc
+ reqsnapc chan struct{}
+ // a chan to receive the requested raft snapshot
+ // snapshotStore will receive from the chan immediately after it sends empty to reqsnapc
+ raftsnapc chan raftpb.Snapshot
+
+ mu sync.Mutex // protect belowing vars
+ // snap is nil iff there is no snapshot stored
+ snap *snapshot
+ inUse bool
+ createOnce sync.Once // ensure at most one snapshot is created when no snapshot stored
+
+ clock clockwork.Clock
+}
+
+func newSnapshotStore(dir string, kv dstorage.KV) *snapshotStore {
+ return &snapshotStore{
+ dir: dir,
+ kv: kv,
+ reqsnapc: make(chan struct{}),
+ raftsnapc: make(chan raftpb.Snapshot),
+ clock: clockwork.NewRealClock(),
+ }
+}
+
+// getSnap returns a snapshot.
+// If there is no available snapshot, ErrSnapshotTemporarilyUnavaliable will be returned.
+//
+// If the snapshot stored is in use, it returns ErrSnapshotTemporarilyUnavailable.
+// If there is no snapshot stored, it creates new snapshot
+// asynchronously and returns ErrSnapshotTemporarilyUnavailable, so
+// caller could get snapshot later when the snapshot is created.
+// Otherwise, it returns the snapshot stored.
+//
+// The created snapshot is cleared from the snapshot store if it is
+// either unused after clearUnusedSnapshotInterval, or explicitly cleared
+// through clearUsedSnap after using.
+// closeSnapBefore is used to close outdated snapshot,
+// so the snapshot will be cleared faster when in use.
+//
+// snapshot store stores at most one snapshot at a time.
+// If raft state machine wants to send two snapshot messages to two followers,
+// the second snapshot message will keep getting snapshot and succeed only after
+// the first message is sent. This increases the time used to send messages,
+// but it is acceptable because this should happen seldomly.
+func (ss *snapshotStore) getSnap() (*snapshot, error) {
+ ss.mu.Lock()
+ defer ss.mu.Unlock()
+
+ if ss.inUse {
+ return nil, raft.ErrSnapshotTemporarilyUnavailable
+ }
+
+ if ss.snap == nil {
+ // create snapshot asynchronously
+ ss.createOnce.Do(func() { go ss.createSnap() })
+ return nil, raft.ErrSnapshotTemporarilyUnavailable
+ }
+
+ ss.inUse = true
+ // give transporter the generated snapshot that is ready to send out
+ ss.tr.SnapshotReady(ss.snap, ss.snap.raft().Metadata.Index)
+ return ss.snap, nil
+}
+
+// clearUsedSnap clears the snapshot from the snapshot store after it
+// is used.
+// After clear, snapshotStore could create new snapshot when getSnap.
+func (ss *snapshotStore) clearUsedSnap() {
+ ss.mu.Lock()
+ defer ss.mu.Unlock()
+ if !ss.inUse {
+ plog.Panicf("unexpected clearUsedSnap when snapshot is not in use")
+ }
+ ss.clear()
+}
+
+// closeSnapBefore closes the stored snapshot if its index is not greater
+// than the given compact index.
+// If it closes the snapshot, it returns true.
+func (ss *snapshotStore) closeSnapBefore(index uint64) bool {
+ ss.mu.Lock()
+ defer ss.mu.Unlock()
+ if ss.snap != nil && ss.snap.raft().Metadata.Index <= index {
+ if err := ss.snap.Close(); err != nil {
+ plog.Errorf("snapshot close error (%v)", err)
+ }
+ return true
+ }
+ return false
+}
+
+// createSnap creates a new snapshot and stores it into the snapshot store.
+// It also sets a timer to clear the snapshot if it is not in use after
+// some time interval.
+// It should only be called in snapshotStore functions.
+func (ss *snapshotStore) createSnap() {
+ // ask to generate v2 snapshot
+ ss.reqsnapc <- struct{}{}
+ // generate KV snapshot
+ kvsnap := ss.kv.Snapshot()
+ raftsnap := <-ss.raftsnapc
+ snap := newSnapshot(raftsnap, kvsnap)
+
+ ss.mu.Lock()
+ ss.snap = snap
+ ss.mu.Unlock()
+
+ go func() {
+ <-ss.clock.After(clearUnusedSnapshotInterval)
+ ss.mu.Lock()
+ defer ss.mu.Unlock()
+ if snap == ss.snap && !ss.inUse {
+ ss.clear()
+ }
+ }()
+}
+
+// clear clears snapshot related variables in snapshotStore. It closes
+// the snapshot stored and sets the variables to initial values.
+// It should only be called in snapshotStore functions.
+func (ss *snapshotStore) clear() {
+ if err := ss.snap.Close(); err != nil {
+ plog.Errorf("snapshot close error (%v)", err)
+ }
+ ss.snap = nil
+ ss.inUse = false
+ ss.createOnce = sync.Once{}
+}
+
+// SaveFrom saves snapshot at the given index from the given reader.
+// If the snapshot with the given index has been saved successfully, it keeps
+// the original saved snapshot and returns error.
+// The function guarantees that SaveFrom always saves either complete
+// snapshot or no snapshot, even if the call is aborted because program
+// is hard killed.
+func (ss *snapshotStore) SaveFrom(r io.Reader, index uint64) error {
+ f, err := ioutil.TempFile(ss.dir, "tmp")
+ if err != nil {
+ return err
+ }
+ _, err = io.Copy(f, r)
+ f.Close()
+ if err != nil {
+ os.Remove(f.Name())
+ return err
+ }
+ fn := path.Join(ss.dir, fmt.Sprintf("%016x.db", index))
+ if fileutil.Exist(fn) {
+ os.Remove(f.Name())
+ return fmt.Errorf("snapshot to save has existed")
+ }
+ err = os.Rename(f.Name(), fn)
+ if err != nil {
+ os.Remove(f.Name())
+ return err
+ }
+ return nil
+}
+
+// getSnapFilePath returns the file path for the snapshot with given index.
+// If the snapshot does not exist, it returns error.
+func (ss *snapshotStore) getSnapFilePath(index uint64) (string, error) {
+ fns, err := fileutil.ReadDir(ss.dir)
+ if err != nil {
+ return "", err
+ }
+ wfn := fmt.Sprintf("%016x.db", index)
+ for _, fn := range fns {
+ if fn == wfn {
+ return path.Join(ss.dir, fn), nil
+ }
+ }
+ return "", fmt.Errorf("snapshot file doesn't exist")
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/snapshot_store_test.go b/vendor/github.com/coreos/etcd/etcdserver/snapshot_store_test.go
new file mode 100644
index 000000000..119108a34
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/snapshot_store_test.go
@@ -0,0 +1,204 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "io"
+ "reflect"
+ "sync"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ dstorage "github.com/coreos/etcd/storage"
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+func TestSnapshotStoreCreateSnap(t *testing.T) {
+ snap := raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{Index: 1},
+ }
+ ss := newSnapshotStore("", &nopKV{})
+ fakeClock := clockwork.NewFakeClock()
+ ss.clock = fakeClock
+ go func() {
+ <-ss.reqsnapc
+ ss.raftsnapc <- snap
+ }()
+
+ // create snapshot
+ ss.createSnap()
+ if !reflect.DeepEqual(ss.snap.raft(), snap) {
+ t.Errorf("raftsnap = %+v, want %+v", ss.snap.raft(), snap)
+ }
+
+ // unused snapshot is cleared after clearUnusedSnapshotInterval
+ fakeClock.BlockUntil(1)
+ fakeClock.Advance(clearUnusedSnapshotInterval)
+ testutil.WaitSchedule()
+ ss.mu.Lock()
+ if ss.snap != nil {
+ t.Errorf("snap = %+v, want %+v", ss.snap, nil)
+ }
+ ss.mu.Unlock()
+}
+
+func TestSnapshotStoreGetSnap(t *testing.T) {
+ snap := raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{Index: 1},
+ }
+ ss := newSnapshotStore("", &nopKV{})
+ fakeClock := clockwork.NewFakeClock()
+ ss.clock = fakeClock
+ ss.tr = &nopTransporter{}
+ go func() {
+ <-ss.reqsnapc
+ ss.raftsnapc <- snap
+ }()
+
+ // get snap when no snapshot stored
+ _, err := ss.getSnap()
+ if err != raft.ErrSnapshotTemporarilyUnavailable {
+ t.Fatalf("getSnap error = %v, want %v", err, raft.ErrSnapshotTemporarilyUnavailable)
+ }
+
+ // wait for asynchronous snapshot creation to finish
+ testutil.WaitSchedule()
+ // get the created snapshot
+ s, err := ss.getSnap()
+ if err != nil {
+ t.Fatalf("getSnap error = %v, want nil", err)
+ }
+ if !reflect.DeepEqual(s.raft(), snap) {
+ t.Errorf("raftsnap = %+v, want %+v", s.raft(), snap)
+ }
+ if !ss.inUse {
+ t.Errorf("inUse = %v, want true", ss.inUse)
+ }
+
+ // get snap when snapshot stored has been in use
+ _, err = ss.getSnap()
+ if err != raft.ErrSnapshotTemporarilyUnavailable {
+ t.Fatalf("getSnap error = %v, want %v", err, raft.ErrSnapshotTemporarilyUnavailable)
+ }
+
+ // clean up
+ fakeClock.Advance(clearUnusedSnapshotInterval)
+}
+
+func TestSnapshotStoreClearUsedSnap(t *testing.T) {
+ s := &fakeSnapshot{}
+ var once sync.Once
+ once.Do(func() {})
+ ss := &snapshotStore{
+ snap: newSnapshot(raftpb.Snapshot{}, s),
+ inUse: true,
+ createOnce: once,
+ }
+
+ ss.clearUsedSnap()
+ // wait for underlying KV snapshot closed
+ testutil.WaitSchedule()
+ s.mu.Lock()
+ if !s.closed {
+ t.Errorf("snapshot closed = %v, want true", s.closed)
+ }
+ s.mu.Unlock()
+ if ss.snap != nil {
+ t.Errorf("snapshot = %v, want nil", ss.snap)
+ }
+ if ss.inUse {
+ t.Errorf("isUse = %v, want false", ss.inUse)
+ }
+ // test createOnce is reset
+ if ss.createOnce == once {
+ t.Errorf("createOnce fails to reset")
+ }
+}
+
+func TestSnapshotStoreCloseSnapBefore(t *testing.T) {
+ snapIndex := uint64(5)
+
+ tests := []struct {
+ index uint64
+ wok bool
+ }{
+ {snapIndex - 2, false},
+ {snapIndex - 1, false},
+ {snapIndex, true},
+ }
+ for i, tt := range tests {
+ rs := raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{Index: 5},
+ }
+ s := &fakeSnapshot{}
+ ss := &snapshotStore{
+ snap: newSnapshot(rs, s),
+ }
+
+ ok := ss.closeSnapBefore(tt.index)
+ if ok != tt.wok {
+ t.Errorf("#%d: closeSnapBefore = %v, want %v", i, ok, tt.wok)
+ }
+ if ok {
+ // wait for underlying KV snapshot closed
+ testutil.WaitSchedule()
+ s.mu.Lock()
+ if !s.closed {
+ t.Errorf("#%d: snapshot closed = %v, want true", i, s.closed)
+ }
+ s.mu.Unlock()
+ }
+ }
+}
+
+type nopKV struct{}
+
+func (kv *nopKV) Rev() int64 { return 0 }
+func (kv *nopKV) Range(key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error) {
+ return nil, 0, nil
+}
+func (kv *nopKV) Put(key, value []byte) (rev int64) { return 0 }
+func (kv *nopKV) DeleteRange(key, end []byte) (n, rev int64) { return 0, 0 }
+func (kv *nopKV) TxnBegin() int64 { return 0 }
+func (kv *nopKV) TxnEnd(txnID int64) error { return nil }
+func (kv *nopKV) TxnRange(txnID int64, key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error) {
+ return nil, 0, nil
+}
+func (kv *nopKV) TxnPut(txnID int64, key, value []byte) (rev int64, err error) { return 0, nil }
+func (kv *nopKV) TxnDeleteRange(txnID int64, key, end []byte) (n, rev int64, err error) {
+ return 0, 0, nil
+}
+func (kv *nopKV) Compact(rev int64) error { return nil }
+func (kv *nopKV) Hash() (uint32, error) { return 0, nil }
+func (kv *nopKV) Snapshot() dstorage.Snapshot { return &fakeSnapshot{} }
+func (kv *nopKV) Restore() error { return nil }
+func (kv *nopKV) Close() error { return nil }
+
+type fakeSnapshot struct {
+ mu sync.Mutex
+ closed bool
+}
+
+func (s *fakeSnapshot) Size() int64 { return 0 }
+func (s *fakeSnapshot) WriteTo(w io.Writer) (int64, error) { return 0, nil }
+func (s *fakeSnapshot) Close() error {
+ s.mu.Lock()
+ s.closed = true
+ s.mu.Unlock()
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/stats/leader.go b/vendor/github.com/coreos/etcd/etcdserver/stats/leader.go
new file mode 100644
index 000000000..a56c9b327
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/stats/leader.go
@@ -0,0 +1,122 @@
+// Copyright 2015 CoreOS, 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 stats
+
+import (
+ "encoding/json"
+ "math"
+ "sync"
+ "time"
+)
+
+// LeaderStats is used by the leader in an etcd cluster, and encapsulates
+// statistics about communication with its followers
+type LeaderStats struct {
+ // TODO(jonboulle): clarify that these are IDs, not names
+ Leader string `json:"leader"`
+ Followers map[string]*FollowerStats `json:"followers"`
+
+ sync.Mutex
+}
+
+// NewLeaderStats generates a new LeaderStats with the given id as leader
+func NewLeaderStats(id string) *LeaderStats {
+ return &LeaderStats{
+ Leader: id,
+ Followers: make(map[string]*FollowerStats),
+ }
+}
+
+func (ls *LeaderStats) JSON() []byte {
+ ls.Lock()
+ stats := *ls
+ ls.Unlock()
+ b, err := json.Marshal(stats)
+ // TODO(jonboulle): appropriate error handling?
+ if err != nil {
+ plog.Errorf("error marshalling leader stats (%v)", err)
+ }
+ return b
+}
+
+func (ls *LeaderStats) Follower(name string) *FollowerStats {
+ ls.Lock()
+ defer ls.Unlock()
+ fs, ok := ls.Followers[name]
+ if !ok {
+ fs = &FollowerStats{}
+ fs.Latency.Minimum = 1 << 63
+ ls.Followers[name] = fs
+ }
+ return fs
+}
+
+// FollowerStats encapsulates various statistics about a follower in an etcd cluster
+type FollowerStats struct {
+ Latency LatencyStats `json:"latency"`
+ Counts CountsStats `json:"counts"`
+
+ sync.Mutex
+}
+
+// LatencyStats encapsulates latency statistics.
+type LatencyStats struct {
+ Current float64 `json:"current"`
+ Average float64 `json:"average"`
+ averageSquare float64
+ StandardDeviation float64 `json:"standardDeviation"`
+ Minimum float64 `json:"minimum"`
+ Maximum float64 `json:"maximum"`
+}
+
+// CountsStats encapsulates raft statistics.
+type CountsStats struct {
+ Fail uint64 `json:"fail"`
+ Success uint64 `json:"success"`
+}
+
+// Succ updates the FollowerStats with a successful send
+func (fs *FollowerStats) Succ(d time.Duration) {
+ fs.Lock()
+ defer fs.Unlock()
+
+ total := float64(fs.Counts.Success) * fs.Latency.Average
+ totalSquare := float64(fs.Counts.Success) * fs.Latency.averageSquare
+
+ fs.Counts.Success++
+
+ fs.Latency.Current = float64(d) / (1000000.0)
+
+ if fs.Latency.Current > fs.Latency.Maximum {
+ fs.Latency.Maximum = fs.Latency.Current
+ }
+
+ if fs.Latency.Current < fs.Latency.Minimum {
+ fs.Latency.Minimum = fs.Latency.Current
+ }
+
+ fs.Latency.Average = (total + fs.Latency.Current) / float64(fs.Counts.Success)
+ fs.Latency.averageSquare = (totalSquare + fs.Latency.Current*fs.Latency.Current) / float64(fs.Counts.Success)
+
+ // sdv = sqrt(avg(x^2) - avg(x)^2)
+ fs.Latency.StandardDeviation = math.Sqrt(fs.Latency.averageSquare - fs.Latency.Average*fs.Latency.Average)
+}
+
+// Fail updates the FollowerStats with an unsuccessful send
+func (fs *FollowerStats) Fail() {
+ fs.Lock()
+ defer fs.Unlock()
+ fs.Counts.Fail++
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/stats/queue.go b/vendor/github.com/coreos/etcd/etcdserver/stats/queue.go
new file mode 100644
index 000000000..aa8d36e54
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/stats/queue.go
@@ -0,0 +1,110 @@
+// Copyright 2015 CoreOS, 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 stats
+
+import (
+ "sync"
+ "time"
+)
+
+const (
+ queueCapacity = 200
+)
+
+// RequestStats represent the stats for a request.
+// It encapsulates the sending time and the size of the request.
+type RequestStats struct {
+ SendingTime time.Time
+ Size int
+}
+
+type statsQueue struct {
+ items [queueCapacity]*RequestStats
+ size int
+ front int
+ back int
+ totalReqSize int
+ rwl sync.RWMutex
+}
+
+func (q *statsQueue) Len() int {
+ return q.size
+}
+
+func (q *statsQueue) ReqSize() int {
+ return q.totalReqSize
+}
+
+// FrontAndBack gets the front and back elements in the queue
+// We must grab front and back together with the protection of the lock
+func (q *statsQueue) frontAndBack() (*RequestStats, *RequestStats) {
+ q.rwl.RLock()
+ defer q.rwl.RUnlock()
+ if q.size != 0 {
+ return q.items[q.front], q.items[q.back]
+ }
+ return nil, nil
+}
+
+// Insert function insert a RequestStats into the queue and update the records
+func (q *statsQueue) Insert(p *RequestStats) {
+ q.rwl.Lock()
+ defer q.rwl.Unlock()
+
+ q.back = (q.back + 1) % queueCapacity
+
+ if q.size == queueCapacity { //dequeue
+ q.totalReqSize -= q.items[q.front].Size
+ q.front = (q.back + 1) % queueCapacity
+ } else {
+ q.size++
+ }
+
+ q.items[q.back] = p
+ q.totalReqSize += q.items[q.back].Size
+
+}
+
+// Rate function returns the package rate and byte rate
+func (q *statsQueue) Rate() (float64, float64) {
+ front, back := q.frontAndBack()
+
+ if front == nil || back == nil {
+ return 0, 0
+ }
+
+ if time.Now().Sub(back.SendingTime) > time.Second {
+ q.Clear()
+ return 0, 0
+ }
+
+ sampleDuration := back.SendingTime.Sub(front.SendingTime)
+
+ pr := float64(q.Len()) / float64(sampleDuration) * float64(time.Second)
+
+ br := float64(q.ReqSize()) / float64(sampleDuration) * float64(time.Second)
+
+ return pr, br
+}
+
+// Clear function clear up the statsQueue
+func (q *statsQueue) Clear() {
+ q.rwl.Lock()
+ defer q.rwl.Unlock()
+ q.back = -1
+ q.front = 0
+ q.size = 0
+ q.totalReqSize = 0
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/stats/server.go b/vendor/github.com/coreos/etcd/etcdserver/stats/server.go
new file mode 100644
index 000000000..f38cf4660
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/stats/server.go
@@ -0,0 +1,149 @@
+// Copyright 2015 CoreOS, 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 stats
+
+import (
+ "encoding/json"
+ "log"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/raft"
+)
+
+// ServerStats encapsulates various statistics about an EtcdServer and its
+// communication with other members of the cluster
+type ServerStats struct {
+ Name string `json:"name"`
+ // TODO(jonboulle): use ID instead of name?
+ ID string `json:"id"`
+ State raft.StateType `json:"state"`
+ StartTime time.Time `json:"startTime"`
+
+ LeaderInfo struct {
+ Name string `json:"leader"`
+ Uptime string `json:"uptime"`
+ StartTime time.Time `json:"startTime"`
+ } `json:"leaderInfo"`
+
+ RecvAppendRequestCnt uint64 `json:"recvAppendRequestCnt,"`
+ RecvingPkgRate float64 `json:"recvPkgRate,omitempty"`
+ RecvingBandwidthRate float64 `json:"recvBandwidthRate,omitempty"`
+
+ SendAppendRequestCnt uint64 `json:"sendAppendRequestCnt"`
+ SendingPkgRate float64 `json:"sendPkgRate,omitempty"`
+ SendingBandwidthRate float64 `json:"sendBandwidthRate,omitempty"`
+
+ sendRateQueue *statsQueue
+ recvRateQueue *statsQueue
+
+ sync.Mutex
+}
+
+func (ss *ServerStats) JSON() []byte {
+ ss.Lock()
+ stats := *ss
+ ss.Unlock()
+ stats.LeaderInfo.Uptime = time.Now().Sub(stats.LeaderInfo.StartTime).String()
+ stats.SendingPkgRate, stats.SendingBandwidthRate = stats.SendRates()
+ stats.RecvingPkgRate, stats.RecvingBandwidthRate = stats.RecvRates()
+ b, err := json.Marshal(stats)
+ // TODO(jonboulle): appropriate error handling?
+ if err != nil {
+ log.Printf("stats: error marshalling server stats: %v", err)
+ }
+ return b
+}
+
+// Initialize clears the statistics of ServerStats and resets its start time
+func (ss *ServerStats) Initialize() {
+ if ss == nil {
+ return
+ }
+ now := time.Now()
+ ss.StartTime = now
+ ss.LeaderInfo.StartTime = now
+ ss.sendRateQueue = &statsQueue{
+ back: -1,
+ }
+ ss.recvRateQueue = &statsQueue{
+ back: -1,
+ }
+}
+
+// RecvRates calculates and returns the rate of received append requests
+func (ss *ServerStats) RecvRates() (float64, float64) {
+ return ss.recvRateQueue.Rate()
+}
+
+// SendRates calculates and returns the rate of sent append requests
+func (ss *ServerStats) SendRates() (float64, float64) {
+ return ss.sendRateQueue.Rate()
+}
+
+// RecvAppendReq updates the ServerStats in response to an AppendRequest
+// from the given leader being received
+func (ss *ServerStats) RecvAppendReq(leader string, reqSize int) {
+ ss.Lock()
+ defer ss.Unlock()
+
+ now := time.Now()
+
+ ss.State = raft.StateFollower
+ if leader != ss.LeaderInfo.Name {
+ ss.LeaderInfo.Name = leader
+ ss.LeaderInfo.StartTime = now
+ }
+
+ ss.recvRateQueue.Insert(
+ &RequestStats{
+ SendingTime: now,
+ Size: reqSize,
+ },
+ )
+ ss.RecvAppendRequestCnt++
+}
+
+// SendAppendReq updates the ServerStats in response to an AppendRequest
+// being sent by this server
+func (ss *ServerStats) SendAppendReq(reqSize int) {
+ ss.Lock()
+ defer ss.Unlock()
+
+ now := time.Now()
+
+ if ss.State != raft.StateLeader {
+ ss.State = raft.StateLeader
+ ss.LeaderInfo.Name = ss.ID
+ ss.LeaderInfo.StartTime = now
+ }
+
+ ss.sendRateQueue.Insert(
+ &RequestStats{
+ SendingTime: now,
+ Size: reqSize,
+ },
+ )
+
+ ss.SendAppendRequestCnt++
+}
+
+func (ss *ServerStats) BecomeLeader() {
+ if ss.State != raft.StateLeader {
+ ss.State = raft.StateLeader
+ ss.LeaderInfo.Name = ss.ID
+ ss.LeaderInfo.StartTime = time.Now()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/stats/stats.go b/vendor/github.com/coreos/etcd/etcdserver/stats/stats.go
new file mode 100644
index 000000000..5bda72327
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/stats/stats.go
@@ -0,0 +1,31 @@
+// Copyright 2015 CoreOS, 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 stats
+
+import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd/etcdserver", "stats")
+)
+
+type Stats interface {
+ // SelfStats returns the struct representing statistics of this server
+ SelfStats() []byte
+ // LeaderStats returns the statistics of all followers in the cluster
+ // if this server is leader. Otherwise, nil is returned.
+ LeaderStats() []byte
+ // StoreStats returns statistics of the store backing this EtcdServer
+ StoreStats() []byte
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/storage.go b/vendor/github.com/coreos/etcd/etcdserver/storage.go
new file mode 100644
index 000000000..b9870eae4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/storage.go
@@ -0,0 +1,143 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "io"
+ "os"
+ "path"
+
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/snap"
+ "github.com/coreos/etcd/version"
+ "github.com/coreos/etcd/wal"
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+type Storage interface {
+ // Save function saves ents and state to the underlying stable storage.
+ // Save MUST block until st and ents are on stable storage.
+ Save(st raftpb.HardState, ents []raftpb.Entry) error
+ // SaveSnap function saves snapshot to the underlying stable storage.
+ SaveSnap(snap raftpb.Snapshot) error
+ // Close closes the Storage and performs finalization.
+ Close() error
+}
+
+type storage struct {
+ *wal.WAL
+ *snap.Snapshotter
+}
+
+func NewStorage(w *wal.WAL, s *snap.Snapshotter) Storage {
+ return &storage{w, s}
+}
+
+// SaveSnap saves the snapshot to disk and release the locked
+// wal files since they will not be used.
+func (st *storage) SaveSnap(snap raftpb.Snapshot) error {
+ walsnap := walpb.Snapshot{
+ Index: snap.Metadata.Index,
+ Term: snap.Metadata.Term,
+ }
+ err := st.WAL.SaveSnapshot(walsnap)
+ if err != nil {
+ return err
+ }
+ err = st.Snapshotter.SaveSnap(snap)
+ if err != nil {
+ return err
+ }
+ err = st.WAL.ReleaseLockTo(snap.Metadata.Index)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+func readWAL(waldir string, snap walpb.Snapshot) (w *wal.WAL, id, cid types.ID, st raftpb.HardState, ents []raftpb.Entry) {
+ var (
+ err error
+ wmetadata []byte
+ )
+
+ repaired := false
+ for {
+ if w, err = wal.Open(waldir, snap); err != nil {
+ plog.Fatalf("open wal error: %v", err)
+ }
+ if wmetadata, st, ents, err = w.ReadAll(); err != nil {
+ w.Close()
+ // we can only repair ErrUnexpectedEOF and we never repair twice.
+ if repaired || err != io.ErrUnexpectedEOF {
+ plog.Fatalf("read wal error (%v) and cannot be repaired", err)
+ }
+ if !wal.Repair(waldir) {
+ plog.Fatalf("WAL error (%v) cannot be repaired", err)
+ } else {
+ plog.Infof("repaired WAL error (%v)", err)
+ repaired = true
+ }
+ continue
+ }
+ break
+ }
+ var metadata pb.Metadata
+ pbutil.MustUnmarshal(&metadata, wmetadata)
+ id = types.ID(metadata.NodeID)
+ cid = types.ID(metadata.ClusterID)
+ return
+}
+
+// upgradeWAL converts an older version of the etcdServer data to the newest version.
+// It must ensure that, after upgrading, the most recent version is present.
+func upgradeDataDir(baseDataDir string, name string, ver version.DataDirVersion) error {
+ switch ver {
+ case version.DataDir2_0:
+ err := makeMemberDir(baseDataDir)
+ if err != nil {
+ return err
+ }
+ fallthrough
+ case version.DataDir2_0_1:
+ fallthrough
+ default:
+ }
+ return nil
+}
+
+func makeMemberDir(dir string) error {
+ membdir := path.Join(dir, "member")
+ _, err := os.Stat(membdir)
+ switch {
+ case err == nil:
+ return nil
+ case !os.IsNotExist(err):
+ return err
+ }
+ if err := os.MkdirAll(membdir, 0700); err != nil {
+ return err
+ }
+ names := []string{"snap", "wal"}
+ for _, name := range names {
+ if err := os.Rename(path.Join(dir, name), path.Join(membdir, name)); err != nil {
+ return err
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/etcdserver/v3demo_server.go b/vendor/github.com/coreos/etcd/etcdserver/v3demo_server.go
new file mode 100644
index 000000000..0a0bc415a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/etcdserver/v3demo_server.go
@@ -0,0 +1,229 @@
+// Copyright 2015 CoreOS, 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 etcdserver
+
+import (
+ "bytes"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
+ dstorage "github.com/coreos/etcd/storage"
+)
+
+type V3DemoServer interface {
+ V3DemoDo(ctx context.Context, r pb.InternalRaftRequest) (proto.Message, error)
+}
+
+type applyResult struct {
+ resp proto.Message
+ err error
+}
+
+func (s *EtcdServer) V3DemoDo(ctx context.Context, r pb.InternalRaftRequest) (proto.Message, error) {
+ r.ID = s.reqIDGen.Next()
+
+ data, err := r.Marshal()
+ if err != nil {
+ return &pb.EmptyResponse{}, err
+ }
+ ch := s.w.Register(r.ID)
+
+ s.r.Propose(ctx, data)
+
+ select {
+ case x := <-ch:
+ result := x.(*applyResult)
+ return result.resp, result.err
+ case <-ctx.Done():
+ s.w.Trigger(r.ID, nil) // GC wait
+ return &pb.EmptyResponse{}, ctx.Err()
+ case <-s.done:
+ return &pb.EmptyResponse{}, ErrStopped
+ }
+}
+
+func (s *EtcdServer) applyV3Request(r *pb.InternalRaftRequest) interface{} {
+ ar := &applyResult{}
+
+ switch {
+ case r.Range != nil:
+ ar.resp, ar.err = applyRange(s.kv, r.Range)
+ case r.Put != nil:
+ ar.resp, ar.err = applyPut(s.kv, r.Put)
+ case r.DeleteRange != nil:
+ ar.resp, ar.err = applyDeleteRange(s.kv, r.DeleteRange)
+ case r.Txn != nil:
+ ar.resp, ar.err = applyTxn(s.kv, r.Txn)
+ case r.Compaction != nil:
+ ar.resp, ar.err = applyCompaction(s.kv, r.Compaction)
+ default:
+ panic("not implemented")
+ }
+
+ return ar
+}
+
+func applyPut(kv dstorage.KV, p *pb.PutRequest) (*pb.PutResponse, error) {
+ resp := &pb.PutResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ rev := kv.Put(p.Key, p.Value)
+ resp.Header.Revision = rev
+ return resp, nil
+}
+
+func applyRange(kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeResponse, error) {
+ resp := &pb.RangeResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ kvs, rev, err := kv.Range(r.Key, r.RangeEnd, r.Limit, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ resp.Header.Revision = rev
+ for i := range kvs {
+ resp.Kvs = append(resp.Kvs, &kvs[i])
+ }
+ return resp, nil
+}
+
+func applyDeleteRange(kv dstorage.KV, dr *pb.DeleteRangeRequest) (*pb.DeleteRangeResponse, error) {
+ resp := &pb.DeleteRangeResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ _, rev := kv.DeleteRange(dr.Key, dr.RangeEnd)
+ resp.Header.Revision = rev
+ return resp, nil
+}
+
+func applyTxn(kv dstorage.KV, rt *pb.TxnRequest) (*pb.TxnResponse, error) {
+ var revision int64
+
+ ok := true
+ for _, c := range rt.Compare {
+ if revision, ok = applyCompare(kv, c); !ok {
+ break
+ }
+ }
+
+ // TODO: check potential errors before actually applying anything
+
+ var reqs []*pb.RequestUnion
+ if ok {
+ reqs = rt.Success
+ } else {
+ reqs = rt.Failure
+ }
+ resps := make([]*pb.ResponseUnion, len(reqs))
+ for i := range reqs {
+ resps[i] = applyUnion(kv, reqs[i])
+ }
+ if len(resps) != 0 {
+ revision += 1
+ }
+
+ txnResp := &pb.TxnResponse{}
+ txnResp.Header = &pb.ResponseHeader{}
+ txnResp.Header.Revision = revision
+ txnResp.Responses = resps
+ txnResp.Succeeded = ok
+ return txnResp, nil
+}
+
+func applyCompaction(kv dstorage.KV, compaction *pb.CompactionRequest) (*pb.CompactionResponse, error) {
+ resp := &pb.CompactionResponse{}
+ resp.Header = &pb.ResponseHeader{}
+ err := kv.Compact(compaction.Revision)
+ if err != nil {
+ return nil, err
+ }
+ // get the current revision. which key to get is not important.
+ _, resp.Header.Revision, _ = kv.Range([]byte("compaction"), nil, 1, 0)
+ return resp, err
+}
+
+func applyUnion(kv dstorage.KV, union *pb.RequestUnion) *pb.ResponseUnion {
+ switch {
+ case union.RequestRange != nil:
+ resp, err := applyRange(kv, union.RequestRange)
+ if err != nil {
+ panic("unexpected error during txn")
+ }
+ return &pb.ResponseUnion{ResponseRange: resp}
+ case union.RequestPut != nil:
+ resp, err := applyPut(kv, union.RequestPut)
+ if err != nil {
+ panic("unexpected error during txn")
+ }
+ return &pb.ResponseUnion{ResponsePut: resp}
+ case union.RequestDeleteRange != nil:
+ resp, err := applyDeleteRange(kv, union.RequestDeleteRange)
+ if err != nil {
+ panic("unexpected error during txn")
+ }
+ return &pb.ResponseUnion{ResponseDeleteRange: resp}
+ default:
+ // empty union
+ return nil
+ }
+}
+
+func applyCompare(kv dstorage.KV, c *pb.Compare) (int64, bool) {
+ ckvs, rev, err := kv.Range(c.Key, nil, 1, 0)
+ if err != nil {
+ return rev, false
+ }
+
+ ckv := ckvs[0]
+
+ // -1 is less, 0 is equal, 1 is greater
+ var result int
+ switch c.Target {
+ case pb.Compare_VALUE:
+ result = bytes.Compare(ckv.Value, c.Value)
+ case pb.Compare_CREATE:
+ result = compareInt64(ckv.CreateRevision, c.CreateRevision)
+ case pb.Compare_MOD:
+ result = compareInt64(ckv.ModRevision, c.ModRevision)
+ case pb.Compare_VERSION:
+ result = compareInt64(ckv.Version, c.Version)
+ }
+
+ switch c.Result {
+ case pb.Compare_EQUAL:
+ if result != 0 {
+ return rev, false
+ }
+ case pb.Compare_GREATER:
+ if result != 1 {
+ return rev, false
+ }
+ case pb.Compare_LESS:
+ if result != -1 {
+ return rev, false
+ }
+ }
+ return rev, true
+}
+
+func compareInt64(a, b int64) int {
+ switch {
+ case a < b:
+ return -1
+ case a > b:
+ return 1
+ default:
+ return 0
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/crc/crc.go b/vendor/github.com/coreos/etcd/pkg/crc/crc.go
new file mode 100644
index 000000000..b4e7c8da0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/crc/crc.go
@@ -0,0 +1,41 @@
+// Copyright 2009 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.
+
+package crc
+
+import (
+ "hash"
+ "hash/crc32"
+)
+
+// The size of a CRC-32 checksum in bytes.
+const Size = 4
+
+type digest struct {
+ crc uint32
+ tab *crc32.Table
+}
+
+// New creates a new hash.Hash32 computing the CRC-32 checksum
+// using the polynomial represented by the Table.
+// Modified by xiangli to take a prevcrc.
+func New(prev uint32, tab *crc32.Table) hash.Hash32 { return &digest{prev, tab} }
+
+func (d *digest) Size() int { return Size }
+
+func (d *digest) BlockSize() int { return 1 }
+
+func (d *digest) Reset() { d.crc = 0 }
+
+func (d *digest) Write(p []byte) (n int, err error) {
+ d.crc = crc32.Update(d.crc, d.tab, p)
+ return len(p), nil
+}
+
+func (d *digest) Sum32() uint32 { return d.crc }
+
+func (d *digest) Sum(in []byte) []byte {
+ s := d.Sum32()
+ return append(in, byte(s>>24), byte(s>>16), byte(s>>8), byte(s))
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/crc/crc_test.go b/vendor/github.com/coreos/etcd/pkg/crc/crc_test.go
new file mode 100644
index 000000000..457596409
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/crc/crc_test.go
@@ -0,0 +1,59 @@
+// Copyright 2009 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.
+
+package crc
+
+import (
+ "hash/crc32"
+ "reflect"
+ "testing"
+)
+
+// TestHash32 tests that Hash32 provided by this package can take an initial
+// crc and behaves exactly the same as the standard one in the following calls.
+func TestHash32(t *testing.T) {
+ stdhash := crc32.New(crc32.IEEETable)
+ if _, err := stdhash.Write([]byte("test data")); err != nil {
+ t.Fatalf("unexpected write error: %v", err)
+ }
+ // create a new hash with stdhash.Sum32() as initial crc
+ hash := New(stdhash.Sum32(), crc32.IEEETable)
+
+ wsize := stdhash.Size()
+ if g := hash.Size(); g != wsize {
+ t.Errorf("size = %d, want %d", g, wsize)
+ }
+ wbsize := stdhash.BlockSize()
+ if g := hash.BlockSize(); g != wbsize {
+ t.Errorf("block size = %d, want %d", g, wbsize)
+ }
+ wsum32 := stdhash.Sum32()
+ if g := hash.Sum32(); g != wsum32 {
+ t.Errorf("Sum32 = %d, want %d", g, wsum32)
+ }
+ wsum := stdhash.Sum(make([]byte, 32))
+ if g := hash.Sum(make([]byte, 32)); !reflect.DeepEqual(g, wsum) {
+ t.Errorf("sum = %v, want %v", g, wsum)
+ }
+
+ // write something
+ if _, err := stdhash.Write([]byte("test data")); err != nil {
+ t.Fatalf("unexpected write error: %v", err)
+ }
+ if _, err := hash.Write([]byte("test data")); err != nil {
+ t.Fatalf("unexpected write error: %v", err)
+ }
+ wsum32 = stdhash.Sum32()
+ if g := hash.Sum32(); g != wsum32 {
+ t.Errorf("Sum32 after write = %d, want %d", g, wsum32)
+ }
+
+ // reset
+ stdhash.Reset()
+ hash.Reset()
+ wsum32 = stdhash.Sum32()
+ if g := hash.Sum32(); g != wsum32 {
+ t.Errorf("Sum32 after reset = %d, want %d", g, wsum32)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go b/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go
new file mode 100644
index 000000000..7a39ca554
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil.go
@@ -0,0 +1,62 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "sort"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+)
+
+const (
+ privateFileMode = 0600
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "fileutil")
+)
+
+// IsDirWriteable checks if dir is writable by writing and removing a file
+// to dir. It returns nil if dir is writable.
+func IsDirWriteable(dir string) error {
+ f := path.Join(dir, ".touch")
+ if err := ioutil.WriteFile(f, []byte(""), privateFileMode); err != nil {
+ return err
+ }
+ return os.Remove(f)
+}
+
+// ReadDir returns the filenames in the given directory in sorted order.
+func ReadDir(dirpath string) ([]string, error) {
+ dir, err := os.Open(dirpath)
+ if err != nil {
+ return nil, err
+ }
+ defer dir.Close()
+ names, err := dir.Readdirnames(-1)
+ if err != nil {
+ return nil, err
+ }
+ sort.Strings(names)
+ return names, nil
+}
+
+func Exist(name string) bool {
+ _, err := os.Stat(name)
+ return err == nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil_test.go b/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil_test.go
new file mode 100644
index 000000000..bb2db2a21
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/fileutil_test.go
@@ -0,0 +1,84 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "reflect"
+ "testing"
+)
+
+func TestIsDirWriteable(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("unexpected ioutil.TempDir error: %v", err)
+ }
+ defer os.RemoveAll(tmpdir)
+ if err := IsDirWriteable(tmpdir); err != nil {
+ t.Fatalf("unexpected IsDirWriteable error: %v", err)
+ }
+ if err := os.Chmod(tmpdir, 0444); err != nil {
+ t.Fatalf("unexpected os.Chmod error: %v", err)
+ }
+ if err := IsDirWriteable(tmpdir); err == nil {
+ t.Fatalf("expected IsDirWriteable to error")
+ }
+}
+
+func TestReadDir(t *testing.T) {
+ tmpdir, err := ioutil.TempDir("", "")
+ defer os.RemoveAll(tmpdir)
+ if err != nil {
+ t.Fatalf("unexpected ioutil.TempDir error: %v", err)
+ }
+ files := []string{"def", "abc", "xyz", "ghi"}
+ for _, f := range files {
+ var fh *os.File
+ fh, err = os.Create(filepath.Join(tmpdir, f))
+ if err != nil {
+ t.Fatalf("error creating file: %v", err)
+ }
+ if err := fh.Close(); err != nil {
+ t.Fatalf("error closing file: %v", err)
+ }
+ }
+ fs, err := ReadDir(tmpdir)
+ if err != nil {
+ t.Fatalf("error calling ReadDir: %v", err)
+ }
+ wfs := []string{"abc", "def", "ghi", "xyz"}
+ if !reflect.DeepEqual(fs, wfs) {
+ t.Fatalf("ReadDir: got %v, want %v", fs, wfs)
+ }
+}
+
+func TestExist(t *testing.T) {
+ f, err := ioutil.TempFile(os.TempDir(), "fileutil")
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ if g := Exist(f.Name()); g != true {
+ t.Errorf("exist = %v, want true", g)
+ }
+
+ os.Remove(f.Name())
+ if g := Exist(f.Name()); g != false {
+ t.Errorf("exist = %v, want false", g)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/lock_plan9.go b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_plan9.go
new file mode 100644
index 000000000..311288de0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_plan9.go
@@ -0,0 +1,90 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "errors"
+ "os"
+ "syscall"
+ "time"
+)
+
+var (
+ ErrLocked = errors.New("file already locked")
+)
+
+type Lock interface {
+ Name() string
+ TryLock() error
+ Lock() error
+ Unlock() error
+ Destroy() error
+}
+
+type lock struct {
+ fname string
+ file *os.File
+}
+
+func (l *lock) Name() string {
+ return l.fname
+}
+
+// TryLock acquires exclusivity on the lock without blocking
+func (l *lock) TryLock() error {
+ err := os.Chmod(l.fname, syscall.DMEXCL|0600)
+ if err != nil {
+ return err
+ }
+
+ f, err := os.Open(l.fname)
+ if err != nil {
+ return ErrLocked
+ }
+
+ l.file = f
+ return nil
+}
+
+// Lock acquires exclusivity on the lock with blocking
+func (l *lock) Lock() error {
+ err := os.Chmod(l.fname, syscall.DMEXCL|0600)
+ if err != nil {
+ return err
+ }
+
+ for {
+ f, err := os.Open(l.fname)
+ if err == nil {
+ l.file = f
+ return nil
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+}
+
+// Unlock unlocks the lock
+func (l *lock) Unlock() error {
+ return l.file.Close()
+}
+
+func (l *lock) Destroy() error {
+ return nil
+}
+
+func NewLock(file string) (Lock, error) {
+ l := &lock{fname: file}
+ return l, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/lock_solaris.go b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_solaris.go
new file mode 100644
index 000000000..1929bd1fc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_solaris.go
@@ -0,0 +1,98 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build solaris
+
+package fileutil
+
+import (
+ "errors"
+ "os"
+ "syscall"
+)
+
+var (
+ ErrLocked = errors.New("file already locked")
+)
+
+type Lock interface {
+ Name() string
+ TryLock() error
+ Lock() error
+ Unlock() error
+ Destroy() error
+}
+
+type lock struct {
+ fd int
+ file *os.File
+}
+
+func (l *lock) Name() string {
+ return l.file.Name()
+}
+
+// TryLock acquires exclusivity on the lock without blocking
+func (l *lock) TryLock() error {
+ var lock syscall.Flock_t
+ lock.Start = 0
+ lock.Len = 0
+ lock.Pid = 0
+ lock.Type = syscall.F_WRLCK
+ lock.Whence = 0
+ lock.Pid = 0
+ err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
+ if err != nil && err == syscall.EAGAIN {
+ return ErrLocked
+ }
+ return err
+}
+
+// Lock acquires exclusivity on the lock without blocking
+func (l *lock) Lock() error {
+ var lock syscall.Flock_t
+ lock.Start = 0
+ lock.Len = 0
+ lock.Type = syscall.F_WRLCK
+ lock.Whence = 0
+ lock.Pid = 0
+ return syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
+}
+
+// Unlock unlocks the lock
+func (l *lock) Unlock() error {
+ var lock syscall.Flock_t
+ lock.Start = 0
+ lock.Len = 0
+ lock.Type = syscall.F_UNLCK
+ lock.Whence = 0
+ err := syscall.FcntlFlock(uintptr(l.fd), syscall.F_SETLK, &lock)
+ if err != nil && err == syscall.EAGAIN {
+ return ErrLocked
+ }
+ return err
+}
+
+func (l *lock) Destroy() error {
+ return l.file.Close()
+}
+
+func NewLock(file string) (Lock, error) {
+ f, err := os.OpenFile(file, os.O_WRONLY, 0600)
+ if err != nil {
+ return nil, err
+ }
+ l := &lock{int(f.Fd()), f}
+ return l, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/lock_test.go b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_test.go
new file mode 100644
index 000000000..cda47deea
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_test.go
@@ -0,0 +1,96 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+ "time"
+)
+
+func TestLockAndUnlock(t *testing.T) {
+ f, err := ioutil.TempFile("", "lock")
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+ defer func() {
+ err := os.Remove(f.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ }()
+
+ // lock the file
+ l, err := NewLock(f.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer l.Destroy()
+ err = l.Lock()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // try lock a locked file
+ dupl, err := NewLock(f.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = dupl.TryLock()
+ if err != ErrLocked {
+ t.Errorf("err = %v, want %v", err, ErrLocked)
+ }
+
+ // unlock the file
+ err = l.Unlock()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // try lock the unlocked file
+ err = dupl.TryLock()
+ if err != nil {
+ t.Errorf("err = %v, want %v", err, nil)
+ }
+ defer dupl.Destroy()
+
+ // blocking on locked file
+ locked := make(chan struct{}, 1)
+ go func() {
+ l.Lock()
+ locked <- struct{}{}
+ }()
+
+ select {
+ case <-locked:
+ t.Error("unexpected unblocking")
+ case <-time.After(10 * time.Millisecond):
+ }
+
+ // unlock
+ err = dupl.Unlock()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // the previously blocked routine should be unblocked
+ select {
+ case <-locked:
+ case <-time.After(20 * time.Millisecond):
+ t.Error("unexpected blocking")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/lock_unix.go b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_unix.go
new file mode 100644
index 000000000..f6e69cc34
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_unix.go
@@ -0,0 +1,76 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build !windows,!plan9,!solaris
+
+package fileutil
+
+import (
+ "errors"
+ "os"
+ "syscall"
+)
+
+var (
+ ErrLocked = errors.New("file already locked")
+)
+
+type Lock interface {
+ Name() string
+ TryLock() error
+ Lock() error
+ Unlock() error
+ Destroy() error
+}
+
+type lock struct {
+ fd int
+ file *os.File
+}
+
+func (l *lock) Name() string {
+ return l.file.Name()
+}
+
+// TryLock acquires exclusivity on the lock without blocking
+func (l *lock) TryLock() error {
+ err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
+ if err != nil && err == syscall.EWOULDBLOCK {
+ return ErrLocked
+ }
+ return err
+}
+
+// Lock acquires exclusivity on the lock without blocking
+func (l *lock) Lock() error {
+ return syscall.Flock(l.fd, syscall.LOCK_EX)
+}
+
+// Unlock unlocks the lock
+func (l *lock) Unlock() error {
+ return syscall.Flock(l.fd, syscall.LOCK_UN)
+}
+
+func (l *lock) Destroy() error {
+ return l.file.Close()
+}
+
+func NewLock(file string) (Lock, error) {
+ f, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ l := &lock{int(f.Fd()), f}
+ return l, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/lock_windows.go b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_windows.go
new file mode 100644
index 000000000..a0a928b90
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/lock_windows.go
@@ -0,0 +1,71 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build windows
+
+package fileutil
+
+import (
+ "errors"
+ "os"
+)
+
+var (
+ ErrLocked = errors.New("file already locked")
+)
+
+type Lock interface {
+ Name() string
+ TryLock() error
+ Lock() error
+ Unlock() error
+ Destroy() error
+}
+
+type lock struct {
+ fd int
+ file *os.File
+}
+
+func (l *lock) Name() string {
+ return l.file.Name()
+}
+
+// TryLock acquires exclusivity on the lock without blocking
+func (l *lock) TryLock() error {
+ return nil
+}
+
+// Lock acquires exclusivity on the lock without blocking
+func (l *lock) Lock() error {
+ return nil
+}
+
+// Unlock unlocks the lock
+func (l *lock) Unlock() error {
+ return nil
+}
+
+func (l *lock) Destroy() error {
+ return l.file.Close()
+}
+
+func NewLock(file string) (Lock, error) {
+ f, err := os.Open(file)
+ if err != nil {
+ return nil, err
+ }
+ l := &lock{int(f.Fd()), f}
+ return l, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/perallocate_unsupported.go b/vendor/github.com/coreos/etcd/pkg/fileutil/perallocate_unsupported.go
new file mode 100644
index 000000000..c1a952bb7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/perallocate_unsupported.go
@@ -0,0 +1,28 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build !linux
+
+package fileutil
+
+import "os"
+
+// Preallocate tries to allocate the space for given
+// file. This operation is only supported on linux by a
+// few filesystems (btrfs, ext4, etc.).
+// If the operation is unsupported, no error will be returned.
+// Otherwise, the error encountered will be returned.
+func Preallocate(f *os.File, sizeInBytes int) error {
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/preallocate.go b/vendor/github.com/coreos/etcd/pkg/fileutil/preallocate.go
new file mode 100644
index 000000000..c4bd4f4c8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/preallocate.go
@@ -0,0 +1,42 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build linux
+
+package fileutil
+
+import (
+ "os"
+ "syscall"
+)
+
+// Preallocate tries to allocate the space for given
+// file. This operation is only supported on linux by a
+// few filesystems (btrfs, ext4, etc.).
+// If the operation is unsupported, no error will be returned.
+// Otherwise, the error encountered will be returned.
+func Preallocate(f *os.File, sizeInBytes int) error {
+ // use mode = 1 to keep size
+ // see FALLOC_FL_KEEP_SIZE
+ err := syscall.Fallocate(int(f.Fd()), 1, 0, int64(sizeInBytes))
+ if err != nil {
+ errno, ok := err.(syscall.Errno)
+ // treat not support as nil error
+ if ok && errno == syscall.ENOTSUP {
+ return nil
+ }
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_test.go b/vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_test.go
new file mode 100644
index 000000000..d5f2a71f3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/preallocate_test.go
@@ -0,0 +1,53 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "io/ioutil"
+ "os"
+ "runtime"
+ "testing"
+)
+
+func TestPreallocate(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ t.Skipf("skip testPreallocate, OS = %s", runtime.GOOS)
+ }
+
+ p, err := ioutil.TempDir(os.TempDir(), "preallocateTest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ f, err := ioutil.TempFile(p, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ size := 64 * 1000
+ err = Preallocate(f, size)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stat, err := f.Stat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if stat.Size() != 0 {
+ t.Errorf("size = %d, want %d", stat.Size(), 0)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go b/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go
new file mode 100644
index 000000000..375aa9719
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/purge.go
@@ -0,0 +1,80 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "os"
+ "path"
+ "sort"
+ "strings"
+ "time"
+)
+
+func PurgeFile(dirname string, suffix string, max uint, interval time.Duration, stop <-chan struct{}) <-chan error {
+ errC := make(chan error, 1)
+ go func() {
+ for {
+ fnames, err := ReadDir(dirname)
+ if err != nil {
+ errC <- err
+ return
+ }
+ newfnames := make([]string, 0)
+ for _, fname := range fnames {
+ if strings.HasSuffix(fname, suffix) {
+ newfnames = append(newfnames, fname)
+ }
+ }
+ sort.Strings(newfnames)
+ for len(newfnames) > int(max) {
+ f := path.Join(dirname, newfnames[0])
+ l, err := NewLock(f)
+ if err != nil {
+ errC <- err
+ return
+ }
+ err = l.TryLock()
+ if err != nil {
+ break
+ }
+ err = os.Remove(f)
+ if err != nil {
+ errC <- err
+ return
+ }
+ err = l.Unlock()
+ if err != nil {
+ plog.Errorf("error unlocking %s when purging file (%v)", l.Name(), err)
+ errC <- err
+ return
+ }
+ err = l.Destroy()
+ if err != nil {
+ plog.Errorf("error destroying lock %s when purging file (%v)", l.Name(), err)
+ errC <- err
+ return
+ }
+ plog.Infof("purged file %s successfully", f)
+ newfnames = newfnames[1:]
+ }
+ select {
+ case <-time.After(interval):
+ case <-stop:
+ return
+ }
+ }
+ }()
+ return errC
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/fileutil/purge_test.go b/vendor/github.com/coreos/etcd/pkg/fileutil/purge_test.go
new file mode 100644
index 000000000..0b11b23a5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/fileutil/purge_test.go
@@ -0,0 +1,132 @@
+// Copyright 2015 CoreOS, 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 fileutil
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path"
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestPurgeFile(t *testing.T) {
+ dir, err := ioutil.TempDir("", "purgefile")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ for i := 0; i < 5; i++ {
+ _, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ stop := make(chan struct{})
+ errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
+ for i := 5; i < 10; i++ {
+ _, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ fnames, err := ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wnames := []string{"7.test", "8.test", "9.test"}
+ if !reflect.DeepEqual(fnames, wnames) {
+ t.Errorf("filenames = %v, want %v", fnames, wnames)
+ }
+ select {
+ case err := <-errch:
+ t.Errorf("unexpected purge error %v", err)
+ case <-time.After(time.Millisecond):
+ }
+ close(stop)
+}
+
+func TestPurgeFileHoldingLock(t *testing.T) {
+ dir, err := ioutil.TempDir("", "purgefile")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ for i := 0; i < 10; i++ {
+ _, err = os.Create(path.Join(dir, fmt.Sprintf("%d.test", i)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // create a purge barrier at 5
+ l, err := NewLock(path.Join(dir, fmt.Sprintf("%d.test", 5)))
+ err = l.Lock()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stop := make(chan struct{})
+ errch := PurgeFile(dir, "test", 3, time.Millisecond, stop)
+ time.Sleep(20 * time.Millisecond)
+
+ fnames, err := ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wnames := []string{"5.test", "6.test", "7.test", "8.test", "9.test"}
+ if !reflect.DeepEqual(fnames, wnames) {
+ t.Errorf("filenames = %v, want %v", fnames, wnames)
+ }
+ select {
+ case err := <-errch:
+ t.Errorf("unexpected purge error %v", err)
+ case <-time.After(time.Millisecond):
+ }
+
+ // remove the purge barrier
+ err = l.Unlock()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = l.Destroy()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ time.Sleep(20 * time.Millisecond)
+
+ fnames, err = ReadDir(dir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wnames = []string{"7.test", "8.test", "9.test"}
+ if !reflect.DeepEqual(fnames, wnames) {
+ t.Errorf("filenames = %v, want %v", fnames, wnames)
+ }
+ select {
+ case err := <-errch:
+ t.Errorf("unexpected purge error %v", err)
+ case <-time.After(time.Millisecond):
+ }
+
+ close(stop)
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/httputil/cancelreq.go b/vendor/github.com/coreos/etcd/pkg/httputil/cancelreq.go
new file mode 100644
index 000000000..ebebcb3d9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/httputil/cancelreq.go
@@ -0,0 +1,20 @@
+// Copyright 2015 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.
+
+// borrowed from golang/net/context/ctxhttp/cancelreq.go
+
+// +build go1.5
+
+package httputil
+
+import "net/http"
+
+func RequestCanceler(rt http.RoundTripper, req *http.Request) func() {
+ ch := make(chan struct{})
+ req.Cancel = ch
+
+ return func() {
+ close(ch)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/httputil/cancelreq_go14.go b/vendor/github.com/coreos/etcd/pkg/httputil/cancelreq_go14.go
new file mode 100644
index 000000000..b270f9212
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/httputil/cancelreq_go14.go
@@ -0,0 +1,25 @@
+// Copyright 2015 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.
+
+// borrowed from golang/net/context/ctxhttp/cancelreq_go14.go
+
+// +build !go1.5
+
+package httputil
+
+import "net/http"
+
+type requestCanceler interface {
+ CancelRequest(req *http.Request)
+}
+
+func RequestCanceler(rt http.RoundTripper, req *http.Request) func() {
+ c, ok := rt.(requestCanceler)
+ if !ok {
+ return func() {}
+ }
+ return func() {
+ c.CancelRequest(req)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/idutil/id.go b/vendor/github.com/coreos/etcd/pkg/idutil/id.go
new file mode 100644
index 000000000..02f3aa866
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/idutil/id.go
@@ -0,0 +1,73 @@
+// Copyright 2015 CoreOS, 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 idutil
+
+import (
+ "math"
+ "sync"
+ "time"
+)
+
+const (
+ tsLen = 5 * 8
+ cntLen = 2 * 8
+ suffixLen = tsLen + cntLen
+)
+
+// The initial id is in this format:
+// High order byte is memberID, next 5 bytes are from timestamp,
+// and low order 2 bytes are 0s.
+// | prefix | suffix |
+// | 1 byte | 5 bytes | 2 bytes |
+// | memberID | timestamp | cnt |
+//
+// The timestamp 5 bytes is different when the machine is restart
+// after 1 ms and before 35 years.
+//
+// It increases suffix to generate the next id.
+// The count field may overflow to timestamp field, which is intentional.
+// It helps to extend the event window to 2^56. This doesn't break that
+// id generated after restart is unique because etcd throughput is <<
+// 65536req/ms.
+type Generator struct {
+ mu sync.Mutex
+ // high order byte
+ prefix uint64
+ // low order 7 bytes
+ suffix uint64
+}
+
+func NewGenerator(memberID uint8, now time.Time) *Generator {
+ prefix := uint64(memberID) << suffixLen
+ unixMilli := uint64(now.UnixNano()) / uint64(time.Millisecond/time.Nanosecond)
+ suffix := lowbit(unixMilli, tsLen) << cntLen
+ return &Generator{
+ prefix: prefix,
+ suffix: suffix,
+ }
+}
+
+// Next generates a id that is unique.
+func (g *Generator) Next() uint64 {
+ g.mu.Lock()
+ defer g.mu.Unlock()
+ g.suffix++
+ id := g.prefix | lowbit(g.suffix, suffixLen)
+ return id
+}
+
+func lowbit(x uint64, n uint) uint64 {
+ return x & (math.MaxUint64 >> (64 - n))
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/idutil/id_test.go b/vendor/github.com/coreos/etcd/pkg/idutil/id_test.go
new file mode 100644
index 000000000..b5249883f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/idutil/id_test.go
@@ -0,0 +1,55 @@
+// Copyright 2015 CoreOS, 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 idutil
+
+import (
+ "testing"
+ "time"
+)
+
+func TestNewGenerator(t *testing.T) {
+ g := NewGenerator(0x12, time.Unix(0, 0).Add(0x3456*time.Millisecond))
+ id := g.Next()
+ wid := uint64(0x1200000034560001)
+ if id != wid {
+ t.Errorf("id = %x, want %x", id, wid)
+ }
+}
+
+func TestNewGeneratorUnique(t *testing.T) {
+ g := NewGenerator(0, time.Time{})
+ id := g.Next()
+ // different server generates different ID
+ g1 := NewGenerator(1, time.Time{})
+ if gid := g1.Next(); id == gid {
+ t.Errorf("generate the same id %x using different server ID", id)
+ }
+ // restarted server generates different ID
+ g2 := NewGenerator(0, time.Now())
+ if gid := g2.Next(); id == gid {
+ t.Errorf("generate the same id %x after restart", id)
+ }
+}
+
+func TestNext(t *testing.T) {
+ g := NewGenerator(0x12, time.Unix(0, 0).Add(0x3456*time.Millisecond))
+ wid := uint64(0x1200000034560001)
+ for i := 0; i < 1000; i++ {
+ id := g.Next()
+ if id != wid+uint64(i) {
+ t.Errorf("id = %x, want %x", id, wid+uint64(i))
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/ioutil/reader.go b/vendor/github.com/coreos/etcd/pkg/ioutil/reader.go
new file mode 100644
index 000000000..412f3915a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/ioutil/reader.go
@@ -0,0 +1,39 @@
+// Copyright 2015 CoreOS, 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 ioutil
+
+import "io"
+
+// NewLimitedBufferReader returns a reader that reads from the given reader
+// but limits the amount of data returned to at most n bytes.
+func NewLimitedBufferReader(r io.Reader, n int) io.Reader {
+ return &limitedBufferReader{
+ r: r,
+ n: n,
+ }
+}
+
+type limitedBufferReader struct {
+ r io.Reader
+ n int
+}
+
+func (r *limitedBufferReader) Read(p []byte) (n int, err error) {
+ np := p
+ if len(np) > r.n {
+ np = np[:r.n]
+ }
+ return r.r.Read(np)
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/ioutil/reader_test.go b/vendor/github.com/coreos/etcd/pkg/ioutil/reader_test.go
new file mode 100644
index 000000000..5f7c97b0f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/ioutil/reader_test.go
@@ -0,0 +1,33 @@
+// Copyright 2015 CoreOS, 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 ioutil
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestLimitedBufferReaderRead(t *testing.T) {
+ buf := bytes.NewBuffer(make([]byte, 10))
+ ln := 1
+ lr := NewLimitedBufferReader(buf, ln)
+ n, err := lr.Read(make([]byte, 10))
+ if err != nil {
+ t.Fatalf("unexpected read error: %v", err)
+ }
+ if n != ln {
+ t.Errorf("len(data read) = %d, want %d", n, ln)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/netutil/isolate_linux.go b/vendor/github.com/coreos/etcd/pkg/netutil/isolate_linux.go
new file mode 100644
index 000000000..31727a83f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/netutil/isolate_linux.go
@@ -0,0 +1,42 @@
+// Copyright 2015 CoreOS, 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 netutil
+
+import (
+ "fmt"
+ "os/exec"
+)
+
+// DropPort drops all tcp packets that are received from the given port and sent to the given port.
+func DropPort(port int) error {
+ cmdStr := fmt.Sprintf("sudo iptables -A OUTPUT -p tcp --destination-port %d -j DROP", port)
+ if _, err := exec.Command("/bin/sh", "-c", cmdStr).Output(); err != nil {
+ return err
+ }
+ cmdStr = fmt.Sprintf("sudo iptables -A INPUT -p tcp --destination-port %d -j DROP", port)
+ _, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
+ return err
+}
+
+// RecoverPort stops dropping tcp packets at given port.
+func RecoverPort(port int) error {
+ cmdStr := fmt.Sprintf("sudo iptables -D OUTPUT -p tcp --destination-port %d -j DROP", port)
+ if _, err := exec.Command("/bin/sh", "-c", cmdStr).Output(); err != nil {
+ return err
+ }
+ cmdStr = fmt.Sprintf("sudo iptables -D INPUT -p tcp --destination-port %d -j DROP", port)
+ _, err := exec.Command("/bin/sh", "-c", cmdStr).Output()
+ return err
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/netutil/isolate_stub.go b/vendor/github.com/coreos/etcd/pkg/netutil/isolate_stub.go
new file mode 100644
index 000000000..c8f70f9b6
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/netutil/isolate_stub.go
@@ -0,0 +1,21 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build !linux
+
+package netutil
+
+func DropPort(port int) error { return nil }
+
+func RecoverPort(port int) error { return nil }
diff --git a/vendor/github.com/coreos/etcd/pkg/netutil/netutil.go b/vendor/github.com/coreos/etcd/pkg/netutil/netutil.go
new file mode 100644
index 000000000..62aacf2c3
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/netutil/netutil.go
@@ -0,0 +1,117 @@
+// Copyright 2015 CoreOS, 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 netutil
+
+import (
+ "net"
+ "net/url"
+ "reflect"
+ "sort"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ "github.com/coreos/etcd/pkg/types"
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "netutil")
+
+ // indirection for testing
+ resolveTCPAddr = net.ResolveTCPAddr
+)
+
+// resolveTCPAddrs is a convenience wrapper for net.ResolveTCPAddr.
+// resolveTCPAddrs return a new set of url.URLs, in which all DNS hostnames
+// are resolved.
+func resolveTCPAddrs(urls [][]url.URL) ([][]url.URL, error) {
+ newurls := make([][]url.URL, 0)
+ for _, us := range urls {
+ nus := make([]url.URL, len(us))
+ for i, u := range us {
+ nu, err := url.Parse(u.String())
+ if err != nil {
+ return nil, err
+ }
+ nus[i] = *nu
+ }
+ for i, u := range nus {
+ host, _, err := net.SplitHostPort(u.Host)
+ if err != nil {
+ plog.Errorf("could not parse url %s during tcp resolving", u.Host)
+ return nil, err
+ }
+ if host == "localhost" {
+ continue
+ }
+ if net.ParseIP(host) != nil {
+ continue
+ }
+ tcpAddr, err := resolveTCPAddr("tcp", u.Host)
+ if err != nil {
+ plog.Errorf("could not resolve host %s", u.Host)
+ return nil, err
+ }
+ plog.Infof("resolving %s to %s", u.Host, tcpAddr.String())
+ nus[i].Host = tcpAddr.String()
+ }
+ newurls = append(newurls, nus)
+ }
+ return newurls, nil
+}
+
+// urlsEqual checks equality of url.URLS between two arrays.
+// This check pass even if an URL is in hostname and opposite is in IP address.
+func urlsEqual(a []url.URL, b []url.URL) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ urls, err := resolveTCPAddrs([][]url.URL{a, b})
+ if err != nil {
+ return false
+ }
+ a, b = urls[0], urls[1]
+ sort.Sort(types.URLs(a))
+ sort.Sort(types.URLs(b))
+ for i := range a {
+ if !reflect.DeepEqual(a[i], b[i]) {
+ return false
+ }
+ }
+
+ return true
+}
+
+func URLStringsEqual(a []string, b []string) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ urlsA := make([]url.URL, 0)
+ for _, str := range a {
+ u, err := url.Parse(str)
+ if err != nil {
+ return false
+ }
+ urlsA = append(urlsA, *u)
+ }
+ urlsB := make([]url.URL, 0)
+ for _, str := range b {
+ u, err := url.Parse(str)
+ if err != nil {
+ return false
+ }
+ urlsB = append(urlsB, *u)
+ }
+
+ return urlsEqual(urlsA, urlsB)
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/netutil/netutil_test.go b/vendor/github.com/coreos/etcd/pkg/netutil/netutil_test.go
new file mode 100644
index 000000000..9ac84a8d2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/netutil/netutil_test.go
@@ -0,0 +1,258 @@
+// Copyright 2015 CoreOS, 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 netutil
+
+import (
+ "errors"
+ "net"
+ "net/url"
+ "reflect"
+ "strconv"
+ "testing"
+)
+
+func TestResolveTCPAddrs(t *testing.T) {
+ defer func() { resolveTCPAddr = net.ResolveTCPAddr }()
+ tests := []struct {
+ urls [][]url.URL
+ expected [][]url.URL
+ hostMap map[string]string
+ hasError bool
+ }{
+ {
+ urls: [][]url.URL{
+ {
+ {Scheme: "http", Host: "127.0.0.1:4001"},
+ {Scheme: "http", Host: "127.0.0.1:2379"},
+ },
+ {
+ {Scheme: "http", Host: "127.0.0.1:7001"},
+ {Scheme: "http", Host: "127.0.0.1:2380"},
+ },
+ },
+ expected: [][]url.URL{
+ {
+ {Scheme: "http", Host: "127.0.0.1:4001"},
+ {Scheme: "http", Host: "127.0.0.1:2379"},
+ },
+ {
+ {Scheme: "http", Host: "127.0.0.1:7001"},
+ {Scheme: "http", Host: "127.0.0.1:2380"},
+ },
+ },
+ },
+ {
+ urls: [][]url.URL{
+ {
+ {Scheme: "http", Host: "infra0.example.com:4001"},
+ {Scheme: "http", Host: "infra0.example.com:2379"},
+ },
+ {
+ {Scheme: "http", Host: "infra0.example.com:7001"},
+ {Scheme: "http", Host: "infra0.example.com:2380"},
+ },
+ },
+ expected: [][]url.URL{
+ {
+ {Scheme: "http", Host: "10.0.1.10:4001"},
+ {Scheme: "http", Host: "10.0.1.10:2379"},
+ },
+ {
+ {Scheme: "http", Host: "10.0.1.10:7001"},
+ {Scheme: "http", Host: "10.0.1.10:2380"},
+ },
+ },
+ hostMap: map[string]string{
+ "infra0.example.com": "10.0.1.10",
+ },
+ hasError: false,
+ },
+ {
+ urls: [][]url.URL{
+ {
+ {Scheme: "http", Host: "infra0.example.com:4001"},
+ {Scheme: "http", Host: "infra0.example.com:2379"},
+ },
+ {
+ {Scheme: "http", Host: "infra0.example.com:7001"},
+ {Scheme: "http", Host: "infra0.example.com:2380"},
+ },
+ },
+ hostMap: map[string]string{
+ "infra0.example.com": "",
+ },
+ hasError: true,
+ },
+ {
+ urls: [][]url.URL{
+ {
+ {Scheme: "http", Host: "ssh://infra0.example.com:4001"},
+ {Scheme: "http", Host: "ssh://infra0.example.com:2379"},
+ },
+ {
+ {Scheme: "http", Host: "ssh://infra0.example.com:7001"},
+ {Scheme: "http", Host: "ssh://infra0.example.com:2380"},
+ },
+ },
+ hasError: true,
+ },
+ }
+ for _, tt := range tests {
+ resolveTCPAddr = func(network, addr string) (*net.TCPAddr, error) {
+ host, port, err := net.SplitHostPort(addr)
+ if err != nil {
+ return nil, err
+ }
+ if tt.hostMap[host] == "" {
+ return nil, errors.New("cannot resolve host.")
+ }
+ i, err := strconv.Atoi(port)
+ if err != nil {
+ return nil, err
+ }
+ return &net.TCPAddr{IP: net.ParseIP(tt.hostMap[host]), Port: i, Zone: ""}, nil
+ }
+ urls, err := resolveTCPAddrs(tt.urls)
+ if tt.hasError {
+ if err == nil {
+ t.Errorf("expected error")
+ }
+ continue
+ }
+ if !reflect.DeepEqual(urls, tt.expected) {
+ t.Errorf("expected: %v, got %v", tt.expected, urls)
+ }
+ }
+}
+
+func TestURLsEqual(t *testing.T) {
+ defer func() { resolveTCPAddr = net.ResolveTCPAddr }()
+ hostm := map[string]string{
+ "example.com": "10.0.10.1",
+ "first.com": "10.0.11.1",
+ "second.com": "10.0.11.2",
+ }
+ resolveTCPAddr = func(network, addr string) (*net.TCPAddr, error) {
+ host, port, err := net.SplitHostPort(addr)
+ if _, ok := hostm[host]; !ok {
+ return nil, errors.New("cannot resolve host.")
+ }
+ i, err := strconv.Atoi(port)
+ if err != nil {
+ return nil, err
+ }
+ return &net.TCPAddr{IP: net.ParseIP(hostm[host]), Port: i, Zone: ""}, nil
+ }
+
+ tests := []struct {
+ a []url.URL
+ b []url.URL
+ expect bool
+ }{
+ {
+ a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
+ expect: true,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "example.com:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}},
+ expect: true,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: true,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: true,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: true,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "example.com:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.10.1:2379"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "example.com:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2380"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "127.0.0.1:2380"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "127.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "example.com:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.0.1:2379"}, {Scheme: "http", Host: "127.0.0.1:2380"}},
+ expect: false,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "first.com:2379"}, {Scheme: "http", Host: "second.com:2380"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.11.1:2379"}, {Scheme: "http", Host: "10.0.11.2:2380"}},
+ expect: true,
+ },
+ {
+ a: []url.URL{{Scheme: "http", Host: "second.com:2380"}, {Scheme: "http", Host: "first.com:2379"}},
+ b: []url.URL{{Scheme: "http", Host: "10.0.11.1:2379"}, {Scheme: "http", Host: "10.0.11.2:2380"}},
+ expect: true,
+ },
+ }
+
+ for _, test := range tests {
+ result := urlsEqual(test.a, test.b)
+ if result != test.expect {
+ t.Errorf("a:%v b:%v, expected %v but %v", test.a, test.b, test.expect, result)
+ }
+ }
+}
+func TestURLStringsEqual(t *testing.T) {
+ result := URLStringsEqual([]string{"http://127.0.0.1:8080"}, []string{"http://127.0.0.1:8080"})
+ if !result {
+ t.Errorf("unexpected result %v", result)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/pathutil/path.go b/vendor/github.com/coreos/etcd/pkg/pathutil/path.go
new file mode 100644
index 000000000..82fd1db39
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/pathutil/path.go
@@ -0,0 +1,29 @@
+// Copyright 2009 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.
+
+package pathutil
+
+import "path"
+
+// CanonicalURLPath returns the canonical url path for p, which follows the rules:
+// 1. the path always starts with "/"
+// 2. replace multiple slashes with a single slash
+// 3. replace each '.' '..' path name element with equivalent one
+// 4. keep the trailing slash
+// The function is borrowed from stdlib http.cleanPath in server.go.
+func CanonicalURLPath(p string) string {
+ if p == "" {
+ return "/"
+ }
+ if p[0] != '/' {
+ p = "/" + p
+ }
+ np := path.Clean(p)
+ // path.Clean removes trailing slash except for root,
+ // put the trailing slash back if necessary.
+ if p[len(p)-1] == '/' && np != "/" {
+ np += "/"
+ }
+ return np
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/pathutil/path_test.go b/vendor/github.com/coreos/etcd/pkg/pathutil/path_test.go
new file mode 100644
index 000000000..6d3d803cf
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/pathutil/path_test.go
@@ -0,0 +1,38 @@
+// Copyright 2015 CoreOS, 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 pathutil
+
+import "testing"
+
+func TestCanonicalURLPath(t *testing.T) {
+ tests := []struct {
+ p string
+ wp string
+ }{
+ {"/a", "/a"},
+ {"", "/"},
+ {"a", "/a"},
+ {"//a", "/a"},
+ {"/a/.", "/a"},
+ {"/a/..", "/"},
+ {"/a/", "/a/"},
+ {"/a//", "/a/"},
+ }
+ for i, tt := range tests {
+ if g := CanonicalURLPath(tt.p); g != tt.wp {
+ t.Errorf("#%d: canonical path = %s, want %s", i, g, tt.wp)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/pbutil/pbutil.go b/vendor/github.com/coreos/etcd/pkg/pbutil/pbutil.go
new file mode 100644
index 000000000..66693a2f2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/pbutil/pbutil.go
@@ -0,0 +1,59 @@
+// Copyright 2015 CoreOS, 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 pbutil
+
+import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd/pkg", "flags")
+)
+
+type Marshaler interface {
+ Marshal() (data []byte, err error)
+}
+
+type Unmarshaler interface {
+ Unmarshal(data []byte) error
+}
+
+func MustMarshal(m Marshaler) []byte {
+ d, err := m.Marshal()
+ if err != nil {
+ plog.Panicf("marshal should never fail (%v)", err)
+ }
+ return d
+}
+
+func MustUnmarshal(um Unmarshaler, data []byte) {
+ if err := um.Unmarshal(data); err != nil {
+ plog.Panicf("unmarshal should never fail (%v)", err)
+ }
+}
+
+func MaybeUnmarshal(um Unmarshaler, data []byte) bool {
+ if err := um.Unmarshal(data); err != nil {
+ return false
+ }
+ return true
+}
+
+func GetBool(v *bool) (vv bool, set bool) {
+ if v == nil {
+ return false, false
+ }
+ return *v, true
+}
+
+func Boolp(b bool) *bool { return &b }
diff --git a/vendor/github.com/coreos/etcd/pkg/pbutil/pbutil_test.go b/vendor/github.com/coreos/etcd/pkg/pbutil/pbutil_test.go
new file mode 100644
index 000000000..79d48f85c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/pbutil/pbutil_test.go
@@ -0,0 +1,98 @@
+// Copyright 2015 CoreOS, 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 pbutil
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+)
+
+func TestMarshaler(t *testing.T) {
+ data := []byte("test data")
+ m := &fakeMarshaler{data: data}
+ if g := MustMarshal(m); !reflect.DeepEqual(g, data) {
+ t.Errorf("data = %s, want %s", g, m)
+ }
+}
+
+func TestMarshalerPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("recover = nil, want error")
+ }
+ }()
+ m := &fakeMarshaler{err: errors.New("blah")}
+ MustMarshal(m)
+}
+
+func TestUnmarshaler(t *testing.T) {
+ data := []byte("test data")
+ m := &fakeUnmarshaler{}
+ MustUnmarshal(m, data)
+ if !reflect.DeepEqual(m.data, data) {
+ t.Errorf("data = %s, want %s", m.data, m)
+ }
+}
+
+func TestUnmarshalerPanic(t *testing.T) {
+ defer func() {
+ if r := recover(); r == nil {
+ t.Errorf("recover = nil, want error")
+ }
+ }()
+ m := &fakeUnmarshaler{err: errors.New("blah")}
+ MustUnmarshal(m, nil)
+}
+
+func TestGetBool(t *testing.T) {
+ tests := []struct {
+ b *bool
+ wb bool
+ wset bool
+ }{
+ {nil, false, false},
+ {Boolp(true), true, true},
+ {Boolp(false), false, true},
+ }
+ for i, tt := range tests {
+ b, set := GetBool(tt.b)
+ if b != tt.wb {
+ t.Errorf("#%d: value = %v, want %v", i, b, tt.wb)
+ }
+ if set != tt.wset {
+ t.Errorf("#%d: set = %v, want %v", i, set, tt.wset)
+ }
+ }
+}
+
+type fakeMarshaler struct {
+ data []byte
+ err error
+}
+
+func (m *fakeMarshaler) Marshal() ([]byte, error) {
+ return m.data, m.err
+}
+
+type fakeUnmarshaler struct {
+ data []byte
+ err error
+}
+
+func (m *fakeUnmarshaler) Unmarshal(data []byte) error {
+ m.data = data
+ return m.err
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/runtime/fds_linux.go b/vendor/github.com/coreos/etcd/pkg/runtime/fds_linux.go
new file mode 100644
index 000000000..85297c09d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/runtime/fds_linux.go
@@ -0,0 +1,36 @@
+// Copyright 2015 CoreOS, 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 runtime
+
+import (
+ "io/ioutil"
+ "syscall"
+)
+
+func FDLimit() (uint64, error) {
+ var rlimit syscall.Rlimit
+ if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil {
+ return 0, err
+ }
+ return rlimit.Cur, nil
+}
+
+func FDUsage() (uint64, error) {
+ fds, err := ioutil.ReadDir("/proc/self/fd")
+ if err != nil {
+ return 0, err
+ }
+ return uint64(len(fds)), nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/runtime/fds_other.go b/vendor/github.com/coreos/etcd/pkg/runtime/fds_other.go
new file mode 100644
index 000000000..0ed152f0f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/runtime/fds_other.go
@@ -0,0 +1,30 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build !linux
+
+package runtime
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func FDLimit() (uint64, error) {
+ return 0, fmt.Errorf("cannot get FDLimit on %s", runtime.GOOS)
+}
+
+func FDUsage() (uint64, error) {
+ return 0, fmt.Errorf("cannot get FDUsage on %s", runtime.GOOS)
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/timeutil/timeutil.go b/vendor/github.com/coreos/etcd/pkg/timeutil/timeutil.go
new file mode 100644
index 000000000..58da59397
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/timeutil/timeutil.go
@@ -0,0 +1,27 @@
+// Copyright 2015 CoreOS, 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 timeutil
+
+import "time"
+
+// UnixNanoToTime returns the local time corresponding to the given Unix time in nanoseconds.
+// If the given Unix time is zero, an uninitialized zero time is returned.
+func UnixNanoToTime(ns int64) time.Time {
+ var t time.Time
+ if ns != 0 {
+ t = time.Unix(0, ns)
+ }
+ return t
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/timeutil/timeutil_test.go b/vendor/github.com/coreos/etcd/pkg/timeutil/timeutil_test.go
new file mode 100644
index 000000000..42a84131b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/timeutil/timeutil_test.go
@@ -0,0 +1,48 @@
+// Copyright 2015 CoreOS, 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 timeutil
+
+import (
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestUnixNanoToTime(t *testing.T) {
+ tests := []struct {
+ ns int64
+ want time.Time
+ }{
+ {
+ 0,
+ time.Time{},
+ },
+ {
+ 60000,
+ time.Unix(0, 60000),
+ },
+ {
+ -60000,
+ time.Unix(0, -60000),
+ },
+ }
+
+ for i, tt := range tests {
+ got := UnixNanoToTime(tt.ns)
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("#%d: time = %v, want %v", i, got, tt.want)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener.go b/vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener.go
new file mode 100644
index 000000000..6f580619a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener.go
@@ -0,0 +1,97 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "crypto/tls"
+ "fmt"
+ "net"
+ "time"
+)
+
+// NewKeepAliveListener returns a listener that listens on the given address.
+// http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
+func NewKeepAliveListener(addr string, scheme string, info TLSInfo) (net.Listener, error) {
+ l, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+
+ if scheme == "https" {
+ if info.Empty() {
+ return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
+ }
+ cfg, err := info.ServerConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ return newTLSKeepaliveListener(l, cfg), nil
+ }
+
+ return &keepaliveListener{
+ Listener: l,
+ }, nil
+}
+
+type keepaliveListener struct{ net.Listener }
+
+func (kln *keepaliveListener) Accept() (net.Conn, error) {
+ c, err := kln.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ tcpc := c.(*net.TCPConn)
+ // detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
+ // default on linux: 30 + 8 * 30
+ // default on osx: 30 + 8 * 75
+ tcpc.SetKeepAlive(true)
+ tcpc.SetKeepAlivePeriod(30 * time.Second)
+ return tcpc, nil
+}
+
+// A tlsKeepaliveListener implements a network listener (net.Listener) for TLS connections.
+type tlsKeepaliveListener struct {
+ net.Listener
+ config *tls.Config
+}
+
+// Accept waits for and returns the next incoming TLS connection.
+// The returned connection c is a *tls.Conn.
+func (l *tlsKeepaliveListener) Accept() (c net.Conn, err error) {
+ c, err = l.Listener.Accept()
+ if err != nil {
+ return
+ }
+ tcpc := c.(*net.TCPConn)
+ // detection time: tcp_keepalive_time + tcp_keepalive_probes + tcp_keepalive_intvl
+ // default on linux: 30 + 8 * 30
+ // default on osx: 30 + 8 * 75
+ tcpc.SetKeepAlive(true)
+ tcpc.SetKeepAlivePeriod(30 * time.Second)
+ c = tls.Server(c, l.config)
+ return
+}
+
+// NewListener creates a Listener which accepts connections from an inner
+// Listener and wraps each connection with Server.
+// The configuration config must be non-nil and must have
+// at least one certificate.
+func newTLSKeepaliveListener(inner net.Listener, config *tls.Config) net.Listener {
+ l := &tlsKeepaliveListener{}
+ l.Listener = inner
+ l.config = config
+ return l
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener_test.go b/vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener_test.go
new file mode 100644
index 000000000..b8317dc93
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/keepalive_listener_test.go
@@ -0,0 +1,71 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "crypto/tls"
+ "net/http"
+ "os"
+ "testing"
+)
+
+// TestNewKeepAliveListener tests NewKeepAliveListener returns a listener
+// that accepts connections.
+// TODO: verify the keepalive option is set correctly
+func TestNewKeepAliveListener(t *testing.T) {
+ ln, err := NewKeepAliveListener("127.0.0.1:0", "http", TLSInfo{})
+ if err != nil {
+ t.Fatalf("unexpected NewKeepAliveListener error: %v", err)
+ }
+
+ go http.Get("http://" + ln.Addr().String())
+ conn, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("unexpected Accept error: %v", err)
+ }
+ conn.Close()
+ ln.Close()
+
+ // tls
+ tmp, err := createTempFile([]byte("XXX"))
+ if err != nil {
+ t.Fatalf("unable to create tmpfile: %v", err)
+ }
+ defer os.Remove(tmp)
+ tlsInfo := TLSInfo{CertFile: tmp, KeyFile: tmp}
+ tlsInfo.parseFunc = fakeCertificateParserFunc(tls.Certificate{}, nil)
+ tlsln, err := NewKeepAliveListener("127.0.0.1:0", "https", tlsInfo)
+ if err != nil {
+ t.Fatalf("unexpected NewKeepAliveListener error: %v", err)
+ }
+
+ go http.Get("https://" + tlsln.Addr().String())
+ conn, err = tlsln.Accept()
+ if err != nil {
+ t.Fatalf("unexpected Accept error: %v", err)
+ }
+ if _, ok := conn.(*tls.Conn); !ok {
+ t.Errorf("failed to accept *tls.Conn")
+ }
+ conn.Close()
+ tlsln.Close()
+}
+
+func TestNewKeepAliveListenerTLSEmptyInfo(t *testing.T) {
+ _, err := NewListener("127.0.0.1:0", "https", TLSInfo{})
+ if err == nil {
+ t.Errorf("err = nil, want not presented error")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/listener.go b/vendor/github.com/coreos/etcd/pkg/transport/listener.go
new file mode 100644
index 000000000..de7b9f0b2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/listener.go
@@ -0,0 +1,207 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "time"
+)
+
+func NewListener(addr string, scheme string, info TLSInfo) (net.Listener, error) {
+ l, err := net.Listen("tcp", addr)
+ if err != nil {
+ return nil, err
+ }
+
+ if scheme == "https" {
+ if info.Empty() {
+ return nil, fmt.Errorf("cannot listen on TLS for %s: KeyFile and CertFile are not presented", scheme+"://"+addr)
+ }
+ cfg, err := info.ServerConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ l = tls.NewListener(l, cfg)
+ }
+
+ return l, nil
+}
+
+func NewTransport(info TLSInfo, dialtimeoutd time.Duration) (*http.Transport, error) {
+ cfg, err := info.ClientConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ t := &http.Transport{
+ Dial: (&net.Dialer{
+ Timeout: dialtimeoutd,
+ // value taken from http.DefaultTransport
+ KeepAlive: 30 * time.Second,
+ }).Dial,
+ // value taken from http.DefaultTransport
+ TLSHandshakeTimeout: 10 * time.Second,
+ TLSClientConfig: cfg,
+ }
+
+ return t, nil
+}
+
+type TLSInfo struct {
+ CertFile string
+ KeyFile string
+ CAFile string
+ TrustedCAFile string
+ ClientCertAuth bool
+
+ // parseFunc exists to simplify testing. Typically, parseFunc
+ // should be left nil. In that case, tls.X509KeyPair will be used.
+ parseFunc func([]byte, []byte) (tls.Certificate, error)
+}
+
+func (info TLSInfo) String() string {
+ return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth)
+}
+
+func (info TLSInfo) Empty() bool {
+ return info.CertFile == "" && info.KeyFile == ""
+}
+
+func (info TLSInfo) baseConfig() (*tls.Config, error) {
+ if info.KeyFile == "" || info.CertFile == "" {
+ return nil, fmt.Errorf("KeyFile and CertFile must both be present[key: %v, cert: %v]", info.KeyFile, info.CertFile)
+ }
+
+ cert, err := ioutil.ReadFile(info.CertFile)
+ if err != nil {
+ return nil, err
+ }
+
+ key, err := ioutil.ReadFile(info.KeyFile)
+ if err != nil {
+ return nil, err
+ }
+
+ parseFunc := info.parseFunc
+ if parseFunc == nil {
+ parseFunc = tls.X509KeyPair
+ }
+
+ tlsCert, err := parseFunc(cert, key)
+ if err != nil {
+ return nil, err
+ }
+
+ cfg := &tls.Config{
+ Certificates: []tls.Certificate{tlsCert},
+ MinVersion: tls.VersionTLS10,
+ }
+ return cfg, nil
+}
+
+// cafiles returns a list of CA file paths.
+func (info TLSInfo) cafiles() []string {
+ cs := make([]string, 0)
+ if info.CAFile != "" {
+ cs = append(cs, info.CAFile)
+ }
+ if info.TrustedCAFile != "" {
+ cs = append(cs, info.TrustedCAFile)
+ }
+ return cs
+}
+
+// ServerConfig generates a tls.Config object for use by an HTTP server.
+func (info TLSInfo) ServerConfig() (*tls.Config, error) {
+ cfg, err := info.baseConfig()
+ if err != nil {
+ return nil, err
+ }
+
+ cfg.ClientAuth = tls.NoClientCert
+ if info.CAFile != "" || info.ClientCertAuth {
+ cfg.ClientAuth = tls.RequireAndVerifyClientCert
+ }
+
+ CAFiles := info.cafiles()
+ if len(CAFiles) > 0 {
+ cp, err := newCertPool(CAFiles)
+ if err != nil {
+ return nil, err
+ }
+ cfg.ClientCAs = cp
+ }
+
+ return cfg, nil
+}
+
+// ClientConfig generates a tls.Config object for use by an HTTP client.
+func (info TLSInfo) ClientConfig() (*tls.Config, error) {
+ var cfg *tls.Config
+ var err error
+
+ if !info.Empty() {
+ cfg, err = info.baseConfig()
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ cfg = &tls.Config{}
+ }
+
+ CAFiles := info.cafiles()
+ if len(CAFiles) > 0 {
+ cfg.RootCAs, err = newCertPool(CAFiles)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return cfg, nil
+}
+
+// newCertPool creates x509 certPool with provided CA files.
+func newCertPool(CAFiles []string) (*x509.CertPool, error) {
+ certPool := x509.NewCertPool()
+
+ for _, CAFile := range CAFiles {
+ pemByte, err := ioutil.ReadFile(CAFile)
+ if err != nil {
+ return nil, err
+ }
+
+ for {
+ var block *pem.Block
+ block, pemByte = pem.Decode(pemByte)
+ if block == nil {
+ break
+ }
+ cert, err := x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, err
+ }
+ certPool.AddCert(cert)
+ }
+ }
+
+ return certPool, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/listener_test.go b/vendor/github.com/coreos/etcd/pkg/transport/listener_test.go
new file mode 100644
index 000000000..e2acbdd1e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/listener_test.go
@@ -0,0 +1,243 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "crypto/tls"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "testing"
+ "time"
+)
+
+func createTempFile(b []byte) (string, error) {
+ f, err := ioutil.TempFile("", "etcd-test-tls-")
+ if err != nil {
+ return "", err
+ }
+ defer f.Close()
+
+ if _, err = f.Write(b); err != nil {
+ return "", err
+ }
+
+ return f.Name(), nil
+}
+
+func fakeCertificateParserFunc(cert tls.Certificate, err error) func(certPEMBlock, keyPEMBlock []byte) (tls.Certificate, error) {
+ return func(certPEMBlock, keyPEMBlock []byte) (tls.Certificate, error) {
+ return cert, err
+ }
+}
+
+// TestNewListenerTLSInfo tests that NewListener with valid TLSInfo returns
+// a TLS listerner that accepts TLS connections.
+func TestNewListenerTLSInfo(t *testing.T) {
+ tmp, err := createTempFile([]byte("XXX"))
+ if err != nil {
+ t.Fatalf("unable to create tmpfile: %v", err)
+ }
+ defer os.Remove(tmp)
+ tlsInfo := TLSInfo{CertFile: tmp, KeyFile: tmp}
+ tlsInfo.parseFunc = fakeCertificateParserFunc(tls.Certificate{}, nil)
+ ln, err := NewListener("127.0.0.1:0", "https", tlsInfo)
+ if err != nil {
+ t.Fatalf("unexpected NewListener error: %v", err)
+ }
+ defer ln.Close()
+
+ go http.Get("https://" + ln.Addr().String())
+ conn, err := ln.Accept()
+ if err != nil {
+ t.Fatalf("unexpected Accept error: %v", err)
+ }
+ defer conn.Close()
+ if _, ok := conn.(*tls.Conn); !ok {
+ t.Errorf("failed to accept *tls.Conn")
+ }
+}
+
+func TestNewListenerTLSEmptyInfo(t *testing.T) {
+ _, err := NewListener("127.0.0.1:0", "https", TLSInfo{})
+ if err == nil {
+ t.Errorf("err = nil, want not presented error")
+ }
+}
+
+func TestNewListenerTLSInfoNonexist(t *testing.T) {
+ tlsInfo := TLSInfo{CertFile: "@badname", KeyFile: "@badname"}
+ _, err := NewListener("127.0.0.1:0", "https", tlsInfo)
+ werr := &os.PathError{
+ Op: "open",
+ Path: "@badname",
+ Err: errors.New("no such file or directory"),
+ }
+ if err.Error() != werr.Error() {
+ t.Errorf("err = %v, want %v", err, werr)
+ }
+}
+
+func TestNewTransportTLSInfo(t *testing.T) {
+ tmp, err := createTempFile([]byte("XXX"))
+ if err != nil {
+ t.Fatalf("Unable to prepare tmpfile: %v", err)
+ }
+ defer os.Remove(tmp)
+
+ tests := []TLSInfo{
+ {},
+ {
+ CertFile: tmp,
+ KeyFile: tmp,
+ },
+ {
+ CertFile: tmp,
+ KeyFile: tmp,
+ CAFile: tmp,
+ },
+ {
+ CAFile: tmp,
+ },
+ }
+
+ for i, tt := range tests {
+ tt.parseFunc = fakeCertificateParserFunc(tls.Certificate{}, nil)
+ trans, err := NewTransport(tt, time.Second)
+ if err != nil {
+ t.Fatalf("Received unexpected error from NewTransport: %v", err)
+ }
+
+ if trans.TLSClientConfig == nil {
+ t.Fatalf("#%d: want non-nil TLSClientConfig", i)
+ }
+ }
+}
+
+func TestTLSInfoEmpty(t *testing.T) {
+ tests := []struct {
+ info TLSInfo
+ want bool
+ }{
+ {TLSInfo{}, true},
+ {TLSInfo{CAFile: "baz"}, true},
+ {TLSInfo{CertFile: "foo"}, false},
+ {TLSInfo{KeyFile: "bar"}, false},
+ {TLSInfo{CertFile: "foo", KeyFile: "bar"}, false},
+ {TLSInfo{CertFile: "foo", CAFile: "baz"}, false},
+ {TLSInfo{KeyFile: "bar", CAFile: "baz"}, false},
+ {TLSInfo{CertFile: "foo", KeyFile: "bar", CAFile: "baz"}, false},
+ }
+
+ for i, tt := range tests {
+ got := tt.info.Empty()
+ if tt.want != got {
+ t.Errorf("#%d: result of Empty() incorrect: want=%t got=%t", i, tt.want, got)
+ }
+ }
+}
+
+func TestTLSInfoMissingFields(t *testing.T) {
+ tmp, err := createTempFile([]byte("XXX"))
+ if err != nil {
+ t.Fatalf("Unable to prepare tmpfile: %v", err)
+ }
+ defer os.Remove(tmp)
+
+ tests := []TLSInfo{
+ {CertFile: tmp},
+ {KeyFile: tmp},
+ {CertFile: tmp, CAFile: tmp},
+ {KeyFile: tmp, CAFile: tmp},
+ }
+
+ for i, info := range tests {
+ if _, err = info.ServerConfig(); err == nil {
+ t.Errorf("#%d: expected non-nil error from ServerConfig()", i)
+ }
+
+ if _, err = info.ClientConfig(); err == nil {
+ t.Errorf("#%d: expected non-nil error from ClientConfig()", i)
+ }
+ }
+}
+
+func TestTLSInfoParseFuncError(t *testing.T) {
+ tmp, err := createTempFile([]byte("XXX"))
+ if err != nil {
+ t.Fatalf("Unable to prepare tmpfile: %v", err)
+ }
+ defer os.Remove(tmp)
+
+ info := TLSInfo{CertFile: tmp, KeyFile: tmp, CAFile: tmp}
+ info.parseFunc = fakeCertificateParserFunc(tls.Certificate{}, errors.New("fake"))
+
+ if _, err = info.ServerConfig(); err == nil {
+ t.Errorf("expected non-nil error from ServerConfig()")
+ }
+
+ if _, err = info.ClientConfig(); err == nil {
+ t.Errorf("expected non-nil error from ClientConfig()")
+ }
+}
+
+func TestTLSInfoConfigFuncs(t *testing.T) {
+ tmp, err := createTempFile([]byte("XXX"))
+ if err != nil {
+ t.Fatalf("Unable to prepare tmpfile: %v", err)
+ }
+ defer os.Remove(tmp)
+
+ tests := []struct {
+ info TLSInfo
+ clientAuth tls.ClientAuthType
+ wantCAs bool
+ }{
+ {
+ info: TLSInfo{CertFile: tmp, KeyFile: tmp},
+ clientAuth: tls.NoClientCert,
+ wantCAs: false,
+ },
+
+ {
+ info: TLSInfo{CertFile: tmp, KeyFile: tmp, CAFile: tmp},
+ clientAuth: tls.RequireAndVerifyClientCert,
+ wantCAs: true,
+ },
+ }
+
+ for i, tt := range tests {
+ tt.info.parseFunc = fakeCertificateParserFunc(tls.Certificate{}, nil)
+
+ sCfg, err := tt.info.ServerConfig()
+ if err != nil {
+ t.Errorf("#%d: expected nil error from ServerConfig(), got non-nil: %v", i, err)
+ }
+
+ if tt.wantCAs != (sCfg.ClientCAs != nil) {
+ t.Errorf("#%d: wantCAs=%t but ClientCAs=%v", i, tt.wantCAs, sCfg.ClientCAs)
+ }
+
+ cCfg, err := tt.info.ClientConfig()
+ if err != nil {
+ t.Errorf("#%d: expected nil error from ClientConfig(), got non-nil: %v", i, err)
+ }
+
+ if tt.wantCAs != (cCfg.RootCAs != nil) {
+ t.Errorf("#%d: wantCAs=%t but RootCAs=%v", i, tt.wantCAs, sCfg.RootCAs)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_conn.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_conn.go
new file mode 100644
index 000000000..daca554e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_conn.go
@@ -0,0 +1,44 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "net"
+ "time"
+)
+
+type timeoutConn struct {
+ net.Conn
+ wtimeoutd time.Duration
+ rdtimeoutd time.Duration
+}
+
+func (c timeoutConn) Write(b []byte) (n int, err error) {
+ if c.wtimeoutd > 0 {
+ if err := c.SetWriteDeadline(time.Now().Add(c.wtimeoutd)); err != nil {
+ return 0, err
+ }
+ }
+ return c.Conn.Write(b)
+}
+
+func (c timeoutConn) Read(b []byte) (n int, err error) {
+ if c.rdtimeoutd > 0 {
+ if err := c.SetReadDeadline(time.Now().Add(c.rdtimeoutd)); err != nil {
+ return 0, err
+ }
+ }
+ return c.Conn.Read(b)
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer.go
new file mode 100644
index 000000000..9bd612980
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer.go
@@ -0,0 +1,36 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "net"
+ "time"
+)
+
+type rwTimeoutDialer struct {
+ wtimeoutd time.Duration
+ rdtimeoutd time.Duration
+ net.Dialer
+}
+
+func (d *rwTimeoutDialer) Dial(network, address string) (net.Conn, error) {
+ conn, err := d.Dialer.Dial(network, address)
+ tconn := &timeoutConn{
+ rdtimeoutd: d.rdtimeoutd,
+ wtimeoutd: d.wtimeoutd,
+ Conn: conn,
+ }
+ return tconn, err
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer_test.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer_test.go
new file mode 100644
index 000000000..14e95516c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_dialer_test.go
@@ -0,0 +1,102 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "net"
+ "testing"
+ "time"
+)
+
+func TestReadWriteTimeoutDialer(t *testing.T) {
+ stop := make(chan struct{})
+
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unexpected listen error: %v", err)
+ }
+ ts := testBlockingServer{ln, 2, stop}
+ go ts.Start(t)
+
+ d := rwTimeoutDialer{
+ wtimeoutd: 10 * time.Millisecond,
+ rdtimeoutd: 10 * time.Millisecond,
+ }
+ conn, err := d.Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("unexpected dial error: %v", err)
+ }
+ defer conn.Close()
+
+ // fill the socket buffer
+ data := make([]byte, 5*1024*1024)
+ done := make(chan struct{})
+ go func() {
+ _, err = conn.Write(data)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ // It waits 1s more to avoid delay in low-end system.
+ case <-time.After(d.wtimeoutd*10 + time.Second):
+ t.Fatal("wait timeout")
+ }
+
+ if operr, ok := err.(*net.OpError); !ok || operr.Op != "write" || !operr.Timeout() {
+ t.Errorf("err = %v, want write i/o timeout error", err)
+ }
+
+ conn, err = d.Dial("tcp", ln.Addr().String())
+ if err != nil {
+ t.Fatalf("unexpected dial error: %v", err)
+ }
+ defer conn.Close()
+
+ buf := make([]byte, 10)
+ go func() {
+ _, err = conn.Read(buf)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ case <-time.After(d.rdtimeoutd * 10):
+ t.Fatal("wait timeout")
+ }
+
+ if operr, ok := err.(*net.OpError); !ok || operr.Op != "read" || !operr.Timeout() {
+ t.Errorf("err = %v, want write i/o timeout error", err)
+ }
+
+ stop <- struct{}{}
+}
+
+type testBlockingServer struct {
+ ln net.Listener
+ n int
+ stop chan struct{}
+}
+
+func (ts *testBlockingServer) Start(t *testing.T) {
+ for i := 0; i < ts.n; i++ {
+ conn, err := ts.ln.Accept()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ }
+ <-ts.stop
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_listener.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_listener.go
new file mode 100644
index 000000000..6992a8eee
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_listener.go
@@ -0,0 +1,53 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "net"
+ "time"
+)
+
+// NewTimeoutListener returns a listener that listens on the given address.
+// If read/write on the accepted connection blocks longer than its time limit,
+// it will return timeout error.
+func NewTimeoutListener(addr string, scheme string, info TLSInfo, rdtimeoutd, wtimeoutd time.Duration) (net.Listener, error) {
+ ln, err := NewListener(addr, scheme, info)
+ if err != nil {
+ return nil, err
+ }
+ return &rwTimeoutListener{
+ Listener: ln,
+ rdtimeoutd: rdtimeoutd,
+ wtimeoutd: wtimeoutd,
+ }, nil
+}
+
+type rwTimeoutListener struct {
+ net.Listener
+ wtimeoutd time.Duration
+ rdtimeoutd time.Duration
+}
+
+func (rwln *rwTimeoutListener) Accept() (net.Conn, error) {
+ c, err := rwln.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+ return timeoutConn{
+ Conn: c,
+ wtimeoutd: rwln.wtimeoutd,
+ rdtimeoutd: rwln.rdtimeoutd,
+ }, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_listener_test.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_listener_test.go
new file mode 100644
index 000000000..085a57559
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_listener_test.go
@@ -0,0 +1,112 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "net"
+ "testing"
+ "time"
+)
+
+// TestNewTimeoutListener tests that NewTimeoutListener returns a
+// rwTimeoutListener struct with timeouts set.
+func TestNewTimeoutListener(t *testing.T) {
+ l, err := NewTimeoutListener("127.0.0.1:0", "http", TLSInfo{}, time.Hour, time.Hour)
+ if err != nil {
+ t.Fatalf("unexpected NewTimeoutListener error: %v", err)
+ }
+ defer l.Close()
+ tln := l.(*rwTimeoutListener)
+ if tln.rdtimeoutd != time.Hour {
+ t.Errorf("read timeout = %s, want %s", tln.rdtimeoutd, time.Hour)
+ }
+ if tln.wtimeoutd != time.Hour {
+ t.Errorf("write timeout = %s, want %s", tln.wtimeoutd, time.Hour)
+ }
+}
+
+func TestWriteReadTimeoutListener(t *testing.T) {
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ t.Fatalf("unexpected listen error: %v", err)
+ }
+ wln := rwTimeoutListener{
+ Listener: ln,
+ wtimeoutd: 10 * time.Millisecond,
+ rdtimeoutd: 10 * time.Millisecond,
+ }
+ stop := make(chan struct{})
+
+ blocker := func() {
+ conn, derr := net.Dial("tcp", ln.Addr().String())
+ if derr != nil {
+ t.Fatalf("unexpected dail error: %v", derr)
+ }
+ defer conn.Close()
+ // block the receiver until the writer timeout
+ <-stop
+ }
+ go blocker()
+
+ conn, err := wln.Accept()
+ if err != nil {
+ t.Fatalf("unexpected accept error: %v", err)
+ }
+ defer conn.Close()
+
+ // fill the socket buffer
+ data := make([]byte, 5*1024*1024)
+ done := make(chan struct{})
+ go func() {
+ _, err = conn.Write(data)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ // It waits 1s more to avoid delay in low-end system.
+ case <-time.After(wln.wtimeoutd*10 + time.Second):
+ t.Fatal("wait timeout")
+ }
+
+ if operr, ok := err.(*net.OpError); !ok || operr.Op != "write" || !operr.Timeout() {
+ t.Errorf("err = %v, want write i/o timeout error", err)
+ }
+ stop <- struct{}{}
+
+ go blocker()
+
+ conn, err = wln.Accept()
+ if err != nil {
+ t.Fatalf("unexpected accept error: %v", err)
+ }
+ buf := make([]byte, 10)
+
+ go func() {
+ _, err = conn.Read(buf)
+ done <- struct{}{}
+ }()
+
+ select {
+ case <-done:
+ case <-time.After(wln.rdtimeoutd * 10):
+ t.Fatal("wait timeout")
+ }
+
+ if operr, ok := err.(*net.OpError); !ok || operr.Op != "read" || !operr.Timeout() {
+ t.Errorf("err = %v, want write i/o timeout error", err)
+ }
+ stop <- struct{}{}
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_transport.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_transport.go
new file mode 100644
index 000000000..365a0834b
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_transport.go
@@ -0,0 +1,43 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "net"
+ "net/http"
+ "time"
+)
+
+// NewTimeoutTransport returns a transport created using the given TLS info.
+// If read/write on the created connection blocks longer than its time limit,
+// it will return timeout error.
+func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.Duration) (*http.Transport, error) {
+ tr, err := NewTransport(info, dialtimeoutd)
+ if err != nil {
+ return nil, err
+ }
+ // the timeouted connection will tiemout soon after it is idle.
+ // it should not be put back to http transport as an idle connection for future usage.
+ tr.MaxIdleConnsPerHost = -1
+ tr.Dial = (&rwTimeoutDialer{
+ Dialer: net.Dialer{
+ Timeout: dialtimeoutd,
+ KeepAlive: 30 * time.Second,
+ },
+ rdtimeoutd: rdtimeoutd,
+ wtimeoutd: wtimeoutd,
+ }).Dial
+ return tr, nil
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/transport/timeout_transport_test.go b/vendor/github.com/coreos/etcd/pkg/transport/timeout_transport_test.go
new file mode 100644
index 000000000..e218ebeab
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/transport/timeout_transport_test.go
@@ -0,0 +1,85 @@
+// Copyright 2015 CoreOS, 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 transport
+
+import (
+ "bytes"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+ "time"
+)
+
+// TestNewTimeoutTransport tests that NewTimeoutTransport returns a transport
+// that can dial out timeout connections.
+func TestNewTimeoutTransport(t *testing.T) {
+ tr, err := NewTimeoutTransport(TLSInfo{}, time.Hour, time.Hour, time.Hour)
+ if err != nil {
+ t.Fatalf("unexpected NewTimeoutTransport error: %v", err)
+ }
+
+ remoteAddr := func(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte(r.RemoteAddr))
+ }
+ srv := httptest.NewServer(http.HandlerFunc(remoteAddr))
+
+ defer srv.Close()
+ conn, err := tr.Dial("tcp", srv.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("unexpected dial error: %v", err)
+ }
+ defer conn.Close()
+
+ tconn, ok := conn.(*timeoutConn)
+ if !ok {
+ t.Fatalf("failed to dial out *timeoutConn")
+ }
+ if tconn.rdtimeoutd != time.Hour {
+ t.Errorf("read timeout = %s, want %s", tconn.rdtimeoutd, time.Hour)
+ }
+ if tconn.wtimeoutd != time.Hour {
+ t.Errorf("write timeout = %s, want %s", tconn.wtimeoutd, time.Hour)
+ }
+
+ // ensure not reuse timeout connection
+ req, err := http.NewRequest("GET", srv.URL, nil)
+ if err != nil {
+ t.Fatalf("unexpected err %v", err)
+ }
+ resp, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatalf("unexpected err %v", err)
+ }
+ addr0, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ t.Fatalf("unexpected err %v", err)
+ }
+
+ resp, err = tr.RoundTrip(req)
+ if err != nil {
+ t.Fatalf("unexpected err %v", err)
+ }
+ addr1, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ t.Fatalf("unexpected err %v", err)
+ }
+
+ if bytes.Equal(addr0, addr1) {
+ t.Errorf("addr0 = %s addr1= %s, want not equal", string(addr0), string(addr1))
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/id.go b/vendor/github.com/coreos/etcd/pkg/types/id.go
new file mode 100644
index 000000000..88cb9e634
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/id.go
@@ -0,0 +1,41 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "strconv"
+)
+
+// ID represents a generic identifier which is canonically
+// stored as a uint64 but is typically represented as a
+// base-16 string for input/output
+type ID uint64
+
+func (i ID) String() string {
+ return strconv.FormatUint(uint64(i), 16)
+}
+
+// IDFromString attempts to create an ID from a base-16 string.
+func IDFromString(s string) (ID, error) {
+ i, err := strconv.ParseUint(s, 16, 64)
+ return ID(i), err
+}
+
+// IDSlice implements the sort interface
+type IDSlice []ID
+
+func (p IDSlice) Len() int { return len(p) }
+func (p IDSlice) Less(i, j int) bool { return uint64(p[i]) < uint64(p[j]) }
+func (p IDSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/vendor/github.com/coreos/etcd/pkg/types/id_test.go b/vendor/github.com/coreos/etcd/pkg/types/id_test.go
new file mode 100644
index 000000000..97d168f58
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/id_test.go
@@ -0,0 +1,95 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+)
+
+func TestIDString(t *testing.T) {
+ tests := []struct {
+ input ID
+ want string
+ }{
+ {
+ input: 12,
+ want: "c",
+ },
+ {
+ input: 4918257920282737594,
+ want: "444129853c343bba",
+ },
+ }
+
+ for i, tt := range tests {
+ got := tt.input.String()
+ if tt.want != got {
+ t.Errorf("#%d: ID.String failure: want=%v, got=%v", i, tt.want, got)
+ }
+ }
+}
+
+func TestIDFromString(t *testing.T) {
+ tests := []struct {
+ input string
+ want ID
+ }{
+ {
+ input: "17",
+ want: 23,
+ },
+ {
+ input: "612840dae127353",
+ want: 437557308098245459,
+ },
+ }
+
+ for i, tt := range tests {
+ got, err := IDFromString(tt.input)
+ if err != nil {
+ t.Errorf("#%d: IDFromString failure: err=%v", i, err)
+ continue
+ }
+ if tt.want != got {
+ t.Errorf("#%d: IDFromString failure: want=%v, got=%v", i, tt.want, got)
+ }
+ }
+}
+
+func TestIDFromStringFail(t *testing.T) {
+ tests := []string{
+ "",
+ "XXX",
+ "612840dae127353612840dae127353",
+ }
+
+ for i, tt := range tests {
+ _, err := IDFromString(tt)
+ if err == nil {
+ t.Fatalf("#%d: IDFromString expected error, but err=nil", i)
+ }
+ }
+}
+
+func TestIDSlice(t *testing.T) {
+ g := []ID{10, 500, 5, 1, 100, 25}
+ w := []ID{1, 5, 10, 25, 100, 500}
+ sort.Sort(IDSlice(g))
+ if !reflect.DeepEqual(g, w) {
+ t.Errorf("slice after sort = %#v, want %#v", g, w)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/set.go b/vendor/github.com/coreos/etcd/pkg/types/set.go
new file mode 100644
index 000000000..bb997174c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/set.go
@@ -0,0 +1,178 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "reflect"
+ "sort"
+ "sync"
+)
+
+type Set interface {
+ Add(string)
+ Remove(string)
+ Contains(string) bool
+ Equals(Set) bool
+ Length() int
+ Values() []string
+ Copy() Set
+ Sub(Set) Set
+}
+
+func NewUnsafeSet(values ...string) *unsafeSet {
+ set := &unsafeSet{make(map[string]struct{})}
+ for _, v := range values {
+ set.Add(v)
+ }
+ return set
+}
+
+func NewThreadsafeSet(values ...string) *tsafeSet {
+ us := NewUnsafeSet(values...)
+ return &tsafeSet{us, sync.RWMutex{}}
+}
+
+type unsafeSet struct {
+ d map[string]struct{}
+}
+
+// Add adds a new value to the set (no-op if the value is already present)
+func (us *unsafeSet) Add(value string) {
+ us.d[value] = struct{}{}
+}
+
+// Remove removes the given value from the set
+func (us *unsafeSet) Remove(value string) {
+ delete(us.d, value)
+}
+
+// Contains returns whether the set contains the given value
+func (us *unsafeSet) Contains(value string) (exists bool) {
+ _, exists = us.d[value]
+ return
+}
+
+// ContainsAll returns whether the set contains all given values
+func (us *unsafeSet) ContainsAll(values []string) bool {
+ for _, s := range values {
+ if !us.Contains(s) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equals returns whether the contents of two sets are identical
+func (us *unsafeSet) Equals(other Set) bool {
+ v1 := sort.StringSlice(us.Values())
+ v2 := sort.StringSlice(other.Values())
+ v1.Sort()
+ v2.Sort()
+ return reflect.DeepEqual(v1, v2)
+}
+
+// Length returns the number of elements in the set
+func (us *unsafeSet) Length() int {
+ return len(us.d)
+}
+
+// Values returns the values of the Set in an unspecified order.
+func (us *unsafeSet) Values() (values []string) {
+ values = make([]string, 0)
+ for val := range us.d {
+ values = append(values, val)
+ }
+ return
+}
+
+// Copy creates a new Set containing the values of the first
+func (us *unsafeSet) Copy() Set {
+ cp := NewUnsafeSet()
+ for val := range us.d {
+ cp.Add(val)
+ }
+
+ return cp
+}
+
+// Sub removes all elements in other from the set
+func (us *unsafeSet) Sub(other Set) Set {
+ oValues := other.Values()
+ result := us.Copy().(*unsafeSet)
+
+ for _, val := range oValues {
+ if _, ok := result.d[val]; !ok {
+ continue
+ }
+ delete(result.d, val)
+ }
+
+ return result
+}
+
+type tsafeSet struct {
+ us *unsafeSet
+ m sync.RWMutex
+}
+
+func (ts *tsafeSet) Add(value string) {
+ ts.m.Lock()
+ defer ts.m.Unlock()
+ ts.us.Add(value)
+}
+
+func (ts *tsafeSet) Remove(value string) {
+ ts.m.Lock()
+ defer ts.m.Unlock()
+ ts.us.Remove(value)
+}
+
+func (ts *tsafeSet) Contains(value string) (exists bool) {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ return ts.us.Contains(value)
+}
+
+func (ts *tsafeSet) Equals(other Set) bool {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ return ts.us.Equals(other)
+}
+
+func (ts *tsafeSet) Length() int {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ return ts.us.Length()
+}
+
+func (ts *tsafeSet) Values() (values []string) {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ return ts.us.Values()
+}
+
+func (ts *tsafeSet) Copy() Set {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ usResult := ts.us.Copy().(*unsafeSet)
+ return &tsafeSet{usResult, sync.RWMutex{}}
+}
+
+func (ts *tsafeSet) Sub(other Set) Set {
+ ts.m.RLock()
+ defer ts.m.RUnlock()
+ usResult := ts.us.Sub(other).(*unsafeSet)
+ return &tsafeSet{usResult, sync.RWMutex{}}
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/set_test.go b/vendor/github.com/coreos/etcd/pkg/types/set_test.go
new file mode 100644
index 000000000..ff1ecc68d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/set_test.go
@@ -0,0 +1,186 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+)
+
+func TestUnsafeSet(t *testing.T) {
+ driveSetTests(t, NewUnsafeSet())
+}
+
+func TestThreadsafeSet(t *testing.T) {
+ driveSetTests(t, NewThreadsafeSet())
+}
+
+// Check that two slices contents are equal; order is irrelevant
+func equal(a, b []string) bool {
+ as := sort.StringSlice(a)
+ bs := sort.StringSlice(b)
+ as.Sort()
+ bs.Sort()
+ return reflect.DeepEqual(as, bs)
+}
+
+func driveSetTests(t *testing.T, s Set) {
+ // Verify operations on an empty set
+ eValues := []string{}
+ values := s.Values()
+ if !reflect.DeepEqual(values, eValues) {
+ t.Fatalf("Expect values=%v got %v", eValues, values)
+ }
+ if l := s.Length(); l != 0 {
+ t.Fatalf("Expected length=0, got %d", l)
+ }
+ for _, v := range []string{"foo", "bar", "baz"} {
+ if s.Contains(v) {
+ t.Fatalf("Expect s.Contains(%q) to be fale, got true", v)
+ }
+ }
+
+ // Add three items, ensure they show up
+ s.Add("foo")
+ s.Add("bar")
+ s.Add("baz")
+
+ eValues = []string{"foo", "bar", "baz"}
+ values = s.Values()
+ if !equal(values, eValues) {
+ t.Fatalf("Expect values=%v got %v", eValues, values)
+ }
+
+ for _, v := range eValues {
+ if !s.Contains(v) {
+ t.Fatalf("Expect s.Contains(%q) to be true, got false", v)
+ }
+ }
+
+ if l := s.Length(); l != 3 {
+ t.Fatalf("Expected length=3, got %d", l)
+ }
+
+ // Add the same item a second time, ensuring it is not duplicated
+ s.Add("foo")
+
+ values = s.Values()
+ if !equal(values, eValues) {
+ t.Fatalf("Expect values=%v got %v", eValues, values)
+ }
+ if l := s.Length(); l != 3 {
+ t.Fatalf("Expected length=3, got %d", l)
+ }
+
+ // Remove all items, ensure they are gone
+ s.Remove("foo")
+ s.Remove("bar")
+ s.Remove("baz")
+
+ eValues = []string{}
+ values = s.Values()
+ if !equal(values, eValues) {
+ t.Fatalf("Expect values=%v got %v", eValues, values)
+ }
+
+ if l := s.Length(); l != 0 {
+ t.Fatalf("Expected length=0, got %d", l)
+ }
+
+ // Create new copies of the set, and ensure they are unlinked to the
+ // original Set by making modifications
+ s.Add("foo")
+ s.Add("bar")
+ cp1 := s.Copy()
+ cp2 := s.Copy()
+ s.Remove("foo")
+ cp3 := s.Copy()
+ cp1.Add("baz")
+
+ for i, tt := range []struct {
+ want []string
+ got []string
+ }{
+ {[]string{"bar"}, s.Values()},
+ {[]string{"foo", "bar", "baz"}, cp1.Values()},
+ {[]string{"foo", "bar"}, cp2.Values()},
+ {[]string{"bar"}, cp3.Values()},
+ } {
+ if !equal(tt.want, tt.got) {
+ t.Fatalf("case %d: expect values=%v got %v", i, tt.want, tt.got)
+ }
+ }
+
+ for i, tt := range []struct {
+ want bool
+ got bool
+ }{
+ {true, s.Equals(cp3)},
+ {true, cp3.Equals(s)},
+ {false, s.Equals(cp2)},
+ {false, s.Equals(cp1)},
+ {false, cp1.Equals(s)},
+ {false, cp2.Equals(s)},
+ {false, cp2.Equals(cp1)},
+ } {
+ if tt.got != tt.want {
+ t.Fatalf("case %d: want %t, got %t", i, tt.want, tt.got)
+
+ }
+ }
+
+ // Subtract values from a Set, ensuring a new Set is created and
+ // the original Sets are unmodified
+ sub1 := cp1.Sub(s)
+ sub2 := cp2.Sub(cp1)
+
+ for i, tt := range []struct {
+ want []string
+ got []string
+ }{
+ {[]string{"foo", "bar", "baz"}, cp1.Values()},
+ {[]string{"foo", "bar"}, cp2.Values()},
+ {[]string{"bar"}, s.Values()},
+ {[]string{"foo", "baz"}, sub1.Values()},
+ {[]string{}, sub2.Values()},
+ } {
+ if !equal(tt.want, tt.got) {
+ t.Fatalf("case %d: expect values=%v got %v", i, tt.want, tt.got)
+ }
+ }
+}
+
+func TestUnsafeSetContainsAll(t *testing.T) {
+ vals := []string{"foo", "bar", "baz"}
+ s := NewUnsafeSet(vals...)
+
+ tests := []struct {
+ strs []string
+ wcontain bool
+ }{
+ {[]string{}, true},
+ {vals[:1], true},
+ {vals[:2], true},
+ {vals, true},
+ {[]string{"cuz"}, false},
+ {[]string{vals[0], "cuz"}, false},
+ }
+ for i, tt := range tests {
+ if g := s.ContainsAll(tt.strs); g != tt.wcontain {
+ t.Errorf("#%d: ok = %v, want %v", i, g, tt.wcontain)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/slice.go b/vendor/github.com/coreos/etcd/pkg/types/slice.go
new file mode 100644
index 000000000..0327950f7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/slice.go
@@ -0,0 +1,22 @@
+// Copyright 2015 CoreOS, 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 types
+
+// Uint64Slice implements sort interface
+type Uint64Slice []uint64
+
+func (p Uint64Slice) Len() int { return len(p) }
+func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
+func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
diff --git a/vendor/github.com/coreos/etcd/pkg/types/slice_test.go b/vendor/github.com/coreos/etcd/pkg/types/slice_test.go
new file mode 100644
index 000000000..95e37e04d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/slice_test.go
@@ -0,0 +1,30 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "reflect"
+ "sort"
+ "testing"
+)
+
+func TestUint64Slice(t *testing.T) {
+ g := Uint64Slice{10, 500, 5, 1, 100, 25}
+ w := Uint64Slice{1, 5, 10, 25, 100, 500}
+ sort.Sort(g)
+ if !reflect.DeepEqual(g, w) {
+ t.Errorf("slice after sort = %#v, want %#v", g, w)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/urls.go b/vendor/github.com/coreos/etcd/pkg/types/urls.go
new file mode 100644
index 000000000..ce2483ffa
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/urls.go
@@ -0,0 +1,74 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "errors"
+ "fmt"
+ "net"
+ "net/url"
+ "sort"
+ "strings"
+)
+
+type URLs []url.URL
+
+func NewURLs(strs []string) (URLs, error) {
+ all := make([]url.URL, len(strs))
+ if len(all) == 0 {
+ return nil, errors.New("no valid URLs given")
+ }
+ for i, in := range strs {
+ in = strings.TrimSpace(in)
+ u, err := url.Parse(in)
+ if err != nil {
+ return nil, err
+ }
+ if u.Scheme != "http" && u.Scheme != "https" {
+ return nil, fmt.Errorf("URL scheme must be http or https: %s", in)
+ }
+ if _, _, err := net.SplitHostPort(u.Host); err != nil {
+ return nil, fmt.Errorf(`URL address does not have the form "host:port": %s`, in)
+ }
+ if u.Path != "" {
+ return nil, fmt.Errorf("URL must not contain a path: %s", in)
+ }
+ all[i] = *u
+ }
+ us := URLs(all)
+ us.Sort()
+
+ return us, nil
+}
+
+func (us URLs) String() string {
+ return strings.Join(us.StringSlice(), ",")
+}
+
+func (us *URLs) Sort() {
+ sort.Sort(us)
+}
+func (us URLs) Len() int { return len(us) }
+func (us URLs) Less(i, j int) bool { return us[i].String() < us[j].String() }
+func (us URLs) Swap(i, j int) { us[i], us[j] = us[j], us[i] }
+
+func (us URLs) StringSlice() []string {
+ out := make([]string, len(us))
+ for i := range us {
+ out[i] = us[i].String()
+ }
+
+ return out
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/urls_test.go b/vendor/github.com/coreos/etcd/pkg/types/urls_test.go
new file mode 100644
index 000000000..ffa2cf007
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/urls_test.go
@@ -0,0 +1,169 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+func TestNewURLs(t *testing.T) {
+ tests := []struct {
+ strs []string
+ wurls URLs
+ }{
+ {
+ []string{"http://127.0.0.1:2379"},
+ testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
+ },
+ // it can trim space
+ {
+ []string{" http://127.0.0.1:2379 "},
+ testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
+ },
+ // it does sort
+ {
+ []string{
+ "http://127.0.0.2:2379",
+ "http://127.0.0.1:2379",
+ },
+ testutil.MustNewURLs(t, []string{
+ "http://127.0.0.1:2379",
+ "http://127.0.0.2:2379",
+ }),
+ },
+ }
+ for i, tt := range tests {
+ urls, _ := NewURLs(tt.strs)
+ if !reflect.DeepEqual(urls, tt.wurls) {
+ t.Errorf("#%d: urls = %+v, want %+v", i, urls, tt.wurls)
+ }
+ }
+}
+
+func TestURLsString(t *testing.T) {
+ tests := []struct {
+ us URLs
+ wstr string
+ }{
+ {
+ URLs{},
+ "",
+ },
+ {
+ testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
+ "http://127.0.0.1:2379",
+ },
+ {
+ testutil.MustNewURLs(t, []string{
+ "http://127.0.0.1:2379",
+ "http://127.0.0.2:2379",
+ }),
+ "http://127.0.0.1:2379,http://127.0.0.2:2379",
+ },
+ {
+ testutil.MustNewURLs(t, []string{
+ "http://127.0.0.2:2379",
+ "http://127.0.0.1:2379",
+ }),
+ "http://127.0.0.2:2379,http://127.0.0.1:2379",
+ },
+ }
+ for i, tt := range tests {
+ g := tt.us.String()
+ if g != tt.wstr {
+ t.Errorf("#%d: string = %s, want %s", i, g, tt.wstr)
+ }
+ }
+}
+
+func TestURLsSort(t *testing.T) {
+ g := testutil.MustNewURLs(t, []string{
+ "http://127.0.0.4:2379",
+ "http://127.0.0.2:2379",
+ "http://127.0.0.1:2379",
+ "http://127.0.0.3:2379",
+ })
+ w := testutil.MustNewURLs(t, []string{
+ "http://127.0.0.1:2379",
+ "http://127.0.0.2:2379",
+ "http://127.0.0.3:2379",
+ "http://127.0.0.4:2379",
+ })
+ gurls := URLs(g)
+ gurls.Sort()
+ if !reflect.DeepEqual(g, w) {
+ t.Errorf("URLs after sort = %#v, want %#v", g, w)
+ }
+}
+
+func TestURLsStringSlice(t *testing.T) {
+ tests := []struct {
+ us URLs
+ wstr []string
+ }{
+ {
+ URLs{},
+ []string{},
+ },
+ {
+ testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
+ []string{"http://127.0.0.1:2379"},
+ },
+ {
+ testutil.MustNewURLs(t, []string{
+ "http://127.0.0.1:2379",
+ "http://127.0.0.2:2379",
+ }),
+ []string{"http://127.0.0.1:2379", "http://127.0.0.2:2379"},
+ },
+ {
+ testutil.MustNewURLs(t, []string{
+ "http://127.0.0.2:2379",
+ "http://127.0.0.1:2379",
+ }),
+ []string{"http://127.0.0.2:2379", "http://127.0.0.1:2379"},
+ },
+ }
+ for i, tt := range tests {
+ g := tt.us.StringSlice()
+ if !reflect.DeepEqual(g, tt.wstr) {
+ t.Errorf("#%d: string slice = %+v, want %+v", i, g, tt.wstr)
+ }
+ }
+}
+
+func TestNewURLsFail(t *testing.T) {
+ tests := [][]string{
+ // no urls given
+ {},
+ // missing protocol scheme
+ {"://127.0.0.1:2379"},
+ // unsupported scheme
+ {"mailto://127.0.0.1:2379"},
+ // not conform to host:port
+ {"http://127.0.0.1"},
+ // contain a path
+ {"http://127.0.0.1:2379/path"},
+ }
+ for i, tt := range tests {
+ _, err := NewURLs(tt)
+ if err == nil {
+ t.Errorf("#%d: err = nil, but error", i)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/urlsmap.go b/vendor/github.com/coreos/etcd/pkg/types/urlsmap.go
new file mode 100644
index 000000000..f7d5c1c90
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/urlsmap.go
@@ -0,0 +1,91 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+type URLsMap map[string]URLs
+
+// NewURLsMap returns a URLsMap instantiated from the given string,
+// which consists of discovery-formatted names-to-URLs, like:
+// mach0=http://1.1.1.1:2380,mach0=http://2.2.2.2::2380,mach1=http://3.3.3.3:2380,mach2=http://4.4.4.4:2380
+func NewURLsMap(s string) (URLsMap, error) {
+ m := parse(s)
+
+ cl := URLsMap{}
+ for name, urls := range m {
+ us, err := NewURLs(urls)
+ if err != nil {
+ return nil, err
+ }
+ cl[name] = us
+ }
+ return cl, nil
+}
+
+// String returns NameURLPairs into discovery-formatted name-to-URLs sorted by name.
+func (c URLsMap) String() string {
+ pairs := make([]string, 0)
+ for name, urls := range c {
+ for _, url := range urls {
+ pairs = append(pairs, fmt.Sprintf("%s=%s", name, url.String()))
+ }
+ }
+ sort.Strings(pairs)
+ return strings.Join(pairs, ",")
+}
+
+// URLs returns a list of all URLs.
+// The returned list is sorted in ascending lexicographical order.
+func (c URLsMap) URLs() []string {
+ urls := make([]string, 0)
+ for _, us := range c {
+ for _, u := range us {
+ urls = append(urls, u.String())
+ }
+ }
+ sort.Strings(urls)
+ return urls
+}
+
+func (c URLsMap) Len() int {
+ return len(c)
+}
+
+// parse parses the given string and returns a map listing the values specified for each key.
+func parse(s string) map[string][]string {
+ m := make(map[string][]string)
+ for s != "" {
+ key := s
+ if i := strings.IndexAny(key, ","); i >= 0 {
+ key, s = key[:i], key[i+1:]
+ } else {
+ s = ""
+ }
+ if key == "" {
+ continue
+ }
+ value := ""
+ if i := strings.Index(key, "="); i >= 0 {
+ key, value = key[:i], key[i+1:]
+ }
+ m[key] = append(m[key], value)
+ }
+ return m
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/urlsmap_go15_test.go b/vendor/github.com/coreos/etcd/pkg/types/urlsmap_go15_test.go
new file mode 100644
index 000000000..1955bb9cb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/urlsmap_go15_test.go
@@ -0,0 +1,40 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build go1.5
+
+package types
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+// This is only tested in Go1.5+ because Go1.4 doesn't support literal IPv6 address with zone in
+// URI (https://github.com/golang/go/issues/6530).
+func TestNewURLsMapIPV6(t *testing.T) {
+ c, err := NewURLsMap("mem1=http://[2001:db8::1]:2380,mem1=http://[fe80::6e40:8ff:feb1:58e4%25en0]:2380,mem2=http://[fe80::92e2:baff:fe7c:3224%25ext0]:2380")
+ if err != nil {
+ t.Fatalf("unexpected parse error: %v", err)
+ }
+ wc := URLsMap(map[string]URLs{
+ "mem1": testutil.MustNewURLs(t, []string{"http://[2001:db8::1]:2380", "http://[fe80::6e40:8ff:feb1:58e4%25en0]:2380"}),
+ "mem2": testutil.MustNewURLs(t, []string{"http://[fe80::92e2:baff:fe7c:3224%25ext0]:2380"}),
+ })
+ if !reflect.DeepEqual(c, wc) {
+ t.Errorf("cluster = %#v, want %#v", c, wc)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/types/urlsmap_test.go b/vendor/github.com/coreos/etcd/pkg/types/urlsmap_test.go
new file mode 100644
index 000000000..a01b5efa9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/types/urlsmap_test.go
@@ -0,0 +1,99 @@
+// Copyright 2015 CoreOS, 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 types
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+func TestParseInitialCluster(t *testing.T) {
+ c, err := NewURLsMap("mem1=http://10.0.0.1:2379,mem1=http://128.193.4.20:2379,mem2=http://10.0.0.2:2379,default=http://127.0.0.1:2379")
+ if err != nil {
+ t.Fatalf("unexpected parse error: %v", err)
+ }
+ wc := URLsMap(map[string]URLs{
+ "mem1": testutil.MustNewURLs(t, []string{"http://10.0.0.1:2379", "http://128.193.4.20:2379"}),
+ "mem2": testutil.MustNewURLs(t, []string{"http://10.0.0.2:2379"}),
+ "default": testutil.MustNewURLs(t, []string{"http://127.0.0.1:2379"}),
+ })
+ if !reflect.DeepEqual(c, wc) {
+ t.Errorf("cluster = %+v, want %+v", c, wc)
+ }
+}
+
+func TestParseInitialClusterBad(t *testing.T) {
+ tests := []string{
+ // invalid URL
+ "%^",
+ // no URL defined for member
+ "mem1=,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
+ "mem1,mem2=http://128.193.4.20:2379,mem3=http://10.0.0.2:2379",
+ // bad URL for member
+ "default=http://localhost/",
+ }
+ for i, tt := range tests {
+ if _, err := NewURLsMap(tt); err == nil {
+ t.Errorf("#%d: unexpected successful parse, want err", i)
+ }
+ }
+}
+
+func TestNameURLPairsString(t *testing.T) {
+ cls := URLsMap(map[string]URLs{
+ "abc": testutil.MustNewURLs(t, []string{"http://1.1.1.1:1111", "http://0.0.0.0:0000"}),
+ "def": testutil.MustNewURLs(t, []string{"http://2.2.2.2:2222"}),
+ "ghi": testutil.MustNewURLs(t, []string{"http://3.3.3.3:1234", "http://127.0.0.1:2380"}),
+ // no PeerURLs = not included
+ "four": testutil.MustNewURLs(t, []string{}),
+ "five": testutil.MustNewURLs(t, nil),
+ })
+ w := "abc=http://0.0.0.0:0000,abc=http://1.1.1.1:1111,def=http://2.2.2.2:2222,ghi=http://127.0.0.1:2380,ghi=http://3.3.3.3:1234"
+ if g := cls.String(); g != w {
+ t.Fatalf("NameURLPairs.String():\ngot %#v\nwant %#v", g, w)
+ }
+}
+
+func TestParse(t *testing.T) {
+ tests := []struct {
+ s string
+ wm map[string][]string
+ }{
+ {
+ "",
+ map[string][]string{},
+ },
+ {
+ "a=b",
+ map[string][]string{"a": {"b"}},
+ },
+ {
+ "a=b,a=c",
+ map[string][]string{"a": {"b", "c"}},
+ },
+ {
+ "a=b,a1=c",
+ map[string][]string{"a": {"b"}, "a1": {"c"}},
+ },
+ }
+ for i, tt := range tests {
+ m := parse(tt.s)
+ if !reflect.DeepEqual(m, tt.wm) {
+ t.Errorf("#%d: m = %+v, want %+v", i, m, tt.wm)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/wait/wait.go b/vendor/github.com/coreos/etcd/pkg/wait/wait.go
new file mode 100644
index 000000000..e8cf073f8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/wait/wait.go
@@ -0,0 +1,55 @@
+// Copyright 2015 CoreOS, 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 wait
+
+import (
+ "sync"
+)
+
+type Wait interface {
+ Register(id uint64) <-chan interface{}
+ Trigger(id uint64, x interface{})
+}
+
+type List struct {
+ l sync.Mutex
+ m map[uint64]chan interface{}
+}
+
+func New() *List {
+ return &List{m: make(map[uint64]chan interface{})}
+}
+
+func (w *List) Register(id uint64) <-chan interface{} {
+ w.l.Lock()
+ defer w.l.Unlock()
+ ch := w.m[id]
+ if ch == nil {
+ ch = make(chan interface{}, 1)
+ w.m[id] = ch
+ }
+ return ch
+}
+
+func (w *List) Trigger(id uint64, x interface{}) {
+ w.l.Lock()
+ ch := w.m[id]
+ delete(w.m, id)
+ w.l.Unlock()
+ if ch != nil {
+ ch <- x
+ close(ch)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/wait/wait_test.go b/vendor/github.com/coreos/etcd/pkg/wait/wait_test.go
new file mode 100644
index 000000000..a8ede8930
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/wait/wait_test.go
@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, 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 wait
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestWait(t *testing.T) {
+ const eid = 1
+ wt := New()
+ ch := wt.Register(eid)
+ wt.Trigger(eid, "foo")
+ v := <-ch
+ if g, w := fmt.Sprintf("%v (%T)", v, v), "foo (string)"; g != w {
+ t.Errorf("<-ch = %v, want %v", g, w)
+ }
+
+ if g := <-ch; g != nil {
+ t.Errorf("unexpected non-nil value: %v (%T)", g, g)
+ }
+}
+
+func TestRegisterDupSuppression(t *testing.T) {
+ const eid = 1
+ wt := New()
+ ch1 := wt.Register(eid)
+ ch2 := wt.Register(eid)
+ wt.Trigger(eid, "foo")
+ <-ch1
+ g := <-ch2
+ if g != nil {
+ t.Errorf("unexpected non-nil value: %v (%T)", g, g)
+ }
+}
+
+func TestTriggerDupSuppression(t *testing.T) {
+ const eid = 1
+ wt := New()
+ ch := wt.Register(eid)
+ wt.Trigger(eid, "foo")
+ wt.Trigger(eid, "bar")
+
+ v := <-ch
+ if g, w := fmt.Sprintf("%v (%T)", v, v), "foo (string)"; g != w {
+ t.Errorf("<-ch = %v, want %v", g, w)
+ }
+
+ if g := <-ch; g != nil {
+ t.Errorf("unexpected non-nil value: %v (%T)", g, g)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/wait/wait_time.go b/vendor/github.com/coreos/etcd/pkg/wait/wait_time.go
new file mode 100644
index 000000000..99fb3a96c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/wait/wait_time.go
@@ -0,0 +1,76 @@
+// Copyright 2015 CoreOS, 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.
+
+/*
+ Copyright 2015 CoreOS, 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 wait
+
+import (
+ "sync"
+ "time"
+)
+
+type WaitTime interface {
+ // Wait returns a chan that waits on the given deadline.
+ // The chan will be triggered when Trigger is called with a
+ // deadline that is later than the one it is waiting for.
+ // The given deadline MUST be unique. The deadline should be
+ // retrived by calling time.Now() in most cases.
+ Wait(deadline time.Time) <-chan struct{}
+ // Trigger triggers all the waiting chans with an earlier deadline.
+ Trigger(deadline time.Time)
+}
+
+type timeList struct {
+ l sync.Mutex
+ m map[int64]chan struct{}
+}
+
+func NewTimeList() *timeList {
+ return &timeList{m: make(map[int64]chan struct{})}
+}
+
+func (tl *timeList) Wait(deadline time.Time) <-chan struct{} {
+ tl.l.Lock()
+ defer tl.l.Unlock()
+ ch := make(chan struct{}, 1)
+ // The given deadline SHOULD be unique.
+ tl.m[deadline.UnixNano()] = ch
+ return ch
+}
+
+func (tl *timeList) Trigger(deadline time.Time) {
+ tl.l.Lock()
+ defer tl.l.Unlock()
+ for t, ch := range tl.m {
+ if t < deadline.UnixNano() {
+ delete(tl.m, t)
+ close(ch)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/pkg/wait/wait_time_test.go b/vendor/github.com/coreos/etcd/pkg/wait/wait_time_test.go
new file mode 100644
index 000000000..a76607ae8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/pkg/wait/wait_time_test.go
@@ -0,0 +1,101 @@
+// Copyright 2015 CoreOS, 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.
+
+/*
+
+ Copyright 2015 CoreOS, 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 wait
+
+import (
+ "testing"
+ "time"
+)
+
+func TestWaitTime(t *testing.T) {
+ wt := NewTimeList()
+ ch1 := wt.Wait(time.Now())
+ t1 := time.Now()
+ wt.Trigger(t1)
+ select {
+ case <-ch1:
+ case <-time.After(10 * time.Millisecond):
+ t.Fatalf("cannot receive from ch as expected")
+ }
+
+ ch2 := wt.Wait(time.Now())
+ t2 := time.Now()
+ wt.Trigger(t1)
+ select {
+ case <-ch2:
+ t.Fatalf("unexpected to receive from ch")
+ case <-time.After(10 * time.Millisecond):
+ }
+ wt.Trigger(t2)
+ select {
+ case <-ch2:
+ case <-time.After(10 * time.Millisecond):
+ t.Fatalf("cannot receive from ch as expected")
+ }
+}
+
+func TestWaitTestStress(t *testing.T) {
+ chs := make([]<-chan struct{}, 0)
+ wt := NewTimeList()
+ for i := 0; i < 10000; i++ {
+ chs = append(chs, wt.Wait(time.Now()))
+ // sleep one nanosecond before waiting on the next event
+ time.Sleep(time.Nanosecond)
+ }
+ wt.Trigger(time.Now())
+
+ for _, ch := range chs {
+ select {
+ case <-ch:
+ case <-time.After(time.Second):
+ t.Fatalf("cannot receive from ch as expected")
+ }
+ }
+}
+
+func BenchmarkWaitTime(b *testing.B) {
+ t := time.Now()
+ wt := NewTimeList()
+ for i := 0; i < b.N; i++ {
+ wt.Wait(t)
+ }
+}
+
+func BenchmarkTriggerAnd10KWaitTime(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ t := time.Now()
+ wt := NewTimeList()
+ for j := 0; j < 10000; j++ {
+ wt.Wait(t)
+ }
+ wt.Trigger(time.Now())
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/design.md b/vendor/github.com/coreos/etcd/raft/design.md
new file mode 100644
index 000000000..7bc0531dc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/design.md
@@ -0,0 +1,57 @@
+## Progress
+
+Progress represents a follower’s progress in the view of the leader. Leader maintains progresses of all followers, and sends `replication message` to the follower based on its progress.
+
+`replication message` is a `msgApp` with log entries.
+
+A progress has two attribute: `match` and `next`. `match` is the index of the highest known matched entry. If leader knows nothing about follower’s replication status, `match` is set to zero. `next` is the index of the first entry that will be replicated to the follower. Leader puts entries from `next` to its latest one in next `replication message`.
+
+A progress is in one of the three state: `probe`, `replicate`, `snapshot`.
+
+```
+ +--------------------------------------------------------+
+ | send snapshot |
+ | |
+ +---------+----------+ +----------v---------+
+ +---> probe | | snapshot |
+ | | max inflight = 1 <----------------------------------+ max inflight = 0 |
+ | +---------+----------+ +--------------------+
+ | | 1. snapshot success
+ | | (next=snapshot.index + 1)
+ | | 2. snapshot failure
+ | | (no change)
+ | | 3. receives msgAppResp(rej=false&&index>lastsnap.index)
+ | | (match=m.index,next=match+1)
+receives msgAppResp(rej=true)
+(next=match+1)| |
+ | |
+ | |
+ | | receives msgAppResp(rej=false&&index>match)
+ | | (match=m.index,next=match+1)
+ | |
+ | |
+ | |
+ | +---------v----------+
+ | | replicate |
+ +---+ max inflight = n |
+ +--------------------+
+```
+
+When the progress of a follower is in `probe` state, leader sends at most one `replication message` per heartbeat interval. The leader sends `replication message` slowly and probing the actual progress of the follower. A `msgHeartbeatResp` or a `msgAppResp` with reject might trigger the sending of the next `replication message`.
+
+When the progress of a follower is in `replicate` state, leader sends `replication message`, then optimistically increases `next` to the latest entry sent. This is an optimized state for fast replicating log entries to the follower.
+
+When the progress of a follower is in `snapshot` state, leader stops sending any `replication message`.
+
+A newly elected leader sets the progresses of all the followers to `probe` state with `match` = 0 and `next` = last index. The leader slowly (at most once per heartbeat) sends `replication message` to the follower and probes its progress.
+
+A progress changes to `replicate` when the follower replies with a non-rejection `msgAppResp`, which implies that it has matched the index sent. At this point, leader starts to stream log entries to the follower fast. The progress will fall back to `probe` when the follower replies a rejection `msgAppResp` or the link layer reports the follower is unreachable. We aggressively reset `next` to `match`+1 since if we receive any `msgAppResp` soon, both `match` and `next` will increase directly to the `index` in `msgAppResp`. (We might end up with sending some duplicate entries when aggressively reset `next` too low. see open question)
+
+A progress changes from `probe` to `snapshot` when the follower falls very far behind and requires a snapshot. After sending `msgSnap`, the leader waits until the success, failure or abortion of the previous snapshot sent. The progress will go back to `probe` after the sending result is applied.
+
+### Flow Control
+
+1. limit the max size of message sent per message. Max should be configurable.
+Lower the cost at probing state as we limit the size per message; lower the penalty when aggressively decreased to a too low `next`
+
+2. limit the # of in flight messages < N when in `replicate` state. N should be configurable. Most implementation will have a sending buffer on top of its actual network transport layer (not blocking raft node). We want to make sure raft does not overflow that buffer, which can cause message dropping and triggering a bunch of unnecessary resending repeatedly.
diff --git a/vendor/github.com/coreos/etcd/raft/diff_test.go b/vendor/github.com/coreos/etcd/raft/diff_test.go
new file mode 100644
index 000000000..4e0702436
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/diff_test.go
@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "strings"
+)
+
+func diffu(a, b string) string {
+ if a == b {
+ return ""
+ }
+ aname, bname := mustTemp("base", a), mustTemp("other", b)
+ defer os.Remove(aname)
+ defer os.Remove(bname)
+ cmd := exec.Command("diff", "-u", aname, bname)
+ buf, err := cmd.CombinedOutput()
+ if err != nil {
+ if _, ok := err.(*exec.ExitError); ok {
+ // do nothing
+ return string(buf)
+ }
+ panic(err)
+ }
+ return string(buf)
+}
+
+func mustTemp(pre, body string) string {
+ f, err := ioutil.TempFile("", pre)
+ if err != nil {
+ panic(err)
+ }
+ _, err = io.Copy(f, strings.NewReader(body))
+ if err != nil {
+ panic(err)
+ }
+ f.Close()
+ return f.Name()
+}
+
+func ltoa(l *raftLog) string {
+ s := fmt.Sprintf("committed: %d\n", l.committed)
+ s += fmt.Sprintf("applied: %d\n", l.applied)
+ for i, e := range l.allEntries() {
+ s += fmt.Sprintf("#%d: %+v\n", i, e)
+ }
+ return s
+}
diff --git a/vendor/github.com/coreos/etcd/raft/doc.go b/vendor/github.com/coreos/etcd/raft/doc.go
new file mode 100644
index 000000000..6e3a1c81e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/doc.go
@@ -0,0 +1,153 @@
+// Copyright 2015 CoreOS, 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 raft provides an implementation of the raft consensus algorithm.
+
+Usage
+
+The primary object in raft is a Node. You either start a Node from scratch
+using raft.StartNode or start a Node from some initial state using raft.RestartNode.
+ storage := raft.NewMemoryStorage()
+ c := &Config{
+ ID: 0x01,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: storage,
+ MaxSizePerMsg: 4096,
+ MaxInflightMsgs: 256,
+ }
+ n := raft.StartNode(c, []raft.Peer{{ID: 0x02}, {ID: 0x03}})
+
+Now that you are holding onto a Node you have a few responsibilities:
+
+First, you must read from the Node.Ready() channel and process the updates
+it contains. These steps may be performed in parallel, except as noted in step
+2.
+
+1. Write HardState, Entries, and Snapshot to persistent storage if they are
+not empty. Note that when writing an Entry with Index i, any
+previously-persisted entries with Index >= i must be discarded.
+
+2. Send all Messages to the nodes named in the To field. It is important that
+no messages be sent until after the latest HardState has been persisted to disk,
+and all Entries written by any previous Ready batch (Messages may be sent while
+entries from the same batch are being persisted). If any Message has type MsgSnap,
+call Node.ReportSnapshot() after it has been sent (these messages may be large).
+
+3. Apply Snapshot (if any) and CommittedEntries to the state machine.
+If any committed Entry has Type EntryConfChange, call Node.ApplyConfChange()
+to apply it to the node. The configuration change may be cancelled at this point
+by setting the NodeID field to zero before calling ApplyConfChange
+(but ApplyConfChange must be called one way or the other, and the decision to cancel
+must be based solely on the state machine and not external information such as
+the observed health of the node).
+
+4. Call Node.Advance() to signal readiness for the next batch of updates.
+This may be done at any time after step 1, although all updates must be processed
+in the order they were returned by Ready.
+
+Second, all persisted log entries must be made available via an
+implementation of the Storage interface. The provided MemoryStorage
+type can be used for this (if you repopulate its state upon a
+restart), or you can supply your own disk-backed implementation.
+
+Third, when you receive a message from another node, pass it to Node.Step:
+
+ func recvRaftRPC(ctx context.Context, m raftpb.Message) {
+ n.Step(ctx, m)
+ }
+
+Finally, you need to call Node.Tick() at regular intervals (probably
+via a time.Ticker). Raft has two important timeouts: heartbeat and the
+election timeout. However, internally to the raft package time is
+represented by an abstract "tick".
+
+The total state machine handling loop will look something like this:
+
+ for {
+ select {
+ case <-s.Ticker:
+ n.Tick()
+ case rd := <-s.Node.Ready():
+ saveToStorage(rd.State, rd.Entries, rd.Snapshot)
+ send(rd.Messages)
+ if !raft.IsEmptySnap(rd.Snapshot) {
+ processSnapshot(rd.Snapshot)
+ }
+ for _, entry := range rd.CommittedEntries {
+ process(entry)
+ if entry.Type == raftpb.EntryConfChange {
+ var cc raftpb.ConfChange
+ cc.Unmarshal(entry.Data)
+ s.Node.ApplyConfChange(cc)
+ }
+ s.Node.Advance()
+ case <-s.done:
+ return
+ }
+ }
+
+To propose changes to the state machine from your node take your application
+data, serialize it into a byte slice and call:
+
+ n.Propose(ctx, data)
+
+If the proposal is committed, data will appear in committed entries with type
+raftpb.EntryNormal. There is no guarantee that a proposed command will be
+committed; you may have to re-propose after a timeout.
+
+To add or remove node in a cluster, build ConfChange struct 'cc' and call:
+
+ n.ProposeConfChange(ctx, cc)
+
+After config change is committed, some committed entry with type
+raftpb.EntryConfChange will be returned. You must apply it to node through:
+
+ var cc raftpb.ConfChange
+ cc.Unmarshal(data)
+ n.ApplyConfChange(cc)
+
+Note: An ID represents a unique node in a cluster for all time. A
+given ID MUST be used only once even if the old node has been removed.
+This means that for example IP addresses make poor node IDs since they
+may be reused. Node IDs must be non-zero.
+
+Implementation notes
+
+This implementation is up to date with the final Raft thesis
+(https://ramcloud.stanford.edu/~ongaro/thesis.pdf), although our
+implementation of the membership change protocol differs somewhat from
+that described in chapter 4. The key invariant that membership changes
+happen one node at a time is preserved, but in our implementation the
+membership change takes effect when its entry is applied, not when it
+is added to the log (so the entry is committed under the old
+membership instead of the new). This is equivalent in terms of safety,
+since the old and new configurations are guaranteed to overlap.
+
+To ensure that we do not attempt to commit two membership changes at
+once by matching log positions (which would be unsafe since they
+should have different quorum requirements), we simply disallow any
+proposed membership change while any uncommitted change appears in
+the leader's log.
+
+This approach introduces a problem when you try to remove a member
+from a two-member cluster: If one of the members dies before the
+other one receives the commit of the confchange entry, then the member
+cannot be removed any more since the cluster cannot make progress.
+For this reason it is highly recommended to use three or more nodes in
+every cluster.
+
+*/
+package raft
diff --git a/vendor/github.com/coreos/etcd/raft/example_test.go b/vendor/github.com/coreos/etcd/raft/example_test.go
new file mode 100644
index 000000000..876b38d5c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/example_test.go
@@ -0,0 +1,46 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+func applyToStore(ents []pb.Entry) {}
+func sendMessages(msgs []pb.Message) {}
+func saveStateToDisk(st pb.HardState) {}
+func saveToDisk(ents []pb.Entry) {}
+
+func ExampleNode() {
+ c := &Config{}
+ n := StartNode(c, nil)
+
+ // stuff to n happens in other goroutines
+
+ // the last known state
+ var prev pb.HardState
+ for {
+ // ReadState blocks until there is new state ready.
+ rd := <-n.Ready()
+ if !isHardStateEqual(prev, rd.HardState) {
+ saveStateToDisk(rd.HardState)
+ prev = rd.HardState
+ }
+
+ saveToDisk(rd.Entries)
+ go applyToStore(rd.CommittedEntries)
+ sendMessages(rd.Messages)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/log.go b/vendor/github.com/coreos/etcd/raft/log.go
new file mode 100644
index 000000000..b5f0b46c5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/log.go
@@ -0,0 +1,351 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "fmt"
+ "log"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+type raftLog struct {
+ // storage contains all stable entries since the last snapshot.
+ storage Storage
+
+ // unstable contains all unstable entries and snapshot.
+ // they will be saved into storage.
+ unstable unstable
+
+ // committed is the highest log position that is known to be in
+ // stable storage on a quorum of nodes.
+ committed uint64
+ // applied is the highest log position that the application has
+ // been instructed to apply to its state machine.
+ // Invariant: applied <= committed
+ applied uint64
+
+ logger Logger
+}
+
+// newLog returns log using the given storage. It recovers the log to the state
+// that it just commits and applies the latest snapshot.
+func newLog(storage Storage, logger Logger) *raftLog {
+ if storage == nil {
+ log.Panic("storage must not be nil")
+ }
+ log := &raftLog{
+ storage: storage,
+ logger: logger,
+ }
+ firstIndex, err := storage.FirstIndex()
+ if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+ lastIndex, err := storage.LastIndex()
+ if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+ log.unstable.offset = lastIndex + 1
+ log.unstable.logger = logger
+ // Initialize our committed and applied pointers to the time of the last compaction.
+ log.committed = firstIndex - 1
+ log.applied = firstIndex - 1
+
+ return log
+}
+
+func (l *raftLog) String() string {
+ return fmt.Sprintf("committed=%d, applied=%d, unstable.offset=%d, len(unstable.Entries)=%d", l.committed, l.applied, l.unstable.offset, len(l.unstable.entries))
+}
+
+// maybeAppend returns (0, false) if the entries cannot be appended. Otherwise,
+// it returns (last index of new entries, true).
+func (l *raftLog) maybeAppend(index, logTerm, committed uint64, ents ...pb.Entry) (lastnewi uint64, ok bool) {
+ lastnewi = index + uint64(len(ents))
+ if l.matchTerm(index, logTerm) {
+ ci := l.findConflict(ents)
+ switch {
+ case ci == 0:
+ case ci <= l.committed:
+ l.logger.Panicf("entry %d conflict with committed entry [committed(%d)]", ci, l.committed)
+ default:
+ offset := index + 1
+ l.append(ents[ci-offset:]...)
+ }
+ l.commitTo(min(committed, lastnewi))
+ return lastnewi, true
+ }
+ return 0, false
+}
+
+func (l *raftLog) append(ents ...pb.Entry) uint64 {
+ if len(ents) == 0 {
+ return l.lastIndex()
+ }
+ if after := ents[0].Index - 1; after < l.committed {
+ l.logger.Panicf("after(%d) is out of range [committed(%d)]", after, l.committed)
+ }
+ l.unstable.truncateAndAppend(ents)
+ return l.lastIndex()
+}
+
+// findConflict finds the index of the conflict.
+// It returns the first pair of conflicting entries between the existing
+// entries and the given entries, if there are any.
+// If there is no conflicting entries, and the existing entries contains
+// all the given entries, zero will be returned.
+// If there is no conflicting entries, but the given entries contains new
+// entries, the index of the first new entry will be returned.
+// An entry is considered to be conflicting if it has the same index but
+// a different term.
+// The first entry MUST have an index equal to the argument 'from'.
+// The index of the given entries MUST be continuously increasing.
+func (l *raftLog) findConflict(ents []pb.Entry) uint64 {
+ for _, ne := range ents {
+ if !l.matchTerm(ne.Index, ne.Term) {
+ if ne.Index <= l.lastIndex() {
+ l.logger.Infof("found conflict at index %d [existing term: %d, conflicting term: %d]",
+ ne.Index, l.zeroTermOnErrCompacted(l.term(ne.Index)), ne.Term)
+ }
+ return ne.Index
+ }
+ }
+ return 0
+}
+
+func (l *raftLog) unstableEntries() []pb.Entry {
+ if len(l.unstable.entries) == 0 {
+ return nil
+ }
+ return l.unstable.entries
+}
+
+// nextEnts returns all the available entries for execution.
+// If applied is smaller than the index of snapshot, it returns all committed
+// entries after the index of snapshot.
+func (l *raftLog) nextEnts() (ents []pb.Entry) {
+ off := max(l.applied+1, l.firstIndex())
+ if l.committed+1 > off {
+ ents, err := l.slice(off, l.committed+1, noLimit)
+ if err != nil {
+ l.logger.Panicf("unexpected error when getting unapplied entries (%v)", err)
+ }
+ return ents
+ }
+ return nil
+}
+
+func (l *raftLog) snapshot() (pb.Snapshot, error) {
+ if l.unstable.snapshot != nil {
+ return *l.unstable.snapshot, nil
+ }
+ return l.storage.Snapshot()
+}
+
+func (l *raftLog) firstIndex() uint64 {
+ if i, ok := l.unstable.maybeFirstIndex(); ok {
+ return i
+ }
+ index, err := l.storage.FirstIndex()
+ if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+ return index
+}
+
+func (l *raftLog) lastIndex() uint64 {
+ if i, ok := l.unstable.maybeLastIndex(); ok {
+ return i
+ }
+ i, err := l.storage.LastIndex()
+ if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+ return i
+}
+
+func (l *raftLog) commitTo(tocommit uint64) {
+ // never decrease commit
+ if l.committed < tocommit {
+ if l.lastIndex() < tocommit {
+ l.logger.Panicf("tocommit(%d) is out of range [lastIndex(%d)]. Was the raft log corrupted, truncated, or lost?", tocommit, l.lastIndex())
+ }
+ l.committed = tocommit
+ }
+}
+
+func (l *raftLog) appliedTo(i uint64) {
+ if i == 0 {
+ return
+ }
+ if l.committed < i || i < l.applied {
+ l.logger.Panicf("applied(%d) is out of range [prevApplied(%d), committed(%d)]", i, l.applied, l.committed)
+ }
+ l.applied = i
+}
+
+func (l *raftLog) stableTo(i, t uint64) { l.unstable.stableTo(i, t) }
+
+func (l *raftLog) stableSnapTo(i uint64) { l.unstable.stableSnapTo(i) }
+
+func (l *raftLog) lastTerm() uint64 {
+ t, err := l.term(l.lastIndex())
+ if err != nil {
+ l.logger.Panicf("unexpected error when getting the last term (%v)", err)
+ }
+ return t
+}
+
+func (l *raftLog) term(i uint64) (uint64, error) {
+ // the valid term range is [index of dummy entry, last index]
+ dummyIndex := l.firstIndex() - 1
+ if i < dummyIndex || i > l.lastIndex() {
+ // TODO: return an error instead?
+ return 0, nil
+ }
+
+ if t, ok := l.unstable.maybeTerm(i); ok {
+ return t, nil
+ }
+
+ t, err := l.storage.Term(i)
+ if err == nil {
+ return t, nil
+ }
+ if err == ErrCompacted {
+ return 0, err
+ }
+ panic(err) // TODO(bdarnell)
+}
+
+func (l *raftLog) entries(i, maxsize uint64) ([]pb.Entry, error) {
+ if i > l.lastIndex() {
+ return nil, nil
+ }
+ return l.slice(i, l.lastIndex()+1, maxsize)
+}
+
+// allEntries returns all entries in the log.
+func (l *raftLog) allEntries() []pb.Entry {
+ ents, err := l.entries(l.firstIndex(), noLimit)
+ if err == nil {
+ return ents
+ }
+ if err == ErrCompacted { // try again if there was a racing compaction
+ return l.allEntries()
+ }
+ // TODO (xiangli): handle error?
+ panic(err)
+}
+
+// isUpToDate determines if the given (lastIndex,term) log is more up-to-date
+// by comparing the index and term of the last entries in the existing logs.
+// If the logs have last entries with different terms, then the log with the
+// later term is more up-to-date. If the logs end with the same term, then
+// whichever log has the larger lastIndex is more up-to-date. If the logs are
+// the same, the given log is up-to-date.
+func (l *raftLog) isUpToDate(lasti, term uint64) bool {
+ return term > l.lastTerm() || (term == l.lastTerm() && lasti >= l.lastIndex())
+}
+
+func (l *raftLog) matchTerm(i, term uint64) bool {
+ t, err := l.term(i)
+ if err != nil {
+ return false
+ }
+ return t == term
+}
+
+func (l *raftLog) maybeCommit(maxIndex, term uint64) bool {
+ if maxIndex > l.committed && l.zeroTermOnErrCompacted(l.term(maxIndex)) == term {
+ l.commitTo(maxIndex)
+ return true
+ }
+ return false
+}
+
+func (l *raftLog) restore(s pb.Snapshot) {
+ l.logger.Infof("log [%s] starts to restore snapshot [index: %d, term: %d]", l, s.Metadata.Index, s.Metadata.Term)
+ l.committed = s.Metadata.Index
+ l.unstable.restore(s)
+}
+
+// slice returns a slice of log entries from lo through hi-1, inclusive.
+func (l *raftLog) slice(lo, hi, maxSize uint64) ([]pb.Entry, error) {
+ err := l.mustCheckOutOfBounds(lo, hi)
+ if err != nil {
+ return nil, err
+ }
+ if lo == hi {
+ return nil, nil
+ }
+ var ents []pb.Entry
+ if lo < l.unstable.offset {
+ storedEnts, err := l.storage.Entries(lo, min(hi, l.unstable.offset), maxSize)
+ if err == ErrCompacted {
+ return nil, err
+ } else if err == ErrUnavailable {
+ l.logger.Panicf("entries[%d:%d) is unavailable from storage", lo, min(hi, l.unstable.offset))
+ } else if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+
+ // check if ents has reached the size limitation
+ if uint64(len(storedEnts)) < min(hi, l.unstable.offset)-lo {
+ return storedEnts, nil
+ }
+
+ ents = storedEnts
+ }
+ if hi > l.unstable.offset {
+ unstable := l.unstable.slice(max(lo, l.unstable.offset), hi)
+ if len(ents) > 0 {
+ ents = append([]pb.Entry{}, ents...)
+ ents = append(ents, unstable...)
+ } else {
+ ents = unstable
+ }
+ }
+ return limitSize(ents, maxSize), nil
+}
+
+// l.firstIndex <= lo <= hi <= l.firstIndex + len(l.entries)
+func (l *raftLog) mustCheckOutOfBounds(lo, hi uint64) error {
+ if lo > hi {
+ l.logger.Panicf("invalid slice %d > %d", lo, hi)
+ }
+ fi := l.firstIndex()
+ if lo < fi {
+ return ErrCompacted
+ }
+
+ length := l.lastIndex() - fi + 1
+ if lo < fi || hi > fi+length {
+ l.logger.Panicf("slice[%d,%d) out of bound [%d,%d]", lo, hi, fi, l.lastIndex())
+ }
+ return nil
+}
+
+func (l *raftLog) zeroTermOnErrCompacted(t uint64, err error) uint64 {
+ if err == nil {
+ return t
+ }
+ if err == ErrCompacted {
+ return 0
+ }
+ l.logger.Panicf("unexpected error (%v)", err)
+ return 0
+}
diff --git a/vendor/github.com/coreos/etcd/raft/log_test.go b/vendor/github.com/coreos/etcd/raft/log_test.go
new file mode 100644
index 000000000..1b0590075
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/log_test.go
@@ -0,0 +1,786 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "reflect"
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestFindConflict(t *testing.T) {
+ previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
+ tests := []struct {
+ ents []pb.Entry
+ wconflict uint64
+ }{
+ // no conflict, empty ent
+ {[]pb.Entry{}, 0},
+ {[]pb.Entry{}, 0},
+ // no conflict
+ {[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}, 0},
+ {[]pb.Entry{{Index: 2, Term: 2}, {Index: 3, Term: 3}}, 0},
+ {[]pb.Entry{{Index: 3, Term: 3}}, 0},
+ // no conflict, but has new entries
+ {[]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
+ {[]pb.Entry{{Index: 2, Term: 2}, {Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
+ {[]pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
+ {[]pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 4}}, 4},
+ // conflicts with existing entries
+ {[]pb.Entry{{Index: 1, Term: 4}, {Index: 2, Term: 4}}, 1},
+ {[]pb.Entry{{Index: 2, Term: 1}, {Index: 3, Term: 4}, {Index: 4, Term: 4}}, 2},
+ {[]pb.Entry{{Index: 3, Term: 1}, {Index: 4, Term: 2}, {Index: 5, Term: 4}, {Index: 6, Term: 4}}, 3},
+ }
+
+ for i, tt := range tests {
+ raftLog := newLog(NewMemoryStorage(), raftLogger)
+ raftLog.append(previousEnts...)
+
+ gconflict := raftLog.findConflict(tt.ents)
+ if gconflict != tt.wconflict {
+ t.Errorf("#%d: conflict = %d, want %d", i, gconflict, tt.wconflict)
+ }
+ }
+}
+
+func TestIsUpToDate(t *testing.T) {
+ previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
+ raftLog := newLog(NewMemoryStorage(), raftLogger)
+ raftLog.append(previousEnts...)
+ tests := []struct {
+ lastIndex uint64
+ term uint64
+ wUpToDate bool
+ }{
+ // greater term, ignore lastIndex
+ {raftLog.lastIndex() - 1, 4, true},
+ {raftLog.lastIndex(), 4, true},
+ {raftLog.lastIndex() + 1, 4, true},
+ // smaller term, ignore lastIndex
+ {raftLog.lastIndex() - 1, 2, false},
+ {raftLog.lastIndex(), 2, false},
+ {raftLog.lastIndex() + 1, 2, false},
+ // equal term, lager lastIndex wins
+ {raftLog.lastIndex() - 1, 3, false},
+ {raftLog.lastIndex(), 3, true},
+ {raftLog.lastIndex() + 1, 3, true},
+ }
+
+ for i, tt := range tests {
+ gUpToDate := raftLog.isUpToDate(tt.lastIndex, tt.term)
+ if gUpToDate != tt.wUpToDate {
+ t.Errorf("#%d: uptodate = %v, want %v", i, gUpToDate, tt.wUpToDate)
+ }
+ }
+}
+
+func TestAppend(t *testing.T) {
+ previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}
+ tests := []struct {
+ ents []pb.Entry
+ windex uint64
+ wents []pb.Entry
+ wunstable uint64
+ }{
+ {
+ []pb.Entry{},
+ 2,
+ []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}},
+ 3,
+ },
+ {
+ []pb.Entry{{Index: 3, Term: 2}},
+ 3,
+ []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 2}},
+ 3,
+ },
+ // conflicts with index 1
+ {
+ []pb.Entry{{Index: 1, Term: 2}},
+ 1,
+ []pb.Entry{{Index: 1, Term: 2}},
+ 1,
+ },
+ // conflicts with index 2
+ {
+ []pb.Entry{{Index: 2, Term: 3}, {Index: 3, Term: 3}},
+ 3,
+ []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 3}, {Index: 3, Term: 3}},
+ 2,
+ },
+ }
+
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append(previousEnts)
+ raftLog := newLog(storage, raftLogger)
+
+ index := raftLog.append(tt.ents...)
+ if index != tt.windex {
+ t.Errorf("#%d: lastIndex = %d, want %d", i, index, tt.windex)
+ }
+ g, err := raftLog.entries(1, noLimit)
+ if err != nil {
+ t.Fatalf("#%d: unexpected error %v", i, err)
+ }
+ if !reflect.DeepEqual(g, tt.wents) {
+ t.Errorf("#%d: logEnts = %+v, want %+v", i, g, tt.wents)
+ }
+ if goff := raftLog.unstable.offset; goff != tt.wunstable {
+ t.Errorf("#%d: unstable = %d, want %d", i, goff, tt.wunstable)
+ }
+ }
+}
+
+// TestLogMaybeAppend ensures:
+// If the given (index, term) matches with the existing log:
+// 1. If an existing entry conflicts with a new one (same index
+// but different terms), delete the existing entry and all that
+// follow it
+// 2.Append any new entries not already in the log
+// If the given (index, term) does not match with the existing log:
+// return false
+func TestLogMaybeAppend(t *testing.T) {
+ previousEnts := []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}}
+ lastindex := uint64(3)
+ lastterm := uint64(3)
+ commit := uint64(1)
+
+ tests := []struct {
+ logTerm uint64
+ index uint64
+ committed uint64
+ ents []pb.Entry
+
+ wlasti uint64
+ wappend bool
+ wcommit uint64
+ wpanic bool
+ }{
+ // not match: term is different
+ {
+ lastterm - 1, lastindex, lastindex, []pb.Entry{{Index: lastindex + 1, Term: 4}},
+ 0, false, commit, false,
+ },
+ // not match: index out of bound
+ {
+ lastterm, lastindex + 1, lastindex, []pb.Entry{{Index: lastindex + 2, Term: 4}},
+ 0, false, commit, false,
+ },
+ // match with the last existing entry
+ {
+ lastterm, lastindex, lastindex, nil,
+ lastindex, true, lastindex, false,
+ },
+ {
+ lastterm, lastindex, lastindex + 1, nil,
+ lastindex, true, lastindex, false, // do not increase commit higher than lastnewi
+ },
+ {
+ lastterm, lastindex, lastindex - 1, nil,
+ lastindex, true, lastindex - 1, false, // commit up to the commit in the message
+ },
+ {
+ lastterm, lastindex, 0, nil,
+ lastindex, true, commit, false, // commit do not decrease
+ },
+ {
+ 0, 0, lastindex, nil,
+ 0, true, commit, false, // commit do not decrease
+ },
+ {
+ lastterm, lastindex, lastindex, []pb.Entry{{Index: lastindex + 1, Term: 4}},
+ lastindex + 1, true, lastindex, false,
+ },
+ {
+ lastterm, lastindex, lastindex + 1, []pb.Entry{{Index: lastindex + 1, Term: 4}},
+ lastindex + 1, true, lastindex + 1, false,
+ },
+ {
+ lastterm, lastindex, lastindex + 2, []pb.Entry{{Index: lastindex + 1, Term: 4}},
+ lastindex + 1, true, lastindex + 1, false, // do not increase commit higher than lastnewi
+ },
+ {
+ lastterm, lastindex, lastindex + 2, []pb.Entry{{Index: lastindex + 1, Term: 4}, {Index: lastindex + 2, Term: 4}},
+ lastindex + 2, true, lastindex + 2, false,
+ },
+ // match with the the entry in the middle
+ {
+ lastterm - 1, lastindex - 1, lastindex, []pb.Entry{{Index: lastindex, Term: 4}},
+ lastindex, true, lastindex, false,
+ },
+ {
+ lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Index: lastindex - 1, Term: 4}},
+ lastindex - 1, true, lastindex - 1, false,
+ },
+ {
+ lastterm - 3, lastindex - 3, lastindex, []pb.Entry{{Index: lastindex - 2, Term: 4}},
+ lastindex - 2, true, lastindex - 2, true, // conflict with existing committed entry
+ },
+ {
+ lastterm - 2, lastindex - 2, lastindex, []pb.Entry{{Index: lastindex - 1, Term: 4}, {Index: lastindex, Term: 4}},
+ lastindex, true, lastindex, false,
+ },
+ }
+
+ for i, tt := range tests {
+ raftLog := newLog(NewMemoryStorage(), raftLogger)
+ raftLog.append(previousEnts...)
+ raftLog.committed = commit
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if tt.wpanic != true {
+ t.Errorf("%d: panic = %v, want %v", i, true, tt.wpanic)
+ }
+ }
+ }()
+ glasti, gappend := raftLog.maybeAppend(tt.index, tt.logTerm, tt.committed, tt.ents...)
+ gcommit := raftLog.committed
+
+ if glasti != tt.wlasti {
+ t.Errorf("#%d: lastindex = %d, want %d", i, glasti, tt.wlasti)
+ }
+ if gappend != tt.wappend {
+ t.Errorf("#%d: append = %v, want %v", i, gappend, tt.wappend)
+ }
+ if gcommit != tt.wcommit {
+ t.Errorf("#%d: committed = %d, want %d", i, gcommit, tt.wcommit)
+ }
+ if gappend && len(tt.ents) != 0 {
+ gents, err := raftLog.slice(raftLog.lastIndex()-uint64(len(tt.ents))+1, raftLog.lastIndex()+1, noLimit)
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ if !reflect.DeepEqual(tt.ents, gents) {
+ t.Errorf("%d: appended entries = %v, want %v", i, gents, tt.ents)
+ }
+ }
+ }()
+ }
+}
+
+// TestCompactionSideEffects ensures that all the log related funcationality works correctly after
+// a compaction.
+func TestCompactionSideEffects(t *testing.T) {
+ var i uint64
+ // Populate the log with 1000 entries; 750 in stable storage and 250 in unstable.
+ lastIndex := uint64(1000)
+ unstableIndex := uint64(750)
+ lastTerm := lastIndex
+ storage := NewMemoryStorage()
+ for i = 1; i <= unstableIndex; i++ {
+ storage.Append([]pb.Entry{{Term: uint64(i), Index: uint64(i)}})
+ }
+ raftLog := newLog(storage, raftLogger)
+ for i = unstableIndex; i < lastIndex; i++ {
+ raftLog.append(pb.Entry{Term: uint64(i + 1), Index: uint64(i + 1)})
+ }
+
+ ok := raftLog.maybeCommit(lastIndex, lastTerm)
+ if !ok {
+ t.Fatalf("maybeCommit returned false")
+ }
+ raftLog.appliedTo(raftLog.committed)
+
+ offset := uint64(500)
+ storage.Compact(offset)
+
+ if raftLog.lastIndex() != lastIndex {
+ t.Errorf("lastIndex = %d, want %d", raftLog.lastIndex(), lastIndex)
+ }
+
+ for j := offset; j <= raftLog.lastIndex(); j++ {
+ if mustTerm(raftLog.term(j)) != j {
+ t.Errorf("term(%d) = %d, want %d", j, mustTerm(raftLog.term(j)), j)
+ }
+ }
+
+ for j := offset; j <= raftLog.lastIndex(); j++ {
+ if !raftLog.matchTerm(j, j) {
+ t.Errorf("matchTerm(%d) = false, want true", j)
+ }
+ }
+
+ unstableEnts := raftLog.unstableEntries()
+ if g := len(unstableEnts); g != 250 {
+ t.Errorf("len(unstableEntries) = %d, want = %d", g, 250)
+ }
+ if unstableEnts[0].Index != 751 {
+ t.Errorf("Index = %d, want = %d", unstableEnts[0].Index, 751)
+ }
+
+ prev := raftLog.lastIndex()
+ raftLog.append(pb.Entry{Index: raftLog.lastIndex() + 1, Term: raftLog.lastIndex() + 1})
+ if raftLog.lastIndex() != prev+1 {
+ t.Errorf("lastIndex = %d, want = %d", raftLog.lastIndex(), prev+1)
+ }
+
+ ents, err := raftLog.entries(raftLog.lastIndex(), noLimit)
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ if len(ents) != 1 {
+ t.Errorf("len(entries) = %d, want = %d", len(ents), 1)
+ }
+}
+
+func TestNextEnts(t *testing.T) {
+ snap := pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{Term: 1, Index: 3},
+ }
+ ents := []pb.Entry{
+ {Term: 1, Index: 4},
+ {Term: 1, Index: 5},
+ {Term: 1, Index: 6},
+ }
+ tests := []struct {
+ applied uint64
+ wents []pb.Entry
+ }{
+ {0, ents[:2]},
+ {3, ents[:2]},
+ {4, ents[1:2]},
+ {5, nil},
+ }
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(snap)
+ raftLog := newLog(storage, raftLogger)
+ raftLog.append(ents...)
+ raftLog.maybeCommit(5, 1)
+ raftLog.appliedTo(tt.applied)
+
+ nents := raftLog.nextEnts()
+ if !reflect.DeepEqual(nents, tt.wents) {
+ t.Errorf("#%d: nents = %+v, want %+v", i, nents, tt.wents)
+ }
+ }
+}
+
+// TestUnstableEnts ensures unstableEntries returns the unstable part of the
+// entries correctly.
+func TestUnstableEnts(t *testing.T) {
+ previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}
+ tests := []struct {
+ unstable uint64
+ wents []pb.Entry
+ }{
+ {3, nil},
+ {1, previousEnts},
+ }
+
+ for i, tt := range tests {
+ // append stable entries to storage
+ storage := NewMemoryStorage()
+ storage.Append(previousEnts[:tt.unstable-1])
+
+ // append unstable entries to raftlog
+ raftLog := newLog(storage, raftLogger)
+ raftLog.append(previousEnts[tt.unstable-1:]...)
+
+ ents := raftLog.unstableEntries()
+ if l := len(ents); l > 0 {
+ raftLog.stableTo(ents[l-1].Index, ents[l-i].Term)
+ }
+ if !reflect.DeepEqual(ents, tt.wents) {
+ t.Errorf("#%d: unstableEnts = %+v, want %+v", i, ents, tt.wents)
+ }
+ w := previousEnts[len(previousEnts)-1].Index + 1
+ if g := raftLog.unstable.offset; g != w {
+ t.Errorf("#%d: unstable = %d, want %d", i, g, w)
+ }
+ }
+}
+
+func TestCommitTo(t *testing.T) {
+ previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}, {Term: 3, Index: 3}}
+ commit := uint64(2)
+ tests := []struct {
+ commit uint64
+ wcommit uint64
+ wpanic bool
+ }{
+ {3, 3, false},
+ {1, 2, false}, // never decrease
+ {4, 0, true}, // commit out of range -> panic
+ }
+ for i, tt := range tests {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if tt.wpanic != true {
+ t.Errorf("%d: panic = %v, want %v", i, true, tt.wpanic)
+ }
+ }
+ }()
+ raftLog := newLog(NewMemoryStorage(), raftLogger)
+ raftLog.append(previousEnts...)
+ raftLog.committed = commit
+ raftLog.commitTo(tt.commit)
+ if raftLog.committed != tt.wcommit {
+ t.Errorf("#%d: committed = %d, want %d", i, raftLog.committed, tt.wcommit)
+ }
+ }()
+ }
+}
+
+func TestStableTo(t *testing.T) {
+ tests := []struct {
+ stablei uint64
+ stablet uint64
+ wunstable uint64
+ }{
+ {1, 1, 2},
+ {2, 2, 3},
+ {2, 1, 1}, // bad term
+ {3, 1, 1}, // bad index
+ }
+ for i, tt := range tests {
+ raftLog := newLog(NewMemoryStorage(), raftLogger)
+ raftLog.append([]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}...)
+ raftLog.stableTo(tt.stablei, tt.stablet)
+ if raftLog.unstable.offset != tt.wunstable {
+ t.Errorf("#%d: unstable = %d, want %d", i, raftLog.unstable.offset, tt.wunstable)
+ }
+ }
+}
+
+func TestStableToWithSnap(t *testing.T) {
+ snapi, snapt := uint64(5), uint64(2)
+ tests := []struct {
+ stablei uint64
+ stablet uint64
+ newEnts []pb.Entry
+
+ wunstable uint64
+ }{
+ {snapi + 1, snapt, nil, snapi + 1},
+ {snapi, snapt, nil, snapi + 1},
+ {snapi - 1, snapt, nil, snapi + 1},
+
+ {snapi + 1, snapt + 1, nil, snapi + 1},
+ {snapi, snapt + 1, nil, snapi + 1},
+ {snapi - 1, snapt + 1, nil, snapi + 1},
+
+ {snapi + 1, snapt, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 2},
+ {snapi, snapt, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
+ {snapi - 1, snapt, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
+
+ {snapi + 1, snapt + 1, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
+ {snapi, snapt + 1, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
+ {snapi - 1, snapt + 1, []pb.Entry{{Index: snapi + 1, Term: snapt}}, snapi + 1},
+ }
+ for i, tt := range tests {
+ s := NewMemoryStorage()
+ s.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: snapi, Term: snapt}})
+ raftLog := newLog(s, raftLogger)
+ raftLog.append(tt.newEnts...)
+ raftLog.stableTo(tt.stablei, tt.stablet)
+ if raftLog.unstable.offset != tt.wunstable {
+ t.Errorf("#%d: unstable = %d, want %d", i, raftLog.unstable.offset, tt.wunstable)
+ }
+ }
+}
+
+//TestCompaction ensures that the number of log entries is correct after compactions.
+func TestCompaction(t *testing.T) {
+ tests := []struct {
+ lastIndex uint64
+ compact []uint64
+ wleft []int
+ wallow bool
+ }{
+ // out of upper bound
+ {1000, []uint64{1001}, []int{-1}, false},
+ {1000, []uint64{300, 500, 800, 900}, []int{700, 500, 200, 100}, true},
+ // out of lower bound
+ {1000, []uint64{300, 299}, []int{700, -1}, false},
+ }
+
+ for i, tt := range tests {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if tt.wallow == true {
+ t.Errorf("%d: allow = %v, want %v: %v", i, false, true, r)
+ }
+ }
+ }()
+
+ storage := NewMemoryStorage()
+ for i := uint64(1); i <= tt.lastIndex; i++ {
+ storage.Append([]pb.Entry{{Index: i}})
+ }
+ raftLog := newLog(storage, raftLogger)
+ raftLog.maybeCommit(tt.lastIndex, 0)
+ raftLog.appliedTo(raftLog.committed)
+
+ for j := 0; j < len(tt.compact); j++ {
+ err := storage.Compact(tt.compact[j])
+ if err != nil {
+ if tt.wallow {
+ t.Errorf("#%d.%d allow = %t, want %t", i, j, false, tt.wallow)
+ }
+ continue
+ }
+ if len(raftLog.allEntries()) != tt.wleft[j] {
+ t.Errorf("#%d.%d len = %d, want %d", i, j, len(raftLog.allEntries()), tt.wleft[j])
+ }
+ }
+ }()
+ }
+}
+
+func TestLogRestore(t *testing.T) {
+ index := uint64(1000)
+ term := uint64(1000)
+ snap := pb.SnapshotMetadata{Index: index, Term: term}
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(pb.Snapshot{Metadata: snap})
+ raftLog := newLog(storage, raftLogger)
+
+ if len(raftLog.allEntries()) != 0 {
+ t.Errorf("len = %d, want 0", len(raftLog.allEntries()))
+ }
+ if raftLog.firstIndex() != index+1 {
+ t.Errorf("firstIndex = %d, want %d", raftLog.firstIndex(), index+1)
+ }
+ if raftLog.committed != index {
+ t.Errorf("committed = %d, want %d", raftLog.committed, index)
+ }
+ if raftLog.unstable.offset != index+1 {
+ t.Errorf("unstable = %d, want %d", raftLog.unstable.offset, index+1)
+ }
+ if mustTerm(raftLog.term(index)) != term {
+ t.Errorf("term = %d, want %d", mustTerm(raftLog.term(index)), term)
+ }
+}
+
+func TestIsOutOfBounds(t *testing.T) {
+ offset := uint64(100)
+ num := uint64(100)
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: offset}})
+ l := newLog(storage, raftLogger)
+ for i := uint64(1); i <= num; i++ {
+ l.append(pb.Entry{Index: i + offset})
+ }
+
+ first := offset + 1
+ tests := []struct {
+ lo, hi uint64
+ wpanic bool
+ wErrCompacted bool
+ }{
+ {
+ first - 2, first + 1,
+ false,
+ true,
+ },
+ {
+ first - 1, first + 1,
+ false,
+ true,
+ },
+ {
+ first, first,
+ false,
+ false,
+ },
+ {
+ first + num/2, first + num/2,
+ false,
+ false,
+ },
+ {
+ first + num - 1, first + num - 1,
+ false,
+ false,
+ },
+ {
+ first + num, first + num,
+ false,
+ false,
+ },
+ {
+ first + num, first + num + 1,
+ true,
+ false,
+ },
+ {
+ first + num + 1, first + num + 1,
+ true,
+ false,
+ },
+ }
+
+ for i, tt := range tests {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if !tt.wpanic {
+ t.Errorf("%d: panic = %v, want %v: %v", i, true, false, r)
+ }
+ }
+ }()
+ err := l.mustCheckOutOfBounds(tt.lo, tt.hi)
+ if tt.wpanic {
+ t.Errorf("%d: panic = %v, want %v", i, false, true)
+ }
+ if tt.wErrCompacted && err != ErrCompacted {
+ t.Errorf("%d: err = %v, want %v", i, err, ErrCompacted)
+ }
+ if !tt.wErrCompacted && err != nil {
+ t.Errorf("%d: unexpected err %v", i, err)
+ }
+ }()
+ }
+}
+
+func TestTerm(t *testing.T) {
+ var i uint64
+ offset := uint64(100)
+ num := uint64(100)
+
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: offset, Term: 1}})
+ l := newLog(storage, raftLogger)
+ for i = 1; i < num; i++ {
+ l.append(pb.Entry{Index: offset + i, Term: i})
+ }
+
+ tests := []struct {
+ index uint64
+ w uint64
+ }{
+ {offset - 1, 0},
+ {offset, 1},
+ {offset + num/2, num / 2},
+ {offset + num - 1, num - 1},
+ {offset + num, 0},
+ }
+
+ for j, tt := range tests {
+ term := mustTerm(l.term(tt.index))
+ if !reflect.DeepEqual(term, tt.w) {
+ t.Errorf("#%d: at = %d, want %d", j, term, tt.w)
+ }
+ }
+}
+
+func TestTermWithUnstableSnapshot(t *testing.T) {
+ storagesnapi := uint64(100)
+ unstablesnapi := storagesnapi + 5
+
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: storagesnapi, Term: 1}})
+ l := newLog(storage, raftLogger)
+ l.restore(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: unstablesnapi, Term: 1}})
+
+ tests := []struct {
+ index uint64
+ w uint64
+ }{
+ // cannot get term from storage
+ {storagesnapi, 0},
+ // cannot get term from the gap between storage ents and unstable snapshot
+ {storagesnapi + 1, 0},
+ {unstablesnapi - 1, 0},
+ // get term from unstable snapshot index
+ {unstablesnapi, 1},
+ }
+
+ for i, tt := range tests {
+ term := mustTerm(l.term(tt.index))
+ if !reflect.DeepEqual(term, tt.w) {
+ t.Errorf("#%d: at = %d, want %d", i, term, tt.w)
+ }
+ }
+}
+
+func TestSlice(t *testing.T) {
+ var i uint64
+ offset := uint64(100)
+ num := uint64(100)
+ last := offset + num
+ half := offset + num/2
+ halfe := pb.Entry{Index: half, Term: half}
+
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: offset}})
+ for i = 1; i < num/2; i++ {
+ storage.Append([]pb.Entry{{Index: offset + i, Term: offset + i}})
+ }
+ l := newLog(storage, raftLogger)
+ for i = num / 2; i < num; i++ {
+ l.append(pb.Entry{Index: offset + i, Term: offset + i})
+ }
+
+ tests := []struct {
+ from uint64
+ to uint64
+ limit uint64
+
+ w []pb.Entry
+ wpanic bool
+ }{
+ // test no limit
+ {offset - 1, offset + 1, noLimit, nil, false},
+ {offset, offset + 1, noLimit, nil, false},
+ {half - 1, half + 1, noLimit, []pb.Entry{{Index: half - 1, Term: half - 1}, {Index: half, Term: half}}, false},
+ {half, half + 1, noLimit, []pb.Entry{{Index: half, Term: half}}, false},
+ {last - 1, last, noLimit, []pb.Entry{{Index: last - 1, Term: last - 1}}, false},
+ {last, last + 1, noLimit, nil, true},
+
+ // test limit
+ {half - 1, half + 1, 0, []pb.Entry{{Index: half - 1, Term: half - 1}}, false},
+ {half - 1, half + 1, uint64(halfe.Size() + 1), []pb.Entry{{Index: half - 1, Term: half - 1}}, false},
+ {half - 1, half + 1, uint64(halfe.Size() * 2), []pb.Entry{{Index: half - 1, Term: half - 1}, {Index: half, Term: half}}, false},
+ {half - 1, half + 2, uint64(halfe.Size() * 3), []pb.Entry{{Index: half - 1, Term: half - 1}, {Index: half, Term: half}, {Index: half + 1, Term: half + 1}}, false},
+ {half, half + 2, uint64(halfe.Size()), []pb.Entry{{Index: half, Term: half}}, false},
+ {half, half + 2, uint64(halfe.Size() * 2), []pb.Entry{{Index: half, Term: half}, {Index: half + 1, Term: half + 1}}, false},
+ }
+
+ for j, tt := range tests {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if !tt.wpanic {
+ t.Errorf("%d: panic = %v, want %v: %v", j, true, false, r)
+ }
+ }
+ }()
+ g, err := l.slice(tt.from, tt.to, tt.limit)
+ if tt.from <= offset && err != ErrCompacted {
+ t.Fatalf("#%d: err = %v, want %v", j, err, ErrCompacted)
+ }
+ if tt.from > offset && err != nil {
+ t.Fatalf("#%d: unexpected error %v", j, err)
+ }
+ if !reflect.DeepEqual(g, tt.w) {
+ t.Errorf("#%d: from %d to %d = %v, want %v", j, tt.from, tt.to, g, tt.w)
+ }
+ }()
+ }
+}
+
+func mustTerm(term uint64, err error) uint64 {
+ if err != nil {
+ panic(err)
+ }
+ return term
+}
diff --git a/vendor/github.com/coreos/etcd/raft/log_unstable.go b/vendor/github.com/coreos/etcd/raft/log_unstable.go
new file mode 100644
index 000000000..7a1b2353d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/log_unstable.go
@@ -0,0 +1,139 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import pb "github.com/coreos/etcd/raft/raftpb"
+
+// unstable.entris[i] has raft log position i+unstable.offset.
+// Note that unstable.offset may be less than the highest log
+// position in storage; this means that the next write to storage
+// might need to truncate the log before persisting unstable.entries.
+type unstable struct {
+ // the incoming unstable snapshot, if any.
+ snapshot *pb.Snapshot
+ // all entries that have not yet been written to storage.
+ entries []pb.Entry
+ offset uint64
+
+ logger Logger
+}
+
+// maybeFirstIndex returns the index of the first possible entry in entries
+// if it has a snapshot.
+func (u *unstable) maybeFirstIndex() (uint64, bool) {
+ if u.snapshot != nil {
+ return u.snapshot.Metadata.Index + 1, true
+ }
+ return 0, false
+}
+
+// maybeLastIndex returns the last index if it has at least one
+// unstable entry or snapshot.
+func (u *unstable) maybeLastIndex() (uint64, bool) {
+ if l := len(u.entries); l != 0 {
+ return u.offset + uint64(l) - 1, true
+ }
+ if u.snapshot != nil {
+ return u.snapshot.Metadata.Index, true
+ }
+ return 0, false
+}
+
+// myabeTerm returns the term of the entry at index i, if there
+// is any.
+func (u *unstable) maybeTerm(i uint64) (uint64, bool) {
+ if i < u.offset {
+ if u.snapshot == nil {
+ return 0, false
+ }
+ if u.snapshot.Metadata.Index == i {
+ return u.snapshot.Metadata.Term, true
+ }
+ return 0, false
+ }
+
+ last, ok := u.maybeLastIndex()
+ if !ok {
+ return 0, false
+ }
+ if i > last {
+ return 0, false
+ }
+ return u.entries[i-u.offset].Term, true
+}
+
+func (u *unstable) stableTo(i, t uint64) {
+ gt, ok := u.maybeTerm(i)
+ if !ok {
+ return
+ }
+ // if i < offest, term is matched with the snapshot
+ // only update the unstalbe entries if term is matched with
+ // an unstable entry.
+ if gt == t && i >= u.offset {
+ u.entries = u.entries[i+1-u.offset:]
+ u.offset = i + 1
+ }
+}
+
+func (u *unstable) stableSnapTo(i uint64) {
+ if u.snapshot != nil && u.snapshot.Metadata.Index == i {
+ u.snapshot = nil
+ }
+}
+
+func (u *unstable) restore(s pb.Snapshot) {
+ u.offset = s.Metadata.Index + 1
+ u.entries = nil
+ u.snapshot = &s
+}
+
+func (u *unstable) truncateAndAppend(ents []pb.Entry) {
+ after := ents[0].Index - 1
+ switch {
+ case after == u.offset+uint64(len(u.entries))-1:
+ // after is the last index in the u.entries
+ // directly append
+ u.entries = append(u.entries, ents...)
+ case after < u.offset:
+ u.logger.Infof("replace the unstable entries from index %d", after+1)
+ // The log is being truncated to before our current offset
+ // portion, so set the offset and replace the entries
+ u.offset = after + 1
+ u.entries = ents
+ default:
+ // truncate to after and copy to u.entries
+ // then append
+ u.logger.Infof("truncate the unstable entries to index %d", after)
+ u.entries = append([]pb.Entry{}, u.slice(u.offset, after+1)...)
+ u.entries = append(u.entries, ents...)
+ }
+}
+
+func (u *unstable) slice(lo uint64, hi uint64) []pb.Entry {
+ u.mustCheckOutOfBounds(lo, hi)
+ return u.entries[lo-u.offset : hi-u.offset]
+}
+
+// u.offset <= lo <= hi <= u.offset+len(u.offset)
+func (u *unstable) mustCheckOutOfBounds(lo, hi uint64) {
+ if lo > hi {
+ u.logger.Panicf("invalid unstable.slice %d > %d", lo, hi)
+ }
+ upper := u.offset + uint64(len(u.entries))
+ if lo < u.offset || hi > upper {
+ u.logger.Panicf("unstable.slice[%d,%d) out of bound [%d,%d]", lo, hi, u.offset, upper)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/log_unstable_test.go b/vendor/github.com/coreos/etcd/raft/log_unstable_test.go
new file mode 100644
index 000000000..8012795ec
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/log_unstable_test.go
@@ -0,0 +1,354 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "reflect"
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestUnstableMaybeFirstIndex(t *testing.T) {
+ tests := []struct {
+ entries []pb.Entry
+ offset uint64
+ snap *pb.Snapshot
+
+ wok bool
+ windex uint64
+ }{
+ // no snapshot
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ false, 0,
+ },
+ {
+ []pb.Entry{}, 0, nil,
+ false, 0,
+ },
+ // has snapshot
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ true, 5,
+ },
+ {
+ []pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ true, 5,
+ },
+ }
+
+ for i, tt := range tests {
+ u := unstable{
+ entries: tt.entries,
+ offset: tt.offset,
+ snapshot: tt.snap,
+ logger: raftLogger,
+ }
+ index, ok := u.maybeFirstIndex()
+ if ok != tt.wok {
+ t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
+ }
+ if index != tt.windex {
+ t.Errorf("#%d: index = %d, want %d", i, index, tt.windex)
+ }
+ }
+}
+
+func TestMaybeLastIndex(t *testing.T) {
+ tests := []struct {
+ entries []pb.Entry
+ offset uint64
+ snap *pb.Snapshot
+
+ wok bool
+ windex uint64
+ }{
+ // last in entries
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ true, 5,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ true, 5,
+ },
+ // last in snapshot
+ {
+ []pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ true, 4,
+ },
+ // empty unstable
+ {
+ []pb.Entry{}, 0, nil,
+ false, 0,
+ },
+ }
+
+ for i, tt := range tests {
+ u := unstable{
+ entries: tt.entries,
+ offset: tt.offset,
+ snapshot: tt.snap,
+ logger: raftLogger,
+ }
+ index, ok := u.maybeLastIndex()
+ if ok != tt.wok {
+ t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
+ }
+ if index != tt.windex {
+ t.Errorf("#%d: index = %d, want %d", i, index, tt.windex)
+ }
+ }
+}
+
+func TestUnstableMaybeTerm(t *testing.T) {
+ tests := []struct {
+ entries []pb.Entry
+ offset uint64
+ snap *pb.Snapshot
+ index uint64
+
+ wok bool
+ wterm uint64
+ }{
+ // term from entries
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ 5,
+ true, 1,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ 6,
+ false, 0,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ 4,
+ false, 0,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 5,
+ true, 1,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 6,
+ false, 0,
+ },
+ // term from snapshot
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 4,
+ true, 1,
+ },
+ {
+ []pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 5,
+ false, 0,
+ },
+ {
+ []pb.Entry{}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 4,
+ true, 1,
+ },
+ {
+ []pb.Entry{}, 0, nil,
+ 5,
+ false, 0,
+ },
+ }
+
+ for i, tt := range tests {
+ u := unstable{
+ entries: tt.entries,
+ offset: tt.offset,
+ snapshot: tt.snap,
+ logger: raftLogger,
+ }
+ term, ok := u.maybeTerm(tt.index)
+ if ok != tt.wok {
+ t.Errorf("#%d: ok = %t, want %t", i, ok, tt.wok)
+ }
+ if term != tt.wterm {
+ t.Errorf("#%d: term = %d, want %d", i, term, tt.wterm)
+ }
+ }
+}
+
+func TestUnstableRestore(t *testing.T) {
+ u := unstable{
+ entries: []pb.Entry{{Index: 5, Term: 1}},
+ offset: 5,
+ snapshot: &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ logger: raftLogger,
+ }
+ s := pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 6, Term: 2}}
+ u.restore(s)
+
+ if u.offset != s.Metadata.Index+1 {
+ t.Errorf("offset = %d, want %d", u.offset, s.Metadata.Index+1)
+ }
+ if len(u.entries) != 0 {
+ t.Errorf("len = %d, want 0", len(u.entries))
+ }
+ if !reflect.DeepEqual(u.snapshot, &s) {
+ t.Errorf("snap = %v, want %v", u.snapshot, &s)
+ }
+}
+
+func TestUnstableStableTo(t *testing.T) {
+ tests := []struct {
+ entries []pb.Entry
+ offset uint64
+ snap *pb.Snapshot
+ index, term uint64
+
+ woffset uint64
+ wlen int
+ }{
+ {
+ []pb.Entry{}, 0, nil,
+ 5, 1,
+ 0, 0,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ 5, 1, // stable to the first entry
+ 6, 0,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}}, 5, nil,
+ 5, 1, // stable to the first entry
+ 6, 1,
+ },
+ {
+ []pb.Entry{{Index: 6, Term: 2}}, 5, nil,
+ 6, 1, // stable to the first entry and term mismatch
+ 5, 1,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ 4, 1, // stable to old entry
+ 5, 1,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ 4, 2, // stable to old entry
+ 5, 1,
+ },
+ // with snapshot
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 5, 1, // stable to the first entry
+ 6, 0,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 5, 1, // stable to the first entry
+ 6, 1,
+ },
+ {
+ []pb.Entry{{Index: 6, Term: 2}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 5, Term: 1}},
+ 6, 1, // stable to the first entry and term mismatch
+ 5, 1,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 1}},
+ 4, 1, // stable to snapshot
+ 5, 1,
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 2}}, 5, &pb.Snapshot{Metadata: pb.SnapshotMetadata{Index: 4, Term: 2}},
+ 4, 1, // stable to old entry
+ 5, 1,
+ },
+ }
+
+ for i, tt := range tests {
+ u := unstable{
+ entries: tt.entries,
+ offset: tt.offset,
+ snapshot: tt.snap,
+ logger: raftLogger,
+ }
+ u.stableTo(tt.index, tt.term)
+ if u.offset != tt.woffset {
+ t.Errorf("#%d: offset = %d, want %d", i, u.offset, tt.woffset)
+ }
+ if len(u.entries) != tt.wlen {
+ t.Errorf("#%d: len = %d, want %d", i, len(u.entries), tt.wlen)
+ }
+ }
+}
+
+func TestUnstableTruncateAndAppend(t *testing.T) {
+ tests := []struct {
+ entries []pb.Entry
+ offset uint64
+ snap *pb.Snapshot
+ toappend []pb.Entry
+
+ woffset uint64
+ wentries []pb.Entry
+ }{
+ // append to the end
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ []pb.Entry{{Index: 6, Term: 1}, {Index: 7, Term: 1}},
+ 5, []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 1}},
+ },
+ // replace the unstable entries
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ []pb.Entry{{Index: 5, Term: 2}, {Index: 6, Term: 2}},
+ 5, []pb.Entry{{Index: 5, Term: 2}, {Index: 6, Term: 2}},
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}}, 5, nil,
+ []pb.Entry{{Index: 4, Term: 2}, {Index: 5, Term: 2}, {Index: 6, Term: 2}},
+ 4, []pb.Entry{{Index: 4, Term: 2}, {Index: 5, Term: 2}, {Index: 6, Term: 2}},
+ },
+ // truncate the existing entries and append
+ {
+ []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 1}}, 5, nil,
+ []pb.Entry{{Index: 6, Term: 2}},
+ 5, []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 2}},
+ },
+ {
+ []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 1}}, 5, nil,
+ []pb.Entry{{Index: 7, Term: 2}, {Index: 8, Term: 2}},
+ 5, []pb.Entry{{Index: 5, Term: 1}, {Index: 6, Term: 1}, {Index: 7, Term: 2}, {Index: 8, Term: 2}},
+ },
+ }
+
+ for i, tt := range tests {
+ u := unstable{
+ entries: tt.entries,
+ offset: tt.offset,
+ snapshot: tt.snap,
+ logger: raftLogger,
+ }
+ u.truncateAndAppend(tt.toappend)
+ if u.offset != tt.woffset {
+ t.Errorf("#%d: offset = %d, want %d", i, u.offset, tt.woffset)
+ }
+ if !reflect.DeepEqual(u.entries, tt.wentries) {
+ t.Errorf("#%d: entries = %v, want %v", i, u.entries, tt.wentries)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/logger.go b/vendor/github.com/coreos/etcd/raft/logger.go
new file mode 100644
index 000000000..12be16f3c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/logger.go
@@ -0,0 +1,126 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+)
+
+type Logger interface {
+ Debug(v ...interface{})
+ Debugf(format string, v ...interface{})
+
+ Error(v ...interface{})
+ Errorf(format string, v ...interface{})
+
+ Info(v ...interface{})
+ Infof(format string, v ...interface{})
+
+ Warning(v ...interface{})
+ Warningf(format string, v ...interface{})
+
+ Fatal(v ...interface{})
+ Fatalf(format string, v ...interface{})
+
+ Panic(v ...interface{})
+ Panicf(format string, v ...interface{})
+}
+
+func SetLogger(l Logger) { raftLogger = l }
+
+var (
+ defaultLogger = &DefaultLogger{Logger: log.New(os.Stderr, "raft", log.LstdFlags)}
+ discardLogger = &DefaultLogger{Logger: log.New(ioutil.Discard, "", 0)}
+ raftLogger = Logger(defaultLogger)
+)
+
+const (
+ calldepth = 2
+)
+
+// DefaultLogger is a defualt implementation of the Logger interface.
+type DefaultLogger struct {
+ *log.Logger
+ debug bool
+}
+
+func (l *DefaultLogger) EnableTimestamps() {
+ l.SetFlags(l.Flags() | log.Ldate | log.Ltime)
+}
+
+func (l *DefaultLogger) EnableDebug() {
+ l.debug = true
+}
+
+func (l *DefaultLogger) Debug(v ...interface{}) {
+ if l.debug {
+ l.Output(calldepth, header("DEBUG", fmt.Sprint(v...)))
+ }
+}
+
+func (l *DefaultLogger) Debugf(format string, v ...interface{}) {
+ if l.debug {
+ l.Output(calldepth, header("DEBUG", fmt.Sprintf(format, v...)))
+ }
+}
+
+func (l *DefaultLogger) Info(v ...interface{}) {
+ l.Output(calldepth, header("INFO", fmt.Sprint(v...)))
+}
+
+func (l *DefaultLogger) Infof(format string, v ...interface{}) {
+ l.Output(calldepth, header("INFO", fmt.Sprintf(format, v...)))
+}
+
+func (l *DefaultLogger) Error(v ...interface{}) {
+ l.Output(calldepth, header("ERROR", fmt.Sprint(v...)))
+}
+
+func (l *DefaultLogger) Errorf(format string, v ...interface{}) {
+ l.Output(calldepth, header("ERROR", fmt.Sprintf(format, v...)))
+}
+
+func (l *DefaultLogger) Warning(v ...interface{}) {
+ l.Output(calldepth, header("WARN", fmt.Sprint(v...)))
+}
+
+func (l *DefaultLogger) Warningf(format string, v ...interface{}) {
+ l.Output(calldepth, header("WARN", fmt.Sprintf(format, v...)))
+}
+
+func (l *DefaultLogger) Fatal(v ...interface{}) {
+ l.Output(calldepth, header("FATAL", fmt.Sprint(v...)))
+ os.Exit(1)
+}
+
+func (l *DefaultLogger) Fatalf(format string, v ...interface{}) {
+ l.Output(calldepth, header("FATAL", fmt.Sprintf(format, v...)))
+ os.Exit(1)
+}
+
+func (l *DefaultLogger) Panic(v ...interface{}) {
+ l.Logger.Panic(v)
+}
+
+func (l *DefaultLogger) Panicf(format string, v ...interface{}) {
+ l.Logger.Panicf(format, v...)
+}
+
+func header(lvl, msg string) string {
+ return fmt.Sprintf("%s: %s", lvl, msg)
+}
diff --git a/vendor/github.com/coreos/etcd/raft/multinode.go b/vendor/github.com/coreos/etcd/raft/multinode.go
new file mode 100644
index 000000000..42d2a6959
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/multinode.go
@@ -0,0 +1,503 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+// MultiNode represents a node that is participating in multiple consensus groups.
+// A MultiNode is more efficient than a collection of Nodes.
+// The methods of this interface correspond to the methods of Node and are described
+// more fully there.
+type MultiNode interface {
+ // CreateGroup adds a new group to the MultiNode. The application must call CreateGroup
+ // on each particpating node with the same group ID; it may create groups on demand as it
+ // receives messages. If the given storage contains existing log entries the list of peers
+ // may be empty. If Config.ID field is zero it will be replaced by the ID passed
+ // to StartMultiNode.
+ CreateGroup(group uint64, c *Config, peers []Peer) error
+ // RemoveGroup removes a group from the MultiNode.
+ RemoveGroup(group uint64) error
+ // Tick advances the internal logical clock by a single tick.
+ Tick()
+ // Campaign causes this MultiNode to transition to candidate state in the given group.
+ Campaign(ctx context.Context, group uint64) error
+ // Propose proposes that data be appended to the given group's log.
+ Propose(ctx context.Context, group uint64, data []byte) error
+ // ProposeConfChange proposes a config change.
+ ProposeConfChange(ctx context.Context, group uint64, cc pb.ConfChange) error
+ // ApplyConfChange applies a config change to the local node.
+ ApplyConfChange(group uint64, cc pb.ConfChange) *pb.ConfState
+ // Step advances the state machine using the given message.
+ Step(ctx context.Context, group uint64, msg pb.Message) error
+ // Ready returns a channel that returns the current point-in-time state of any ready
+ // groups. Only groups with something to report will appear in the map.
+ Ready() <-chan map[uint64]Ready
+ // Advance notifies the node that the application has applied and saved progress in the
+ // last Ready results. It must be called with the last value returned from the Ready()
+ // channel.
+ Advance(map[uint64]Ready)
+ // Status returns the current status of the given group. Returns nil if no such group
+ // exists.
+ Status(group uint64) *Status
+ // Report reports the given node is not reachable for the last send.
+ ReportUnreachable(id, groupID uint64)
+ // ReportSnapshot reports the stutus of the sent snapshot.
+ ReportSnapshot(id, groupID uint64, status SnapshotStatus)
+ // Stop performs any necessary termination of the MultiNode.
+ Stop()
+}
+
+// StartMultiNode creates a MultiNode and starts its background
+// goroutine. If id is non-zero it identifies this node and will be
+// used as its node ID in all groups. The election and heartbeat
+// timers are in units of ticks.
+func StartMultiNode(id uint64) MultiNode {
+ mn := newMultiNode(id)
+ go mn.run()
+ return &mn
+}
+
+// TODO(bdarnell): add group ID to the underlying protos?
+type multiMessage struct {
+ group uint64
+ msg pb.Message
+}
+
+type multiConfChange struct {
+ group uint64
+ msg pb.ConfChange
+ ch chan pb.ConfState
+}
+
+type multiStatus struct {
+ group uint64
+ ch chan *Status
+}
+
+type groupCreation struct {
+ id uint64
+ config *Config
+ peers []Peer
+ // TODO(bdarnell): do we really need the done channel here? It's
+ // unlike the rest of this package, but we need the group creation
+ // to be complete before any Propose or other calls.
+ done chan struct{}
+}
+
+type groupRemoval struct {
+ id uint64
+ // TODO(bdarnell): see comment on groupCreation.done
+ done chan struct{}
+}
+
+type multiNode struct {
+ id uint64
+ groupc chan groupCreation
+ rmgroupc chan groupRemoval
+ propc chan multiMessage
+ recvc chan multiMessage
+ confc chan multiConfChange
+ readyc chan map[uint64]Ready
+ advancec chan map[uint64]Ready
+ tickc chan struct{}
+ stop chan struct{}
+ done chan struct{}
+ status chan multiStatus
+}
+
+func newMultiNode(id uint64) multiNode {
+ return multiNode{
+ id: id,
+ groupc: make(chan groupCreation),
+ rmgroupc: make(chan groupRemoval),
+ propc: make(chan multiMessage),
+ recvc: make(chan multiMessage),
+ confc: make(chan multiConfChange),
+ readyc: make(chan map[uint64]Ready),
+ advancec: make(chan map[uint64]Ready),
+ tickc: make(chan struct{}),
+ stop: make(chan struct{}),
+ done: make(chan struct{}),
+ status: make(chan multiStatus),
+ }
+}
+
+type groupState struct {
+ id uint64
+ raft *raft
+ prevSoftSt *SoftState
+ prevHardSt pb.HardState
+ prevSnapi uint64
+}
+
+func (g *groupState) newReady() Ready {
+ return newReady(g.raft, g.prevSoftSt, g.prevHardSt)
+}
+
+func (g *groupState) commitReady(rd Ready) {
+ if rd.SoftState != nil {
+ g.prevSoftSt = rd.SoftState
+ }
+ if !IsEmptyHardState(rd.HardState) {
+ g.prevHardSt = rd.HardState
+ }
+ if g.prevHardSt.Commit != 0 {
+ // In most cases, prevHardSt and rd.HardState will be the same
+ // because when there are new entries to apply we just sent a
+ // HardState with an updated Commit value. However, on initial
+ // startup the two are different because we don't send a HardState
+ // until something changes, but we do send any un-applied but
+ // committed entries (and previously-committed entries may be
+ // incorporated into the snapshot, even if rd.CommittedEntries is
+ // empty). Therefore we mark all committed entries as applied
+ // whether they were included in rd.HardState or not.
+ g.raft.raftLog.appliedTo(g.prevHardSt.Commit)
+ }
+ if len(rd.Entries) > 0 {
+ e := rd.Entries[len(rd.Entries)-1]
+ g.raft.raftLog.stableTo(e.Index, e.Term)
+ }
+ if !IsEmptySnap(rd.Snapshot) {
+ g.prevSnapi = rd.Snapshot.Metadata.Index
+ g.raft.raftLog.stableSnapTo(g.prevSnapi)
+ }
+}
+
+func (mn *multiNode) run() {
+ groups := map[uint64]*groupState{}
+ rds := map[uint64]Ready{}
+ var advancec chan map[uint64]Ready
+ for {
+ // Only select readyc if we have something to report and we are not
+ // currently waiting for an advance.
+ readyc := mn.readyc
+ if len(rds) == 0 || advancec != nil {
+ readyc = nil
+ }
+
+ // group points to the group that was touched on this iteration (if any)
+ var group *groupState
+ select {
+ case gc := <-mn.groupc:
+ if (gc.config.ID != mn.id) && (gc.config.ID != 0 && mn.id != 0) {
+ panic("if gc.config.ID and mn.id differ, one of them must be zero")
+ }
+ if gc.config.ID == 0 {
+ gc.config.ID = mn.id
+ }
+ r := newRaft(gc.config)
+ group = &groupState{
+ id: gc.id,
+ raft: r,
+ }
+ groups[gc.id] = group
+ lastIndex, err := gc.config.Storage.LastIndex()
+ if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+ // If the log is empty, this is a new group (like StartNode); otherwise it's
+ // restoring an existing group (like RestartNode).
+ // TODO(bdarnell): rethink group initialization and whether the application needs
+ // to be able to tell us when it expects the group to exist.
+ if lastIndex == 0 {
+ r.becomeFollower(1, None)
+ ents := make([]pb.Entry, len(gc.peers))
+ for i, peer := range gc.peers {
+ cc := pb.ConfChange{Type: pb.ConfChangeAddNode, NodeID: peer.ID, Context: peer.Context}
+ data, err := cc.Marshal()
+ if err != nil {
+ panic("unexpected marshal error")
+ }
+ ents[i] = pb.Entry{Type: pb.EntryConfChange, Term: 1, Index: uint64(i + 1), Data: data}
+ }
+ r.raftLog.append(ents...)
+ r.raftLog.committed = uint64(len(ents))
+ for _, peer := range gc.peers {
+ r.addNode(peer.ID)
+ }
+ }
+ // Set the initial hard and soft states after performing all initialization.
+ group.prevSoftSt = r.softState()
+ group.prevHardSt = r.HardState
+ close(gc.done)
+
+ case gr := <-mn.rmgroupc:
+ delete(groups, gr.id)
+ delete(rds, gr.id)
+ close(gr.done)
+
+ case mm := <-mn.propc:
+ // TODO(bdarnell): single-node impl doesn't read from propc unless the group
+ // has a leader; we can't do that since we have one propc for many groups.
+ // We'll have to buffer somewhere on a group-by-group basis, or just let
+ // raft.Step drop any such proposals on the floor.
+ var ok bool
+ if group, ok = groups[mm.group]; ok {
+ mm.msg.From = group.raft.id
+ group.raft.Step(mm.msg)
+ }
+
+ case mm := <-mn.recvc:
+ group = groups[mm.group]
+ if _, ok := group.raft.prs[mm.msg.From]; ok || !IsResponseMsg(mm.msg) {
+ group.raft.Step(mm.msg)
+ }
+
+ case mcc := <-mn.confc:
+ group = groups[mcc.group]
+ if mcc.msg.NodeID == None {
+ group.raft.resetPendingConf()
+ select {
+ case mcc.ch <- pb.ConfState{Nodes: group.raft.nodes()}:
+ case <-mn.done:
+ }
+ break
+ }
+ switch mcc.msg.Type {
+ case pb.ConfChangeAddNode:
+ group.raft.addNode(mcc.msg.NodeID)
+ case pb.ConfChangeRemoveNode:
+ group.raft.removeNode(mcc.msg.NodeID)
+ case pb.ConfChangeUpdateNode:
+ group.raft.resetPendingConf()
+ default:
+ panic("unexpected conf type")
+ }
+ select {
+ case mcc.ch <- pb.ConfState{Nodes: group.raft.nodes()}:
+ case <-mn.done:
+ }
+
+ case <-mn.tickc:
+ // TODO(bdarnell): instead of calling every group on every tick,
+ // we should have a priority queue of groups based on their next
+ // time-based event.
+ for _, g := range groups {
+ g.raft.tick()
+ rd := g.newReady()
+ if rd.containsUpdates() {
+ rds[g.id] = rd
+ }
+ }
+
+ case readyc <- rds:
+ // Clear outgoing messages as soon as we've passed them to the application.
+ for g := range rds {
+ groups[g].raft.msgs = nil
+ }
+ rds = map[uint64]Ready{}
+ advancec = mn.advancec
+
+ case advs := <-advancec:
+ for groupID, rd := range advs {
+ g, ok := groups[groupID]
+ if !ok {
+ continue
+ }
+ g.commitReady(rd)
+
+ // We've been accumulating new entries in rds which may now be obsolete.
+ // Drop the old Ready object and create a new one if needed.
+ delete(rds, groupID)
+ newRd := g.newReady()
+ if newRd.containsUpdates() {
+ rds[groupID] = newRd
+ }
+ }
+ advancec = nil
+
+ case ms := <-mn.status:
+ if g, ok := groups[ms.group]; ok {
+ s := getStatus(g.raft)
+ ms.ch <- &s
+ } else {
+ ms.ch <- nil
+ }
+
+ case <-mn.stop:
+ close(mn.done)
+ return
+ }
+
+ if group != nil {
+ rd := group.newReady()
+ if rd.containsUpdates() {
+ rds[group.id] = rd
+ }
+ }
+ }
+}
+
+func (mn *multiNode) CreateGroup(id uint64, config *Config, peers []Peer) error {
+ gc := groupCreation{
+ id: id,
+ config: config,
+ peers: peers,
+ done: make(chan struct{}),
+ }
+ mn.groupc <- gc
+ select {
+ case <-gc.done:
+ return nil
+ case <-mn.done:
+ return ErrStopped
+ }
+}
+
+func (mn *multiNode) RemoveGroup(id uint64) error {
+ gr := groupRemoval{
+ id: id,
+ done: make(chan struct{}),
+ }
+ mn.rmgroupc <- gr
+ select {
+ case <-gr.done:
+ return nil
+ case <-mn.done:
+ return ErrStopped
+ }
+}
+
+func (mn *multiNode) Stop() {
+ select {
+ case mn.stop <- struct{}{}:
+ case <-mn.done:
+ }
+ <-mn.done
+}
+
+func (mn *multiNode) Tick() {
+ select {
+ case mn.tickc <- struct{}{}:
+ case <-mn.done:
+ }
+}
+
+func (mn *multiNode) Campaign(ctx context.Context, group uint64) error {
+ return mn.step(ctx, multiMessage{group,
+ pb.Message{
+ Type: pb.MsgHup,
+ },
+ })
+}
+
+func (mn *multiNode) Propose(ctx context.Context, group uint64, data []byte) error {
+ return mn.step(ctx, multiMessage{group,
+ pb.Message{
+ Type: pb.MsgProp,
+ Entries: []pb.Entry{
+ {Data: data},
+ },
+ }})
+}
+
+func (mn *multiNode) ProposeConfChange(ctx context.Context, group uint64, cc pb.ConfChange) error {
+ data, err := cc.Marshal()
+ if err != nil {
+ return err
+ }
+ return mn.Step(ctx, group,
+ pb.Message{
+ Type: pb.MsgProp,
+ Entries: []pb.Entry{
+ {Type: pb.EntryConfChange, Data: data},
+ },
+ })
+}
+
+func (mn *multiNode) step(ctx context.Context, m multiMessage) error {
+ ch := mn.recvc
+ if m.msg.Type == pb.MsgProp {
+ ch = mn.propc
+ }
+
+ select {
+ case ch <- m:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-mn.done:
+ return ErrStopped
+ }
+}
+
+func (mn *multiNode) ApplyConfChange(group uint64, cc pb.ConfChange) *pb.ConfState {
+ mcc := multiConfChange{group, cc, make(chan pb.ConfState)}
+ select {
+ case mn.confc <- mcc:
+ case <-mn.done:
+ }
+ select {
+ case cs := <-mcc.ch:
+ return &cs
+ case <-mn.done:
+ // Per comments on Node.ApplyConfChange, this method should never return nil.
+ return &pb.ConfState{}
+ }
+}
+
+func (mn *multiNode) Step(ctx context.Context, group uint64, m pb.Message) error {
+ // ignore unexpected local messages receiving over network
+ if IsLocalMsg(m) {
+ // TODO: return an error?
+ return nil
+ }
+ return mn.step(ctx, multiMessage{group, m})
+}
+
+func (mn *multiNode) Ready() <-chan map[uint64]Ready {
+ return mn.readyc
+}
+
+func (mn *multiNode) Advance(rds map[uint64]Ready) {
+ select {
+ case mn.advancec <- rds:
+ case <-mn.done:
+ }
+}
+
+func (mn *multiNode) Status(group uint64) *Status {
+ ms := multiStatus{
+ group: group,
+ ch: make(chan *Status),
+ }
+ mn.status <- ms
+ return <-ms.ch
+}
+
+func (mn *multiNode) ReportUnreachable(id, groupID uint64) {
+ select {
+ case mn.recvc <- multiMessage{
+ group: groupID,
+ msg: pb.Message{Type: pb.MsgUnreachable, From: id},
+ }:
+ case <-mn.done:
+ }
+}
+
+func (mn *multiNode) ReportSnapshot(id, groupID uint64, status SnapshotStatus) {
+ rej := status == SnapshotFailure
+
+ select {
+ case mn.recvc <- multiMessage{
+ group: groupID,
+ msg: pb.Message{Type: pb.MsgSnapStatus, From: id, Reject: rej},
+ }:
+ case <-mn.done:
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/multinode_test.go b/vendor/github.com/coreos/etcd/raft/multinode_test.go
new file mode 100644
index 000000000..060e2a091
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/multinode_test.go
@@ -0,0 +1,573 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+// TestMultiNodeStep ensures that multiNode.Step sends MsgProp to propc
+// chan and other kinds of messages to recvc chan.
+func TestMultiNodeStep(t *testing.T) {
+ for i, msgn := range raftpb.MessageType_name {
+ mn := &multiNode{
+ propc: make(chan multiMessage, 1),
+ recvc: make(chan multiMessage, 1),
+ }
+ msgt := raftpb.MessageType(i)
+ mn.Step(context.TODO(), 1, raftpb.Message{Type: msgt})
+ // Proposal goes to proc chan. Others go to recvc chan.
+ if msgt == raftpb.MsgProp {
+ select {
+ case <-mn.propc:
+ default:
+ t.Errorf("%d: cannot receive %s on propc chan", msgt, msgn)
+ }
+ } else {
+ if msgt == raftpb.MsgBeat || msgt == raftpb.MsgHup || msgt == raftpb.MsgUnreachable || msgt == raftpb.MsgSnapStatus {
+ select {
+ case <-mn.recvc:
+ t.Errorf("%d: step should ignore %s", msgt, msgn)
+ default:
+ }
+ } else {
+ select {
+ case <-mn.recvc:
+ default:
+ t.Errorf("%d: cannot receive %s on recvc chan", msgt, msgn)
+ }
+ }
+ }
+ }
+}
+
+// Cancel and Stop should unblock Step()
+func TestMultiNodeStepUnblock(t *testing.T) {
+ // a node without buffer to block step
+ mn := &multiNode{
+ propc: make(chan multiMessage),
+ done: make(chan struct{}),
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ stopFunc := func() { close(mn.done) }
+
+ tests := []struct {
+ unblock func()
+ werr error
+ }{
+ {stopFunc, ErrStopped},
+ {cancel, context.Canceled},
+ }
+
+ for i, tt := range tests {
+ errc := make(chan error, 1)
+ go func() {
+ err := mn.Step(ctx, 1, raftpb.Message{Type: raftpb.MsgProp})
+ errc <- err
+ }()
+ tt.unblock()
+ select {
+ case err := <-errc:
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ //clean up side-effect
+ if ctx.Err() != nil {
+ ctx = context.TODO()
+ }
+ select {
+ case <-mn.done:
+ mn.done = make(chan struct{})
+ default:
+ }
+ case <-time.After(time.Millisecond * 100):
+ t.Errorf("#%d: failed to unblock step", i)
+ }
+ }
+}
+
+// TestMultiNodePropose ensures that node.Propose sends the given proposal to the underlying raft.
+func TestMultiNodePropose(t *testing.T) {
+ mn := newMultiNode(1)
+ go mn.run()
+ s := NewMemoryStorage()
+ mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, s), []Peer{{ID: 1}})
+ mn.Campaign(context.TODO(), 1)
+ proposed := false
+ for {
+ rds := <-mn.Ready()
+ rd := rds[1]
+ s.Append(rd.Entries)
+ // Once we are the leader, propose a command.
+ if !proposed && rd.SoftState.Lead == mn.id {
+ mn.Propose(context.TODO(), 1, []byte("somedata"))
+ proposed = true
+ }
+ mn.Advance(rds)
+
+ // Exit when we have three entries: one ConfChange, one no-op for the election,
+ // and our proposed command.
+ lastIndex, err := s.LastIndex()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if lastIndex >= 3 {
+ break
+ }
+ }
+ mn.Stop()
+
+ lastIndex, err := s.LastIndex()
+ if err != nil {
+ t.Fatal(err)
+ }
+ entries, err := s.Entries(lastIndex, lastIndex+1, noLimit)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(entries) != 1 {
+ t.Fatalf("len(entries) = %d, want %d", len(entries), 1)
+ }
+ if !bytes.Equal(entries[0].Data, []byte("somedata")) {
+ t.Errorf("entries[0].Data = %v, want %v", entries[0].Data, []byte("somedata"))
+ }
+}
+
+// TestMultiNodeProposeConfig ensures that multiNode.ProposeConfChange
+// sends the given configuration proposal to the underlying raft.
+func TestMultiNodeProposeConfig(t *testing.T) {
+ mn := newMultiNode(1)
+ go mn.run()
+ s := NewMemoryStorage()
+ mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, s), []Peer{{ID: 1}})
+ mn.Campaign(context.TODO(), 1)
+ proposed := false
+ var lastIndex uint64
+ var ccdata []byte
+ for {
+ rds := <-mn.Ready()
+ rd := rds[1]
+ s.Append(rd.Entries)
+ // change the step function to appendStep until this raft becomes leader
+ if !proposed && rd.SoftState.Lead == mn.id {
+ cc := raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1}
+ var err error
+ ccdata, err = cc.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ mn.ProposeConfChange(context.TODO(), 1, cc)
+ proposed = true
+ }
+ mn.Advance(rds)
+
+ var err error
+ lastIndex, err = s.LastIndex()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if lastIndex >= 3 {
+ break
+ }
+ }
+ mn.Stop()
+
+ entries, err := s.Entries(lastIndex, lastIndex+1, noLimit)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(entries) != 1 {
+ t.Fatalf("len(entries) = %d, want %d", len(entries), 1)
+ }
+ if entries[0].Type != raftpb.EntryConfChange {
+ t.Fatalf("type = %v, want %v", entries[0].Type, raftpb.EntryConfChange)
+ }
+ if !bytes.Equal(entries[0].Data, ccdata) {
+ t.Errorf("data = %v, want %v", entries[0].Data, ccdata)
+ }
+}
+
+// TestProposeUnknownGroup ensures that we gracefully handle proposals
+// for groups we don't know about (which can happen on a former leader
+// that has been removed from the group).
+//
+// It is analogous to TestBlockProposal from node_test.go but in
+// MultiNode we cannot block proposals based on individual group
+// leader status.
+func TestProposeUnknownGroup(t *testing.T) {
+ mn := newMultiNode(1)
+ go mn.run()
+ defer mn.Stop()
+
+ // A nil error from Propose() doesn't mean much. In this case the
+ // proposal will be dropped on the floor because we don't know
+ // anything about group 42. This is a very crude test that mainly
+ // guarantees that we don't panic in this case.
+ if err := mn.Propose(context.TODO(), 42, []byte("somedata")); err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+}
+
+// TestProposeAfterRemoveLeader ensures that we gracefully handle
+// proposals that are attempted after a leader has been removed from
+// the active configuration, but before that leader has called
+// MultiNode.RemoveGroup.
+func TestProposeAfterRemoveLeader(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ mn := newMultiNode(1)
+ go mn.run()
+ defer mn.Stop()
+
+ storage := NewMemoryStorage()
+ if err := mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, storage),
+ []Peer{{ID: 1}}); err != nil {
+ t.Fatal(err)
+ }
+ if err := mn.Campaign(ctx, 1); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := mn.ProposeConfChange(ctx, 1, raftpb.ConfChange{
+ Type: raftpb.ConfChangeRemoveNode,
+ NodeID: 1,
+ }); err != nil {
+ t.Fatal(err)
+ }
+ gs := <-mn.Ready()
+ g := gs[1]
+ if err := storage.Append(g.Entries); err != nil {
+ t.Fatal(err)
+ }
+ for _, e := range g.CommittedEntries {
+ if e.Type == raftpb.EntryConfChange {
+ var cc raftpb.ConfChange
+ if err := cc.Unmarshal(e.Data); err != nil {
+ t.Fatal(err)
+ }
+ mn.ApplyConfChange(1, cc)
+ }
+ }
+ mn.Advance(gs)
+
+ if err := mn.Propose(ctx, 1, []byte("somedata")); err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+}
+
+// TestNodeTick from node_test.go has no equivalent in multiNode because
+// it reaches into the raft object which is not exposed.
+
+// TestMultiNodeStop ensures that multiNode.Stop() blocks until the node has stopped
+// processing, and that it is idempotent
+func TestMultiNodeStop(t *testing.T) {
+ mn := newMultiNode(1)
+ donec := make(chan struct{})
+
+ go func() {
+ mn.run()
+ close(donec)
+ }()
+
+ mn.Tick()
+ mn.Stop()
+
+ select {
+ case <-donec:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for node to stop!")
+ }
+
+ // Further ticks should have no effect, the node is stopped.
+ // There is no way to verify this in multinode but at least we can test
+ // it doesn't block or panic.
+ mn.Tick()
+ // Subsequent Stops should have no effect.
+ mn.Stop()
+}
+
+// TestMultiNodeStart ensures that a node can be started correctly. The node should
+// start with correct configuration change entries, and can accept and commit
+// proposals.
+func TestMultiNodeStart(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ cc := raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1}
+ ccdata, err := cc.Marshal()
+ if err != nil {
+ t.Fatalf("unexpected marshal error: %v", err)
+ }
+ wants := []Ready{
+ {
+ SoftState: &SoftState{Lead: 1, RaftState: StateLeader},
+ HardState: raftpb.HardState{Term: 2, Commit: 2, Vote: 1},
+ Entries: []raftpb.Entry{
+ {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
+ {Term: 2, Index: 2},
+ },
+ CommittedEntries: []raftpb.Entry{
+ {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
+ {Term: 2, Index: 2},
+ },
+ },
+ {
+ HardState: raftpb.HardState{Term: 2, Commit: 3, Vote: 1},
+ Entries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
+ CommittedEntries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
+ },
+ }
+ mn := StartMultiNode(1)
+ storage := NewMemoryStorage()
+ mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, storage), []Peer{{ID: 1}})
+ mn.Campaign(ctx, 1)
+ gs := <-mn.Ready()
+ g := gs[1]
+ if !reflect.DeepEqual(g, wants[0]) {
+ t.Fatalf("#%d: g = %+v,\n w %+v", 1, g, wants[0])
+ } else {
+ storage.Append(g.Entries)
+ mn.Advance(gs)
+ }
+
+ mn.Propose(ctx, 1, []byte("foo"))
+ if gs2 := <-mn.Ready(); !reflect.DeepEqual(gs2[1], wants[1]) {
+ t.Errorf("#%d: g = %+v,\n w %+v", 2, gs2[1], wants[1])
+ } else {
+ storage.Append(gs2[1].Entries)
+ mn.Advance(gs2)
+ }
+
+ select {
+ case rd := <-mn.Ready():
+ t.Errorf("unexpected Ready: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+}
+
+func TestMultiNodeRestart(t *testing.T) {
+ entries := []raftpb.Entry{
+ {Term: 1, Index: 1},
+ {Term: 1, Index: 2, Data: []byte("foo")},
+ }
+ st := raftpb.HardState{Term: 1, Commit: 1}
+
+ want := Ready{
+ HardState: emptyState,
+ // commit up to index commit index in st
+ CommittedEntries: entries[:st.Commit],
+ }
+
+ storage := NewMemoryStorage()
+ storage.SetHardState(st)
+ storage.Append(entries)
+ mn := StartMultiNode(1)
+ mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, storage), nil)
+ gs := <-mn.Ready()
+ if !reflect.DeepEqual(gs[1], want) {
+ t.Errorf("g = %+v,\n w %+v", gs[1], want)
+ }
+ mn.Advance(gs)
+
+ select {
+ case rd := <-mn.Ready():
+ t.Errorf("unexpected Ready: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+ mn.Stop()
+}
+
+func TestMultiNodeRestartFromSnapshot(t *testing.T) {
+ snap := raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{
+ ConfState: raftpb.ConfState{Nodes: []uint64{1, 2}},
+ Index: 2,
+ Term: 1,
+ },
+ }
+ entries := []raftpb.Entry{
+ {Term: 1, Index: 3, Data: []byte("foo")},
+ }
+ st := raftpb.HardState{Term: 1, Commit: 3}
+
+ want := Ready{
+ HardState: emptyState,
+ // commit up to index commit index in st
+ CommittedEntries: entries,
+ }
+
+ s := NewMemoryStorage()
+ s.SetHardState(st)
+ s.ApplySnapshot(snap)
+ s.Append(entries)
+ mn := StartMultiNode(1)
+ mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, s), nil)
+ if gs := <-mn.Ready(); !reflect.DeepEqual(gs[1], want) {
+ t.Errorf("g = %+v,\n w %+v", gs[1], want)
+ } else {
+ mn.Advance(gs)
+ }
+
+ select {
+ case rd := <-mn.Ready():
+ t.Errorf("unexpected Ready: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+}
+
+func TestMultiNodeAdvance(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ storage := NewMemoryStorage()
+ mn := StartMultiNode(1)
+ mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, storage), []Peer{{ID: 1}})
+ mn.Campaign(ctx, 1)
+ rd1 := <-mn.Ready()
+ mn.Propose(ctx, 1, []byte("foo"))
+ select {
+ case rd2 := <-mn.Ready():
+ t.Fatalf("unexpected Ready before Advance: %+v", rd2)
+ case <-time.After(time.Millisecond):
+ }
+ storage.Append(rd1[1].Entries)
+ mn.Advance(rd1)
+ select {
+ case <-mn.Ready():
+ case <-time.After(time.Millisecond):
+ t.Errorf("expect Ready after Advance, but there is no Ready available")
+ }
+}
+
+func TestMultiNodeStatus(t *testing.T) {
+ storage := NewMemoryStorage()
+ mn := StartMultiNode(1)
+ err := mn.CreateGroup(1, newTestConfig(1, nil, 10, 1, storage), []Peer{{ID: 1}})
+ if err != nil {
+ t.Fatal(err)
+ }
+ status := mn.Status(1)
+ if status == nil {
+ t.Errorf("expected status struct, got nil")
+ }
+
+ status = mn.Status(2)
+ if status != nil {
+ t.Errorf("expected nil status, got %+v", status)
+ }
+}
+
+// TestMultiNodePerGroupID tests that MultiNode may have a different
+// node ID for each group, if and only if the Config.ID field is
+// filled in when calling CreateGroup.
+func TestMultiNodePerGroupID(t *testing.T) {
+ storage := NewMemoryStorage()
+ mn := StartMultiNode(0)
+
+ // Maps group ID to node ID.
+ groups := map[uint64]uint64{
+ 1: 10,
+ 2: 20,
+ }
+
+ // Create two groups.
+ for g, nodeID := range groups {
+ err := mn.CreateGroup(g, newTestConfig(nodeID, nil, 10, 1, storage),
+ []Peer{{ID: nodeID}, {ID: nodeID + 1}, {ID: nodeID + 2}})
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Campaign on both groups.
+ for g := range groups {
+ err := mn.Campaign(context.Background(), g)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // All outgoing messages (two MsgVotes for each group) should have
+ // the correct From IDs.
+ var rd map[uint64]Ready
+ select {
+ case rd = <-mn.Ready():
+ case <-time.After(100 * time.Millisecond):
+ t.Fatal("timed out waiting for ready")
+ }
+ for g, nodeID := range groups {
+ if len(rd[g].Messages) != 2 {
+ t.Errorf("expected 2 messages in group %d; got %d", g, len(rd[g].Messages))
+ }
+
+ for _, m := range rd[g].Messages {
+ if m.From != nodeID {
+ t.Errorf("expected %s message in group %d to have From: %d; got %d",
+ m.Type, g, nodeID, m.From)
+ }
+ }
+ }
+ mn.Advance(rd)
+
+ // Become a follower in both groups.
+ for g, nodeID := range groups {
+ err := mn.Step(context.Background(), g, raftpb.Message{
+ Type: raftpb.MsgHeartbeat,
+ To: nodeID,
+ From: nodeID + 1,
+ })
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Propose a command on each group (Propose is tested separately
+ // because proposals in follower mode go through a different code path).
+ for g := range groups {
+ err := mn.Propose(context.Background(), g, []byte("foo"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Validate that all outgoing messages (heartbeat response and
+ // proposal) have the correct From IDs.
+ select {
+ case rd = <-mn.Ready():
+ case <-time.After(100 * time.Millisecond):
+ t.Fatal("timed out waiting for ready")
+ }
+ for g, nodeID := range groups {
+ if len(rd[g].Messages) != 2 {
+ t.Errorf("expected 2 messages in group %d; got %d", g, len(rd[g].Messages))
+ }
+
+ for _, m := range rd[g].Messages {
+ if m.From != nodeID {
+ t.Errorf("expected %s message in group %d to have From: %d; got %d",
+ m.Type, g, nodeID, m.From)
+ }
+ }
+ }
+ mn.Advance(rd)
+}
diff --git a/vendor/github.com/coreos/etcd/raft/node.go b/vendor/github.com/coreos/etcd/raft/node.go
new file mode 100644
index 000000000..8f047863d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/node.go
@@ -0,0 +1,463 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "errors"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+type SnapshotStatus int
+
+const (
+ SnapshotFinish SnapshotStatus = 1
+ SnapshotFailure SnapshotStatus = 2
+)
+
+var (
+ emptyState = pb.HardState{}
+
+ // ErrStopped is returned by methods on Nodes that have been stopped.
+ ErrStopped = errors.New("raft: stopped")
+)
+
+// SoftState provides state that is useful for logging and debugging.
+// The state is volatile and does not need to be persisted to the WAL.
+type SoftState struct {
+ Lead uint64
+ RaftState StateType
+}
+
+func (a *SoftState) equal(b *SoftState) bool {
+ return a.Lead == b.Lead && a.RaftState == b.RaftState
+}
+
+// Ready encapsulates the entries and messages that are ready to read,
+// be saved to stable storage, committed or sent to other peers.
+// All fields in Ready are read-only.
+type Ready struct {
+ // The current volatile state of a Node.
+ // SoftState will be nil if there is no update.
+ // It is not required to consume or store SoftState.
+ *SoftState
+
+ // The current state of a Node to be saved to stable storage BEFORE
+ // Messages are sent.
+ // HardState will be equal to empty state if there is no update.
+ pb.HardState
+
+ // Entries specifies entries to be saved to stable storage BEFORE
+ // Messages are sent.
+ Entries []pb.Entry
+
+ // Snapshot specifies the snapshot to be saved to stable storage.
+ Snapshot pb.Snapshot
+
+ // CommittedEntries specifies entries to be committed to a
+ // store/state-machine. These have previously been committed to stable
+ // store.
+ CommittedEntries []pb.Entry
+
+ // Messages specifies outbound messages to be sent AFTER Entries are
+ // committed to stable storage.
+ // If it contains a MsgSnap message, the application MUST report back to raft
+ // when the snapshot has been received or has failed by calling ReportSnapshot.
+ Messages []pb.Message
+}
+
+func isHardStateEqual(a, b pb.HardState) bool {
+ return a.Term == b.Term && a.Vote == b.Vote && a.Commit == b.Commit
+}
+
+// IsEmptyHardState returns true if the given HardState is empty.
+func IsEmptyHardState(st pb.HardState) bool {
+ return isHardStateEqual(st, emptyState)
+}
+
+// IsEmptySnap returns true if the given Snapshot is empty.
+func IsEmptySnap(sp pb.Snapshot) bool {
+ return sp.Metadata.Index == 0
+}
+
+func (rd Ready) containsUpdates() bool {
+ return rd.SoftState != nil || !IsEmptyHardState(rd.HardState) ||
+ !IsEmptySnap(rd.Snapshot) || len(rd.Entries) > 0 ||
+ len(rd.CommittedEntries) > 0 || len(rd.Messages) > 0
+}
+
+// Node represents a node in a raft cluster.
+type Node interface {
+ // Tick increments the internal logical clock for the Node by a single tick. Election
+ // timeouts and heartbeat timeouts are in units of ticks.
+ Tick()
+ // Campaign causes the Node to transition to candidate state and start campaigning to become leader.
+ Campaign(ctx context.Context) error
+ // Propose proposes that data be appended to the log.
+ Propose(ctx context.Context, data []byte) error
+ // ProposeConfChange proposes config change.
+ // At most one ConfChange can be in the process of going through consensus.
+ // Application needs to call ApplyConfChange when applying EntryConfChange type entry.
+ ProposeConfChange(ctx context.Context, cc pb.ConfChange) error
+ // Step advances the state machine using the given message. ctx.Err() will be returned, if any.
+ Step(ctx context.Context, msg pb.Message) error
+ // Ready returns a channel that returns the current point-in-time state.
+ // Users of the Node must call Advance after applying the state returned by Ready.
+ Ready() <-chan Ready
+ // Advance notifies the Node that the application has applied and saved progress up to the last Ready.
+ // It prepares the node to return the next available Ready.
+ Advance()
+ // ApplyConfChange applies config change to the local node.
+ // Returns an opaque ConfState protobuf which must be recorded
+ // in snapshots. Will never return nil; it returns a pointer only
+ // to match MemoryStorage.Compact.
+ ApplyConfChange(cc pb.ConfChange) *pb.ConfState
+ // Status returns the current status of the raft state machine.
+ Status() Status
+ // Report reports the given node is not reachable for the last send.
+ ReportUnreachable(id uint64)
+ // ReportSnapshot reports the status of the sent snapshot.
+ ReportSnapshot(id uint64, status SnapshotStatus)
+ // Stop performs any necessary termination of the Node.
+ Stop()
+}
+
+type Peer struct {
+ ID uint64
+ Context []byte
+}
+
+// StartNode returns a new Node given configuration and a list of raft peers.
+// It appends a ConfChangeAddNode entry for each given peer to the initial log.
+func StartNode(c *Config, peers []Peer) Node {
+ r := newRaft(c)
+ // become the follower at term 1 and apply initial configuration
+ // entires of term 1
+ r.becomeFollower(1, None)
+ for _, peer := range peers {
+ cc := pb.ConfChange{Type: pb.ConfChangeAddNode, NodeID: peer.ID, Context: peer.Context}
+ d, err := cc.Marshal()
+ if err != nil {
+ panic("unexpected marshal error")
+ }
+ e := pb.Entry{Type: pb.EntryConfChange, Term: 1, Index: r.raftLog.lastIndex() + 1, Data: d}
+ r.raftLog.append(e)
+ }
+ // Mark these initial entries as committed.
+ // TODO(bdarnell): These entries are still unstable; do we need to preserve
+ // the invariant that committed < unstable?
+ r.raftLog.committed = r.raftLog.lastIndex()
+ r.Commit = r.raftLog.committed
+ // Now apply them, mainly so that the application can call Campaign
+ // immediately after StartNode in tests. Note that these nodes will
+ // be added to raft twice: here and when the application's Ready
+ // loop calls ApplyConfChange. The calls to addNode must come after
+ // all calls to raftLog.append so progress.next is set after these
+ // bootstrapping entries (it is an error if we try to append these
+ // entries since they have already been committed).
+ // We do not set raftLog.applied so the application will be able
+ // to observe all conf changes via Ready.CommittedEntries.
+ for _, peer := range peers {
+ r.addNode(peer.ID)
+ }
+
+ n := newNode()
+ go n.run(r)
+ return &n
+}
+
+// RestartNode is similar to StartNode but does not take a list of peers.
+// The current membership of the cluster will be restored from the Storage.
+// If the caller has an existing state machine, pass in the last log index that
+// has been applied to it; otherwise use zero.
+func RestartNode(c *Config) Node {
+ r := newRaft(c)
+
+ n := newNode()
+ go n.run(r)
+ return &n
+}
+
+// node is the canonical implementation of the Node interface
+type node struct {
+ propc chan pb.Message
+ recvc chan pb.Message
+ confc chan pb.ConfChange
+ confstatec chan pb.ConfState
+ readyc chan Ready
+ advancec chan struct{}
+ tickc chan struct{}
+ done chan struct{}
+ stop chan struct{}
+ status chan chan Status
+}
+
+func newNode() node {
+ return node{
+ propc: make(chan pb.Message),
+ recvc: make(chan pb.Message),
+ confc: make(chan pb.ConfChange),
+ confstatec: make(chan pb.ConfState),
+ readyc: make(chan Ready),
+ advancec: make(chan struct{}),
+ tickc: make(chan struct{}),
+ done: make(chan struct{}),
+ stop: make(chan struct{}),
+ status: make(chan chan Status),
+ }
+}
+
+func (n *node) Stop() {
+ select {
+ case n.stop <- struct{}{}:
+ // Not already stopped, so trigger it
+ case <-n.done:
+ // Node has already been stopped - no need to do anything
+ return
+ }
+ // Block until the stop has been acknowledged by run()
+ <-n.done
+}
+
+func (n *node) run(r *raft) {
+ var propc chan pb.Message
+ var readyc chan Ready
+ var advancec chan struct{}
+ var prevLastUnstablei, prevLastUnstablet uint64
+ var havePrevLastUnstablei bool
+ var prevSnapi uint64
+ var rd Ready
+
+ lead := None
+ prevSoftSt := r.softState()
+ prevHardSt := emptyState
+
+ for {
+ if advancec != nil {
+ readyc = nil
+ } else {
+ rd = newReady(r, prevSoftSt, prevHardSt)
+ if rd.containsUpdates() {
+ readyc = n.readyc
+ } else {
+ readyc = nil
+ }
+ }
+
+ if lead != r.lead {
+ if r.hasLeader() {
+ if lead == None {
+ raftLogger.Infof("raft.node: %x elected leader %x at term %d", r.id, r.lead, r.Term)
+ } else {
+ raftLogger.Infof("raft.node: %x changed leader from %x to %x at term %d", r.id, lead, r.lead, r.Term)
+ }
+ propc = n.propc
+ } else {
+ raftLogger.Infof("raft.node: %x lost leader %x at term %d", r.id, lead, r.Term)
+ propc = nil
+ }
+ lead = r.lead
+ }
+
+ select {
+ // TODO: maybe buffer the config propose if there exists one (the way
+ // described in raft dissertation)
+ // Currently it is dropped in Step silently.
+ case m := <-propc:
+ m.From = r.id
+ r.Step(m)
+ case m := <-n.recvc:
+ // filter out response message from unknown From.
+ if _, ok := r.prs[m.From]; ok || !IsResponseMsg(m) {
+ r.Step(m) // raft never returns an error
+ }
+ case cc := <-n.confc:
+ if cc.NodeID == None {
+ r.resetPendingConf()
+ select {
+ case n.confstatec <- pb.ConfState{Nodes: r.nodes()}:
+ case <-n.done:
+ }
+ break
+ }
+ switch cc.Type {
+ case pb.ConfChangeAddNode:
+ r.addNode(cc.NodeID)
+ case pb.ConfChangeRemoveNode:
+ // block incoming proposal when local node is
+ // removed
+ if cc.NodeID == r.id {
+ n.propc = nil
+ }
+ r.removeNode(cc.NodeID)
+ case pb.ConfChangeUpdateNode:
+ r.resetPendingConf()
+ default:
+ panic("unexpected conf type")
+ }
+ select {
+ case n.confstatec <- pb.ConfState{Nodes: r.nodes()}:
+ case <-n.done:
+ }
+ case <-n.tickc:
+ r.tick()
+ case readyc <- rd:
+ if rd.SoftState != nil {
+ prevSoftSt = rd.SoftState
+ }
+ if len(rd.Entries) > 0 {
+ prevLastUnstablei = rd.Entries[len(rd.Entries)-1].Index
+ prevLastUnstablet = rd.Entries[len(rd.Entries)-1].Term
+ havePrevLastUnstablei = true
+ }
+ if !IsEmptyHardState(rd.HardState) {
+ prevHardSt = rd.HardState
+ }
+ if !IsEmptySnap(rd.Snapshot) {
+ prevSnapi = rd.Snapshot.Metadata.Index
+ }
+ r.msgs = nil
+ advancec = n.advancec
+ case <-advancec:
+ if prevHardSt.Commit != 0 {
+ r.raftLog.appliedTo(prevHardSt.Commit)
+ }
+ if havePrevLastUnstablei {
+ r.raftLog.stableTo(prevLastUnstablei, prevLastUnstablet)
+ havePrevLastUnstablei = false
+ }
+ r.raftLog.stableSnapTo(prevSnapi)
+ advancec = nil
+ case c := <-n.status:
+ c <- getStatus(r)
+ case <-n.stop:
+ close(n.done)
+ return
+ }
+ }
+}
+
+// Tick increments the internal logical clock for this Node. Election timeouts
+// and heartbeat timeouts are in units of ticks.
+func (n *node) Tick() {
+ select {
+ case n.tickc <- struct{}{}:
+ case <-n.done:
+ }
+}
+
+func (n *node) Campaign(ctx context.Context) error { return n.step(ctx, pb.Message{Type: pb.MsgHup}) }
+
+func (n *node) Propose(ctx context.Context, data []byte) error {
+ return n.step(ctx, pb.Message{Type: pb.MsgProp, Entries: []pb.Entry{{Data: data}}})
+}
+
+func (n *node) Step(ctx context.Context, m pb.Message) error {
+ // ignore unexpected local messages receiving over network
+ if IsLocalMsg(m) {
+ // TODO: return an error?
+ return nil
+ }
+ return n.step(ctx, m)
+}
+
+func (n *node) ProposeConfChange(ctx context.Context, cc pb.ConfChange) error {
+ data, err := cc.Marshal()
+ if err != nil {
+ return err
+ }
+ return n.Step(ctx, pb.Message{Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange, Data: data}}})
+}
+
+// Step advances the state machine using msgs. The ctx.Err() will be returned,
+// if any.
+func (n *node) step(ctx context.Context, m pb.Message) error {
+ ch := n.recvc
+ if m.Type == pb.MsgProp {
+ ch = n.propc
+ }
+
+ select {
+ case ch <- m:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-n.done:
+ return ErrStopped
+ }
+}
+
+func (n *node) Ready() <-chan Ready { return n.readyc }
+
+func (n *node) Advance() {
+ select {
+ case n.advancec <- struct{}{}:
+ case <-n.done:
+ }
+}
+
+func (n *node) ApplyConfChange(cc pb.ConfChange) *pb.ConfState {
+ var cs pb.ConfState
+ select {
+ case n.confc <- cc:
+ case <-n.done:
+ }
+ select {
+ case cs = <-n.confstatec:
+ case <-n.done:
+ }
+ return &cs
+}
+
+func (n *node) Status() Status {
+ c := make(chan Status)
+ n.status <- c
+ return <-c
+}
+
+func (n *node) ReportUnreachable(id uint64) {
+ select {
+ case n.recvc <- pb.Message{Type: pb.MsgUnreachable, From: id}:
+ case <-n.done:
+ }
+}
+
+func (n *node) ReportSnapshot(id uint64, status SnapshotStatus) {
+ rej := status == SnapshotFailure
+
+ select {
+ case n.recvc <- pb.Message{Type: pb.MsgSnapStatus, From: id, Reject: rej}:
+ case <-n.done:
+ }
+}
+
+func newReady(r *raft, prevSoftSt *SoftState, prevHardSt pb.HardState) Ready {
+ rd := Ready{
+ Entries: r.raftLog.unstableEntries(),
+ CommittedEntries: r.raftLog.nextEnts(),
+ Messages: r.msgs,
+ }
+ if softSt := r.softState(); !softSt.equal(prevSoftSt) {
+ rd.SoftState = softSt
+ }
+ if !isHardStateEqual(r.HardState, prevHardSt) {
+ rd.HardState = r.HardState
+ }
+ if r.raftLog.unstable.snapshot != nil {
+ rd.Snapshot = *r.raftLog.unstable.snapshot
+ }
+ return rd
+}
diff --git a/vendor/github.com/coreos/etcd/raft/node_bench_test.go b/vendor/github.com/coreos/etcd/raft/node_bench_test.go
new file mode 100644
index 000000000..dcb967b35
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/node_bench_test.go
@@ -0,0 +1,52 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+)
+
+func BenchmarkOneNode(b *testing.B) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ n := newNode()
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1}, 10, 1, s)
+ go n.run(r)
+
+ defer n.Stop()
+
+ n.Campaign(ctx)
+ go func() {
+ for i := 0; i < b.N; i++ {
+ n.Propose(ctx, []byte("foo"))
+ }
+ }()
+
+ for {
+ rd := <-n.Ready()
+ s.Append(rd.Entries)
+ // a reasonable disk sync latency
+ time.Sleep(1 * time.Millisecond)
+ n.Advance()
+ if rd.HardState.Commit == uint64(b.N+1) {
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/node_test.go b/vendor/github.com/coreos/etcd/raft/node_test.go
new file mode 100644
index 000000000..f637971af
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/node_test.go
@@ -0,0 +1,503 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+// TestNodeStep ensures that node.Step sends msgProp to propc chan
+// and other kinds of messages to recvc chan.
+func TestNodeStep(t *testing.T) {
+ for i, msgn := range raftpb.MessageType_name {
+ n := &node{
+ propc: make(chan raftpb.Message, 1),
+ recvc: make(chan raftpb.Message, 1),
+ }
+ msgt := raftpb.MessageType(i)
+ n.Step(context.TODO(), raftpb.Message{Type: msgt})
+ // Proposal goes to proc chan. Others go to recvc chan.
+ if msgt == raftpb.MsgProp {
+ select {
+ case <-n.propc:
+ default:
+ t.Errorf("%d: cannot receive %s on propc chan", msgt, msgn)
+ }
+ } else {
+ if msgt == raftpb.MsgBeat || msgt == raftpb.MsgHup || msgt == raftpb.MsgUnreachable || msgt == raftpb.MsgSnapStatus {
+ select {
+ case <-n.recvc:
+ t.Errorf("%d: step should ignore %s", msgt, msgn)
+ default:
+ }
+ } else {
+ select {
+ case <-n.recvc:
+ default:
+ t.Errorf("%d: cannot receive %s on recvc chan", msgt, msgn)
+ }
+ }
+ }
+ }
+}
+
+// Cancel and Stop should unblock Step()
+func TestNodeStepUnblock(t *testing.T) {
+ // a node without buffer to block step
+ n := &node{
+ propc: make(chan raftpb.Message),
+ done: make(chan struct{}),
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ stopFunc := func() { close(n.done) }
+
+ tests := []struct {
+ unblock func()
+ werr error
+ }{
+ {stopFunc, ErrStopped},
+ {cancel, context.Canceled},
+ }
+
+ for i, tt := range tests {
+ errc := make(chan error, 1)
+ go func() {
+ err := n.Step(ctx, raftpb.Message{Type: raftpb.MsgProp})
+ errc <- err
+ }()
+ tt.unblock()
+ select {
+ case err := <-errc:
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ //clean up side-effect
+ if ctx.Err() != nil {
+ ctx = context.TODO()
+ }
+ select {
+ case <-n.done:
+ n.done = make(chan struct{})
+ default:
+ }
+ case <-time.After(time.Millisecond * 100):
+ t.Errorf("#%d: failed to unblock step", i)
+ }
+ }
+}
+
+// TestNodePropose ensures that node.Propose sends the given proposal to the underlying raft.
+func TestNodePropose(t *testing.T) {
+ msgs := []raftpb.Message{}
+ appendStep := func(r *raft, m raftpb.Message) {
+ msgs = append(msgs, m)
+ }
+
+ n := newNode()
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1}, 10, 1, s)
+ go n.run(r)
+ n.Campaign(context.TODO())
+ for {
+ rd := <-n.Ready()
+ s.Append(rd.Entries)
+ // change the step function to appendStep until this raft becomes leader
+ if rd.SoftState.Lead == r.id {
+ r.step = appendStep
+ n.Advance()
+ break
+ }
+ n.Advance()
+ }
+ n.Propose(context.TODO(), []byte("somedata"))
+ n.Stop()
+
+ if len(msgs) != 1 {
+ t.Fatalf("len(msgs) = %d, want %d", len(msgs), 1)
+ }
+ if msgs[0].Type != raftpb.MsgProp {
+ t.Errorf("msg type = %d, want %d", msgs[0].Type, raftpb.MsgProp)
+ }
+ if !reflect.DeepEqual(msgs[0].Entries[0].Data, []byte("somedata")) {
+ t.Errorf("data = %v, want %v", msgs[0].Entries[0].Data, []byte("somedata"))
+ }
+}
+
+// TestNodeProposeConfig ensures that node.ProposeConfChange sends the given configuration proposal
+// to the underlying raft.
+func TestNodeProposeConfig(t *testing.T) {
+ msgs := []raftpb.Message{}
+ appendStep := func(r *raft, m raftpb.Message) {
+ msgs = append(msgs, m)
+ }
+
+ n := newNode()
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1}, 10, 1, s)
+ go n.run(r)
+ n.Campaign(context.TODO())
+ for {
+ rd := <-n.Ready()
+ s.Append(rd.Entries)
+ // change the step function to appendStep until this raft becomes leader
+ if rd.SoftState.Lead == r.id {
+ r.step = appendStep
+ n.Advance()
+ break
+ }
+ n.Advance()
+ }
+ cc := raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1}
+ ccdata, err := cc.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ n.ProposeConfChange(context.TODO(), cc)
+ n.Stop()
+
+ if len(msgs) != 1 {
+ t.Fatalf("len(msgs) = %d, want %d", len(msgs), 1)
+ }
+ if msgs[0].Type != raftpb.MsgProp {
+ t.Errorf("msg type = %d, want %d", msgs[0].Type, raftpb.MsgProp)
+ }
+ if !reflect.DeepEqual(msgs[0].Entries[0].Data, ccdata) {
+ t.Errorf("data = %v, want %v", msgs[0].Entries[0].Data, ccdata)
+ }
+}
+
+// TestBlockProposal ensures that node will block proposal when it does not
+// know who is the current leader; node will accept proposal when it knows
+// who is the current leader.
+func TestBlockProposal(t *testing.T) {
+ n := newNode()
+ r := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
+ go n.run(r)
+ defer n.Stop()
+
+ errc := make(chan error, 1)
+ go func() {
+ errc <- n.Propose(context.TODO(), []byte("somedata"))
+ }()
+
+ testutil.WaitSchedule()
+ select {
+ case err := <-errc:
+ t.Errorf("err = %v, want blocking", err)
+ default:
+ }
+
+ n.Campaign(context.TODO())
+ testutil.WaitSchedule()
+ select {
+ case err := <-errc:
+ if err != nil {
+ t.Errorf("err = %v, want %v", err, nil)
+ }
+ default:
+ t.Errorf("blocking proposal, want unblocking")
+ }
+}
+
+// TestNodeTick ensures that node.Tick() will increase the
+// elapsed of the underlying raft state machine.
+func TestNodeTick(t *testing.T) {
+ n := newNode()
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1}, 10, 1, s)
+ go n.run(r)
+ elapsed := r.elapsed
+ n.Tick()
+ n.Stop()
+ if r.elapsed != elapsed+1 {
+ t.Errorf("elapsed = %d, want %d", r.elapsed, elapsed+1)
+ }
+}
+
+// TestNodeStop ensures that node.Stop() blocks until the node has stopped
+// processing, and that it is idempotent
+func TestNodeStop(t *testing.T) {
+ n := newNode()
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1}, 10, 1, s)
+ donec := make(chan struct{})
+
+ go func() {
+ n.run(r)
+ close(donec)
+ }()
+
+ elapsed := r.elapsed
+ n.Tick()
+ n.Stop()
+
+ select {
+ case <-donec:
+ case <-time.After(time.Second):
+ t.Fatalf("timed out waiting for node to stop!")
+ }
+
+ if r.elapsed != elapsed+1 {
+ t.Errorf("elapsed = %d, want %d", r.elapsed, elapsed+1)
+ }
+ // Further ticks should have no effect, the node is stopped.
+ n.Tick()
+ if r.elapsed != elapsed+1 {
+ t.Errorf("elapsed = %d, want %d", r.elapsed, elapsed+1)
+ }
+ // Subsequent Stops should have no effect.
+ n.Stop()
+}
+
+func TestReadyContainUpdates(t *testing.T) {
+ tests := []struct {
+ rd Ready
+ wcontain bool
+ }{
+ {Ready{}, false},
+ {Ready{SoftState: &SoftState{Lead: 1}}, true},
+ {Ready{HardState: raftpb.HardState{Vote: 1}}, true},
+ {Ready{Entries: make([]raftpb.Entry, 1, 1)}, true},
+ {Ready{CommittedEntries: make([]raftpb.Entry, 1, 1)}, true},
+ {Ready{Messages: make([]raftpb.Message, 1, 1)}, true},
+ {Ready{Snapshot: raftpb.Snapshot{Metadata: raftpb.SnapshotMetadata{Index: 1}}}, true},
+ }
+
+ for i, tt := range tests {
+ if g := tt.rd.containsUpdates(); g != tt.wcontain {
+ t.Errorf("#%d: containUpdates = %v, want %v", i, g, tt.wcontain)
+ }
+ }
+}
+
+// TestNodeStart ensures that a node can be started correctly. The node should
+// start with correct configuration change entries, and can accept and commit
+// proposals.
+func TestNodeStart(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ cc := raftpb.ConfChange{Type: raftpb.ConfChangeAddNode, NodeID: 1}
+ ccdata, err := cc.Marshal()
+ if err != nil {
+ t.Fatalf("unexpected marshal error: %v", err)
+ }
+ wants := []Ready{
+ {
+ SoftState: &SoftState{Lead: 1, RaftState: StateLeader},
+ HardState: raftpb.HardState{Term: 2, Commit: 2, Vote: 1},
+ Entries: []raftpb.Entry{
+ {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
+ {Term: 2, Index: 2},
+ },
+ CommittedEntries: []raftpb.Entry{
+ {Type: raftpb.EntryConfChange, Term: 1, Index: 1, Data: ccdata},
+ {Term: 2, Index: 2},
+ },
+ },
+ {
+ HardState: raftpb.HardState{Term: 2, Commit: 3, Vote: 1},
+ Entries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
+ CommittedEntries: []raftpb.Entry{{Term: 2, Index: 3, Data: []byte("foo")}},
+ },
+ }
+ storage := NewMemoryStorage()
+ c := &Config{
+ ID: 1,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: storage,
+ MaxSizePerMsg: noLimit,
+ MaxInflightMsgs: 256,
+ }
+ n := StartNode(c, []Peer{{ID: 1}})
+ n.Campaign(ctx)
+ g := <-n.Ready()
+ if !reflect.DeepEqual(g, wants[0]) {
+ t.Fatalf("#%d: g = %+v,\n w %+v", 1, g, wants[0])
+ } else {
+ storage.Append(g.Entries)
+ n.Advance()
+ }
+
+ n.Propose(ctx, []byte("foo"))
+ if g2 := <-n.Ready(); !reflect.DeepEqual(g2, wants[1]) {
+ t.Errorf("#%d: g = %+v,\n w %+v", 2, g2, wants[1])
+ } else {
+ storage.Append(g2.Entries)
+ n.Advance()
+ }
+
+ select {
+ case rd := <-n.Ready():
+ t.Errorf("unexpected Ready: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+}
+
+func TestNodeRestart(t *testing.T) {
+ entries := []raftpb.Entry{
+ {Term: 1, Index: 1},
+ {Term: 1, Index: 2, Data: []byte("foo")},
+ }
+ st := raftpb.HardState{Term: 1, Commit: 1}
+
+ want := Ready{
+ HardState: st,
+ // commit up to index commit index in st
+ CommittedEntries: entries[:st.Commit],
+ }
+
+ storage := NewMemoryStorage()
+ storage.SetHardState(st)
+ storage.Append(entries)
+ c := &Config{
+ ID: 1,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: storage,
+ MaxSizePerMsg: noLimit,
+ MaxInflightMsgs: 256,
+ }
+ n := RestartNode(c)
+ if g := <-n.Ready(); !reflect.DeepEqual(g, want) {
+ t.Errorf("g = %+v,\n w %+v", g, want)
+ }
+ n.Advance()
+
+ select {
+ case rd := <-n.Ready():
+ t.Errorf("unexpected Ready: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+}
+
+func TestNodeRestartFromSnapshot(t *testing.T) {
+ snap := raftpb.Snapshot{
+ Metadata: raftpb.SnapshotMetadata{
+ ConfState: raftpb.ConfState{Nodes: []uint64{1, 2}},
+ Index: 2,
+ Term: 1,
+ },
+ }
+ entries := []raftpb.Entry{
+ {Term: 1, Index: 3, Data: []byte("foo")},
+ }
+ st := raftpb.HardState{Term: 1, Commit: 3}
+
+ want := Ready{
+ HardState: st,
+ // commit up to index commit index in st
+ CommittedEntries: entries,
+ }
+
+ s := NewMemoryStorage()
+ s.SetHardState(st)
+ s.ApplySnapshot(snap)
+ s.Append(entries)
+ c := &Config{
+ ID: 1,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: s,
+ MaxSizePerMsg: noLimit,
+ MaxInflightMsgs: 256,
+ }
+ n := RestartNode(c)
+ if g := <-n.Ready(); !reflect.DeepEqual(g, want) {
+ t.Errorf("g = %+v,\n w %+v", g, want)
+ } else {
+ n.Advance()
+ }
+
+ select {
+ case rd := <-n.Ready():
+ t.Errorf("unexpected Ready: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+}
+
+func TestNodeAdvance(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ storage := NewMemoryStorage()
+ c := &Config{
+ ID: 1,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: storage,
+ MaxSizePerMsg: noLimit,
+ MaxInflightMsgs: 256,
+ }
+ n := StartNode(c, []Peer{{ID: 1}})
+ n.Campaign(ctx)
+ <-n.Ready()
+ n.Propose(ctx, []byte("foo"))
+ var rd Ready
+ select {
+ case rd = <-n.Ready():
+ t.Fatalf("unexpected Ready before Advance: %+v", rd)
+ case <-time.After(time.Millisecond):
+ }
+ storage.Append(rd.Entries)
+ n.Advance()
+ select {
+ case <-n.Ready():
+ case <-time.After(time.Millisecond):
+ t.Errorf("expect Ready after Advance, but there is no Ready available")
+ }
+}
+
+func TestSoftStateEqual(t *testing.T) {
+ tests := []struct {
+ st *SoftState
+ we bool
+ }{
+ {&SoftState{}, true},
+ {&SoftState{Lead: 1}, false},
+ {&SoftState{RaftState: StateLeader}, false},
+ }
+ for i, tt := range tests {
+ if g := tt.st.equal(&SoftState{}); g != tt.we {
+ t.Errorf("#%d, equal = %v, want %v", i, g, tt.we)
+ }
+ }
+}
+
+func TestIsHardStateEqual(t *testing.T) {
+ tests := []struct {
+ st raftpb.HardState
+ we bool
+ }{
+ {emptyState, true},
+ {raftpb.HardState{Vote: 1}, false},
+ {raftpb.HardState{Commit: 1}, false},
+ {raftpb.HardState{Term: 1}, false},
+ }
+
+ for i, tt := range tests {
+ if isHardStateEqual(tt.st, emptyState) != tt.we {
+ t.Errorf("#%d, equal = %v, want %v", i, isHardStateEqual(tt.st, emptyState), tt.we)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/progress.go b/vendor/github.com/coreos/etcd/raft/progress.go
new file mode 100644
index 000000000..3c3ae5259
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/progress.go
@@ -0,0 +1,237 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import "fmt"
+
+const (
+ ProgressStateProbe ProgressStateType = iota
+ ProgressStateReplicate
+ ProgressStateSnapshot
+)
+
+type ProgressStateType uint64
+
+var prstmap = [...]string{
+ "ProgressStateProbe",
+ "ProgressStateReplicate",
+ "ProgressStateSnapshot",
+}
+
+func (st ProgressStateType) String() string { return prstmap[uint64(st)] }
+
+// Progress represents a follower’s progress in the view of the leader. Leader maintains
+// progresses of all followers, and sends entries to the follower based on its progress.
+type Progress struct {
+ Match, Next uint64
+ // When in ProgressStateProbe, leader sends at most one replication message
+ // per heartbeat interval. It also probes actual progress of the follower.
+ //
+ // When in ProgressStateReplicate, leader optimistically increases next
+ // to the latest entry sent after sending replication message. This is
+ // an optimized state for fast replicating log entries to the follower.
+ //
+ // When in ProgressStateSnapshot, leader should have sent out snapshot
+ // before and stops sending any replication message.
+ State ProgressStateType
+ // Paused is used in ProgressStateProbe.
+ // When Paused is true, raft should pause sending replication message to this peer.
+ Paused bool
+ // PendingSnapshot is used in ProgressStateSnapshot.
+ // If there is a pending snapshot, the pendingSnapshot will be set to the
+ // index of the snapshot. If pendingSnapshot is set, the replication process of
+ // this Progress will be paused. raft will not resend snapshot until the pending one
+ // is reported to be failed.
+ PendingSnapshot uint64
+
+ // inflights is a sliding window for the inflight messages.
+ // When inflights is full, no more message should be sent.
+ // When a leader sends out a message, the index of the last
+ // entry should be add to inflights. The index MUST be added
+ // into inflights in order.
+ // When a leader receives a reply, the previous inflights should
+ // be freed by calling inflights.freeTo.
+ ins *inflights
+}
+
+func (pr *Progress) resetState(state ProgressStateType) {
+ pr.Paused = false
+ pr.PendingSnapshot = 0
+ pr.State = state
+ pr.ins.reset()
+}
+
+func (pr *Progress) becomeProbe() {
+ // If the original state is ProgressStateSnapshot, progress knows that
+ // the pending snapshot has been sent to this peer successfully, then
+ // probes from pendingSnapshot + 1.
+ if pr.State == ProgressStateSnapshot {
+ pendingSnapshot := pr.PendingSnapshot
+ pr.resetState(ProgressStateProbe)
+ pr.Next = max(pr.Match+1, pendingSnapshot+1)
+ } else {
+ pr.resetState(ProgressStateProbe)
+ pr.Next = pr.Match + 1
+ }
+}
+
+func (pr *Progress) becomeReplicate() {
+ pr.resetState(ProgressStateReplicate)
+ pr.Next = pr.Match + 1
+}
+
+func (pr *Progress) becomeSnapshot(snapshoti uint64) {
+ pr.resetState(ProgressStateSnapshot)
+ pr.PendingSnapshot = snapshoti
+}
+
+// maybeUpdate returns false if the given n index comes from an outdated message.
+// Otherwise it updates the progress and returns true.
+func (pr *Progress) maybeUpdate(n uint64) bool {
+ var updated bool
+ if pr.Match < n {
+ pr.Match = n
+ updated = true
+ pr.resume()
+ }
+ if pr.Next < n+1 {
+ pr.Next = n + 1
+ }
+ return updated
+}
+
+func (pr *Progress) optimisticUpdate(n uint64) { pr.Next = n + 1 }
+
+// maybeDecrTo returns false if the given to index comes from an out of order message.
+// Otherwise it decreases the progress next index to min(rejected, last) and returns true.
+func (pr *Progress) maybeDecrTo(rejected, last uint64) bool {
+ if pr.State == ProgressStateReplicate {
+ // the rejection must be stale if the progress has matched and "rejected"
+ // is smaller than "match".
+ if rejected <= pr.Match {
+ return false
+ }
+ // directly decrease next to match + 1
+ pr.Next = pr.Match + 1
+ return true
+ }
+
+ // the rejection must be stale if "rejected" does not match next - 1
+ if pr.Next-1 != rejected {
+ return false
+ }
+
+ if pr.Next = min(rejected, last+1); pr.Next < 1 {
+ pr.Next = 1
+ }
+ pr.resume()
+ return true
+}
+
+func (pr *Progress) pause() { pr.Paused = true }
+func (pr *Progress) resume() { pr.Paused = false }
+
+// isPaused returns whether progress stops sending message.
+func (pr *Progress) isPaused() bool {
+ switch pr.State {
+ case ProgressStateProbe:
+ return pr.Paused
+ case ProgressStateReplicate:
+ return pr.ins.full()
+ case ProgressStateSnapshot:
+ return true
+ default:
+ panic("unexpected state")
+ }
+}
+
+func (pr *Progress) snapshotFailure() { pr.PendingSnapshot = 0 }
+
+// maybeSnapshotAbort unsets pendingSnapshot if Match is equal or higher than
+// the pendingSnapshot
+func (pr *Progress) maybeSnapshotAbort() bool {
+ return pr.State == ProgressStateSnapshot && pr.Match >= pr.PendingSnapshot
+}
+
+func (pr *Progress) String() string {
+ return fmt.Sprintf("next = %d, match = %d, state = %s, waiting = %v, pendingSnapshot = %d", pr.Next, pr.Match, pr.State, pr.isPaused(), pr.PendingSnapshot)
+}
+
+type inflights struct {
+ // the starting index in the buffer
+ start int
+ // number of inflights in the buffer
+ count int
+
+ // the size of the buffer
+ size int
+ buffer []uint64
+}
+
+func newInflights(size int) *inflights {
+ return &inflights{
+ size: size,
+ buffer: make([]uint64, size),
+ }
+}
+
+// add adds an inflight into inflights
+func (in *inflights) add(inflight uint64) {
+ if in.full() {
+ panic("cannot add into a full inflights")
+ }
+ next := in.start + in.count
+ if next >= in.size {
+ next -= in.size
+ }
+ in.buffer[next] = inflight
+ in.count++
+}
+
+// freeTo frees the inflights smaller or equal to the given `to` flight.
+func (in *inflights) freeTo(to uint64) {
+ if in.count == 0 || to < in.buffer[in.start] {
+ // out of the left side of the window
+ return
+ }
+
+ i, idx := 0, in.start
+ for i = 0; i < in.count; i++ {
+ if to < in.buffer[idx] { // found the first large inflight
+ break
+ }
+
+ // increase index and maybe rotate
+ if idx += 1; idx >= in.size {
+ idx -= in.size
+ }
+ }
+ // free i inflights and set new start index
+ in.count -= i
+ in.start = idx
+}
+
+func (in *inflights) freeFirstOne() { in.freeTo(in.buffer[in.start]) }
+
+// full returns true if the inflights is full.
+func (in *inflights) full() bool {
+ return in.count == in.size
+}
+
+// resets frees all inflights.
+func (in *inflights) reset() {
+ in.count = 0
+ in.start = 0
+}
diff --git a/vendor/github.com/coreos/etcd/raft/progress_test.go b/vendor/github.com/coreos/etcd/raft/progress_test.go
new file mode 100644
index 000000000..4cea14e87
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/progress_test.go
@@ -0,0 +1,189 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestInflightsAdd(t *testing.T) {
+ // no rotating case
+ in := &inflights{
+ size: 10,
+ buffer: make([]uint64, 10),
+ }
+
+ for i := 0; i < 5; i++ {
+ in.add(uint64(i))
+ }
+
+ wantIn := &inflights{
+ start: 0,
+ count: 5,
+ size: 10,
+ // ↓------------
+ buffer: []uint64{0, 1, 2, 3, 4, 0, 0, 0, 0, 0},
+ }
+
+ if !reflect.DeepEqual(in, wantIn) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn)
+ }
+
+ for i := 5; i < 10; i++ {
+ in.add(uint64(i))
+ }
+
+ wantIn2 := &inflights{
+ start: 0,
+ count: 10,
+ size: 10,
+ // ↓---------------------------
+ buffer: []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ }
+
+ if !reflect.DeepEqual(in, wantIn2) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn2)
+ }
+
+ // rotating case
+ in2 := &inflights{
+ start: 5,
+ size: 10,
+ buffer: make([]uint64, 10),
+ }
+
+ for i := 0; i < 5; i++ {
+ in2.add(uint64(i))
+ }
+
+ wantIn21 := &inflights{
+ start: 5,
+ count: 5,
+ size: 10,
+ // ↓------------
+ buffer: []uint64{0, 0, 0, 0, 0, 0, 1, 2, 3, 4},
+ }
+
+ if !reflect.DeepEqual(in2, wantIn21) {
+ t.Fatalf("in = %+v, want %+v", in2, wantIn21)
+ }
+
+ for i := 5; i < 10; i++ {
+ in2.add(uint64(i))
+ }
+
+ wantIn22 := &inflights{
+ start: 5,
+ count: 10,
+ size: 10,
+ // -------------- ↓------------
+ buffer: []uint64{5, 6, 7, 8, 9, 0, 1, 2, 3, 4},
+ }
+
+ if !reflect.DeepEqual(in2, wantIn22) {
+ t.Fatalf("in = %+v, want %+v", in2, wantIn22)
+ }
+}
+
+func TestInflightFreeTo(t *testing.T) {
+ // no rotating case
+ in := newInflights(10)
+ for i := 0; i < 10; i++ {
+ in.add(uint64(i))
+ }
+
+ in.freeTo(4)
+
+ wantIn := &inflights{
+ start: 5,
+ count: 5,
+ size: 10,
+ // ↓------------
+ buffer: []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ }
+
+ if !reflect.DeepEqual(in, wantIn) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn)
+ }
+
+ in.freeTo(8)
+
+ wantIn2 := &inflights{
+ start: 9,
+ count: 1,
+ size: 10,
+ // ↓
+ buffer: []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ }
+
+ if !reflect.DeepEqual(in, wantIn2) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn2)
+ }
+
+ // rotating case
+ for i := 10; i < 15; i++ {
+ in.add(uint64(i))
+ }
+
+ in.freeTo(12)
+
+ wantIn3 := &inflights{
+ start: 3,
+ count: 2,
+ size: 10,
+ // ↓-----
+ buffer: []uint64{10, 11, 12, 13, 14, 5, 6, 7, 8, 9},
+ }
+
+ if !reflect.DeepEqual(in, wantIn3) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn3)
+ }
+
+ in.freeTo(14)
+
+ wantIn4 := &inflights{
+ start: 5,
+ count: 0,
+ size: 10,
+ // ↓
+ buffer: []uint64{10, 11, 12, 13, 14, 5, 6, 7, 8, 9},
+ }
+
+ if !reflect.DeepEqual(in, wantIn4) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn4)
+ }
+}
+
+func TestInflightFreeFirstOne(t *testing.T) {
+ in := newInflights(10)
+ for i := 0; i < 10; i++ {
+ in.add(uint64(i))
+ }
+
+ in.freeFirstOne()
+
+ wantIn := &inflights{
+ start: 1,
+ count: 9,
+ size: 10,
+ // ↓------------------------
+ buffer: []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
+ }
+
+ if !reflect.DeepEqual(in, wantIn) {
+ t.Fatalf("in = %+v, want %+v", in, wantIn)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/raft.go b/vendor/github.com/coreos/etcd/raft/raft.go
new file mode 100644
index 000000000..4baa114b1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raft.go
@@ -0,0 +1,797 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "errors"
+ "fmt"
+ "math"
+ "math/rand"
+ "sort"
+ "strings"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+// None is a placeholder node ID used when there is no leader.
+const None uint64 = 0
+const noLimit = math.MaxUint64
+
+var errNoLeader = errors.New("no leader")
+
+var ErrSnapshotTemporarilyUnavailable = errors.New("snapshot is temporarily unavailable")
+
+// Possible values for StateType.
+const (
+ StateFollower StateType = iota
+ StateCandidate
+ StateLeader
+)
+
+// StateType represents the role of a node in a cluster.
+type StateType uint64
+
+var stmap = [...]string{
+ "StateFollower",
+ "StateCandidate",
+ "StateLeader",
+}
+
+func (st StateType) String() string {
+ return stmap[uint64(st)]
+}
+
+// Config contains the parameters to start a raft.
+type Config struct {
+ // ID is the identity of the local raft. ID cannot be 0.
+ ID uint64
+
+ // peers contains the IDs of all nodes (including self) in
+ // the raft cluster. It should only be set when starting a new
+ // raft cluster.
+ // Restarting raft from previous configuration will panic if
+ // peers is set.
+ // peer is private and only used for testing right now.
+ peers []uint64
+
+ // ElectionTick is the election timeout. If a follower does not
+ // receive any message from the leader of current term during
+ // ElectionTick, it will become candidate and start an election.
+ // ElectionTick must be greater than HeartbeatTick. We suggest
+ // to use ElectionTick = 10 * HeartbeatTick to avoid unnecessary
+ // leader switching.
+ ElectionTick int
+ // HeartbeatTick is the heartbeat interval. A leader sends heartbeat
+ // message to maintain the leadership every heartbeat interval.
+ HeartbeatTick int
+
+ // Storage is the storage for raft. raft generates entires and
+ // states to be stored in storage. raft reads the persisted entires
+ // and states out of Storage when it needs. raft reads out the previous
+ // state and configuration out of storage when restarting.
+ Storage Storage
+ // Applied is the last applied index. It should only be set when restarting
+ // raft. raft will not return entries to the application smaller or equal to Applied.
+ // If Applied is unset when restarting, raft might return previous applied entries.
+ // This is a very application dependent configuration.
+ Applied uint64
+
+ // MaxSizePerMsg limits the max size of each append message. Smaller value lowers
+ // the raft recovery cost(initial probing and message lost during normal operation).
+ // On the other side, it might affect the throughput during normal replication.
+ // Note: math.MaxUint64 for unlimited, 0 for at most one entry per message.
+ MaxSizePerMsg uint64
+ // MaxInflightMsgs limits the max number of in-flight append messages during optimistic
+ // replication phase. The application transportation layer usually has its own sending
+ // buffer over TCP/UDP. Setting MaxInflightMsgs to avoid overflowing that sending buffer.
+ // TODO (xiangli): feedback to application to limit the proposal rate?
+ MaxInflightMsgs int
+
+ // logger is the logger used for raft log. For multinode which
+ // can host multiple raft group, each raft group can have its
+ // own logger
+ Logger Logger
+}
+
+func (c *Config) validate() error {
+ if c.ID == None {
+ return errors.New("cannot use none as id")
+ }
+
+ if c.HeartbeatTick <= 0 {
+ return errors.New("heartbeat tick must be greater than 0")
+ }
+
+ if c.ElectionTick <= c.HeartbeatTick {
+ return errors.New("election tick must be greater than heartbeat tick")
+ }
+
+ if c.Storage == nil {
+ return errors.New("storage cannot be nil")
+ }
+
+ if c.MaxInflightMsgs <= 0 {
+ return errors.New("max inflight messages must be greater than 0")
+ }
+
+ if c.Logger == nil {
+ c.Logger = raftLogger
+ }
+
+ return nil
+}
+
+type raft struct {
+ pb.HardState
+
+ id uint64
+
+ // the log
+ raftLog *raftLog
+
+ maxInflight int
+ maxMsgSize uint64
+ prs map[uint64]*Progress
+
+ state StateType
+
+ votes map[uint64]bool
+
+ msgs []pb.Message
+
+ // the leader id
+ lead uint64
+
+ // New configuration is ignored if there exists unapplied configuration.
+ pendingConf bool
+
+ elapsed int // number of ticks since the last msg
+ heartbeatTimeout int
+ electionTimeout int
+ rand *rand.Rand
+ tick func()
+ step stepFunc
+
+ logger Logger
+}
+
+func newRaft(c *Config) *raft {
+ if err := c.validate(); err != nil {
+ panic(err.Error())
+ }
+ raftlog := newLog(c.Storage, c.Logger)
+ hs, cs, err := c.Storage.InitialState()
+ if err != nil {
+ panic(err) // TODO(bdarnell)
+ }
+ peers := c.peers
+ if len(cs.Nodes) > 0 {
+ if len(peers) > 0 {
+ // TODO(bdarnell): the peers argument is always nil except in
+ // tests; the argument should be removed and these tests should be
+ // updated to specify their nodes through a snapshot.
+ panic("cannot specify both newRaft(peers) and ConfState.Nodes)")
+ }
+ peers = cs.Nodes
+ }
+ r := &raft{
+ id: c.ID,
+ lead: None,
+ raftLog: raftlog,
+ maxMsgSize: c.MaxSizePerMsg,
+ maxInflight: c.MaxInflightMsgs,
+ prs: make(map[uint64]*Progress),
+ electionTimeout: c.ElectionTick,
+ heartbeatTimeout: c.HeartbeatTick,
+ logger: c.Logger,
+ }
+ r.rand = rand.New(rand.NewSource(int64(c.ID)))
+ for _, p := range peers {
+ r.prs[p] = &Progress{Next: 1, ins: newInflights(r.maxInflight)}
+ }
+ if !isHardStateEqual(hs, emptyState) {
+ r.loadState(hs)
+ }
+ if c.Applied > 0 {
+ raftlog.appliedTo(c.Applied)
+ }
+ r.becomeFollower(r.Term, None)
+
+ nodesStrs := make([]string, 0)
+ for _, n := range r.nodes() {
+ nodesStrs = append(nodesStrs, fmt.Sprintf("%x", n))
+ }
+
+ r.logger.Infof("newRaft %x [peers: [%s], term: %d, commit: %d, applied: %d, lastindex: %d, lastterm: %d]",
+ r.id, strings.Join(nodesStrs, ","), r.Term, r.raftLog.committed, r.raftLog.applied, r.raftLog.lastIndex(), r.raftLog.lastTerm())
+ return r
+}
+
+func (r *raft) hasLeader() bool { return r.lead != None }
+
+func (r *raft) softState() *SoftState { return &SoftState{Lead: r.lead, RaftState: r.state} }
+
+func (r *raft) q() int { return len(r.prs)/2 + 1 }
+
+func (r *raft) nodes() []uint64 {
+ nodes := make([]uint64, 0, len(r.prs))
+ for k := range r.prs {
+ nodes = append(nodes, k)
+ }
+ sort.Sort(uint64Slice(nodes))
+ return nodes
+}
+
+// send persists state to stable storage and then sends to its mailbox.
+func (r *raft) send(m pb.Message) {
+ m.From = r.id
+ // do not attach term to MsgProp
+ // proposals are a way to forward to the leader and
+ // should be treated as local message.
+ if m.Type != pb.MsgProp {
+ m.Term = r.Term
+ }
+ r.msgs = append(r.msgs, m)
+}
+
+// sendAppend sends RRPC, with entries to the given peer.
+func (r *raft) sendAppend(to uint64) {
+ pr := r.prs[to]
+ if pr.isPaused() {
+ return
+ }
+ m := pb.Message{}
+ m.To = to
+
+ term, errt := r.raftLog.term(pr.Next - 1)
+ ents, erre := r.raftLog.entries(pr.Next, r.maxMsgSize)
+
+ if errt != nil || erre != nil { // send snapshot if we failed to get term or entries
+ m.Type = pb.MsgSnap
+ snapshot, err := r.raftLog.snapshot()
+ if err != nil {
+ if err == ErrSnapshotTemporarilyUnavailable {
+ r.logger.Debugf("%x failed to send snapshot to %x because snapshot is temporarily unavailable", r.id, to)
+ return
+ }
+ panic(err) // TODO(bdarnell)
+ }
+ if IsEmptySnap(snapshot) {
+ panic("need non-empty snapshot")
+ }
+ m.Snapshot = snapshot
+ sindex, sterm := snapshot.Metadata.Index, snapshot.Metadata.Term
+ r.logger.Debugf("%x [firstindex: %d, commit: %d] sent snapshot[index: %d, term: %d] to %x [%s]",
+ r.id, r.raftLog.firstIndex(), r.Commit, sindex, sterm, to, pr)
+ pr.becomeSnapshot(sindex)
+ r.logger.Debugf("%x paused sending replication messages to %x [%s]", r.id, to, pr)
+ } else {
+ m.Type = pb.MsgApp
+ m.Index = pr.Next - 1
+ m.LogTerm = term
+ m.Entries = ents
+ m.Commit = r.raftLog.committed
+ if n := len(m.Entries); n != 0 {
+ switch pr.State {
+ // optimistically increase the next when in ProgressStateReplicate
+ case ProgressStateReplicate:
+ last := m.Entries[n-1].Index
+ pr.optimisticUpdate(last)
+ pr.ins.add(last)
+ case ProgressStateProbe:
+ pr.pause()
+ default:
+ r.logger.Panicf("%x is sending append in unhandled state %s", r.id, pr.State)
+ }
+ }
+ }
+ r.send(m)
+}
+
+// sendHeartbeat sends an empty MsgApp
+func (r *raft) sendHeartbeat(to uint64) {
+ // Attach the commit as min(to.matched, r.committed).
+ // When the leader sends out heartbeat message,
+ // the receiver(follower) might not be matched with the leader
+ // or it might not have all the committed entries.
+ // The leader MUST NOT forward the follower's commit to
+ // an unmatched index.
+ commit := min(r.prs[to].Match, r.raftLog.committed)
+ m := pb.Message{
+ To: to,
+ Type: pb.MsgHeartbeat,
+ Commit: commit,
+ }
+ r.send(m)
+}
+
+// bcastAppend sends RRPC, with entries to all peers that are not up-to-date
+// according to the progress recorded in r.prs.
+func (r *raft) bcastAppend() {
+ for i := range r.prs {
+ if i == r.id {
+ continue
+ }
+ r.sendAppend(i)
+ }
+}
+
+// bcastHeartbeat sends RRPC, without entries to all the peers.
+func (r *raft) bcastHeartbeat() {
+ for i := range r.prs {
+ if i == r.id {
+ continue
+ }
+ r.sendHeartbeat(i)
+ r.prs[i].resume()
+ }
+}
+
+func (r *raft) maybeCommit() bool {
+ // TODO(bmizerany): optimize.. Currently naive
+ mis := make(uint64Slice, 0, len(r.prs))
+ for i := range r.prs {
+ mis = append(mis, r.prs[i].Match)
+ }
+ sort.Sort(sort.Reverse(mis))
+ mci := mis[r.q()-1]
+ return r.raftLog.maybeCommit(mci, r.Term)
+}
+
+func (r *raft) reset(term uint64) {
+ if r.Term != term {
+ r.Term = term
+ r.Vote = None
+ }
+ r.lead = None
+ r.elapsed = 0
+ r.votes = make(map[uint64]bool)
+ for i := range r.prs {
+ r.prs[i] = &Progress{Next: r.raftLog.lastIndex() + 1, ins: newInflights(r.maxInflight)}
+ if i == r.id {
+ r.prs[i].Match = r.raftLog.lastIndex()
+ }
+ }
+ r.pendingConf = false
+}
+
+func (r *raft) appendEntry(es ...pb.Entry) {
+ li := r.raftLog.lastIndex()
+ for i := range es {
+ es[i].Term = r.Term
+ es[i].Index = li + 1 + uint64(i)
+ }
+ r.raftLog.append(es...)
+ r.prs[r.id].maybeUpdate(r.raftLog.lastIndex())
+ r.maybeCommit()
+}
+
+// tickElection is run by followers and candidates after r.electionTimeout.
+func (r *raft) tickElection() {
+ if !r.promotable() {
+ r.elapsed = 0
+ return
+ }
+ r.elapsed++
+ if r.isElectionTimeout() {
+ r.elapsed = 0
+ r.Step(pb.Message{From: r.id, Type: pb.MsgHup})
+ }
+}
+
+// tickHeartbeat is run by leaders to send a MsgBeat after r.heartbeatTimeout.
+func (r *raft) tickHeartbeat() {
+ r.elapsed++
+ if r.elapsed >= r.heartbeatTimeout {
+ r.elapsed = 0
+ r.Step(pb.Message{From: r.id, Type: pb.MsgBeat})
+ }
+}
+
+func (r *raft) becomeFollower(term uint64, lead uint64) {
+ r.step = stepFollower
+ r.reset(term)
+ r.tick = r.tickElection
+ r.lead = lead
+ r.state = StateFollower
+ r.logger.Infof("%x became follower at term %d", r.id, r.Term)
+}
+
+func (r *raft) becomeCandidate() {
+ // TODO(xiangli) remove the panic when the raft implementation is stable
+ if r.state == StateLeader {
+ panic("invalid transition [leader -> candidate]")
+ }
+ r.step = stepCandidate
+ r.reset(r.Term + 1)
+ r.tick = r.tickElection
+ r.Vote = r.id
+ r.state = StateCandidate
+ r.logger.Infof("%x became candidate at term %d", r.id, r.Term)
+}
+
+func (r *raft) becomeLeader() {
+ // TODO(xiangli) remove the panic when the raft implementation is stable
+ if r.state == StateFollower {
+ panic("invalid transition [follower -> leader]")
+ }
+ r.step = stepLeader
+ r.reset(r.Term)
+ r.tick = r.tickHeartbeat
+ r.lead = r.id
+ r.state = StateLeader
+ ents, err := r.raftLog.entries(r.raftLog.committed+1, noLimit)
+ if err != nil {
+ r.logger.Panicf("unexpected error getting uncommitted entries (%v)", err)
+ }
+
+ for _, e := range ents {
+ if e.Type != pb.EntryConfChange {
+ continue
+ }
+ if r.pendingConf {
+ panic("unexpected double uncommitted config entry")
+ }
+ r.pendingConf = true
+ }
+ r.appendEntry(pb.Entry{Data: nil})
+ r.logger.Infof("%x became leader at term %d", r.id, r.Term)
+}
+
+func (r *raft) campaign() {
+ r.becomeCandidate()
+ if r.q() == r.poll(r.id, true) {
+ r.becomeLeader()
+ return
+ }
+ for i := range r.prs {
+ if i == r.id {
+ continue
+ }
+ r.logger.Infof("%x [logterm: %d, index: %d] sent vote request to %x at term %d",
+ r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), i, r.Term)
+ r.send(pb.Message{To: i, Type: pb.MsgVote, Index: r.raftLog.lastIndex(), LogTerm: r.raftLog.lastTerm()})
+ }
+}
+
+func (r *raft) poll(id uint64, v bool) (granted int) {
+ if v {
+ r.logger.Infof("%x received vote from %x at term %d", r.id, id, r.Term)
+ } else {
+ r.logger.Infof("%x received vote rejection from %x at term %d", r.id, id, r.Term)
+ }
+ if _, ok := r.votes[id]; !ok {
+ r.votes[id] = v
+ }
+ for _, vv := range r.votes {
+ if vv {
+ granted++
+ }
+ }
+ return granted
+}
+
+func (r *raft) Step(m pb.Message) error {
+ if m.Type == pb.MsgHup {
+ r.logger.Infof("%x is starting a new election at term %d", r.id, r.Term)
+ r.campaign()
+ r.Commit = r.raftLog.committed
+ return nil
+ }
+
+ switch {
+ case m.Term == 0:
+ // local message
+ case m.Term > r.Term:
+ lead := m.From
+ if m.Type == pb.MsgVote {
+ lead = None
+ }
+ r.logger.Infof("%x [term: %d] received a %s message with higher term from %x [term: %d]",
+ r.id, r.Term, m.Type, m.From, m.Term)
+ r.becomeFollower(m.Term, lead)
+ case m.Term < r.Term:
+ // ignore
+ r.logger.Infof("%x [term: %d] ignored a %s message with lower term from %x [term: %d]",
+ r.id, r.Term, m.Type, m.From, m.Term)
+ return nil
+ }
+ r.step(r, m)
+ r.Commit = r.raftLog.committed
+ return nil
+}
+
+type stepFunc func(r *raft, m pb.Message)
+
+func stepLeader(r *raft, m pb.Message) {
+ pr := r.prs[m.From]
+
+ switch m.Type {
+ case pb.MsgBeat:
+ r.bcastHeartbeat()
+ case pb.MsgProp:
+ if len(m.Entries) == 0 {
+ r.logger.Panicf("%x stepped empty MsgProp", r.id)
+ }
+ if _, ok := r.prs[r.id]; !ok {
+ // If we are not currently a member of the range (i.e. this node
+ // was removed from the configuration while serving as leader),
+ // drop any new proposals.
+ return
+ }
+ for i, e := range m.Entries {
+ if e.Type == pb.EntryConfChange {
+ if r.pendingConf {
+ m.Entries[i] = pb.Entry{Type: pb.EntryNormal}
+ }
+ r.pendingConf = true
+ }
+ }
+ r.appendEntry(m.Entries...)
+ r.bcastAppend()
+ case pb.MsgAppResp:
+ if m.Reject {
+ r.logger.Debugf("%x received msgApp rejection(lastindex: %d) from %x for index %d",
+ r.id, m.RejectHint, m.From, m.Index)
+ if pr.maybeDecrTo(m.Index, m.RejectHint) {
+ r.logger.Debugf("%x decreased progress of %x to [%s]", r.id, m.From, pr)
+ if pr.State == ProgressStateReplicate {
+ pr.becomeProbe()
+ }
+ r.sendAppend(m.From)
+ }
+ } else {
+ oldPaused := pr.isPaused()
+ if pr.maybeUpdate(m.Index) {
+ switch {
+ case pr.State == ProgressStateProbe:
+ pr.becomeReplicate()
+ case pr.State == ProgressStateSnapshot && pr.maybeSnapshotAbort():
+ r.logger.Debugf("%x snapshot aborted, resumed sending replication messages to %x [%s]", r.id, m.From, pr)
+ pr.becomeProbe()
+ case pr.State == ProgressStateReplicate:
+ pr.ins.freeTo(m.Index)
+ }
+
+ if r.maybeCommit() {
+ r.bcastAppend()
+ } else if oldPaused {
+ // update() reset the wait state on this node. If we had delayed sending
+ // an update before, send it now.
+ r.sendAppend(m.From)
+ }
+ }
+ }
+ case pb.MsgHeartbeatResp:
+ // free one slot for the full inflights window to allow progress.
+ if pr.State == ProgressStateReplicate && pr.ins.full() {
+ pr.ins.freeFirstOne()
+ }
+ if pr.Match < r.raftLog.lastIndex() {
+ r.sendAppend(m.From)
+ }
+ case pb.MsgVote:
+ r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected vote from %x [logterm: %d, index: %d] at term %d",
+ r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.From, m.LogTerm, m.Index, r.Term)
+ r.send(pb.Message{To: m.From, Type: pb.MsgVoteResp, Reject: true})
+ case pb.MsgSnapStatus:
+ if pr.State != ProgressStateSnapshot {
+ return
+ }
+ if !m.Reject {
+ pr.becomeProbe()
+ r.logger.Debugf("%x snapshot succeeded, resumed sending replication messages to %x [%s]", r.id, m.From, pr)
+ } else {
+ pr.snapshotFailure()
+ pr.becomeProbe()
+ r.logger.Debugf("%x snapshot failed, resumed sending replication messages to %x [%s]", r.id, m.From, pr)
+ }
+ // If snapshot finish, wait for the msgAppResp from the remote node before sending
+ // out the next msgApp.
+ // If snapshot failure, wait for a heartbeat interval before next try
+ pr.pause()
+ case pb.MsgUnreachable:
+ // During optimistic replication, if the remote becomes unreachable,
+ // there is huge probability that a MsgApp is lost.
+ if pr.State == ProgressStateReplicate {
+ pr.becomeProbe()
+ }
+ r.logger.Debugf("%x failed to send message to %x because it is unreachable [%s]", r.id, m.From, pr)
+ }
+}
+
+func stepCandidate(r *raft, m pb.Message) {
+ switch m.Type {
+ case pb.MsgProp:
+ r.logger.Infof("%x no leader at term %d; dropping proposal", r.id, r.Term)
+ return
+ case pb.MsgApp:
+ r.becomeFollower(r.Term, m.From)
+ r.handleAppendEntries(m)
+ case pb.MsgHeartbeat:
+ r.becomeFollower(r.Term, m.From)
+ r.handleHeartbeat(m)
+ case pb.MsgSnap:
+ r.becomeFollower(m.Term, m.From)
+ r.handleSnapshot(m)
+ case pb.MsgVote:
+ r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected vote from %x [logterm: %d, index: %d] at term %x",
+ r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.From, m.LogTerm, m.Index, r.Term)
+ r.send(pb.Message{To: m.From, Type: pb.MsgVoteResp, Reject: true})
+ case pb.MsgVoteResp:
+ gr := r.poll(m.From, !m.Reject)
+ r.logger.Infof("%x [q:%d] has received %d votes and %d vote rejections", r.id, r.q(), gr, len(r.votes)-gr)
+ switch r.q() {
+ case gr:
+ r.becomeLeader()
+ r.bcastAppend()
+ case len(r.votes) - gr:
+ r.becomeFollower(r.Term, None)
+ }
+ }
+}
+
+func stepFollower(r *raft, m pb.Message) {
+ switch m.Type {
+ case pb.MsgProp:
+ if r.lead == None {
+ r.logger.Infof("%x no leader at term %d; dropping proposal", r.id, r.Term)
+ return
+ }
+ m.To = r.lead
+ r.send(m)
+ case pb.MsgApp:
+ r.elapsed = 0
+ r.lead = m.From
+ r.handleAppendEntries(m)
+ case pb.MsgHeartbeat:
+ r.elapsed = 0
+ r.lead = m.From
+ r.handleHeartbeat(m)
+ case pb.MsgSnap:
+ r.elapsed = 0
+ r.handleSnapshot(m)
+ case pb.MsgVote:
+ if (r.Vote == None || r.Vote == m.From) && r.raftLog.isUpToDate(m.Index, m.LogTerm) {
+ r.elapsed = 0
+ r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] voted for %x [logterm: %d, index: %d] at term %d",
+ r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.From, m.LogTerm, m.Index, r.Term)
+ r.Vote = m.From
+ r.send(pb.Message{To: m.From, Type: pb.MsgVoteResp})
+ } else {
+ r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected vote from %x [logterm: %d, index: %d] at term %d",
+ r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.From, m.LogTerm, m.Index, r.Term)
+ r.send(pb.Message{To: m.From, Type: pb.MsgVoteResp, Reject: true})
+ }
+ }
+}
+
+func (r *raft) handleAppendEntries(m pb.Message) {
+ if m.Index < r.Commit {
+ r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: r.Commit})
+ return
+ }
+
+ if mlastIndex, ok := r.raftLog.maybeAppend(m.Index, m.LogTerm, m.Commit, m.Entries...); ok {
+ r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: mlastIndex})
+ } else {
+ r.logger.Debugf("%x [logterm: %d, index: %d] rejected msgApp [logterm: %d, index: %d] from %x",
+ r.id, r.raftLog.zeroTermOnErrCompacted(r.raftLog.term(m.Index)), m.Index, m.LogTerm, m.Index, m.From)
+ r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: m.Index, Reject: true, RejectHint: r.raftLog.lastIndex()})
+ }
+}
+
+func (r *raft) handleHeartbeat(m pb.Message) {
+ r.raftLog.commitTo(m.Commit)
+ r.send(pb.Message{To: m.From, Type: pb.MsgHeartbeatResp})
+}
+
+func (r *raft) handleSnapshot(m pb.Message) {
+ sindex, sterm := m.Snapshot.Metadata.Index, m.Snapshot.Metadata.Term
+ if r.restore(m.Snapshot) {
+ r.logger.Infof("%x [commit: %d] restored snapshot [index: %d, term: %d]",
+ r.id, r.Commit, sindex, sterm)
+ r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: r.raftLog.lastIndex()})
+ } else {
+ r.logger.Infof("%x [commit: %d] ignored snapshot [index: %d, term: %d]",
+ r.id, r.Commit, sindex, sterm)
+ r.send(pb.Message{To: m.From, Type: pb.MsgAppResp, Index: r.raftLog.committed})
+ }
+}
+
+// restore recovers the state machine from a snapshot. It restores the log and the
+// configuration of state machine.
+func (r *raft) restore(s pb.Snapshot) bool {
+ if s.Metadata.Index <= r.raftLog.committed {
+ return false
+ }
+ if r.raftLog.matchTerm(s.Metadata.Index, s.Metadata.Term) {
+ r.logger.Infof("%x [commit: %d, lastindex: %d, lastterm: %d] fast-forwarded commit to snapshot [index: %d, term: %d]",
+ r.id, r.Commit, r.raftLog.lastIndex(), r.raftLog.lastTerm(), s.Metadata.Index, s.Metadata.Term)
+ r.raftLog.commitTo(s.Metadata.Index)
+ return false
+ }
+
+ r.logger.Infof("%x [commit: %d, lastindex: %d, lastterm: %d] starts to restore snapshot [index: %d, term: %d]",
+ r.id, r.Commit, r.raftLog.lastIndex(), r.raftLog.lastTerm(), s.Metadata.Index, s.Metadata.Term)
+
+ r.raftLog.restore(s)
+ r.prs = make(map[uint64]*Progress)
+ for _, n := range s.Metadata.ConfState.Nodes {
+ match, next := uint64(0), uint64(r.raftLog.lastIndex())+1
+ if n == r.id {
+ match = next - 1
+ } else {
+ match = 0
+ }
+ r.setProgress(n, match, next)
+ r.logger.Infof("%x restored progress of %x [%s]", r.id, n, r.prs[n])
+ }
+ return true
+}
+
+// promotable indicates whether state machine can be promoted to leader,
+// which is true when its own id is in progress list.
+func (r *raft) promotable() bool {
+ _, ok := r.prs[r.id]
+ return ok
+}
+
+func (r *raft) addNode(id uint64) {
+ if _, ok := r.prs[id]; ok {
+ // Ignore any redundant addNode calls (which can happen because the
+ // initial bootstrapping entries are applied twice).
+ return
+ }
+
+ r.setProgress(id, 0, r.raftLog.lastIndex()+1)
+ r.pendingConf = false
+}
+
+func (r *raft) removeNode(id uint64) {
+ r.delProgress(id)
+ r.pendingConf = false
+}
+
+func (r *raft) resetPendingConf() { r.pendingConf = false }
+
+func (r *raft) setProgress(id, match, next uint64) {
+ r.prs[id] = &Progress{Next: next, Match: match, ins: newInflights(r.maxInflight)}
+}
+
+func (r *raft) delProgress(id uint64) {
+ delete(r.prs, id)
+}
+
+func (r *raft) loadState(state pb.HardState) {
+ if state.Commit < r.raftLog.committed || state.Commit > r.raftLog.lastIndex() {
+ r.logger.Panicf("%x state.commit %d is out of range [%d, %d]", r.id, state.Commit, r.raftLog.committed, r.raftLog.lastIndex())
+ }
+ r.raftLog.committed = state.Commit
+ r.Term = state.Term
+ r.Vote = state.Vote
+ r.Commit = state.Commit
+}
+
+// isElectionTimeout returns true if r.elapsed is greater than the
+// randomized election timeout in (electiontimeout, 2 * electiontimeout - 1).
+// Otherwise, it returns false.
+func (r *raft) isElectionTimeout() bool {
+ d := r.elapsed - r.electionTimeout
+ if d < 0 {
+ return false
+ }
+ return d > r.rand.Int()%r.electionTimeout
+}
diff --git a/vendor/github.com/coreos/etcd/raft/raft_flow_control_test.go b/vendor/github.com/coreos/etcd/raft/raft_flow_control_test.go
new file mode 100644
index 000000000..947f29752
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raft_flow_control_test.go
@@ -0,0 +1,155 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+// TestMsgAppFlowControlFull ensures:
+// 1. msgApp can fill the sending window until full
+// 2. when the window is full, no more msgApp can be sent.
+func TestMsgAppFlowControlFull(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 5, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+
+ pr2 := r.prs[2]
+ // force the progress to be in replicate state
+ pr2.becomeReplicate()
+ // fill in the inflights window
+ for i := 0; i < r.maxInflight; i++ {
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ ms := r.readMessages()
+ if len(ms) != 1 {
+ t.Fatalf("#%d: len(ms) = %d, want 1", i, len(ms))
+ }
+ }
+
+ // ensure 1
+ if !pr2.ins.full() {
+ t.Fatalf("inflights.full = %t, want %t", pr2.ins.full(), true)
+ }
+
+ // ensure 2
+ for i := 0; i < 10; i++ {
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ ms := r.readMessages()
+ if len(ms) != 0 {
+ t.Fatalf("#%d: len(ms) = %d, want 0", i, len(ms))
+ }
+ }
+}
+
+// TestMsgAppFlowControlMoveForward ensures msgAppResp can move
+// forward the sending window correctly:
+// 1. valid msgAppResp.index moves the windows to pass all smaller or equal index.
+// 2. out-of-dated msgAppResp has no effect on the silding window.
+func TestMsgAppFlowControlMoveForward(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 5, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+
+ pr2 := r.prs[2]
+ // force the progress to be in replicate state
+ pr2.becomeReplicate()
+ // fill in the inflights window
+ for i := 0; i < r.maxInflight; i++ {
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ r.readMessages()
+ }
+
+ // 1 is noop, 2 is the first proposal we just sent.
+ // so we start with 2.
+ for tt := 2; tt < r.maxInflight; tt++ {
+ // move forward the window
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Index: uint64(tt)})
+ r.readMessages()
+
+ // fill in the inflights window again
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ ms := r.readMessages()
+ if len(ms) != 1 {
+ t.Fatalf("#%d: len(ms) = %d, want 1", tt, len(ms))
+ }
+
+ // ensure 1
+ if !pr2.ins.full() {
+ t.Fatalf("inflights.full = %t, want %t", pr2.ins.full(), true)
+ }
+
+ // ensure 2
+ for i := 0; i < tt; i++ {
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Index: uint64(i)})
+ if !pr2.ins.full() {
+ t.Fatalf("#%d: inflights.full = %t, want %t", tt, pr2.ins.full(), true)
+ }
+ }
+ }
+}
+
+// TestMsgAppFlowControlRecvHeartbeat ensures a heartbeat response
+// frees one slot if the window is full.
+func TestMsgAppFlowControlRecvHeartbeat(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 5, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+
+ pr2 := r.prs[2]
+ // force the progress to be in replicate state
+ pr2.becomeReplicate()
+ // fill in the inflights window
+ for i := 0; i < r.maxInflight; i++ {
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ r.readMessages()
+ }
+
+ for tt := 1; tt < 5; tt++ {
+ if !pr2.ins.full() {
+ t.Fatalf("#%d: inflights.full = %t, want %t", tt, pr2.ins.full(), true)
+ }
+
+ // recv tt msgHeartbeatResp and expect one free slot
+ for i := 0; i < tt; i++ {
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgHeartbeatResp})
+ r.readMessages()
+ if pr2.ins.full() {
+ t.Fatalf("#%d.%d: inflights.full = %t, want %t", tt, i, pr2.ins.full(), false)
+ }
+ }
+
+ // one slot
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ ms := r.readMessages()
+ if len(ms) != 1 {
+ t.Fatalf("#%d: free slot = 0, want 1", tt)
+ }
+
+ // and just one slot
+ for i := 0; i < 10; i++ {
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ ms1 := r.readMessages()
+ if len(ms1) != 0 {
+ t.Fatalf("#%d.%d: len(ms) = %d, want 0", tt, i, len(ms1))
+ }
+ }
+
+ // clear all pending messages.
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgHeartbeatResp})
+ r.readMessages()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/raft_paper_test.go b/vendor/github.com/coreos/etcd/raft/raft_paper_test.go
new file mode 100644
index 000000000..be5473bf2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raft_paper_test.go
@@ -0,0 +1,934 @@
+// Copyright 2015 CoreOS, 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.
+
+/*
+This file contains tests which verify that the scenarios described
+in the raft paper (https://ramcloud.stanford.edu/raft.pdf) are
+handled by the raft implementation correctly. Each test focuses on
+several sentences written in the paper. This could help us to prevent
+most implementation bugs.
+
+Each test is composed of three parts: init, test and check.
+Init part uses simple and understandable way to simulate the init state.
+Test part uses Step function to generate the scenario. Check part checks
+outgoing messages and state.
+*/
+package raft
+
+import (
+ "fmt"
+ "testing"
+
+ "reflect"
+ "sort"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestFollowerUpdateTermFromMessage(t *testing.T) {
+ testUpdateTermFromMessage(t, StateFollower)
+}
+func TestCandidateUpdateTermFromMessage(t *testing.T) {
+ testUpdateTermFromMessage(t, StateCandidate)
+}
+func TestLeaderUpdateTermFromMessage(t *testing.T) {
+ testUpdateTermFromMessage(t, StateLeader)
+}
+
+// testUpdateTermFromMessage tests that if one server’s current term is
+// smaller than the other’s, then it updates its current term to the larger
+// value. If a candidate or leader discovers that its term is out of date,
+// it immediately reverts to follower state.
+// Reference: section 5.1
+func testUpdateTermFromMessage(t *testing.T, state StateType) {
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ switch state {
+ case StateFollower:
+ r.becomeFollower(1, 2)
+ case StateCandidate:
+ r.becomeCandidate()
+ case StateLeader:
+ r.becomeCandidate()
+ r.becomeLeader()
+ }
+
+ r.Step(pb.Message{Type: pb.MsgApp, Term: 2})
+
+ if r.Term != 2 {
+ t.Errorf("term = %d, want %d", r.Term, 2)
+ }
+ if r.state != StateFollower {
+ t.Errorf("state = %v, want %v", r.state, StateFollower)
+ }
+}
+
+// TestRejectStaleTermMessage tests that if a server receives a request with
+// a stale term number, it rejects the request.
+// Our implementation ignores the request instead.
+// Reference: section 5.1
+func TestRejectStaleTermMessage(t *testing.T) {
+ called := false
+ fakeStep := func(r *raft, m pb.Message) {
+ called = true
+ }
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ r.step = fakeStep
+ r.loadState(pb.HardState{Term: 2})
+
+ r.Step(pb.Message{Type: pb.MsgApp, Term: r.Term - 1})
+
+ if called == true {
+ t.Errorf("stepFunc called = %v, want %v", called, false)
+ }
+}
+
+// TestStartAsFollower tests that when servers start up, they begin as followers.
+// Reference: section 5.2
+func TestStartAsFollower(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ if r.state != StateFollower {
+ t.Errorf("state = %s, want %s", r.state, StateFollower)
+ }
+}
+
+// TestLeaderBcastBeat tests that if the leader receives a heartbeat tick,
+// it will send a msgApp with m.Index = 0, m.LogTerm=0 and empty entries as
+// heartbeat to all followers.
+// Reference: section 5.2
+func TestLeaderBcastBeat(t *testing.T) {
+ // heartbeat interval
+ hi := 1
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, hi, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ for i := 0; i < 10; i++ {
+ r.appendEntry(pb.Entry{Index: uint64(i) + 1})
+ }
+
+ for i := 0; i < hi; i++ {
+ r.tick()
+ }
+
+ msgs := r.readMessages()
+ sort.Sort(messageSlice(msgs))
+ wmsgs := []pb.Message{
+ {From: 1, To: 2, Term: 1, Type: pb.MsgHeartbeat},
+ {From: 1, To: 3, Term: 1, Type: pb.MsgHeartbeat},
+ }
+ if !reflect.DeepEqual(msgs, wmsgs) {
+ t.Errorf("msgs = %v, want %v", msgs, wmsgs)
+ }
+}
+
+func TestFollowerStartElection(t *testing.T) {
+ testNonleaderStartElection(t, StateFollower)
+}
+func TestCandidateStartNewElection(t *testing.T) {
+ testNonleaderStartElection(t, StateCandidate)
+}
+
+// testNonleaderStartElection tests that if a follower receives no communication
+// over election timeout, it begins an election to choose a new leader. It
+// increments its current term and transitions to candidate state. It then
+// votes for itself and issues RequestVote RPCs in parallel to each of the
+// other servers in the cluster.
+// Reference: section 5.2
+// Also if a candidate fails to obtain a majority, it will time out and
+// start a new election by incrementing its term and initiating another
+// round of RequestVote RPCs.
+// Reference: section 5.2
+func testNonleaderStartElection(t *testing.T, state StateType) {
+ // election timeout
+ et := 10
+ r := newTestRaft(1, []uint64{1, 2, 3}, et, 1, NewMemoryStorage())
+ switch state {
+ case StateFollower:
+ r.becomeFollower(1, 2)
+ case StateCandidate:
+ r.becomeCandidate()
+ }
+
+ for i := 0; i < 2*et; i++ {
+ r.tick()
+ }
+
+ if r.Term != 2 {
+ t.Errorf("term = %d, want 2", r.Term)
+ }
+ if r.state != StateCandidate {
+ t.Errorf("state = %s, want %s", r.state, StateCandidate)
+ }
+ if r.votes[r.id] != true {
+ t.Errorf("vote for self = false, want true")
+ }
+ msgs := r.readMessages()
+ sort.Sort(messageSlice(msgs))
+ wmsgs := []pb.Message{
+ {From: 1, To: 2, Term: 2, Type: pb.MsgVote},
+ {From: 1, To: 3, Term: 2, Type: pb.MsgVote},
+ }
+ if !reflect.DeepEqual(msgs, wmsgs) {
+ t.Errorf("msgs = %v, want %v", msgs, wmsgs)
+ }
+}
+
+// TestLeaderElectionInOneRoundRPC tests all cases that may happen in
+// leader election during one round of RequestVote RPC:
+// a) it wins the election
+// b) it loses the election
+// c) it is unclear about the result
+// Reference: section 5.2
+func TestLeaderElectionInOneRoundRPC(t *testing.T) {
+ tests := []struct {
+ size int
+ votes map[uint64]bool
+ state StateType
+ }{
+ // win the election when receiving votes from a majority of the servers
+ {1, map[uint64]bool{}, StateLeader},
+ {3, map[uint64]bool{2: true, 3: true}, StateLeader},
+ {3, map[uint64]bool{2: true}, StateLeader},
+ {5, map[uint64]bool{2: true, 3: true, 4: true, 5: true}, StateLeader},
+ {5, map[uint64]bool{2: true, 3: true, 4: true}, StateLeader},
+ {5, map[uint64]bool{2: true, 3: true}, StateLeader},
+
+ // return to follower state if it receives vote denial from a majority
+ {3, map[uint64]bool{2: false, 3: false}, StateFollower},
+ {5, map[uint64]bool{2: false, 3: false, 4: false, 5: false}, StateFollower},
+ {5, map[uint64]bool{2: true, 3: false, 4: false, 5: false}, StateFollower},
+
+ // stay in candidate if it does not obtain the majority
+ {3, map[uint64]bool{}, StateCandidate},
+ {5, map[uint64]bool{2: true}, StateCandidate},
+ {5, map[uint64]bool{2: false, 3: false}, StateCandidate},
+ {5, map[uint64]bool{}, StateCandidate},
+ }
+ for i, tt := range tests {
+ r := newTestRaft(1, idsBySize(tt.size), 10, 1, NewMemoryStorage())
+
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ for id, vote := range tt.votes {
+ r.Step(pb.Message{From: id, To: 1, Type: pb.MsgVoteResp, Reject: !vote})
+ }
+
+ if r.state != tt.state {
+ t.Errorf("#%d: state = %s, want %s", i, r.state, tt.state)
+ }
+ if g := r.Term; g != 1 {
+ t.Errorf("#%d: term = %d, want %d", i, g, 1)
+ }
+ }
+}
+
+// TestFollowerVote tests that each follower will vote for at most one
+// candidate in a given term, on a first-come-first-served basis.
+// Reference: section 5.2
+func TestFollowerVote(t *testing.T) {
+ tests := []struct {
+ vote uint64
+ nvote uint64
+ wreject bool
+ }{
+ {None, 1, false},
+ {None, 2, false},
+ {1, 1, false},
+ {2, 2, false},
+ {1, 2, true},
+ {2, 1, true},
+ }
+ for i, tt := range tests {
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ r.loadState(pb.HardState{Term: 1, Vote: tt.vote})
+
+ r.Step(pb.Message{From: tt.nvote, To: 1, Term: 1, Type: pb.MsgVote})
+
+ msgs := r.readMessages()
+ wmsgs := []pb.Message{
+ {From: 1, To: tt.nvote, Term: 1, Type: pb.MsgVoteResp, Reject: tt.wreject},
+ }
+ if !reflect.DeepEqual(msgs, wmsgs) {
+ t.Errorf("#%d: msgs = %v, want %v", i, msgs, wmsgs)
+ }
+ }
+}
+
+// TestCandidateFallback tests that while waiting for votes,
+// if a candidate receives an AppendEntries RPC from another server claiming
+// to be leader whose term is at least as large as the candidate's current term,
+// it recognizes the leader as legitimate and returns to follower state.
+// Reference: section 5.2
+func TestCandidateFallback(t *testing.T) {
+ tests := []pb.Message{
+ {From: 2, To: 1, Term: 1, Type: pb.MsgApp},
+ {From: 2, To: 1, Term: 2, Type: pb.MsgApp},
+ }
+ for i, tt := range tests {
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ if r.state != StateCandidate {
+ t.Fatalf("unexpected state = %s, want %s", r.state, StateCandidate)
+ }
+
+ r.Step(tt)
+
+ if g := r.state; g != StateFollower {
+ t.Errorf("#%d: state = %s, want %s", i, g, StateFollower)
+ }
+ if g := r.Term; g != tt.Term {
+ t.Errorf("#%d: term = %d, want %d", i, g, tt.Term)
+ }
+ }
+}
+
+func TestFollowerElectionTimeoutRandomized(t *testing.T) {
+ SetLogger(discardLogger)
+ defer SetLogger(defaultLogger)
+ testNonleaderElectionTimeoutRandomized(t, StateFollower)
+}
+func TestCandidateElectionTimeoutRandomized(t *testing.T) {
+ SetLogger(discardLogger)
+ defer SetLogger(defaultLogger)
+ testNonleaderElectionTimeoutRandomized(t, StateCandidate)
+}
+
+// testNonleaderElectionTimeoutRandomized tests that election timeout for
+// follower or candidate is randomized.
+// Reference: section 5.2
+func testNonleaderElectionTimeoutRandomized(t *testing.T, state StateType) {
+ et := 10
+ r := newTestRaft(1, []uint64{1, 2, 3}, et, 1, NewMemoryStorage())
+ timeouts := make(map[int]bool)
+ for round := 0; round < 50*et; round++ {
+ switch state {
+ case StateFollower:
+ r.becomeFollower(r.Term+1, 2)
+ case StateCandidate:
+ r.becomeCandidate()
+ }
+
+ time := 0
+ for len(r.readMessages()) == 0 {
+ r.tick()
+ time++
+ }
+ timeouts[time] = true
+ }
+
+ for d := et + 1; d < 2*et; d++ {
+ if timeouts[d] != true {
+ t.Errorf("timeout in %d ticks should happen", d)
+ }
+ }
+}
+
+func TestFollowersElectioinTimeoutNonconflict(t *testing.T) {
+ SetLogger(discardLogger)
+ defer SetLogger(defaultLogger)
+ testNonleadersElectionTimeoutNonconflict(t, StateFollower)
+}
+func TestCandidatesElectionTimeoutNonconflict(t *testing.T) {
+ SetLogger(discardLogger)
+ defer SetLogger(defaultLogger)
+ testNonleadersElectionTimeoutNonconflict(t, StateCandidate)
+}
+
+// testNonleadersElectionTimeoutNonconflict tests that in most cases only a
+// single server(follower or candidate) will time out, which reduces the
+// likelihood of split vote in the new election.
+// Reference: section 5.2
+func testNonleadersElectionTimeoutNonconflict(t *testing.T, state StateType) {
+ et := 10
+ size := 5
+ rs := make([]*raft, size)
+ ids := idsBySize(size)
+ for k := range rs {
+ rs[k] = newTestRaft(ids[k], ids, et, 1, NewMemoryStorage())
+ }
+ conflicts := 0
+ for round := 0; round < 1000; round++ {
+ for _, r := range rs {
+ switch state {
+ case StateFollower:
+ r.becomeFollower(r.Term+1, None)
+ case StateCandidate:
+ r.becomeCandidate()
+ }
+ }
+
+ timeoutNum := 0
+ for timeoutNum == 0 {
+ for _, r := range rs {
+ r.tick()
+ if len(r.readMessages()) > 0 {
+ timeoutNum++
+ }
+ }
+ }
+ // several rafts time out at the same tick
+ if timeoutNum > 1 {
+ conflicts++
+ }
+ }
+
+ if g := float64(conflicts) / 1000; g > 0.4 {
+ t.Errorf("probability of conflicts = %v, want <= 0.4", g)
+ }
+}
+
+// TestLeaderStartReplication tests that when receiving client proposals,
+// the leader appends the proposal to its log as a new entry, then issues
+// AppendEntries RPCs in parallel to each of the other servers to replicate
+// the entry. Also, when sending an AppendEntries RPC, the leader includes
+// the index and term of the entry in its log that immediately precedes
+// the new entries.
+// Also, it writes the new entry into stable storage.
+// Reference: section 5.3
+func TestLeaderStartReplication(t *testing.T) {
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, s)
+ r.becomeCandidate()
+ r.becomeLeader()
+ commitNoopEntry(r, s)
+ li := r.raftLog.lastIndex()
+
+ ents := []pb.Entry{{Data: []byte("some data")}}
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: ents})
+
+ if g := r.raftLog.lastIndex(); g != li+1 {
+ t.Errorf("lastIndex = %d, want %d", g, li+1)
+ }
+ if g := r.raftLog.committed; g != li {
+ t.Errorf("committed = %d, want %d", g, li)
+ }
+ msgs := r.readMessages()
+ sort.Sort(messageSlice(msgs))
+ wents := []pb.Entry{{Index: li + 1, Term: 1, Data: []byte("some data")}}
+ wmsgs := []pb.Message{
+ {From: 1, To: 2, Term: 1, Type: pb.MsgApp, Index: li, LogTerm: 1, Entries: wents, Commit: li},
+ {From: 1, To: 3, Term: 1, Type: pb.MsgApp, Index: li, LogTerm: 1, Entries: wents, Commit: li},
+ }
+ if !reflect.DeepEqual(msgs, wmsgs) {
+ t.Errorf("msgs = %+v, want %+v", msgs, wmsgs)
+ }
+ if g := r.raftLog.unstableEntries(); !reflect.DeepEqual(g, wents) {
+ t.Errorf("ents = %+v, want %+v", g, wents)
+ }
+}
+
+// TestLeaderCommitEntry tests that when the entry has been safely replicated,
+// the leader gives out the applied entries, which can be applied to its state
+// machine.
+// Also, the leader keeps track of the highest index it knows to be committed,
+// and it includes that index in future AppendEntries RPCs so that the other
+// servers eventually find out.
+// Reference: section 5.3
+func TestLeaderCommitEntry(t *testing.T) {
+ s := NewMemoryStorage()
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, s)
+ r.becomeCandidate()
+ r.becomeLeader()
+ commitNoopEntry(r, s)
+ li := r.raftLog.lastIndex()
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+
+ for _, m := range r.readMessages() {
+ r.Step(acceptAndReply(m))
+ }
+
+ if g := r.raftLog.committed; g != li+1 {
+ t.Errorf("committed = %d, want %d", g, li+1)
+ }
+ wents := []pb.Entry{{Index: li + 1, Term: 1, Data: []byte("some data")}}
+ if g := r.raftLog.nextEnts(); !reflect.DeepEqual(g, wents) {
+ t.Errorf("nextEnts = %+v, want %+v", g, wents)
+ }
+ msgs := r.readMessages()
+ sort.Sort(messageSlice(msgs))
+ for i, m := range msgs {
+ if w := uint64(i + 2); m.To != w {
+ t.Errorf("to = %x, want %x", m.To, w)
+ }
+ if m.Type != pb.MsgApp {
+ t.Errorf("type = %s, want %s", m.Type, pb.MsgApp)
+ }
+ if m.Commit != li+1 {
+ t.Errorf("commit = %d, want %d", m.Commit, li+1)
+ }
+ }
+}
+
+// TestLeaderAcknowledgeCommit tests that a log entry is committed once the
+// leader that created the entry has replicated it on a majority of the servers.
+// Reference: section 5.3
+func TestLeaderAcknowledgeCommit(t *testing.T) {
+ tests := []struct {
+ size int
+ acceptors map[uint64]bool
+ wack bool
+ }{
+ {1, nil, true},
+ {3, nil, false},
+ {3, map[uint64]bool{2: true}, true},
+ {3, map[uint64]bool{2: true, 3: true}, true},
+ {5, nil, false},
+ {5, map[uint64]bool{2: true}, false},
+ {5, map[uint64]bool{2: true, 3: true}, true},
+ {5, map[uint64]bool{2: true, 3: true, 4: true}, true},
+ {5, map[uint64]bool{2: true, 3: true, 4: true, 5: true}, true},
+ }
+ for i, tt := range tests {
+ s := NewMemoryStorage()
+ r := newTestRaft(1, idsBySize(tt.size), 10, 1, s)
+ r.becomeCandidate()
+ r.becomeLeader()
+ commitNoopEntry(r, s)
+ li := r.raftLog.lastIndex()
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+
+ for _, m := range r.readMessages() {
+ if tt.acceptors[m.To] {
+ r.Step(acceptAndReply(m))
+ }
+ }
+
+ if g := r.raftLog.committed > li; g != tt.wack {
+ t.Errorf("#%d: ack commit = %v, want %v", i, g, tt.wack)
+ }
+ }
+}
+
+// TestLeaderCommitPrecedingEntries tests that when leader commits a log entry,
+// it also commits all preceding entries in the leader’s log, including
+// entries created by previous leaders.
+// Also, it applies the entry to its local state machine (in log order).
+// Reference: section 5.3
+func TestLeaderCommitPrecedingEntries(t *testing.T) {
+ tests := [][]pb.Entry{
+ {},
+ {{Term: 2, Index: 1}},
+ {{Term: 1, Index: 1}, {Term: 2, Index: 2}},
+ {{Term: 1, Index: 1}},
+ }
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append(tt)
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, storage)
+ r.loadState(pb.HardState{Term: 2})
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+
+ for _, m := range r.readMessages() {
+ r.Step(acceptAndReply(m))
+ }
+
+ li := uint64(len(tt))
+ wents := append(tt, pb.Entry{Term: 3, Index: li + 1}, pb.Entry{Term: 3, Index: li + 2, Data: []byte("some data")})
+ if g := r.raftLog.nextEnts(); !reflect.DeepEqual(g, wents) {
+ t.Errorf("#%d: ents = %+v, want %+v", i, g, wents)
+ }
+ }
+}
+
+// TestFollowerCommitEntry tests that once a follower learns that a log entry
+// is committed, it applies the entry to its local state machine (in log order).
+// Reference: section 5.3
+func TestFollowerCommitEntry(t *testing.T) {
+ tests := []struct {
+ ents []pb.Entry
+ commit uint64
+ }{
+ {
+ []pb.Entry{
+ {Term: 1, Index: 1, Data: []byte("some data")},
+ },
+ 1,
+ },
+ {
+ []pb.Entry{
+ {Term: 1, Index: 1, Data: []byte("some data")},
+ {Term: 1, Index: 2, Data: []byte("some data2")},
+ },
+ 2,
+ },
+ {
+ []pb.Entry{
+ {Term: 1, Index: 1, Data: []byte("some data2")},
+ {Term: 1, Index: 2, Data: []byte("some data")},
+ },
+ 2,
+ },
+ {
+ []pb.Entry{
+ {Term: 1, Index: 1, Data: []byte("some data")},
+ {Term: 1, Index: 2, Data: []byte("some data2")},
+ },
+ 1,
+ },
+ }
+ for i, tt := range tests {
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ r.becomeFollower(1, 2)
+
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgApp, Term: 1, Entries: tt.ents, Commit: tt.commit})
+
+ if g := r.raftLog.committed; g != tt.commit {
+ t.Errorf("#%d: committed = %d, want %d", i, g, tt.commit)
+ }
+ wents := tt.ents[:int(tt.commit)]
+ if g := r.raftLog.nextEnts(); !reflect.DeepEqual(g, wents) {
+ t.Errorf("#%d: nextEnts = %v, want %v", i, g, wents)
+ }
+ }
+}
+
+// TestFollowerCheckMsgApp tests that if the follower does not find an
+// entry in its log with the same index and term as the one in AppendEntries RPC,
+// then it refuses the new entries. Otherwise it replies that it accepts the
+// append entries.
+// Reference: section 5.3
+func TestFollowerCheckMsgApp(t *testing.T) {
+ ents := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}
+ tests := []struct {
+ term uint64
+ index uint64
+ windex uint64
+ wreject bool
+ wrejectHint uint64
+ }{
+ // match with committed entries
+ {0, 0, 1, false, 0},
+ {ents[0].Term, ents[0].Index, 1, false, 0},
+ // match with uncommitted entries
+ {ents[1].Term, ents[1].Index, 2, false, 0},
+
+ // unmatch with existing entry
+ {ents[0].Term, ents[1].Index, ents[1].Index, true, 2},
+ // unexisting entry
+ {ents[1].Term + 1, ents[1].Index + 1, ents[1].Index + 1, true, 2},
+ }
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append(ents)
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, storage)
+ r.loadState(pb.HardState{Commit: 1})
+ r.becomeFollower(2, 2)
+
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgApp, Term: 2, LogTerm: tt.term, Index: tt.index})
+
+ msgs := r.readMessages()
+ wmsgs := []pb.Message{
+ {From: 1, To: 2, Type: pb.MsgAppResp, Term: 2, Index: tt.windex, Reject: tt.wreject, RejectHint: tt.wrejectHint},
+ }
+ if !reflect.DeepEqual(msgs, wmsgs) {
+ t.Errorf("#%d: msgs = %+v, want %+v", i, msgs, wmsgs)
+ }
+ }
+}
+
+// TestFollowerAppendEntries tests that when AppendEntries RPC is valid,
+// the follower will delete the existing conflict entry and all that follow it,
+// and append any new entries not already in the log.
+// Also, it writes the new entry into stable storage.
+// Reference: section 5.3
+func TestFollowerAppendEntries(t *testing.T) {
+ tests := []struct {
+ index, term uint64
+ ents []pb.Entry
+ wents []pb.Entry
+ wunstable []pb.Entry
+ }{
+ {
+ 2, 2,
+ []pb.Entry{{Term: 3, Index: 3}},
+ []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}, {Term: 3, Index: 3}},
+ []pb.Entry{{Term: 3, Index: 3}},
+ },
+ {
+ 1, 1,
+ []pb.Entry{{Term: 3, Index: 2}, {Term: 4, Index: 3}},
+ []pb.Entry{{Term: 1, Index: 1}, {Term: 3, Index: 2}, {Term: 4, Index: 3}},
+ []pb.Entry{{Term: 3, Index: 2}, {Term: 4, Index: 3}},
+ },
+ {
+ 0, 0,
+ []pb.Entry{{Term: 1, Index: 1}},
+ []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}},
+ nil,
+ },
+ {
+ 0, 0,
+ []pb.Entry{{Term: 3, Index: 1}},
+ []pb.Entry{{Term: 3, Index: 1}},
+ []pb.Entry{{Term: 3, Index: 1}},
+ },
+ }
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append([]pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}})
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, storage)
+ r.becomeFollower(2, 2)
+
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgApp, Term: 2, LogTerm: tt.term, Index: tt.index, Entries: tt.ents})
+
+ if g := r.raftLog.allEntries(); !reflect.DeepEqual(g, tt.wents) {
+ t.Errorf("#%d: ents = %+v, want %+v", i, g, tt.wents)
+ }
+ if g := r.raftLog.unstableEntries(); !reflect.DeepEqual(g, tt.wunstable) {
+ t.Errorf("#%d: unstableEnts = %+v, want %+v", i, g, tt.wunstable)
+ }
+ }
+}
+
+// TestLeaderSyncFollowerLog tests that the leader could bring a follower's log
+// into consistency with its own.
+// Reference: section 5.3, figure 7
+func TestLeaderSyncFollowerLog(t *testing.T) {
+ ents := []pb.Entry{
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 4, Index: 4}, {Term: 4, Index: 5},
+ {Term: 5, Index: 6}, {Term: 5, Index: 7},
+ {Term: 6, Index: 8}, {Term: 6, Index: 9}, {Term: 6, Index: 10},
+ }
+ term := uint64(8)
+ tests := [][]pb.Entry{
+ {
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 4, Index: 4}, {Term: 4, Index: 5},
+ {Term: 5, Index: 6}, {Term: 5, Index: 7},
+ {Term: 6, Index: 8}, {Term: 6, Index: 9},
+ },
+ {
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 4, Index: 4},
+ },
+ {
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 4, Index: 4}, {Term: 4, Index: 5},
+ {Term: 5, Index: 6}, {Term: 5, Index: 7},
+ {Term: 6, Index: 8}, {Term: 6, Index: 9}, {Term: 6, Index: 10}, {Term: 6, Index: 11},
+ },
+ {
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 4, Index: 4}, {Term: 4, Index: 5},
+ {Term: 5, Index: 6}, {Term: 5, Index: 7},
+ {Term: 6, Index: 8}, {Term: 6, Index: 9}, {Term: 6, Index: 10},
+ {Term: 7, Index: 11}, {Term: 7, Index: 12},
+ },
+ {
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 4, Index: 4}, {Term: 4, Index: 5}, {Term: 4, Index: 6}, {Term: 4, Index: 7},
+ },
+ {
+ {},
+ {Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3},
+ {Term: 2, Index: 4}, {Term: 2, Index: 5}, {Term: 2, Index: 6},
+ {Term: 3, Index: 7}, {Term: 3, Index: 8}, {Term: 3, Index: 9}, {Term: 3, Index: 10}, {Term: 3, Index: 11},
+ },
+ }
+ for i, tt := range tests {
+ leadStorage := NewMemoryStorage()
+ leadStorage.Append(ents)
+ lead := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, leadStorage)
+ lead.loadState(pb.HardState{Commit: lead.raftLog.lastIndex(), Term: term})
+ followerStorage := NewMemoryStorage()
+ followerStorage.Append(tt)
+ follower := newTestRaft(2, []uint64{1, 2, 3}, 10, 1, followerStorage)
+ follower.loadState(pb.HardState{Term: term - 1})
+ // It is necessary to have a three-node cluster.
+ // The second may have more up-to-date log than the first one, so the
+ // first node needs the vote from the third node to become the leader.
+ n := newNetwork(lead, follower, nopStepper)
+ n.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ n.send(pb.Message{From: 3, To: 1, Type: pb.MsgVoteResp, Term: 1})
+
+ n.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{}}})
+
+ if g := diffu(ltoa(lead.raftLog), ltoa(follower.raftLog)); g != "" {
+ t.Errorf("#%d: log diff:\n%s", i, g)
+ }
+ }
+}
+
+// TestVoteRequest tests that the vote request includes information about the candidate’s log
+// and are sent to all of the other nodes.
+// Reference: section 5.4.1
+func TestVoteRequest(t *testing.T) {
+ tests := []struct {
+ ents []pb.Entry
+ wterm uint64
+ }{
+ {[]pb.Entry{{Term: 1, Index: 1}}, 2},
+ {[]pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}, 3},
+ }
+ for j, tt := range tests {
+ r := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ r.Step(pb.Message{
+ From: 2, To: 1, Type: pb.MsgApp, Term: tt.wterm - 1, LogTerm: 0, Index: 0, Entries: tt.ents,
+ })
+ r.readMessages()
+
+ for i := 0; i < r.electionTimeout*2; i++ {
+ r.tickElection()
+ }
+
+ msgs := r.readMessages()
+ sort.Sort(messageSlice(msgs))
+ if len(msgs) != 2 {
+ t.Fatalf("#%d: len(msg) = %d, want %d", j, len(msgs), 2)
+ }
+ for i, m := range msgs {
+ if m.Type != pb.MsgVote {
+ t.Errorf("#%d: msgType = %d, want %d", i, m.Type, pb.MsgVote)
+ }
+ if m.To != uint64(i+2) {
+ t.Errorf("#%d: to = %d, want %d", i, m.To, i+2)
+ }
+ if m.Term != tt.wterm {
+ t.Errorf("#%d: term = %d, want %d", i, m.Term, tt.wterm)
+ }
+ windex, wlogterm := tt.ents[len(tt.ents)-1].Index, tt.ents[len(tt.ents)-1].Term
+ if m.Index != windex {
+ t.Errorf("#%d: index = %d, want %d", i, m.Index, windex)
+ }
+ if m.LogTerm != wlogterm {
+ t.Errorf("#%d: logterm = %d, want %d", i, m.LogTerm, wlogterm)
+ }
+ }
+ }
+}
+
+// TestVoter tests the voter denies its vote if its own log is more up-to-date
+// than that of the candidate.
+// Reference: section 5.4.1
+func TestVoter(t *testing.T) {
+ tests := []struct {
+ ents []pb.Entry
+ logterm uint64
+ index uint64
+
+ wreject bool
+ }{
+ // same logterm
+ {[]pb.Entry{{Term: 1, Index: 1}}, 1, 1, false},
+ {[]pb.Entry{{Term: 1, Index: 1}}, 1, 2, false},
+ {[]pb.Entry{{Term: 1, Index: 1}, {Term: 1, Index: 2}}, 1, 1, true},
+ // candidate higher logterm
+ {[]pb.Entry{{Term: 1, Index: 1}}, 2, 1, false},
+ {[]pb.Entry{{Term: 1, Index: 1}}, 2, 2, false},
+ {[]pb.Entry{{Term: 1, Index: 1}, {Term: 1, Index: 2}}, 2, 1, false},
+ // voter higher logterm
+ {[]pb.Entry{{Term: 2, Index: 1}}, 1, 1, true},
+ {[]pb.Entry{{Term: 2, Index: 1}}, 1, 2, true},
+ {[]pb.Entry{{Term: 2, Index: 1}, {Term: 1, Index: 2}}, 1, 1, true},
+ }
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append(tt.ents)
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgVote, Term: 3, LogTerm: tt.logterm, Index: tt.index})
+
+ msgs := r.readMessages()
+ if len(msgs) != 1 {
+ t.Fatalf("#%d: len(msg) = %d, want %d", i, len(msgs), 1)
+ }
+ m := msgs[0]
+ if m.Type != pb.MsgVoteResp {
+ t.Errorf("#%d: msgType = %d, want %d", i, m.Type, pb.MsgVoteResp)
+ }
+ if m.Reject != tt.wreject {
+ t.Errorf("#%d: reject = %t, want %t", i, m.Reject, tt.wreject)
+ }
+ }
+}
+
+// TestLeaderOnlyCommitsLogFromCurrentTerm tests that only log entries from the leader’s
+// current term are committed by counting replicas.
+// Reference: section 5.4.2
+func TestLeaderOnlyCommitsLogFromCurrentTerm(t *testing.T) {
+ ents := []pb.Entry{{Term: 1, Index: 1}, {Term: 2, Index: 2}}
+ tests := []struct {
+ index uint64
+ wcommit uint64
+ }{
+ // do not commit log entries in previous terms
+ {1, 0},
+ {2, 0},
+ // commit log in current term
+ {3, 3},
+ }
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append(ents)
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ r.loadState(pb.HardState{Term: 2})
+ // become leader at term 3
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.readMessages()
+ // propose a entry to current term
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{}}})
+
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Term: r.Term, Index: tt.index})
+ if r.raftLog.committed != tt.wcommit {
+ t.Errorf("#%d: commit = %d, want %d", i, r.raftLog.committed, tt.wcommit)
+ }
+ }
+}
+
+type messageSlice []pb.Message
+
+func (s messageSlice) Len() int { return len(s) }
+func (s messageSlice) Less(i, j int) bool { return fmt.Sprint(s[i]) < fmt.Sprint(s[j]) }
+func (s messageSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+func commitNoopEntry(r *raft, s *MemoryStorage) {
+ if r.state != StateLeader {
+ panic("it should only be used when it is the leader")
+ }
+ r.bcastAppend()
+ // simulate the response of MsgApp
+ msgs := r.readMessages()
+ for _, m := range msgs {
+ if m.Type != pb.MsgApp || len(m.Entries) != 1 || m.Entries[0].Data != nil {
+ panic("not a message to append noop entry")
+ }
+ r.Step(acceptAndReply(m))
+ }
+ // ignore further messages to refresh followers' commmit index
+ r.readMessages()
+ s.Append(r.raftLog.unstableEntries())
+ r.raftLog.appliedTo(r.raftLog.committed)
+ r.raftLog.stableTo(r.raftLog.lastIndex(), r.raftLog.lastTerm())
+}
+
+func acceptAndReply(m pb.Message) pb.Message {
+ if m.Type != pb.MsgApp {
+ panic("type should be MsgApp")
+ }
+ return pb.Message{
+ From: m.To,
+ To: m.From,
+ Term: m.Term,
+ Type: pb.MsgAppResp,
+ Index: m.Index + uint64(len(m.Entries)),
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/raft_snap_test.go b/vendor/github.com/coreos/etcd/raft/raft_snap_test.go
new file mode 100644
index 000000000..f75c784c2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raft_snap_test.go
@@ -0,0 +1,134 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+var (
+ testingSnap = pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{
+ Index: 11, // magic number
+ Term: 11, // magic number
+ ConfState: pb.ConfState{Nodes: []uint64{1, 2}},
+ },
+ }
+)
+
+func TestSendingSnapshotSetPendingSnapshot(t *testing.T) {
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1}, 10, 1, storage)
+ sm.restore(testingSnap)
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ // force set the next of node 1, so that
+ // node 1 needs a snapshot
+ sm.prs[2].Next = sm.raftLog.firstIndex()
+
+ sm.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Index: sm.prs[2].Next - 1, Reject: true})
+ if sm.prs[2].PendingSnapshot != 11 {
+ t.Fatalf("PendingSnapshot = %d, want 11", sm.prs[2].PendingSnapshot)
+ }
+}
+
+func TestPendingSnapshotPauseReplication(t *testing.T) {
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ sm.restore(testingSnap)
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ sm.prs[2].becomeSnapshot(11)
+
+ sm.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ msgs := sm.readMessages()
+ if len(msgs) != 0 {
+ t.Fatalf("len(msgs) = %d, want 0", len(msgs))
+ }
+}
+
+func TestSnapshotFailure(t *testing.T) {
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ sm.restore(testingSnap)
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ sm.prs[2].Next = 1
+ sm.prs[2].becomeSnapshot(11)
+
+ sm.Step(pb.Message{From: 2, To: 1, Type: pb.MsgSnapStatus, Reject: true})
+ if sm.prs[2].PendingSnapshot != 0 {
+ t.Fatalf("PendingSnapshot = %d, want 0", sm.prs[2].PendingSnapshot)
+ }
+ if sm.prs[2].Next != 1 {
+ t.Fatalf("Next = %d, want 1", sm.prs[2].Next)
+ }
+ if sm.prs[2].Paused != true {
+ t.Errorf("Paused = %v, want true", sm.prs[2].Paused)
+ }
+}
+
+func TestSnapshotSucceed(t *testing.T) {
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ sm.restore(testingSnap)
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ sm.prs[2].Next = 1
+ sm.prs[2].becomeSnapshot(11)
+
+ sm.Step(pb.Message{From: 2, To: 1, Type: pb.MsgSnapStatus, Reject: false})
+ if sm.prs[2].PendingSnapshot != 0 {
+ t.Fatalf("PendingSnapshot = %d, want 0", sm.prs[2].PendingSnapshot)
+ }
+ if sm.prs[2].Next != 12 {
+ t.Fatalf("Next = %d, want 12", sm.prs[2].Next)
+ }
+ if sm.prs[2].Paused != true {
+ t.Errorf("Paused = %v, want true", sm.prs[2].Paused)
+ }
+}
+
+func TestSnapshotAbort(t *testing.T) {
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ sm.restore(testingSnap)
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ sm.prs[2].Next = 1
+ sm.prs[2].becomeSnapshot(11)
+
+ // A successful msgAppResp that has a higher/equal index than the
+ // pending snapshot should abort the pending snapshot.
+ sm.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Index: 11})
+ if sm.prs[2].PendingSnapshot != 0 {
+ t.Fatalf("PendingSnapshot = %d, want 0", sm.prs[2].PendingSnapshot)
+ }
+ if sm.prs[2].Next != 12 {
+ t.Fatalf("Next = %d, want 12", sm.prs[2].Next)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/raft_test.go b/vendor/github.com/coreos/etcd/raft/raft_test.go
new file mode 100644
index 000000000..9534632db
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raft_test.go
@@ -0,0 +1,1902 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "math/rand"
+ "reflect"
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+// nextEnts returns the appliable entries and updates the applied index
+func nextEnts(r *raft, s *MemoryStorage) (ents []pb.Entry) {
+ // Transfer all unstable entries to "stable" storage.
+ s.Append(r.raftLog.unstableEntries())
+ r.raftLog.stableTo(r.raftLog.lastIndex(), r.raftLog.lastTerm())
+
+ ents = r.raftLog.nextEnts()
+ r.raftLog.appliedTo(r.raftLog.committed)
+ return ents
+}
+
+type Interface interface {
+ Step(m pb.Message) error
+ readMessages() []pb.Message
+}
+
+func (r *raft) readMessages() []pb.Message {
+ msgs := r.msgs
+ r.msgs = make([]pb.Message, 0)
+
+ return msgs
+}
+
+func TestProgressBecomeProbe(t *testing.T) {
+ match := uint64(1)
+ tests := []struct {
+ p *Progress
+ wnext uint64
+ }{
+ {
+ &Progress{State: ProgressStateReplicate, Match: match, Next: 5, ins: newInflights(256)},
+ 2,
+ },
+ {
+ // snapshot finish
+ &Progress{State: ProgressStateSnapshot, Match: match, Next: 5, PendingSnapshot: 10, ins: newInflights(256)},
+ 11,
+ },
+ {
+ // snapshot failure
+ &Progress{State: ProgressStateSnapshot, Match: match, Next: 5, PendingSnapshot: 0, ins: newInflights(256)},
+ 2,
+ },
+ }
+ for i, tt := range tests {
+ tt.p.becomeProbe()
+ if tt.p.State != ProgressStateProbe {
+ t.Errorf("#%d: state = %s, want %s", i, tt.p.State, ProgressStateProbe)
+ }
+ if tt.p.Match != match {
+ t.Errorf("#%d: match = %d, want %d", i, tt.p.Match, match)
+ }
+ if tt.p.Next != tt.wnext {
+ t.Errorf("#%d: next = %d, want %d", i, tt.p.Next, tt.wnext)
+ }
+ }
+}
+
+func TestProgressBecomeReplicate(t *testing.T) {
+ p := &Progress{State: ProgressStateProbe, Match: 1, Next: 5, ins: newInflights(256)}
+ p.becomeReplicate()
+
+ if p.State != ProgressStateReplicate {
+ t.Errorf("state = %s, want %s", p.State, ProgressStateReplicate)
+ }
+ if p.Match != 1 {
+ t.Errorf("match = %d, want 1", p.Match)
+ }
+ if w := p.Match + 1; p.Next != w {
+ t.Errorf("next = %d, want %d", p.Next, w)
+ }
+}
+
+func TestProgressBecomeSnapshot(t *testing.T) {
+ p := &Progress{State: ProgressStateProbe, Match: 1, Next: 5, ins: newInflights(256)}
+ p.becomeSnapshot(10)
+
+ if p.State != ProgressStateSnapshot {
+ t.Errorf("state = %s, want %s", p.State, ProgressStateSnapshot)
+ }
+ if p.Match != 1 {
+ t.Errorf("match = %d, want 1", p.Match)
+ }
+ if p.PendingSnapshot != 10 {
+ t.Errorf("pendingSnapshot = %d, want 10", p.PendingSnapshot)
+ }
+}
+
+func TestProgressUpdate(t *testing.T) {
+ prevM, prevN := uint64(3), uint64(5)
+ tests := []struct {
+ update uint64
+
+ wm uint64
+ wn uint64
+ wok bool
+ }{
+ {prevM - 1, prevM, prevN, false}, // do not decrease match, next
+ {prevM, prevM, prevN, false}, // do not decrease next
+ {prevM + 1, prevM + 1, prevN, true}, // increase match, do not decrease next
+ {prevM + 2, prevM + 2, prevN + 1, true}, // increase match, next
+ }
+ for i, tt := range tests {
+ p := &Progress{
+ Match: prevM,
+ Next: prevN,
+ }
+ ok := p.maybeUpdate(tt.update)
+ if ok != tt.wok {
+ t.Errorf("#%d: ok= %v, want %v", i, ok, tt.wok)
+ }
+ if p.Match != tt.wm {
+ t.Errorf("#%d: match= %d, want %d", i, p.Match, tt.wm)
+ }
+ if p.Next != tt.wn {
+ t.Errorf("#%d: next= %d, want %d", i, p.Next, tt.wn)
+ }
+ }
+}
+
+func TestProgressMaybeDecr(t *testing.T) {
+ tests := []struct {
+ state ProgressStateType
+ m uint64
+ n uint64
+ rejected uint64
+ last uint64
+
+ w bool
+ wn uint64
+ }{
+ {
+ // state replicate and rejected is not greater than match
+ ProgressStateReplicate, 5, 10, 5, 5, false, 10,
+ },
+ {
+ // state replicate and rejected is not greater than match
+ ProgressStateReplicate, 5, 10, 4, 4, false, 10,
+ },
+ {
+ // state replicate and rejected is greater than match
+ // directly decrease to match+1
+ ProgressStateReplicate, 5, 10, 9, 9, true, 6,
+ },
+ {
+ // next-1 != rejected is always false
+ ProgressStateProbe, 0, 0, 0, 0, false, 0,
+ },
+ {
+ // next-1 != rejected is always false
+ ProgressStateProbe, 0, 10, 5, 5, false, 10,
+ },
+ {
+ // next>1 = decremented by 1
+ ProgressStateProbe, 0, 10, 9, 9, true, 9,
+ },
+ {
+ // next>1 = decremented by 1
+ ProgressStateProbe, 0, 2, 1, 1, true, 1,
+ },
+ {
+ // next<=1 = reset to 1
+ ProgressStateProbe, 0, 1, 0, 0, true, 1,
+ },
+ {
+ // decrease to min(rejected, last+1)
+ ProgressStateProbe, 0, 10, 9, 2, true, 3,
+ },
+ {
+ // rejected < 1, reset to 1
+ ProgressStateProbe, 0, 10, 9, 0, true, 1,
+ },
+ }
+ for i, tt := range tests {
+ p := &Progress{
+ State: tt.state,
+ Match: tt.m,
+ Next: tt.n,
+ }
+ if g := p.maybeDecrTo(tt.rejected, tt.last); g != tt.w {
+ t.Errorf("#%d: maybeDecrTo= %t, want %t", i, g, tt.w)
+ }
+ if gm := p.Match; gm != tt.m {
+ t.Errorf("#%d: match= %d, want %d", i, gm, tt.m)
+ }
+ if gn := p.Next; gn != tt.wn {
+ t.Errorf("#%d: next= %d, want %d", i, gn, tt.wn)
+ }
+ }
+}
+
+func TestProgressIsPaused(t *testing.T) {
+ tests := []struct {
+ state ProgressStateType
+ paused bool
+
+ w bool
+ }{
+ {ProgressStateProbe, false, false},
+ {ProgressStateProbe, true, true},
+ {ProgressStateReplicate, false, false},
+ {ProgressStateReplicate, true, false},
+ {ProgressStateSnapshot, false, true},
+ {ProgressStateSnapshot, true, true},
+ }
+ for i, tt := range tests {
+ p := &Progress{
+ State: tt.state,
+ Paused: tt.paused,
+ ins: newInflights(256),
+ }
+ if g := p.isPaused(); g != tt.w {
+ t.Errorf("#%d: shouldwait = %t, want %t", i, g, tt.w)
+ }
+ }
+}
+
+// TestProgressResume ensures that progress.maybeUpdate and progress.maybeDecrTo
+// will reset progress.paused.
+func TestProgressResume(t *testing.T) {
+ p := &Progress{
+ Next: 2,
+ Paused: true,
+ }
+ p.maybeDecrTo(1, 1)
+ if p.Paused != false {
+ t.Errorf("paused= %v, want false", p.Paused)
+ }
+ p.Paused = true
+ p.maybeUpdate(2)
+ if p.Paused != false {
+ t.Errorf("paused= %v, want false", p.Paused)
+ }
+}
+
+// TestProgressResumeByHeartbeat ensures raft.heartbeat reset progress.paused by heartbeat.
+func TestProgressResumeByHeartbeat(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 5, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.prs[2].Paused = true
+
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgBeat})
+ if r.prs[2].Paused != false {
+ t.Errorf("paused = %v, want false", r.prs[2].Paused)
+ }
+}
+
+func TestProgressPaused(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 5, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+
+ ms := r.readMessages()
+ if len(ms) != 1 {
+ t.Errorf("len(ms) = %d, want 1", len(ms))
+ }
+}
+
+func TestLeaderElection(t *testing.T) {
+ tests := []struct {
+ *network
+ state StateType
+ }{
+ {newNetwork(nil, nil, nil), StateLeader},
+ {newNetwork(nil, nil, nopStepper), StateLeader},
+ {newNetwork(nil, nopStepper, nopStepper), StateCandidate},
+ {newNetwork(nil, nopStepper, nopStepper, nil), StateCandidate},
+ {newNetwork(nil, nopStepper, nopStepper, nil, nil), StateLeader},
+
+ // three logs further along than 0
+ {newNetwork(nil, ents(1), ents(2), ents(1, 3), nil), StateFollower},
+
+ // logs converge
+ {newNetwork(ents(1), nil, ents(2), ents(1), nil), StateLeader},
+ }
+
+ for i, tt := range tests {
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ sm := tt.network.peers[1].(*raft)
+ if sm.state != tt.state {
+ t.Errorf("#%d: state = %s, want %s", i, sm.state, tt.state)
+ }
+ if g := sm.Term; g != 1 {
+ t.Errorf("#%d: term = %d, want %d", i, g, 1)
+ }
+ }
+}
+
+func TestLogReplication(t *testing.T) {
+ tests := []struct {
+ *network
+ msgs []pb.Message
+ wcommitted uint64
+ }{
+ {
+ newNetwork(nil, nil, nil),
+ []pb.Message{
+ {From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}},
+ },
+ 2,
+ },
+ {
+ newNetwork(nil, nil, nil),
+ []pb.Message{
+ {From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}},
+ {From: 1, To: 2, Type: pb.MsgHup},
+ {From: 1, To: 2, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}},
+ },
+ 4,
+ },
+ }
+
+ for i, tt := range tests {
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+
+ for _, m := range tt.msgs {
+ tt.send(m)
+ }
+
+ for j, x := range tt.network.peers {
+ sm := x.(*raft)
+
+ if sm.raftLog.committed != tt.wcommitted {
+ t.Errorf("#%d.%d: committed = %d, want %d", i, j, sm.raftLog.committed, tt.wcommitted)
+ }
+
+ ents := []pb.Entry{}
+ for _, e := range nextEnts(sm, tt.network.storage[j]) {
+ if e.Data != nil {
+ ents = append(ents, e)
+ }
+ }
+ props := []pb.Message{}
+ for _, m := range tt.msgs {
+ if m.Type == pb.MsgProp {
+ props = append(props, m)
+ }
+ }
+ for k, m := range props {
+ if !bytes.Equal(ents[k].Data, m.Entries[0].Data) {
+ t.Errorf("#%d.%d: data = %d, want %d", i, j, ents[k].Data, m.Entries[0].Data)
+ }
+ }
+ }
+ }
+}
+
+func TestSingleNodeCommit(t *testing.T) {
+ tt := newNetwork(nil)
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+
+ sm := tt.peers[1].(*raft)
+ if sm.raftLog.committed != 3 {
+ t.Errorf("committed = %d, want %d", sm.raftLog.committed, 3)
+ }
+}
+
+// TestCannotCommitWithoutNewTermEntry tests the entries cannot be committed
+// when leader changes, no new proposal comes in and ChangeTerm proposal is
+// filtered.
+func TestCannotCommitWithoutNewTermEntry(t *testing.T) {
+ tt := newNetwork(nil, nil, nil, nil, nil)
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+
+ // 0 cannot reach 2,3,4
+ tt.cut(1, 3)
+ tt.cut(1, 4)
+ tt.cut(1, 5)
+
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+
+ sm := tt.peers[1].(*raft)
+ if sm.raftLog.committed != 1 {
+ t.Errorf("committed = %d, want %d", sm.raftLog.committed, 1)
+ }
+
+ // network recovery
+ tt.recover()
+ // avoid committing ChangeTerm proposal
+ tt.ignore(pb.MsgApp)
+
+ // elect 2 as the new leader with term 2
+ tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgHup})
+
+ // no log entries from previous term should be committed
+ sm = tt.peers[2].(*raft)
+ if sm.raftLog.committed != 1 {
+ t.Errorf("committed = %d, want %d", sm.raftLog.committed, 1)
+ }
+
+ tt.recover()
+ // send heartbeat; reset wait
+ tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgBeat})
+ // append an entry at current term
+ tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+ // expect the committed to be advanced
+ if sm.raftLog.committed != 5 {
+ t.Errorf("committed = %d, want %d", sm.raftLog.committed, 5)
+ }
+}
+
+// TestCommitWithoutNewTermEntry tests the entries could be committed
+// when leader changes, no new proposal comes in.
+func TestCommitWithoutNewTermEntry(t *testing.T) {
+ tt := newNetwork(nil, nil, nil, nil, nil)
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+
+ // 0 cannot reach 2,3,4
+ tt.cut(1, 3)
+ tt.cut(1, 4)
+ tt.cut(1, 5)
+
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("some data")}}})
+
+ sm := tt.peers[1].(*raft)
+ if sm.raftLog.committed != 1 {
+ t.Errorf("committed = %d, want %d", sm.raftLog.committed, 1)
+ }
+
+ // network recovery
+ tt.recover()
+
+ // elect 1 as the new leader with term 2
+ // after append a ChangeTerm entry from the current term, all entries
+ // should be committed
+ tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgHup})
+
+ if sm.raftLog.committed != 4 {
+ t.Errorf("committed = %d, want %d", sm.raftLog.committed, 4)
+ }
+}
+
+func TestDuelingCandidates(t *testing.T) {
+ a := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ b := newTestRaft(2, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ c := newTestRaft(3, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+
+ nt := newNetwork(a, b, c)
+ nt.cut(1, 3)
+
+ nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ nt.send(pb.Message{From: 3, To: 3, Type: pb.MsgHup})
+
+ nt.recover()
+ nt.send(pb.Message{From: 3, To: 3, Type: pb.MsgHup})
+
+ wlog := &raftLog{
+ storage: &MemoryStorage{ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}}},
+ committed: 1,
+ unstable: unstable{offset: 2},
+ }
+ tests := []struct {
+ sm *raft
+ state StateType
+ term uint64
+ raftLog *raftLog
+ }{
+ {a, StateFollower, 2, wlog},
+ {b, StateFollower, 2, wlog},
+ {c, StateFollower, 2, newLog(NewMemoryStorage(), raftLogger)},
+ }
+
+ for i, tt := range tests {
+ if g := tt.sm.state; g != tt.state {
+ t.Errorf("#%d: state = %s, want %s", i, g, tt.state)
+ }
+ if g := tt.sm.Term; g != tt.term {
+ t.Errorf("#%d: term = %d, want %d", i, g, tt.term)
+ }
+ base := ltoa(tt.raftLog)
+ if sm, ok := nt.peers[1+uint64(i)].(*raft); ok {
+ l := ltoa(sm.raftLog)
+ if g := diffu(base, l); g != "" {
+ t.Errorf("#%d: diff:\n%s", i, g)
+ }
+ } else {
+ t.Logf("#%d: empty log", i)
+ }
+ }
+}
+
+func TestCandidateConcede(t *testing.T) {
+ tt := newNetwork(nil, nil, nil)
+ tt.isolate(1)
+
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ tt.send(pb.Message{From: 3, To: 3, Type: pb.MsgHup})
+
+ // heal the partition
+ tt.recover()
+ // send heartbeat; reset wait
+ tt.send(pb.Message{From: 3, To: 3, Type: pb.MsgBeat})
+
+ data := []byte("force follower")
+ // send a proposal to 3 to flush out a MsgApp to 1
+ tt.send(pb.Message{From: 3, To: 3, Type: pb.MsgProp, Entries: []pb.Entry{{Data: data}}})
+ // send heartbeat; flush out commit
+ tt.send(pb.Message{From: 3, To: 3, Type: pb.MsgBeat})
+
+ a := tt.peers[1].(*raft)
+ if g := a.state; g != StateFollower {
+ t.Errorf("state = %s, want %s", g, StateFollower)
+ }
+ if g := a.Term; g != 1 {
+ t.Errorf("term = %d, want %d", g, 1)
+ }
+ wantLog := ltoa(&raftLog{
+ storage: &MemoryStorage{
+ ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Index: 2, Data: data}},
+ },
+ unstable: unstable{offset: 3},
+ committed: 2,
+ })
+ for i, p := range tt.peers {
+ if sm, ok := p.(*raft); ok {
+ l := ltoa(sm.raftLog)
+ if g := diffu(wantLog, l); g != "" {
+ t.Errorf("#%d: diff:\n%s", i, g)
+ }
+ } else {
+ t.Logf("#%d: empty log", i)
+ }
+ }
+}
+
+func TestSingleNodeCandidate(t *testing.T) {
+ tt := newNetwork(nil)
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+
+ sm := tt.peers[1].(*raft)
+ if sm.state != StateLeader {
+ t.Errorf("state = %d, want %d", sm.state, StateLeader)
+ }
+}
+
+func TestOldMessages(t *testing.T) {
+ tt := newNetwork(nil, nil, nil)
+ // make 0 leader @ term 3
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgHup})
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ // pretend we're an old leader trying to make progress; this entry is expected to be ignored.
+ tt.send(pb.Message{From: 2, To: 1, Type: pb.MsgApp, Term: 2, Entries: []pb.Entry{{Index: 3, Term: 2}}})
+ // commit a new entry
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+
+ ilog := &raftLog{
+ storage: &MemoryStorage{
+ ents: []pb.Entry{
+ {}, {Data: nil, Term: 1, Index: 1},
+ {Data: nil, Term: 2, Index: 2}, {Data: nil, Term: 3, Index: 3},
+ {Data: []byte("somedata"), Term: 3, Index: 4},
+ },
+ },
+ unstable: unstable{offset: 5},
+ committed: 4,
+ }
+ base := ltoa(ilog)
+ for i, p := range tt.peers {
+ if sm, ok := p.(*raft); ok {
+ l := ltoa(sm.raftLog)
+ if g := diffu(base, l); g != "" {
+ t.Errorf("#%d: diff:\n%s", i, g)
+ }
+ } else {
+ t.Logf("#%d: empty log", i)
+ }
+ }
+}
+
+// TestOldMessagesReply - optimization - reply with new term.
+
+func TestProposal(t *testing.T) {
+ tests := []struct {
+ *network
+ success bool
+ }{
+ {newNetwork(nil, nil, nil), true},
+ {newNetwork(nil, nil, nopStepper), true},
+ {newNetwork(nil, nopStepper, nopStepper), false},
+ {newNetwork(nil, nopStepper, nopStepper, nil), false},
+ {newNetwork(nil, nopStepper, nopStepper, nil, nil), true},
+ }
+
+ for j, tt := range tests {
+ send := func(m pb.Message) {
+ defer func() {
+ // only recover is we expect it to panic so
+ // panics we don't expect go up.
+ if !tt.success {
+ e := recover()
+ if e != nil {
+ t.Logf("#%d: err: %s", j, e)
+ }
+ }
+ }()
+ tt.send(m)
+ }
+
+ data := []byte("somedata")
+
+ // promote 0 the leader
+ send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+ send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: data}}})
+
+ wantLog := newLog(NewMemoryStorage(), raftLogger)
+ if tt.success {
+ wantLog = &raftLog{
+ storage: &MemoryStorage{
+ ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Index: 2, Data: data}},
+ },
+ unstable: unstable{offset: 3},
+ committed: 2}
+ }
+ base := ltoa(wantLog)
+ for i, p := range tt.peers {
+ if sm, ok := p.(*raft); ok {
+ l := ltoa(sm.raftLog)
+ if g := diffu(base, l); g != "" {
+ t.Errorf("#%d: diff:\n%s", i, g)
+ }
+ } else {
+ t.Logf("#%d: empty log", i)
+ }
+ }
+ sm := tt.network.peers[1].(*raft)
+ if g := sm.Term; g != 1 {
+ t.Errorf("#%d: term = %d, want %d", j, g, 1)
+ }
+ }
+}
+
+func TestProposalByProxy(t *testing.T) {
+ data := []byte("somedata")
+ tests := []*network{
+ newNetwork(nil, nil, nil),
+ newNetwork(nil, nil, nopStepper),
+ }
+
+ for j, tt := range tests {
+ // promote 0 the leader
+ tt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+
+ // propose via follower
+ tt.send(pb.Message{From: 2, To: 2, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+
+ wantLog := &raftLog{
+ storage: &MemoryStorage{
+ ents: []pb.Entry{{}, {Data: nil, Term: 1, Index: 1}, {Term: 1, Data: data, Index: 2}},
+ },
+ unstable: unstable{offset: 3},
+ committed: 2}
+ base := ltoa(wantLog)
+ for i, p := range tt.peers {
+ if sm, ok := p.(*raft); ok {
+ l := ltoa(sm.raftLog)
+ if g := diffu(base, l); g != "" {
+ t.Errorf("#%d: diff:\n%s", i, g)
+ }
+ } else {
+ t.Logf("#%d: empty log", i)
+ }
+ }
+ sm := tt.peers[1].(*raft)
+ if g := sm.Term; g != 1 {
+ t.Errorf("#%d: term = %d, want %d", j, g, 1)
+ }
+ }
+}
+
+func TestCommit(t *testing.T) {
+ tests := []struct {
+ matches []uint64
+ logs []pb.Entry
+ smTerm uint64
+ w uint64
+ }{
+ // single
+ {[]uint64{1}, []pb.Entry{{Index: 1, Term: 1}}, 1, 1},
+ {[]uint64{1}, []pb.Entry{{Index: 1, Term: 1}}, 2, 0},
+ {[]uint64{2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}, 2, 2},
+ {[]uint64{1}, []pb.Entry{{Index: 1, Term: 2}}, 2, 1},
+
+ // odd
+ {[]uint64{2, 1, 1}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}, 1, 1},
+ {[]uint64{2, 1, 1}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 1}}, 2, 0},
+ {[]uint64{2, 1, 2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}, 2, 2},
+ {[]uint64{2, 1, 2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 1}}, 2, 0},
+
+ // even
+ {[]uint64{2, 1, 1, 1}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}, 1, 1},
+ {[]uint64{2, 1, 1, 1}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 1}}, 2, 0},
+ {[]uint64{2, 1, 1, 2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}, 1, 1},
+ {[]uint64{2, 1, 1, 2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 1}}, 2, 0},
+ {[]uint64{2, 1, 2, 2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}}, 2, 2},
+ {[]uint64{2, 1, 2, 2}, []pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 1}}, 2, 0},
+ }
+
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append(tt.logs)
+ storage.hardState = pb.HardState{Term: tt.smTerm}
+
+ sm := newTestRaft(1, []uint64{1}, 5, 1, storage)
+ for j := 0; j < len(tt.matches); j++ {
+ sm.setProgress(uint64(j)+1, tt.matches[j], tt.matches[j]+1)
+ }
+ sm.maybeCommit()
+ if g := sm.raftLog.committed; g != tt.w {
+ t.Errorf("#%d: committed = %d, want %d", i, g, tt.w)
+ }
+ }
+}
+
+func TestIsElectionTimeout(t *testing.T) {
+ tests := []struct {
+ elapse int
+ wprobability float64
+ round bool
+ }{
+ {5, 0, false},
+ {13, 0.3, true},
+ {15, 0.5, true},
+ {18, 0.8, true},
+ {20, 1, false},
+ }
+
+ for i, tt := range tests {
+ sm := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
+ sm.elapsed = tt.elapse
+ c := 0
+ for j := 0; j < 10000; j++ {
+ if sm.isElectionTimeout() {
+ c++
+ }
+ }
+ got := float64(c) / 10000.0
+ if tt.round {
+ got = math.Floor(got*10+0.5) / 10.0
+ }
+ if got != tt.wprobability {
+ t.Errorf("#%d: possibility = %v, want %v", i, got, tt.wprobability)
+ }
+ }
+}
+
+// ensure that the Step function ignores the message from old term and does not pass it to the
+// actual stepX function.
+func TestStepIgnoreOldTermMsg(t *testing.T) {
+ called := false
+ fakeStep := func(r *raft, m pb.Message) {
+ called = true
+ }
+ sm := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
+ sm.step = fakeStep
+ sm.Term = 2
+ sm.Step(pb.Message{Type: pb.MsgApp, Term: sm.Term - 1})
+ if called == true {
+ t.Errorf("stepFunc called = %v , want %v", called, false)
+ }
+}
+
+// TestHandleMsgApp ensures:
+// 1. Reply false if log doesn’t contain an entry at prevLogIndex whose term matches prevLogTerm.
+// 2. If an existing entry conflicts with a new one (same index but different terms),
+// delete the existing entry and all that follow it; append any new entries not already in the log.
+// 3. If leaderCommit > commitIndex, set commitIndex = min(leaderCommit, index of last new entry).
+func TestHandleMsgApp(t *testing.T) {
+ tests := []struct {
+ m pb.Message
+ wIndex uint64
+ wCommit uint64
+ wReject bool
+ }{
+ // Ensure 1
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 3, Index: 2, Commit: 3}, 2, 0, true}, // previous log mismatch
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 3, Index: 3, Commit: 3}, 2, 0, true}, // previous log non-exist
+
+ // Ensure 2
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 1, Index: 1, Commit: 1}, 2, 1, false},
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 0, Index: 0, Commit: 1, Entries: []pb.Entry{{Index: 1, Term: 2}}}, 1, 1, false},
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 2, Index: 2, Commit: 3, Entries: []pb.Entry{{Index: 3, Term: 2}, {Index: 4, Term: 2}}}, 4, 3, false},
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 2, Index: 2, Commit: 4, Entries: []pb.Entry{{Index: 3, Term: 2}}}, 3, 3, false},
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 1, Index: 1, Commit: 4, Entries: []pb.Entry{{Index: 2, Term: 2}}}, 2, 2, false},
+
+ // Ensure 3
+ {pb.Message{Type: pb.MsgApp, Term: 1, LogTerm: 1, Index: 1, Commit: 3}, 2, 1, false}, // match entry 1, commit up to last new entry 1
+ {pb.Message{Type: pb.MsgApp, Term: 1, LogTerm: 1, Index: 1, Commit: 3, Entries: []pb.Entry{{Index: 2, Term: 2}}}, 2, 2, false}, // match entry 1, commit up to last new entry 2
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 2, Index: 2, Commit: 3}, 2, 2, false}, // match entry 2, commit up to last new entry 2
+ {pb.Message{Type: pb.MsgApp, Term: 2, LogTerm: 2, Index: 2, Commit: 4}, 2, 2, false}, // commit up to log.last()
+ }
+
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append([]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}})
+ sm := newTestRaft(1, []uint64{1}, 10, 1, storage)
+ sm.becomeFollower(2, None)
+
+ sm.handleAppendEntries(tt.m)
+ if sm.raftLog.lastIndex() != tt.wIndex {
+ t.Errorf("#%d: lastIndex = %d, want %d", i, sm.raftLog.lastIndex(), tt.wIndex)
+ }
+ if sm.raftLog.committed != tt.wCommit {
+ t.Errorf("#%d: committed = %d, want %d", i, sm.raftLog.committed, tt.wCommit)
+ }
+ m := sm.readMessages()
+ if len(m) != 1 {
+ t.Fatalf("#%d: msg = nil, want 1", i)
+ }
+ if m[0].Reject != tt.wReject {
+ t.Errorf("#%d: reject = %v, want %v", i, m[0].Reject, tt.wReject)
+ }
+ }
+}
+
+// TestHandleHeartbeat ensures that the follower commits to the commit in the message.
+func TestHandleHeartbeat(t *testing.T) {
+ commit := uint64(2)
+ tests := []struct {
+ m pb.Message
+ wCommit uint64
+ }{
+ {pb.Message{From: 2, To: 1, Type: pb.MsgApp, Term: 2, Commit: commit + 1}, commit + 1},
+ {pb.Message{From: 2, To: 1, Type: pb.MsgApp, Term: 2, Commit: commit - 1}, commit}, // do not decrease commit
+ }
+
+ for i, tt := range tests {
+ storage := NewMemoryStorage()
+ storage.Append([]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}})
+ sm := newTestRaft(1, []uint64{1, 2}, 5, 1, storage)
+ sm.becomeFollower(2, 2)
+ sm.raftLog.commitTo(commit)
+ sm.handleHeartbeat(tt.m)
+ if sm.raftLog.committed != tt.wCommit {
+ t.Errorf("#%d: committed = %d, want %d", i, sm.raftLog.committed, tt.wCommit)
+ }
+ m := sm.readMessages()
+ if len(m) != 1 {
+ t.Fatalf("#%d: msg = nil, want 1", i)
+ }
+ if m[0].Type != pb.MsgHeartbeatResp {
+ t.Errorf("#%d: type = %v, want MsgHeartbeatResp", i, m[0].Type)
+ }
+ }
+}
+
+// TestHandleHeartbeatResp ensures that we re-send log entries when we get a heartbeat response.
+func TestHandleHeartbeatResp(t *testing.T) {
+ storage := NewMemoryStorage()
+ storage.Append([]pb.Entry{{Index: 1, Term: 1}, {Index: 2, Term: 2}, {Index: 3, Term: 3}})
+ sm := newTestRaft(1, []uint64{1, 2}, 5, 1, storage)
+ sm.becomeCandidate()
+ sm.becomeLeader()
+ sm.raftLog.commitTo(sm.raftLog.lastIndex())
+
+ // A heartbeat response from a node that is behind; re-send MsgApp
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
+ msgs := sm.readMessages()
+ if len(msgs) != 1 {
+ t.Fatalf("len(msgs) = %d, want 1", len(msgs))
+ }
+ if msgs[0].Type != pb.MsgApp {
+ t.Errorf("type = %v, want MsgApp", msgs[0].Type)
+ }
+
+ // A second heartbeat response with no AppResp does not re-send because we are in the wait state.
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
+ msgs = sm.readMessages()
+ if len(msgs) != 0 {
+ t.Fatalf("len(msgs) = %d, want 0", len(msgs))
+ }
+
+ // Send a heartbeat to reset the wait state; next heartbeat will re-send MsgApp.
+ sm.bcastHeartbeat()
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
+ msgs = sm.readMessages()
+ if len(msgs) != 2 {
+ t.Fatalf("len(msgs) = %d, want 2", len(msgs))
+ }
+ if msgs[0].Type != pb.MsgHeartbeat {
+ t.Errorf("type = %v, want MsgHeartbeat", msgs[0].Type)
+ }
+ if msgs[1].Type != pb.MsgApp {
+ t.Errorf("type = %v, want MsgApp", msgs[1].Type)
+ }
+
+ // Once we have an MsgAppResp, heartbeats no longer send MsgApp.
+ sm.Step(pb.Message{
+ From: 2,
+ Type: pb.MsgAppResp,
+ Index: msgs[1].Index + uint64(len(msgs[1].Entries)),
+ })
+ // Consume the message sent in response to MsgAppResp
+ sm.readMessages()
+
+ sm.bcastHeartbeat() // reset wait state
+ sm.Step(pb.Message{From: 2, Type: pb.MsgHeartbeatResp})
+ msgs = sm.readMessages()
+ if len(msgs) != 1 {
+ t.Fatalf("len(msgs) = %d, want 1: %+v", len(msgs), msgs)
+ }
+ if msgs[0].Type != pb.MsgHeartbeat {
+ t.Errorf("type = %v, want MsgHeartbeat", msgs[0].Type)
+ }
+}
+
+// TestMsgAppRespWaitReset verifies the waitReset behavior of a leader
+// MsgAppResp.
+func TestMsgAppRespWaitReset(t *testing.T) {
+ sm := newTestRaft(1, []uint64{1, 2, 3}, 5, 1, NewMemoryStorage())
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ // The new leader has just emitted a new Term 4 entry; consume those messages
+ // from the outgoing queue.
+ sm.bcastAppend()
+ sm.readMessages()
+
+ // Node 2 acks the first entry, making it committed.
+ sm.Step(pb.Message{
+ From: 2,
+ Type: pb.MsgAppResp,
+ Index: 1,
+ })
+ if sm.Commit != 1 {
+ t.Fatalf("expected Commit to be 1, got %d", sm.Commit)
+ }
+ // Also consume the MsgApp messages that update Commit on the followers.
+ sm.readMessages()
+
+ // A new command is now proposed on node 1.
+ sm.Step(pb.Message{
+ From: 1,
+ Type: pb.MsgProp,
+ Entries: []pb.Entry{{}},
+ })
+
+ // The command is broadcast to all nodes not in the wait state.
+ // Node 2 left the wait state due to its MsgAppResp, but node 3 is still waiting.
+ msgs := sm.readMessages()
+ if len(msgs) != 1 {
+ t.Fatalf("expected 1 message, got %d: %+v", len(msgs), msgs)
+ }
+ if msgs[0].Type != pb.MsgApp || msgs[0].To != 2 {
+ t.Errorf("expected MsgApp to node 2, got %s to %d", msgs[0].Type, msgs[0].To)
+ }
+ if len(msgs[0].Entries) != 1 || msgs[0].Entries[0].Index != 2 {
+ t.Errorf("expected to send entry 2, but got %v", msgs[0].Entries)
+ }
+
+ // Now Node 3 acks the first entry. This releases the wait and entry 2 is sent.
+ sm.Step(pb.Message{
+ From: 3,
+ Type: pb.MsgAppResp,
+ Index: 1,
+ })
+ msgs = sm.readMessages()
+ if len(msgs) != 1 {
+ t.Fatalf("expected 1 message, got %d: %+v", len(msgs), msgs)
+ }
+ if msgs[0].Type != pb.MsgApp || msgs[0].To != 3 {
+ t.Errorf("expected MsgApp to node 3, got %s to %d", msgs[0].Type, msgs[0].To)
+ }
+ if len(msgs[0].Entries) != 1 || msgs[0].Entries[0].Index != 2 {
+ t.Errorf("expected to send entry 2, but got %v", msgs[0].Entries)
+ }
+}
+
+func TestRecvMsgVote(t *testing.T) {
+ tests := []struct {
+ state StateType
+ i, term uint64
+ voteFor uint64
+ wreject bool
+ }{
+ {StateFollower, 0, 0, None, true},
+ {StateFollower, 0, 1, None, true},
+ {StateFollower, 0, 2, None, true},
+ {StateFollower, 0, 3, None, false},
+
+ {StateFollower, 1, 0, None, true},
+ {StateFollower, 1, 1, None, true},
+ {StateFollower, 1, 2, None, true},
+ {StateFollower, 1, 3, None, false},
+
+ {StateFollower, 2, 0, None, true},
+ {StateFollower, 2, 1, None, true},
+ {StateFollower, 2, 2, None, false},
+ {StateFollower, 2, 3, None, false},
+
+ {StateFollower, 3, 0, None, true},
+ {StateFollower, 3, 1, None, true},
+ {StateFollower, 3, 2, None, false},
+ {StateFollower, 3, 3, None, false},
+
+ {StateFollower, 3, 2, 2, false},
+ {StateFollower, 3, 2, 1, true},
+
+ {StateLeader, 3, 3, 1, true},
+ {StateCandidate, 3, 3, 1, true},
+ }
+
+ for i, tt := range tests {
+ sm := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
+ sm.state = tt.state
+ switch tt.state {
+ case StateFollower:
+ sm.step = stepFollower
+ case StateCandidate:
+ sm.step = stepCandidate
+ case StateLeader:
+ sm.step = stepLeader
+ }
+ sm.HardState = pb.HardState{Vote: tt.voteFor}
+ sm.raftLog = &raftLog{
+ storage: &MemoryStorage{ents: []pb.Entry{{}, {Index: 1, Term: 2}, {Index: 2, Term: 2}}},
+ unstable: unstable{offset: 3},
+ }
+
+ sm.Step(pb.Message{Type: pb.MsgVote, From: 2, Index: tt.i, LogTerm: tt.term})
+
+ msgs := sm.readMessages()
+ if g := len(msgs); g != 1 {
+ t.Fatalf("#%d: len(msgs) = %d, want 1", i, g)
+ continue
+ }
+ if g := msgs[0].Reject; g != tt.wreject {
+ t.Errorf("#%d, m.Reject = %v, want %v", i, g, tt.wreject)
+ }
+ }
+}
+
+func TestStateTransition(t *testing.T) {
+ tests := []struct {
+ from StateType
+ to StateType
+ wallow bool
+ wterm uint64
+ wlead uint64
+ }{
+ {StateFollower, StateFollower, true, 1, None},
+ {StateFollower, StateCandidate, true, 1, None},
+ {StateFollower, StateLeader, false, 0, None},
+
+ {StateCandidate, StateFollower, true, 0, None},
+ {StateCandidate, StateCandidate, true, 1, None},
+ {StateCandidate, StateLeader, true, 0, 1},
+
+ {StateLeader, StateFollower, true, 1, None},
+ {StateLeader, StateCandidate, false, 1, None},
+ {StateLeader, StateLeader, true, 0, 1},
+ }
+
+ for i, tt := range tests {
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ if tt.wallow == true {
+ t.Errorf("%d: allow = %v, want %v", i, false, true)
+ }
+ }
+ }()
+
+ sm := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
+ sm.state = tt.from
+
+ switch tt.to {
+ case StateFollower:
+ sm.becomeFollower(tt.wterm, tt.wlead)
+ case StateCandidate:
+ sm.becomeCandidate()
+ case StateLeader:
+ sm.becomeLeader()
+ }
+
+ if sm.Term != tt.wterm {
+ t.Errorf("%d: term = %d, want %d", i, sm.Term, tt.wterm)
+ }
+ if sm.lead != tt.wlead {
+ t.Errorf("%d: lead = %d, want %d", i, sm.lead, tt.wlead)
+ }
+ }()
+ }
+}
+
+func TestAllServerStepdown(t *testing.T) {
+ tests := []struct {
+ state StateType
+
+ wstate StateType
+ wterm uint64
+ windex uint64
+ }{
+ {StateFollower, StateFollower, 3, 0},
+ {StateCandidate, StateFollower, 3, 0},
+ {StateLeader, StateFollower, 3, 1},
+ }
+
+ tmsgTypes := [...]pb.MessageType{pb.MsgVote, pb.MsgApp}
+ tterm := uint64(3)
+
+ for i, tt := range tests {
+ sm := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ switch tt.state {
+ case StateFollower:
+ sm.becomeFollower(1, None)
+ case StateCandidate:
+ sm.becomeCandidate()
+ case StateLeader:
+ sm.becomeCandidate()
+ sm.becomeLeader()
+ }
+
+ for j, msgType := range tmsgTypes {
+ sm.Step(pb.Message{From: 2, Type: msgType, Term: tterm, LogTerm: tterm})
+
+ if sm.state != tt.wstate {
+ t.Errorf("#%d.%d state = %v , want %v", i, j, sm.state, tt.wstate)
+ }
+ if sm.Term != tt.wterm {
+ t.Errorf("#%d.%d term = %v , want %v", i, j, sm.Term, tt.wterm)
+ }
+ if uint64(sm.raftLog.lastIndex()) != tt.windex {
+ t.Errorf("#%d.%d index = %v , want %v", i, j, sm.raftLog.lastIndex(), tt.windex)
+ }
+ if uint64(len(sm.raftLog.allEntries())) != tt.windex {
+ t.Errorf("#%d.%d len(ents) = %v , want %v", i, j, len(sm.raftLog.allEntries()), tt.windex)
+ }
+ wlead := uint64(2)
+ if msgType == pb.MsgVote {
+ wlead = None
+ }
+ if sm.lead != wlead {
+ t.Errorf("#%d, sm.lead = %d, want %d", i, sm.lead, None)
+ }
+ }
+ }
+}
+
+func TestLeaderAppResp(t *testing.T) {
+ // initial progress: match = 0; next = 3
+ tests := []struct {
+ index uint64
+ reject bool
+ // progress
+ wmatch uint64
+ wnext uint64
+ // message
+ wmsgNum int
+ windex uint64
+ wcommitted uint64
+ }{
+ {3, true, 0, 3, 0, 0, 0}, // stale resp; no replies
+ {2, true, 0, 2, 1, 1, 0}, // denied resp; leader does not commit; decrease next and send probing msg
+ {2, false, 2, 4, 2, 2, 2}, // accept resp; leader commits; broadcast with commit index
+ {0, false, 0, 3, 0, 0, 0}, // ignore heartbeat replies
+ }
+
+ for i, tt := range tests {
+ // sm term is 1 after it becomes the leader.
+ // thus the last log term must be 1 to be committed.
+ sm := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ sm.raftLog = &raftLog{
+ storage: &MemoryStorage{ents: []pb.Entry{{}, {Index: 1, Term: 0}, {Index: 2, Term: 1}}},
+ unstable: unstable{offset: 3},
+ }
+ sm.becomeCandidate()
+ sm.becomeLeader()
+ sm.readMessages()
+ sm.Step(pb.Message{From: 2, Type: pb.MsgAppResp, Index: tt.index, Term: sm.Term, Reject: tt.reject, RejectHint: tt.index})
+
+ p := sm.prs[2]
+ if p.Match != tt.wmatch {
+ t.Errorf("#%d match = %d, want %d", i, p.Match, tt.wmatch)
+ }
+ if p.Next != tt.wnext {
+ t.Errorf("#%d next = %d, want %d", i, p.Next, tt.wnext)
+ }
+
+ msgs := sm.readMessages()
+
+ if len(msgs) != tt.wmsgNum {
+ t.Errorf("#%d msgNum = %d, want %d", i, len(msgs), tt.wmsgNum)
+ }
+ for j, msg := range msgs {
+ if msg.Index != tt.windex {
+ t.Errorf("#%d.%d index = %d, want %d", i, j, msg.Index, tt.windex)
+ }
+ if msg.Commit != tt.wcommitted {
+ t.Errorf("#%d.%d commit = %d, want %d", i, j, msg.Commit, tt.wcommitted)
+ }
+ }
+ }
+}
+
+// When the leader receives a heartbeat tick, it should
+// send a MsgApp with m.Index = 0, m.LogTerm=0 and empty entries.
+func TestBcastBeat(t *testing.T) {
+ offset := uint64(1000)
+ // make a state machine with log.offset = 1000
+ s := pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{
+ Index: offset,
+ Term: 1,
+ ConfState: pb.ConfState{Nodes: []uint64{1, 2, 3}},
+ },
+ }
+ storage := NewMemoryStorage()
+ storage.ApplySnapshot(s)
+ sm := newTestRaft(1, nil, 10, 1, storage)
+ sm.Term = 1
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+ for i := 0; i < 10; i++ {
+ sm.appendEntry(pb.Entry{Index: uint64(i) + 1})
+ }
+ // slow follower
+ sm.prs[2].Match, sm.prs[2].Next = 5, 6
+ // normal follower
+ sm.prs[3].Match, sm.prs[3].Next = sm.raftLog.lastIndex(), sm.raftLog.lastIndex()+1
+
+ sm.Step(pb.Message{Type: pb.MsgBeat})
+ msgs := sm.readMessages()
+ if len(msgs) != 2 {
+ t.Fatalf("len(msgs) = %v, want 2", len(msgs))
+ }
+ wantCommitMap := map[uint64]uint64{
+ 2: min(sm.raftLog.committed, sm.prs[2].Match),
+ 3: min(sm.raftLog.committed, sm.prs[3].Match),
+ }
+ for i, m := range msgs {
+ if m.Type != pb.MsgHeartbeat {
+ t.Fatalf("#%d: type = %v, want = %v", i, m.Type, pb.MsgHeartbeat)
+ }
+ if m.Index != 0 {
+ t.Fatalf("#%d: prevIndex = %d, want %d", i, m.Index, 0)
+ }
+ if m.LogTerm != 0 {
+ t.Fatalf("#%d: prevTerm = %d, want %d", i, m.LogTerm, 0)
+ }
+ if wantCommitMap[m.To] == 0 {
+ t.Fatalf("#%d: unexpected to %d", i, m.To)
+ } else {
+ if m.Commit != wantCommitMap[m.To] {
+ t.Fatalf("#%d: commit = %d, want %d", i, m.Commit, wantCommitMap[m.To])
+ }
+ delete(wantCommitMap, m.To)
+ }
+ if len(m.Entries) != 0 {
+ t.Fatalf("#%d: len(entries) = %d, want 0", i, len(m.Entries))
+ }
+ }
+}
+
+// tests the output of the statemachine when receiving MsgBeat
+func TestRecvMsgBeat(t *testing.T) {
+ tests := []struct {
+ state StateType
+ wMsg int
+ }{
+ {StateLeader, 2},
+ // candidate and follower should ignore MsgBeat
+ {StateCandidate, 0},
+ {StateFollower, 0},
+ }
+
+ for i, tt := range tests {
+ sm := newTestRaft(1, []uint64{1, 2, 3}, 10, 1, NewMemoryStorage())
+ sm.raftLog = &raftLog{storage: &MemoryStorage{ents: []pb.Entry{{}, {Index: 1, Term: 0}, {Index: 2, Term: 1}}}}
+ sm.Term = 1
+ sm.state = tt.state
+ switch tt.state {
+ case StateFollower:
+ sm.step = stepFollower
+ case StateCandidate:
+ sm.step = stepCandidate
+ case StateLeader:
+ sm.step = stepLeader
+ }
+ sm.Step(pb.Message{From: 1, To: 1, Type: pb.MsgBeat})
+
+ msgs := sm.readMessages()
+ if len(msgs) != tt.wMsg {
+ t.Errorf("%d: len(msgs) = %d, want %d", i, len(msgs), tt.wMsg)
+ }
+ for _, m := range msgs {
+ if m.Type != pb.MsgHeartbeat {
+ t.Errorf("%d: msg.type = %v, want %v", i, m.Type, pb.MsgHeartbeat)
+ }
+ }
+ }
+}
+
+func TestLeaderIncreaseNext(t *testing.T) {
+ previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3}}
+ tests := []struct {
+ // progress
+ state ProgressStateType
+ next uint64
+
+ wnext uint64
+ }{
+ // state replicate, optimistically increase next
+ // previous entries + noop entry + propose + 1
+ {ProgressStateReplicate, 2, uint64(len(previousEnts) + 1 + 1 + 1)},
+ // state probe, not optimistically increase next
+ {ProgressStateProbe, 2, 2},
+ }
+
+ for i, tt := range tests {
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ sm.raftLog.append(previousEnts...)
+ sm.becomeCandidate()
+ sm.becomeLeader()
+ sm.prs[2].State = tt.state
+ sm.prs[2].Next = tt.next
+ sm.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Data: []byte("somedata")}}})
+
+ p := sm.prs[2]
+ if p.Next != tt.wnext {
+ t.Errorf("#%d next = %d, want %d", i, p.Next, tt.wnext)
+ }
+ }
+}
+
+func TestSendAppendForProgressProbe(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.readMessages()
+ r.prs[2].becomeProbe()
+
+ // each round is a heartbeat
+ for i := 0; i < 3; i++ {
+ // we expect that raft will only send out one msgAPP per heartbeat timeout
+ r.appendEntry(pb.Entry{Data: []byte("somedata")})
+ r.sendAppend(2)
+ msg := r.readMessages()
+ if len(msg) != 1 {
+ t.Errorf("len(msg) = %d, want %d", len(msg), 1)
+ }
+ if msg[0].Index != 0 {
+ t.Errorf("index = %d, want %d", msg[0].Index, 0)
+ }
+
+ if r.prs[2].Paused != true {
+ t.Errorf("paused = %v, want true", r.prs[2].Paused)
+ }
+ for j := 0; j < 10; j++ {
+ r.appendEntry(pb.Entry{Data: []byte("somedata")})
+ r.sendAppend(2)
+ if l := len(r.readMessages()); l != 0 {
+ t.Errorf("len(msg) = %d, want %d", l, 0)
+ }
+ }
+
+ // do a heartbeat
+ for j := 0; j < r.heartbeatTimeout; j++ {
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgBeat})
+ }
+ // consume the heartbeat
+ msg = r.readMessages()
+ if len(msg) != 1 {
+ t.Errorf("len(msg) = %d, want %d", len(msg), 1)
+ }
+ if msg[0].Type != pb.MsgHeartbeat {
+ t.Errorf("type = %s, want %s", msg[0].Type, pb.MsgHeartbeat)
+ }
+ }
+}
+
+func TestSendAppendForProgressReplicate(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.readMessages()
+ r.prs[2].becomeReplicate()
+
+ for i := 0; i < 10; i++ {
+ r.appendEntry(pb.Entry{Data: []byte("somedata")})
+ r.sendAppend(2)
+ msgs := r.readMessages()
+ if len(msgs) != 1 {
+ t.Errorf("len(msg) = %d, want %d", len(msgs), 1)
+ }
+ }
+}
+
+func TestSendAppendForProgressSnapshot(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.readMessages()
+ r.prs[2].becomeSnapshot(10)
+
+ for i := 0; i < 10; i++ {
+ r.appendEntry(pb.Entry{Data: []byte("somedata")})
+ r.sendAppend(2)
+ msgs := r.readMessages()
+ if len(msgs) != 0 {
+ t.Errorf("len(msg) = %d, want %d", len(msgs), 0)
+ }
+ }
+}
+
+func TestRecvMsgUnreachable(t *testing.T) {
+ previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3}}
+ s := NewMemoryStorage()
+ s.Append(previousEnts)
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, s)
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.readMessages()
+ // set node 2 to state replicate
+ r.prs[2].Match = 3
+ r.prs[2].becomeReplicate()
+ r.prs[2].optimisticUpdate(5)
+
+ r.Step(pb.Message{From: 2, To: 1, Type: pb.MsgUnreachable})
+
+ if r.prs[2].State != ProgressStateProbe {
+ t.Errorf("state = %s, want %s", r.prs[2].State, ProgressStateProbe)
+ }
+ if wnext := r.prs[2].Match + 1; r.prs[2].Next != wnext {
+ t.Errorf("next = %d, want %d", r.prs[2].Next, wnext)
+ }
+}
+
+func TestRestore(t *testing.T) {
+ s := pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{
+ Index: 11, // magic number
+ Term: 11, // magic number
+ ConfState: pb.ConfState{Nodes: []uint64{1, 2, 3}},
+ },
+ }
+
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ if ok := sm.restore(s); !ok {
+ t.Fatal("restore fail, want succeed")
+ }
+
+ if sm.raftLog.lastIndex() != s.Metadata.Index {
+ t.Errorf("log.lastIndex = %d, want %d", sm.raftLog.lastIndex(), s.Metadata.Index)
+ }
+ if mustTerm(sm.raftLog.term(s.Metadata.Index)) != s.Metadata.Term {
+ t.Errorf("log.lastTerm = %d, want %d", mustTerm(sm.raftLog.term(s.Metadata.Index)), s.Metadata.Term)
+ }
+ sg := sm.nodes()
+ if !reflect.DeepEqual(sg, s.Metadata.ConfState.Nodes) {
+ t.Errorf("sm.Nodes = %+v, want %+v", sg, s.Metadata.ConfState.Nodes)
+ }
+
+ if ok := sm.restore(s); ok {
+ t.Fatal("restore succeed, want fail")
+ }
+}
+
+func TestRestoreIgnoreSnapshot(t *testing.T) {
+ previousEnts := []pb.Entry{{Term: 1, Index: 1}, {Term: 1, Index: 2}, {Term: 1, Index: 3}}
+ commit := uint64(1)
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1, 2}, 10, 1, storage)
+ sm.raftLog.append(previousEnts...)
+ sm.raftLog.commitTo(commit)
+
+ s := pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{
+ Index: commit,
+ Term: 1,
+ ConfState: pb.ConfState{Nodes: []uint64{1, 2}},
+ },
+ }
+
+ // ignore snapshot
+ if ok := sm.restore(s); ok {
+ t.Errorf("restore = %t, want %t", ok, false)
+ }
+ if sm.raftLog.committed != commit {
+ t.Errorf("commit = %d, want %d", sm.raftLog.committed, commit)
+ }
+
+ // ignore snapshot and fast forward commit
+ s.Metadata.Index = commit + 1
+ if ok := sm.restore(s); ok {
+ t.Errorf("restore = %t, want %t", ok, false)
+ }
+ if sm.raftLog.committed != commit+1 {
+ t.Errorf("commit = %d, want %d", sm.raftLog.committed, commit+1)
+ }
+}
+
+func TestProvideSnap(t *testing.T) {
+ // restore the statemachin from a snapshot
+ // so it has a compacted log and a snapshot
+ s := pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{
+ Index: 11, // magic number
+ Term: 11, // magic number
+ ConfState: pb.ConfState{Nodes: []uint64{1, 2}},
+ },
+ }
+ storage := NewMemoryStorage()
+ sm := newTestRaft(1, []uint64{1}, 10, 1, storage)
+ sm.restore(s)
+
+ sm.becomeCandidate()
+ sm.becomeLeader()
+
+ // force set the next of node 1, so that
+ // node 1 needs a snapshot
+ sm.prs[2].Next = sm.raftLog.firstIndex()
+
+ sm.Step(pb.Message{From: 2, To: 1, Type: pb.MsgAppResp, Index: sm.prs[2].Next - 1, Reject: true})
+ msgs := sm.readMessages()
+ if len(msgs) != 1 {
+ t.Fatalf("len(msgs) = %d, want 1", len(msgs))
+ }
+ m := msgs[0]
+ if m.Type != pb.MsgSnap {
+ t.Errorf("m.Type = %v, want %v", m.Type, pb.MsgSnap)
+ }
+}
+
+func TestRestoreFromSnapMsg(t *testing.T) {
+ s := pb.Snapshot{
+ Metadata: pb.SnapshotMetadata{
+ Index: 11, // magic number
+ Term: 11, // magic number
+ ConfState: pb.ConfState{Nodes: []uint64{1, 2}},
+ },
+ }
+ m := pb.Message{Type: pb.MsgSnap, From: 1, Term: 2, Snapshot: s}
+
+ sm := newTestRaft(2, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ sm.Step(m)
+
+ // TODO(bdarnell): what should this test?
+}
+
+func TestSlowNodeRestore(t *testing.T) {
+ nt := newNetwork(nil, nil, nil)
+ nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgHup})
+
+ nt.isolate(3)
+ for j := 0; j <= 100; j++ {
+ nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{}}})
+ }
+ lead := nt.peers[1].(*raft)
+ nextEnts(lead, nt.storage[1])
+ nt.storage[1].CreateSnapshot(lead.raftLog.applied, &pb.ConfState{Nodes: lead.nodes()}, nil)
+ nt.storage[1].Compact(lead.raftLog.applied)
+
+ nt.recover()
+ // trigger a snapshot
+ nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{}}})
+ follower := nt.peers[3].(*raft)
+
+ // trigger a commit
+ nt.send(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{}}})
+ if follower.raftLog.committed != lead.raftLog.committed {
+ t.Errorf("follower.committed = %d, want %d", follower.raftLog.committed, lead.raftLog.committed)
+ }
+}
+
+// TestStepConfig tests that when raft step msgProp in EntryConfChange type,
+// it appends the entry to log and sets pendingConf to be true.
+func TestStepConfig(t *testing.T) {
+ // a raft that cannot make progress
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ index := r.raftLog.lastIndex()
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange}}})
+ if g := r.raftLog.lastIndex(); g != index+1 {
+ t.Errorf("index = %d, want %d", g, index+1)
+ }
+ if r.pendingConf != true {
+ t.Errorf("pendingConf = %v, want true", r.pendingConf)
+ }
+}
+
+// TestStepIgnoreConfig tests that if raft step the second msgProp in
+// EntryConfChange type when the first one is uncommitted, the node will set
+// the proposal to noop and keep its original state.
+func TestStepIgnoreConfig(t *testing.T) {
+ // a raft that cannot make progress
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.becomeCandidate()
+ r.becomeLeader()
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange}}})
+ index := r.raftLog.lastIndex()
+ pendingConf := r.pendingConf
+ r.Step(pb.Message{From: 1, To: 1, Type: pb.MsgProp, Entries: []pb.Entry{{Type: pb.EntryConfChange}}})
+ wents := []pb.Entry{{Type: pb.EntryNormal, Term: 1, Index: 3, Data: nil}}
+ ents, err := r.raftLog.entries(index+1, noLimit)
+ if err != nil {
+ t.Fatalf("unexpected error %v", err)
+ }
+ if !reflect.DeepEqual(ents, wents) {
+ t.Errorf("ents = %+v, want %+v", ents, wents)
+ }
+ if r.pendingConf != pendingConf {
+ t.Errorf("pendingConf = %v, want %v", r.pendingConf, pendingConf)
+ }
+}
+
+// TestRecoverPendingConfig tests that new leader recovers its pendingConf flag
+// based on uncommitted entries.
+func TestRecoverPendingConfig(t *testing.T) {
+ tests := []struct {
+ entType pb.EntryType
+ wpending bool
+ }{
+ {pb.EntryNormal, false},
+ {pb.EntryConfChange, true},
+ }
+ for i, tt := range tests {
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.appendEntry(pb.Entry{Type: tt.entType})
+ r.becomeCandidate()
+ r.becomeLeader()
+ if r.pendingConf != tt.wpending {
+ t.Errorf("#%d: pendingConf = %v, want %v", i, r.pendingConf, tt.wpending)
+ }
+ }
+}
+
+// TestRecoverDoublePendingConfig tests that new leader will panic if
+// there exist two uncommitted config entries.
+func TestRecoverDoublePendingConfig(t *testing.T) {
+ func() {
+ defer func() {
+ if err := recover(); err == nil {
+ t.Errorf("expect panic, but nothing happens")
+ }
+ }()
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.appendEntry(pb.Entry{Type: pb.EntryConfChange})
+ r.appendEntry(pb.Entry{Type: pb.EntryConfChange})
+ r.becomeCandidate()
+ r.becomeLeader()
+ }()
+}
+
+// TestAddNode tests that addNode could update pendingConf and nodes correctly.
+func TestAddNode(t *testing.T) {
+ r := newTestRaft(1, []uint64{1}, 10, 1, NewMemoryStorage())
+ r.pendingConf = true
+ r.addNode(2)
+ if r.pendingConf != false {
+ t.Errorf("pendingConf = %v, want false", r.pendingConf)
+ }
+ nodes := r.nodes()
+ wnodes := []uint64{1, 2}
+ if !reflect.DeepEqual(nodes, wnodes) {
+ t.Errorf("nodes = %v, want %v", nodes, wnodes)
+ }
+}
+
+// TestRemoveNode tests that removeNode could update pendingConf, nodes and
+// and removed list correctly.
+func TestRemoveNode(t *testing.T) {
+ r := newTestRaft(1, []uint64{1, 2}, 10, 1, NewMemoryStorage())
+ r.pendingConf = true
+ r.removeNode(2)
+ if r.pendingConf != false {
+ t.Errorf("pendingConf = %v, want false", r.pendingConf)
+ }
+ w := []uint64{1}
+ if g := r.nodes(); !reflect.DeepEqual(g, w) {
+ t.Errorf("nodes = %v, want %v", g, w)
+ }
+}
+
+func TestPromotable(t *testing.T) {
+ id := uint64(1)
+ tests := []struct {
+ peers []uint64
+ wp bool
+ }{
+ {[]uint64{1}, true},
+ {[]uint64{1, 2, 3}, true},
+ {[]uint64{}, false},
+ {[]uint64{2, 3}, false},
+ }
+ for i, tt := range tests {
+ r := newTestRaft(id, tt.peers, 5, 1, NewMemoryStorage())
+ if g := r.promotable(); g != tt.wp {
+ t.Errorf("#%d: promotable = %v, want %v", i, g, tt.wp)
+ }
+ }
+}
+
+func TestRaftNodes(t *testing.T) {
+ tests := []struct {
+ ids []uint64
+ wids []uint64
+ }{
+ {
+ []uint64{1, 2, 3},
+ []uint64{1, 2, 3},
+ },
+ {
+ []uint64{3, 2, 1},
+ []uint64{1, 2, 3},
+ },
+ }
+ for i, tt := range tests {
+ r := newTestRaft(1, tt.ids, 10, 1, NewMemoryStorage())
+ if !reflect.DeepEqual(r.nodes(), tt.wids) {
+ t.Errorf("#%d: nodes = %+v, want %+v", i, r.nodes(), tt.wids)
+ }
+ }
+}
+
+func ents(terms ...uint64) *raft {
+ storage := NewMemoryStorage()
+ for i, term := range terms {
+ storage.Append([]pb.Entry{{Index: uint64(i + 1), Term: term}})
+ }
+ sm := newTestRaft(1, []uint64{}, 5, 1, storage)
+ sm.reset(0)
+ return sm
+}
+
+type network struct {
+ peers map[uint64]Interface
+ storage map[uint64]*MemoryStorage
+ dropm map[connem]float64
+ ignorem map[pb.MessageType]bool
+}
+
+// newNetwork initializes a network from peers.
+// A nil node will be replaced with a new *stateMachine.
+// A *stateMachine will get its k, id.
+// When using stateMachine, the address list is always [1, n].
+func newNetwork(peers ...Interface) *network {
+ size := len(peers)
+ peerAddrs := idsBySize(size)
+
+ npeers := make(map[uint64]Interface, size)
+ nstorage := make(map[uint64]*MemoryStorage, size)
+
+ for j, p := range peers {
+ id := peerAddrs[j]
+ switch v := p.(type) {
+ case nil:
+ nstorage[id] = NewMemoryStorage()
+ sm := newTestRaft(id, peerAddrs, 10, 1, nstorage[id])
+ npeers[id] = sm
+ case *raft:
+ v.id = id
+ v.prs = make(map[uint64]*Progress)
+ for i := 0; i < size; i++ {
+ v.prs[peerAddrs[i]] = &Progress{}
+ }
+ v.reset(0)
+ npeers[id] = v
+ case *blackHole:
+ npeers[id] = v
+ default:
+ panic(fmt.Sprintf("unexpected state machine type: %T", p))
+ }
+ }
+ return &network{
+ peers: npeers,
+ storage: nstorage,
+ dropm: make(map[connem]float64),
+ ignorem: make(map[pb.MessageType]bool),
+ }
+}
+
+func (nw *network) send(msgs ...pb.Message) {
+ for len(msgs) > 0 {
+ m := msgs[0]
+ p := nw.peers[m.To]
+ p.Step(m)
+ msgs = append(msgs[1:], nw.filter(p.readMessages())...)
+ }
+}
+
+func (nw *network) drop(from, to uint64, perc float64) {
+ nw.dropm[connem{from, to}] = perc
+}
+
+func (nw *network) cut(one, other uint64) {
+ nw.drop(one, other, 1)
+ nw.drop(other, one, 1)
+}
+
+func (nw *network) isolate(id uint64) {
+ for i := 0; i < len(nw.peers); i++ {
+ nid := uint64(i) + 1
+ if nid != id {
+ nw.drop(id, nid, 1.0)
+ nw.drop(nid, id, 1.0)
+ }
+ }
+}
+
+func (nw *network) ignore(t pb.MessageType) {
+ nw.ignorem[t] = true
+}
+
+func (nw *network) recover() {
+ nw.dropm = make(map[connem]float64)
+ nw.ignorem = make(map[pb.MessageType]bool)
+}
+
+func (nw *network) filter(msgs []pb.Message) []pb.Message {
+ mm := []pb.Message{}
+ for _, m := range msgs {
+ if nw.ignorem[m.Type] {
+ continue
+ }
+ switch m.Type {
+ case pb.MsgHup:
+ // hups never go over the network, so don't drop them but panic
+ panic("unexpected msgHup")
+ default:
+ perc := nw.dropm[connem{m.From, m.To}]
+ if n := rand.Float64(); n < perc {
+ continue
+ }
+ }
+ mm = append(mm, m)
+ }
+ return mm
+}
+
+type connem struct {
+ from, to uint64
+}
+
+type blackHole struct{}
+
+func (blackHole) Step(pb.Message) error { return nil }
+func (blackHole) readMessages() []pb.Message { return nil }
+
+var nopStepper = &blackHole{}
+
+func idsBySize(size int) []uint64 {
+ ids := make([]uint64, size)
+ for i := 0; i < size; i++ {
+ ids[i] = 1 + uint64(i)
+ }
+ return ids
+}
+
+func newTestConfig(id uint64, peers []uint64, election, heartbeat int, storage Storage) *Config {
+ return &Config{
+ ID: id,
+ peers: peers,
+ ElectionTick: election,
+ HeartbeatTick: heartbeat,
+ Storage: storage,
+ MaxSizePerMsg: noLimit,
+ MaxInflightMsgs: 256,
+ }
+}
+
+func newTestRaft(id uint64, peers []uint64, election, heartbeat int, storage Storage) *raft {
+ return newRaft(newTestConfig(id, peers, election, heartbeat, storage))
+}
diff --git a/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go b/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go
new file mode 100644
index 000000000..e302ffada
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raftpb/raft.pb.go
@@ -0,0 +1,1614 @@
+// Code generated by protoc-gen-gogo.
+// source: raft.proto
+// DO NOT EDIT!
+
+/*
+ Package raftpb is a generated protocol buffer package.
+
+ It is generated from these files:
+ raft.proto
+
+ It has these top-level messages:
+ Entry
+ SnapshotMetadata
+ Snapshot
+ Message
+ HardState
+ ConfState
+ ConfChange
+*/
+package raftpb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+import math "math"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type EntryType int32
+
+const (
+ EntryNormal EntryType = 0
+ EntryConfChange EntryType = 1
+)
+
+var EntryType_name = map[int32]string{
+ 0: "EntryNormal",
+ 1: "EntryConfChange",
+}
+var EntryType_value = map[string]int32{
+ "EntryNormal": 0,
+ "EntryConfChange": 1,
+}
+
+func (x EntryType) Enum() *EntryType {
+ p := new(EntryType)
+ *p = x
+ return p
+}
+func (x EntryType) String() string {
+ return proto.EnumName(EntryType_name, int32(x))
+}
+func (x *EntryType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(EntryType_value, data, "EntryType")
+ if err != nil {
+ return err
+ }
+ *x = EntryType(value)
+ return nil
+}
+
+type MessageType int32
+
+const (
+ MsgHup MessageType = 0
+ MsgBeat MessageType = 1
+ MsgProp MessageType = 2
+ MsgApp MessageType = 3
+ MsgAppResp MessageType = 4
+ MsgVote MessageType = 5
+ MsgVoteResp MessageType = 6
+ MsgSnap MessageType = 7
+ MsgHeartbeat MessageType = 8
+ MsgHeartbeatResp MessageType = 9
+ MsgUnreachable MessageType = 10
+ MsgSnapStatus MessageType = 11
+)
+
+var MessageType_name = map[int32]string{
+ 0: "MsgHup",
+ 1: "MsgBeat",
+ 2: "MsgProp",
+ 3: "MsgApp",
+ 4: "MsgAppResp",
+ 5: "MsgVote",
+ 6: "MsgVoteResp",
+ 7: "MsgSnap",
+ 8: "MsgHeartbeat",
+ 9: "MsgHeartbeatResp",
+ 10: "MsgUnreachable",
+ 11: "MsgSnapStatus",
+}
+var MessageType_value = map[string]int32{
+ "MsgHup": 0,
+ "MsgBeat": 1,
+ "MsgProp": 2,
+ "MsgApp": 3,
+ "MsgAppResp": 4,
+ "MsgVote": 5,
+ "MsgVoteResp": 6,
+ "MsgSnap": 7,
+ "MsgHeartbeat": 8,
+ "MsgHeartbeatResp": 9,
+ "MsgUnreachable": 10,
+ "MsgSnapStatus": 11,
+}
+
+func (x MessageType) Enum() *MessageType {
+ p := new(MessageType)
+ *p = x
+ return p
+}
+func (x MessageType) String() string {
+ return proto.EnumName(MessageType_name, int32(x))
+}
+func (x *MessageType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(MessageType_value, data, "MessageType")
+ if err != nil {
+ return err
+ }
+ *x = MessageType(value)
+ return nil
+}
+
+type ConfChangeType int32
+
+const (
+ ConfChangeAddNode ConfChangeType = 0
+ ConfChangeRemoveNode ConfChangeType = 1
+ ConfChangeUpdateNode ConfChangeType = 2
+)
+
+var ConfChangeType_name = map[int32]string{
+ 0: "ConfChangeAddNode",
+ 1: "ConfChangeRemoveNode",
+ 2: "ConfChangeUpdateNode",
+}
+var ConfChangeType_value = map[string]int32{
+ "ConfChangeAddNode": 0,
+ "ConfChangeRemoveNode": 1,
+ "ConfChangeUpdateNode": 2,
+}
+
+func (x ConfChangeType) Enum() *ConfChangeType {
+ p := new(ConfChangeType)
+ *p = x
+ return p
+}
+func (x ConfChangeType) String() string {
+ return proto.EnumName(ConfChangeType_name, int32(x))
+}
+func (x *ConfChangeType) UnmarshalJSON(data []byte) error {
+ value, err := proto.UnmarshalJSONEnum(ConfChangeType_value, data, "ConfChangeType")
+ if err != nil {
+ return err
+ }
+ *x = ConfChangeType(value)
+ return nil
+}
+
+type Entry struct {
+ Type EntryType `protobuf:"varint,1,opt,enum=raftpb.EntryType" json:"Type"`
+ Term uint64 `protobuf:"varint,2,opt" json:"Term"`
+ Index uint64 `protobuf:"varint,3,opt" json:"Index"`
+ Data []byte `protobuf:"bytes,4,opt" json:"Data,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Entry) Reset() { *m = Entry{} }
+func (m *Entry) String() string { return proto.CompactTextString(m) }
+func (*Entry) ProtoMessage() {}
+
+type SnapshotMetadata struct {
+ ConfState ConfState `protobuf:"bytes,1,opt,name=conf_state" json:"conf_state"`
+ Index uint64 `protobuf:"varint,2,opt,name=index" json:"index"`
+ Term uint64 `protobuf:"varint,3,opt,name=term" json:"term"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *SnapshotMetadata) Reset() { *m = SnapshotMetadata{} }
+func (m *SnapshotMetadata) String() string { return proto.CompactTextString(m) }
+func (*SnapshotMetadata) ProtoMessage() {}
+
+type Snapshot struct {
+ Data []byte `protobuf:"bytes,1,opt,name=data" json:"data,omitempty"`
+ Metadata SnapshotMetadata `protobuf:"bytes,2,opt,name=metadata" json:"metadata"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Snapshot) Reset() { *m = Snapshot{} }
+func (m *Snapshot) String() string { return proto.CompactTextString(m) }
+func (*Snapshot) ProtoMessage() {}
+
+type Message struct {
+ Type MessageType `protobuf:"varint,1,opt,name=type,enum=raftpb.MessageType" json:"type"`
+ To uint64 `protobuf:"varint,2,opt,name=to" json:"to"`
+ From uint64 `protobuf:"varint,3,opt,name=from" json:"from"`
+ Term uint64 `protobuf:"varint,4,opt,name=term" json:"term"`
+ LogTerm uint64 `protobuf:"varint,5,opt,name=logTerm" json:"logTerm"`
+ Index uint64 `protobuf:"varint,6,opt,name=index" json:"index"`
+ Entries []Entry `protobuf:"bytes,7,rep,name=entries" json:"entries"`
+ Commit uint64 `protobuf:"varint,8,opt,name=commit" json:"commit"`
+ Snapshot Snapshot `protobuf:"bytes,9,opt,name=snapshot" json:"snapshot"`
+ Reject bool `protobuf:"varint,10,opt,name=reject" json:"reject"`
+ RejectHint uint64 `protobuf:"varint,11,opt,name=rejectHint" json:"rejectHint"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Message) Reset() { *m = Message{} }
+func (m *Message) String() string { return proto.CompactTextString(m) }
+func (*Message) ProtoMessage() {}
+
+type HardState struct {
+ Term uint64 `protobuf:"varint,1,opt,name=term" json:"term"`
+ Vote uint64 `protobuf:"varint,2,opt,name=vote" json:"vote"`
+ Commit uint64 `protobuf:"varint,3,opt,name=commit" json:"commit"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *HardState) Reset() { *m = HardState{} }
+func (m *HardState) String() string { return proto.CompactTextString(m) }
+func (*HardState) ProtoMessage() {}
+
+type ConfState struct {
+ Nodes []uint64 `protobuf:"varint,1,rep,name=nodes" json:"nodes,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ConfState) Reset() { *m = ConfState{} }
+func (m *ConfState) String() string { return proto.CompactTextString(m) }
+func (*ConfState) ProtoMessage() {}
+
+type ConfChange struct {
+ ID uint64 `protobuf:"varint,1,opt" json:"ID"`
+ Type ConfChangeType `protobuf:"varint,2,opt,enum=raftpb.ConfChangeType" json:"Type"`
+ NodeID uint64 `protobuf:"varint,3,opt" json:"NodeID"`
+ Context []byte `protobuf:"bytes,4,opt" json:"Context,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *ConfChange) Reset() { *m = ConfChange{} }
+func (m *ConfChange) String() string { return proto.CompactTextString(m) }
+func (*ConfChange) ProtoMessage() {}
+
+func init() {
+ proto.RegisterEnum("raftpb.EntryType", EntryType_name, EntryType_value)
+ proto.RegisterEnum("raftpb.MessageType", MessageType_name, MessageType_value)
+ proto.RegisterEnum("raftpb.ConfChangeType", ConfChangeType_name, ConfChangeType_value)
+}
+func (m *Entry) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Entry) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Type))
+ data[i] = 0x10
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Term))
+ data[i] = 0x18
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Index))
+ if m.Data != nil {
+ data[i] = 0x22
+ i++
+ i = encodeVarintRaft(data, i, uint64(len(m.Data)))
+ i += copy(data[i:], m.Data)
+ }
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *SnapshotMetadata) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *SnapshotMetadata) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0xa
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.ConfState.Size()))
+ n1, err := m.ConfState.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n1
+ data[i] = 0x10
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Index))
+ data[i] = 0x18
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Term))
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *Snapshot) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Snapshot) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Data != nil {
+ data[i] = 0xa
+ i++
+ i = encodeVarintRaft(data, i, uint64(len(m.Data)))
+ i += copy(data[i:], m.Data)
+ }
+ data[i] = 0x12
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Metadata.Size()))
+ n2, err := m.Metadata.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n2
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *Message) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Message) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Type))
+ data[i] = 0x10
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.To))
+ data[i] = 0x18
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.From))
+ data[i] = 0x20
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Term))
+ data[i] = 0x28
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.LogTerm))
+ data[i] = 0x30
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Index))
+ if len(m.Entries) > 0 {
+ for _, msg := range m.Entries {
+ data[i] = 0x3a
+ i++
+ i = encodeVarintRaft(data, i, uint64(msg.Size()))
+ n, err := msg.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n
+ }
+ }
+ data[i] = 0x40
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Commit))
+ data[i] = 0x4a
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Snapshot.Size()))
+ n3, err := m.Snapshot.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n3
+ data[i] = 0x50
+ i++
+ if m.Reject {
+ data[i] = 1
+ } else {
+ data[i] = 0
+ }
+ i++
+ data[i] = 0x58
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.RejectHint))
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *HardState) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *HardState) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Term))
+ data[i] = 0x10
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Vote))
+ data[i] = 0x18
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Commit))
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *ConfState) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *ConfState) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if len(m.Nodes) > 0 {
+ for _, num := range m.Nodes {
+ data[i] = 0x8
+ i++
+ i = encodeVarintRaft(data, i, uint64(num))
+ }
+ }
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *ConfChange) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *ConfChange) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.ID))
+ data[i] = 0x10
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.Type))
+ data[i] = 0x18
+ i++
+ i = encodeVarintRaft(data, i, uint64(m.NodeID))
+ if m.Context != nil {
+ data[i] = 0x22
+ i++
+ i = encodeVarintRaft(data, i, uint64(len(m.Context)))
+ i += copy(data[i:], m.Context)
+ }
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func encodeFixed64Raft(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Raft(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintRaft(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *Entry) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovRaft(uint64(m.Type))
+ n += 1 + sovRaft(uint64(m.Term))
+ n += 1 + sovRaft(uint64(m.Index))
+ if m.Data != nil {
+ l = len(m.Data)
+ n += 1 + l + sovRaft(uint64(l))
+ }
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *SnapshotMetadata) Size() (n int) {
+ var l int
+ _ = l
+ l = m.ConfState.Size()
+ n += 1 + l + sovRaft(uint64(l))
+ n += 1 + sovRaft(uint64(m.Index))
+ n += 1 + sovRaft(uint64(m.Term))
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *Snapshot) Size() (n int) {
+ var l int
+ _ = l
+ if m.Data != nil {
+ l = len(m.Data)
+ n += 1 + l + sovRaft(uint64(l))
+ }
+ l = m.Metadata.Size()
+ n += 1 + l + sovRaft(uint64(l))
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *Message) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovRaft(uint64(m.Type))
+ n += 1 + sovRaft(uint64(m.To))
+ n += 1 + sovRaft(uint64(m.From))
+ n += 1 + sovRaft(uint64(m.Term))
+ n += 1 + sovRaft(uint64(m.LogTerm))
+ n += 1 + sovRaft(uint64(m.Index))
+ if len(m.Entries) > 0 {
+ for _, e := range m.Entries {
+ l = e.Size()
+ n += 1 + l + sovRaft(uint64(l))
+ }
+ }
+ n += 1 + sovRaft(uint64(m.Commit))
+ l = m.Snapshot.Size()
+ n += 1 + l + sovRaft(uint64(l))
+ n += 2
+ n += 1 + sovRaft(uint64(m.RejectHint))
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *HardState) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovRaft(uint64(m.Term))
+ n += 1 + sovRaft(uint64(m.Vote))
+ n += 1 + sovRaft(uint64(m.Commit))
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *ConfState) Size() (n int) {
+ var l int
+ _ = l
+ if len(m.Nodes) > 0 {
+ for _, e := range m.Nodes {
+ n += 1 + sovRaft(uint64(e))
+ }
+ }
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *ConfChange) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovRaft(uint64(m.ID))
+ n += 1 + sovRaft(uint64(m.Type))
+ n += 1 + sovRaft(uint64(m.NodeID))
+ if m.Context != nil {
+ l = len(m.Context)
+ n += 1 + l + sovRaft(uint64(l))
+ }
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func sovRaft(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozRaft(x uint64) (n int) {
+ return sovRaft(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Entry) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ m.Type = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Type |= (EntryType(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
+ }
+ m.Term = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Term |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
+ }
+ m.Index = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Index |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Data = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *SnapshotMetadata) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ConfState", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.ConfState.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
+ }
+ m.Index = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Index |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
+ }
+ m.Term = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Term |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *Snapshot) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Data = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Metadata.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *Message) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ m.Type = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Type |= (MessageType(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field To", wireType)
+ }
+ m.To = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.To |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field From", wireType)
+ }
+ m.From = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.From |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
+ }
+ m.Term = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Term |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 5:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field LogTerm", wireType)
+ }
+ m.LogTerm = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.LogTerm |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 6:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
+ }
+ m.Index = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Index |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 7:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Entries = append(m.Entries, Entry{})
+ if err := m.Entries[len(m.Entries)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 8:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType)
+ }
+ m.Commit = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Commit |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 9:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Snapshot", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if err := m.Snapshot.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ case 10:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Reject", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Reject = bool(v != 0)
+ case 11:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field RejectHint", wireType)
+ }
+ m.RejectHint = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.RejectHint |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *HardState) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
+ }
+ m.Term = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Term |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Vote", wireType)
+ }
+ m.Vote = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Vote |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Commit", wireType)
+ }
+ m.Commit = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Commit |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *ConfState) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Nodes", wireType)
+ }
+ var v uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ v |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.Nodes = append(m.Nodes, v)
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *ConfChange) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+ }
+ m.ID = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ID |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ m.Type = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Type |= (ConfChangeType(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field NodeID", wireType)
+ }
+ m.NodeID = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.NodeID |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Context", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRaft
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Context = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRaft(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRaft
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipRaft(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthRaft
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipRaft(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthRaft = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/raft/raftpb/raft.proto b/vendor/github.com/coreos/etcd/raft/raftpb/raft.proto
new file mode 100644
index 000000000..1da4fc008
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/raftpb/raft.proto
@@ -0,0 +1,85 @@
+syntax = "proto2";
+package raftpb;
+
+import "gogoproto/gogo.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.goproto_enum_prefix_all) = false;
+
+enum EntryType {
+ EntryNormal = 0;
+ EntryConfChange = 1;
+}
+
+message Entry {
+ optional EntryType Type = 1 [(gogoproto.nullable) = false];
+ optional uint64 Term = 2 [(gogoproto.nullable) = false];
+ optional uint64 Index = 3 [(gogoproto.nullable) = false];
+ optional bytes Data = 4;
+}
+
+message SnapshotMetadata {
+ optional ConfState conf_state = 1 [(gogoproto.nullable) = false];
+ optional uint64 index = 2 [(gogoproto.nullable) = false];
+ optional uint64 term = 3 [(gogoproto.nullable) = false];
+}
+
+message Snapshot {
+ optional bytes data = 1;
+ optional SnapshotMetadata metadata = 2 [(gogoproto.nullable) = false];
+}
+
+enum MessageType {
+ MsgHup = 0;
+ MsgBeat = 1;
+ MsgProp = 2;
+ MsgApp = 3;
+ MsgAppResp = 4;
+ MsgVote = 5;
+ MsgVoteResp = 6;
+ MsgSnap = 7;
+ MsgHeartbeat = 8;
+ MsgHeartbeatResp = 9;
+ MsgUnreachable = 10;
+ MsgSnapStatus = 11;
+}
+
+message Message {
+ optional MessageType type = 1 [(gogoproto.nullable) = false];
+ optional uint64 to = 2 [(gogoproto.nullable) = false];
+ optional uint64 from = 3 [(gogoproto.nullable) = false];
+ optional uint64 term = 4 [(gogoproto.nullable) = false];
+ optional uint64 logTerm = 5 [(gogoproto.nullable) = false];
+ optional uint64 index = 6 [(gogoproto.nullable) = false];
+ repeated Entry entries = 7 [(gogoproto.nullable) = false];
+ optional uint64 commit = 8 [(gogoproto.nullable) = false];
+ optional Snapshot snapshot = 9 [(gogoproto.nullable) = false];
+ optional bool reject = 10 [(gogoproto.nullable) = false];
+ optional uint64 rejectHint = 11 [(gogoproto.nullable) = false];
+}
+
+message HardState {
+ optional uint64 term = 1 [(gogoproto.nullable) = false];
+ optional uint64 vote = 2 [(gogoproto.nullable) = false];
+ optional uint64 commit = 3 [(gogoproto.nullable) = false];
+}
+
+message ConfState {
+ repeated uint64 nodes = 1;
+}
+
+enum ConfChangeType {
+ ConfChangeAddNode = 0;
+ ConfChangeRemoveNode = 1;
+ ConfChangeUpdateNode = 2;
+}
+
+message ConfChange {
+ optional uint64 ID = 1 [(gogoproto.nullable) = false];
+ optional ConfChangeType Type = 2 [(gogoproto.nullable) = false];
+ optional uint64 NodeID = 3 [(gogoproto.nullable) = false];
+ optional bytes Context = 4;
+}
diff --git a/vendor/github.com/coreos/etcd/raft/rafttest/network.go b/vendor/github.com/coreos/etcd/raft/rafttest/network.go
new file mode 100644
index 000000000..5779812b5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/rafttest/network.go
@@ -0,0 +1,171 @@
+// Copyright 2015 CoreOS, 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 rafttest
+
+import (
+ "math/rand"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+// a network interface
+type iface interface {
+ send(m raftpb.Message)
+ recv() chan raftpb.Message
+ disconnect()
+ connect()
+}
+
+// a network
+type network interface {
+ // drop message at given rate (1.0 drops all messages)
+ drop(from, to uint64, rate float64)
+ // delay message for (0, d] randomly at given rate (1.0 delay all messages)
+ // do we need rate here?
+ delay(from, to uint64, d time.Duration, rate float64)
+ disconnect(id uint64)
+ connect(id uint64)
+ // heal heals the network
+ heal()
+}
+
+type raftNetwork struct {
+ mu sync.Mutex
+ disconnected map[uint64]bool
+ dropmap map[conn]float64
+ delaymap map[conn]delay
+ recvQueues map[uint64]chan raftpb.Message
+}
+
+type conn struct {
+ from, to uint64
+}
+
+type delay struct {
+ d time.Duration
+ rate float64
+}
+
+func newRaftNetwork(nodes ...uint64) *raftNetwork {
+ pn := &raftNetwork{
+ recvQueues: make(map[uint64]chan raftpb.Message),
+ dropmap: make(map[conn]float64),
+ delaymap: make(map[conn]delay),
+ disconnected: make(map[uint64]bool),
+ }
+
+ for _, n := range nodes {
+ pn.recvQueues[n] = make(chan raftpb.Message, 1024)
+ }
+ return pn
+}
+
+func (rn *raftNetwork) nodeNetwork(id uint64) iface {
+ return &nodeNetwork{id: id, raftNetwork: rn}
+}
+
+func (rn *raftNetwork) send(m raftpb.Message) {
+ rn.mu.Lock()
+ to := rn.recvQueues[m.To]
+ if rn.disconnected[m.To] {
+ to = nil
+ }
+ drop := rn.dropmap[conn{m.From, m.To}]
+ delay := rn.delaymap[conn{m.From, m.To}]
+ rn.mu.Unlock()
+
+ if to == nil {
+ return
+ }
+ if drop != 0 && rand.Float64() < drop {
+ return
+ }
+ // TODO: shall we delay without blocking the send call?
+ if delay.d != 0 && rand.Float64() < delay.rate {
+ rd := rand.Int63n(int64(delay.d))
+ time.Sleep(time.Duration(rd))
+ }
+
+ select {
+ case to <- m:
+ default:
+ // drop messages when the receiver queue is full.
+ }
+}
+
+func (rn *raftNetwork) recvFrom(from uint64) chan raftpb.Message {
+ rn.mu.Lock()
+ fromc := rn.recvQueues[from]
+ if rn.disconnected[from] {
+ fromc = nil
+ }
+ rn.mu.Unlock()
+
+ return fromc
+}
+
+func (rn *raftNetwork) drop(from, to uint64, rate float64) {
+ rn.mu.Lock()
+ defer rn.mu.Unlock()
+ rn.dropmap[conn{from, to}] = rate
+}
+
+func (rn *raftNetwork) delay(from, to uint64, d time.Duration, rate float64) {
+ rn.mu.Lock()
+ defer rn.mu.Unlock()
+ rn.delaymap[conn{from, to}] = delay{d, rate}
+}
+
+func (rn *raftNetwork) heal() {
+ rn.mu.Lock()
+ defer rn.mu.Unlock()
+ rn.dropmap = make(map[conn]float64)
+ rn.delaymap = make(map[conn]delay)
+}
+
+func (rn *raftNetwork) disconnect(id uint64) {
+ rn.mu.Lock()
+ defer rn.mu.Unlock()
+ rn.disconnected[id] = true
+}
+
+func (rn *raftNetwork) connect(id uint64) {
+ rn.mu.Lock()
+ defer rn.mu.Unlock()
+ rn.disconnected[id] = false
+}
+
+type nodeNetwork struct {
+ id uint64
+ *raftNetwork
+}
+
+func (nt *nodeNetwork) connect() {
+ nt.raftNetwork.connect(nt.id)
+}
+
+func (nt *nodeNetwork) disconnect() {
+ nt.raftNetwork.disconnect(nt.id)
+}
+
+func (nt *nodeNetwork) send(m raftpb.Message) {
+ nt.raftNetwork.send(m)
+}
+
+func (nt *nodeNetwork) recv() chan raftpb.Message {
+ return nt.recvFrom(nt.id)
+}
diff --git a/vendor/github.com/coreos/etcd/raft/rafttest/network_test.go b/vendor/github.com/coreos/etcd/raft/rafttest/network_test.go
new file mode 100644
index 000000000..9ea296383
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/rafttest/network_test.go
@@ -0,0 +1,72 @@
+// Copyright 2015 CoreOS, 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 rafttest
+
+import (
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestNetworkDrop(t *testing.T) {
+ // drop around 10% messages
+ sent := 1000
+ droprate := 0.1
+ nt := newRaftNetwork(1, 2)
+ nt.drop(1, 2, droprate)
+ for i := 0; i < sent; i++ {
+ nt.send(raftpb.Message{From: 1, To: 2})
+ }
+
+ c := nt.recvFrom(2)
+
+ received := 0
+ done := false
+ for !done {
+ select {
+ case <-c:
+ received++
+ default:
+ done = true
+ }
+ }
+
+ drop := sent - received
+ if drop > int((droprate+0.1)*float64(sent)) || drop < int((droprate-0.1)*float64(sent)) {
+ t.Errorf("drop = %d, want around %.2f", drop, droprate*float64(sent))
+ }
+}
+
+func TestNetworkDelay(t *testing.T) {
+ sent := 1000
+ delay := time.Millisecond
+ delayrate := 0.1
+ nt := newRaftNetwork(1, 2)
+
+ nt.delay(1, 2, delay, delayrate)
+ var total time.Duration
+ for i := 0; i < sent; i++ {
+ s := time.Now()
+ nt.send(raftpb.Message{From: 1, To: 2})
+ total += time.Since(s)
+ }
+
+ w := time.Duration(float64(sent)*delayrate/2) * delay
+ // there are pretty overhead in the send call, since it genarete random numbers.
+ if total < w {
+ t.Errorf("total = %v, want > %v", total, w)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/rafttest/node.go b/vendor/github.com/coreos/etcd/raft/rafttest/node.go
new file mode 100644
index 000000000..556355d24
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/rafttest/node.go
@@ -0,0 +1,145 @@
+// Copyright 2015 CoreOS, 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 rafttest
+
+import (
+ "log"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+type node struct {
+ raft.Node
+ id uint64
+ iface iface
+ stopc chan struct{}
+ pausec chan bool
+
+ // stable
+ storage *raft.MemoryStorage
+ state raftpb.HardState
+}
+
+func startNode(id uint64, peers []raft.Peer, iface iface) *node {
+ st := raft.NewMemoryStorage()
+ c := &raft.Config{
+ ID: id,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: st,
+ MaxSizePerMsg: 1024 * 1024,
+ MaxInflightMsgs: 256,
+ }
+ rn := raft.StartNode(c, peers)
+ n := &node{
+ Node: rn,
+ id: id,
+ storage: st,
+ iface: iface,
+ pausec: make(chan bool),
+ }
+ n.start()
+ return n
+}
+
+func (n *node) start() {
+ n.stopc = make(chan struct{})
+ ticker := time.Tick(5 * time.Millisecond)
+
+ go func() {
+ for {
+ select {
+ case <-ticker:
+ n.Tick()
+ case rd := <-n.Ready():
+ if !raft.IsEmptyHardState(rd.HardState) {
+ n.state = rd.HardState
+ n.storage.SetHardState(n.state)
+ }
+ n.storage.Append(rd.Entries)
+ time.Sleep(time.Millisecond)
+ // TODO: make send async, more like real world...
+ for _, m := range rd.Messages {
+ n.iface.send(m)
+ }
+ n.Advance()
+ case m := <-n.iface.recv():
+ n.Step(context.TODO(), m)
+ case <-n.stopc:
+ n.Stop()
+ log.Printf("raft.%d: stop", n.id)
+ n.Node = nil
+ close(n.stopc)
+ return
+ case p := <-n.pausec:
+ recvms := make([]raftpb.Message, 0)
+ for p {
+ select {
+ case m := <-n.iface.recv():
+ recvms = append(recvms, m)
+ case p = <-n.pausec:
+ }
+ }
+ // step all pending messages
+ for _, m := range recvms {
+ n.Step(context.TODO(), m)
+ }
+ }
+ }
+ }()
+}
+
+// stop stops the node. stop a stopped node might panic.
+// All in memory state of node is discarded.
+// All stable MUST be unchanged.
+func (n *node) stop() {
+ n.iface.disconnect()
+ n.stopc <- struct{}{}
+ // wait for the shutdown
+ <-n.stopc
+}
+
+// restart restarts the node. restart a started node
+// blocks and might affect the future stop operation.
+func (n *node) restart() {
+ // wait for the shutdown
+ <-n.stopc
+ c := &raft.Config{
+ ID: n.id,
+ ElectionTick: 10,
+ HeartbeatTick: 1,
+ Storage: n.storage,
+ MaxSizePerMsg: 1024 * 1024,
+ MaxInflightMsgs: 256,
+ }
+ n.Node = raft.RestartNode(c)
+ n.start()
+ n.iface.connect()
+}
+
+// pause pauses the node.
+// The paused node buffers the received messages and replies
+// all of them when it resumes.
+func (n *node) pause() {
+ n.pausec <- true
+}
+
+// resume resumes the paused node.
+func (n *node) resume() {
+ n.pausec <- false
+}
diff --git a/vendor/github.com/coreos/etcd/raft/rafttest/node_bench_test.go b/vendor/github.com/coreos/etcd/raft/rafttest/node_bench_test.go
new file mode 100644
index 000000000..0f8508138
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/rafttest/node_bench_test.go
@@ -0,0 +1,53 @@
+// Copyright 2015 CoreOS, 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 rafttest
+
+import (
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/raft"
+)
+
+func BenchmarkProposal3Nodes(b *testing.B) {
+ peers := []raft.Peer{{1, nil}, {2, nil}, {3, nil}}
+ nt := newRaftNetwork(1, 2, 3)
+
+ nodes := make([]*node, 0)
+
+ for i := 1; i <= 3; i++ {
+ n := startNode(uint64(i), peers, nt.nodeNetwork(uint64(i)))
+ nodes = append(nodes, n)
+ }
+ // get ready and warm up
+ time.Sleep(50 * time.Millisecond)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+
+ for _, n := range nodes {
+ if n.state.Commit != uint64(b.N+4) {
+ continue
+ }
+ }
+ b.StopTimer()
+
+ for _, n := range nodes {
+ n.stop()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/rafttest/node_test.go b/vendor/github.com/coreos/etcd/raft/rafttest/node_test.go
new file mode 100644
index 000000000..1a101d804
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/rafttest/node_test.go
@@ -0,0 +1,127 @@
+// Copyright 2015 CoreOS, 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 rafttest
+
+import (
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/raft"
+)
+
+func TestBasicProgress(t *testing.T) {
+ peers := []raft.Peer{{1, nil}, {2, nil}, {3, nil}, {4, nil}, {5, nil}}
+ nt := newRaftNetwork(1, 2, 3, 4, 5)
+
+ nodes := make([]*node, 0)
+
+ for i := 1; i <= 5; i++ {
+ n := startNode(uint64(i), peers, nt.nodeNetwork(uint64(i)))
+ nodes = append(nodes, n)
+ }
+
+ time.Sleep(10 * time.Millisecond)
+
+ for i := 0; i < 10000; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+
+ time.Sleep(500 * time.Millisecond)
+ for _, n := range nodes {
+ n.stop()
+ if n.state.Commit != 10006 {
+ t.Errorf("commit = %d, want = 10006", n.state.Commit)
+ }
+ }
+}
+
+func TestRestart(t *testing.T) {
+ peers := []raft.Peer{{1, nil}, {2, nil}, {3, nil}, {4, nil}, {5, nil}}
+ nt := newRaftNetwork(1, 2, 3, 4, 5)
+
+ nodes := make([]*node, 0)
+
+ for i := 1; i <= 5; i++ {
+ n := startNode(uint64(i), peers, nt.nodeNetwork(uint64(i)))
+ nodes = append(nodes, n)
+ }
+
+ time.Sleep(50 * time.Millisecond)
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[1].stop()
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[2].stop()
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[2].restart()
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[1].restart()
+
+ // give some time for nodes to catch up with the raft leader
+ time.Sleep(500 * time.Millisecond)
+ for _, n := range nodes {
+ n.stop()
+ if n.state.Commit != 1206 {
+ t.Errorf("commit = %d, want = 1206", n.state.Commit)
+ }
+ }
+}
+
+func TestPause(t *testing.T) {
+ peers := []raft.Peer{{1, nil}, {2, nil}, {3, nil}, {4, nil}, {5, nil}}
+ nt := newRaftNetwork(1, 2, 3, 4, 5)
+
+ nodes := make([]*node, 0)
+
+ for i := 1; i <= 5; i++ {
+ n := startNode(uint64(i), peers, nt.nodeNetwork(uint64(i)))
+ nodes = append(nodes, n)
+ }
+
+ time.Sleep(50 * time.Millisecond)
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[1].pause()
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[2].pause()
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[2].resume()
+ for i := 0; i < 300; i++ {
+ nodes[0].Propose(context.TODO(), []byte("somedata"))
+ }
+ nodes[1].resume()
+
+ // give some time for nodes to catch up with the raft leader
+ time.Sleep(300 * time.Millisecond)
+ for _, n := range nodes {
+ n.stop()
+ if n.state.Commit != 1206 {
+ t.Errorf("commit = %d, want = 1206", n.state.Commit)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/status.go b/vendor/github.com/coreos/etcd/raft/status.go
new file mode 100644
index 000000000..fca7fedb8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/status.go
@@ -0,0 +1,75 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "fmt"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+type Status struct {
+ ID uint64
+
+ pb.HardState
+ SoftState
+
+ Applied uint64
+ Progress map[uint64]Progress
+}
+
+// getStatus gets a copy of the current raft status.
+func getStatus(r *raft) Status {
+ s := Status{ID: r.id}
+ s.HardState = r.HardState
+ s.SoftState = *r.softState()
+
+ s.Applied = r.raftLog.applied
+
+ if s.RaftState == StateLeader {
+ s.Progress = make(map[uint64]Progress)
+ for id, p := range r.prs {
+ s.Progress[id] = *p
+ }
+ }
+
+ return s
+}
+
+// TODO: try to simplify this by introducing ID type into raft
+func (s Status) MarshalJSON() ([]byte, error) {
+ j := fmt.Sprintf(`{"id":"%x","term":%d,"vote":"%x","commit":%d,"lead":"%x","raftState":%q,"progress":{`,
+ s.ID, s.Term, s.Vote, s.Commit, s.Lead, s.RaftState)
+
+ if len(s.Progress) == 0 {
+ j += "}}"
+ } else {
+ for k, v := range s.Progress {
+ subj := fmt.Sprintf(`"%x":{"match":%d,"next":%d,"state":%q},`, k, v.Match, v.Next, v.State)
+ j += subj
+ }
+ // remove the trailing ","
+ j = j[:len(j)-1] + "}}"
+ }
+ return []byte(j), nil
+}
+
+func (s Status) String() string {
+ b, err := s.MarshalJSON()
+ if err != nil {
+ raftLogger.Panicf("unexpected error: %v", err)
+ }
+ return string(b)
+}
diff --git a/vendor/github.com/coreos/etcd/raft/storage.go b/vendor/github.com/coreos/etcd/raft/storage.go
new file mode 100644
index 000000000..904e185fc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/storage.go
@@ -0,0 +1,252 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "errors"
+ "sync"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+// ErrCompacted is returned by Storage.Entries/Compact when a requested
+// index is unavailable because it predates the last snapshot.
+var ErrCompacted = errors.New("requested index is unavailable due to compaction")
+
+// ErrOutOfDataSnap is returned by Storage.CreateSnapshot when a requested
+// index is older than the existing snapshot.
+var ErrSnapOutOfDate = errors.New("requested index is older than the existing snapshot")
+
+var ErrUnavailable = errors.New("requested entry at index is unavailable")
+
+// Storage is an interface that may be implemented by the application
+// to retrieve log entries from storage.
+//
+// If any Storage method returns an error, the raft instance will
+// become inoperable and refuse to participate in elections; the
+// application is responsible for cleanup and recovery in this case.
+type Storage interface {
+ // InitialState returns the saved HardState and ConfState information.
+ InitialState() (pb.HardState, pb.ConfState, error)
+ // Entries returns a slice of log entries in the range [lo,hi).
+ // MaxSize limits the total size of the log entries returned, but
+ // Entries returns at least one entry if any.
+ Entries(lo, hi, maxSize uint64) ([]pb.Entry, error)
+ // Term returns the term of entry i, which must be in the range
+ // [FirstIndex()-1, LastIndex()]. The term of the entry before
+ // FirstIndex is retained for matching purposes even though the
+ // rest of that entry may not be available.
+ Term(i uint64) (uint64, error)
+ // LastIndex returns the index of the last entry in the log.
+ LastIndex() (uint64, error)
+ // FirstIndex returns the index of the first log entry that is
+ // possibly available via Entries (older entries have been incorporated
+ // into the latest Snapshot; if storage only contains the dummy entry the
+ // first log entry is not available).
+ FirstIndex() (uint64, error)
+ // Snapshot returns the most recent snapshot.
+ // If snapshot is temporarily unavailable, it should return ErrTemporarilyUnavailable,
+ // so raft state machine could know that Storage needs some time to prepare
+ // snapshot and call Snapshot later.
+ Snapshot() (pb.Snapshot, error)
+}
+
+// MemoryStorage implements the Storage interface backed by an
+// in-memory array.
+type MemoryStorage struct {
+ // Protects access to all fields. Most methods of MemoryStorage are
+ // run on the raft goroutine, but Append() is run on an application
+ // goroutine.
+ sync.Mutex
+
+ hardState pb.HardState
+ snapshot pb.Snapshot
+ // ents[i] has raft log position i+snapshot.Metadata.Index
+ ents []pb.Entry
+}
+
+// NewMemoryStorage creates an empty MemoryStorage.
+func NewMemoryStorage() *MemoryStorage {
+ return &MemoryStorage{
+ // When starting from scratch populate the list with a dummy entry at term zero.
+ ents: make([]pb.Entry, 1),
+ }
+}
+
+// InitialState implements the Storage interface.
+func (ms *MemoryStorage) InitialState() (pb.HardState, pb.ConfState, error) {
+ return ms.hardState, ms.snapshot.Metadata.ConfState, nil
+}
+
+// SetHardState saves the current HardState.
+func (ms *MemoryStorage) SetHardState(st pb.HardState) error {
+ ms.hardState = st
+ return nil
+}
+
+// Entries implements the Storage interface.
+func (ms *MemoryStorage) Entries(lo, hi, maxSize uint64) ([]pb.Entry, error) {
+ ms.Lock()
+ defer ms.Unlock()
+ offset := ms.ents[0].Index
+ if lo <= offset {
+ return nil, ErrCompacted
+ }
+ if hi > ms.lastIndex()+1 {
+ raftLogger.Panicf("entries's hi(%d) is out of bound lastindex(%d)", hi, ms.lastIndex())
+ }
+ // only contains dummy entries.
+ if len(ms.ents) == 1 {
+ return nil, ErrUnavailable
+ }
+
+ ents := ms.ents[lo-offset : hi-offset]
+ return limitSize(ents, maxSize), nil
+}
+
+// Term implements the Storage interface.
+func (ms *MemoryStorage) Term(i uint64) (uint64, error) {
+ ms.Lock()
+ defer ms.Unlock()
+ offset := ms.ents[0].Index
+ if i < offset {
+ return 0, ErrCompacted
+ }
+ return ms.ents[i-offset].Term, nil
+}
+
+// LastIndex implements the Storage interface.
+func (ms *MemoryStorage) LastIndex() (uint64, error) {
+ ms.Lock()
+ defer ms.Unlock()
+ return ms.lastIndex(), nil
+}
+
+func (ms *MemoryStorage) lastIndex() uint64 {
+ return ms.ents[0].Index + uint64(len(ms.ents)) - 1
+}
+
+// FirstIndex implements the Storage interface.
+func (ms *MemoryStorage) FirstIndex() (uint64, error) {
+ ms.Lock()
+ defer ms.Unlock()
+ return ms.firstIndex(), nil
+}
+
+func (ms *MemoryStorage) firstIndex() uint64 {
+ return ms.ents[0].Index + 1
+}
+
+// Snapshot implements the Storage interface.
+func (ms *MemoryStorage) Snapshot() (pb.Snapshot, error) {
+ ms.Lock()
+ defer ms.Unlock()
+ return ms.snapshot, nil
+}
+
+// ApplySnapshot overwrites the contents of this Storage object with
+// those of the given snapshot.
+func (ms *MemoryStorage) ApplySnapshot(snap pb.Snapshot) error {
+ ms.Lock()
+ defer ms.Unlock()
+
+ // TODO: return snapOutOfDate?
+ ms.snapshot = snap
+ ms.ents = []pb.Entry{{Term: snap.Metadata.Term, Index: snap.Metadata.Index}}
+ return nil
+}
+
+// Creates a snapshot which can be retrieved with the Snapshot() method and
+// can be used to reconstruct the state at that point.
+// If any configuration changes have been made since the last compaction,
+// the result of the last ApplyConfChange must be passed in.
+func (ms *MemoryStorage) CreateSnapshot(i uint64, cs *pb.ConfState, data []byte) (pb.Snapshot, error) {
+ ms.Lock()
+ defer ms.Unlock()
+ if i <= ms.snapshot.Metadata.Index {
+ return pb.Snapshot{}, ErrSnapOutOfDate
+ }
+
+ offset := ms.ents[0].Index
+ if i > ms.lastIndex() {
+ raftLogger.Panicf("snapshot %d is out of bound lastindex(%d)", i, ms.lastIndex())
+ }
+
+ ms.snapshot.Metadata.Index = i
+ ms.snapshot.Metadata.Term = ms.ents[i-offset].Term
+ if cs != nil {
+ ms.snapshot.Metadata.ConfState = *cs
+ }
+ ms.snapshot.Data = data
+ return ms.snapshot, nil
+}
+
+// Compact discards all log entries prior to compactIndex.
+// It is the application's responsibility to not attempt to compact an index
+// greater than raftLog.applied.
+func (ms *MemoryStorage) Compact(compactIndex uint64) error {
+ ms.Lock()
+ defer ms.Unlock()
+ offset := ms.ents[0].Index
+ if compactIndex <= offset {
+ return ErrCompacted
+ }
+ if compactIndex > ms.lastIndex() {
+ raftLogger.Panicf("compact %d is out of bound lastindex(%d)", compactIndex, ms.lastIndex())
+ }
+
+ i := compactIndex - offset
+ ents := make([]pb.Entry, 1, 1+uint64(len(ms.ents))-i)
+ ents[0].Index = ms.ents[i].Index
+ ents[0].Term = ms.ents[i].Term
+ ents = append(ents, ms.ents[i+1:]...)
+ ms.ents = ents
+ return nil
+}
+
+// Append the new entries to storage.
+// TODO (xiangli): ensure the entries are continuous and
+// entries[0].Index > ms.entries[0].Index
+func (ms *MemoryStorage) Append(entries []pb.Entry) error {
+ ms.Lock()
+ defer ms.Unlock()
+ if len(entries) == 0 {
+ return nil
+ }
+ first := ms.ents[0].Index + 1
+ last := entries[0].Index + uint64(len(entries)) - 1
+
+ // shortcut if there is no new entry.
+ if last < first {
+ return nil
+ }
+ // truncate compacted entries
+ if first > entries[0].Index {
+ entries = entries[first-entries[0].Index:]
+ }
+
+ offset := entries[0].Index - ms.ents[0].Index
+ switch {
+ case uint64(len(ms.ents)) > offset:
+ ms.ents = append([]pb.Entry{}, ms.ents[:offset]...)
+ ms.ents = append(ms.ents, entries...)
+ case uint64(len(ms.ents)) == offset:
+ ms.ents = append(ms.ents, entries...)
+ default:
+ raftLogger.Panicf("missing log entry [last: %d, append at: %d]",
+ ms.lastIndex(), entries[0].Index)
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/raft/storage_test.go b/vendor/github.com/coreos/etcd/raft/storage_test.go
new file mode 100644
index 000000000..d782b0320
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/storage_test.go
@@ -0,0 +1,247 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "math"
+ "reflect"
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+// TODO(xiangli): Test panic cases
+
+func TestStorageTerm(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}}
+ tests := []struct {
+ i uint64
+
+ werr error
+ wterm uint64
+ }{
+ {2, ErrCompacted, 0},
+ {3, nil, 3},
+ {4, nil, 4},
+ {5, nil, 5},
+ }
+
+ for i, tt := range tests {
+ s := &MemoryStorage{ents: ents}
+ term, err := s.Term(tt.i)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if term != tt.wterm {
+ t.Errorf("#%d: term = %d, want %d", i, term, tt.wterm)
+ }
+ }
+}
+
+func TestStorageEntries(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}
+ tests := []struct {
+ lo, hi, maxsize uint64
+
+ werr error
+ wentries []pb.Entry
+ }{
+ {2, 6, math.MaxUint64, ErrCompacted, nil},
+ {3, 4, math.MaxUint64, ErrCompacted, nil},
+ {4, 5, math.MaxUint64, nil, []pb.Entry{{Index: 4, Term: 4}}},
+ {4, 6, math.MaxUint64, nil, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ {4, 7, math.MaxUint64, nil, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}},
+ // even if maxsize is zero, the first entry should be returned
+ {4, 7, 0, nil, []pb.Entry{{Index: 4, Term: 4}}},
+ // limit to 2
+ {4, 7, uint64(ents[1].Size() + ents[2].Size()), nil, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ // limit to 2
+ {4, 7, uint64(ents[1].Size() + ents[2].Size() + ents[3].Size()/2), nil, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ {4, 7, uint64(ents[1].Size() + ents[2].Size() + ents[3].Size() - 1), nil, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ // all
+ {4, 7, uint64(ents[1].Size() + ents[2].Size() + ents[3].Size()), nil, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}},
+ }
+
+ for i, tt := range tests {
+ s := &MemoryStorage{ents: ents}
+ entries, err := s.Entries(tt.lo, tt.hi, tt.maxsize)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if !reflect.DeepEqual(entries, tt.wentries) {
+ t.Errorf("#%d: entries = %v, want %v", i, entries, tt.wentries)
+ }
+ }
+}
+
+func TestStorageLastIndex(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}}
+ s := &MemoryStorage{ents: ents}
+
+ last, err := s.LastIndex()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if last != 5 {
+ t.Errorf("term = %d, want %d", last, 5)
+ }
+
+ s.Append([]pb.Entry{{Index: 6, Term: 5}})
+ last, err = s.LastIndex()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if last != 6 {
+ t.Errorf("last = %d, want %d", last, 5)
+ }
+}
+
+func TestStorageFirstIndex(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}}
+ s := &MemoryStorage{ents: ents}
+
+ first, err := s.FirstIndex()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if first != 4 {
+ t.Errorf("first = %d, want %d", first, 4)
+ }
+
+ s.Compact(4)
+ first, err = s.FirstIndex()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if first != 5 {
+ t.Errorf("first = %d, want %d", first, 5)
+ }
+}
+
+func TestStorageCompact(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}}
+ tests := []struct {
+ i uint64
+
+ werr error
+ windex uint64
+ wterm uint64
+ wlen int
+ }{
+ {2, ErrCompacted, 3, 3, 3},
+ {3, ErrCompacted, 3, 3, 3},
+ {4, nil, 4, 4, 2},
+ {5, nil, 5, 5, 1},
+ }
+
+ for i, tt := range tests {
+ s := &MemoryStorage{ents: ents}
+ err := s.Compact(tt.i)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if s.ents[0].Index != tt.windex {
+ t.Errorf("#%d: index = %d, want %d", i, s.ents[0].Index, tt.windex)
+ }
+ if s.ents[0].Term != tt.wterm {
+ t.Errorf("#%d: term = %d, want %d", i, s.ents[0].Term, tt.wterm)
+ }
+ if len(s.ents) != tt.wlen {
+ t.Errorf("#%d: len = %d, want %d", i, len(s.ents), tt.wlen)
+ }
+ }
+}
+
+func TestStorageCreateSnapshot(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}}
+ cs := &pb.ConfState{Nodes: []uint64{1, 2, 3}}
+ data := []byte("data")
+
+ tests := []struct {
+ i uint64
+
+ werr error
+ wsnap pb.Snapshot
+ }{
+ {4, nil, pb.Snapshot{Data: data, Metadata: pb.SnapshotMetadata{Index: 4, Term: 4, ConfState: *cs}}},
+ {5, nil, pb.Snapshot{Data: data, Metadata: pb.SnapshotMetadata{Index: 5, Term: 5, ConfState: *cs}}},
+ }
+
+ for i, tt := range tests {
+ s := &MemoryStorage{ents: ents}
+ snap, err := s.CreateSnapshot(tt.i, cs, data)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if !reflect.DeepEqual(snap, tt.wsnap) {
+ t.Errorf("#%d: snap = %+v, want %+v", i, snap, tt.wsnap)
+ }
+ }
+}
+
+func TestStorageAppend(t *testing.T) {
+ ents := []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}}
+ tests := []struct {
+ entries []pb.Entry
+
+ werr error
+ wentries []pb.Entry
+ }{
+ {
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}},
+ nil,
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}},
+ },
+ {
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 6}, {Index: 5, Term: 6}},
+ nil,
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 6}, {Index: 5, Term: 6}},
+ },
+ {
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 5}},
+ nil,
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 5}},
+ },
+ // truncate incoming entries, truncate the existing entries and append
+ {
+ []pb.Entry{{Index: 2, Term: 3}, {Index: 3, Term: 3}, {Index: 4, Term: 5}},
+ nil,
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 5}},
+ },
+ // tunncate the existing entries and append
+ {
+ []pb.Entry{{Index: 4, Term: 5}},
+ nil,
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 5}},
+ },
+ // direct append
+ {
+ []pb.Entry{{Index: 6, Term: 5}},
+ nil,
+ []pb.Entry{{Index: 3, Term: 3}, {Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 5}},
+ },
+ }
+
+ for i, tt := range tests {
+ s := &MemoryStorage{ents: ents}
+ err := s.Append(tt.entries)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if !reflect.DeepEqual(s.ents, tt.wentries) {
+ t.Errorf("#%d: entries = %v, want %v", i, s.ents, tt.wentries)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/raft/util.go b/vendor/github.com/coreos/etcd/raft/util.go
new file mode 100644
index 000000000..738eebc0f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/util.go
@@ -0,0 +1,110 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "bytes"
+ "fmt"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+func (st StateType) MarshalJSON() ([]byte, error) {
+ return []byte(fmt.Sprintf("%q", st.String())), nil
+}
+
+// uint64Slice implements sort interface
+type uint64Slice []uint64
+
+func (p uint64Slice) Len() int { return len(p) }
+func (p uint64Slice) Less(i, j int) bool { return p[i] < p[j] }
+func (p uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+
+func min(a, b uint64) uint64 {
+ if a > b {
+ return b
+ }
+ return a
+}
+
+func max(a, b uint64) uint64 {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func IsLocalMsg(m pb.Message) bool {
+ return m.Type == pb.MsgHup || m.Type == pb.MsgBeat || m.Type == pb.MsgUnreachable || m.Type == pb.MsgSnapStatus
+}
+
+func IsResponseMsg(m pb.Message) bool {
+ return m.Type == pb.MsgAppResp || m.Type == pb.MsgVoteResp || m.Type == pb.MsgHeartbeatResp || m.Type == pb.MsgUnreachable
+}
+
+// EntryFormatter can be implemented by the application to provide human-readable formatting
+// of entry data. Nil is a valid EntryFormatter and will use a default format.
+type EntryFormatter func([]byte) string
+
+// DescribeMessage returns a concise human-readable description of a
+// Message for debugging.
+func DescribeMessage(m pb.Message, f EntryFormatter) string {
+ var buf bytes.Buffer
+ fmt.Fprintf(&buf, "%x->%x %s Term:%d Log:%d/%d", m.From, m.To, m.Type, m.Term, m.LogTerm, m.Index)
+ if m.Reject {
+ fmt.Fprintf(&buf, " Rejected")
+ }
+ if m.Commit != 0 {
+ fmt.Fprintf(&buf, " Commit:%d", m.Commit)
+ }
+ if len(m.Entries) > 0 {
+ fmt.Fprintf(&buf, " Entries:[")
+ for _, e := range m.Entries {
+ buf.WriteString(DescribeEntry(e, f))
+ }
+ fmt.Fprintf(&buf, "]")
+ }
+ if !IsEmptySnap(m.Snapshot) {
+ fmt.Fprintf(&buf, " Snapshot:%v", m.Snapshot)
+ }
+ return buf.String()
+}
+
+// DescribeEntry returns a concise human-readable description of an
+// Entry for debugging.
+func DescribeEntry(e pb.Entry, f EntryFormatter) string {
+ var formatted string
+ if e.Type == pb.EntryNormal && f != nil {
+ formatted = f(e.Data)
+ } else {
+ formatted = fmt.Sprintf("%q", e.Data)
+ }
+ return fmt.Sprintf("%d/%d %s %s", e.Term, e.Index, e.Type, formatted)
+}
+
+func limitSize(ents []pb.Entry, maxSize uint64) []pb.Entry {
+ if len(ents) == 0 {
+ return ents
+ }
+ size := ents[0].Size()
+ var limit int
+ for limit = 1; limit < len(ents); limit++ {
+ size += ents[limit].Size()
+ if uint64(size) > maxSize {
+ break
+ }
+ }
+ return ents[:limit]
+}
diff --git a/vendor/github.com/coreos/etcd/raft/util_test.go b/vendor/github.com/coreos/etcd/raft/util_test.go
new file mode 100644
index 000000000..920879df9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/raft/util_test.go
@@ -0,0 +1,72 @@
+// Copyright 2015 CoreOS, 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 raft
+
+import (
+ "math"
+ "reflect"
+ "strings"
+ "testing"
+
+ pb "github.com/coreos/etcd/raft/raftpb"
+)
+
+var testFormatter EntryFormatter = func(data []byte) string {
+ return strings.ToUpper(string(data))
+}
+
+func TestDescribeEntry(t *testing.T) {
+ entry := pb.Entry{
+ Term: 1,
+ Index: 2,
+ Type: pb.EntryNormal,
+ Data: []byte("hello\x00world"),
+ }
+
+ defaultFormatted := DescribeEntry(entry, nil)
+ if defaultFormatted != "1/2 EntryNormal \"hello\\x00world\"" {
+ t.Errorf("unexpected default output: %s", defaultFormatted)
+ }
+
+ customFormatted := DescribeEntry(entry, testFormatter)
+ if customFormatted != "1/2 EntryNormal HELLO\x00WORLD" {
+ t.Errorf("unexpected custom output: %s", customFormatted)
+ }
+}
+
+func TestLimitSize(t *testing.T) {
+ ents := []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}
+ tests := []struct {
+ maxsize uint64
+ wentries []pb.Entry
+ }{
+ {math.MaxUint64, []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}},
+ // even if maxsize is zero, the first entry should be returned
+ {0, []pb.Entry{{Index: 4, Term: 4}}},
+ // limit to 2
+ {uint64(ents[0].Size() + ents[1].Size()), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ // limit to 2
+ {uint64(ents[0].Size() + ents[1].Size() + ents[2].Size()/2), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ {uint64(ents[0].Size() + ents[1].Size() + ents[2].Size() - 1), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}}},
+ // all
+ {uint64(ents[0].Size() + ents[1].Size() + ents[2].Size()), []pb.Entry{{Index: 4, Term: 4}, {Index: 5, Term: 5}, {Index: 6, Term: 6}}},
+ }
+
+ for i, tt := range tests {
+ if !reflect.DeepEqual(limitSize(ents, tt.maxsize), tt.wentries) {
+ t.Errorf("#%d: entries = %v, want %v", i, limitSize(ents, tt.maxsize), tt.wentries)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/coder.go b/vendor/github.com/coreos/etcd/rafthttp/coder.go
new file mode 100644
index 000000000..de6ac6cc0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/coder.go
@@ -0,0 +1,27 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import "github.com/coreos/etcd/raft/raftpb"
+
+type encoder interface {
+ // encode encodes the given message to an output stream.
+ encode(m raftpb.Message) error
+}
+
+type decoder interface {
+ // decode decodes the message from an input stream.
+ decode() (raftpb.Message, error)
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/fake_roundtripper_go14_test.go b/vendor/github.com/coreos/etcd/rafthttp/fake_roundtripper_go14_test.go
new file mode 100644
index 000000000..ed005a4ca
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/fake_roundtripper_go14_test.go
@@ -0,0 +1,35 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build !go1.5
+
+package rafthttp
+
+import (
+ "errors"
+ "net/http"
+)
+
+func (t *roundTripperBlocker) RoundTrip(req *http.Request) (*http.Response, error) {
+ c := make(chan struct{}, 1)
+ t.mu.Lock()
+ t.cancel[req] = c
+ t.mu.Unlock()
+ select {
+ case <-t.unblockc:
+ return &http.Response{StatusCode: http.StatusNoContent, Body: &nopReadCloser{}}, nil
+ case <-c:
+ return nil, errors.New("request canceled")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/fake_roundtripper_test.go b/vendor/github.com/coreos/etcd/rafthttp/fake_roundtripper_test.go
new file mode 100644
index 000000000..749dca278
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/fake_roundtripper_test.go
@@ -0,0 +1,37 @@
+// Copyright 2015 CoreOS, 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.
+
+// +build go1.5
+
+package rafthttp
+
+import (
+ "errors"
+ "net/http"
+)
+
+func (t *roundTripperBlocker) RoundTrip(req *http.Request) (*http.Response, error) {
+ c := make(chan struct{}, 1)
+ t.mu.Lock()
+ t.cancel[req] = c
+ t.mu.Unlock()
+ select {
+ case <-t.unblockc:
+ return &http.Response{StatusCode: http.StatusNoContent, Body: &nopReadCloser{}}, nil
+ case <-req.Cancel:
+ return nil, errors.New("request canceled")
+ case <-c:
+ return nil, errors.New("request canceled")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/functional_test.go b/vendor/github.com/coreos/etcd/rafthttp/functional_test.go
new file mode 100644
index 000000000..e07c0ba73
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/functional_test.go
@@ -0,0 +1,180 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/http/httptest"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestSendMessage(t *testing.T) {
+ // member 1
+ tr := &Transport{
+ ID: types.ID(1),
+ ClusterID: types.ID(1),
+ Raft: &fakeRaft{},
+ ServerStats: newServerStats(),
+ LeaderStats: stats.NewLeaderStats("1"),
+ }
+ tr.Start()
+ srv := httptest.NewServer(tr.Handler())
+ defer srv.Close()
+
+ // member 2
+ recvc := make(chan raftpb.Message, 1)
+ p := &fakeRaft{recvc: recvc}
+ tr2 := &Transport{
+ ID: types.ID(2),
+ ClusterID: types.ID(1),
+ Raft: p,
+ ServerStats: newServerStats(),
+ LeaderStats: stats.NewLeaderStats("2"),
+ }
+ tr2.Start()
+ srv2 := httptest.NewServer(tr2.Handler())
+ defer srv2.Close()
+
+ tr.AddPeer(types.ID(2), []string{srv2.URL})
+ defer tr.Stop()
+ tr2.AddPeer(types.ID(1), []string{srv.URL})
+ defer tr2.Stop()
+ if !waitStreamWorking(tr.Get(types.ID(2)).(*peer)) {
+ t.Fatalf("stream from 1 to 2 is not in work as expected")
+ }
+
+ data := []byte("some data")
+ tests := []raftpb.Message{
+ // these messages are set to send to itself, which facilitates testing.
+ {Type: raftpb.MsgProp, From: 1, To: 2, Entries: []raftpb.Entry{{Data: data}}},
+ {Type: raftpb.MsgApp, From: 1, To: 2, Term: 1, Index: 3, LogTerm: 0, Entries: []raftpb.Entry{{Index: 4, Term: 1, Data: data}}, Commit: 3},
+ {Type: raftpb.MsgAppResp, From: 1, To: 2, Term: 1, Index: 3},
+ {Type: raftpb.MsgVote, From: 1, To: 2, Term: 1, Index: 3, LogTerm: 0},
+ {Type: raftpb.MsgVoteResp, From: 1, To: 2, Term: 1},
+ {Type: raftpb.MsgSnap, From: 1, To: 2, Term: 1, Snapshot: raftpb.Snapshot{Metadata: raftpb.SnapshotMetadata{Index: 1000, Term: 1}, Data: data}},
+ {Type: raftpb.MsgHeartbeat, From: 1, To: 2, Term: 1, Commit: 3},
+ {Type: raftpb.MsgHeartbeatResp, From: 1, To: 2, Term: 1},
+ }
+ for i, tt := range tests {
+ tr.Send([]raftpb.Message{tt})
+ msg := <-recvc
+ if !reflect.DeepEqual(msg, tt) {
+ t.Errorf("#%d: msg = %+v, want %+v", i, msg, tt)
+ }
+ }
+}
+
+// TestSendMessageWhenStreamIsBroken tests that message can be sent to the
+// remote in a limited time when all underlying connections are broken.
+func TestSendMessageWhenStreamIsBroken(t *testing.T) {
+ // member 1
+ tr := &Transport{
+ ID: types.ID(1),
+ ClusterID: types.ID(1),
+ Raft: &fakeRaft{},
+ ServerStats: newServerStats(),
+ LeaderStats: stats.NewLeaderStats("1"),
+ }
+ tr.Start()
+ srv := httptest.NewServer(tr.Handler())
+ defer srv.Close()
+
+ // member 2
+ recvc := make(chan raftpb.Message, 1)
+ p := &fakeRaft{recvc: recvc}
+ tr2 := &Transport{
+ ID: types.ID(2),
+ ClusterID: types.ID(1),
+ Raft: p,
+ ServerStats: newServerStats(),
+ LeaderStats: stats.NewLeaderStats("2"),
+ }
+ tr2.Start()
+ srv2 := httptest.NewServer(tr2.Handler())
+ defer srv2.Close()
+
+ tr.AddPeer(types.ID(2), []string{srv2.URL})
+ defer tr.Stop()
+ tr2.AddPeer(types.ID(1), []string{srv.URL})
+ defer tr2.Stop()
+ if !waitStreamWorking(tr.Get(types.ID(2)).(*peer)) {
+ t.Fatalf("stream from 1 to 2 is not in work as expected")
+ }
+
+ // break the stream
+ srv.CloseClientConnections()
+ srv2.CloseClientConnections()
+ var n int
+ for {
+ select {
+ // TODO: remove this resend logic when we add retry logic into the code
+ case <-time.After(time.Millisecond):
+ n++
+ tr.Send([]raftpb.Message{{Type: raftpb.MsgHeartbeat, From: 1, To: 2, Term: 1, Commit: 3}})
+ case <-recvc:
+ if n > 10 {
+ t.Errorf("disconnection time = %dms, want < 10ms", n)
+ }
+ return
+ }
+ }
+}
+
+func newServerStats() *stats.ServerStats {
+ ss := &stats.ServerStats{}
+ ss.Initialize()
+ return ss
+}
+
+func waitStreamWorking(p *peer) bool {
+ for i := 0; i < 1000; i++ {
+ time.Sleep(time.Millisecond)
+ if _, ok := p.msgAppV2Writer.writec(); !ok {
+ continue
+ }
+ if _, ok := p.writer.writec(); !ok {
+ continue
+ }
+ return true
+ }
+ return false
+}
+
+type fakeRaft struct {
+ recvc chan<- raftpb.Message
+ err error
+ removedID uint64
+}
+
+func (p *fakeRaft) Process(ctx context.Context, m raftpb.Message) error {
+ select {
+ case p.recvc <- m:
+ default:
+ }
+ return p.err
+}
+
+func (p *fakeRaft) IsIDRemoved(id uint64) bool { return id == p.removedID }
+
+func (p *fakeRaft) ReportUnreachable(id uint64) {}
+
+func (p *fakeRaft) ReportSnapshot(id uint64, status raft.SnapshotStatus) {}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/http.go b/vendor/github.com/coreos/etcd/rafthttp/http.go
new file mode 100644
index 000000000..ae14d4202
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/http.go
@@ -0,0 +1,318 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ pioutil "github.com/coreos/etcd/pkg/ioutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+const (
+ // connReadLimitByte limits the number of bytes
+ // a single read can read out.
+ //
+ // 64KB should be large enough for not causing
+ // throughput bottleneck as well as small enough
+ // for not causing a read timeout.
+ connReadLimitByte = 64 * 1024
+)
+
+var (
+ RaftPrefix = "/raft"
+ ProbingPrefix = path.Join(RaftPrefix, "probing")
+ RaftStreamPrefix = path.Join(RaftPrefix, "stream")
+ RaftSnapshotPrefix = path.Join(RaftPrefix, "snapshot")
+
+ errIncompatibleVersion = errors.New("incompatible version")
+ errClusterIDMismatch = errors.New("cluster ID mismatch")
+)
+
+type peerGetter interface {
+ Get(id types.ID) Peer
+}
+
+type writerToResponse interface {
+ WriteTo(w http.ResponseWriter)
+}
+
+type pipelineHandler struct {
+ r Raft
+ cid types.ID
+}
+
+// newPipelineHandler returns a handler for handling raft messages
+// from pipeline for RaftPrefix.
+//
+// The handler reads out the raft message from request body,
+// and forwards it to the given raft state machine for processing.
+func newPipelineHandler(r Raft, cid types.ID) http.Handler {
+ return &pipelineHandler{
+ r: r,
+ cid: cid,
+ }
+}
+
+func (h *pipelineHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ w.Header().Set("Allow", "POST")
+ http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ w.Header().Set("X-Etcd-Cluster-ID", h.cid.String())
+
+ if err := checkClusterCompatibilityFromHeader(r.Header, h.cid); err != nil {
+ http.Error(w, err.Error(), http.StatusPreconditionFailed)
+ return
+ }
+
+ // Limit the data size that could be read from the request body, which ensures that read from
+ // connection will not time out accidentally due to possible blocking in underlying implementation.
+ limitedr := pioutil.NewLimitedBufferReader(r.Body, connReadLimitByte)
+ b, err := ioutil.ReadAll(limitedr)
+ if err != nil {
+ plog.Errorf("failed to read raft message (%v)", err)
+ http.Error(w, "error reading raft message", http.StatusBadRequest)
+ return
+ }
+ var m raftpb.Message
+ if err := m.Unmarshal(b); err != nil {
+ plog.Errorf("failed to unmarshal raft message (%v)", err)
+ http.Error(w, "error unmarshaling raft message", http.StatusBadRequest)
+ return
+ }
+ if err := h.r.Process(context.TODO(), m); err != nil {
+ switch v := err.(type) {
+ case writerToResponse:
+ v.WriteTo(w)
+ default:
+ plog.Warningf("failed to process raft message (%v)", err)
+ http.Error(w, "error processing raft message", http.StatusInternalServerError)
+ }
+ return
+ }
+ // Write StatusNoContet header after the message has been processed by
+ // raft, which facilitates the client to report MsgSnap status.
+ w.WriteHeader(http.StatusNoContent)
+}
+
+type snapshotHandler struct {
+ r Raft
+ snapSaver SnapshotSaver
+ cid types.ID
+}
+
+func newSnapshotHandler(r Raft, snapSaver SnapshotSaver, cid types.ID) http.Handler {
+ return &snapshotHandler{
+ r: r,
+ snapSaver: snapSaver,
+ cid: cid,
+ }
+}
+
+// ServeHTTP serves HTTP request to receive and process snapshot message.
+//
+// If request sender dies without closing underlying TCP connection,
+// the handler will keep waiting for the request body until TCP keepalive
+// finds out that the connection is broken after several minutes.
+// This is acceptable because
+// 1. snapshot messages sent through other TCP connections could still be
+// received and processed.
+// 2. this case should happen rarely, so no further optimization is done.
+func (h *snapshotHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "POST" {
+ w.Header().Set("Allow", "POST")
+ http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ w.Header().Set("X-Etcd-Cluster-ID", h.cid.String())
+
+ if err := checkClusterCompatibilityFromHeader(r.Header, h.cid); err != nil {
+ http.Error(w, err.Error(), http.StatusPreconditionFailed)
+ return
+ }
+
+ dec := &messageDecoder{r: r.Body}
+ m, err := dec.decode()
+ if err != nil {
+ msg := fmt.Sprintf("failed to decode raft message (%v)", err)
+ plog.Errorf(msg)
+ http.Error(w, msg, http.StatusBadRequest)
+ return
+ }
+ if m.Type != raftpb.MsgSnap {
+ plog.Errorf("unexpected raft message type %s on snapshot path", m.Type)
+ http.Error(w, "wrong raft message type", http.StatusBadRequest)
+ return
+ }
+
+ // save snapshot
+ if err := h.snapSaver.SaveFrom(r.Body, m.Snapshot.Metadata.Index); err != nil {
+ msg := fmt.Sprintf("failed to save KV snapshot (%v)", err)
+ plog.Error(msg)
+ http.Error(w, msg, http.StatusInternalServerError)
+ return
+ }
+ plog.Infof("received and saved snapshot [index: %d, from: %s] successfully", m.Snapshot.Metadata.Index, types.ID(m.From))
+
+ if err := h.r.Process(context.TODO(), m); err != nil {
+ switch v := err.(type) {
+ // Process may return writerToResponse error when doing some
+ // additional checks before calling raft.Node.Step.
+ case writerToResponse:
+ v.WriteTo(w)
+ default:
+ msg := fmt.Sprintf("failed to process raft message (%v)", err)
+ plog.Warningf(msg)
+ http.Error(w, msg, http.StatusInternalServerError)
+ }
+ return
+ }
+ // Write StatusNoContet header after the message has been processed by
+ // raft, which facilitates the client to report MsgSnap status.
+ w.WriteHeader(http.StatusNoContent)
+}
+
+type streamHandler struct {
+ peerGetter peerGetter
+ r Raft
+ id types.ID
+ cid types.ID
+}
+
+func newStreamHandler(peerGetter peerGetter, r Raft, id, cid types.ID) http.Handler {
+ return &streamHandler{
+ peerGetter: peerGetter,
+ r: r,
+ id: id,
+ cid: cid,
+ }
+}
+
+func (h *streamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ if r.Method != "GET" {
+ w.Header().Set("Allow", "GET")
+ http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
+ return
+ }
+
+ w.Header().Set("X-Server-Version", version.Version)
+ w.Header().Set("X-Etcd-Cluster-ID", h.cid.String())
+
+ if err := checkClusterCompatibilityFromHeader(r.Header, h.cid); err != nil {
+ http.Error(w, err.Error(), http.StatusPreconditionFailed)
+ return
+ }
+
+ var t streamType
+ switch path.Dir(r.URL.Path) {
+ case streamTypeMsgAppV2.endpoint():
+ t = streamTypeMsgAppV2
+ case streamTypeMessage.endpoint():
+ t = streamTypeMessage
+ default:
+ plog.Debugf("ignored unexpected streaming request path %s", r.URL.Path)
+ http.Error(w, "invalid path", http.StatusNotFound)
+ return
+ }
+
+ fromStr := path.Base(r.URL.Path)
+ from, err := types.IDFromString(fromStr)
+ if err != nil {
+ plog.Errorf("failed to parse from %s into ID (%v)", fromStr, err)
+ http.Error(w, "invalid from", http.StatusNotFound)
+ return
+ }
+ if h.r.IsIDRemoved(uint64(from)) {
+ plog.Warningf("rejected the stream from peer %s since it was removed", from)
+ http.Error(w, "removed member", http.StatusGone)
+ return
+ }
+ p := h.peerGetter.Get(from)
+ if p == nil {
+ // This may happen in following cases:
+ // 1. user starts a remote peer that belongs to a different cluster
+ // with the same cluster ID.
+ // 2. local etcd falls behind of the cluster, and cannot recognize
+ // the members that joined after its current progress.
+ plog.Errorf("failed to find member %s in cluster %s", from, h.cid)
+ http.Error(w, "error sender not found", http.StatusNotFound)
+ return
+ }
+
+ wto := h.id.String()
+ if gto := r.Header.Get("X-Raft-To"); gto != wto {
+ plog.Errorf("streaming request ignored (ID mismatch got %s want %s)", gto, wto)
+ http.Error(w, "to field mismatch", http.StatusPreconditionFailed)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ w.(http.Flusher).Flush()
+
+ c := newCloseNotifier()
+ conn := &outgoingConn{
+ t: t,
+ Writer: w,
+ Flusher: w.(http.Flusher),
+ Closer: c,
+ }
+ p.attachOutgoingConn(conn)
+ <-c.closeNotify()
+}
+
+// checkClusterCompatibilityFromHeader checks the cluster compatibility of
+// the local member from the given header.
+// It checks whether the version of local member is compatible with
+// the versions in the header, and whether the cluster ID of local member
+// matches the one in the header.
+func checkClusterCompatibilityFromHeader(header http.Header, cid types.ID) error {
+ if err := checkVersionCompability(header.Get("X-Server-From"), serverVersion(header), minClusterVersion(header)); err != nil {
+ plog.Errorf("request version incompatibility (%v)", err)
+ return errIncompatibleVersion
+ }
+ if gcid := header.Get("X-Etcd-Cluster-ID"); gcid != cid.String() {
+ plog.Errorf("request cluster ID mismatch (got %s want %s)", gcid, cid)
+ return errClusterIDMismatch
+ }
+ return nil
+}
+
+type closeNotifier struct {
+ done chan struct{}
+}
+
+func newCloseNotifier() *closeNotifier {
+ return &closeNotifier{
+ done: make(chan struct{}),
+ }
+}
+
+func (n *closeNotifier) Close() error {
+ close(n.done)
+ return nil
+}
+
+func (n *closeNotifier) closeNotify() <-chan struct{} { return n.done }
diff --git a/vendor/github.com/coreos/etcd/rafthttp/http_test.go b/vendor/github.com/coreos/etcd/rafthttp/http_test.go
new file mode 100644
index 000000000..c4ef9672a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/http_test.go
@@ -0,0 +1,358 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+func TestServeRaftPrefix(t *testing.T) {
+ testCases := []struct {
+ method string
+ body io.Reader
+ p Raft
+ clusterID string
+
+ wcode int
+ }{
+ {
+ // bad method
+ "GET",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{},
+ "0",
+ http.StatusMethodNotAllowed,
+ },
+ {
+ // bad method
+ "PUT",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{},
+ "0",
+ http.StatusMethodNotAllowed,
+ },
+ {
+ // bad method
+ "DELETE",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{},
+ "0",
+ http.StatusMethodNotAllowed,
+ },
+ {
+ // bad request body
+ "POST",
+ &errReader{},
+ &fakeRaft{},
+ "0",
+ http.StatusBadRequest,
+ },
+ {
+ // bad request protobuf
+ "POST",
+ strings.NewReader("malformed garbage"),
+ &fakeRaft{},
+ "0",
+ http.StatusBadRequest,
+ },
+ {
+ // good request, wrong cluster ID
+ "POST",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{},
+ "1",
+ http.StatusPreconditionFailed,
+ },
+ {
+ // good request, Processor failure
+ "POST",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{
+ err: &resWriterToError{code: http.StatusForbidden},
+ },
+ "0",
+ http.StatusForbidden,
+ },
+ {
+ // good request, Processor failure
+ "POST",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{
+ err: &resWriterToError{code: http.StatusInternalServerError},
+ },
+ "0",
+ http.StatusInternalServerError,
+ },
+ {
+ // good request, Processor failure
+ "POST",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{err: errors.New("blah")},
+ "0",
+ http.StatusInternalServerError,
+ },
+ {
+ // good request
+ "POST",
+ bytes.NewReader(
+ pbutil.MustMarshal(&raftpb.Message{}),
+ ),
+ &fakeRaft{},
+ "0",
+ http.StatusNoContent,
+ },
+ }
+ for i, tt := range testCases {
+ req, err := http.NewRequest(tt.method, "foo", tt.body)
+ if err != nil {
+ t.Fatalf("#%d: could not create request: %#v", i, err)
+ }
+ req.Header.Set("X-Etcd-Cluster-ID", tt.clusterID)
+ req.Header.Set("X-Server-Version", version.Version)
+ rw := httptest.NewRecorder()
+ h := newPipelineHandler(tt.p, types.ID(0))
+ h.ServeHTTP(rw, req)
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: got code=%d, want %d", i, rw.Code, tt.wcode)
+ }
+ }
+}
+
+func TestServeRaftStreamPrefix(t *testing.T) {
+ tests := []struct {
+ path string
+ wtype streamType
+ }{
+ {
+ RaftStreamPrefix + "/message/1",
+ streamTypeMessage,
+ },
+ {
+ RaftStreamPrefix + "/msgapp/1",
+ streamTypeMsgAppV2,
+ },
+ }
+ for i, tt := range tests {
+ req, err := http.NewRequest("GET", "http://localhost:2380"+tt.path, nil)
+ if err != nil {
+ t.Fatalf("#%d: could not create request: %#v", i, err)
+ }
+ req.Header.Set("X-Etcd-Cluster-ID", "1")
+ req.Header.Set("X-Server-Version", version.Version)
+ req.Header.Set("X-Raft-To", "2")
+
+ peer := newFakePeer()
+ peerGetter := &fakePeerGetter{peers: map[types.ID]Peer{types.ID(1): peer}}
+ h := newStreamHandler(peerGetter, &fakeRaft{}, types.ID(2), types.ID(1))
+
+ rw := httptest.NewRecorder()
+ go h.ServeHTTP(rw, req)
+
+ var conn *outgoingConn
+ select {
+ case conn = <-peer.connc:
+ case <-time.After(time.Second):
+ t.Fatalf("#%d: failed to attach outgoingConn", i)
+ }
+ if g := rw.Header().Get("X-Server-Version"); g != version.Version {
+ t.Errorf("#%d: X-Server-Version = %s, want %s", i, g, version.Version)
+ }
+ if conn.t != tt.wtype {
+ t.Errorf("#%d: type = %s, want %s", i, conn.t, tt.wtype)
+ }
+ conn.Close()
+ }
+}
+
+func TestServeRaftStreamPrefixBad(t *testing.T) {
+ removedID := uint64(5)
+ tests := []struct {
+ method string
+ path string
+ clusterID string
+ remote string
+
+ wcode int
+ }{
+ // bad method
+ {
+ "PUT",
+ RaftStreamPrefix + "/message/1",
+ "1",
+ "1",
+ http.StatusMethodNotAllowed,
+ },
+ // bad method
+ {
+ "POST",
+ RaftStreamPrefix + "/message/1",
+ "1",
+ "1",
+ http.StatusMethodNotAllowed,
+ },
+ // bad method
+ {
+ "DELETE",
+ RaftStreamPrefix + "/message/1",
+ "1",
+ "1",
+ http.StatusMethodNotAllowed,
+ },
+ // bad path
+ {
+ "GET",
+ RaftStreamPrefix + "/strange/1",
+ "1",
+ "1",
+ http.StatusNotFound,
+ },
+ // bad path
+ {
+ "GET",
+ RaftStreamPrefix + "/strange",
+ "1",
+ "1",
+ http.StatusNotFound,
+ },
+ // non-existant peer
+ {
+ "GET",
+ RaftStreamPrefix + "/message/2",
+ "1",
+ "1",
+ http.StatusNotFound,
+ },
+ // removed peer
+ {
+ "GET",
+ RaftStreamPrefix + "/message/" + fmt.Sprint(removedID),
+ "1",
+ "1",
+ http.StatusGone,
+ },
+ // wrong cluster ID
+ {
+ "GET",
+ RaftStreamPrefix + "/message/1",
+ "2",
+ "1",
+ http.StatusPreconditionFailed,
+ },
+ // wrong remote id
+ {
+ "GET",
+ RaftStreamPrefix + "/message/1",
+ "1",
+ "2",
+ http.StatusPreconditionFailed,
+ },
+ }
+ for i, tt := range tests {
+ req, err := http.NewRequest(tt.method, "http://localhost:2380"+tt.path, nil)
+ if err != nil {
+ t.Fatalf("#%d: could not create request: %#v", i, err)
+ }
+ req.Header.Set("X-Etcd-Cluster-ID", tt.clusterID)
+ req.Header.Set("X-Server-Version", version.Version)
+ req.Header.Set("X-Raft-To", tt.remote)
+ rw := httptest.NewRecorder()
+ peerGetter := &fakePeerGetter{peers: map[types.ID]Peer{types.ID(1): newFakePeer()}}
+ r := &fakeRaft{removedID: removedID}
+ h := newStreamHandler(peerGetter, r, types.ID(1), types.ID(1))
+ h.ServeHTTP(rw, req)
+
+ if rw.Code != tt.wcode {
+ t.Errorf("#%d: code = %d, want %d", i, rw.Code, tt.wcode)
+ }
+ }
+}
+
+func TestCloseNotifier(t *testing.T) {
+ c := newCloseNotifier()
+ select {
+ case <-c.closeNotify():
+ t.Fatalf("received unexpected close notification")
+ default:
+ }
+ c.Close()
+ select {
+ case <-c.closeNotify():
+ default:
+ t.Fatalf("failed to get close notification")
+ }
+}
+
+// errReader implements io.Reader to facilitate a broken request.
+type errReader struct{}
+
+func (er *errReader) Read(_ []byte) (int, error) { return 0, errors.New("some error") }
+
+type resWriterToError struct {
+ code int
+}
+
+func (e *resWriterToError) Error() string { return "" }
+func (e *resWriterToError) WriteTo(w http.ResponseWriter) { w.WriteHeader(e.code) }
+
+type fakePeerGetter struct {
+ peers map[types.ID]Peer
+}
+
+func (pg *fakePeerGetter) Get(id types.ID) Peer { return pg.peers[id] }
+
+type fakePeer struct {
+ msgs []raftpb.Message
+ urls types.URLs
+ connc chan *outgoingConn
+}
+
+func newFakePeer() *fakePeer {
+ return &fakePeer{
+ connc: make(chan *outgoingConn, 1),
+ }
+}
+
+func (pr *fakePeer) send(m raftpb.Message) { pr.msgs = append(pr.msgs, m) }
+func (pr *fakePeer) update(urls types.URLs) { pr.urls = urls }
+func (pr *fakePeer) attachOutgoingConn(conn *outgoingConn) { pr.connc <- conn }
+func (pr *fakePeer) activeSince() time.Time { return time.Time{} }
+func (pr *fakePeer) stop() {}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/metrics.go b/vendor/github.com/coreos/etcd/rafthttp/metrics.go
new file mode 100644
index 000000000..e1c9508dd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/metrics.go
@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+var (
+ msgSentDuration = prometheus.NewSummaryVec(
+ prometheus.SummaryOpts{
+ Namespace: "etcd",
+ Subsystem: "rafthttp",
+ Name: "message_sent_latency_microseconds",
+ Help: "message sent latency distributions.",
+ },
+ []string{"sendingType", "remoteID", "msgType"},
+ )
+
+ msgSentFailed = prometheus.NewCounterVec(prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "rafthttp",
+ Name: "message_sent_failed_total",
+ Help: "The total number of failed messages sent.",
+ },
+ []string{"sendingType", "remoteID", "msgType"},
+ )
+)
+
+func init() {
+ prometheus.MustRegister(msgSentDuration)
+ prometheus.MustRegister(msgSentFailed)
+}
+
+func reportSentDuration(sendingType string, m raftpb.Message, duration time.Duration) {
+ typ := m.Type.String()
+ if isLinkHeartbeatMessage(m) {
+ typ = "MsgLinkHeartbeat"
+ }
+ msgSentDuration.WithLabelValues(sendingType, types.ID(m.To).String(), typ).Observe(float64(duration.Nanoseconds() / int64(time.Microsecond)))
+}
+
+func reportSentFailure(sendingType string, m raftpb.Message) {
+ typ := m.Type.String()
+ if isLinkHeartbeatMessage(m) {
+ typ = "MsgLinkHeartbeat"
+ }
+ msgSentFailed.WithLabelValues(sendingType, types.ID(m.To).String(), typ).Inc()
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/msg_codec.go b/vendor/github.com/coreos/etcd/rafthttp/msg_codec.go
new file mode 100644
index 000000000..6616872e0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/msg_codec.go
@@ -0,0 +1,55 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "encoding/binary"
+ "io"
+
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+// messageEncoder is a encoder that can encode all kinds of messages.
+// It MUST be used with a paired messageDecoder.
+type messageEncoder struct {
+ w io.Writer
+}
+
+func (enc *messageEncoder) encode(m raftpb.Message) error {
+ if err := binary.Write(enc.w, binary.BigEndian, uint64(m.Size())); err != nil {
+ return err
+ }
+ _, err := enc.w.Write(pbutil.MustMarshal(&m))
+ return err
+}
+
+// messageDecoder is a decoder that can decode all kinds of messages.
+type messageDecoder struct {
+ r io.Reader
+}
+
+func (dec *messageDecoder) decode() (raftpb.Message, error) {
+ var m raftpb.Message
+ var l uint64
+ if err := binary.Read(dec.r, binary.BigEndian, &l); err != nil {
+ return m, err
+ }
+ buf := make([]byte, int(l))
+ if _, err := io.ReadFull(dec.r, buf); err != nil {
+ return m, err
+ }
+ return m, m.Unmarshal(buf)
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/msg_codec_test.go b/vendor/github.com/coreos/etcd/rafthttp/msg_codec_test.go
new file mode 100644
index 000000000..2e6e8d69c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/msg_codec_test.go
@@ -0,0 +1,65 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestMessage(t *testing.T) {
+ tests := []raftpb.Message{
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 1,
+ LogTerm: 1,
+ Index: 3,
+ Entries: []raftpb.Entry{{Term: 1, Index: 4}},
+ },
+ {
+ Type: raftpb.MsgProp,
+ From: 1,
+ To: 2,
+ Entries: []raftpb.Entry{
+ {Data: []byte("some data")},
+ {Data: []byte("some data")},
+ {Data: []byte("some data")},
+ },
+ },
+ linkHeartbeatMessage,
+ }
+ for i, tt := range tests {
+ b := &bytes.Buffer{}
+ enc := &messageEncoder{w: b}
+ if err := enc.encode(tt); err != nil {
+ t.Errorf("#%d: unexpected encode message error: %v", i, err)
+ continue
+ }
+ dec := &messageDecoder{r: b}
+ m, err := dec.decode()
+ if err != nil {
+ t.Errorf("#%d: unexpected decode message error: %v", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(m, tt) {
+ t.Errorf("#%d: message = %+v, want %+v", i, m, tt)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/msgappv2_codec.go b/vendor/github.com/coreos/etcd/rafthttp/msgappv2_codec.go
new file mode 100644
index 000000000..5b0cf8e2e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/msgappv2_codec.go
@@ -0,0 +1,248 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "time"
+
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+const (
+ msgTypeLinkHeartbeat uint8 = 0
+ msgTypeAppEntries uint8 = 1
+ msgTypeApp uint8 = 2
+
+ msgAppV2BufSize = 1024 * 1024
+)
+
+// msgappv2 stream sends three types of message: linkHeartbeatMessage,
+// AppEntries and MsgApp. AppEntries is the MsgApp that is sent in
+// replicate state in raft, whose index and term are fully predicatable.
+//
+// Data format of linkHeartbeatMessage:
+// | offset | bytes | description |
+// +--------+-------+-------------+
+// | 0 | 1 | \x00 |
+//
+// Data format of AppEntries:
+// | offset | bytes | description |
+// +--------+-------+-------------+
+// | 0 | 1 | \x01 |
+// | 1 | 8 | length of entries |
+// | 9 | 8 | length of first entry |
+// | 17 | n1 | first entry |
+// ...
+// | x | 8 | length of k-th entry data |
+// | x+8 | nk | k-th entry data |
+// | x+8+nk | 8 | commit index |
+//
+// Data format of MsgApp:
+// | offset | bytes | description |
+// +--------+-------+-------------+
+// | 0 | 1 | \x01 |
+// | 1 | 8 | length of encoded message |
+// | 9 | n | encoded message |
+type msgAppV2Encoder struct {
+ w io.Writer
+ fs *stats.FollowerStats
+
+ term uint64
+ index uint64
+ buf []byte
+ uint64buf []byte
+ uint8buf []byte
+}
+
+func newMsgAppV2Encoder(w io.Writer, fs *stats.FollowerStats) *msgAppV2Encoder {
+ return &msgAppV2Encoder{
+ w: w,
+ fs: fs,
+ buf: make([]byte, msgAppV2BufSize),
+ uint64buf: make([]byte, 8),
+ uint8buf: make([]byte, 1),
+ }
+}
+
+func (enc *msgAppV2Encoder) encode(m raftpb.Message) error {
+ start := time.Now()
+ switch {
+ case isLinkHeartbeatMessage(m):
+ enc.uint8buf[0] = byte(msgTypeLinkHeartbeat)
+ if _, err := enc.w.Write(enc.uint8buf); err != nil {
+ return err
+ }
+ case enc.index == m.Index && enc.term == m.LogTerm && m.LogTerm == m.Term:
+ enc.uint8buf[0] = byte(msgTypeAppEntries)
+ if _, err := enc.w.Write(enc.uint8buf); err != nil {
+ return err
+ }
+ // write length of entries
+ binary.BigEndian.PutUint64(enc.uint64buf, uint64(len(m.Entries)))
+ if _, err := enc.w.Write(enc.uint64buf); err != nil {
+ return err
+ }
+ for i := 0; i < len(m.Entries); i++ {
+ // write length of entry
+ binary.BigEndian.PutUint64(enc.uint64buf, uint64(m.Entries[i].Size()))
+ if _, err := enc.w.Write(enc.uint64buf); err != nil {
+ return err
+ }
+ if n := m.Entries[i].Size(); n < msgAppV2BufSize {
+ if _, err := m.Entries[i].MarshalTo(enc.buf); err != nil {
+ return err
+ }
+ if _, err := enc.w.Write(enc.buf[:n]); err != nil {
+ return err
+ }
+ } else {
+ if _, err := enc.w.Write(pbutil.MustMarshal(&m.Entries[i])); err != nil {
+ return err
+ }
+ }
+ enc.index++
+ }
+ // write commit index
+ binary.BigEndian.PutUint64(enc.uint64buf, m.Commit)
+ if _, err := enc.w.Write(enc.uint64buf); err != nil {
+ return err
+ }
+ enc.fs.Succ(time.Since(start))
+ default:
+ if err := binary.Write(enc.w, binary.BigEndian, msgTypeApp); err != nil {
+ return err
+ }
+ // write size of message
+ if err := binary.Write(enc.w, binary.BigEndian, uint64(m.Size())); err != nil {
+ return err
+ }
+ // write message
+ if _, err := enc.w.Write(pbutil.MustMarshal(&m)); err != nil {
+ return err
+ }
+
+ enc.term = m.Term
+ enc.index = m.Index
+ if l := len(m.Entries); l > 0 {
+ enc.index = m.Entries[l-1].Index
+ }
+ enc.fs.Succ(time.Since(start))
+ }
+ return nil
+}
+
+type msgAppV2Decoder struct {
+ r io.Reader
+ local, remote types.ID
+
+ term uint64
+ index uint64
+ buf []byte
+ uint64buf []byte
+ uint8buf []byte
+}
+
+func newMsgAppV2Decoder(r io.Reader, local, remote types.ID) *msgAppV2Decoder {
+ return &msgAppV2Decoder{
+ r: r,
+ local: local,
+ remote: remote,
+ buf: make([]byte, msgAppV2BufSize),
+ uint64buf: make([]byte, 8),
+ uint8buf: make([]byte, 1),
+ }
+}
+
+func (dec *msgAppV2Decoder) decode() (raftpb.Message, error) {
+ var (
+ m raftpb.Message
+ typ uint8
+ )
+ if _, err := io.ReadFull(dec.r, dec.uint8buf); err != nil {
+ return m, err
+ }
+ typ = uint8(dec.uint8buf[0])
+ switch typ {
+ case msgTypeLinkHeartbeat:
+ return linkHeartbeatMessage, nil
+ case msgTypeAppEntries:
+ m = raftpb.Message{
+ Type: raftpb.MsgApp,
+ From: uint64(dec.remote),
+ To: uint64(dec.local),
+ Term: dec.term,
+ LogTerm: dec.term,
+ Index: dec.index,
+ }
+
+ // decode entries
+ if _, err := io.ReadFull(dec.r, dec.uint64buf); err != nil {
+ return m, err
+ }
+ l := binary.BigEndian.Uint64(dec.uint64buf)
+ m.Entries = make([]raftpb.Entry, int(l))
+ for i := 0; i < int(l); i++ {
+ if _, err := io.ReadFull(dec.r, dec.uint64buf); err != nil {
+ return m, err
+ }
+ size := binary.BigEndian.Uint64(dec.uint64buf)
+ var buf []byte
+ if size < msgAppV2BufSize {
+ buf = dec.buf[:size]
+ if _, err := io.ReadFull(dec.r, buf); err != nil {
+ return m, err
+ }
+ } else {
+ buf = make([]byte, int(size))
+ if _, err := io.ReadFull(dec.r, buf); err != nil {
+ return m, err
+ }
+ }
+ dec.index++
+ // 1 alloc
+ pbutil.MustUnmarshal(&m.Entries[i], buf)
+ }
+ // decode commit index
+ if _, err := io.ReadFull(dec.r, dec.uint64buf); err != nil {
+ return m, err
+ }
+ m.Commit = binary.BigEndian.Uint64(dec.uint64buf)
+ case msgTypeApp:
+ var size uint64
+ if err := binary.Read(dec.r, binary.BigEndian, &size); err != nil {
+ return m, err
+ }
+ buf := make([]byte, int(size))
+ if _, err := io.ReadFull(dec.r, buf); err != nil {
+ return m, err
+ }
+ pbutil.MustUnmarshal(&m, buf)
+
+ dec.term = m.Term
+ dec.index = m.Index
+ if l := len(m.Entries); l > 0 {
+ dec.index = m.Entries[l-1].Index
+ }
+ default:
+ return m, fmt.Errorf("failed to parse type %d in msgappv2 stream", typ)
+ }
+ return m, nil
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/msgappv2_codec_test.go b/vendor/github.com/coreos/etcd/rafthttp/msgappv2_codec_test.go
new file mode 100644
index 000000000..10c98460f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/msgappv2_codec_test.go
@@ -0,0 +1,123 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestMsgAppV2(t *testing.T) {
+ tests := []raftpb.Message{
+ linkHeartbeatMessage,
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 1,
+ LogTerm: 1,
+ Index: 0,
+ Entries: []raftpb.Entry{
+ {Term: 1, Index: 1, Data: []byte("some data")},
+ {Term: 1, Index: 2, Data: []byte("some data")},
+ {Term: 1, Index: 3, Data: []byte("some data")},
+ },
+ },
+ // consecutive MsgApp
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 1,
+ LogTerm: 1,
+ Index: 3,
+ Entries: []raftpb.Entry{
+ {Term: 1, Index: 4, Data: []byte("some data")},
+ },
+ },
+ linkHeartbeatMessage,
+ // consecutive MsgApp after linkHeartbeatMessage
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 1,
+ LogTerm: 1,
+ Index: 4,
+ Entries: []raftpb.Entry{
+ {Term: 1, Index: 5, Data: []byte("some data")},
+ },
+ },
+ // MsgApp with higher term
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 3,
+ LogTerm: 1,
+ Index: 5,
+ Entries: []raftpb.Entry{
+ {Term: 3, Index: 6, Data: []byte("some data")},
+ },
+ },
+ linkHeartbeatMessage,
+ // consecutive MsgApp
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 3,
+ LogTerm: 2,
+ Index: 6,
+ Entries: []raftpb.Entry{
+ {Term: 3, Index: 7, Data: []byte("some data")},
+ },
+ },
+ // consecutive empty MsgApp
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Term: 3,
+ LogTerm: 2,
+ Index: 7,
+ Entries: nil,
+ },
+ linkHeartbeatMessage,
+ }
+ b := &bytes.Buffer{}
+ enc := newMsgAppV2Encoder(b, &stats.FollowerStats{})
+ dec := newMsgAppV2Decoder(b, types.ID(2), types.ID(1))
+
+ for i, tt := range tests {
+ if err := enc.encode(tt); err != nil {
+ t.Errorf("#%d: unexpected encode message error: %v", i, err)
+ continue
+ }
+ m, err := dec.decode()
+ if err != nil {
+ t.Errorf("#%d: unexpected decode message error: %v", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(m, tt) {
+ t.Errorf("#%d: message = %+v, want %+v", i, m, tt)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/peer.go b/vendor/github.com/coreos/etcd/rafthttp/peer.go
new file mode 100644
index 000000000..8a95d5031
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/peer.go
@@ -0,0 +1,277 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/http"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+const (
+ // ConnRead/WriteTimeout is the i/o timeout set on each connection rafthttp pkg creates.
+ // A 5 seconds timeout is good enough for recycling bad connections. Or we have to wait for
+ // tcp keepalive failing to detect a bad connection, which is at minutes level.
+ // For long term streaming connections, rafthttp pkg sends application level linkHeartbeat
+ // to keep the connection alive.
+ // For short term pipeline connections, the connection MUST be killed to avoid it being
+ // put back to http pkg connection pool.
+ ConnReadTimeout = 5 * time.Second
+ ConnWriteTimeout = 5 * time.Second
+
+ recvBufSize = 4096
+ // maxPendingProposals holds the proposals during one leader election process.
+ // Generally one leader election takes at most 1 sec. It should have
+ // 0-2 election conflicts, and each one takes 0.5 sec.
+ // We assume the number of concurrent proposers is smaller than 4096.
+ // One client blocks on its proposal for at least 1 sec, so 4096 is enough
+ // to hold all proposals.
+ maxPendingProposals = 4096
+
+ streamAppV2 = "streamMsgAppV2"
+ streamMsg = "streamMsg"
+ pipelineMsg = "pipeline"
+ sendSnap = "sendMsgSnap"
+)
+
+type Peer interface {
+ // send sends the message to the remote peer. The function is non-blocking
+ // and has no promise that the message will be received by the remote.
+ // When it fails to send message out, it will report the status to underlying
+ // raft.
+ send(m raftpb.Message)
+ // update updates the urls of remote peer.
+ update(urls types.URLs)
+ // attachOutgoingConn attachs the outgoing connection to the peer for
+ // stream usage. After the call, the ownership of the outgoing
+ // connection hands over to the peer. The peer will close the connection
+ // when it is no longer used.
+ attachOutgoingConn(conn *outgoingConn)
+ // activeSince returns the time that the connection with the
+ // peer becomes active.
+ activeSince() time.Time
+ // stop performs any necessary finalization and terminates the peer
+ // elegantly.
+ stop()
+}
+
+// peer is the representative of a remote raft node. Local raft node sends
+// messages to the remote through peer.
+// Each peer has two underlying mechanisms to send out a message: stream and
+// pipeline.
+// A stream is a receiver initialized long-polling connection, which
+// is always open to transfer messages. Besides general stream, peer also has
+// a optimized stream for sending msgApp since msgApp accounts for large part
+// of all messages. Only raft leader uses the optimized stream to send msgApp
+// to the remote follower node.
+// A pipeline is a series of http clients that send http requests to the remote.
+// It is only used when the stream has not been established.
+type peer struct {
+ // id of the remote raft peer node
+ id types.ID
+ r Raft
+ v3demo bool
+
+ status *peerStatus
+
+ msgAppV2Writer *streamWriter
+ writer *streamWriter
+ pipeline *pipeline
+ snapSender *snapshotSender // snapshot sender to send v3 snapshot messages
+ msgAppV2Reader *streamReader
+
+ sendc chan raftpb.Message
+ recvc chan raftpb.Message
+ propc chan raftpb.Message
+ newURLsC chan types.URLs
+
+ // for testing
+ pausec chan struct{}
+ resumec chan struct{}
+
+ stopc chan struct{}
+ done chan struct{}
+}
+
+func startPeer(streamRt, pipelineRt http.RoundTripper, urls types.URLs, local, to, cid types.ID, snapst *snapshotStore, r Raft, fs *stats.FollowerStats, errorc chan error, v3demo bool) *peer {
+ picker := newURLPicker(urls)
+ status := newPeerStatus(to)
+ p := &peer{
+ id: to,
+ r: r,
+ v3demo: v3demo,
+ status: status,
+ msgAppV2Writer: startStreamWriter(to, status, fs, r),
+ writer: startStreamWriter(to, status, fs, r),
+ pipeline: newPipeline(pipelineRt, picker, local, to, cid, status, fs, r, errorc),
+ snapSender: newSnapshotSender(pipelineRt, picker, local, to, cid, status, snapst, r, errorc),
+ sendc: make(chan raftpb.Message),
+ recvc: make(chan raftpb.Message, recvBufSize),
+ propc: make(chan raftpb.Message, maxPendingProposals),
+ newURLsC: make(chan types.URLs),
+ pausec: make(chan struct{}),
+ resumec: make(chan struct{}),
+ stopc: make(chan struct{}),
+ done: make(chan struct{}),
+ }
+
+ // Use go-routine for process of MsgProp because it is
+ // blocking when there is no leader.
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ for {
+ select {
+ case mm := <-p.propc:
+ if err := r.Process(ctx, mm); err != nil {
+ plog.Warningf("failed to process raft message (%v)", err)
+ }
+ case <-p.stopc:
+ return
+ }
+ }
+ }()
+
+ p.msgAppV2Reader = startStreamReader(streamRt, picker, streamTypeMsgAppV2, local, to, cid, status, p.recvc, p.propc, errorc)
+ reader := startStreamReader(streamRt, picker, streamTypeMessage, local, to, cid, status, p.recvc, p.propc, errorc)
+ go func() {
+ var paused bool
+ for {
+ select {
+ case m := <-p.sendc:
+ if paused {
+ continue
+ }
+ if p.v3demo && isMsgSnap(m) {
+ go p.snapSender.send(m)
+ continue
+ }
+ writec, name := p.pick(m)
+ select {
+ case writec <- m:
+ default:
+ p.r.ReportUnreachable(m.To)
+ if isMsgSnap(m) {
+ p.r.ReportSnapshot(m.To, raft.SnapshotFailure)
+ }
+ if status.isActive() {
+ plog.Warningf("dropped %s to %s since %s's sending buffer is full", m.Type, p.id, name)
+ } else {
+ plog.Debugf("dropped %s to %s since %s's sending buffer is full", m.Type, p.id, name)
+ }
+ }
+ case mm := <-p.recvc:
+ if err := r.Process(context.TODO(), mm); err != nil {
+ plog.Warningf("failed to process raft message (%v)", err)
+ }
+ case urls := <-p.newURLsC:
+ picker.update(urls)
+ case <-p.pausec:
+ paused = true
+ case <-p.resumec:
+ paused = false
+ case <-p.stopc:
+ cancel()
+ p.msgAppV2Writer.stop()
+ p.writer.stop()
+ p.pipeline.stop()
+ p.snapSender.stop()
+ p.msgAppV2Reader.stop()
+ reader.stop()
+ close(p.done)
+ return
+ }
+ }
+ }()
+
+ return p
+}
+
+func (p *peer) send(m raftpb.Message) {
+ select {
+ case p.sendc <- m:
+ case <-p.done:
+ }
+}
+
+func (p *peer) update(urls types.URLs) {
+ select {
+ case p.newURLsC <- urls:
+ case <-p.done:
+ }
+}
+
+func (p *peer) attachOutgoingConn(conn *outgoingConn) {
+ var ok bool
+ switch conn.t {
+ case streamTypeMsgAppV2:
+ ok = p.msgAppV2Writer.attach(conn)
+ case streamTypeMessage:
+ ok = p.writer.attach(conn)
+ default:
+ plog.Panicf("unhandled stream type %s", conn.t)
+ }
+ if !ok {
+ conn.Close()
+ }
+}
+
+func (p *peer) activeSince() time.Time { return p.status.activeSince }
+
+// Pause pauses the peer. The peer will simply drops all incoming
+// messages without retruning an error.
+func (p *peer) Pause() {
+ select {
+ case p.pausec <- struct{}{}:
+ case <-p.done:
+ }
+}
+
+// Resume resumes a paused peer.
+func (p *peer) Resume() {
+ select {
+ case p.resumec <- struct{}{}:
+ case <-p.done:
+ }
+}
+
+func (p *peer) stop() {
+ close(p.stopc)
+ <-p.done
+}
+
+// pick picks a chan for sending the given message. The picked chan and the picked chan
+// string name are returned.
+func (p *peer) pick(m raftpb.Message) (writec chan<- raftpb.Message, picked string) {
+ var ok bool
+ // Considering MsgSnap may have a big size, e.g., 1G, and will block
+ // stream for a long time, only use one of the N pipelines to send MsgSnap.
+ if isMsgSnap(m) {
+ return p.pipeline.msgc, pipelineMsg
+ } else if writec, ok = p.msgAppV2Writer.writec(); ok && isMsgApp(m) {
+ return writec, streamAppV2
+ } else if writec, ok = p.writer.writec(); ok {
+ return writec, streamMsg
+ }
+ return p.pipeline.msgc, pipelineMsg
+}
+
+func isMsgApp(m raftpb.Message) bool { return m.Type == raftpb.MsgApp }
+
+func isMsgSnap(m raftpb.Message) bool { return m.Type == raftpb.MsgSnap }
diff --git a/vendor/github.com/coreos/etcd/rafthttp/peer_status.go b/vendor/github.com/coreos/etcd/rafthttp/peer_status.go
new file mode 100644
index 000000000..97893fc92
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/peer_status.go
@@ -0,0 +1,77 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+type failureType struct {
+ source string
+ action string
+}
+
+type peerStatus struct {
+ id types.ID
+ mu sync.Mutex // protect variables below
+ active bool
+ failureMap map[failureType]string
+ activeSince time.Time
+}
+
+func newPeerStatus(id types.ID) *peerStatus {
+ return &peerStatus{
+ id: id,
+ failureMap: make(map[failureType]string),
+ }
+}
+
+func (s *peerStatus) activate() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if !s.active {
+ plog.Infof("the connection with %s became active", s.id)
+ s.active = true
+ s.activeSince = time.Now()
+ s.failureMap = make(map[failureType]string)
+ }
+}
+
+func (s *peerStatus) deactivate(failure failureType, reason string) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if s.active {
+ plog.Infof("the connection with %s became inactive", s.id)
+ s.active = false
+ s.activeSince = time.Time{}
+ }
+ logline := fmt.Sprintf("failed to %s %s on %s (%s)", failure.action, s.id, failure.source, reason)
+ if r, ok := s.failureMap[failure]; ok && r == reason {
+ plog.Debugf(logline)
+ return
+ }
+ s.failureMap[failure] = reason
+ plog.Errorf(logline)
+}
+
+func (s *peerStatus) isActive() bool {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ return s.active
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/peer_test.go b/vendor/github.com/coreos/etcd/rafthttp/peer_test.go
new file mode 100644
index 000000000..1b5dd66df
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/peer_test.go
@@ -0,0 +1,87 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "testing"
+
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func TestPeerPick(t *testing.T) {
+ tests := []struct {
+ msgappWorking bool
+ messageWorking bool
+ m raftpb.Message
+ wpicked string
+ }{
+ {
+ true, true,
+ raftpb.Message{Type: raftpb.MsgSnap},
+ pipelineMsg,
+ },
+ {
+ true, true,
+ raftpb.Message{Type: raftpb.MsgApp, Term: 1, LogTerm: 1},
+ streamAppV2,
+ },
+ {
+ true, true,
+ raftpb.Message{Type: raftpb.MsgProp},
+ streamMsg,
+ },
+ {
+ true, true,
+ raftpb.Message{Type: raftpb.MsgHeartbeat},
+ streamMsg,
+ },
+ {
+ false, true,
+ raftpb.Message{Type: raftpb.MsgApp, Term: 1, LogTerm: 1},
+ streamMsg,
+ },
+ {
+ false, false,
+ raftpb.Message{Type: raftpb.MsgApp, Term: 1, LogTerm: 1},
+ pipelineMsg,
+ },
+ {
+ false, false,
+ raftpb.Message{Type: raftpb.MsgProp},
+ pipelineMsg,
+ },
+ {
+ false, false,
+ raftpb.Message{Type: raftpb.MsgSnap},
+ pipelineMsg,
+ },
+ {
+ false, false,
+ raftpb.Message{Type: raftpb.MsgHeartbeat},
+ pipelineMsg,
+ },
+ }
+ for i, tt := range tests {
+ peer := &peer{
+ msgAppV2Writer: &streamWriter{working: tt.msgappWorking},
+ writer: &streamWriter{working: tt.messageWorking},
+ pipeline: &pipeline{},
+ }
+ _, picked := peer.pick(tt.m)
+ if picked != tt.wpicked {
+ t.Errorf("#%d: picked = %v, want %v", i, picked, tt.wpicked)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/pipeline.go b/vendor/github.com/coreos/etcd/rafthttp/pipeline.go
new file mode 100644
index 000000000..90b747cdd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/pipeline.go
@@ -0,0 +1,172 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "bytes"
+ "errors"
+ "io/ioutil"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/httputil"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+const (
+ connPerPipeline = 4
+ // pipelineBufSize is the size of pipeline buffer, which helps hold the
+ // temporary network latency.
+ // The size ensures that pipeline does not drop messages when the network
+ // is out of work for less than 1 second in good path.
+ pipelineBufSize = 64
+)
+
+var errStopped = errors.New("stopped")
+
+type pipeline struct {
+ from, to types.ID
+ cid types.ID
+
+ tr http.RoundTripper
+ picker *urlPicker
+ status *peerStatus
+ fs *stats.FollowerStats
+ r Raft
+ errorc chan error
+
+ msgc chan raftpb.Message
+ // wait for the handling routines
+ wg sync.WaitGroup
+ stopc chan struct{}
+}
+
+func newPipeline(tr http.RoundTripper, picker *urlPicker, from, to, cid types.ID, status *peerStatus, fs *stats.FollowerStats, r Raft, errorc chan error) *pipeline {
+ p := &pipeline{
+ from: from,
+ to: to,
+ cid: cid,
+ tr: tr,
+ picker: picker,
+ status: status,
+ fs: fs,
+ r: r,
+ errorc: errorc,
+ stopc: make(chan struct{}),
+ msgc: make(chan raftpb.Message, pipelineBufSize),
+ }
+ p.wg.Add(connPerPipeline)
+ for i := 0; i < connPerPipeline; i++ {
+ go p.handle()
+ }
+ return p
+}
+
+func (p *pipeline) stop() {
+ close(p.msgc)
+ close(p.stopc)
+ p.wg.Wait()
+}
+
+func (p *pipeline) handle() {
+ defer p.wg.Done()
+ for m := range p.msgc {
+ start := time.Now()
+ err := p.post(pbutil.MustMarshal(&m))
+ if err == errStopped {
+ return
+ }
+ end := time.Now()
+
+ if err != nil {
+ p.status.deactivate(failureType{source: pipelineMsg, action: "write"}, err.Error())
+
+ reportSentFailure(pipelineMsg, m)
+ if m.Type == raftpb.MsgApp && p.fs != nil {
+ p.fs.Fail()
+ }
+ p.r.ReportUnreachable(m.To)
+ if isMsgSnap(m) {
+ p.r.ReportSnapshot(m.To, raft.SnapshotFailure)
+ }
+ return
+ }
+
+ p.status.activate()
+ if m.Type == raftpb.MsgApp && p.fs != nil {
+ p.fs.Succ(end.Sub(start))
+ }
+ if isMsgSnap(m) {
+ p.r.ReportSnapshot(m.To, raft.SnapshotFinish)
+ }
+ reportSentDuration(pipelineMsg, m, time.Since(start))
+ }
+}
+
+// post POSTs a data payload to a url. Returns nil if the POST succeeds,
+// error on any failure.
+func (p *pipeline) post(data []byte) (err error) {
+ u := p.picker.pick()
+ req := createPostRequest(u, RaftPrefix, bytes.NewBuffer(data), "application/protobuf", p.from, p.cid)
+
+ var stopped bool
+ defer func() {
+ if stopped {
+ // rewrite to errStopped so the caller goroutine can stop itself
+ err = errStopped
+ }
+ }()
+ done := make(chan struct{}, 1)
+ cancel := httputil.RequestCanceler(p.tr, req)
+ go func() {
+ select {
+ case <-done:
+ case <-p.stopc:
+ waitSchedule()
+ stopped = true
+ cancel()
+ }
+ }()
+
+ resp, err := p.tr.RoundTrip(req)
+ done <- struct{}{}
+ if err != nil {
+ p.picker.unreachable(u)
+ return err
+ }
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ p.picker.unreachable(u)
+ return err
+ }
+ resp.Body.Close()
+
+ err = checkPostResponse(resp, b, req, p.to)
+ // errMemberRemoved is a critical error since a removed member should
+ // always be stopped. So we use reportCriticalError to report it to errorc.
+ if err == errMemberRemoved {
+ reportCriticalError(err, p.errorc)
+ return nil
+ }
+ return err
+}
+
+// waitSchedule waits other goroutines to be scheduled for a while
+func waitSchedule() { time.Sleep(time.Millisecond) }
diff --git a/vendor/github.com/coreos/etcd/rafthttp/pipeline_test.go b/vendor/github.com/coreos/etcd/rafthttp/pipeline_test.go
new file mode 100644
index 000000000..86e5bf9f7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/pipeline_test.go
@@ -0,0 +1,271 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "errors"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+// TestPipelineSend tests that pipeline could send data using roundtripper
+// and increase success count in stats.
+func TestPipelineSend(t *testing.T) {
+ tr := &roundTripperRecorder{}
+ picker := mustNewURLPicker(t, []string{"http://localhost:2380"})
+ fs := &stats.FollowerStats{}
+ p := newPipeline(tr, picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), fs, &fakeRaft{}, nil)
+
+ p.msgc <- raftpb.Message{Type: raftpb.MsgApp}
+ testutil.WaitSchedule()
+ p.stop()
+
+ if tr.Request() == nil {
+ t.Errorf("sender fails to post the data")
+ }
+ fs.Lock()
+ defer fs.Unlock()
+ if fs.Counts.Success != 1 {
+ t.Errorf("success = %d, want 1", fs.Counts.Success)
+ }
+}
+
+func TestPipelineExceedMaximumServing(t *testing.T) {
+ tr := newRoundTripperBlocker()
+ picker := mustNewURLPicker(t, []string{"http://localhost:2380"})
+ fs := &stats.FollowerStats{}
+ p := newPipeline(tr, picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), fs, &fakeRaft{}, nil)
+
+ // keep the sender busy and make the buffer full
+ // nothing can go out as we block the sender
+ testutil.WaitSchedule()
+ for i := 0; i < connPerPipeline+pipelineBufSize; i++ {
+ select {
+ case p.msgc <- raftpb.Message{}:
+ default:
+ t.Errorf("failed to send out message")
+ }
+ // force the sender to grab data
+ testutil.WaitSchedule()
+ }
+
+ // try to send a data when we are sure the buffer is full
+ select {
+ case p.msgc <- raftpb.Message{}:
+ t.Errorf("unexpected message sendout")
+ default:
+ }
+
+ // unblock the senders and force them to send out the data
+ tr.unblock()
+ testutil.WaitSchedule()
+
+ // It could send new data after previous ones succeed
+ select {
+ case p.msgc <- raftpb.Message{}:
+ default:
+ t.Errorf("failed to send out message")
+ }
+ p.stop()
+}
+
+// TestPipelineSendFailed tests that when send func meets the post error,
+// it increases fail count in stats.
+func TestPipelineSendFailed(t *testing.T) {
+ picker := mustNewURLPicker(t, []string{"http://localhost:2380"})
+ fs := &stats.FollowerStats{}
+ p := newPipeline(newRespRoundTripper(0, errors.New("blah")), picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), fs, &fakeRaft{}, nil)
+
+ p.msgc <- raftpb.Message{Type: raftpb.MsgApp}
+ testutil.WaitSchedule()
+ p.stop()
+
+ fs.Lock()
+ defer fs.Unlock()
+ if fs.Counts.Fail != 1 {
+ t.Errorf("fail = %d, want 1", fs.Counts.Fail)
+ }
+}
+
+func TestPipelinePost(t *testing.T) {
+ tr := &roundTripperRecorder{}
+ picker := mustNewURLPicker(t, []string{"http://localhost:2380"})
+ p := newPipeline(tr, picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), nil, &fakeRaft{}, nil)
+ if err := p.post([]byte("some data")); err != nil {
+ t.Fatalf("unexpect post error: %v", err)
+ }
+ p.stop()
+
+ if g := tr.Request().Method; g != "POST" {
+ t.Errorf("method = %s, want %s", g, "POST")
+ }
+ if g := tr.Request().URL.String(); g != "http://localhost:2380/raft" {
+ t.Errorf("url = %s, want %s", g, "http://localhost:2380/raft")
+ }
+ if g := tr.Request().Header.Get("Content-Type"); g != "application/protobuf" {
+ t.Errorf("content type = %s, want %s", g, "application/protobuf")
+ }
+ if g := tr.Request().Header.Get("X-Server-Version"); g != version.Version {
+ t.Errorf("version = %s, want %s", g, version.Version)
+ }
+ if g := tr.Request().Header.Get("X-Min-Cluster-Version"); g != version.MinClusterVersion {
+ t.Errorf("min version = %s, want %s", g, version.MinClusterVersion)
+ }
+ if g := tr.Request().Header.Get("X-Etcd-Cluster-ID"); g != "1" {
+ t.Errorf("cluster id = %s, want %s", g, "1")
+ }
+ b, err := ioutil.ReadAll(tr.Request().Body)
+ if err != nil {
+ t.Fatalf("unexpected ReadAll error: %v", err)
+ }
+ if string(b) != "some data" {
+ t.Errorf("body = %s, want %s", b, "some data")
+ }
+}
+
+func TestPipelinePostBad(t *testing.T) {
+ tests := []struct {
+ u string
+ code int
+ err error
+ }{
+ // RoundTrip returns error
+ {"http://localhost:2380", 0, errors.New("blah")},
+ // unexpected response status code
+ {"http://localhost:2380", http.StatusOK, nil},
+ {"http://localhost:2380", http.StatusCreated, nil},
+ }
+ for i, tt := range tests {
+ picker := mustNewURLPicker(t, []string{tt.u})
+ p := newPipeline(newRespRoundTripper(tt.code, tt.err), picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), nil, &fakeRaft{}, make(chan error))
+ err := p.post([]byte("some data"))
+ p.stop()
+
+ if err == nil {
+ t.Errorf("#%d: err = nil, want not nil", i)
+ }
+ }
+}
+
+func TestPipelinePostErrorc(t *testing.T) {
+ tests := []struct {
+ u string
+ code int
+ err error
+ }{
+ {"http://localhost:2380", http.StatusForbidden, nil},
+ }
+ for i, tt := range tests {
+ picker := mustNewURLPicker(t, []string{tt.u})
+ errorc := make(chan error, 1)
+ p := newPipeline(newRespRoundTripper(tt.code, tt.err), picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), nil, &fakeRaft{}, errorc)
+ p.post([]byte("some data"))
+ p.stop()
+ select {
+ case <-errorc:
+ default:
+ t.Fatalf("#%d: cannot receive from errorc", i)
+ }
+ }
+}
+
+func TestStopBlockedPipeline(t *testing.T) {
+ picker := mustNewURLPicker(t, []string{"http://localhost:2380"})
+ p := newPipeline(newRoundTripperBlocker(), picker, types.ID(2), types.ID(1), types.ID(1), newPeerStatus(types.ID(1)), nil, &fakeRaft{}, nil)
+ // send many messages that most of them will be blocked in buffer
+ for i := 0; i < connPerPipeline*10; i++ {
+ p.msgc <- raftpb.Message{}
+ }
+
+ done := make(chan struct{})
+ go func() {
+ p.stop()
+ done <- struct{}{}
+ }()
+ select {
+ case <-done:
+ case <-time.After(time.Second):
+ t.Fatalf("failed to stop pipeline in 1s")
+ }
+}
+
+type roundTripperBlocker struct {
+ unblockc chan struct{}
+ mu sync.Mutex
+ cancel map[*http.Request]chan struct{}
+}
+
+func newRoundTripperBlocker() *roundTripperBlocker {
+ return &roundTripperBlocker{
+ unblockc: make(chan struct{}),
+ cancel: make(map[*http.Request]chan struct{}),
+ }
+}
+func (t *roundTripperBlocker) unblock() {
+ close(t.unblockc)
+}
+func (t *roundTripperBlocker) CancelRequest(req *http.Request) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if c, ok := t.cancel[req]; ok {
+ c <- struct{}{}
+ delete(t.cancel, req)
+ }
+}
+
+type respRoundTripper struct {
+ code int
+ header http.Header
+ err error
+}
+
+func newRespRoundTripper(code int, err error) *respRoundTripper {
+ return &respRoundTripper{code: code, err: err}
+}
+func (t *respRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+ return &http.Response{StatusCode: t.code, Header: t.header, Body: &nopReadCloser{}}, t.err
+}
+
+type roundTripperRecorder struct {
+ req *http.Request
+ sync.Mutex
+}
+
+func (t *roundTripperRecorder) RoundTrip(req *http.Request) (*http.Response, error) {
+ t.Lock()
+ defer t.Unlock()
+ t.req = req
+ return &http.Response{StatusCode: http.StatusNoContent, Body: &nopReadCloser{}}, nil
+}
+func (t *roundTripperRecorder) Request() *http.Request {
+ t.Lock()
+ defer t.Unlock()
+ return t.req
+}
+
+type nopReadCloser struct{}
+
+func (n *nopReadCloser) Read(p []byte) (int, error) { return 0, io.EOF }
+func (n *nopReadCloser) Close() error { return nil }
diff --git a/vendor/github.com/coreos/etcd/rafthttp/probing_status.go b/vendor/github.com/coreos/etcd/rafthttp/probing_status.go
new file mode 100644
index 000000000..ed042a683
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/probing_status.go
@@ -0,0 +1,60 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing"
+)
+
+var (
+ // proberInterval must be shorter than read timeout.
+ // Or the connection will time-out.
+ proberInterval = ConnReadTimeout - time.Second
+ statusMonitoringInterval = 30 * time.Second
+)
+
+func addPeerToProber(p probing.Prober, id string, us []string) {
+ hus := make([]string, len(us))
+ for i := range us {
+ hus[i] = us[i] + ProbingPrefix
+ }
+
+ p.AddHTTP(id, proberInterval, hus)
+
+ s, err := p.Status(id)
+ if err != nil {
+ plog.Errorf("failed to add peer %s into prober", id)
+ } else {
+ go monitorProbingStatus(s, id)
+ }
+}
+
+func monitorProbingStatus(s probing.Status, id string) {
+ for {
+ select {
+ case <-time.After(statusMonitoringInterval):
+ if !s.Health() {
+ plog.Warningf("the connection to peer %s is unhealthy", id)
+ }
+ if s.ClockDiff() > time.Second {
+ plog.Warningf("the clock difference against peer %s is too high [%v > %v]", id, s.ClockDiff(), time.Second)
+ }
+ case <-s.StopNotify():
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/remote.go b/vendor/github.com/coreos/etcd/rafthttp/remote.go
new file mode 100644
index 000000000..9b3bfcd36
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/remote.go
@@ -0,0 +1,54 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/http"
+
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+type remote struct {
+ id types.ID
+ status *peerStatus
+ pipeline *pipeline
+}
+
+func startRemote(tr http.RoundTripper, urls types.URLs, local, to, cid types.ID, r Raft, errorc chan error) *remote {
+ picker := newURLPicker(urls)
+ status := newPeerStatus(to)
+ return &remote{
+ id: to,
+ status: status,
+ pipeline: newPipeline(tr, picker, local, to, cid, status, nil, r, errorc),
+ }
+}
+
+func (g *remote) send(m raftpb.Message) {
+ select {
+ case g.pipeline.msgc <- m:
+ default:
+ if g.status.isActive() {
+ plog.Warningf("dropped %s to %s since sending buffer is full", m.Type, g.id)
+ } else {
+ plog.Debugf("dropped %s to %s since sending buffer is full", m.Type, g.id)
+ }
+ }
+}
+
+func (g *remote) stop() {
+ g.pipeline.stop()
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/snapshot_sender.go b/vendor/github.com/coreos/etcd/rafthttp/snapshot_sender.go
new file mode 100644
index 000000000..bf297505d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/snapshot_sender.go
@@ -0,0 +1,161 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "time"
+
+ "github.com/coreos/etcd/pkg/httputil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+type snapshotSender struct {
+ from, to types.ID
+ cid types.ID
+
+ tr http.RoundTripper
+ picker *urlPicker
+ status *peerStatus
+ snapst *snapshotStore
+ r Raft
+ errorc chan error
+
+ stopc chan struct{}
+}
+
+func newSnapshotSender(tr http.RoundTripper, picker *urlPicker, from, to, cid types.ID, status *peerStatus, snapst *snapshotStore, r Raft, errorc chan error) *snapshotSender {
+ return &snapshotSender{
+ from: from,
+ to: to,
+ cid: cid,
+ tr: tr,
+ picker: picker,
+ status: status,
+ snapst: snapst,
+ r: r,
+ errorc: errorc,
+ stopc: make(chan struct{}),
+ }
+}
+
+func (s *snapshotSender) stop() { close(s.stopc) }
+
+func (s *snapshotSender) send(m raftpb.Message) {
+ start := time.Now()
+
+ body := createSnapBody(m, s.snapst)
+ defer body.Close()
+
+ u := s.picker.pick()
+ req := createPostRequest(u, RaftSnapshotPrefix, body, "application/octet-stream", s.from, s.cid)
+
+ err := s.post(req)
+ if err != nil {
+ // errMemberRemoved is a critical error since a removed member should
+ // always be stopped. So we use reportCriticalError to report it to errorc.
+ if err == errMemberRemoved {
+ reportCriticalError(err, s.errorc)
+ }
+ s.picker.unreachable(u)
+ reportSentFailure(sendSnap, m)
+ s.status.deactivate(failureType{source: sendSnap, action: "post"}, err.Error())
+ s.r.ReportUnreachable(m.To)
+ // report SnapshotFailure to raft state machine. After raft state
+ // machine knows about it, it would pause a while and retry sending
+ // new snapshot message.
+ s.r.ReportSnapshot(m.To, raft.SnapshotFailure)
+ if s.status.isActive() {
+ plog.Warningf("snapshot [index: %d, to: %s] failed to be sent out (%v)", m.Snapshot.Metadata.Index, types.ID(m.To), err)
+ } else {
+ plog.Debugf("snapshot [index: %d, to: %s] failed to be sent out (%v)", m.Snapshot.Metadata.Index, types.ID(m.To), err)
+ }
+ return
+ }
+ reportSentDuration(sendSnap, m, time.Since(start))
+ s.status.activate()
+ s.r.ReportSnapshot(m.To, raft.SnapshotFinish)
+ plog.Infof("snapshot [index: %d, to: %s] sent out successfully", m.Snapshot.Metadata.Index, types.ID(m.To))
+}
+
+// post posts the given request.
+// It returns nil when request is sent out and processed successfully.
+func (s *snapshotSender) post(req *http.Request) (err error) {
+ cancel := httputil.RequestCanceler(s.tr, req)
+
+ type responseAndError struct {
+ resp *http.Response
+ body []byte
+ err error
+ }
+ result := make(chan responseAndError, 1)
+
+ go func() {
+ // TODO: cancel the request if it has waited for a long time(~5s) after
+ // it has write out the full request body, which helps to avoid receiver
+ // dies when sender is waiting for response
+ // TODO: the snapshot could be large and eat up all resources when writing
+ // it out. Send it block by block and rest some time between to give the
+ // time for main loop to run.
+ resp, err := s.tr.RoundTrip(req)
+ if err != nil {
+ result <- responseAndError{resp, nil, err}
+ return
+ }
+ body, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ result <- responseAndError{resp, body, err}
+ }()
+
+ select {
+ case <-s.stopc:
+ cancel()
+ return errStopped
+ case r := <-result:
+ if r.err != nil {
+ return r.err
+ }
+ return checkPostResponse(r.resp, r.body, req, s.to)
+ }
+}
+
+// readCloser implements io.ReadCloser interface.
+type readCloser struct {
+ io.Reader
+ io.Closer
+}
+
+// createSnapBody creates the request body for the given raft snapshot message.
+// Callers should close body when done reading from it.
+func createSnapBody(m raftpb.Message, snapst *snapshotStore) io.ReadCloser {
+ buf := new(bytes.Buffer)
+ enc := &messageEncoder{w: buf}
+ // encode raft message
+ if err := enc.encode(m); err != nil {
+ plog.Panicf("encode message error (%v)", err)
+ }
+ // get snapshot
+ rc := snapst.get(m.Snapshot.Metadata.Index)
+
+ return &readCloser{
+ Reader: io.MultiReader(buf, rc),
+ Closer: rc,
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/snapshot_store.go b/vendor/github.com/coreos/etcd/rafthttp/snapshot_store.go
new file mode 100644
index 000000000..239a0ed55
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/snapshot_store.go
@@ -0,0 +1,45 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "io"
+)
+
+// snapshotStore is the store of snapshot. Caller could put one
+// snapshot into the store, and get it later.
+// snapshotStore stores at most one snapshot at a time, or it panics.
+type snapshotStore struct {
+ rc io.ReadCloser
+ // index of the stored snapshot
+ // index is 0 if and only if there is no snapshot stored.
+ index uint64
+}
+
+func (s *snapshotStore) put(rc io.ReadCloser, index uint64) {
+ if s.index != 0 {
+ plog.Panicf("unexpected put when there is one snapshot stored")
+ }
+ s.rc, s.index = rc, index
+}
+
+func (s *snapshotStore) get(index uint64) io.ReadCloser {
+ if s.index == index {
+ // set index to 0 to indicate no snapshot stored
+ s.index = 0
+ return s.rc
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/stream.go b/vendor/github.com/coreos/etcd/rafthttp/stream.go
new file mode 100644
index 000000000..566806cc7
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/stream.go
@@ -0,0 +1,464 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "path"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/httputil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+const (
+ streamTypeMessage streamType = "message"
+ streamTypeMsgAppV2 streamType = "msgappv2"
+
+ streamBufSize = 4096
+)
+
+var (
+ errUnsupportedStreamType = fmt.Errorf("unsupported stream type")
+
+ // the key is in string format "major.minor.patch"
+ supportedStream = map[string][]streamType{
+ "2.0.0": {},
+ "2.1.0": {streamTypeMsgAppV2, streamTypeMessage},
+ "2.2.0": {streamTypeMsgAppV2, streamTypeMessage},
+ }
+)
+
+type streamType string
+
+func (t streamType) endpoint() string {
+ switch t {
+ case streamTypeMsgAppV2:
+ return path.Join(RaftStreamPrefix, "msgapp")
+ case streamTypeMessage:
+ return path.Join(RaftStreamPrefix, "message")
+ default:
+ plog.Panicf("unhandled stream type %v", t)
+ return ""
+ }
+}
+
+func (t streamType) String() string {
+ switch t {
+ case streamTypeMsgAppV2:
+ return "stream MsgApp v2"
+ case streamTypeMessage:
+ return "stream Message"
+ default:
+ return "unknown stream"
+ }
+}
+
+var (
+ // linkHeartbeatMessage is a special message used as heartbeat message in
+ // link layer. It never conflicts with messages from raft because raft
+ // doesn't send out messages without From and To fields.
+ linkHeartbeatMessage = raftpb.Message{Type: raftpb.MsgHeartbeat}
+)
+
+func isLinkHeartbeatMessage(m raftpb.Message) bool {
+ return m.Type == raftpb.MsgHeartbeat && m.From == 0 && m.To == 0
+}
+
+type outgoingConn struct {
+ t streamType
+ io.Writer
+ http.Flusher
+ io.Closer
+}
+
+// streamWriter is a long-running go-routine that writes messages into the
+// attached outgoingConn.
+type streamWriter struct {
+ id types.ID
+ status *peerStatus
+ fs *stats.FollowerStats
+ r Raft
+
+ mu sync.Mutex // guard field working and closer
+ closer io.Closer
+ working bool
+
+ msgc chan raftpb.Message
+ connc chan *outgoingConn
+ stopc chan struct{}
+ done chan struct{}
+}
+
+func startStreamWriter(id types.ID, status *peerStatus, fs *stats.FollowerStats, r Raft) *streamWriter {
+ w := &streamWriter{
+ id: id,
+ status: status,
+ fs: fs,
+ r: r,
+ msgc: make(chan raftpb.Message, streamBufSize),
+ connc: make(chan *outgoingConn),
+ stopc: make(chan struct{}),
+ done: make(chan struct{}),
+ }
+ go w.run()
+ return w
+}
+
+func (cw *streamWriter) run() {
+ var msgc chan raftpb.Message
+ var heartbeatc <-chan time.Time
+ var t streamType
+ var enc encoder
+ var flusher http.Flusher
+ tickc := time.Tick(ConnReadTimeout / 3)
+
+ for {
+ select {
+ case <-heartbeatc:
+ start := time.Now()
+ if err := enc.encode(linkHeartbeatMessage); err != nil {
+ reportSentFailure(string(t), linkHeartbeatMessage)
+
+ cw.status.deactivate(failureType{source: t.String(), action: "heartbeat"}, err.Error())
+ cw.close()
+ heartbeatc, msgc = nil, nil
+ continue
+ }
+ flusher.Flush()
+ reportSentDuration(string(t), linkHeartbeatMessage, time.Since(start))
+ case m := <-msgc:
+ start := time.Now()
+ if err := enc.encode(m); err != nil {
+ reportSentFailure(string(t), m)
+
+ cw.status.deactivate(failureType{source: t.String(), action: "write"}, err.Error())
+ cw.close()
+ heartbeatc, msgc = nil, nil
+ cw.r.ReportUnreachable(m.To)
+ continue
+ }
+ flusher.Flush()
+ reportSentDuration(string(t), m, time.Since(start))
+ case conn := <-cw.connc:
+ cw.close()
+ t = conn.t
+ switch conn.t {
+ case streamTypeMsgAppV2:
+ enc = newMsgAppV2Encoder(conn.Writer, cw.fs)
+ case streamTypeMessage:
+ enc = &messageEncoder{w: conn.Writer}
+ default:
+ plog.Panicf("unhandled stream type %s", conn.t)
+ }
+ flusher = conn.Flusher
+ cw.mu.Lock()
+ cw.status.activate()
+ cw.closer = conn.Closer
+ cw.working = true
+ cw.mu.Unlock()
+ heartbeatc, msgc = tickc, cw.msgc
+ case <-cw.stopc:
+ cw.close()
+ close(cw.done)
+ return
+ }
+ }
+}
+
+func (cw *streamWriter) writec() (chan<- raftpb.Message, bool) {
+ cw.mu.Lock()
+ defer cw.mu.Unlock()
+ return cw.msgc, cw.working
+}
+
+func (cw *streamWriter) close() {
+ cw.mu.Lock()
+ defer cw.mu.Unlock()
+ if !cw.working {
+ return
+ }
+ cw.closer.Close()
+ if len(cw.msgc) > 0 {
+ cw.r.ReportUnreachable(uint64(cw.id))
+ }
+ cw.msgc = make(chan raftpb.Message, streamBufSize)
+ cw.working = false
+}
+
+func (cw *streamWriter) attach(conn *outgoingConn) bool {
+ select {
+ case cw.connc <- conn:
+ return true
+ case <-cw.done:
+ return false
+ }
+}
+
+func (cw *streamWriter) stop() {
+ close(cw.stopc)
+ <-cw.done
+}
+
+// streamReader is a long-running go-routine that dials to the remote stream
+// endponit and reads messages from the response body returned.
+type streamReader struct {
+ tr http.RoundTripper
+ picker *urlPicker
+ t streamType
+ local, remote types.ID
+ cid types.ID
+ status *peerStatus
+ recvc chan<- raftpb.Message
+ propc chan<- raftpb.Message
+ errorc chan<- error
+
+ mu sync.Mutex
+ cancel func()
+ closer io.Closer
+ stopc chan struct{}
+ done chan struct{}
+}
+
+func startStreamReader(tr http.RoundTripper, picker *urlPicker, t streamType, local, remote, cid types.ID, status *peerStatus, recvc chan<- raftpb.Message, propc chan<- raftpb.Message, errorc chan<- error) *streamReader {
+ r := &streamReader{
+ tr: tr,
+ picker: picker,
+ t: t,
+ local: local,
+ remote: remote,
+ cid: cid,
+ status: status,
+ recvc: recvc,
+ propc: propc,
+ errorc: errorc,
+ stopc: make(chan struct{}),
+ done: make(chan struct{}),
+ }
+ go r.run()
+ return r
+}
+
+func (cr *streamReader) run() {
+ for {
+ t := cr.t
+ rc, err := cr.dial(t)
+ if err != nil {
+ if err != errUnsupportedStreamType {
+ cr.status.deactivate(failureType{source: t.String(), action: "dial"}, err.Error())
+ }
+ } else {
+ cr.status.activate()
+ err := cr.decodeLoop(rc, t)
+ switch {
+ // all data is read out
+ case err == io.EOF:
+ // connection is closed by the remote
+ case isClosedConnectionError(err):
+ default:
+ cr.status.deactivate(failureType{source: t.String(), action: "read"}, err.Error())
+ }
+ }
+ select {
+ // Wait 100ms to create a new stream, so it doesn't bring too much
+ // overhead when retry.
+ case <-time.After(100 * time.Millisecond):
+ case <-cr.stopc:
+ close(cr.done)
+ return
+ }
+ }
+}
+
+func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
+ var dec decoder
+ cr.mu.Lock()
+ switch t {
+ case streamTypeMsgAppV2:
+ dec = newMsgAppV2Decoder(rc, cr.local, cr.remote)
+ case streamTypeMessage:
+ dec = &messageDecoder{r: rc}
+ default:
+ plog.Panicf("unhandled stream type %s", t)
+ }
+ cr.closer = rc
+ cr.mu.Unlock()
+
+ for {
+ m, err := dec.decode()
+ if err != nil {
+ cr.mu.Lock()
+ cr.close()
+ cr.mu.Unlock()
+ return err
+ }
+
+ if isLinkHeartbeatMessage(m) {
+ // raft is not interested in link layer
+ // heartbeat message, so we should ignore
+ // it.
+ continue
+ }
+
+ recvc := cr.recvc
+ if m.Type == raftpb.MsgProp {
+ recvc = cr.propc
+ }
+
+ select {
+ case recvc <- m:
+ default:
+ if cr.status.isActive() {
+ plog.Warningf("dropped %s from %s since receiving buffer is full", m.Type, types.ID(m.From))
+ } else {
+ plog.Debugf("dropped %s from %s since receiving buffer is full", m.Type, types.ID(m.From))
+ }
+ }
+ }
+}
+
+func (cr *streamReader) stop() {
+ close(cr.stopc)
+ cr.mu.Lock()
+ if cr.cancel != nil {
+ cr.cancel()
+ }
+ cr.close()
+ cr.mu.Unlock()
+ <-cr.done
+}
+
+func (cr *streamReader) isWorking() bool {
+ cr.mu.Lock()
+ defer cr.mu.Unlock()
+ return cr.closer != nil
+}
+
+func (cr *streamReader) dial(t streamType) (io.ReadCloser, error) {
+ u := cr.picker.pick()
+ uu := u
+ uu.Path = path.Join(t.endpoint(), cr.local.String())
+
+ req, err := http.NewRequest("GET", uu.String(), nil)
+ if err != nil {
+ cr.picker.unreachable(u)
+ return nil, fmt.Errorf("failed to make http request to %s (%v)", u, err)
+ }
+ req.Header.Set("X-Server-From", cr.local.String())
+ req.Header.Set("X-Server-Version", version.Version)
+ req.Header.Set("X-Min-Cluster-Version", version.MinClusterVersion)
+ req.Header.Set("X-Etcd-Cluster-ID", cr.cid.String())
+ req.Header.Set("X-Raft-To", cr.remote.String())
+
+ cr.mu.Lock()
+ select {
+ case <-cr.stopc:
+ cr.mu.Unlock()
+ return nil, fmt.Errorf("stream reader is stopped")
+ default:
+ }
+ cr.cancel = httputil.RequestCanceler(cr.tr, req)
+ cr.mu.Unlock()
+
+ resp, err := cr.tr.RoundTrip(req)
+ if err != nil {
+ cr.picker.unreachable(u)
+ return nil, err
+ }
+
+ rv := serverVersion(resp.Header)
+ lv := semver.Must(semver.NewVersion(version.Version))
+ if compareMajorMinorVersion(rv, lv) == -1 && !checkStreamSupport(rv, t) {
+ resp.Body.Close()
+ return nil, errUnsupportedStreamType
+ }
+
+ switch resp.StatusCode {
+ case http.StatusGone:
+ resp.Body.Close()
+ err := fmt.Errorf("the member has been permanently removed from the cluster")
+ select {
+ case cr.errorc <- err:
+ default:
+ }
+ return nil, err
+ case http.StatusOK:
+ return resp.Body, nil
+ case http.StatusNotFound:
+ resp.Body.Close()
+ return nil, fmt.Errorf("remote member %s could not recognize local member", cr.remote)
+ case http.StatusPreconditionFailed:
+ b, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ cr.picker.unreachable(u)
+ return nil, err
+ }
+ resp.Body.Close()
+
+ switch strings.TrimSuffix(string(b), "\n") {
+ case errIncompatibleVersion.Error():
+ plog.Errorf("request sent was ignored by peer %s (server version incompatible)", cr.remote)
+ return nil, errIncompatibleVersion
+ case errClusterIDMismatch.Error():
+ plog.Errorf("request sent was ignored (cluster ID mismatch: remote[%s]=%s, local=%s)",
+ cr.remote, resp.Header.Get("X-Etcd-Cluster-ID"), cr.cid)
+ return nil, errClusterIDMismatch
+ default:
+ return nil, fmt.Errorf("unhandled error %q when precondition failed", string(b))
+ }
+ default:
+ resp.Body.Close()
+ return nil, fmt.Errorf("unhandled http status %d", resp.StatusCode)
+ }
+}
+
+func (cr *streamReader) close() {
+ if cr.closer != nil {
+ cr.closer.Close()
+ }
+ cr.closer = nil
+}
+
+func isClosedConnectionError(err error) bool {
+ operr, ok := err.(*net.OpError)
+ return ok && operr.Err.Error() == "use of closed network connection"
+}
+
+// checkStreamSupport checks whether the stream type is supported in the
+// given version.
+func checkStreamSupport(v *semver.Version, t streamType) bool {
+ nv := &semver.Version{Major: v.Major, Minor: v.Minor}
+ for _, s := range supportedStream[nv.String()] {
+ if s == t {
+ return true
+ }
+ }
+ return false
+}
+
+func isNetworkTimeoutError(err error) bool {
+ nerr, ok := err.(net.Error)
+ return ok && nerr.Timeout()
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/stream_test.go b/vendor/github.com/coreos/etcd/rafthttp/stream_test.go
new file mode 100644
index 000000000..3dfa4ced4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/stream_test.go
@@ -0,0 +1,331 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+// TestStreamWriterAttachOutgoingConn tests that outgoingConn can be attached
+// to streamWriter. After that, streamWriter can use it to send messages
+// continuously, and closes it when stopped.
+func TestStreamWriterAttachOutgoingConn(t *testing.T) {
+ sw := startStreamWriter(types.ID(1), newPeerStatus(types.ID(1)), &stats.FollowerStats{}, &fakeRaft{})
+ // the expected initial state of streamWrite is not working
+ if _, ok := sw.writec(); ok != false {
+ t.Errorf("initial working status = %v, want false", ok)
+ }
+
+ // repeatitive tests to ensure it can use latest connection
+ var wfc *fakeWriteFlushCloser
+ for i := 0; i < 3; i++ {
+ prevwfc := wfc
+ wfc = &fakeWriteFlushCloser{}
+ sw.attach(&outgoingConn{t: streamTypeMessage, Writer: wfc, Flusher: wfc, Closer: wfc})
+ testutil.WaitSchedule()
+ // previous attached connection should be closed
+ if prevwfc != nil && prevwfc.closed != true {
+ t.Errorf("#%d: close of previous connection = %v, want true", i, prevwfc.closed)
+ }
+ // starts working
+ if _, ok := sw.writec(); ok != true {
+ t.Errorf("#%d: working status = %v, want true", i, ok)
+ }
+
+ sw.msgc <- raftpb.Message{}
+ testutil.WaitSchedule()
+ // still working
+ if _, ok := sw.writec(); ok != true {
+ t.Errorf("#%d: working status = %v, want true", i, ok)
+ }
+ if wfc.written == 0 {
+ t.Errorf("#%d: failed to write to the underlying connection", i)
+ }
+ }
+
+ sw.stop()
+ // no longer in working status now
+ if _, ok := sw.writec(); ok != false {
+ t.Errorf("working status after stop = %v, want false", ok)
+ }
+ if wfc.closed != true {
+ t.Errorf("failed to close the underlying connection")
+ }
+}
+
+// TestStreamWriterAttachBadOutgoingConn tests that streamWriter with bad
+// outgoingConn will close the outgoingConn and fall back to non-working status.
+func TestStreamWriterAttachBadOutgoingConn(t *testing.T) {
+ sw := startStreamWriter(types.ID(1), newPeerStatus(types.ID(1)), &stats.FollowerStats{}, &fakeRaft{})
+ defer sw.stop()
+ wfc := &fakeWriteFlushCloser{err: errors.New("blah")}
+ sw.attach(&outgoingConn{t: streamTypeMessage, Writer: wfc, Flusher: wfc, Closer: wfc})
+
+ sw.msgc <- raftpb.Message{}
+ testutil.WaitSchedule()
+ // no longer working
+ if _, ok := sw.writec(); ok != false {
+ t.Errorf("working = %v, want false", ok)
+ }
+ if wfc.closed != true {
+ t.Errorf("failed to close the underlying connection")
+ }
+}
+
+func TestStreamReaderDialRequest(t *testing.T) {
+ for i, tt := range []streamType{streamTypeMessage, streamTypeMsgAppV2} {
+ tr := &roundTripperRecorder{}
+ sr := &streamReader{
+ tr: tr,
+ picker: mustNewURLPicker(t, []string{"http://localhost:2380"}),
+ local: types.ID(1),
+ remote: types.ID(2),
+ cid: types.ID(1),
+ }
+ sr.dial(tt)
+
+ req := tr.Request()
+ wurl := fmt.Sprintf("http://localhost:2380" + tt.endpoint() + "/1")
+ if req.URL.String() != wurl {
+ t.Errorf("#%d: url = %s, want %s", i, req.URL.String(), wurl)
+ }
+ if w := "GET"; req.Method != w {
+ t.Errorf("#%d: method = %s, want %s", i, req.Method, w)
+ }
+ if g := req.Header.Get("X-Etcd-Cluster-ID"); g != "1" {
+ t.Errorf("#%d: header X-Etcd-Cluster-ID = %s, want 1", i, g)
+ }
+ if g := req.Header.Get("X-Raft-To"); g != "2" {
+ t.Errorf("#%d: header X-Raft-To = %s, want 2", i, g)
+ }
+ }
+}
+
+// TestStreamReaderDialResult tests the result of the dial func call meets the
+// HTTP response received.
+func TestStreamReaderDialResult(t *testing.T) {
+ tests := []struct {
+ code int
+ err error
+ wok bool
+ whalt bool
+ }{
+ {0, errors.New("blah"), false, false},
+ {http.StatusOK, nil, true, false},
+ {http.StatusMethodNotAllowed, nil, false, false},
+ {http.StatusNotFound, nil, false, false},
+ {http.StatusPreconditionFailed, nil, false, false},
+ {http.StatusGone, nil, false, true},
+ }
+ for i, tt := range tests {
+ h := http.Header{}
+ h.Add("X-Server-Version", version.Version)
+ tr := &respRoundTripper{
+ code: tt.code,
+ header: h,
+ err: tt.err,
+ }
+ sr := &streamReader{
+ tr: tr,
+ picker: mustNewURLPicker(t, []string{"http://localhost:2380"}),
+ local: types.ID(1),
+ remote: types.ID(2),
+ cid: types.ID(1),
+ errorc: make(chan error, 1),
+ }
+
+ _, err := sr.dial(streamTypeMessage)
+ if ok := err == nil; ok != tt.wok {
+ t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok)
+ }
+ if halt := len(sr.errorc) > 0; halt != tt.whalt {
+ t.Errorf("#%d: halt = %v, want %v", i, halt, tt.whalt)
+ }
+ }
+}
+
+// TestStreamReaderDialDetectUnsupport tests that dial func could find
+// out that the stream type is not supported by the remote.
+func TestStreamReaderDialDetectUnsupport(t *testing.T) {
+ for i, typ := range []streamType{streamTypeMsgAppV2, streamTypeMessage} {
+ // the response from etcd 2.0
+ tr := &respRoundTripper{
+ code: http.StatusNotFound,
+ header: http.Header{},
+ }
+ sr := &streamReader{
+ tr: tr,
+ picker: mustNewURLPicker(t, []string{"http://localhost:2380"}),
+ local: types.ID(1),
+ remote: types.ID(2),
+ cid: types.ID(1),
+ }
+
+ _, err := sr.dial(typ)
+ if err != errUnsupportedStreamType {
+ t.Errorf("#%d: error = %v, want %v", i, err, errUnsupportedStreamType)
+ }
+ }
+}
+
+// TestStream tests that streamReader and streamWriter can build stream to
+// send messages between each other.
+func TestStream(t *testing.T) {
+ recvc := make(chan raftpb.Message, streamBufSize)
+ propc := make(chan raftpb.Message, streamBufSize)
+ msgapp := raftpb.Message{
+ Type: raftpb.MsgApp,
+ From: 2,
+ To: 1,
+ Term: 1,
+ LogTerm: 1,
+ Index: 3,
+ Entries: []raftpb.Entry{{Term: 1, Index: 4}},
+ }
+
+ tests := []struct {
+ t streamType
+ m raftpb.Message
+ wc chan raftpb.Message
+ }{
+ {
+ streamTypeMessage,
+ raftpb.Message{Type: raftpb.MsgProp, To: 2},
+ propc,
+ },
+ {
+ streamTypeMessage,
+ msgapp,
+ recvc,
+ },
+ {
+ streamTypeMsgAppV2,
+ msgapp,
+ recvc,
+ },
+ }
+ for i, tt := range tests {
+ h := &fakeStreamHandler{t: tt.t}
+ srv := httptest.NewServer(h)
+ defer srv.Close()
+
+ sw := startStreamWriter(types.ID(1), newPeerStatus(types.ID(1)), &stats.FollowerStats{}, &fakeRaft{})
+ defer sw.stop()
+ h.sw = sw
+
+ picker := mustNewURLPicker(t, []string{srv.URL})
+ sr := startStreamReader(&http.Transport{}, picker, tt.t, types.ID(1), types.ID(2), types.ID(1), newPeerStatus(types.ID(1)), recvc, propc, nil)
+ defer sr.stop()
+ // wait for stream to work
+ var writec chan<- raftpb.Message
+ for {
+ var ok bool
+ if writec, ok = sw.writec(); ok {
+ break
+ }
+ time.Sleep(time.Millisecond)
+ }
+
+ writec <- tt.m
+ var m raftpb.Message
+ select {
+ case m = <-tt.wc:
+ case <-time.After(time.Second):
+ t.Fatalf("#%d: failed to receive message from the channel", i)
+ }
+ if !reflect.DeepEqual(m, tt.m) {
+ t.Fatalf("#%d: message = %+v, want %+v", i, m, tt.m)
+ }
+ }
+}
+
+func TestCheckStreamSupport(t *testing.T) {
+ tests := []struct {
+ v *semver.Version
+ t streamType
+ w bool
+ }{
+ // support
+ {
+ semver.Must(semver.NewVersion("2.1.0")),
+ streamTypeMsgAppV2,
+ true,
+ },
+ // ignore patch
+ {
+ semver.Must(semver.NewVersion("2.1.9")),
+ streamTypeMsgAppV2,
+ true,
+ },
+ // ignore prerelease
+ {
+ semver.Must(semver.NewVersion("2.1.0-alpha")),
+ streamTypeMsgAppV2,
+ true,
+ },
+ }
+ for i, tt := range tests {
+ if g := checkStreamSupport(tt.v, tt.t); g != tt.w {
+ t.Errorf("#%d: check = %v, want %v", i, g, tt.w)
+ }
+ }
+}
+
+type fakeWriteFlushCloser struct {
+ err error
+ written int
+ closed bool
+}
+
+func (wfc *fakeWriteFlushCloser) Write(p []byte) (n int, err error) {
+ wfc.written += len(p)
+ return len(p), wfc.err
+}
+func (wfc *fakeWriteFlushCloser) Flush() {}
+func (wfc *fakeWriteFlushCloser) Close() error {
+ wfc.closed = true
+ return wfc.err
+}
+
+type fakeStreamHandler struct {
+ t streamType
+ sw *streamWriter
+}
+
+func (h *fakeStreamHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("X-Server-Version", version.Version)
+ w.(http.Flusher).Flush()
+ c := newCloseNotifier()
+ h.sw.attach(&outgoingConn{
+ t: h.t,
+ Writer: w,
+ Flusher: w.(http.Flusher),
+ Closer: c,
+ })
+ <-c.closeNotify()
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/transport.go b/vendor/github.com/coreos/etcd/rafthttp/transport.go
new file mode 100644
index 000000000..b6f895f57
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/transport.go
@@ -0,0 +1,318 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "io"
+ "net/http"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing"
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/transport"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+var plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "rafthttp")
+
+type Raft interface {
+ Process(ctx context.Context, m raftpb.Message) error
+ IsIDRemoved(id uint64) bool
+ ReportUnreachable(id uint64)
+ ReportSnapshot(id uint64, status raft.SnapshotStatus)
+}
+
+// SnapshotSaver is the interface that wraps the SaveFrom method.
+type SnapshotSaver interface {
+ // SaveFrom saves the snapshot data at the given index from the given reader.
+ SaveFrom(r io.Reader, index uint64) error
+}
+
+type Transporter interface {
+ // Start starts the given Transporter.
+ // Start MUST be called before calling other functions in the interface.
+ Start() error
+ // Handler returns the HTTP handler of the transporter.
+ // A transporter HTTP handler handles the HTTP requests
+ // from remote peers.
+ // The handler MUST be used to handle RaftPrefix(/raft)
+ // endpoint.
+ Handler() http.Handler
+ // Send sends out the given messages to the remote peers.
+ // Each message has a To field, which is an id that maps
+ // to an existing peer in the transport.
+ // If the id cannot be found in the transport, the message
+ // will be ignored.
+ Send(m []raftpb.Message)
+ // AddRemote adds a remote with given peer urls into the transport.
+ // A remote helps newly joined member to catch up the progress of cluster,
+ // and will not be used after that.
+ // It is the caller's responsibility to ensure the urls are all valid,
+ // or it panics.
+ AddRemote(id types.ID, urls []string)
+ // AddPeer adds a peer with given peer urls into the transport.
+ // It is the caller's responsibility to ensure the urls are all valid,
+ // or it panics.
+ // Peer urls are used to connect to the remote peer.
+ AddPeer(id types.ID, urls []string)
+ // RemovePeer removes the peer with given id.
+ RemovePeer(id types.ID)
+ // RemoveAllPeers removes all the existing peers in the transport.
+ RemoveAllPeers()
+ // UpdatePeer updates the peer urls of the peer with the given id.
+ // It is the caller's responsibility to ensure the urls are all valid,
+ // or it panics.
+ UpdatePeer(id types.ID, urls []string)
+ // ActiveSince returns the time that the connection with the peer
+ // of the given id becomes active.
+ // If the connection is active since peer was added, it returns the adding time.
+ // If the connection is currently inactive, it returns zero time.
+ ActiveSince(id types.ID) time.Time
+ // SnapshotReady accepts a snapshot at the given index that is ready to send out.
+ // It is expected that caller sends a raft snapshot message with
+ // the given index soon, and the accepted snapshot will be sent out
+ // together. After sending, snapshot sent status is reported
+ // through Raft.SnapshotStatus.
+ // SnapshotReady MUST not be called when the snapshot sent status of previous
+ // accepted one has not been reported.
+ SnapshotReady(rc io.ReadCloser, index uint64)
+ // Stop closes the connections and stops the transporter.
+ Stop()
+}
+
+// Transport implements Transporter interface. It provides the functionality
+// to send raft messages to peers, and receive raft messages from peers.
+// User should call Handler method to get a handler to serve requests
+// received from peerURLs.
+// User needs to call Start before calling other functions, and call
+// Stop when the Transport is no longer used.
+type Transport struct {
+ DialTimeout time.Duration // maximum duration before timing out dial of the request
+ TLSInfo transport.TLSInfo // TLS information used when creating connection
+
+ ID types.ID // local member ID
+ ClusterID types.ID // raft cluster ID for request validation
+ Raft Raft // raft state machine, to which the Transport forwards received messages and reports status
+ SnapSaver SnapshotSaver // used to save snapshot in v3 snapshot messages
+ ServerStats *stats.ServerStats // used to record general transportation statistics
+ // used to record transportation statistics with followers when
+ // performing as leader in raft protocol
+ LeaderStats *stats.LeaderStats
+ // error channel used to report detected critical error, e.g.,
+ // the member has been permanently removed from the cluster
+ // When an error is received from ErrorC, user should stop raft state
+ // machine and thus stop the Transport.
+ ErrorC chan error
+ V3demo bool
+
+ streamRt http.RoundTripper // roundTripper used by streams
+ pipelineRt http.RoundTripper // roundTripper used by pipelines
+
+ mu sync.RWMutex // protect the remote and peer map
+ remotes map[types.ID]*remote // remotes map that helps newly joined member to catch up
+ peers map[types.ID]Peer // peers map
+
+ snapst *snapshotStore
+
+ prober probing.Prober
+}
+
+func (t *Transport) Start() error {
+ var err error
+ // Read/write timeout is set for stream roundTripper to promptly
+ // find out broken status, which minimizes the number of messages
+ // sent on broken connection.
+ t.streamRt, err = transport.NewTimeoutTransport(t.TLSInfo, t.DialTimeout, ConnReadTimeout, ConnWriteTimeout)
+ if err != nil {
+ return err
+ }
+ t.pipelineRt, err = transport.NewTransport(t.TLSInfo, t.DialTimeout)
+ if err != nil {
+ return err
+ }
+ t.remotes = make(map[types.ID]*remote)
+ t.peers = make(map[types.ID]Peer)
+ t.snapst = &snapshotStore{}
+ t.prober = probing.NewProber(t.pipelineRt)
+ return nil
+}
+
+func (t *Transport) Handler() http.Handler {
+ pipelineHandler := newPipelineHandler(t.Raft, t.ClusterID)
+ streamHandler := newStreamHandler(t, t.Raft, t.ID, t.ClusterID)
+ snapHandler := newSnapshotHandler(t.Raft, t.SnapSaver, t.ClusterID)
+ mux := http.NewServeMux()
+ mux.Handle(RaftPrefix, pipelineHandler)
+ mux.Handle(RaftStreamPrefix+"/", streamHandler)
+ mux.Handle(RaftSnapshotPrefix, snapHandler)
+ mux.Handle(ProbingPrefix, probing.NewHandler())
+ return mux
+}
+
+func (t *Transport) Get(id types.ID) Peer {
+ t.mu.RLock()
+ defer t.mu.RUnlock()
+ return t.peers[id]
+}
+
+func (t *Transport) Send(msgs []raftpb.Message) {
+ for _, m := range msgs {
+ if m.To == 0 {
+ // ignore intentionally dropped message
+ continue
+ }
+ to := types.ID(m.To)
+
+ p, ok := t.peers[to]
+ if ok {
+ if m.Type == raftpb.MsgApp {
+ t.ServerStats.SendAppendReq(m.Size())
+ }
+ p.send(m)
+ continue
+ }
+
+ g, ok := t.remotes[to]
+ if ok {
+ g.send(m)
+ continue
+ }
+
+ plog.Debugf("ignored message %s (sent to unknown peer %s)", m.Type, to)
+ }
+}
+
+func (t *Transport) Stop() {
+ for _, r := range t.remotes {
+ r.stop()
+ }
+ for _, p := range t.peers {
+ p.stop()
+ }
+ t.prober.RemoveAll()
+ if tr, ok := t.streamRt.(*http.Transport); ok {
+ tr.CloseIdleConnections()
+ }
+ if tr, ok := t.pipelineRt.(*http.Transport); ok {
+ tr.CloseIdleConnections()
+ }
+}
+
+func (t *Transport) AddRemote(id types.ID, us []string) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if _, ok := t.remotes[id]; ok {
+ return
+ }
+ urls, err := types.NewURLs(us)
+ if err != nil {
+ plog.Panicf("newURLs %+v should never fail: %+v", us, err)
+ }
+ t.remotes[id] = startRemote(t.pipelineRt, urls, t.ID, id, t.ClusterID, t.Raft, t.ErrorC)
+}
+
+func (t *Transport) AddPeer(id types.ID, us []string) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if _, ok := t.peers[id]; ok {
+ return
+ }
+ urls, err := types.NewURLs(us)
+ if err != nil {
+ plog.Panicf("newURLs %+v should never fail: %+v", us, err)
+ }
+ fs := t.LeaderStats.Follower(id.String())
+ t.peers[id] = startPeer(t.streamRt, t.pipelineRt, urls, t.ID, id, t.ClusterID, t.snapst, t.Raft, fs, t.ErrorC, t.V3demo)
+ addPeerToProber(t.prober, id.String(), us)
+}
+
+func (t *Transport) RemovePeer(id types.ID) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ t.removePeer(id)
+}
+
+func (t *Transport) RemoveAllPeers() {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ for id := range t.peers {
+ t.removePeer(id)
+ }
+}
+
+// the caller of this function must have the peers mutex.
+func (t *Transport) removePeer(id types.ID) {
+ if peer, ok := t.peers[id]; ok {
+ peer.stop()
+ } else {
+ plog.Panicf("unexpected removal of unknown peer '%d'", id)
+ }
+ delete(t.peers, id)
+ delete(t.LeaderStats.Followers, id.String())
+ t.prober.Remove(id.String())
+}
+
+func (t *Transport) UpdatePeer(id types.ID, us []string) {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ // TODO: return error or just panic?
+ if _, ok := t.peers[id]; !ok {
+ return
+ }
+ urls, err := types.NewURLs(us)
+ if err != nil {
+ plog.Panicf("newURLs %+v should never fail: %+v", us, err)
+ }
+ t.peers[id].update(urls)
+
+ t.prober.Remove(id.String())
+ addPeerToProber(t.prober, id.String(), us)
+}
+
+func (t *Transport) ActiveSince(id types.ID) time.Time {
+ t.mu.Lock()
+ defer t.mu.Unlock()
+ if p, ok := t.peers[id]; ok {
+ return p.activeSince()
+ }
+ return time.Time{}
+}
+
+func (t *Transport) SnapshotReady(rc io.ReadCloser, index uint64) {
+ t.snapst.put(rc, index)
+}
+
+type Pausable interface {
+ Pause()
+ Resume()
+}
+
+// for testing
+func (t *Transport) Pause() {
+ for _, p := range t.peers {
+ p.(Pausable).Pause()
+ }
+}
+
+func (t *Transport) Resume() {
+ for _, p := range t.peers {
+ p.(Pausable).Resume()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/transport_bench_test.go b/vendor/github.com/coreos/etcd/rafthttp/transport_bench_test.go
new file mode 100644
index 000000000..cfbdebab1
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/transport_bench_test.go
@@ -0,0 +1,114 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/http/httptest"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func BenchmarkSendingMsgApp(b *testing.B) {
+ // member 1
+ tr := &Transport{
+ ID: types.ID(1),
+ ClusterID: types.ID(1),
+ Raft: &fakeRaft{},
+ ServerStats: newServerStats(),
+ LeaderStats: stats.NewLeaderStats("1"),
+ }
+ tr.Start()
+ srv := httptest.NewServer(tr.Handler())
+ defer srv.Close()
+
+ // member 2
+ r := &countRaft{}
+ tr2 := &Transport{
+ ID: types.ID(2),
+ ClusterID: types.ID(1),
+ Raft: r,
+ ServerStats: newServerStats(),
+ LeaderStats: stats.NewLeaderStats("2"),
+ }
+ tr2.Start()
+ srv2 := httptest.NewServer(tr2.Handler())
+ defer srv2.Close()
+
+ tr.AddPeer(types.ID(2), []string{srv2.URL})
+ defer tr.Stop()
+ tr2.AddPeer(types.ID(1), []string{srv.URL})
+ defer tr2.Stop()
+ if !waitStreamWorking(tr.Get(types.ID(2)).(*peer)) {
+ b.Fatalf("stream from 1 to 2 is not in work as expected")
+ }
+
+ b.ReportAllocs()
+ b.SetBytes(64)
+
+ b.ResetTimer()
+ data := make([]byte, 64)
+ for i := 0; i < b.N; i++ {
+ tr.Send([]raftpb.Message{
+ {
+ Type: raftpb.MsgApp,
+ From: 1,
+ To: 2,
+ Index: uint64(i),
+ Entries: []raftpb.Entry{
+ {
+ Index: uint64(i + 1),
+ Data: data,
+ },
+ },
+ },
+ })
+ }
+ // wait until all messages are received by the target raft
+ for r.count() != b.N {
+ time.Sleep(time.Millisecond)
+ }
+ b.StopTimer()
+}
+
+type countRaft struct {
+ mu sync.Mutex
+ cnt int
+}
+
+func (r *countRaft) Process(ctx context.Context, m raftpb.Message) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ r.cnt++
+ return nil
+}
+
+func (r *countRaft) IsIDRemoved(id uint64) bool { return false }
+
+func (r *countRaft) ReportUnreachable(id uint64) {}
+
+func (r *countRaft) ReportSnapshot(id uint64, status raft.SnapshotStatus) {}
+
+func (r *countRaft) count() int {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ return r.cnt
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/transport_test.go b/vendor/github.com/coreos/etcd/rafthttp/transport_test.go
new file mode 100644
index 000000000..43a46a925
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/transport_test.go
@@ -0,0 +1,154 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/http"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/xiang90/probing"
+ "github.com/coreos/etcd/etcdserver/stats"
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+// TestTransportSend tests that transport can send messages using correct
+// underlying peer, and drop local or unknown-target messages.
+func TestTransportSend(t *testing.T) {
+ ss := &stats.ServerStats{}
+ ss.Initialize()
+ peer1 := newFakePeer()
+ peer2 := newFakePeer()
+ tr := &Transport{
+ ServerStats: ss,
+ peers: map[types.ID]Peer{types.ID(1): peer1, types.ID(2): peer2},
+ }
+ wmsgsIgnored := []raftpb.Message{
+ // bad local message
+ {Type: raftpb.MsgBeat},
+ // bad remote message
+ {Type: raftpb.MsgProp, To: 3},
+ }
+ wmsgsTo1 := []raftpb.Message{
+ // good message
+ {Type: raftpb.MsgProp, To: 1},
+ {Type: raftpb.MsgApp, To: 1},
+ }
+ wmsgsTo2 := []raftpb.Message{
+ // good message
+ {Type: raftpb.MsgProp, To: 2},
+ {Type: raftpb.MsgApp, To: 2},
+ }
+ tr.Send(wmsgsIgnored)
+ tr.Send(wmsgsTo1)
+ tr.Send(wmsgsTo2)
+
+ if !reflect.DeepEqual(peer1.msgs, wmsgsTo1) {
+ t.Errorf("msgs to peer 1 = %+v, want %+v", peer1.msgs, wmsgsTo1)
+ }
+ if !reflect.DeepEqual(peer2.msgs, wmsgsTo2) {
+ t.Errorf("msgs to peer 2 = %+v, want %+v", peer2.msgs, wmsgsTo2)
+ }
+}
+
+func TestTransportAdd(t *testing.T) {
+ ls := stats.NewLeaderStats("")
+ tr := &Transport{
+ LeaderStats: ls,
+ streamRt: &roundTripperRecorder{},
+ peers: make(map[types.ID]Peer),
+ prober: probing.NewProber(nil),
+ }
+ tr.AddPeer(1, []string{"http://localhost:2380"})
+
+ if _, ok := ls.Followers["1"]; !ok {
+ t.Errorf("FollowerStats[1] is nil, want exists")
+ }
+ s, ok := tr.peers[types.ID(1)]
+ if !ok {
+ tr.Stop()
+ t.Fatalf("senders[1] is nil, want exists")
+ }
+
+ // duplicate AddPeer is ignored
+ tr.AddPeer(1, []string{"http://localhost:2380"})
+ ns := tr.peers[types.ID(1)]
+ if s != ns {
+ t.Errorf("sender = %v, want %v", ns, s)
+ }
+
+ tr.Stop()
+}
+
+func TestTransportRemove(t *testing.T) {
+ tr := &Transport{
+ LeaderStats: stats.NewLeaderStats(""),
+ streamRt: &roundTripperRecorder{},
+ peers: make(map[types.ID]Peer),
+ prober: probing.NewProber(nil),
+ }
+ tr.AddPeer(1, []string{"http://localhost:2380"})
+ tr.RemovePeer(types.ID(1))
+ defer tr.Stop()
+
+ if _, ok := tr.peers[types.ID(1)]; ok {
+ t.Fatalf("senders[1] exists, want removed")
+ }
+}
+
+func TestTransportUpdate(t *testing.T) {
+ peer := newFakePeer()
+ tr := &Transport{
+ peers: map[types.ID]Peer{types.ID(1): peer},
+ prober: probing.NewProber(nil),
+ }
+ u := "http://localhost:2380"
+ tr.UpdatePeer(types.ID(1), []string{u})
+ wurls := types.URLs(testutil.MustNewURLs(t, []string{"http://localhost:2380"}))
+ if !reflect.DeepEqual(peer.urls, wurls) {
+ t.Errorf("urls = %+v, want %+v", peer.urls, wurls)
+ }
+}
+
+func TestTransportErrorc(t *testing.T) {
+ errorc := make(chan error, 1)
+ tr := &Transport{
+ LeaderStats: stats.NewLeaderStats(""),
+ ErrorC: errorc,
+ streamRt: newRespRoundTripper(http.StatusForbidden, nil),
+ pipelineRt: newRespRoundTripper(http.StatusForbidden, nil),
+ peers: make(map[types.ID]Peer),
+ prober: probing.NewProber(nil),
+ }
+ tr.AddPeer(1, []string{"http://localhost:2380"})
+ defer tr.Stop()
+
+ select {
+ case <-errorc:
+ t.Fatalf("received unexpected from errorc")
+ case <-time.After(10 * time.Millisecond):
+ }
+ tr.peers[1].send(raftpb.Message{})
+
+ testutil.WaitSchedule()
+ select {
+ case <-errorc:
+ default:
+ t.Fatalf("cannot receive error from errorc")
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/urlpick.go b/vendor/github.com/coreos/etcd/rafthttp/urlpick.go
new file mode 100644
index 000000000..584f1c0ad
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/urlpick.go
@@ -0,0 +1,57 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/url"
+ "sync"
+
+ "github.com/coreos/etcd/pkg/types"
+)
+
+type urlPicker struct {
+ mu sync.Mutex // guards urls and picked
+ urls types.URLs
+ picked int
+}
+
+func newURLPicker(urls types.URLs) *urlPicker {
+ return &urlPicker{
+ urls: urls,
+ }
+}
+
+func (p *urlPicker) update(urls types.URLs) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ p.urls = urls
+ p.picked = 0
+}
+
+func (p *urlPicker) pick() url.URL {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ return p.urls[p.picked]
+}
+
+// unreachable notices the picker that the given url is unreachable,
+// and it should use other possible urls.
+func (p *urlPicker) unreachable(u url.URL) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if u == p.urls[p.picked] {
+ p.picked = (p.picked + 1) % len(p.urls)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/urlpick_test.go b/vendor/github.com/coreos/etcd/rafthttp/urlpick_test.go
new file mode 100644
index 000000000..b8b390855
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/urlpick_test.go
@@ -0,0 +1,73 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "net/url"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+// TestURLPickerPickTwice tests that pick returns a possible url,
+// and always returns the same one.
+func TestURLPickerPick(t *testing.T) {
+ picker := mustNewURLPicker(t, []string{"http://127.0.0.1:2380", "http://127.0.0.1:7001"})
+
+ u := picker.pick()
+ urlmap := map[url.URL]bool{
+ url.URL{Scheme: "http", Host: "127.0.0.1:2380"}: true,
+ url.URL{Scheme: "http", Host: "127.0.0.1:7001"}: true,
+ }
+ if !urlmap[u] {
+ t.Errorf("url picked = %+v, want a possible url in %+v", u, urlmap)
+ }
+
+ // pick out the same url when calling pick again
+ uu := picker.pick()
+ if u != uu {
+ t.Errorf("url picked = %+v, want %+v", uu, u)
+ }
+}
+
+func TestURLPickerUpdate(t *testing.T) {
+ picker := mustNewURLPicker(t, []string{"http://127.0.0.1:2380", "http://127.0.0.1:7001"})
+ picker.update(testutil.MustNewURLs(t, []string{"http://localhost:2380", "http://localhost:7001"}))
+
+ u := picker.pick()
+ urlmap := map[url.URL]bool{
+ url.URL{Scheme: "http", Host: "localhost:2380"}: true,
+ url.URL{Scheme: "http", Host: "localhost:7001"}: true,
+ }
+ if !urlmap[u] {
+ t.Errorf("url picked = %+v, want a possible url in %+v", u, urlmap)
+ }
+}
+
+func TestURLPickerUnreachable(t *testing.T) {
+ picker := mustNewURLPicker(t, []string{"http://127.0.0.1:2380", "http://127.0.0.1:7001"})
+ u := picker.pick()
+ picker.unreachable(u)
+
+ uu := picker.pick()
+ if u == uu {
+ t.Errorf("url picked = %+v, want other possible urls", uu)
+ }
+}
+
+func mustNewURLPicker(t *testing.T, us []string) *urlPicker {
+ urls := testutil.MustNewURLs(t, us)
+ return newURLPicker(urls)
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/util.go b/vendor/github.com/coreos/etcd/rafthttp/util.go
new file mode 100644
index 000000000..dbf09c223
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/util.go
@@ -0,0 +1,159 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/pkg/types"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+var errMemberRemoved = fmt.Errorf("the member has been permanently removed from the cluster")
+
+func writeEntryTo(w io.Writer, ent *raftpb.Entry) error {
+ size := ent.Size()
+ if err := binary.Write(w, binary.BigEndian, uint64(size)); err != nil {
+ return err
+ }
+ b, err := ent.Marshal()
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(b)
+ return err
+}
+
+func readEntryFrom(r io.Reader, ent *raftpb.Entry) error {
+ var l uint64
+ if err := binary.Read(r, binary.BigEndian, &l); err != nil {
+ return err
+ }
+ buf := make([]byte, int(l))
+ if _, err := io.ReadFull(r, buf); err != nil {
+ return err
+ }
+ return ent.Unmarshal(buf)
+}
+
+// createPostRequest creates a HTTP POST request that sends raft message.
+func createPostRequest(u url.URL, path string, body io.Reader, ct string, from, cid types.ID) *http.Request {
+ uu := u
+ uu.Path = path
+ req, err := http.NewRequest("POST", uu.String(), body)
+ if err != nil {
+ plog.Panicf("unexpected new request error (%v)", err)
+ }
+ req.Header.Set("Content-Type", ct)
+ req.Header.Set("X-Server-From", from.String())
+ req.Header.Set("X-Server-Version", version.Version)
+ req.Header.Set("X-Min-Cluster-Version", version.MinClusterVersion)
+ req.Header.Set("X-Etcd-Cluster-ID", cid.String())
+ return req
+}
+
+// checkPostResponse checks the response of the HTTP POST request that sends
+// raft message.
+func checkPostResponse(resp *http.Response, body []byte, req *http.Request, to types.ID) error {
+ switch resp.StatusCode {
+ case http.StatusPreconditionFailed:
+ switch strings.TrimSuffix(string(body), "\n") {
+ case errIncompatibleVersion.Error():
+ plog.Errorf("request sent was ignored by peer %s (server version incompatible)", to)
+ return errIncompatibleVersion
+ case errClusterIDMismatch.Error():
+ plog.Errorf("request sent was ignored (cluster ID mismatch: remote[%s]=%s, local=%s)",
+ to, resp.Header.Get("X-Etcd-Cluster-ID"), req.Header.Get("X-Etcd-Cluster-ID"))
+ return errClusterIDMismatch
+ default:
+ return fmt.Errorf("unhandled error %q when precondition failed", string(body))
+ }
+ case http.StatusForbidden:
+ return errMemberRemoved
+ case http.StatusNoContent:
+ return nil
+ default:
+ return fmt.Errorf("unexpected http status %s while posting to %q", http.StatusText(resp.StatusCode), req.URL.String())
+ }
+}
+
+// reportErr reports the given error through sending it into
+// the given error channel.
+// If the error channel is filled up when sending error, it drops the error
+// because the fact that error has happened is reported, which is
+// good enough.
+func reportCriticalError(err error, errc chan<- error) {
+ select {
+ case errc <- err:
+ default:
+ }
+}
+
+// compareMajorMinorVersion returns an integer comparing two versions based on
+// their major and minor version. The result will be 0 if a==b, -1 if a < b,
+// and 1 if a > b.
+func compareMajorMinorVersion(a, b *semver.Version) int {
+ na := &semver.Version{Major: a.Major, Minor: a.Minor}
+ nb := &semver.Version{Major: b.Major, Minor: b.Minor}
+ switch {
+ case na.LessThan(*nb):
+ return -1
+ case nb.LessThan(*na):
+ return 1
+ default:
+ return 0
+ }
+}
+
+// serverVersion returns the server version from the given header.
+func serverVersion(h http.Header) *semver.Version {
+ verStr := h.Get("X-Server-Version")
+ // backward compatibility with etcd 2.0
+ if verStr == "" {
+ verStr = "2.0.0"
+ }
+ return semver.Must(semver.NewVersion(verStr))
+}
+
+// serverVersion returns the min cluster version from the given header.
+func minClusterVersion(h http.Header) *semver.Version {
+ verStr := h.Get("X-Min-Cluster-Version")
+ // backward compatibility with etcd 2.0
+ if verStr == "" {
+ verStr = "2.0.0"
+ }
+ return semver.Must(semver.NewVersion(verStr))
+}
+
+// checkVersionCompability checks whether the given version is compatible
+// with the local version.
+func checkVersionCompability(name string, server, minCluster *semver.Version) error {
+ localServer := semver.Must(semver.NewVersion(version.Version))
+ localMinCluster := semver.Must(semver.NewVersion(version.MinClusterVersion))
+ if compareMajorMinorVersion(server, localMinCluster) == -1 {
+ return fmt.Errorf("remote version is too low: remote[%s]=%s, local=%s", name, server, localServer)
+ }
+ if compareMajorMinorVersion(minCluster, localServer) == 1 {
+ return fmt.Errorf("local version is too low: remote[%s]=%s, local=%s", name, server, localServer)
+ }
+ return nil
+}
diff --git a/vendor/github.com/coreos/etcd/rafthttp/util_test.go b/vendor/github.com/coreos/etcd/rafthttp/util_test.go
new file mode 100644
index 000000000..5c7e1ffbc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/rafthttp/util_test.go
@@ -0,0 +1,193 @@
+// Copyright 2015 CoreOS, 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 rafthttp
+
+import (
+ "bytes"
+ "net/http"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/go-semver/semver"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/version"
+)
+
+func TestEntry(t *testing.T) {
+ tests := []raftpb.Entry{
+ {},
+ {Term: 1, Index: 1},
+ {Term: 1, Index: 1, Data: []byte("some data")},
+ }
+ for i, tt := range tests {
+ b := &bytes.Buffer{}
+ if err := writeEntryTo(b, &tt); err != nil {
+ t.Errorf("#%d: unexpected write ents error: %v", i, err)
+ continue
+ }
+ var ent raftpb.Entry
+ if err := readEntryFrom(b, &ent); err != nil {
+ t.Errorf("#%d: unexpected read ents error: %v", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(ent, tt) {
+ t.Errorf("#%d: ent = %+v, want %+v", i, ent, tt)
+ }
+ }
+}
+
+func TestCompareMajorMinorVersion(t *testing.T) {
+ tests := []struct {
+ va, vb *semver.Version
+ w int
+ }{
+ // equal to
+ {
+ semver.Must(semver.NewVersion("2.1.0")),
+ semver.Must(semver.NewVersion("2.1.0")),
+ 0,
+ },
+ // smaller than
+ {
+ semver.Must(semver.NewVersion("2.0.0")),
+ semver.Must(semver.NewVersion("2.1.0")),
+ -1,
+ },
+ // bigger than
+ {
+ semver.Must(semver.NewVersion("2.2.0")),
+ semver.Must(semver.NewVersion("2.1.0")),
+ 1,
+ },
+ // ignore patch
+ {
+ semver.Must(semver.NewVersion("2.1.1")),
+ semver.Must(semver.NewVersion("2.1.0")),
+ 0,
+ },
+ // ignore prerelease
+ {
+ semver.Must(semver.NewVersion("2.1.0-alpha.0")),
+ semver.Must(semver.NewVersion("2.1.0")),
+ 0,
+ },
+ }
+ for i, tt := range tests {
+ if g := compareMajorMinorVersion(tt.va, tt.vb); g != tt.w {
+ t.Errorf("#%d: compare = %d, want %d", i, g, tt.w)
+ }
+ }
+}
+
+func TestServerVersion(t *testing.T) {
+ tests := []struct {
+ h http.Header
+ wv *semver.Version
+ }{
+ // backward compatibility with etcd 2.0
+ {
+ http.Header{},
+ semver.Must(semver.NewVersion("2.0.0")),
+ },
+ {
+ http.Header{"X-Server-Version": []string{"2.1.0"}},
+ semver.Must(semver.NewVersion("2.1.0")),
+ },
+ {
+ http.Header{"X-Server-Version": []string{"2.1.0-alpha.0+git"}},
+ semver.Must(semver.NewVersion("2.1.0-alpha.0+git")),
+ },
+ }
+ for i, tt := range tests {
+ v := serverVersion(tt.h)
+ if v.String() != tt.wv.String() {
+ t.Errorf("#%d: version = %s, want %s", i, v, tt.wv)
+ }
+ }
+}
+
+func TestMinClusterVersion(t *testing.T) {
+ tests := []struct {
+ h http.Header
+ wv *semver.Version
+ }{
+ // backward compatibility with etcd 2.0
+ {
+ http.Header{},
+ semver.Must(semver.NewVersion("2.0.0")),
+ },
+ {
+ http.Header{"X-Min-Cluster-Version": []string{"2.1.0"}},
+ semver.Must(semver.NewVersion("2.1.0")),
+ },
+ {
+ http.Header{"X-Min-Cluster-Version": []string{"2.1.0-alpha.0+git"}},
+ semver.Must(semver.NewVersion("2.1.0-alpha.0+git")),
+ },
+ }
+ for i, tt := range tests {
+ v := minClusterVersion(tt.h)
+ if v.String() != tt.wv.String() {
+ t.Errorf("#%d: version = %s, want %s", i, v, tt.wv)
+ }
+ }
+}
+
+func TestCheckVersionCompatibility(t *testing.T) {
+ ls := semver.Must(semver.NewVersion(version.Version))
+ lmc := semver.Must(semver.NewVersion(version.MinClusterVersion))
+ tests := []struct {
+ server *semver.Version
+ minCluster *semver.Version
+ wok bool
+ }{
+ // the same version as local
+ {
+ ls,
+ lmc,
+ true,
+ },
+ // one version lower
+ {
+ lmc,
+ &semver.Version{},
+ true,
+ },
+ // one version higher
+ {
+ &semver.Version{Major: ls.Major + 1},
+ ls,
+ true,
+ },
+ // too low version
+ {
+ &semver.Version{Major: lmc.Major - 1},
+ &semver.Version{},
+ false,
+ },
+ // too high version
+ {
+ &semver.Version{Major: ls.Major + 1, Minor: 1},
+ &semver.Version{Major: ls.Major + 1},
+ false,
+ },
+ }
+ for i, tt := range tests {
+ err := checkVersionCompability("", tt.server, tt.minCluster)
+ if ok := err == nil; ok != tt.wok {
+ t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/snap/metrics.go b/vendor/github.com/coreos/etcd/snap/metrics.go
new file mode 100644
index 000000000..72758499a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/snap/metrics.go
@@ -0,0 +1,39 @@
+// Copyright 2015 CoreOS, 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 snap
+
+import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+
+var (
+ // TODO: save_fsync latency?
+ saveDurations = prometheus.NewSummary(prometheus.SummaryOpts{
+ Namespace: "etcd",
+ Subsystem: "snapshot",
+ Name: "save_total_durations_microseconds",
+ Help: "The total latency distributions of save called by snapshot.",
+ })
+
+ marshallingDurations = prometheus.NewSummary(prometheus.SummaryOpts{
+ Namespace: "etcd",
+ Subsystem: "snapshot",
+ Name: "save_marshalling_durations_microseconds",
+ Help: "The marshalling cost distributions of save called by snapshot.",
+ })
+)
+
+func init() {
+ prometheus.MustRegister(saveDurations)
+ prometheus.MustRegister(marshallingDurations)
+}
diff --git a/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go b/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go
new file mode 100644
index 000000000..35fa8101c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/snap/snappb/snap.pb.go
@@ -0,0 +1,299 @@
+// Code generated by protoc-gen-gogo.
+// source: snap.proto
+// DO NOT EDIT!
+
+/*
+ Package snappb is a generated protocol buffer package.
+
+ It is generated from these files:
+ snap.proto
+
+ It has these top-level messages:
+ Snapshot
+*/
+package snappb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+import math "math"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type Snapshot struct {
+ Crc uint32 `protobuf:"varint,1,opt,name=crc" json:"crc"`
+ Data []byte `protobuf:"bytes,2,opt,name=data" json:"data,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Snapshot) Reset() { *m = Snapshot{} }
+func (m *Snapshot) String() string { return proto.CompactTextString(m) }
+func (*Snapshot) ProtoMessage() {}
+
+func (m *Snapshot) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Snapshot) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintSnap(data, i, uint64(m.Crc))
+ if m.Data != nil {
+ data[i] = 0x12
+ i++
+ i = encodeVarintSnap(data, i, uint64(len(m.Data)))
+ i += copy(data[i:], m.Data)
+ }
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func encodeFixed64Snap(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Snap(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintSnap(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *Snapshot) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovSnap(uint64(m.Crc))
+ if m.Data != nil {
+ l = len(m.Data)
+ n += 1 + l + sovSnap(uint64(l))
+ }
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func sovSnap(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozSnap(x uint64) (n int) {
+ return sovSnap(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Snapshot) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Crc", wireType)
+ }
+ m.Crc = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Crc |= (uint32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthSnap
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Data = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipSnap(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthSnap
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipSnap(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthSnap
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipSnap(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthSnap = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/snap/snappb/snap.proto b/vendor/github.com/coreos/etcd/snap/snappb/snap.proto
new file mode 100644
index 000000000..cd3d21d0e
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/snap/snappb/snap.proto
@@ -0,0 +1,14 @@
+syntax = "proto2";
+package snappb;
+
+import "gogoproto/gogo.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.goproto_getters_all) = false;
+
+message snapshot {
+ optional uint32 crc = 1 [(gogoproto.nullable) = false];
+ optional bytes data = 2;
+}
diff --git a/vendor/github.com/coreos/etcd/snap/snapshotter.go b/vendor/github.com/coreos/etcd/snap/snapshotter.go
new file mode 100644
index 000000000..4f9eb9ed8
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/snap/snapshotter.go
@@ -0,0 +1,188 @@
+// Copyright 2015 CoreOS, 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 snap
+
+import (
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "io/ioutil"
+ "os"
+ "path"
+ "sort"
+ "strings"
+ "time"
+
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/snap/snappb"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+)
+
+const (
+ snapSuffix = ".snap"
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "snap")
+
+ ErrNoSnapshot = errors.New("snap: no available snapshot")
+ ErrEmptySnapshot = errors.New("snap: empty snapshot")
+ ErrCRCMismatch = errors.New("snap: crc mismatch")
+ crcTable = crc32.MakeTable(crc32.Castagnoli)
+)
+
+type Snapshotter struct {
+ dir string
+}
+
+func New(dir string) *Snapshotter {
+ return &Snapshotter{
+ dir: dir,
+ }
+}
+
+func (s *Snapshotter) SaveSnap(snapshot raftpb.Snapshot) error {
+ if raft.IsEmptySnap(snapshot) {
+ return nil
+ }
+ return s.save(&snapshot)
+}
+
+func (s *Snapshotter) save(snapshot *raftpb.Snapshot) error {
+ start := time.Now()
+
+ fname := fmt.Sprintf("%016x-%016x%s", snapshot.Metadata.Term, snapshot.Metadata.Index, snapSuffix)
+ b := pbutil.MustMarshal(snapshot)
+ crc := crc32.Update(0, crcTable, b)
+ snap := snappb.Snapshot{Crc: crc, Data: b}
+ d, err := snap.Marshal()
+ if err != nil {
+ return err
+ } else {
+ marshallingDurations.Observe(float64(time.Since(start).Nanoseconds() / int64(time.Microsecond)))
+ }
+
+ err = ioutil.WriteFile(path.Join(s.dir, fname), d, 0666)
+ if err == nil {
+ saveDurations.Observe(float64(time.Since(start).Nanoseconds() / int64(time.Microsecond)))
+ }
+ return err
+}
+
+func (s *Snapshotter) Load() (*raftpb.Snapshot, error) {
+ names, err := s.snapNames()
+ if err != nil {
+ return nil, err
+ }
+ var snap *raftpb.Snapshot
+ for _, name := range names {
+ if snap, err = loadSnap(s.dir, name); err == nil {
+ break
+ }
+ }
+ if err != nil {
+ return nil, ErrNoSnapshot
+ }
+ return snap, nil
+}
+
+func loadSnap(dir, name string) (*raftpb.Snapshot, error) {
+ fpath := path.Join(dir, name)
+ snap, err := Read(fpath)
+ if err != nil {
+ renameBroken(fpath)
+ }
+ return snap, err
+}
+
+// Read reads the snapshot named by snapname and returns the snapshot.
+func Read(snapname string) (*raftpb.Snapshot, error) {
+ b, err := ioutil.ReadFile(snapname)
+ if err != nil {
+ plog.Errorf("cannot read file %v: %v", snapname, err)
+ return nil, err
+ }
+
+ if len(b) == 0 {
+ plog.Errorf("unexpected empty snapshot")
+ return nil, ErrEmptySnapshot
+ }
+
+ var serializedSnap snappb.Snapshot
+ if err = serializedSnap.Unmarshal(b); err != nil {
+ plog.Errorf("corrupted snapshot file %v: %v", snapname, err)
+ return nil, err
+ }
+
+ if len(serializedSnap.Data) == 0 || serializedSnap.Crc == 0 {
+ plog.Errorf("unexpected empty snapshot")
+ return nil, ErrEmptySnapshot
+ }
+
+ crc := crc32.Update(0, crcTable, serializedSnap.Data)
+ if crc != serializedSnap.Crc {
+ plog.Errorf("corrupted snapshot file %v: crc mismatch", snapname)
+ return nil, ErrCRCMismatch
+ }
+
+ var snap raftpb.Snapshot
+ if err = snap.Unmarshal(serializedSnap.Data); err != nil {
+ plog.Errorf("corrupted snapshot file %v: %v", snapname, err)
+ return nil, err
+ }
+ return &snap, nil
+}
+
+// snapNames returns the filename of the snapshots in logical time order (from newest to oldest).
+// If there is no available snapshots, an ErrNoSnapshot will be returned.
+func (s *Snapshotter) snapNames() ([]string, error) {
+ dir, err := os.Open(s.dir)
+ if err != nil {
+ return nil, err
+ }
+ defer dir.Close()
+ names, err := dir.Readdirnames(-1)
+ if err != nil {
+ return nil, err
+ }
+ snaps := checkSuffix(names)
+ if len(snaps) == 0 {
+ return nil, ErrNoSnapshot
+ }
+ sort.Sort(sort.Reverse(sort.StringSlice(snaps)))
+ return snaps, nil
+}
+
+func checkSuffix(names []string) []string {
+ snaps := []string{}
+ for i := range names {
+ if strings.HasSuffix(names[i], snapSuffix) {
+ snaps = append(snaps, names[i])
+ } else {
+ plog.Warningf("skipped unexpected non snapshot file %v", names[i])
+ }
+ }
+ return snaps
+}
+
+func renameBroken(path string) {
+ brokenPath := path + ".broken"
+ if err := os.Rename(path, brokenPath); err != nil {
+ plog.Warningf("cannot rename broken snapshot file %v to %v: %v", path, brokenPath, err)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/snap/snapshotter_test.go b/vendor/github.com/coreos/etcd/snap/snapshotter_test.go
new file mode 100644
index 000000000..1d4e9988c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/snap/snapshotter_test.go
@@ -0,0 +1,230 @@
+// Copyright 2015 CoreOS, 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 snap
+
+import (
+ "fmt"
+ "hash/crc32"
+ "io/ioutil"
+ "os"
+ "path"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+var testSnap = &raftpb.Snapshot{
+ Data: []byte("some snapshot"),
+ Metadata: raftpb.SnapshotMetadata{
+ ConfState: raftpb.ConfState{
+ Nodes: []uint64{1, 2, 3},
+ },
+ Index: 1,
+ Term: 1,
+ },
+}
+
+func TestSaveAndLoad(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ ss := New(dir)
+ err = ss.save(testSnap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ g, err := ss.Load()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if !reflect.DeepEqual(g, testSnap) {
+ t.Errorf("snap = %#v, want %#v", g, testSnap)
+ }
+}
+
+func TestBadCRC(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ ss := New(dir)
+ err = ss.save(testSnap)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() { crcTable = crc32.MakeTable(crc32.Castagnoli) }()
+ // switch to use another crc table
+ // fake a crc mismatch
+ crcTable = crc32.MakeTable(crc32.Koopman)
+
+ _, err = Read(path.Join(dir, fmt.Sprintf("%016x-%016x.snap", 1, 1)))
+ if err == nil || err != ErrCRCMismatch {
+ t.Errorf("err = %v, want %v", err, ErrCRCMismatch)
+ }
+}
+
+func TestFailback(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ large := fmt.Sprintf("%016x-%016x-%016x.snap", 0xFFFF, 0xFFFF, 0xFFFF)
+ err = ioutil.WriteFile(path.Join(dir, large), []byte("bad data"), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ss := New(dir)
+ err = ss.save(testSnap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ g, err := ss.Load()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if !reflect.DeepEqual(g, testSnap) {
+ t.Errorf("snap = %#v, want %#v", g, testSnap)
+ }
+ if f, err := os.Open(path.Join(dir, large) + ".broken"); err != nil {
+ t.Fatal("broken snapshot does not exist")
+ } else {
+ f.Close()
+ }
+}
+
+func TestSnapNames(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ for i := 1; i <= 5; i++ {
+ var f *os.File
+ if f, err = os.Create(path.Join(dir, fmt.Sprintf("%d.snap", i))); err != nil {
+ t.Fatal(err)
+ } else {
+ f.Close()
+ }
+ }
+ ss := New(dir)
+ names, err := ss.snapNames()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if len(names) != 5 {
+ t.Errorf("len = %d, want 10", len(names))
+ }
+ w := []string{"5.snap", "4.snap", "3.snap", "2.snap", "1.snap"}
+ if !reflect.DeepEqual(names, w) {
+ t.Errorf("names = %v, want %v", names, w)
+ }
+}
+
+func TestLoadNewestSnap(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ ss := New(dir)
+ err = ss.save(testSnap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ newSnap := *testSnap
+ newSnap.Metadata.Index = 5
+ err = ss.save(&newSnap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ g, err := ss.Load()
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if !reflect.DeepEqual(g, &newSnap) {
+ t.Errorf("snap = %#v, want %#v", g, &newSnap)
+ }
+}
+
+func TestNoSnapshot(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+ ss := New(dir)
+ _, err = ss.Load()
+ if err != ErrNoSnapshot {
+ t.Errorf("err = %v, want %v", err, ErrNoSnapshot)
+ }
+}
+
+func TestEmptySnapshot(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ err = ioutil.WriteFile(path.Join(dir, "1.snap"), []byte(""), 0x700)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = Read(path.Join(dir, "1.snap"))
+ if err != ErrEmptySnapshot {
+ t.Errorf("err = %v, want %v", err, ErrEmptySnapshot)
+ }
+}
+
+// TestAllSnapshotBroken ensures snapshotter returens
+// ErrNoSnapshot if all the snapshots are broken.
+func TestAllSnapshotBroken(t *testing.T) {
+ dir := path.Join(os.TempDir(), "snapshot")
+ err := os.Mkdir(dir, 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ err = ioutil.WriteFile(path.Join(dir, "1.snap"), []byte("bad"), 0x700)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ss := New(dir)
+ _, err = ss.Load()
+ if err != ErrNoSnapshot {
+ t.Errorf("err = %v, want %v", err, ErrNoSnapshot)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/backend/backend.go b/vendor/github.com/coreos/etcd/storage/backend/backend.go
new file mode 100644
index 000000000..b8a2f2ad4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/backend/backend.go
@@ -0,0 +1,158 @@
+// Copyright 2015 CoreOS, 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 backend
+
+import (
+ "fmt"
+ "hash/crc32"
+ "io"
+ "log"
+ "sync/atomic"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/boltdb/bolt"
+)
+
+type Backend interface {
+ BatchTx() BatchTx
+ Snapshot() Snapshot
+ Hash() (uint32, error)
+ // Size returns the current size of the backend.
+ Size() int64
+ ForceCommit()
+ Close() error
+}
+
+type Snapshot interface {
+ // Size gets the size of the snapshot.
+ Size() int64
+ // WriteTo writes the snapshot into the given writter.
+ WriteTo(w io.Writer) (n int64, err error)
+ // Close closes the snapshot.
+ Close() error
+}
+
+type backend struct {
+ db *bolt.DB
+
+ batchInterval time.Duration
+ batchLimit int
+ batchTx *batchTx
+ size int64
+
+ stopc chan struct{}
+ donec chan struct{}
+}
+
+func New(path string, d time.Duration, limit int) Backend {
+ return newBackend(path, d, limit)
+}
+
+func newBackend(path string, d time.Duration, limit int) *backend {
+ db, err := bolt.Open(path, 0600, nil)
+ if err != nil {
+ log.Panicf("backend: cannot open database at %s (%v)", path, err)
+ }
+
+ b := &backend{
+ db: db,
+
+ batchInterval: d,
+ batchLimit: limit,
+
+ stopc: make(chan struct{}),
+ donec: make(chan struct{}),
+ }
+ b.batchTx = newBatchTx(b)
+ go b.run()
+ return b
+}
+
+// BatchTx returns the current batch tx in coalescer. The tx can be used for read and
+// write operations. The write result can be retrieved within the same tx immediately.
+// The write result is isolated with other txs until the current one get committed.
+func (b *backend) BatchTx() BatchTx {
+ return b.batchTx
+}
+
+// force commit the current batching tx.
+func (b *backend) ForceCommit() {
+ b.batchTx.Commit()
+}
+
+func (b *backend) Snapshot() Snapshot {
+ tx, err := b.db.Begin(false)
+ if err != nil {
+ log.Fatalf("storage: cannot begin tx (%s)", err)
+ }
+ return &snapshot{tx}
+}
+
+func (b *backend) Hash() (uint32, error) {
+ h := crc32.New(crc32.MakeTable(crc32.Castagnoli))
+
+ err := b.db.View(func(tx *bolt.Tx) error {
+ c := tx.Cursor()
+ for next, _ := c.First(); next != nil; next, _ = c.Next() {
+ b := tx.Bucket(next)
+ if b == nil {
+ return fmt.Errorf("cannot get hash of bucket %s", string(next))
+ }
+ h.Write(next)
+ b.ForEach(func(k, v []byte) error {
+ h.Write(k)
+ h.Write(v)
+ return nil
+ })
+ }
+ return nil
+ })
+
+ if err != nil {
+ return 0, err
+ }
+
+ return h.Sum32(), nil
+}
+
+func (b *backend) Size() int64 {
+ return atomic.LoadInt64(&b.size)
+}
+
+func (b *backend) run() {
+ defer close(b.donec)
+
+ for {
+ select {
+ case <-time.After(b.batchInterval):
+ case <-b.stopc:
+ b.batchTx.CommitAndStop()
+ return
+ }
+ b.batchTx.Commit()
+ }
+}
+
+func (b *backend) Close() error {
+ close(b.stopc)
+ <-b.donec
+ return b.db.Close()
+}
+
+type snapshot struct {
+ *bolt.Tx
+}
+
+func (s *snapshot) Close() error { return s.Tx.Rollback() }
diff --git a/vendor/github.com/coreos/etcd/storage/backend/backend_bench_test.go b/vendor/github.com/coreos/etcd/storage/backend/backend_bench_test.go
new file mode 100644
index 000000000..1e798b830
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/backend/backend_bench_test.go
@@ -0,0 +1,50 @@
+// Copyright 2015 CoreOS, 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 backend
+
+import (
+ "crypto/rand"
+ "os"
+ "testing"
+ "time"
+)
+
+func BenchmarkBackendPut(b *testing.B) {
+ backend := New("test", 100*time.Millisecond, 10000)
+ defer backend.Close()
+ defer os.Remove("test")
+
+ // prepare keys
+ keys := make([][]byte, b.N)
+ for i := 0; i < b.N; i++ {
+ keys[i] = make([]byte, 64)
+ rand.Read(keys[i])
+ }
+ value := make([]byte, 128)
+ rand.Read(value)
+
+ batchTx := backend.BatchTx()
+
+ batchTx.Lock()
+ batchTx.UnsafeCreateBucket([]byte("test"))
+ batchTx.Unlock()
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ batchTx.Lock()
+ batchTx.UnsafePut([]byte("test"), keys[i], value)
+ batchTx.Unlock()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/backend/backend_test.go b/vendor/github.com/coreos/etcd/storage/backend/backend_test.go
new file mode 100644
index 000000000..9a3fcb112
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/backend/backend_test.go
@@ -0,0 +1,130 @@
+// Copyright 2015 CoreOS, 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 backend
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/boltdb/bolt"
+ "github.com/coreos/etcd/pkg/testutil"
+)
+
+var tmpPath string
+
+func init() {
+ dir, err := ioutil.TempDir(os.TempDir(), "etcd_backend_test")
+ if err != nil {
+ log.Fatal(err)
+ }
+ tmpPath = path.Join(dir, "database")
+}
+
+func TestBackendClose(t *testing.T) {
+ b := newBackend(tmpPath, time.Hour, 10000)
+ defer os.Remove(tmpPath)
+
+ // check close could work
+ done := make(chan struct{})
+ go func() {
+ err := b.Close()
+ if err != nil {
+ t.Errorf("close error = %v, want nil", err)
+ }
+ done <- struct{}{}
+ }()
+ select {
+ case <-done:
+ case <-time.After(time.Second):
+ t.Errorf("failed to close database in 1s")
+ }
+}
+
+func TestBackendSnapshot(t *testing.T) {
+ b := New(tmpPath, time.Hour, 10000)
+ defer cleanup(b, tmpPath)
+
+ tx := b.BatchTx()
+ tx.Lock()
+ tx.UnsafeCreateBucket([]byte("test"))
+ tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
+ tx.Unlock()
+ b.ForceCommit()
+
+ // write snapshot to a new file
+ f, err := ioutil.TempFile(os.TempDir(), "etcd_backend_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ snap := b.Snapshot()
+ defer snap.Close()
+ if _, err := snap.WriteTo(f); err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ // bootstrap new backend from the snapshot
+ nb := New(f.Name(), time.Hour, 10000)
+ defer cleanup(nb, f.Name())
+
+ newTx := b.BatchTx()
+ newTx.Lock()
+ ks, _ := newTx.UnsafeRange([]byte("test"), []byte("foo"), []byte("goo"), 0)
+ if len(ks) != 1 {
+ t.Errorf("len(kvs) = %d, want 1", len(ks))
+ }
+ newTx.Unlock()
+}
+
+func TestBackendBatchIntervalCommit(t *testing.T) {
+ // start backend with super short batch interval
+ b := newBackend(tmpPath, time.Nanosecond, 10000)
+ defer cleanup(b, tmpPath)
+
+ tx := b.BatchTx()
+ tx.Lock()
+ tx.UnsafeCreateBucket([]byte("test"))
+ tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
+ tx.Unlock()
+
+ // give time for batch interval commit to happen
+ time.Sleep(time.Nanosecond)
+ testutil.WaitSchedule()
+ // give time for commit to finish, including possible disk IO
+ time.Sleep(50 * time.Millisecond)
+
+ // check whether put happens via db view
+ b.db.View(func(tx *bolt.Tx) error {
+ bucket := tx.Bucket([]byte("test"))
+ if bucket == nil {
+ t.Errorf("bucket test does not exit")
+ return nil
+ }
+ v := bucket.Get([]byte("foo"))
+ if v == nil {
+ t.Errorf("foo key failed to written in backend")
+ }
+ return nil
+ })
+}
+
+func cleanup(b Backend, path string) {
+ b.Close()
+ os.Remove(path)
+}
diff --git a/vendor/github.com/coreos/etcd/storage/backend/batch_tx.go b/vendor/github.com/coreos/etcd/storage/backend/batch_tx.go
new file mode 100644
index 000000000..9aa25bbc4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/backend/batch_tx.go
@@ -0,0 +1,151 @@
+// Copyright 2015 CoreOS, 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 backend
+
+import (
+ "bytes"
+ "log"
+ "sync"
+ "sync/atomic"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/boltdb/bolt"
+)
+
+type BatchTx interface {
+ Lock()
+ Unlock()
+ UnsafeCreateBucket(name []byte)
+ UnsafePut(bucketName []byte, key []byte, value []byte)
+ UnsafeRange(bucketName []byte, key, endKey []byte, limit int64) (keys [][]byte, vals [][]byte)
+ UnsafeDelete(bucketName []byte, key []byte)
+ Commit()
+ CommitAndStop()
+}
+
+type batchTx struct {
+ sync.Mutex
+ tx *bolt.Tx
+ backend *backend
+ pending int
+}
+
+func newBatchTx(backend *backend) *batchTx {
+ tx := &batchTx{backend: backend}
+ tx.Commit()
+ return tx
+}
+
+func (t *batchTx) UnsafeCreateBucket(name []byte) {
+ _, err := t.tx.CreateBucket(name)
+ if err != nil && err != bolt.ErrBucketExists {
+ log.Fatalf("storage: cannot create bucket %s (%v)", string(name), err)
+ }
+}
+
+// before calling unsafePut, the caller MUST hold the lock on tx.
+func (t *batchTx) UnsafePut(bucketName []byte, key []byte, value []byte) {
+ bucket := t.tx.Bucket(bucketName)
+ if bucket == nil {
+ log.Fatalf("storage: bucket %s does not exist", string(bucketName))
+ }
+ if err := bucket.Put(key, value); err != nil {
+ log.Fatalf("storage: cannot put key into bucket (%v)", err)
+ }
+ t.pending++
+}
+
+// before calling unsafeRange, the caller MUST hold the lock on tx.
+func (t *batchTx) UnsafeRange(bucketName []byte, key, endKey []byte, limit int64) (keys [][]byte, vs [][]byte) {
+ bucket := t.tx.Bucket(bucketName)
+ if bucket == nil {
+ log.Fatalf("storage: bucket %s does not exist", string(bucketName))
+ }
+
+ if len(endKey) == 0 {
+ if v := bucket.Get(key); v == nil {
+ return keys, vs
+ } else {
+ return append(keys, key), append(vs, v)
+ }
+ }
+
+ c := bucket.Cursor()
+ for ck, cv := c.Seek(key); ck != nil && bytes.Compare(ck, endKey) < 0; ck, cv = c.Next() {
+ vs = append(vs, cv)
+ keys = append(keys, ck)
+ if limit > 0 && limit == int64(len(keys)) {
+ break
+ }
+ }
+
+ return keys, vs
+}
+
+// before calling unsafeDelete, the caller MUST hold the lock on tx.
+func (t *batchTx) UnsafeDelete(bucketName []byte, key []byte) {
+ bucket := t.tx.Bucket(bucketName)
+ if bucket == nil {
+ log.Fatalf("storage: bucket %s does not exist", string(bucketName))
+ }
+ err := bucket.Delete(key)
+ if err != nil {
+ log.Fatalf("storage: cannot delete key from bucket (%v)", err)
+ }
+ t.pending++
+}
+
+// Commit commits a previous tx and begins a new writable one.
+func (t *batchTx) Commit() {
+ t.Lock()
+ defer t.Unlock()
+ t.commit(false)
+}
+
+// CommitAndStop commits the previous tx and do not create a new one.
+func (t *batchTx) CommitAndStop() {
+ t.Lock()
+ defer t.Unlock()
+ t.commit(true)
+}
+
+func (t *batchTx) Unlock() {
+ if t.pending >= t.backend.batchLimit {
+ t.commit(false)
+ t.pending = 0
+ }
+ t.Mutex.Unlock()
+}
+
+func (t *batchTx) commit(stop bool) {
+ var err error
+ // commit the last tx
+ if t.tx != nil {
+ err = t.tx.Commit()
+ if err != nil {
+ log.Fatalf("storage: cannot commit tx (%s)", err)
+ }
+ }
+
+ if stop {
+ return
+ }
+
+ // begin a new tx
+ t.tx, err = t.backend.db.Begin(true)
+ if err != nil {
+ log.Fatalf("storage: cannot begin tx (%s)", err)
+ }
+ atomic.StoreInt64(&t.backend.size, t.tx.Size())
+}
diff --git a/vendor/github.com/coreos/etcd/storage/backend/batch_tx_test.go b/vendor/github.com/coreos/etcd/storage/backend/batch_tx_test.go
new file mode 100644
index 000000000..56a8b385f
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/backend/batch_tx_test.go
@@ -0,0 +1,196 @@
+// Copyright 2015 CoreOS, 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 backend
+
+import (
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/boltdb/bolt"
+)
+
+func TestBatchTxPut(t *testing.T) {
+ b := newBackend(tmpPath, time.Hour, 10000)
+ defer cleanup(b, tmpPath)
+
+ tx := b.batchTx
+ tx.Lock()
+ defer tx.Unlock()
+
+ // create bucket
+ tx.UnsafeCreateBucket([]byte("test"))
+
+ // put
+ v := []byte("bar")
+ tx.UnsafePut([]byte("test"), []byte("foo"), v)
+
+ // check put result before and after tx is committed
+ for k := 0; k < 2; k++ {
+ _, gv := tx.UnsafeRange([]byte("test"), []byte("foo"), nil, 0)
+ if !reflect.DeepEqual(gv[0], v) {
+ t.Errorf("v = %s, want %s", string(gv[0]), string(v))
+ }
+ tx.commit(false)
+ }
+}
+
+func TestBatchTxRange(t *testing.T) {
+ b := newBackend(tmpPath, time.Hour, 10000)
+ defer cleanup(b, tmpPath)
+
+ tx := b.batchTx
+ tx.Lock()
+ defer tx.Unlock()
+
+ tx.UnsafeCreateBucket([]byte("test"))
+ // put keys
+ allKeys := [][]byte{[]byte("foo"), []byte("foo1"), []byte("foo2")}
+ allVals := [][]byte{[]byte("bar"), []byte("bar1"), []byte("bar2")}
+ for i := range allKeys {
+ tx.UnsafePut([]byte("test"), allKeys[i], allVals[i])
+ }
+
+ tests := []struct {
+ key []byte
+ endKey []byte
+ limit int64
+
+ wkeys [][]byte
+ wvals [][]byte
+ }{
+ // single key
+ {
+ []byte("foo"), nil, 0,
+ allKeys[:1], allVals[:1],
+ },
+ // single key, bad
+ {
+ []byte("doo"), nil, 0,
+ nil, nil,
+ },
+ // key range
+ {
+ []byte("foo"), []byte("foo1"), 0,
+ allKeys[:1], allVals[:1],
+ },
+ // key range, get all keys
+ {
+ []byte("foo"), []byte("foo3"), 0,
+ allKeys, allVals,
+ },
+ // key range, bad
+ {
+ []byte("goo"), []byte("goo3"), 0,
+ nil, nil,
+ },
+ // key range with effective limit
+ {
+ []byte("foo"), []byte("foo3"), 1,
+ allKeys[:1], allVals[:1],
+ },
+ // key range with limit
+ {
+ []byte("foo"), []byte("foo3"), 4,
+ allKeys, allVals,
+ },
+ }
+ for i, tt := range tests {
+ keys, vals := tx.UnsafeRange([]byte("test"), tt.key, tt.endKey, tt.limit)
+ if !reflect.DeepEqual(keys, tt.wkeys) {
+ t.Errorf("#%d: keys = %+v, want %+v", i, keys, tt.wkeys)
+ }
+ if !reflect.DeepEqual(vals, tt.wvals) {
+ t.Errorf("#%d: vals = %+v, want %+v", i, vals, tt.wvals)
+ }
+ }
+}
+
+func TestBatchTxDelete(t *testing.T) {
+ b := newBackend(tmpPath, time.Hour, 10000)
+ defer cleanup(b, tmpPath)
+
+ tx := b.batchTx
+ tx.Lock()
+ defer tx.Unlock()
+
+ tx.UnsafeCreateBucket([]byte("test"))
+ tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
+
+ tx.UnsafeDelete([]byte("test"), []byte("foo"))
+
+ // check put result before and after tx is committed
+ for k := 0; k < 2; k++ {
+ ks, _ := tx.UnsafeRange([]byte("test"), []byte("foo"), nil, 0)
+ if len(ks) != 0 {
+ t.Errorf("keys on foo = %v, want nil", ks)
+ }
+ tx.commit(false)
+ }
+}
+
+func TestBatchTxCommit(t *testing.T) {
+ b := newBackend(tmpPath, time.Hour, 10000)
+ defer cleanup(b, tmpPath)
+
+ tx := b.batchTx
+ tx.Lock()
+ tx.UnsafeCreateBucket([]byte("test"))
+ tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
+ tx.Unlock()
+
+ tx.Commit()
+
+ // check whether put happens via db view
+ b.db.View(func(tx *bolt.Tx) error {
+ bucket := tx.Bucket([]byte("test"))
+ if bucket == nil {
+ t.Errorf("bucket test does not exit")
+ return nil
+ }
+ v := bucket.Get([]byte("foo"))
+ if v == nil {
+ t.Errorf("foo key failed to written in backend")
+ }
+ return nil
+ })
+}
+
+func TestBatchTxBatchLimitCommit(t *testing.T) {
+ // start backend with batch limit 1
+ b := newBackend(tmpPath, time.Hour, 1)
+ defer cleanup(b, tmpPath)
+
+ tx := b.batchTx
+ tx.Lock()
+ tx.UnsafeCreateBucket([]byte("test"))
+ tx.UnsafePut([]byte("test"), []byte("foo"), []byte("bar"))
+ tx.Unlock()
+
+ // batch limit commit should have been triggered
+ // check whether put happens via db view
+ b.db.View(func(tx *bolt.Tx) error {
+ bucket := tx.Bucket([]byte("test"))
+ if bucket == nil {
+ t.Errorf("bucket test does not exit")
+ return nil
+ }
+ v := bucket.Get([]byte("foo"))
+ if v == nil {
+ t.Errorf("foo key failed to written in backend")
+ }
+ return nil
+ })
+}
diff --git a/vendor/github.com/coreos/etcd/storage/index.go b/vendor/github.com/coreos/etcd/storage/index.go
new file mode 100644
index 000000000..57ab5e137
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/index.go
@@ -0,0 +1,218 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "log"
+ "sort"
+ "sync"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree"
+)
+
+type index interface {
+ Get(key []byte, atRev int64) (rev, created revision, ver int64, err error)
+ Range(key, end []byte, atRev int64) ([][]byte, []revision)
+ Put(key []byte, rev revision)
+ Restore(key []byte, created, modified revision, ver int64)
+ Tombstone(key []byte, rev revision) error
+ RangeEvents(key, end []byte, rev int64) []revision
+ Compact(rev int64) map[revision]struct{}
+ Equal(b index) bool
+}
+
+type treeIndex struct {
+ sync.RWMutex
+ tree *btree.BTree
+}
+
+func newTreeIndex() index {
+ return &treeIndex{
+ tree: btree.New(32),
+ }
+}
+
+func (ti *treeIndex) Put(key []byte, rev revision) {
+ keyi := &keyIndex{key: key}
+
+ ti.Lock()
+ defer ti.Unlock()
+ item := ti.tree.Get(keyi)
+ if item == nil {
+ keyi.put(rev.main, rev.sub)
+ ti.tree.ReplaceOrInsert(keyi)
+ return
+ }
+ okeyi := item.(*keyIndex)
+ okeyi.put(rev.main, rev.sub)
+}
+
+func (ti *treeIndex) Restore(key []byte, created, modified revision, ver int64) {
+ keyi := &keyIndex{key: key}
+
+ ti.Lock()
+ defer ti.Unlock()
+ item := ti.tree.Get(keyi)
+ if item == nil {
+ keyi.restore(created, modified, ver)
+ ti.tree.ReplaceOrInsert(keyi)
+ return
+ }
+ okeyi := item.(*keyIndex)
+ okeyi.put(modified.main, modified.sub)
+}
+
+func (ti *treeIndex) Get(key []byte, atRev int64) (modified, created revision, ver int64, err error) {
+ keyi := &keyIndex{key: key}
+
+ ti.RLock()
+ defer ti.RUnlock()
+ item := ti.tree.Get(keyi)
+ if item == nil {
+ return revision{}, revision{}, 0, ErrRevisionNotFound
+ }
+
+ keyi = item.(*keyIndex)
+ return keyi.get(atRev)
+}
+
+func (ti *treeIndex) Range(key, end []byte, atRev int64) (keys [][]byte, revs []revision) {
+ if end == nil {
+ rev, _, _, err := ti.Get(key, atRev)
+ if err != nil {
+ return nil, nil
+ }
+ return [][]byte{key}, []revision{rev}
+ }
+
+ keyi := &keyIndex{key: key}
+ endi := &keyIndex{key: end}
+
+ ti.RLock()
+ defer ti.RUnlock()
+
+ ti.tree.AscendGreaterOrEqual(keyi, func(item btree.Item) bool {
+ if !item.Less(endi) {
+ return false
+ }
+ curKeyi := item.(*keyIndex)
+ rev, _, _, err := curKeyi.get(atRev)
+ if err != nil {
+ return true
+ }
+ revs = append(revs, rev)
+ keys = append(keys, curKeyi.key)
+ return true
+ })
+
+ return keys, revs
+}
+
+func (ti *treeIndex) Tombstone(key []byte, rev revision) error {
+ keyi := &keyIndex{key: key}
+
+ ti.Lock()
+ defer ti.Unlock()
+ item := ti.tree.Get(keyi)
+ if item == nil {
+ return ErrRevisionNotFound
+ }
+
+ ki := item.(*keyIndex)
+ return ki.tombstone(rev.main, rev.sub)
+}
+
+// RangeEvents returns all revisions from key(including) to end(excluding)
+// at or after the given rev. The returned slice is sorted in the order
+// of revision.
+func (ti *treeIndex) RangeEvents(key, end []byte, rev int64) []revision {
+ ti.RLock()
+ defer ti.RUnlock()
+
+ keyi := &keyIndex{key: key}
+ if end == nil {
+ item := ti.tree.Get(keyi)
+ if item == nil {
+ return nil
+ }
+ keyi = item.(*keyIndex)
+ return keyi.since(rev)
+ }
+
+ endi := &keyIndex{key: end}
+ var revs []revision
+ ti.tree.AscendGreaterOrEqual(keyi, func(item btree.Item) bool {
+ if !item.Less(endi) {
+ return false
+ }
+ curKeyi := item.(*keyIndex)
+ revs = append(revs, curKeyi.since(rev)...)
+ return true
+ })
+ sort.Sort(revisions(revs))
+
+ return revs
+}
+
+func (ti *treeIndex) Compact(rev int64) map[revision]struct{} {
+ available := make(map[revision]struct{})
+ emptyki := make([]*keyIndex, 0)
+ log.Printf("store.index: compact %d", rev)
+ // TODO: do not hold the lock for long time?
+ // This is probably OK. Compacting 10M keys takes O(10ms).
+ ti.Lock()
+ defer ti.Unlock()
+ ti.tree.Ascend(compactIndex(rev, available, &emptyki))
+ for _, ki := range emptyki {
+ item := ti.tree.Delete(ki)
+ if item == nil {
+ log.Panic("store.index: unexpected delete failure during compaction")
+ }
+ }
+ return available
+}
+
+func compactIndex(rev int64, available map[revision]struct{}, emptyki *[]*keyIndex) func(i btree.Item) bool {
+ return func(i btree.Item) bool {
+ keyi := i.(*keyIndex)
+ keyi.compact(rev, available)
+ if keyi.isEmpty() {
+ *emptyki = append(*emptyki, keyi)
+ }
+ return true
+ }
+}
+
+func (a *treeIndex) Equal(bi index) bool {
+ b := bi.(*treeIndex)
+
+ if a.tree.Len() != b.tree.Len() {
+ return false
+ }
+
+ equal := true
+
+ a.tree.Ascend(func(item btree.Item) bool {
+ aki := item.(*keyIndex)
+ bki := b.tree.Get(item).(*keyIndex)
+ if !aki.equal(bki) {
+ equal = false
+ return false
+ }
+ return true
+ })
+
+ return equal
+}
diff --git a/vendor/github.com/coreos/etcd/storage/index_test.go b/vendor/github.com/coreos/etcd/storage/index_test.go
new file mode 100644
index 000000000..5acf51836
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/index_test.go
@@ -0,0 +1,323 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestIndexGet(t *testing.T) {
+ index := newTreeIndex()
+ index.Put([]byte("foo"), revision{main: 2})
+ index.Put([]byte("foo"), revision{main: 4})
+ index.Tombstone([]byte("foo"), revision{main: 6})
+
+ tests := []struct {
+ rev int64
+
+ wrev revision
+ wcreated revision
+ wver int64
+ werr error
+ }{
+ {0, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {1, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {2, revision{main: 2}, revision{main: 2}, 1, nil},
+ {3, revision{main: 2}, revision{main: 2}, 1, nil},
+ {4, revision{main: 4}, revision{main: 2}, 2, nil},
+ {5, revision{main: 4}, revision{main: 2}, 2, nil},
+ {6, revision{}, revision{}, 0, ErrRevisionNotFound},
+ }
+ for i, tt := range tests {
+ rev, created, ver, err := index.Get([]byte("foo"), tt.rev)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if rev != tt.wrev {
+ t.Errorf("#%d: rev = %+v, want %+v", i, rev, tt.wrev)
+ }
+ if created != tt.wcreated {
+ t.Errorf("#%d: created = %+v, want %+v", i, created, tt.wcreated)
+ }
+ if ver != tt.wver {
+ t.Errorf("#%d: ver = %d, want %d", i, ver, tt.wver)
+ }
+ }
+}
+
+func TestIndexRange(t *testing.T) {
+ allKeys := [][]byte{[]byte("foo"), []byte("foo1"), []byte("foo2")}
+ allRevs := []revision{{main: 1}, {main: 2}, {main: 3}}
+
+ index := newTreeIndex()
+ for i := range allKeys {
+ index.Put(allKeys[i], allRevs[i])
+ }
+
+ atRev := int64(3)
+ tests := []struct {
+ key, end []byte
+ wkeys [][]byte
+ wrevs []revision
+ }{
+ // single key that not found
+ {
+ []byte("bar"), nil, nil, nil,
+ },
+ // single key that found
+ {
+ []byte("foo"), nil, allKeys[:1], allRevs[:1],
+ },
+ // range keys, return first member
+ {
+ []byte("foo"), []byte("foo1"), allKeys[:1], allRevs[:1],
+ },
+ // range keys, return first two members
+ {
+ []byte("foo"), []byte("foo2"), allKeys[:2], allRevs[:2],
+ },
+ // range keys, return all members
+ {
+ []byte("foo"), []byte("fop"), allKeys, allRevs,
+ },
+ // range keys, return last two members
+ {
+ []byte("foo1"), []byte("fop"), allKeys[1:], allRevs[1:],
+ },
+ // range keys, return last member
+ {
+ []byte("foo2"), []byte("fop"), allKeys[2:], allRevs[2:],
+ },
+ // range keys, return nothing
+ {
+ []byte("foo3"), []byte("fop"), nil, nil,
+ },
+ }
+ for i, tt := range tests {
+ keys, revs := index.Range(tt.key, tt.end, atRev)
+ if !reflect.DeepEqual(keys, tt.wkeys) {
+ t.Errorf("#%d: keys = %+v, want %+v", i, keys, tt.wkeys)
+ }
+ if !reflect.DeepEqual(revs, tt.wrevs) {
+ t.Errorf("#%d: revs = %+v, want %+v", i, revs, tt.wrevs)
+ }
+ }
+}
+
+func TestIndexTombstone(t *testing.T) {
+ index := newTreeIndex()
+ index.Put([]byte("foo"), revision{main: 1})
+
+ err := index.Tombstone([]byte("foo"), revision{main: 2})
+ if err != nil {
+ t.Errorf("tombstone error = %v, want nil", err)
+ }
+
+ _, _, _, err = index.Get([]byte("foo"), 2)
+ if err != ErrRevisionNotFound {
+ t.Errorf("get error = %v, want nil", err)
+ }
+ err = index.Tombstone([]byte("foo"), revision{main: 3})
+ if err != ErrRevisionNotFound {
+ t.Errorf("tombstone error = %v, want %v", err, ErrRevisionNotFound)
+ }
+}
+
+func TestIndexRangeEvents(t *testing.T) {
+ allKeys := [][]byte{[]byte("foo"), []byte("foo1"), []byte("foo2"), []byte("foo2"), []byte("foo1"), []byte("foo")}
+ allRevs := []revision{{main: 1}, {main: 2}, {main: 3}, {main: 4}, {main: 5}, {main: 6}}
+
+ index := newTreeIndex()
+ for i := range allKeys {
+ index.Put(allKeys[i], allRevs[i])
+ }
+
+ atRev := int64(1)
+ tests := []struct {
+ key, end []byte
+ wrevs []revision
+ }{
+ // single key that not found
+ {
+ []byte("bar"), nil, nil,
+ },
+ // single key that found
+ {
+ []byte("foo"), nil, []revision{{main: 1}, {main: 6}},
+ },
+ // range keys, return first member
+ {
+ []byte("foo"), []byte("foo1"), []revision{{main: 1}, {main: 6}},
+ },
+ // range keys, return first two members
+ {
+ []byte("foo"), []byte("foo2"), []revision{{main: 1}, {main: 2}, {main: 5}, {main: 6}},
+ },
+ // range keys, return all members
+ {
+ []byte("foo"), []byte("fop"), allRevs,
+ },
+ // range keys, return last two members
+ {
+ []byte("foo1"), []byte("fop"), []revision{{main: 2}, {main: 3}, {main: 4}, {main: 5}},
+ },
+ // range keys, return last member
+ {
+ []byte("foo2"), []byte("fop"), []revision{{main: 3}, {main: 4}},
+ },
+ // range keys, return nothing
+ {
+ []byte("foo3"), []byte("fop"), nil,
+ },
+ }
+ for i, tt := range tests {
+ revs := index.RangeEvents(tt.key, tt.end, atRev)
+ if !reflect.DeepEqual(revs, tt.wrevs) {
+ t.Errorf("#%d: revs = %+v, want %+v", i, revs, tt.wrevs)
+ }
+ }
+}
+
+func TestIndexCompact(t *testing.T) {
+ maxRev := int64(20)
+ tests := []struct {
+ key []byte
+ remove bool
+ rev revision
+ created revision
+ ver int64
+ }{
+ {[]byte("foo"), false, revision{main: 1}, revision{main: 1}, 1},
+ {[]byte("foo1"), false, revision{main: 2}, revision{main: 2}, 1},
+ {[]byte("foo2"), false, revision{main: 3}, revision{main: 3}, 1},
+ {[]byte("foo2"), false, revision{main: 4}, revision{main: 3}, 2},
+ {[]byte("foo"), false, revision{main: 5}, revision{main: 1}, 2},
+ {[]byte("foo1"), false, revision{main: 6}, revision{main: 2}, 2},
+ {[]byte("foo1"), true, revision{main: 7}, revision{}, 0},
+ {[]byte("foo2"), true, revision{main: 8}, revision{}, 0},
+ {[]byte("foo"), true, revision{main: 9}, revision{}, 0},
+ {[]byte("foo"), false, revision{10, 0}, revision{10, 0}, 1},
+ {[]byte("foo1"), false, revision{10, 1}, revision{10, 1}, 1},
+ }
+
+ // Continuous Compact
+ index := newTreeIndex()
+ for _, tt := range tests {
+ if tt.remove {
+ index.Tombstone(tt.key, tt.rev)
+ } else {
+ index.Put(tt.key, tt.rev)
+ }
+ }
+ for i := int64(1); i < maxRev; i++ {
+ am := index.Compact(i)
+
+ windex := newTreeIndex()
+ for _, tt := range tests {
+ if _, ok := am[tt.rev]; ok || tt.rev.GreaterThan(revision{main: i}) {
+ if tt.remove {
+ windex.Tombstone(tt.key, tt.rev)
+ } else {
+ windex.Restore(tt.key, tt.created, tt.rev, tt.ver)
+ }
+ }
+ }
+ if !index.Equal(windex) {
+ t.Errorf("#%d: not equal index", i)
+ }
+ }
+
+ // Once Compact
+ for i := int64(1); i < maxRev; i++ {
+ index := newTreeIndex()
+ for _, tt := range tests {
+ if tt.remove {
+ index.Tombstone(tt.key, tt.rev)
+ } else {
+ index.Put(tt.key, tt.rev)
+ }
+ }
+ am := index.Compact(i)
+
+ windex := newTreeIndex()
+ for _, tt := range tests {
+ if _, ok := am[tt.rev]; ok || tt.rev.GreaterThan(revision{main: i}) {
+ if tt.remove {
+ windex.Tombstone(tt.key, tt.rev)
+ } else {
+ windex.Restore(tt.key, tt.created, tt.rev, tt.ver)
+ }
+ }
+ }
+ if !index.Equal(windex) {
+ t.Errorf("#%d: not equal index", i)
+ }
+ }
+}
+
+func TestIndexRestore(t *testing.T) {
+ key := []byte("foo")
+
+ tests := []struct {
+ created revision
+ modified revision
+ ver int64
+ }{
+ {revision{1, 0}, revision{1, 0}, 1},
+ {revision{1, 0}, revision{1, 1}, 2},
+ {revision{1, 0}, revision{2, 0}, 3},
+ }
+
+ // Continuous Restore
+ index := newTreeIndex()
+ for i, tt := range tests {
+ index.Restore(key, tt.created, tt.modified, tt.ver)
+
+ modified, created, ver, err := index.Get(key, tt.modified.main)
+ if modified != tt.modified {
+ t.Errorf("#%d: modified = %v, want %v", i, modified, tt.modified)
+ }
+ if created != tt.created {
+ t.Errorf("#%d: created = %v, want %v", i, created, tt.created)
+ }
+ if ver != tt.ver {
+ t.Errorf("#%d: ver = %d, want %d", i, ver, tt.ver)
+ }
+ if err != nil {
+ t.Errorf("#%d: err = %v, want nil", i, err)
+ }
+ }
+
+ // Once Restore
+ for i, tt := range tests {
+ index := newTreeIndex()
+ index.Restore(key, tt.created, tt.modified, tt.ver)
+
+ modified, created, ver, err := index.Get(key, tt.modified.main)
+ if modified != tt.modified {
+ t.Errorf("#%d: modified = %v, want %v", i, modified, tt.modified)
+ }
+ if created != tt.created {
+ t.Errorf("#%d: created = %v, want %v", i, created, tt.created)
+ }
+ if ver != tt.ver {
+ t.Errorf("#%d: ver = %d, want %d", i, ver, tt.ver)
+ }
+ if err != nil {
+ t.Errorf("#%d: err = %v, want nil", i, err)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/key_index.go b/vendor/github.com/coreos/etcd/storage/key_index.go
new file mode 100644
index 000000000..2463a9810
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/key_index.go
@@ -0,0 +1,340 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "log"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/google/btree"
+)
+
+var (
+ ErrRevisionNotFound = errors.New("stroage: revision not found")
+)
+
+// keyIndex stores the revision of an key in the backend.
+// Each keyIndex has at least one key generation.
+// Each generation might have several key versions.
+// Tombstone on a key appends an tombstone version at the end
+// of the current generation and creates a new empty generation.
+// Each version of a key has an index pointing to the backend.
+//
+// For example: put(1.0);put(2.0);tombstone(3.0);put(4.0);tombstone(5.0) on key "foo"
+// generate a keyIndex:
+// key: "foo"
+// rev: 5
+// generations:
+// {empty}
+// {4.0, 5.0(t)}
+// {1.0, 2.0, 3.0(t)}
+//
+// Compact a keyIndex removes the versions with smaller or equal to
+// rev except the largest one. If the generations becomes empty
+// during compaction, it will be removed. if all the generations get
+// removed, the keyIndex Should be removed.
+
+// For example:
+// compact(2) on the previous example
+// generations:
+// {empty}
+// {4.0, 5.0(t)}
+// {2.0, 3.0(t)}
+//
+// compact(4)
+// generations:
+// {empty}
+// {4.0, 5.0(t)}
+//
+// compact(5):
+// generations:
+// {empty} -> key SHOULD be removed.
+//
+// compact(6):
+// generations:
+// {empty} -> key SHOULD be removed.
+type keyIndex struct {
+ key []byte
+ modified revision // the main rev of the last modification
+ generations []generation
+}
+
+// put puts a revision to the keyIndex.
+func (ki *keyIndex) put(main int64, sub int64) {
+ rev := revision{main: main, sub: sub}
+
+ if !rev.GreaterThan(ki.modified) {
+ log.Panicf("store.keyindex: put with unexpected smaller revision [%v / %v]", rev, ki.modified)
+ }
+ if len(ki.generations) == 0 {
+ ki.generations = append(ki.generations, generation{})
+ }
+ g := &ki.generations[len(ki.generations)-1]
+ if len(g.revs) == 0 { // create a new key
+ keysGauge.Inc()
+ g.created = rev
+ }
+ g.revs = append(g.revs, rev)
+ g.ver++
+ ki.modified = rev
+}
+
+func (ki *keyIndex) restore(created, modified revision, ver int64) {
+ if len(ki.generations) != 0 {
+ log.Panicf("store.keyindex: cannot restore non-empty keyIndex")
+ }
+
+ ki.modified = modified
+ g := generation{created: created, ver: ver, revs: []revision{modified}}
+ ki.generations = append(ki.generations, g)
+ keysGauge.Inc()
+}
+
+// tombstone puts a revision, pointing to a tombstone, to the keyIndex.
+// It also creates a new empty generation in the keyIndex.
+// It returns ErrRevisionNotFound when tombstone on an empty generation.
+func (ki *keyIndex) tombstone(main int64, sub int64) error {
+ if ki.isEmpty() {
+ log.Panicf("store.keyindex: unexpected tombstone on empty keyIndex %s", string(ki.key))
+ }
+ if ki.generations[len(ki.generations)-1].isEmpty() {
+ return ErrRevisionNotFound
+ }
+ ki.put(main, sub)
+ ki.generations = append(ki.generations, generation{})
+ keysGauge.Dec()
+ return nil
+}
+
+// get gets the modified, created revision and version of the key that satisfies the given atRev.
+// Rev must be higher than or equal to the given atRev.
+func (ki *keyIndex) get(atRev int64) (modified, created revision, ver int64, err error) {
+ if ki.isEmpty() {
+ log.Panicf("store.keyindex: unexpected get on empty keyIndex %s", string(ki.key))
+ }
+ g := ki.findGeneration(atRev)
+ if g.isEmpty() {
+ return revision{}, revision{}, 0, ErrRevisionNotFound
+ }
+
+ f := func(rev revision) bool {
+ if rev.main <= atRev {
+ return false
+ }
+ return true
+ }
+
+ n := g.walk(f)
+ if n != -1 {
+ return g.revs[n], g.created, g.ver - int64(len(g.revs)-n-1), nil
+ }
+
+ return revision{}, revision{}, 0, ErrRevisionNotFound
+}
+
+// since returns revisions since the give rev. Only the revision with the
+// largest sub revision will be returned if multiple revisions have the same
+// main revision.
+func (ki *keyIndex) since(rev int64) []revision {
+ if ki.isEmpty() {
+ log.Panicf("store.keyindex: unexpected get on empty keyIndex %s", string(ki.key))
+ }
+ since := revision{rev, 0}
+ var gi int
+ // find the generations to start checking
+ for gi = len(ki.generations) - 1; gi > 0; gi-- {
+ g := ki.generations[gi]
+ if g.isEmpty() {
+ continue
+ }
+ if since.GreaterThan(g.created) {
+ break
+ }
+ }
+
+ var revs []revision
+ var last int64
+ for ; gi < len(ki.generations); gi++ {
+ for _, r := range ki.generations[gi].revs {
+ if since.GreaterThan(r) {
+ continue
+ }
+ if r.main == last {
+ // replace the revision with a new one that has higher sub value,
+ // because the original one should not be seen by external
+ revs[len(revs)-1] = r
+ continue
+ }
+ revs = append(revs, r)
+ last = r.main
+ }
+ }
+ return revs
+}
+
+// compact compacts a keyIndex by removing the versions with smaller or equal
+// revision than the given atRev except the largest one (If the largest one is
+// a tombstone, it will not be kept).
+// If a generation becomes empty during compaction, it will be removed.
+func (ki *keyIndex) compact(atRev int64, available map[revision]struct{}) {
+ if ki.isEmpty() {
+ log.Panicf("store.keyindex: unexpected compact on empty keyIndex %s", string(ki.key))
+ }
+
+ // walk until reaching the first revision that has an revision smaller or equal to
+ // the atRevision.
+ // add it to the available map
+ f := func(rev revision) bool {
+ if rev.main <= atRev {
+ available[rev] = struct{}{}
+ return false
+ }
+ return true
+ }
+
+ i, g := 0, &ki.generations[0]
+ // find first generation includes atRev or created after atRev
+ for i < len(ki.generations)-1 {
+ if tomb := g.revs[len(g.revs)-1].main; tomb > atRev {
+ break
+ }
+ i++
+ g = &ki.generations[i]
+ }
+
+ if !g.isEmpty() {
+ n := g.walk(f)
+ // remove the previous contents.
+ if n != -1 {
+ g.revs = g.revs[n:]
+ }
+ // remove any tombstone
+ if len(g.revs) == 1 && i != len(ki.generations)-1 {
+ delete(available, g.revs[0])
+ i++
+ }
+ }
+ // remove the previous generations.
+ ki.generations = ki.generations[i:]
+ return
+}
+
+func (ki *keyIndex) isEmpty() bool {
+ return len(ki.generations) == 1 && ki.generations[0].isEmpty()
+}
+
+// findGeneartion finds out the generation of the keyIndex that the
+// given rev belongs to. If the given rev is at the gap of two generations,
+// which means that the key does not exist at the given rev, it returns nil.
+func (ki *keyIndex) findGeneration(rev int64) *generation {
+ lastg := len(ki.generations) - 1
+ cg := lastg
+
+ for cg >= 0 {
+ if len(ki.generations[cg].revs) == 0 {
+ cg--
+ continue
+ }
+ g := ki.generations[cg]
+ if cg != lastg {
+ if tomb := g.revs[len(g.revs)-1].main; tomb <= rev {
+ return nil
+ }
+ }
+ if g.revs[0].main <= rev {
+ return &ki.generations[cg]
+ }
+ cg--
+ }
+ return nil
+}
+
+func (a *keyIndex) Less(b btree.Item) bool {
+ return bytes.Compare(a.key, b.(*keyIndex).key) == -1
+}
+
+func (a *keyIndex) equal(b *keyIndex) bool {
+ if !bytes.Equal(a.key, b.key) {
+ return false
+ }
+ if a.modified != b.modified {
+ return false
+ }
+ if len(a.generations) != len(b.generations) {
+ return false
+ }
+ for i := range a.generations {
+ ag, bg := a.generations[i], b.generations[i]
+ if !ag.equal(bg) {
+ return false
+ }
+ }
+ return true
+}
+
+func (ki *keyIndex) String() string {
+ var s string
+ for _, g := range ki.generations {
+ s += g.String()
+ }
+ return s
+}
+
+type generation struct {
+ ver int64
+ created revision // when the generation is created (put in first revision).
+ revs []revision
+}
+
+func (g *generation) isEmpty() bool { return g == nil || len(g.revs) == 0 }
+
+// walk walks through the revisions in the generation in descending order.
+// It passes the revision to the given function.
+// walk returns until: 1. it finishs walking all pairs 2. the function returns false.
+// walk returns the position at where it stopped. If it stopped after
+// finishing walking, -1 will be returned.
+func (g *generation) walk(f func(rev revision) bool) int {
+ l := len(g.revs)
+ for i := range g.revs {
+ ok := f(g.revs[l-i-1])
+ if !ok {
+ return l - i - 1
+ }
+ }
+ return -1
+}
+
+func (g *generation) String() string {
+ return fmt.Sprintf("g: created[%d] ver[%d], revs %#v\n", g.created, g.ver, g.revs)
+}
+
+func (a generation) equal(b generation) bool {
+ if a.ver != b.ver {
+ return false
+ }
+ if len(a.revs) != len(b.revs) {
+ return false
+ }
+
+ for i := range a.revs {
+ ar, br := a.revs[i], b.revs[i]
+ if ar != br {
+ return false
+ }
+ }
+ return true
+}
diff --git a/vendor/github.com/coreos/etcd/storage/key_index_test.go b/vendor/github.com/coreos/etcd/storage/key_index_test.go
new file mode 100644
index 000000000..01d903315
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/key_index_test.go
@@ -0,0 +1,654 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestKeyIndexGet(t *testing.T) {
+ // key: "foo"
+ // rev: 16
+ // generations:
+ // {empty}
+ // {{14, 0}[1], {14, 1}[2], {16, 0}(t)[3]}
+ // {{8, 0}[1], {10, 0}[2], {12, 0}(t)[3]}
+ // {{2, 0}[1], {4, 0}[2], {6, 0}(t)[3]}
+ ki := newTestKeyIndex()
+ ki.compact(4, make(map[revision]struct{}))
+
+ tests := []struct {
+ rev int64
+
+ wmod revision
+ wcreat revision
+ wver int64
+ werr error
+ }{
+ {17, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {16, revision{}, revision{}, 0, ErrRevisionNotFound},
+
+ // get on generation 3
+ {15, revision{14, 1}, revision{14, 0}, 2, nil},
+ {14, revision{14, 1}, revision{14, 0}, 2, nil},
+
+ {13, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {12, revision{}, revision{}, 0, ErrRevisionNotFound},
+
+ // get on generation 2
+ {11, revision{10, 0}, revision{8, 0}, 2, nil},
+ {10, revision{10, 0}, revision{8, 0}, 2, nil},
+ {9, revision{8, 0}, revision{8, 0}, 1, nil},
+ {8, revision{8, 0}, revision{8, 0}, 1, nil},
+
+ {7, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {6, revision{}, revision{}, 0, ErrRevisionNotFound},
+
+ // get on generation 1
+ {5, revision{4, 0}, revision{2, 0}, 2, nil},
+ {4, revision{4, 0}, revision{2, 0}, 2, nil},
+
+ {3, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {2, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {1, revision{}, revision{}, 0, ErrRevisionNotFound},
+ {0, revision{}, revision{}, 0, ErrRevisionNotFound},
+ }
+
+ for i, tt := range tests {
+ mod, creat, ver, err := ki.get(tt.rev)
+ if err != tt.werr {
+ t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
+ }
+ if mod != tt.wmod {
+ t.Errorf("#%d: modified = %+v, want %+v", i, mod, tt.wmod)
+ }
+ if creat != tt.wcreat {
+ t.Errorf("#%d: created = %+v, want %+v", i, creat, tt.wcreat)
+ }
+ if ver != tt.wver {
+ t.Errorf("#%d: version = %d, want %d", i, ver, tt.wver)
+ }
+ }
+}
+
+func TestKeyIndexSince(t *testing.T) {
+ ki := newTestKeyIndex()
+ ki.compact(4, make(map[revision]struct{}))
+
+ allRevs := []revision{{4, 0}, {6, 0}, {8, 0}, {10, 0}, {12, 0}, {14, 1}, {16, 0}}
+ tests := []struct {
+ rev int64
+
+ wrevs []revision
+ }{
+ {17, nil},
+ {16, allRevs[6:]},
+ {15, allRevs[6:]},
+ {14, allRevs[5:]},
+ {13, allRevs[5:]},
+ {12, allRevs[4:]},
+ {11, allRevs[4:]},
+ {10, allRevs[3:]},
+ {9, allRevs[3:]},
+ {8, allRevs[2:]},
+ {7, allRevs[2:]},
+ {6, allRevs[1:]},
+ {5, allRevs[1:]},
+ {4, allRevs},
+ {3, allRevs},
+ {2, allRevs},
+ {1, allRevs},
+ {0, allRevs},
+ }
+
+ for i, tt := range tests {
+ revs := ki.since(tt.rev)
+ if !reflect.DeepEqual(revs, tt.wrevs) {
+ t.Errorf("#%d: revs = %+v, want %+v", i, revs, tt.wrevs)
+ }
+ }
+}
+
+func TestKeyIndexPut(t *testing.T) {
+ ki := &keyIndex{key: []byte("foo")}
+ ki.put(5, 0)
+
+ wki := &keyIndex{
+ key: []byte("foo"),
+ modified: revision{5, 0},
+ generations: []generation{{created: revision{5, 0}, ver: 1, revs: []revision{{main: 5}}}},
+ }
+ if !reflect.DeepEqual(ki, wki) {
+ t.Errorf("ki = %+v, want %+v", ki, wki)
+ }
+
+ ki.put(7, 0)
+
+ wki = &keyIndex{
+ key: []byte("foo"),
+ modified: revision{7, 0},
+ generations: []generation{{created: revision{5, 0}, ver: 2, revs: []revision{{main: 5}, {main: 7}}}},
+ }
+ if !reflect.DeepEqual(ki, wki) {
+ t.Errorf("ki = %+v, want %+v", ki, wki)
+ }
+}
+
+func TestKeyIndexRestore(t *testing.T) {
+ ki := &keyIndex{key: []byte("foo")}
+ ki.restore(revision{5, 0}, revision{7, 0}, 2)
+
+ wki := &keyIndex{
+ key: []byte("foo"),
+ modified: revision{7, 0},
+ generations: []generation{{created: revision{5, 0}, ver: 2, revs: []revision{{main: 7}}}},
+ }
+ if !reflect.DeepEqual(ki, wki) {
+ t.Errorf("ki = %+v, want %+v", ki, wki)
+ }
+}
+
+func TestKeyIndexTombstone(t *testing.T) {
+ ki := &keyIndex{key: []byte("foo")}
+ ki.put(5, 0)
+
+ err := ki.tombstone(7, 0)
+ if err != nil {
+ t.Errorf("unexpected tombstone error: %v", err)
+ }
+
+ wki := &keyIndex{
+ key: []byte("foo"),
+ modified: revision{7, 0},
+ generations: []generation{{created: revision{5, 0}, ver: 2, revs: []revision{{main: 5}, {main: 7}}}, {}},
+ }
+ if !reflect.DeepEqual(ki, wki) {
+ t.Errorf("ki = %+v, want %+v", ki, wki)
+ }
+
+ ki.put(8, 0)
+ ki.put(9, 0)
+ err = ki.tombstone(15, 0)
+ if err != nil {
+ t.Errorf("unexpected tombstone error: %v", err)
+ }
+
+ wki = &keyIndex{
+ key: []byte("foo"),
+ modified: revision{15, 0},
+ generations: []generation{
+ {created: revision{5, 0}, ver: 2, revs: []revision{{main: 5}, {main: 7}}},
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 9}, {main: 15}}},
+ {},
+ },
+ }
+ if !reflect.DeepEqual(ki, wki) {
+ t.Errorf("ki = %+v, want %+v", ki, wki)
+ }
+
+ err = ki.tombstone(16, 0)
+ if err != ErrRevisionNotFound {
+ t.Errorf("tombstone error = %v, want %v", err, ErrRevisionNotFound)
+ }
+}
+
+func TestKeyIndexCompact(t *testing.T) {
+ tests := []struct {
+ compact int64
+
+ wki *keyIndex
+ wam map[revision]struct{}
+ }{
+ {
+ 1,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{2, 0}, ver: 3, revs: []revision{{main: 2}, {main: 4}, {main: 6}}},
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{},
+ },
+ {
+ 2,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{2, 0}, ver: 3, revs: []revision{{main: 2}, {main: 4}, {main: 6}}},
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 2}: {},
+ },
+ },
+ {
+ 3,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{2, 0}, ver: 3, revs: []revision{{main: 2}, {main: 4}, {main: 6}}},
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 2}: {},
+ },
+ },
+ {
+ 4,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{2, 0}, ver: 3, revs: []revision{{main: 4}, {main: 6}}},
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 4}: {},
+ },
+ },
+ {
+ 5,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{2, 0}, ver: 3, revs: []revision{{main: 4}, {main: 6}}},
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 4}: {},
+ },
+ },
+ {
+ 6,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{},
+ },
+ {
+ 7,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{},
+ },
+ {
+ 8,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 8}: {},
+ },
+ },
+ {
+ 9,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 8}: {},
+ },
+ },
+ {
+ 10,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 10}: {},
+ },
+ },
+ {
+ 11,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{8, 0}, ver: 3, revs: []revision{{main: 10}, {main: 12}}},
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 10}: {},
+ },
+ },
+ {
+ 12,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{},
+ },
+ {
+ 13,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{},
+ },
+ {
+ 14,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 14, sub: 1}: {},
+ },
+ },
+ {
+ 15,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14, sub: 1}, {main: 16}}},
+ {},
+ },
+ },
+ map[revision]struct{}{
+ revision{main: 14, sub: 1}: {},
+ },
+ },
+ {
+ 16,
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{16, 0},
+ generations: []generation{
+ {},
+ },
+ },
+ map[revision]struct{}{},
+ },
+ }
+
+ // Continuous Compaction
+ ki := newTestKeyIndex()
+ for i, tt := range tests {
+ am := make(map[revision]struct{})
+ ki.compact(tt.compact, am)
+ if !reflect.DeepEqual(ki, tt.wki) {
+ t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
+ }
+ if !reflect.DeepEqual(am, tt.wam) {
+ t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam)
+ }
+ }
+
+ // Jump Compaction
+ ki = newTestKeyIndex()
+ for i, tt := range tests {
+ if (i%2 == 0 && i < 6) || (i%2 == 1 && i > 6) {
+ am := make(map[revision]struct{})
+ ki.compact(tt.compact, am)
+ if !reflect.DeepEqual(ki, tt.wki) {
+ t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
+ }
+ if !reflect.DeepEqual(am, tt.wam) {
+ t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam)
+ }
+ }
+ }
+
+ // Once Compaction
+ for i, tt := range tests {
+ ki := newTestKeyIndex()
+ am := make(map[revision]struct{})
+ ki.compact(tt.compact, am)
+ if !reflect.DeepEqual(ki, tt.wki) {
+ t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki)
+ }
+ if !reflect.DeepEqual(am, tt.wam) {
+ t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam)
+ }
+ }
+}
+
+// test that compact on version that higher than last modified version works well
+func TestKeyIndexCompactOnFurtherRev(t *testing.T) {
+ ki := &keyIndex{key: []byte("foo")}
+ ki.put(1, 0)
+ ki.put(2, 0)
+ am := make(map[revision]struct{})
+ ki.compact(3, am)
+
+ wki := &keyIndex{
+ key: []byte("foo"),
+ modified: revision{2, 0},
+ generations: []generation{
+ {created: revision{1, 0}, ver: 2, revs: []revision{{main: 2}}},
+ },
+ }
+ wam := map[revision]struct{}{
+ revision{main: 2}: {},
+ }
+ if !reflect.DeepEqual(ki, wki) {
+ t.Errorf("ki = %+v, want %+v", ki, wki)
+ }
+ if !reflect.DeepEqual(am, wam) {
+ t.Errorf("am = %+v, want %+v", am, wam)
+ }
+}
+
+func TestKeyIndexIsEmpty(t *testing.T) {
+ tests := []struct {
+ ki *keyIndex
+ w bool
+ }{
+ {
+ &keyIndex{
+ key: []byte("foo"),
+ generations: []generation{{}},
+ },
+ true,
+ },
+ {
+ &keyIndex{
+ key: []byte("foo"),
+ modified: revision{2, 0},
+ generations: []generation{
+ {created: revision{1, 0}, ver: 2, revs: []revision{{main: 2}}},
+ },
+ },
+ false,
+ },
+ }
+ for i, tt := range tests {
+ g := tt.ki.isEmpty()
+ if g != tt.w {
+ t.Errorf("#%d: isEmpty = %v, want %v", i, g, tt.w)
+ }
+ }
+}
+
+func TestKeyIndexFindGeneration(t *testing.T) {
+ ki := newTestKeyIndex()
+
+ tests := []struct {
+ rev int64
+ wg *generation
+ }{
+ {0, nil},
+ {1, nil},
+ {2, &ki.generations[0]},
+ {3, &ki.generations[0]},
+ {4, &ki.generations[0]},
+ {5, &ki.generations[0]},
+ {6, nil},
+ {7, nil},
+ {8, &ki.generations[1]},
+ {9, &ki.generations[1]},
+ {10, &ki.generations[1]},
+ {11, &ki.generations[1]},
+ {12, nil},
+ {13, nil},
+ }
+ for i, tt := range tests {
+ g := ki.findGeneration(tt.rev)
+ if g != tt.wg {
+ t.Errorf("#%d: generation = %+v, want %+v", i, g, tt.wg)
+ }
+ }
+}
+
+func TestKeyIndexLess(t *testing.T) {
+ ki := &keyIndex{key: []byte("foo")}
+
+ tests := []struct {
+ ki *keyIndex
+ w bool
+ }{
+ {&keyIndex{key: []byte("doo")}, false},
+ {&keyIndex{key: []byte("foo")}, false},
+ {&keyIndex{key: []byte("goo")}, true},
+ }
+ for i, tt := range tests {
+ g := ki.Less(tt.ki)
+ if g != tt.w {
+ t.Errorf("#%d: Less = %v, want %v", i, g, tt.w)
+ }
+ }
+}
+
+func TestGenerationIsEmpty(t *testing.T) {
+ tests := []struct {
+ g *generation
+ w bool
+ }{
+ {nil, true},
+ {&generation{}, true},
+ {&generation{revs: []revision{{main: 1}}}, false},
+ }
+ for i, tt := range tests {
+ g := tt.g.isEmpty()
+ if g != tt.w {
+ t.Errorf("#%d: isEmpty = %v, want %v", i, g, tt.w)
+ }
+ }
+}
+
+func TestGenerationWalk(t *testing.T) {
+ g := &generation{
+ ver: 3,
+ created: revision{2, 0},
+ revs: []revision{{main: 2}, {main: 4}, {main: 6}},
+ }
+ tests := []struct {
+ f func(rev revision) bool
+ wi int
+ }{
+ {func(rev revision) bool { return rev.main >= 7 }, 2},
+ {func(rev revision) bool { return rev.main >= 6 }, 1},
+ {func(rev revision) bool { return rev.main >= 5 }, 1},
+ {func(rev revision) bool { return rev.main >= 4 }, 0},
+ {func(rev revision) bool { return rev.main >= 3 }, 0},
+ {func(rev revision) bool { return rev.main >= 2 }, -1},
+ }
+ for i, tt := range tests {
+ idx := g.walk(tt.f)
+ if idx != tt.wi {
+ t.Errorf("#%d: index = %d, want %d", i, idx, tt.wi)
+ }
+ }
+}
+
+func newTestKeyIndex() *keyIndex {
+ // key: "foo"
+ // rev: 16
+ // generations:
+ // {empty}
+ // {{14, 0}[1], {14, 1}[2], {16, 0}(t)[3]}
+ // {{8, 0}[1], {10, 0}[2], {12, 0}(t)[3]}
+ // {{2, 0}[1], {4, 0}[2], {6, 0}(t)[3]}
+
+ ki := &keyIndex{key: []byte("foo")}
+ ki.put(2, 0)
+ ki.put(4, 0)
+ ki.tombstone(6, 0)
+ ki.put(8, 0)
+ ki.put(10, 0)
+ ki.tombstone(12, 0)
+ ki.put(14, 0)
+ ki.put(14, 1)
+ ki.tombstone(16, 0)
+ return ki
+}
diff --git a/vendor/github.com/coreos/etcd/storage/kv.go b/vendor/github.com/coreos/etcd/storage/kv.go
new file mode 100644
index 000000000..ceec4f0cd
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/kv.go
@@ -0,0 +1,105 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "github.com/coreos/etcd/storage/backend"
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+// CancelFunc tells an operation to abandon its work. A CancelFunc does not
+// wait for the work to stop.
+type CancelFunc func()
+
+type Snapshot backend.Snapshot
+
+type KV interface {
+ // Rev returns the current revision of the KV.
+ Rev() int64
+
+ // Range gets the keys in the range at rangeRev.
+ // If rangeRev <=0, range gets the keys at currentRev.
+ // If `end` is nil, the request returns the key.
+ // If `end` is not nil, it gets the keys in range [key, range_end).
+ // Limit limits the number of keys returned.
+ // If the required rev is compacted, ErrCompacted will be returned.
+ Range(key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error)
+
+ // Put puts the given key,value into the store.
+ // A put also increases the rev of the store, and generates one event in the event history.
+ Put(key, value []byte) (rev int64)
+
+ // DeleteRange deletes the given range from the store.
+ // A deleteRange increases the rev of the store if any key in the range exists.
+ // The number of key deleted will be returned.
+ // It also generates one event for each key delete in the event history.
+ // if the `end` is nil, deleteRange deletes the key.
+ // if the `end` is not nil, deleteRange deletes the keys in range [key, range_end).
+ DeleteRange(key, end []byte) (n, rev int64)
+
+ // TxnBegin begins a txn. Only Txn prefixed operation can be executed, others will be blocked
+ // until txn ends. Only one on-going txn is allowed.
+ // TxnBegin returns an int64 txn ID.
+ // All txn prefixed operations with same txn ID will be done with the same rev.
+ TxnBegin() int64
+ // TxnEnd ends the on-going txn with txn ID. If the on-going txn ID is not matched, error is returned.
+ TxnEnd(txnID int64) error
+ TxnRange(txnID int64, key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error)
+ TxnPut(txnID int64, key, value []byte) (rev int64, err error)
+ TxnDeleteRange(txnID int64, key, end []byte) (n, rev int64, err error)
+
+ Compact(rev int64) error
+
+ // Get the hash of KV state.
+ // This method is designed for consistency checking purpose.
+ Hash() (uint32, error)
+
+ // Snapshot snapshots the full KV store.
+ Snapshot() Snapshot
+
+ Restore() error
+ Close() error
+}
+
+// Watcher watches on the KV. It will be notified if there is an event
+// happened on the watched key or prefix.
+type Watcher interface {
+ // Event returns a channel that receives observed event that matches the
+ // context of watcher. When watch finishes or is canceled or aborted, the
+ // channel is closed and returns empty event.
+ // Successive calls to Event return the same value.
+ Event() <-chan storagepb.Event
+
+ // Err returns a non-nil error value after Event is closed. Err returns
+ // Compacted if the history was compacted, Canceled if watch is canceled,
+ // or EOF if watch reaches the end revision. No other values for Err are defined.
+ // After Event is closed, successive calls to Err return the same value.
+ Err() error
+}
+
+// WatchableKV is a KV that can be watched.
+type WatchableKV interface {
+ KV
+
+ // Watcher watches the events happening or happened on the given key
+ // or key prefix from the given startRev.
+ // The whole event history can be watched unless compacted.
+ // If `prefix` is true, watch observes all events whose key prefix could be the given `key`.
+ // If `startRev` <=0, watch observes events after currentRev.
+ //
+ // Canceling the watcher releases resources associated with it, so code
+ // should always call cancel as soon as watch is done.
+ Watcher(key []byte, prefix bool, startRev int64) (Watcher, CancelFunc)
+}
diff --git a/vendor/github.com/coreos/etcd/storage/kv_test.go b/vendor/github.com/coreos/etcd/storage/kv_test.go
new file mode 100644
index 000000000..f0db69481
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/kv_test.go
@@ -0,0 +1,825 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+ "path"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+// Functional tests for features implemented in v3 store. It treats v3 store
+// as a black box, and tests it by feeding the input and validating the output.
+
+// TODO: add similar tests on operations in one txn/rev
+
+type (
+ rangeFunc func(kv KV, key, end []byte, limit, rangeRev int64) ([]storagepb.KeyValue, int64, error)
+ putFunc func(kv KV, key, value []byte) int64
+ deleteRangeFunc func(kv KV, key, end []byte) (n, rev int64)
+)
+
+var (
+ normalRangeFunc = func(kv KV, key, end []byte, limit, rangeRev int64) ([]storagepb.KeyValue, int64, error) {
+ return kv.Range(key, end, limit, rangeRev)
+ }
+ txnRangeFunc = func(kv KV, key, end []byte, limit, rangeRev int64) ([]storagepb.KeyValue, int64, error) {
+ id := kv.TxnBegin()
+ defer kv.TxnEnd(id)
+ return kv.TxnRange(id, key, end, limit, rangeRev)
+ }
+
+ normalPutFunc = func(kv KV, key, value []byte) int64 {
+ return kv.Put(key, value)
+ }
+ txnPutFunc = func(kv KV, key, value []byte) int64 {
+ id := kv.TxnBegin()
+ defer kv.TxnEnd(id)
+ rev, err := kv.TxnPut(id, key, value)
+ if err != nil {
+ panic("txn put error")
+ }
+ return rev
+ }
+
+ normalDeleteRangeFunc = func(kv KV, key, end []byte) (n, rev int64) {
+ return kv.DeleteRange(key, end)
+ }
+ txnDeleteRangeFunc = func(kv KV, key, end []byte) (n, rev int64) {
+ id := kv.TxnBegin()
+ defer kv.TxnEnd(id)
+ n, rev, err := kv.TxnDeleteRange(id, key, end)
+ if err != nil {
+ panic("txn delete error")
+ }
+ return n, rev
+ }
+
+ tmpPath string
+)
+
+func init() {
+ tmpDir, err := ioutil.TempDir(os.TempDir(), "etcd_test_storage")
+ if err != nil {
+ log.Fatal(err)
+ }
+ tmpPath = path.Join(tmpDir, "database")
+}
+
+func TestKVRange(t *testing.T) { testKVRange(t, normalRangeFunc) }
+func TestKVTxnRange(t *testing.T) { testKVRange(t, txnRangeFunc) }
+
+func testKVRange(t *testing.T, f rangeFunc) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+ kvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ {Key: []byte("foo1"), Value: []byte("bar1"), CreateRevision: 2, ModRevision: 2, Version: 1},
+ {Key: []byte("foo2"), Value: []byte("bar2"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ }
+
+ wrev := int64(3)
+ tests := []struct {
+ key, end []byte
+ wkvs []storagepb.KeyValue
+ }{
+ // get no keys
+ {
+ []byte("doo"), []byte("foo"),
+ nil,
+ },
+ // get no keys when key == end
+ {
+ []byte("foo"), []byte("foo"),
+ nil,
+ },
+ // get no keys when ranging single key
+ {
+ []byte("doo"), nil,
+ nil,
+ },
+ // get all keys
+ {
+ []byte("foo"), []byte("foo3"),
+ kvs,
+ },
+ // get partial keys
+ {
+ []byte("foo"), []byte("foo1"),
+ kvs[:1],
+ },
+ // get single key
+ {
+ []byte("foo"), nil,
+ kvs[:1],
+ },
+ }
+
+ for i, tt := range tests {
+ kvs, rev, err := f(s, tt.key, tt.end, 0, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if rev != wrev {
+ t.Errorf("#%d: rev = %d, want %d", i, rev, wrev)
+ }
+ if !reflect.DeepEqual(kvs, tt.wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, tt.wkvs)
+ }
+ }
+}
+
+func TestKVRangeRev(t *testing.T) { testKVRangeRev(t, normalRangeFunc) }
+func TestKVTxnRangeRev(t *testing.T) { testKVRangeRev(t, normalRangeFunc) }
+
+func testKVRangeRev(t *testing.T, f rangeFunc) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+ kvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ {Key: []byte("foo1"), Value: []byte("bar1"), CreateRevision: 2, ModRevision: 2, Version: 1},
+ {Key: []byte("foo2"), Value: []byte("bar2"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ }
+
+ tests := []struct {
+ rev int64
+ wrev int64
+ wkvs []storagepb.KeyValue
+ }{
+ {-1, 3, kvs},
+ {0, 3, kvs},
+ {1, 1, kvs[:1]},
+ {2, 2, kvs[:2]},
+ {3, 3, kvs},
+ }
+
+ for i, tt := range tests {
+ kvs, rev, err := f(s, []byte("foo"), []byte("foo3"), 0, tt.rev)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if rev != tt.wrev {
+ t.Errorf("#%d: rev = %d, want %d", i, rev, tt.wrev)
+ }
+ if !reflect.DeepEqual(kvs, tt.wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, tt.wkvs)
+ }
+ }
+}
+
+func TestKVRangeBadRev(t *testing.T) { testKVRangeBadRev(t, normalRangeFunc) }
+func TestKVTxnRangeBadRev(t *testing.T) { testKVRangeBadRev(t, normalRangeFunc) }
+
+func testKVRangeBadRev(t *testing.T, f rangeFunc) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+ if err := s.Compact(3); err != nil {
+ t.Fatalf("compact error (%v)", err)
+ }
+
+ tests := []struct {
+ rev int64
+ werr error
+ }{
+ {-1, ErrCompacted},
+ {2, ErrCompacted},
+ {3, ErrCompacted},
+ {4, ErrFutureRev},
+ {100, ErrFutureRev},
+ }
+ for i, tt := range tests {
+ _, _, err := f(s, []byte("foo"), []byte("foo3"), 0, tt.rev)
+ if err != tt.werr {
+ t.Errorf("#%d: error = %v, want %v", i, err, tt.werr)
+ }
+ }
+}
+
+func TestKVRangeLimit(t *testing.T) { testKVRangeLimit(t, normalRangeFunc) }
+func TestKVTxnRangeLimit(t *testing.T) { testKVRangeLimit(t, txnRangeFunc) }
+
+func testKVRangeLimit(t *testing.T, f rangeFunc) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+ kvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ {Key: []byte("foo1"), Value: []byte("bar1"), CreateRevision: 2, ModRevision: 2, Version: 1},
+ {Key: []byte("foo2"), Value: []byte("bar2"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ }
+
+ wrev := int64(3)
+ tests := []struct {
+ limit int64
+ wkvs []storagepb.KeyValue
+ }{
+ // no limit
+ {-1, kvs},
+ // no limit
+ {0, kvs},
+ {1, kvs[:1]},
+ {2, kvs[:2]},
+ {3, kvs},
+ {100, kvs},
+ }
+ for i, tt := range tests {
+ kvs, rev, err := f(s, []byte("foo"), []byte("foo3"), tt.limit, 0)
+ if err != nil {
+ t.Fatalf("#%d: range error (%v)", i, err)
+ }
+ if !reflect.DeepEqual(kvs, tt.wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, tt.wkvs)
+ }
+ if rev != wrev {
+ t.Errorf("#%d: rev = %d, want %d", i, rev, wrev)
+ }
+ }
+}
+
+func TestKVPutMultipleTimes(t *testing.T) { testKVPutMultipleTimes(t, normalPutFunc) }
+func TestKVTxnPutMultipleTimes(t *testing.T) { testKVPutMultipleTimes(t, txnPutFunc) }
+
+func testKVPutMultipleTimes(t *testing.T, f putFunc) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ for i := 0; i < 10; i++ {
+ base := int64(i + 1)
+
+ rev := f(s, []byte("foo"), []byte("bar"))
+ if wrev := base; rev != wrev {
+ t.Errorf("#%d: rev = %d, want %d", i, rev, base)
+ }
+
+ kvs, _, err := s.Range([]byte("foo"), nil, 0, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wkvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: base, Version: base},
+ }
+ if !reflect.DeepEqual(kvs, wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, wkvs)
+ }
+ }
+}
+
+func TestKVDeleteRange(t *testing.T) { testKVDeleteRange(t, normalDeleteRangeFunc) }
+func TestKVTxnDeleteRange(t *testing.T) { testKVDeleteRange(t, txnDeleteRangeFunc) }
+
+func testKVDeleteRange(t *testing.T, f deleteRangeFunc) {
+ tests := []struct {
+ key, end []byte
+
+ wrev int64
+ wN int64
+ }{
+ {
+ []byte("foo"), nil,
+ 4, 1,
+ },
+ {
+ []byte("foo"), []byte("foo1"),
+ 4, 1,
+ },
+ {
+ []byte("foo"), []byte("foo2"),
+ 4, 2,
+ },
+ {
+ []byte("foo"), []byte("foo3"),
+ 4, 3,
+ },
+ {
+ []byte("foo3"), []byte("foo8"),
+ 3, 0,
+ },
+ {
+ []byte("foo3"), nil,
+ 3, 0,
+ },
+ }
+
+ for i, tt := range tests {
+ s := New(tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+
+ n, rev := f(s, tt.key, tt.end)
+ if n != tt.wN || rev != tt.wrev {
+ t.Errorf("#%d: n = %d, rev = %d, want (%d, %d)", i, n, rev, tt.wN, tt.wrev)
+ }
+
+ cleanup(s, tmpPath)
+ }
+}
+
+func TestKVDeleteMultipleTimes(t *testing.T) { testKVDeleteMultipleTimes(t, normalDeleteRangeFunc) }
+func TestKVTxnDeleteMultipleTimes(t *testing.T) { testKVDeleteMultipleTimes(t, txnDeleteRangeFunc) }
+
+func testKVDeleteMultipleTimes(t *testing.T, f deleteRangeFunc) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+
+ n, rev := f(s, []byte("foo"), nil)
+ if n != 1 || rev != 2 {
+ t.Fatalf("n = %d, rev = %d, want (%d, %d)", n, rev, 1, 2)
+ }
+
+ for i := 0; i < 10; i++ {
+ n, rev := f(s, []byte("foo"), nil)
+ if n != 0 || rev != 2 {
+ t.Fatalf("#%d: n = %d, rev = %d, want (%d, %d)", i, n, rev, 0, 2)
+ }
+ }
+}
+
+// test that range, put, delete on single key in sequence repeatedly works correctly.
+func TestKVOperationInSequence(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ for i := 0; i < 10; i++ {
+ base := int64(i * 2)
+
+ // put foo
+ rev := s.Put([]byte("foo"), []byte("bar"))
+ if rev != base+1 {
+ t.Errorf("#%d: put rev = %d, want %d", i, rev, base+1)
+ }
+
+ kvs, rev, err := s.Range([]byte("foo"), nil, 0, base+1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wkvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: base + 1, ModRevision: base + 1, Version: 1},
+ }
+ if !reflect.DeepEqual(kvs, wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, wkvs)
+ }
+ if rev != base+1 {
+ t.Errorf("#%d: range rev = %d, want %d", i, rev, base+1)
+ }
+
+ // delete foo
+ n, rev := s.DeleteRange([]byte("foo"), nil)
+ if n != 1 || rev != base+2 {
+ t.Errorf("#%d: n = %d, rev = %d, want (%d, %d)", i, n, rev, 1, base+2)
+ }
+
+ kvs, rev, err = s.Range([]byte("foo"), nil, 0, base+2)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if kvs != nil {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, nil)
+ }
+ if rev != base+2 {
+ t.Errorf("#%d: range rev = %d, want %d", i, rev, base+2)
+ }
+ }
+}
+
+func TestKVTxnBlockNonTnxOperations(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ tests := []func(){
+ func() { s.Range([]byte("foo"), nil, 0, 0) },
+ func() { s.Put([]byte("foo"), nil) },
+ func() { s.DeleteRange([]byte("foo"), nil) },
+ }
+ for i, tt := range tests {
+ id := s.TxnBegin()
+ done := make(chan struct{})
+ go func() {
+ tt()
+ done <- struct{}{}
+ }()
+ select {
+ case <-done:
+ t.Fatalf("#%d: operation failed to be blocked", i)
+ case <-time.After(10 * time.Millisecond):
+ }
+
+ s.TxnEnd(id)
+ select {
+ case <-done:
+ case <-time.After(100 * time.Millisecond):
+ t.Fatalf("#%d: operation failed to be unblocked", i)
+ }
+ }
+}
+
+func TestKVTxnWrongID(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ id := s.TxnBegin()
+ wrongid := id + 1
+
+ tests := []func() error{
+ func() error {
+ _, _, err := s.TxnRange(wrongid, []byte("foo"), nil, 0, 0)
+ return err
+ },
+ func() error {
+ _, err := s.TxnPut(wrongid, []byte("foo"), nil)
+ return err
+ },
+ func() error {
+ _, _, err := s.TxnDeleteRange(wrongid, []byte("foo"), nil)
+ return err
+ },
+ func() error { return s.TxnEnd(wrongid) },
+ }
+ for i, tt := range tests {
+ err := tt()
+ if err != ErrTxnIDMismatch {
+ t.Fatalf("#%d: err = %+v, want %+v", i, err, ErrTxnIDMismatch)
+ }
+ }
+
+ err := s.TxnEnd(id)
+ if err != nil {
+ t.Fatalf("end err = %+v, want %+v", err, nil)
+ }
+}
+
+// test that txn range, put, delete on single key in sequence repeatedly works correctly.
+func TestKVTnxOperationInSequence(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ for i := 0; i < 10; i++ {
+ id := s.TxnBegin()
+ base := int64(i)
+
+ // put foo
+ rev, err := s.TxnPut(id, []byte("foo"), []byte("bar"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if rev != base+1 {
+ t.Errorf("#%d: put rev = %d, want %d", i, rev, base+1)
+ }
+
+ kvs, rev, err := s.TxnRange(id, []byte("foo"), nil, 0, base+1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ wkvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: base + 1, ModRevision: base + 1, Version: 1},
+ }
+ if !reflect.DeepEqual(kvs, wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, wkvs)
+ }
+ if rev != base+1 {
+ t.Errorf("#%d: range rev = %d, want %d", i, rev, base+1)
+ }
+
+ // delete foo
+ n, rev, err := s.TxnDeleteRange(id, []byte("foo"), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != 1 || rev != base+1 {
+ t.Errorf("#%d: n = %d, rev = %d, want (%d, %d)", i, n, rev, 1, base+1)
+ }
+
+ kvs, rev, err = s.TxnRange(id, []byte("foo"), nil, 0, base+1)
+ if err != nil {
+ t.Errorf("#%d: range error (%v)", i, err)
+ }
+ if kvs != nil {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, nil)
+ }
+ if rev != base+1 {
+ t.Errorf("#%d: range rev = %d, want %d", i, rev, base+1)
+ }
+
+ s.TxnEnd(id)
+ }
+}
+
+func TestKVCompactReserveLastValue(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar0"))
+ s.Put([]byte("foo"), []byte("bar1"))
+ s.DeleteRange([]byte("foo"), nil)
+ s.Put([]byte("foo"), []byte("bar2"))
+
+ // rev in tests will be called in Compact() one by one on the same store
+ tests := []struct {
+ rev int64
+ // wanted kvs right after the compacted rev
+ wkvs []storagepb.KeyValue
+ }{
+ {
+ 0,
+ []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar0"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ },
+ },
+ {
+ 1,
+ []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar1"), CreateRevision: 1, ModRevision: 2, Version: 2},
+ },
+ },
+ {
+ 2,
+ nil,
+ },
+ {
+ 3,
+ []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar2"), CreateRevision: 4, ModRevision: 4, Version: 1},
+ },
+ },
+ }
+ for i, tt := range tests {
+ err := s.Compact(tt.rev)
+ if err != nil {
+ t.Errorf("#%d: unexpect compact error %v", i, err)
+ }
+ kvs, _, err := s.Range([]byte("foo"), nil, 0, tt.rev+1)
+ if err != nil {
+ t.Errorf("#%d: unexpect range error %v", i, err)
+ }
+ if !reflect.DeepEqual(kvs, tt.wkvs) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, tt.wkvs)
+ }
+ }
+}
+
+func TestKVCompactBad(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar0"))
+ s.Put([]byte("foo"), []byte("bar1"))
+ s.Put([]byte("foo"), []byte("bar2"))
+
+ // rev in tests will be called in Compact() one by one on the same store
+ tests := []struct {
+ rev int64
+ werr error
+ }{
+ {0, nil},
+ {1, nil},
+ {1, ErrCompacted},
+ {3, nil},
+ {4, ErrFutureRev},
+ {100, ErrFutureRev},
+ }
+ for i, tt := range tests {
+ err := s.Compact(tt.rev)
+ if err != tt.werr {
+ t.Errorf("#%d: compact error = %v, want %v", i, err, tt.werr)
+ }
+ }
+}
+
+func TestKVHash(t *testing.T) {
+ hashes := make([]uint32, 3)
+
+ for i := 0; i < len(hashes); i++ {
+ var err error
+ kv := New(tmpPath)
+ kv.Put([]byte("foo0"), []byte("bar0"))
+ kv.Put([]byte("foo1"), []byte("bar0"))
+ hashes[i], err = kv.Hash()
+ if err != nil {
+ t.Fatalf("failed to get hash: %v", err)
+ }
+ cleanup(kv, tmpPath)
+ }
+
+ for i := 1; i < len(hashes); i++ {
+ if hashes[i-1] != hashes[i] {
+ t.Errorf("hash[%d](%d) != hash[%d](%d)", i-1, hashes[i-1], i, hashes[i])
+ }
+ }
+}
+
+func TestKVRestore(t *testing.T) {
+ tests := []func(kv KV){
+ func(kv KV) {
+ kv.Put([]byte("foo"), []byte("bar0"))
+ kv.Put([]byte("foo"), []byte("bar1"))
+ kv.Put([]byte("foo"), []byte("bar2"))
+ },
+ func(kv KV) {
+ kv.Put([]byte("foo"), []byte("bar0"))
+ kv.DeleteRange([]byte("foo"), nil)
+ kv.Put([]byte("foo"), []byte("bar1"))
+ },
+ func(kv KV) {
+ kv.Put([]byte("foo"), []byte("bar0"))
+ kv.Put([]byte("foo"), []byte("bar1"))
+ kv.Compact(1)
+ },
+ }
+ for i, tt := range tests {
+ s := New(tmpPath)
+ tt(s)
+ var kvss [][]storagepb.KeyValue
+ for k := int64(0); k < 10; k++ {
+ kvs, _, _ := s.Range([]byte("a"), []byte("z"), 0, k)
+ kvss = append(kvss, kvs)
+ }
+ s.Close()
+
+ ns := New(tmpPath)
+ ns.Restore()
+ // wait for possible compaction to finish
+ testutil.WaitSchedule()
+ var nkvss [][]storagepb.KeyValue
+ for k := int64(0); k < 10; k++ {
+ nkvs, _, _ := ns.Range([]byte("a"), []byte("z"), 0, k)
+ nkvss = append(nkvss, nkvs)
+ }
+ cleanup(ns, tmpPath)
+
+ if !reflect.DeepEqual(nkvss, kvss) {
+ t.Errorf("#%d: kvs history = %+v, want %+v", i, nkvss, kvss)
+ }
+ }
+}
+
+func TestKVSnapshot(t *testing.T) {
+ s := New(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+ wkvs := []storagepb.KeyValue{
+ {Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ {Key: []byte("foo1"), Value: []byte("bar1"), CreateRevision: 2, ModRevision: 2, Version: 1},
+ {Key: []byte("foo2"), Value: []byte("bar2"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ }
+
+ f, err := os.Create("new_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ snap := s.Snapshot()
+ defer snap.Close()
+ _, err = snap.WriteTo(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ ns := New("new_test")
+ defer cleanup(ns, "new_test")
+ ns.Restore()
+ kvs, rev, err := ns.Range([]byte("a"), []byte("z"), 0, 0)
+ if err != nil {
+ t.Errorf("unexpect range error (%v)", err)
+ }
+ if !reflect.DeepEqual(kvs, wkvs) {
+ t.Errorf("kvs = %+v, want %+v", kvs, wkvs)
+ }
+ if rev != 3 {
+ t.Errorf("rev = %d, want %d", rev, 3)
+ }
+}
+
+func TestWatchableKVWatch(t *testing.T) {
+ s := WatchableKV(newWatchableStore(tmpPath))
+ defer cleanup(s, tmpPath)
+
+ wa, cancel := s.Watcher([]byte("foo"), true, 0)
+ defer cancel()
+
+ s.Put([]byte("foo"), []byte("bar"))
+ select {
+ case ev := <-wa.Event():
+ wev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 1,
+ ModRevision: 1,
+ Version: 1,
+ },
+ }
+ if !reflect.DeepEqual(ev, wev) {
+ t.Errorf("watched event = %+v, want %+v", ev, wev)
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("failed to watch the event")
+ }
+
+ s.Put([]byte("foo1"), []byte("bar1"))
+ select {
+ case ev := <-wa.Event():
+ wev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo1"),
+ Value: []byte("bar1"),
+ CreateRevision: 2,
+ ModRevision: 2,
+ Version: 1,
+ },
+ }
+ if !reflect.DeepEqual(ev, wev) {
+ t.Errorf("watched event = %+v, want %+v", ev, wev)
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("failed to watch the event")
+ }
+
+ wa, cancel = s.Watcher([]byte("foo1"), false, 1)
+ defer cancel()
+
+ select {
+ case ev := <-wa.Event():
+ wev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo1"),
+ Value: []byte("bar1"),
+ CreateRevision: 2,
+ ModRevision: 2,
+ Version: 1,
+ },
+ }
+ if !reflect.DeepEqual(ev, wev) {
+ t.Errorf("watched event = %+v, want %+v", ev, wev)
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("failed to watch the event")
+ }
+
+ s.Put([]byte("foo1"), []byte("bar11"))
+ select {
+ case ev := <-wa.Event():
+ wev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo1"),
+ Value: []byte("bar11"),
+ CreateRevision: 2,
+ ModRevision: 3,
+ Version: 2,
+ },
+ }
+ if !reflect.DeepEqual(ev, wev) {
+ t.Errorf("watched event = %+v, want %+v", ev, wev)
+ }
+ case <-time.After(time.Second):
+ t.Fatalf("failed to watch the event")
+ }
+}
+
+func cleanup(s KV, path string) {
+ s.Close()
+ os.Remove(path)
+}
diff --git a/vendor/github.com/coreos/etcd/storage/kvstore.go b/vendor/github.com/coreos/etcd/storage/kvstore.go
new file mode 100644
index 000000000..5dcf10d40
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/kvstore.go
@@ -0,0 +1,493 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "errors"
+ "log"
+ "math"
+ "math/rand"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/storage/backend"
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+var (
+ batchLimit = 10000
+ batchInterval = 100 * time.Millisecond
+ keyBucketName = []byte("key")
+ metaBucketName = []byte("meta")
+
+ scheduledCompactKeyName = []byte("scheduledCompactRev")
+ finishedCompactKeyName = []byte("finishedCompactRev")
+
+ ErrTxnIDMismatch = errors.New("storage: txn id mismatch")
+ ErrCompacted = errors.New("storage: required revision has been compacted")
+ ErrFutureRev = errors.New("storage: required revision is a future revision")
+ ErrCanceled = errors.New("storage: watcher is canceled")
+)
+
+type store struct {
+ mu sync.RWMutex
+
+ b backend.Backend
+ kvindex index
+
+ currentRev revision
+ // the main revision of the last compaction
+ compactMainRev int64
+
+ tx backend.BatchTx
+ tmu sync.Mutex // protect the txnID field
+ txnID int64 // tracks the current txnID to verify txn operations
+
+ wg sync.WaitGroup
+ stopc chan struct{}
+}
+
+func New(path string) KV {
+ return newStore(path)
+}
+
+func newStore(path string) *store {
+ s := &store{
+ b: backend.New(path, batchInterval, batchLimit),
+ kvindex: newTreeIndex(),
+ currentRev: revision{},
+ compactMainRev: -1,
+ stopc: make(chan struct{}),
+ }
+
+ tx := s.b.BatchTx()
+ tx.Lock()
+ tx.UnsafeCreateBucket(keyBucketName)
+ tx.UnsafeCreateBucket(metaBucketName)
+ tx.Unlock()
+ s.b.ForceCommit()
+
+ return s
+}
+
+func (s *store) Rev() int64 {
+ s.mu.RLock()
+ defer s.mu.RUnlock()
+
+ return s.currentRev.main
+}
+
+func (s *store) Put(key, value []byte) int64 {
+ id := s.TxnBegin()
+ s.put(key, value)
+ s.txnEnd(id)
+
+ putCounter.Inc()
+
+ return int64(s.currentRev.main)
+}
+
+func (s *store) Range(key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error) {
+ id := s.TxnBegin()
+ kvs, rev, err = s.rangeKeys(key, end, limit, rangeRev)
+ s.txnEnd(id)
+
+ rangeCounter.Inc()
+
+ return kvs, rev, err
+}
+
+func (s *store) DeleteRange(key, end []byte) (n, rev int64) {
+ id := s.TxnBegin()
+ n = s.deleteRange(key, end)
+ s.txnEnd(id)
+
+ deleteCounter.Inc()
+
+ return n, int64(s.currentRev.main)
+}
+
+func (s *store) TxnBegin() int64 {
+ s.mu.Lock()
+ s.currentRev.sub = 0
+ s.tx = s.b.BatchTx()
+ s.tx.Lock()
+
+ s.tmu.Lock()
+ defer s.tmu.Unlock()
+ s.txnID = rand.Int63()
+ return s.txnID
+}
+
+func (s *store) TxnEnd(txnID int64) error {
+ err := s.txnEnd(txnID)
+ if err != nil {
+ return err
+ }
+
+ txnCounter.Inc()
+ return nil
+}
+
+// txnEnd is used for unlocking an internal txn. It does
+// not increase the txnCounter.
+func (s *store) txnEnd(txnID int64) error {
+ s.tmu.Lock()
+ defer s.tmu.Unlock()
+ if txnID != s.txnID {
+ return ErrTxnIDMismatch
+ }
+
+ s.tx.Unlock()
+ if s.currentRev.sub != 0 {
+ s.currentRev.main += 1
+ }
+ s.currentRev.sub = 0
+
+ dbTotalSize.Set(float64(s.b.Size()))
+ s.mu.Unlock()
+ return nil
+}
+
+func (s *store) TxnRange(txnID int64, key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error) {
+ s.tmu.Lock()
+ defer s.tmu.Unlock()
+ if txnID != s.txnID {
+ return nil, 0, ErrTxnIDMismatch
+ }
+ return s.rangeKeys(key, end, limit, rangeRev)
+}
+
+func (s *store) TxnPut(txnID int64, key, value []byte) (rev int64, err error) {
+ s.tmu.Lock()
+ defer s.tmu.Unlock()
+ if txnID != s.txnID {
+ return 0, ErrTxnIDMismatch
+ }
+
+ s.put(key, value)
+ return int64(s.currentRev.main + 1), nil
+}
+
+func (s *store) TxnDeleteRange(txnID int64, key, end []byte) (n, rev int64, err error) {
+ s.tmu.Lock()
+ defer s.tmu.Unlock()
+ if txnID != s.txnID {
+ return 0, 0, ErrTxnIDMismatch
+ }
+
+ n = s.deleteRange(key, end)
+ if n != 0 || s.currentRev.sub != 0 {
+ rev = int64(s.currentRev.main + 1)
+ } else {
+ rev = int64(s.currentRev.main)
+ }
+ return n, rev, nil
+}
+
+// RangeEvents gets the events from key to end starting from startRev.
+// If `end` is nil, the request only observes the events on key.
+// If `end` is not nil, it observes the events on key range [key, range_end).
+// Limit limits the number of events returned.
+// If startRev <=0, rangeEvents returns events from the beginning of uncompacted history.
+//
+// If the required start rev is compacted, ErrCompacted will be returned.
+// If the required start rev has not happened, ErrFutureRev will be returned.
+//
+// RangeEvents returns events that satisfy the requirement (0 <= n <= limit).
+// If events in the revision range have not all happened, it returns immeidately
+// what is available.
+// It also returns nextRev which indicates the start revision used for the following
+// RangeEvents call. The nextRev could be smaller than the given endRev if the store
+// has not progressed so far or it hits the event limit.
+//
+// TODO: return byte slices instead of events to avoid meaningless encode and decode.
+func (s *store) RangeEvents(key, end []byte, limit, startRev int64) (evs []storagepb.Event, nextRev int64, err error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ if startRev > 0 && startRev <= s.compactMainRev {
+ return nil, 0, ErrCompacted
+ }
+ if startRev > s.currentRev.main {
+ return nil, 0, ErrFutureRev
+ }
+
+ revs := s.kvindex.RangeEvents(key, end, startRev)
+ if len(revs) == 0 {
+ return nil, s.currentRev.main + 1, nil
+ }
+
+ tx := s.b.BatchTx()
+ tx.Lock()
+ defer tx.Unlock()
+ // fetch events from the backend using revisions
+ for _, rev := range revs {
+ revbytes := newRevBytes()
+ revToBytes(rev, revbytes)
+
+ _, vs := tx.UnsafeRange(keyBucketName, revbytes, nil, 0)
+ if len(vs) != 1 {
+ log.Fatalf("storage: range cannot find rev (%d,%d)", rev.main, rev.sub)
+ }
+
+ e := storagepb.Event{}
+ if err := e.Unmarshal(vs[0]); err != nil {
+ log.Fatalf("storage: cannot unmarshal event: %v", err)
+ }
+ evs = append(evs, e)
+ if limit > 0 && len(evs) >= int(limit) {
+ return evs, rev.main + 1, nil
+ }
+ }
+ return evs, s.currentRev.main + 1, nil
+}
+
+func (s *store) Compact(rev int64) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ if rev <= s.compactMainRev {
+ return ErrCompacted
+ }
+ if rev > s.currentRev.main {
+ return ErrFutureRev
+ }
+
+ start := time.Now()
+
+ s.compactMainRev = rev
+
+ rbytes := newRevBytes()
+ revToBytes(revision{main: rev}, rbytes)
+
+ tx := s.b.BatchTx()
+ tx.Lock()
+ tx.UnsafePut(metaBucketName, scheduledCompactKeyName, rbytes)
+ tx.Unlock()
+ // ensure that desired compaction is persisted
+ s.b.ForceCommit()
+
+ keep := s.kvindex.Compact(rev)
+
+ s.wg.Add(1)
+ go s.scheduleCompaction(rev, keep)
+
+ indexCompactionPauseDurations.Observe(float64(time.Now().Sub(start) / time.Millisecond))
+ return nil
+}
+
+func (s *store) Hash() (uint32, error) {
+ s.b.ForceCommit()
+ return s.b.Hash()
+}
+
+func (s *store) Snapshot() Snapshot {
+ s.b.ForceCommit()
+ return s.b.Snapshot()
+}
+
+func (s *store) Restore() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ min, max := newRevBytes(), newRevBytes()
+ revToBytes(revision{}, min)
+ revToBytes(revision{main: math.MaxInt64, sub: math.MaxInt64}, max)
+
+ // restore index
+ tx := s.b.BatchTx()
+ tx.Lock()
+ _, finishedCompactBytes := tx.UnsafeRange(metaBucketName, finishedCompactKeyName, nil, 0)
+ if len(finishedCompactBytes) != 0 {
+ s.compactMainRev = bytesToRev(finishedCompactBytes[0]).main
+ log.Printf("storage: restore compact to %d", s.compactMainRev)
+ }
+
+ // TODO: limit N to reduce max memory usage
+ keys, vals := tx.UnsafeRange(keyBucketName, min, max, 0)
+ for i, key := range keys {
+ e := &storagepb.Event{}
+ if err := e.Unmarshal(vals[i]); err != nil {
+ log.Fatalf("storage: cannot unmarshal event: %v", err)
+ }
+
+ rev := bytesToRev(key)
+
+ // restore index
+ switch e.Type {
+ case storagepb.PUT:
+ s.kvindex.Restore(e.Kv.Key, revision{e.Kv.CreateRevision, 0}, rev, e.Kv.Version)
+ case storagepb.DELETE:
+ s.kvindex.Tombstone(e.Kv.Key, rev)
+ default:
+ log.Panicf("storage: unexpected event type %s", e.Type)
+ }
+
+ // update revision
+ s.currentRev = rev
+ }
+
+ _, scheduledCompactBytes := tx.UnsafeRange(metaBucketName, scheduledCompactKeyName, nil, 0)
+ if len(scheduledCompactBytes) != 0 {
+ scheduledCompact := bytesToRev(scheduledCompactBytes[0]).main
+ if scheduledCompact > s.compactMainRev {
+ log.Printf("storage: resume scheduled compaction at %d", scheduledCompact)
+ go s.Compact(scheduledCompact)
+ }
+ }
+
+ tx.Unlock()
+
+ return nil
+}
+
+func (s *store) Close() error {
+ close(s.stopc)
+ s.wg.Wait()
+ return s.b.Close()
+}
+
+func (a *store) Equal(b *store) bool {
+ if a.currentRev != b.currentRev {
+ return false
+ }
+ if a.compactMainRev != b.compactMainRev {
+ return false
+ }
+ return a.kvindex.Equal(b.kvindex)
+}
+
+// range is a keyword in Go, add Keys suffix.
+func (s *store) rangeKeys(key, end []byte, limit, rangeRev int64) (kvs []storagepb.KeyValue, rev int64, err error) {
+ curRev := int64(s.currentRev.main)
+ if s.currentRev.sub > 0 {
+ curRev += 1
+ }
+
+ if rangeRev > curRev {
+ return nil, s.currentRev.main, ErrFutureRev
+ }
+ if rangeRev <= 0 {
+ rev = curRev
+ } else {
+ rev = rangeRev
+ }
+ if rev <= s.compactMainRev {
+ return nil, 0, ErrCompacted
+ }
+
+ _, revpairs := s.kvindex.Range(key, end, int64(rev))
+ if len(revpairs) == 0 {
+ return nil, rev, nil
+ }
+
+ for _, revpair := range revpairs {
+ revbytes := newRevBytes()
+ revToBytes(revpair, revbytes)
+
+ _, vs := s.tx.UnsafeRange(keyBucketName, revbytes, nil, 0)
+ if len(vs) != 1 {
+ log.Fatalf("storage: range cannot find rev (%d,%d)", revpair.main, revpair.sub)
+ }
+
+ e := &storagepb.Event{}
+ if err := e.Unmarshal(vs[0]); err != nil {
+ log.Fatalf("storage: cannot unmarshal event: %v", err)
+ }
+ kvs = append(kvs, *e.Kv)
+ if limit > 0 && len(kvs) >= int(limit) {
+ break
+ }
+ }
+ return kvs, rev, nil
+}
+
+func (s *store) put(key, value []byte) {
+ rev := s.currentRev.main + 1
+ c := rev
+
+ // if the key exists before, use its previous created
+ _, created, ver, err := s.kvindex.Get(key, rev)
+ if err == nil {
+ c = created.main
+ }
+
+ ibytes := newRevBytes()
+ revToBytes(revision{main: rev, sub: s.currentRev.sub}, ibytes)
+
+ ver = ver + 1
+ event := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: key,
+ Value: value,
+ CreateRevision: c,
+ ModRevision: rev,
+ Version: ver,
+ },
+ }
+
+ d, err := event.Marshal()
+ if err != nil {
+ log.Fatalf("storage: cannot marshal event: %v", err)
+ }
+
+ s.tx.UnsafePut(keyBucketName, ibytes, d)
+ s.kvindex.Put(key, revision{main: rev, sub: s.currentRev.sub})
+ s.currentRev.sub += 1
+}
+
+func (s *store) deleteRange(key, end []byte) int64 {
+ rrev := s.currentRev.main
+ if s.currentRev.sub > 0 {
+ rrev += 1
+ }
+ keys, _ := s.kvindex.Range(key, end, rrev)
+
+ if len(keys) == 0 {
+ return 0
+ }
+
+ for _, key := range keys {
+ s.delete(key)
+ }
+ return int64(len(keys))
+}
+
+func (s *store) delete(key []byte) {
+ mainrev := s.currentRev.main + 1
+
+ ibytes := newRevBytes()
+ revToBytes(revision{main: mainrev, sub: s.currentRev.sub}, ibytes)
+
+ event := storagepb.Event{
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{
+ Key: key,
+ },
+ }
+
+ d, err := event.Marshal()
+ if err != nil {
+ log.Fatalf("storage: cannot marshal event: %v", err)
+ }
+
+ s.tx.UnsafePut(keyBucketName, ibytes, d)
+ err = s.kvindex.Tombstone(key, revision{main: mainrev, sub: s.currentRev.sub})
+ if err != nil {
+ log.Fatalf("storage: cannot tombstone an existing key (%s): %v", string(key), err)
+ }
+ s.currentRev.sub += 1
+}
diff --git a/vendor/github.com/coreos/etcd/storage/kvstore_compaction.go b/vendor/github.com/coreos/etcd/storage/kvstore_compaction.go
new file mode 100644
index 000000000..78d768683
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/kvstore_compaction.go
@@ -0,0 +1,67 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "encoding/binary"
+ "time"
+)
+
+func (s *store) scheduleCompaction(compactMainRev int64, keep map[revision]struct{}) {
+ defer s.wg.Done()
+
+ totalStart := time.Now()
+ defer dbCompactionTotalDurations.Observe(float64(time.Now().Sub(totalStart) / time.Millisecond))
+
+ end := make([]byte, 8)
+ binary.BigEndian.PutUint64(end, uint64(compactMainRev+1))
+
+ batchsize := int64(10000)
+ last := make([]byte, 8+1+8)
+ for {
+ var rev revision
+
+ start := time.Now()
+ tx := s.b.BatchTx()
+ tx.Lock()
+
+ keys, _ := tx.UnsafeRange(keyBucketName, last, end, batchsize)
+ for _, key := range keys {
+ rev = bytesToRev(key)
+ if _, ok := keep[rev]; !ok {
+ tx.UnsafeDelete(keyBucketName, key)
+ }
+ }
+
+ if len(keys) < int(batchsize) {
+ rbytes := make([]byte, 8+1+8)
+ revToBytes(revision{main: compactMainRev}, rbytes)
+ tx.UnsafePut(metaBucketName, finishedCompactKeyName, rbytes)
+ tx.Unlock()
+ return
+ }
+
+ // update last
+ revToBytes(revision{main: rev.main, sub: rev.sub + 1}, last)
+ tx.Unlock()
+ dbCompactionPauseDurations.Observe(float64(time.Now().Sub(start) / time.Millisecond))
+
+ select {
+ case <-time.After(100 * time.Millisecond):
+ case <-s.stopc:
+ return
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/kvstore_compaction_test.go b/vendor/github.com/coreos/etcd/storage/kvstore_compaction_test.go
new file mode 100644
index 000000000..957bd0021
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/kvstore_compaction_test.go
@@ -0,0 +1,93 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "reflect"
+ "testing"
+)
+
+func TestScheduleCompaction(t *testing.T) {
+ revs := []revision{{1, 0}, {2, 0}, {3, 0}}
+
+ tests := []struct {
+ rev int64
+ keep map[revision]struct{}
+ wrevs []revision
+ }{
+ // compact at 1 and discard all history
+ {
+ 1,
+ nil,
+ revs[1:],
+ },
+ // compact at 3 and discard all history
+ {
+ 3,
+ nil,
+ nil,
+ },
+ // compact at 1 and keeps history one step earlier
+ {
+ 1,
+ map[revision]struct{}{
+ revision{main: 1}: {},
+ },
+ revs,
+ },
+ // compact at 1 and keeps history two steps earlier
+ {
+ 3,
+ map[revision]struct{}{
+ revision{main: 2}: {},
+ revision{main: 3}: {},
+ },
+ revs[1:],
+ },
+ }
+ for i, tt := range tests {
+ s := newStore(tmpPath)
+ tx := s.b.BatchTx()
+
+ tx.Lock()
+ ibytes := newRevBytes()
+ for _, rev := range revs {
+ revToBytes(rev, ibytes)
+ tx.UnsafePut(keyBucketName, ibytes, []byte("bar"))
+ }
+ tx.Unlock()
+ // call `s.wg.Add(1)` to match the `s.wg.Done()` call in scheduleCompaction
+ // to avoid panic from wait group
+ s.wg.Add(1)
+ s.scheduleCompaction(tt.rev, tt.keep)
+
+ tx.Lock()
+ for _, rev := range tt.wrevs {
+ revToBytes(rev, ibytes)
+ keys, _ := tx.UnsafeRange(keyBucketName, ibytes, nil, 0)
+ if len(keys) != 1 {
+ t.Errorf("#%d: range on %v = %d, want 1", i, rev, len(keys))
+ }
+ }
+ _, vals := tx.UnsafeRange(metaBucketName, finishedCompactKeyName, nil, 0)
+ revToBytes(revision{main: tt.rev}, ibytes)
+ if w := [][]byte{ibytes}; !reflect.DeepEqual(vals, w) {
+ t.Errorf("#%d: vals on %v = %+v, want %+v", i, finishedCompactKeyName, vals, w)
+ }
+ tx.Unlock()
+
+ cleanup(s, tmpPath)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/kvstore_test.go b/vendor/github.com/coreos/etcd/storage/kvstore_test.go
new file mode 100644
index 000000000..8149a55e9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/kvstore_test.go
@@ -0,0 +1,804 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "crypto/rand"
+ "encoding/binary"
+ "math"
+ "os"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/pkg/testutil"
+ "github.com/coreos/etcd/storage/backend"
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+func TestStoreRev(t *testing.T) {
+ s := newStore(tmpPath)
+ defer os.Remove(tmpPath)
+
+ for i := 0; i < 3; i++ {
+ s.Put([]byte("foo"), []byte("bar"))
+ if r := s.Rev(); r != int64(i+1) {
+ t.Errorf("#%d: rev = %d, want %d", i, r, i+1)
+ }
+ }
+}
+
+func TestStorePut(t *testing.T) {
+ tests := []struct {
+ rev revision
+ r indexGetResp
+
+ wrev revision
+ wev storagepb.Event
+ wputrev revision
+ }{
+ {
+ revision{1, 0},
+ indexGetResp{revision{}, revision{}, 0, ErrRevisionNotFound},
+ revision{1, 1},
+ storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 2,
+ ModRevision: 2,
+ Version: 1,
+ },
+ },
+ revision{2, 0},
+ },
+ {
+ revision{1, 1},
+ indexGetResp{revision{2, 0}, revision{2, 0}, 1, nil},
+ revision{1, 2},
+ storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 2,
+ ModRevision: 2,
+ Version: 2,
+ },
+ },
+ revision{2, 1},
+ },
+ {
+ revision{2, 0},
+ indexGetResp{revision{2, 1}, revision{2, 0}, 2, nil},
+ revision{2, 1},
+ storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 2,
+ ModRevision: 3,
+ Version: 3,
+ },
+ },
+ revision{3, 0},
+ },
+ }
+ for i, tt := range tests {
+ s, b, index := newFakeStore()
+ s.currentRev = tt.rev
+ s.tx = b.BatchTx()
+ index.indexGetRespc <- tt.r
+
+ s.put([]byte("foo"), []byte("bar"))
+
+ data, err := tt.wev.Marshal()
+ if err != nil {
+ t.Errorf("#%d: marshal err = %v, want nil", i, err)
+ }
+ wact := []testutil.Action{
+ {"put", []interface{}{keyBucketName, newTestBytes(tt.wputrev), data}},
+ }
+ if g := b.tx.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: tx action = %+v, want %+v", i, g, wact)
+ }
+ wact = []testutil.Action{
+ {"get", []interface{}{[]byte("foo"), tt.wputrev.main}},
+ {"put", []interface{}{[]byte("foo"), tt.wputrev}},
+ }
+ if g := index.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: index action = %+v, want %+v", i, g, wact)
+ }
+ if s.currentRev != tt.wrev {
+ t.Errorf("#%d: rev = %+v, want %+v", i, s.currentRev, tt.wrev)
+ }
+ }
+}
+
+func TestStoreRange(t *testing.T) {
+ ev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 1,
+ ModRevision: 2,
+ Version: 1,
+ },
+ }
+ evb, err := ev.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ currev := revision{1, 1}
+ wrev := int64(2)
+
+ tests := []struct {
+ idxr indexRangeResp
+ r rangeResp
+ }{
+ {
+ indexRangeResp{[][]byte{[]byte("foo")}, []revision{{2, 0}}},
+ rangeResp{[][]byte{newTestBytes(revision{2, 0})}, [][]byte{evb}},
+ },
+ {
+ indexRangeResp{[][]byte{[]byte("foo"), []byte("foo1")}, []revision{{2, 0}, {3, 0}}},
+ rangeResp{[][]byte{newTestBytes(revision{2, 0})}, [][]byte{evb}},
+ },
+ }
+ for i, tt := range tests {
+ s, b, index := newFakeStore()
+ s.currentRev = currev
+ s.tx = b.BatchTx()
+ b.tx.rangeRespc <- tt.r
+ index.indexRangeRespc <- tt.idxr
+
+ kvs, rev, err := s.rangeKeys([]byte("foo"), []byte("goo"), 1, 0)
+ if err != nil {
+ t.Errorf("#%d: err = %v, want nil", i, err)
+ }
+ if w := []storagepb.KeyValue{*ev.Kv}; !reflect.DeepEqual(kvs, w) {
+ t.Errorf("#%d: kvs = %+v, want %+v", i, kvs, w)
+ }
+ if rev != wrev {
+ t.Errorf("#%d: rev = %d, want %d", i, rev, wrev)
+ }
+
+ wact := []testutil.Action{
+ {"range", []interface{}{keyBucketName, newTestBytes(tt.idxr.revs[0]), []byte(nil), int64(0)}},
+ }
+ if g := b.tx.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: tx action = %+v, want %+v", i, g, wact)
+ }
+ wact = []testutil.Action{
+ {"range", []interface{}{[]byte("foo"), []byte("goo"), wrev}},
+ }
+ if g := index.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: index action = %+v, want %+v", i, g, wact)
+ }
+ if s.currentRev != currev {
+ t.Errorf("#%d: current rev = %+v, want %+v", i, s.currentRev, currev)
+ }
+ }
+}
+
+func TestStoreDeleteRange(t *testing.T) {
+ tests := []struct {
+ rev revision
+ r indexRangeResp
+
+ wrev revision
+ wrrev int64
+ wdelrev revision
+ }{
+ {
+ revision{2, 0},
+ indexRangeResp{[][]byte{[]byte("foo")}, []revision{{2, 0}}},
+ revision{2, 1},
+ 2,
+ revision{3, 0},
+ },
+ {
+ revision{2, 1},
+ indexRangeResp{[][]byte{[]byte("foo")}, []revision{{2, 0}}},
+ revision{2, 2},
+ 3,
+ revision{3, 1},
+ },
+ }
+ for i, tt := range tests {
+ s, b, index := newFakeStore()
+ s.currentRev = tt.rev
+ s.tx = b.BatchTx()
+ index.indexRangeRespc <- tt.r
+
+ n := s.deleteRange([]byte("foo"), []byte("goo"))
+ if n != 1 {
+ t.Errorf("#%d: n = %d, want 1", i, n)
+ }
+
+ data, err := (&storagepb.Event{
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ },
+ }).Marshal()
+ if err != nil {
+ t.Errorf("#%d: marshal err = %v, want nil", i, err)
+ }
+ wact := []testutil.Action{
+ {"put", []interface{}{keyBucketName, newTestBytes(tt.wdelrev), data}},
+ }
+ if g := b.tx.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: tx action = %+v, want %+v", i, g, wact)
+ }
+ wact = []testutil.Action{
+ {"range", []interface{}{[]byte("foo"), []byte("goo"), tt.wrrev}},
+ {"tombstone", []interface{}{[]byte("foo"), tt.wdelrev}},
+ }
+ if g := index.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: index action = %+v, want %+v", i, g, wact)
+ }
+ if s.currentRev != tt.wrev {
+ t.Errorf("#%d: rev = %+v, want %+v", i, s.currentRev, tt.wrev)
+ }
+ }
+}
+
+func TestStoreRangeEvents(t *testing.T) {
+ ev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 1,
+ ModRevision: 2,
+ Version: 1,
+ },
+ }
+ evb, err := ev.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ currev := revision{2, 0}
+
+ tests := []struct {
+ idxr indexRangeEventsResp
+ r rangeResp
+ }{
+ {
+ indexRangeEventsResp{[]revision{{2, 0}}},
+ rangeResp{[][]byte{newTestBytes(revision{2, 0})}, [][]byte{evb}},
+ },
+ {
+ indexRangeEventsResp{[]revision{{2, 0}, {3, 0}}},
+ rangeResp{[][]byte{newTestBytes(revision{2, 0})}, [][]byte{evb}},
+ },
+ }
+ for i, tt := range tests {
+ s, b, index := newFakeStore()
+ s.currentRev = currev
+ index.indexRangeEventsRespc <- tt.idxr
+ b.tx.rangeRespc <- tt.r
+
+ evs, _, err := s.RangeEvents([]byte("foo"), []byte("goo"), 1, 1)
+ if err != nil {
+ t.Errorf("#%d: err = %v, want nil", i, err)
+ }
+ if w := []storagepb.Event{ev}; !reflect.DeepEqual(evs, w) {
+ t.Errorf("#%d: evs = %+v, want %+v", i, evs, w)
+ }
+
+ wact := []testutil.Action{
+ {"rangeEvents", []interface{}{[]byte("foo"), []byte("goo"), int64(1)}},
+ }
+ if g := index.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: index action = %+v, want %+v", i, g, wact)
+ }
+ wact = []testutil.Action{
+ {"range", []interface{}{keyBucketName, newTestBytes(tt.idxr.revs[0]), []byte(nil), int64(0)}},
+ }
+ if g := b.tx.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("#%d: tx action = %+v, want %+v", i, g, wact)
+ }
+ if s.currentRev != currev {
+ t.Errorf("#%d: current rev = %+v, want %+v", i, s.currentRev, currev)
+ }
+ }
+}
+
+func TestStoreCompact(t *testing.T) {
+ s, b, index := newFakeStore()
+ s.currentRev = revision{3, 0}
+ index.indexCompactRespc <- map[revision]struct{}{revision{1, 0}: {}}
+ b.tx.rangeRespc <- rangeResp{[][]byte{newTestBytes(revision{1, 0}), newTestBytes(revision{2, 0})}, nil}
+
+ s.Compact(3)
+ s.wg.Wait()
+
+ if s.compactMainRev != 3 {
+ t.Errorf("compact main rev = %d, want 3", s.compactMainRev)
+ }
+ end := make([]byte, 8)
+ binary.BigEndian.PutUint64(end, uint64(4))
+ wact := []testutil.Action{
+ {"put", []interface{}{metaBucketName, scheduledCompactKeyName, newTestBytes(revision{3, 0})}},
+ {"range", []interface{}{keyBucketName, make([]byte, 17), end, int64(10000)}},
+ {"delete", []interface{}{keyBucketName, newTestBytes(revision{2, 0})}},
+ {"put", []interface{}{metaBucketName, finishedCompactKeyName, newTestBytes(revision{3, 0})}},
+ }
+ if g := b.tx.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("tx actions = %+v, want %+v", g, wact)
+ }
+ wact = []testutil.Action{
+ {"compact", []interface{}{int64(3)}},
+ }
+ if g := index.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("index action = %+v, want %+v", g, wact)
+ }
+}
+
+func TestStoreRestore(t *testing.T) {
+ s, b, index := newFakeStore()
+
+ putev := storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ Value: []byte("bar"),
+ CreateRevision: 3,
+ ModRevision: 3,
+ Version: 1,
+ },
+ }
+ putevb, err := putev.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ delev := storagepb.Event{
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{
+ Key: []byte("foo"),
+ },
+ }
+ delevb, err := delev.Marshal()
+ if err != nil {
+ t.Fatal(err)
+ }
+ b.tx.rangeRespc <- rangeResp{[][]byte{finishedCompactKeyName}, [][]byte{newTestBytes(revision{2, 0})}}
+ b.tx.rangeRespc <- rangeResp{[][]byte{newTestBytes(revision{3, 0}), newTestBytes(revision{4, 0})}, [][]byte{putevb, delevb}}
+ b.tx.rangeRespc <- rangeResp{[][]byte{scheduledCompactKeyName}, [][]byte{newTestBytes(revision{2, 0})}}
+
+ s.Restore()
+
+ if s.compactMainRev != 2 {
+ t.Errorf("compact rev = %d, want 4", s.compactMainRev)
+ }
+ wrev := revision{4, 0}
+ if !reflect.DeepEqual(s.currentRev, wrev) {
+ t.Errorf("current rev = %v, want %v", s.currentRev, wrev)
+ }
+ wact := []testutil.Action{
+ {"range", []interface{}{metaBucketName, finishedCompactKeyName, []byte(nil), int64(0)}},
+ {"range", []interface{}{keyBucketName, newTestBytes(revision{}), newTestBytes(revision{math.MaxInt64, math.MaxInt64}), int64(0)}},
+ {"range", []interface{}{metaBucketName, scheduledCompactKeyName, []byte(nil), int64(0)}},
+ }
+ if g := b.tx.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("tx actions = %+v, want %+v", g, wact)
+ }
+ wact = []testutil.Action{
+ {"restore", []interface{}{[]byte("foo"), revision{3, 0}, revision{3, 0}, int64(1)}},
+ {"tombstone", []interface{}{[]byte("foo"), revision{4, 0}}},
+ }
+ if g := index.Action(); !reflect.DeepEqual(g, wact) {
+ t.Errorf("index action = %+v, want %+v", g, wact)
+ }
+}
+
+// tests end parameter works well
+func TestStoreRangeEventsEnd(t *testing.T) {
+ s := newStore(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo1"), []byte("bar1"))
+ s.Put([]byte("foo2"), []byte("bar2"))
+ evs := []storagepb.Event{
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ },
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo1"), Value: []byte("bar1"), CreateRevision: 2, ModRevision: 2, Version: 1},
+ },
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo2"), Value: []byte("bar2"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ },
+ }
+
+ tests := []struct {
+ key, end []byte
+ wevs []storagepb.Event
+ }{
+ // get no keys
+ {
+ []byte("doo"), []byte("foo"),
+ nil,
+ },
+ // get no keys when key == end
+ {
+ []byte("foo"), []byte("foo"),
+ nil,
+ },
+ // get no keys when ranging single key
+ {
+ []byte("doo"), nil,
+ nil,
+ },
+ // get all keys
+ {
+ []byte("foo"), []byte("foo3"),
+ evs,
+ },
+ // get partial keys
+ {
+ []byte("foo"), []byte("foo1"),
+ evs[:1],
+ },
+ // get single key
+ {
+ []byte("foo"), nil,
+ evs[:1],
+ },
+ }
+
+ for i, tt := range tests {
+ evs, rev, err := s.RangeEvents(tt.key, tt.end, 0, 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if rev != 4 {
+ t.Errorf("#%d: rev = %d, want %d", i, rev, 4)
+ }
+ if !reflect.DeepEqual(evs, tt.wevs) {
+ t.Errorf("#%d: evs = %+v, want %+v", i, evs, tt.wevs)
+ }
+ }
+}
+
+func TestStoreRangeEventsRev(t *testing.T) {
+ s := newStore(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.DeleteRange([]byte("foo"), nil)
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("unrelated"), []byte("unrelated"))
+ evs := []storagepb.Event{
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ },
+ {
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{Key: []byte("foo")},
+ },
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ },
+ }
+
+ tests := []struct {
+ start int64
+
+ wevs []storagepb.Event
+ wnext int64
+ }{
+ {0, evs, 5},
+ {1, evs, 5},
+ {3, evs[2:], 5},
+ }
+
+ for i, tt := range tests {
+ evs, next, err := s.RangeEvents([]byte("foo"), nil, 0, tt.start)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(evs, tt.wevs) {
+ t.Errorf("#%d: evs = %+v, want %+v", i, evs, tt.wevs)
+ }
+ if next != tt.wnext {
+ t.Errorf("#%d: next = %d, want %d", i, next, tt.wnext)
+ }
+ }
+}
+
+func TestStoreRangeEventsBad(t *testing.T) {
+ s := newStore(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.Put([]byte("foo"), []byte("bar1"))
+ s.Put([]byte("foo"), []byte("bar2"))
+ if err := s.Compact(3); err != nil {
+ t.Fatalf("compact error (%v)", err)
+ }
+
+ tests := []struct {
+ rev int64
+ werr error
+ }{
+ {1, ErrCompacted},
+ {2, ErrCompacted},
+ {3, ErrCompacted},
+ {4, ErrFutureRev},
+ {10, ErrFutureRev},
+ }
+ for i, tt := range tests {
+ _, _, err := s.RangeEvents([]byte("foo"), nil, 0, tt.rev)
+ if err != tt.werr {
+ t.Errorf("#%d: error = %v, want %v", i, err, tt.werr)
+ }
+ }
+}
+
+func TestStoreRangeEventsLimit(t *testing.T) {
+ s := newStore(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ s.Put([]byte("foo"), []byte("bar"))
+ s.DeleteRange([]byte("foo"), nil)
+ s.Put([]byte("foo"), []byte("bar"))
+ evs := []storagepb.Event{
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 1, ModRevision: 1, Version: 1},
+ },
+ {
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{Key: []byte("foo")},
+ },
+ {
+ Type: storagepb.PUT,
+ Kv: &storagepb.KeyValue{Key: []byte("foo"), Value: []byte("bar"), CreateRevision: 3, ModRevision: 3, Version: 1},
+ },
+ }
+
+ tests := []struct {
+ limit int64
+ wevs []storagepb.Event
+ }{
+ // no limit
+ {-1, evs},
+ // no limit
+ {0, evs},
+ {1, evs[:1]},
+ {2, evs[:2]},
+ {3, evs},
+ {100, evs},
+ }
+ for i, tt := range tests {
+ evs, _, err := s.RangeEvents([]byte("foo"), nil, tt.limit, 1)
+ if err != nil {
+ t.Fatalf("#%d: range error (%v)", i, err)
+ }
+ if !reflect.DeepEqual(evs, tt.wevs) {
+ t.Errorf("#%d: evs = %+v, want %+v", i, evs, tt.wevs)
+ }
+ }
+}
+
+func TestRestoreContinueUnfinishedCompaction(t *testing.T) {
+ s0 := newStore(tmpPath)
+ defer os.Remove(tmpPath)
+
+ s0.Put([]byte("foo"), []byte("bar"))
+ s0.Put([]byte("foo"), []byte("bar1"))
+ s0.Put([]byte("foo"), []byte("bar2"))
+
+ // write scheduled compaction, but not do compaction
+ rbytes := newRevBytes()
+ revToBytes(revision{main: 2}, rbytes)
+ tx := s0.b.BatchTx()
+ tx.Lock()
+ tx.UnsafePut(metaBucketName, scheduledCompactKeyName, rbytes)
+ tx.Unlock()
+
+ s0.Close()
+
+ s1 := newStore(tmpPath)
+ s1.Restore()
+
+ // wait for scheduled compaction to be finished
+ time.Sleep(100 * time.Millisecond)
+
+ if _, _, err := s1.Range([]byte("foo"), nil, 0, 2); err != ErrCompacted {
+ t.Errorf("range on compacted rev error = %v, want %v", err, ErrCompacted)
+ }
+ // check the key in backend is deleted
+ revbytes := newRevBytes()
+ // TODO: compact should delete main=2 key too
+ revToBytes(revision{main: 1}, revbytes)
+ tx = s1.b.BatchTx()
+ tx.Lock()
+ ks, _ := tx.UnsafeRange(keyBucketName, revbytes, nil, 0)
+ if len(ks) != 0 {
+ t.Errorf("key for rev %+v still exists, want deleted", bytesToRev(revbytes))
+ }
+ tx.Unlock()
+}
+
+func TestTxnBlockBackendForceCommit(t *testing.T) {
+ s := newStore(tmpPath)
+ defer os.Remove(tmpPath)
+
+ id := s.TxnBegin()
+
+ done := make(chan struct{})
+ go func() {
+ s.b.ForceCommit()
+ done <- struct{}{}
+ }()
+ select {
+ case <-done:
+ t.Fatalf("failed to block ForceCommit")
+ case <-time.After(100 * time.Millisecond):
+ }
+
+ s.TxnEnd(id)
+ select {
+ case <-done:
+ case <-time.After(100 * time.Millisecond):
+ t.Fatalf("failed to execute ForceCommit")
+ }
+
+}
+
+func BenchmarkStorePut(b *testing.B) {
+ s := newStore(tmpPath)
+ defer os.Remove(tmpPath)
+
+ // prepare keys
+ keys := make([][]byte, b.N)
+ for i := 0; i < b.N; i++ {
+ keys[i] = make([]byte, 64)
+ rand.Read(keys[i])
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ s.Put(keys[i], []byte("foo"))
+ }
+}
+
+func newTestBytes(rev revision) []byte {
+ bytes := newRevBytes()
+ revToBytes(rev, bytes)
+ return bytes
+}
+
+func newFakeStore() (*store, *fakeBackend, *fakeIndex) {
+ b := &fakeBackend{&fakeBatchTx{rangeRespc: make(chan rangeResp, 5)}}
+ index := &fakeIndex{
+ indexGetRespc: make(chan indexGetResp, 1),
+ indexRangeRespc: make(chan indexRangeResp, 1),
+ indexRangeEventsRespc: make(chan indexRangeEventsResp, 1),
+ indexCompactRespc: make(chan map[revision]struct{}, 1),
+ }
+ return &store{
+ b: b,
+ kvindex: index,
+ currentRev: revision{},
+ compactMainRev: -1,
+ }, b, index
+}
+
+type rangeResp struct {
+ keys [][]byte
+ vals [][]byte
+}
+
+type fakeBatchTx struct {
+ testutil.Recorder
+ rangeRespc chan rangeResp
+}
+
+func (b *fakeBatchTx) Lock() {}
+func (b *fakeBatchTx) Unlock() {}
+func (b *fakeBatchTx) UnsafeCreateBucket(name []byte) {}
+func (b *fakeBatchTx) UnsafePut(bucketName []byte, key []byte, value []byte) {
+ b.Recorder.Record(testutil.Action{Name: "put", Params: []interface{}{bucketName, key, value}})
+}
+func (b *fakeBatchTx) UnsafeRange(bucketName []byte, key, endKey []byte, limit int64) (keys [][]byte, vals [][]byte) {
+ b.Recorder.Record(testutil.Action{Name: "range", Params: []interface{}{bucketName, key, endKey, limit}})
+ r := <-b.rangeRespc
+ return r.keys, r.vals
+}
+func (b *fakeBatchTx) UnsafeDelete(bucketName []byte, key []byte) {
+ b.Recorder.Record(testutil.Action{Name: "delete", Params: []interface{}{bucketName, key}})
+}
+func (b *fakeBatchTx) Commit() {}
+func (b *fakeBatchTx) CommitAndStop() {}
+
+type fakeBackend struct {
+ tx *fakeBatchTx
+}
+
+func (b *fakeBackend) BatchTx() backend.BatchTx { return b.tx }
+func (b *fakeBackend) Hash() (uint32, error) { return 0, nil }
+func (b *fakeBackend) Size() int64 { return 0 }
+func (b *fakeBackend) Snapshot() backend.Snapshot { return nil }
+func (b *fakeBackend) ForceCommit() {}
+func (b *fakeBackend) Close() error { return nil }
+
+type indexGetResp struct {
+ rev revision
+ created revision
+ ver int64
+ err error
+}
+
+type indexRangeResp struct {
+ keys [][]byte
+ revs []revision
+}
+
+type indexRangeEventsResp struct {
+ revs []revision
+}
+
+type fakeIndex struct {
+ testutil.Recorder
+ indexGetRespc chan indexGetResp
+ indexRangeRespc chan indexRangeResp
+ indexRangeEventsRespc chan indexRangeEventsResp
+ indexCompactRespc chan map[revision]struct{}
+}
+
+func (i *fakeIndex) Get(key []byte, atRev int64) (rev, created revision, ver int64, err error) {
+ i.Recorder.Record(testutil.Action{Name: "get", Params: []interface{}{key, atRev}})
+ r := <-i.indexGetRespc
+ return r.rev, r.created, r.ver, r.err
+}
+func (i *fakeIndex) Range(key, end []byte, atRev int64) ([][]byte, []revision) {
+ i.Recorder.Record(testutil.Action{Name: "range", Params: []interface{}{key, end, atRev}})
+ r := <-i.indexRangeRespc
+ return r.keys, r.revs
+}
+func (i *fakeIndex) Put(key []byte, rev revision) {
+ i.Recorder.Record(testutil.Action{Name: "put", Params: []interface{}{key, rev}})
+}
+func (i *fakeIndex) Restore(key []byte, created, modified revision, ver int64) {
+ i.Recorder.Record(testutil.Action{Name: "restore", Params: []interface{}{key, created, modified, ver}})
+}
+func (i *fakeIndex) Tombstone(key []byte, rev revision) error {
+ i.Recorder.Record(testutil.Action{Name: "tombstone", Params: []interface{}{key, rev}})
+ return nil
+}
+func (i *fakeIndex) RangeEvents(key, end []byte, rev int64) []revision {
+ i.Recorder.Record(testutil.Action{Name: "rangeEvents", Params: []interface{}{key, end, rev}})
+ r := <-i.indexRangeEventsRespc
+ return r.revs
+}
+func (i *fakeIndex) Compact(rev int64) map[revision]struct{} {
+ i.Recorder.Record(testutil.Action{Name: "compact", Params: []interface{}{rev}})
+ return <-i.indexCompactRespc
+}
+func (i *fakeIndex) Equal(b index) bool { return false }
diff --git a/vendor/github.com/coreos/etcd/storage/metrics.go b/vendor/github.com/coreos/etcd/storage/metrics.go
new file mode 100644
index 000000000..772b7e242
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/metrics.go
@@ -0,0 +1,154 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+)
+
+var (
+ rangeCounter = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "range_total",
+ Help: "Total number of ranges seen by this member.",
+ })
+
+ putCounter = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "put_total",
+ Help: "Total number of puts seen by this member.",
+ })
+
+ deleteCounter = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "delete_total",
+ Help: "Total number of deletes seen by this member.",
+ })
+
+ txnCounter = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "txn_total",
+ Help: "Total number of txns seen by this member.",
+ })
+
+ keysGauge = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "keys_total",
+ Help: "Total number of keys.",
+ })
+
+ watchersGauge = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "watchers_total",
+ Help: "Total number of watchers.",
+ })
+
+ slowWatchersGauge = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "slow_watchers_total",
+ Help: "Total number of unsynced slow watchers.",
+ })
+
+ totalEventsCounter = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "events_total",
+ Help: "Total number of events sent by this member.",
+ })
+
+ pendingEventsGauge = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "pending_events_total",
+ Help: "Total number of pending events to be sent.",
+ })
+
+ indexCompactionPauseDurations = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "index_compaction_pause_duration_milliseconds",
+ Help: "Bucketed histogram of index compaction puase duration.",
+ // 0.5ms -> 1second
+ Buckets: prometheus.ExponentialBuckets(0.5, 2, 12),
+ })
+
+ dbCompactionPauseDurations = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "db_compaction_pause_duration_milliseconds",
+ Help: "Bucketed histogram of db compaction puase duration.",
+ // 1ms -> 4second
+ Buckets: prometheus.ExponentialBuckets(1, 2, 13),
+ })
+
+ dbCompactionTotalDurations = prometheus.NewHistogram(
+ prometheus.HistogramOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "db_compaction_total_duration_milliseconds",
+ Help: "Bucketed histogram of db compaction total duration.",
+ // 100ms -> 800second
+ Buckets: prometheus.ExponentialBuckets(100, 2, 14),
+ })
+
+ dbTotalSize = prometheus.NewGauge(prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "storage",
+ Name: "db_total_size_in_bytes",
+ Help: "Total size of the underlying database in bytes.",
+ })
+)
+
+func init() {
+ prometheus.MustRegister(rangeCounter)
+ prometheus.MustRegister(putCounter)
+ prometheus.MustRegister(deleteCounter)
+ prometheus.MustRegister(txnCounter)
+ prometheus.MustRegister(keysGauge)
+ prometheus.MustRegister(watchersGauge)
+ prometheus.MustRegister(totalEventsCounter)
+ prometheus.MustRegister(slowWatchersGauge)
+ prometheus.MustRegister(pendingEventsGauge)
+ prometheus.MustRegister(indexCompactionPauseDurations)
+ prometheus.MustRegister(dbCompactionPauseDurations)
+ prometheus.MustRegister(dbCompactionTotalDurations)
+ prometheus.MustRegister(dbTotalSize)
+}
+
+// ReportEventReceived reports that an event is received.
+// This function should be called when the external systems received an
+// event from storage.Watcher.
+func ReportEventReceived() {
+ pendingEventsGauge.Dec()
+ totalEventsCounter.Inc()
+}
diff --git a/vendor/github.com/coreos/etcd/storage/revision.go b/vendor/github.com/coreos/etcd/storage/revision.go
new file mode 100644
index 000000000..d5cf173a2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/revision.go
@@ -0,0 +1,55 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import "encoding/binary"
+
+type revision struct {
+ main int64
+ sub int64
+}
+
+func (a revision) GreaterThan(b revision) bool {
+ if a.main > b.main {
+ return true
+ }
+ if a.main < b.main {
+ return false
+ }
+ return a.sub > b.sub
+}
+
+func newRevBytes() []byte {
+ return make([]byte, 8+1+8)
+}
+
+func revToBytes(rev revision, bytes []byte) {
+ binary.BigEndian.PutUint64(bytes, uint64(rev.main))
+ bytes[8] = '_'
+ binary.BigEndian.PutUint64(bytes[9:], uint64(rev.sub))
+}
+
+func bytesToRev(bytes []byte) revision {
+ return revision{
+ main: int64(binary.BigEndian.Uint64(bytes[0:8])),
+ sub: int64(binary.BigEndian.Uint64(bytes[9:])),
+ }
+}
+
+type revisions []revision
+
+func (a revisions) Len() int { return len(a) }
+func (a revisions) Less(i, j int) bool { return a[j].GreaterThan(a[i]) }
+func (a revisions) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/vendor/github.com/coreos/etcd/storage/revision_test.go b/vendor/github.com/coreos/etcd/storage/revision_test.go
new file mode 100644
index 000000000..38d198f50
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/revision_test.go
@@ -0,0 +1,53 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "bytes"
+ "math"
+ "reflect"
+ "testing"
+)
+
+// TestRevision tests that revision could be encoded to and decoded from
+// bytes slice. Moreover, the lexicograph order of its byte slice representation
+// follows the order of (main, sub).
+func TestRevision(t *testing.T) {
+ tests := []revision{
+ // order in (main, sub)
+ {},
+ {main: 1, sub: 0},
+ {main: 1, sub: 1},
+ {main: 2, sub: 0},
+ {main: math.MaxInt64, sub: math.MaxInt64},
+ }
+
+ bs := make([][]byte, len(tests))
+ for i, tt := range tests {
+ b := newRevBytes()
+ revToBytes(tt, b)
+ bs[i] = b
+
+ if grev := bytesToRev(b); !reflect.DeepEqual(grev, tt) {
+ t.Errorf("#%d: revision = %+v, want %+v", i, grev, tt)
+ }
+ }
+
+ for i := 0; i < len(tests)-1; i++ {
+ if bytes.Compare(bs[i], bs[i+1]) >= 0 {
+ t.Errorf("#%d: %v (%+v) should be smaller than %v (%+v)", i, bs[i], tests[i], bs[i+1], tests[i+1])
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/storagepb/kv.pb.go b/vendor/github.com/coreos/etcd/storage/storagepb/kv.pb.go
new file mode 100644
index 000000000..d7bfc9bba
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/storagepb/kv.pb.go
@@ -0,0 +1,567 @@
+// Code generated by protoc-gen-gogo.
+// source: kv.proto
+// DO NOT EDIT!
+
+/*
+ Package storagepb is a generated protocol buffer package.
+
+ It is generated from these files:
+ kv.proto
+
+ It has these top-level messages:
+ KeyValue
+ Event
+*/
+package storagepb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+
+type Event_EventType int32
+
+const (
+ PUT Event_EventType = 0
+ DELETE Event_EventType = 1
+ EXPIRE Event_EventType = 2
+)
+
+var Event_EventType_name = map[int32]string{
+ 0: "PUT",
+ 1: "DELETE",
+ 2: "EXPIRE",
+}
+var Event_EventType_value = map[string]int32{
+ "PUT": 0,
+ "DELETE": 1,
+ "EXPIRE": 2,
+}
+
+func (x Event_EventType) String() string {
+ return proto.EnumName(Event_EventType_name, int32(x))
+}
+
+type KeyValue struct {
+ Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+ CreateRevision int64 `protobuf:"varint,2,opt,name=create_revision,proto3" json:"create_revision,omitempty"`
+ // mod_revision is the last modified revision of the key.
+ ModRevision int64 `protobuf:"varint,3,opt,name=mod_revision,proto3" json:"mod_revision,omitempty"`
+ // version is the version of the key. A deletion resets
+ // the version to zero and any modification of the key
+ // increases its version.
+ Version int64 `protobuf:"varint,4,opt,name=version,proto3" json:"version,omitempty"`
+ Value []byte `protobuf:"bytes,5,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (m *KeyValue) Reset() { *m = KeyValue{} }
+func (m *KeyValue) String() string { return proto.CompactTextString(m) }
+func (*KeyValue) ProtoMessage() {}
+
+type Event struct {
+ Type Event_EventType `protobuf:"varint,1,opt,name=type,proto3,enum=storagepb.Event_EventType" json:"type,omitempty"`
+ // a put event contains the current key-value
+ // a delete/expire event contains the previous
+ // key-value
+ Kv *KeyValue `protobuf:"bytes,2,opt,name=kv" json:"kv,omitempty"`
+}
+
+func (m *Event) Reset() { *m = Event{} }
+func (m *Event) String() string { return proto.CompactTextString(m) }
+func (*Event) ProtoMessage() {}
+
+func init() {
+ proto.RegisterEnum("storagepb.Event_EventType", Event_EventType_name, Event_EventType_value)
+}
+func (m *KeyValue) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *KeyValue) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Key != nil {
+ if len(m.Key) > 0 {
+ data[i] = 0xa
+ i++
+ i = encodeVarintKv(data, i, uint64(len(m.Key)))
+ i += copy(data[i:], m.Key)
+ }
+ }
+ if m.CreateRevision != 0 {
+ data[i] = 0x10
+ i++
+ i = encodeVarintKv(data, i, uint64(m.CreateRevision))
+ }
+ if m.ModRevision != 0 {
+ data[i] = 0x18
+ i++
+ i = encodeVarintKv(data, i, uint64(m.ModRevision))
+ }
+ if m.Version != 0 {
+ data[i] = 0x20
+ i++
+ i = encodeVarintKv(data, i, uint64(m.Version))
+ }
+ if m.Value != nil {
+ if len(m.Value) > 0 {
+ data[i] = 0x2a
+ i++
+ i = encodeVarintKv(data, i, uint64(len(m.Value)))
+ i += copy(data[i:], m.Value)
+ }
+ }
+ return i, nil
+}
+
+func (m *Event) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Event) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ if m.Type != 0 {
+ data[i] = 0x8
+ i++
+ i = encodeVarintKv(data, i, uint64(m.Type))
+ }
+ if m.Kv != nil {
+ data[i] = 0x12
+ i++
+ i = encodeVarintKv(data, i, uint64(m.Kv.Size()))
+ n1, err := m.Kv.MarshalTo(data[i:])
+ if err != nil {
+ return 0, err
+ }
+ i += n1
+ }
+ return i, nil
+}
+
+func encodeFixed64Kv(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Kv(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintKv(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *KeyValue) Size() (n int) {
+ var l int
+ _ = l
+ if m.Key != nil {
+ l = len(m.Key)
+ if l > 0 {
+ n += 1 + l + sovKv(uint64(l))
+ }
+ }
+ if m.CreateRevision != 0 {
+ n += 1 + sovKv(uint64(m.CreateRevision))
+ }
+ if m.ModRevision != 0 {
+ n += 1 + sovKv(uint64(m.ModRevision))
+ }
+ if m.Version != 0 {
+ n += 1 + sovKv(uint64(m.Version))
+ }
+ if m.Value != nil {
+ l = len(m.Value)
+ if l > 0 {
+ n += 1 + l + sovKv(uint64(l))
+ }
+ }
+ return n
+}
+
+func (m *Event) Size() (n int) {
+ var l int
+ _ = l
+ if m.Type != 0 {
+ n += 1 + sovKv(uint64(m.Type))
+ }
+ if m.Kv != nil {
+ l = m.Kv.Size()
+ n += 1 + l + sovKv(uint64(l))
+ }
+ return n
+}
+
+func sovKv(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozKv(x uint64) (n int) {
+ return sovKv(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *KeyValue) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthKv
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Key = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field CreateRevision", wireType)
+ }
+ m.CreateRevision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.CreateRevision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field ModRevision", wireType)
+ }
+ m.ModRevision = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.ModRevision |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 4:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+ }
+ m.Version = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Version |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 5:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthKv
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Value = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipKv(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthKv
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *Event) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ m.Type = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Type |= (Event_EventType(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Kv", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ msglen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthKv
+ }
+ postIndex := iNdEx + msglen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ if m.Kv == nil {
+ m.Kv = &KeyValue{}
+ }
+ if err := m.Kv.Unmarshal(data[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipKv(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthKv
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipKv(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthKv
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipKv(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthKv = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/storage/storagepb/kv.proto b/vendor/github.com/coreos/etcd/storage/storagepb/kv.proto
new file mode 100644
index 000000000..e1ac0b94a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/storagepb/kv.proto
@@ -0,0 +1,36 @@
+syntax = "proto3";
+package storagepb;
+
+import "gogoproto/gogo.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.goproto_enum_prefix_all) = false;
+
+message KeyValue {
+ bytes key = 1;
+ int64 create_revision = 2;
+ // mod_revision is the last modified revision of the key.
+ int64 mod_revision = 3;
+ // version is the version of the key. A deletion resets
+ // the version to zero and any modification of the key
+ // increases its version.
+ int64 version = 4;
+ bytes value = 5;
+}
+
+message Event {
+ enum EventType {
+ PUT = 0;
+ DELETE = 1;
+ EXPIRE = 2;
+ }
+ EventType type = 1;
+ // a put event contains the current key-value
+ // a delete/expire event contains the previous
+ // key-value
+ KeyValue kv = 2;
+}
+
diff --git a/vendor/github.com/coreos/etcd/storage/watchable_store.go b/vendor/github.com/coreos/etcd/storage/watchable_store.go
new file mode 100644
index 000000000..847a57ff9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/watchable_store.go
@@ -0,0 +1,303 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "log"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+type watchableStore struct {
+ mu sync.Mutex
+
+ *store
+
+ // contains all unsynced watchers that needs to sync events that have happened
+ unsynced map[*watcher]struct{}
+
+ // contains all synced watchers that are tracking the events that will happen
+ // The key of the map is the key that the watcher is watching on.
+ synced map[string][]*watcher
+ tx *ongoingTx
+
+ stopc chan struct{}
+ wg sync.WaitGroup
+}
+
+func newWatchableStore(path string) *watchableStore {
+ s := &watchableStore{
+ store: newStore(path),
+ unsynced: make(map[*watcher]struct{}),
+ synced: make(map[string][]*watcher),
+ stopc: make(chan struct{}),
+ }
+ s.wg.Add(1)
+ go s.syncWatchersLoop()
+ return s
+}
+
+func (s *watchableStore) Put(key, value []byte) (rev int64) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ rev = s.store.Put(key, value)
+ // TODO: avoid this range
+ kvs, _, err := s.store.Range(key, nil, 0, rev)
+ if err != nil {
+ log.Panicf("unexpected range error (%v)", err)
+ }
+ s.handle(rev, storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &kvs[0],
+ })
+ return rev
+}
+
+func (s *watchableStore) DeleteRange(key, end []byte) (n, rev int64) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ // TODO: avoid this range
+ kvs, _, err := s.store.Range(key, end, 0, 0)
+ if err != nil {
+ log.Panicf("unexpected range error (%v)", err)
+ }
+ n, rev = s.store.DeleteRange(key, end)
+ for _, kv := range kvs {
+ s.handle(rev, storagepb.Event{
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{
+ Key: kv.Key,
+ },
+ })
+ }
+ return n, rev
+}
+
+func (s *watchableStore) TxnBegin() int64 {
+ s.mu.Lock()
+ s.tx = newOngoingTx()
+ return s.store.TxnBegin()
+}
+
+func (s *watchableStore) TxnPut(txnID int64, key, value []byte) (rev int64, err error) {
+ rev, err = s.store.TxnPut(txnID, key, value)
+ if err == nil {
+ s.tx.put(string(key))
+ }
+ return rev, err
+}
+
+func (s *watchableStore) TxnDeleteRange(txnID int64, key, end []byte) (n, rev int64, err error) {
+ kvs, _, err := s.store.TxnRange(txnID, key, end, 0, 0)
+ if err != nil {
+ log.Panicf("unexpected range error (%v)", err)
+ }
+ n, rev, err = s.store.TxnDeleteRange(txnID, key, end)
+ if err == nil {
+ for _, kv := range kvs {
+ s.tx.del(string(kv.Key))
+ }
+ }
+ return n, rev, err
+}
+
+func (s *watchableStore) TxnEnd(txnID int64) error {
+ err := s.store.TxnEnd(txnID)
+ if err != nil {
+ return err
+ }
+
+ _, rev, _ := s.store.Range(nil, nil, 0, 0)
+ for k := range s.tx.putm {
+ kvs, _, err := s.store.Range([]byte(k), nil, 0, 0)
+ if err != nil {
+ log.Panicf("unexpected range error (%v)", err)
+ }
+ s.handle(rev, storagepb.Event{
+ Type: storagepb.PUT,
+ Kv: &kvs[0],
+ })
+ }
+ for k := range s.tx.delm {
+ s.handle(rev, storagepb.Event{
+ Type: storagepb.DELETE,
+ Kv: &storagepb.KeyValue{
+ Key: []byte(k),
+ },
+ })
+ }
+ s.mu.Unlock()
+ return nil
+}
+
+func (s *watchableStore) Close() error {
+ close(s.stopc)
+ s.wg.Wait()
+ return s.store.Close()
+}
+
+func (s *watchableStore) Watcher(key []byte, prefix bool, startRev int64) (Watcher, CancelFunc) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ wa := newWatcher(key, prefix, startRev)
+ k := string(key)
+ if startRev == 0 {
+ s.synced[k] = append(s.synced[k], wa)
+ } else {
+ slowWatchersGauge.Inc()
+ s.unsynced[wa] = struct{}{}
+ }
+ watchersGauge.Inc()
+
+ cancel := CancelFunc(func() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+ wa.stopWithError(ErrCanceled)
+
+ // remove global references of the watcher
+ if _, ok := s.unsynced[wa]; ok {
+ delete(s.unsynced, wa)
+ slowWatchersGauge.Dec()
+ watchersGauge.Dec()
+ return
+ }
+
+ for i, w := range s.synced[k] {
+ if w == wa {
+ s.synced[k] = append(s.synced[k][:i], s.synced[k][i+1:]...)
+ watchersGauge.Dec()
+ }
+ }
+ // If we cannot find it, it should have finished watch.
+ })
+
+ return wa, cancel
+}
+
+// keepSyncWatchers syncs the watchers in the unsyncd map every 100ms.
+func (s *watchableStore) syncWatchersLoop() {
+ defer s.wg.Done()
+
+ for {
+ s.mu.Lock()
+ s.syncWatchers()
+ s.mu.Unlock()
+
+ select {
+ case <-time.After(100 * time.Millisecond):
+ case <-s.stopc:
+ return
+ }
+ }
+}
+
+// syncWatchers syncs the watchers in the unsyncd map.
+func (s *watchableStore) syncWatchers() {
+ _, curRev, _ := s.store.Range(nil, nil, 0, 0)
+ for w := range s.unsynced {
+ var end []byte
+ if w.prefix {
+ end = make([]byte, len(w.key))
+ copy(end, w.key)
+ end[len(w.key)-1]++
+ }
+ limit := cap(w.ch) - len(w.ch)
+ // the channel is full, try it in the next round
+ if limit == 0 {
+ continue
+ }
+ evs, nextRev, err := s.store.RangeEvents(w.key, end, int64(limit), w.cur)
+ if err != nil {
+ w.stopWithError(err)
+ delete(s.unsynced, w)
+ continue
+ }
+
+ // push events to the channel
+ for _, ev := range evs {
+ w.ch <- ev
+ pendingEventsGauge.Inc()
+ }
+ // switch to tracking future events if needed
+ if nextRev > curRev {
+ s.synced[string(w.key)] = append(s.synced[string(w.key)], w)
+ delete(s.unsynced, w)
+ continue
+ }
+ // put it back to try it in the next round
+ w.cur = nextRev
+ }
+ slowWatchersGauge.Set(float64(len(s.unsynced)))
+}
+
+// handle handles the change of the happening event on all watchers.
+func (s *watchableStore) handle(rev int64, ev storagepb.Event) {
+ s.notify(rev, ev)
+}
+
+// notify notifies the fact that given event at the given rev just happened to
+// watchers that watch on the key of the event.
+func (s *watchableStore) notify(rev int64, ev storagepb.Event) {
+ // check all prefixes of the key to notify all corresponded watchers
+ for i := 0; i <= len(ev.Kv.Key); i++ {
+ ws := s.synced[string(ev.Kv.Key[:i])]
+ nws := ws[:0]
+ for _, w := range ws {
+ // the watcher needs to be notified when either it watches prefix or
+ // the key is exactly matched.
+ if !w.prefix && i != len(ev.Kv.Key) {
+ continue
+ }
+ select {
+ case w.ch <- ev:
+ pendingEventsGauge.Inc()
+ nws = append(nws, w)
+ default:
+ w.cur = rev
+ s.unsynced[w] = struct{}{}
+ slowWatchersGauge.Inc()
+ }
+ }
+ s.synced[string(ev.Kv.Key[:i])] = nws
+ }
+}
+
+type ongoingTx struct {
+ // keys put/deleted in the ongoing txn
+ putm map[string]bool
+ delm map[string]bool
+}
+
+func newOngoingTx() *ongoingTx {
+ return &ongoingTx{
+ putm: make(map[string]bool),
+ delm: make(map[string]bool),
+ }
+}
+
+func (tx *ongoingTx) put(k string) {
+ tx.putm[k] = true
+ tx.delm[k] = false
+}
+
+func (tx *ongoingTx) del(k string) {
+ tx.delm[k] = true
+ tx.putm[k] = false
+}
diff --git a/vendor/github.com/coreos/etcd/storage/watchable_store_bench_test.go b/vendor/github.com/coreos/etcd/storage/watchable_store_bench_test.go
new file mode 100644
index 000000000..4c84b6097
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/watchable_store_bench_test.go
@@ -0,0 +1,81 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "math/rand"
+ "os"
+ "testing"
+)
+
+// Benchmarks on cancel function performance for unsynced watchers
+// in a WatchableStore. It creates k*N watchers to populate unsynced
+// with a reasonably large number of watchers. And measures the time it
+// takes to cancel N watchers out of k*N watchers. The performance is
+// expected to differ depending on the unsynced member implementation.
+// TODO: k is an arbitrary constant. We need to figure out what factor
+// we should put to simulate the real-world use cases.
+func BenchmarkWatchableStoreUnsyncedCancel(b *testing.B) {
+ const k int = 2
+ benchSampleSize := b.N
+ watcherSize := k * benchSampleSize
+ // manually create watchableStore instead of newWatchableStore
+ // because newWatchableStore periodically calls syncWatchersLoop
+ // method to sync watchers in unsynced map. We want to keep watchers
+ // in unsynced for this benchmark.
+ s := &watchableStore{
+ store: newStore(tmpPath),
+ unsynced: make(map[*watcher]struct{}),
+
+ // For previous implementation, use:
+ // unsynced: make([]*watcher, 0),
+
+ // to make the test not crash from assigning to nil map.
+ // 'synced' doesn't get populated in this test.
+ synced: make(map[string][]*watcher),
+ }
+
+ defer func() {
+ s.store.Close()
+ os.Remove(tmpPath)
+ }()
+
+ // Put a key so that we can spawn watchers on that key
+ // (testKey in this test). This increases the rev to 1,
+ // and later we can we set the watcher's startRev to 1,
+ // and force watchers to be in unsynced.
+ testKey := []byte("foo")
+ testValue := []byte("bar")
+ s.Put(testKey, testValue)
+
+ cancels := make([]CancelFunc, watcherSize)
+ for i := 0; i < watcherSize; i++ {
+ // non-0 value to keep watchers in unsynced
+ _, cancel := s.Watcher(testKey, true, 1)
+ cancels[i] = cancel
+ }
+
+ // random-cancel N watchers to make it not biased towards
+ // data structures with an order, such as slice.
+ ix := rand.Perm(watcherSize)
+
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ // cancel N watchers
+ for _, idx := range ix[:benchSampleSize] {
+ cancels[idx]()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/storage/watcher.go b/vendor/github.com/coreos/etcd/storage/watcher.go
new file mode 100644
index 000000000..5971e9b2c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/watcher.go
@@ -0,0 +1,58 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "sync"
+
+ "github.com/coreos/etcd/storage/storagepb"
+)
+
+type watcher struct {
+ key []byte
+ prefix bool
+ cur int64
+
+ ch chan storagepb.Event
+ mu sync.Mutex
+ err error
+}
+
+func newWatcher(key []byte, prefix bool, start int64) *watcher {
+ return &watcher{
+ key: key,
+ prefix: prefix,
+ cur: start,
+ ch: make(chan storagepb.Event, 10),
+ }
+}
+
+func (w *watcher) Event() <-chan storagepb.Event { return w.ch }
+
+func (w *watcher) Err() error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ return w.err
+}
+
+func (w *watcher) stopWithError(err error) {
+ if w.err != nil {
+ return
+ }
+ close(w.ch)
+ w.mu.Lock()
+ w.err = err
+ w.mu.Unlock()
+}
diff --git a/vendor/github.com/coreos/etcd/storage/watcher_bench_test.go b/vendor/github.com/coreos/etcd/storage/watcher_bench_test.go
new file mode 100644
index 000000000..fd556a49a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/storage/watcher_bench_test.go
@@ -0,0 +1,31 @@
+// Copyright 2015 CoreOS, 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 storage
+
+import (
+ "fmt"
+ "testing"
+)
+
+func BenchmarkKVWatcherMemoryUsage(b *testing.B) {
+ s := newWatchableStore(tmpPath)
+ defer cleanup(s, tmpPath)
+
+ b.ReportAllocs()
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ s.Watcher([]byte(fmt.Sprint("foo", i)), false, 0)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/event.go b/vendor/github.com/coreos/etcd/store/event.go
new file mode 100644
index 000000000..59a1dc654
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/event.go
@@ -0,0 +1,71 @@
+// Copyright 2015 CoreOS, 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 store
+
+const (
+ Get = "get"
+ Create = "create"
+ Set = "set"
+ Update = "update"
+ Delete = "delete"
+ CompareAndSwap = "compareAndSwap"
+ CompareAndDelete = "compareAndDelete"
+ Expire = "expire"
+)
+
+type Event struct {
+ Action string `json:"action"`
+ Node *NodeExtern `json:"node,omitempty"`
+ PrevNode *NodeExtern `json:"prevNode,omitempty"`
+ EtcdIndex uint64 `json:"-"`
+}
+
+func newEvent(action string, key string, modifiedIndex, createdIndex uint64) *Event {
+ n := &NodeExtern{
+ Key: key,
+ ModifiedIndex: modifiedIndex,
+ CreatedIndex: createdIndex,
+ }
+
+ return &Event{
+ Action: action,
+ Node: n,
+ }
+}
+
+func (e *Event) IsCreated() bool {
+ if e.Action == Create {
+ return true
+ }
+
+ if e.Action == Set && e.PrevNode == nil {
+ return true
+ }
+
+ return false
+}
+
+func (e *Event) Index() uint64 {
+ return e.Node.ModifiedIndex
+}
+
+func (e *Event) Clone() *Event {
+ return &Event{
+ Action: e.Action,
+ EtcdIndex: e.EtcdIndex,
+ Node: e.Node.Clone(),
+ PrevNode: e.PrevNode.Clone(),
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/event_history.go b/vendor/github.com/coreos/etcd/store/event_history.go
new file mode 100644
index 000000000..edab33953
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/event_history.go
@@ -0,0 +1,126 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "fmt"
+ "path"
+ "strings"
+ "sync"
+
+ etcdErr "github.com/coreos/etcd/error"
+)
+
+type EventHistory struct {
+ Queue eventQueue
+ StartIndex uint64
+ LastIndex uint64
+ rwl sync.RWMutex
+}
+
+func newEventHistory(capacity int) *EventHistory {
+ return &EventHistory{
+ Queue: eventQueue{
+ Capacity: capacity,
+ Events: make([]*Event, capacity),
+ },
+ }
+}
+
+// addEvent function adds event into the eventHistory
+func (eh *EventHistory) addEvent(e *Event) *Event {
+ eh.rwl.Lock()
+ defer eh.rwl.Unlock()
+
+ eh.Queue.insert(e)
+
+ eh.LastIndex = e.Index()
+
+ eh.StartIndex = eh.Queue.Events[eh.Queue.Front].Index()
+
+ return e
+}
+
+// scan enumerates events from the index history and stops at the first point
+// where the key matches.
+func (eh *EventHistory) scan(key string, recursive bool, index uint64) (*Event, *etcdErr.Error) {
+ eh.rwl.RLock()
+ defer eh.rwl.RUnlock()
+
+ // index should be after the event history's StartIndex
+ if index < eh.StartIndex {
+ return nil,
+ etcdErr.NewError(etcdErr.EcodeEventIndexCleared,
+ fmt.Sprintf("the requested history has been cleared [%v/%v]",
+ eh.StartIndex, index), 0)
+ }
+
+ // the index should come before the size of the queue minus the duplicate count
+ if index > eh.LastIndex { // future index
+ return nil, nil
+ }
+
+ offset := index - eh.StartIndex
+ i := (eh.Queue.Front + int(offset)) % eh.Queue.Capacity
+
+ for {
+ e := eh.Queue.Events[i]
+
+ ok := (e.Node.Key == key)
+
+ if recursive {
+ // add tailing slash
+ key := path.Clean(key)
+ if key[len(key)-1] != '/' {
+ key = key + "/"
+ }
+
+ ok = ok || strings.HasPrefix(e.Node.Key, key)
+ }
+
+ if ok {
+ return e, nil
+ }
+
+ i = (i + 1) % eh.Queue.Capacity
+
+ if i == eh.Queue.Back {
+ return nil, nil
+ }
+ }
+}
+
+// clone will be protected by a stop-world lock
+// do not need to obtain internal lock
+func (eh *EventHistory) clone() *EventHistory {
+ clonedQueue := eventQueue{
+ Capacity: eh.Queue.Capacity,
+ Events: make([]*Event, eh.Queue.Capacity),
+ Size: eh.Queue.Size,
+ Front: eh.Queue.Front,
+ Back: eh.Queue.Back,
+ }
+
+ for i, e := range eh.Queue.Events {
+ clonedQueue.Events[i] = e
+ }
+
+ return &EventHistory{
+ StartIndex: eh.StartIndex,
+ Queue: clonedQueue,
+ LastIndex: eh.LastIndex,
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/store/event_queue.go b/vendor/github.com/coreos/etcd/store/event_queue.go
new file mode 100644
index 000000000..cd35d4352
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/event_queue.go
@@ -0,0 +1,34 @@
+// Copyright 2015 CoreOS, 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 store
+
+type eventQueue struct {
+ Events []*Event
+ Size int
+ Front int
+ Back int
+ Capacity int
+}
+
+func (eq *eventQueue) insert(e *Event) {
+ eq.Events[eq.Back] = e
+ eq.Back = (eq.Back + 1) % eq.Capacity
+
+ if eq.Size == eq.Capacity { //dequeue
+ eq.Front = (eq.Front + 1) % eq.Capacity
+ } else {
+ eq.Size++
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/event_test.go b/vendor/github.com/coreos/etcd/store/event_test.go
new file mode 100644
index 000000000..b25c41318
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/event_test.go
@@ -0,0 +1,131 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "testing"
+)
+
+// TestEventQueue tests a queue with capacity = 100
+// Add 200 events into that queue, and test if the
+// previous 100 events have been swapped out.
+func TestEventQueue(t *testing.T) {
+
+ eh := newEventHistory(100)
+
+ // Add
+ for i := 0; i < 200; i++ {
+ e := newEvent(Create, "/foo", uint64(i), uint64(i))
+ eh.addEvent(e)
+ }
+
+ // Test
+ j := 100
+ i := eh.Queue.Front
+ n := eh.Queue.Size
+ for ; n > 0; n-- {
+ e := eh.Queue.Events[i]
+ if e.Index() != uint64(j) {
+ t.Fatalf("queue error!")
+ }
+ j++
+ i = (i + 1) % eh.Queue.Capacity
+ }
+}
+
+func TestScanHistory(t *testing.T) {
+ eh := newEventHistory(100)
+
+ // Add
+ eh.addEvent(newEvent(Create, "/foo", 1, 1))
+ eh.addEvent(newEvent(Create, "/foo/bar", 2, 2))
+ eh.addEvent(newEvent(Create, "/foo/foo", 3, 3))
+ eh.addEvent(newEvent(Create, "/foo/bar/bar", 4, 4))
+ eh.addEvent(newEvent(Create, "/foo/foo/foo", 5, 5))
+
+ e, err := eh.scan("/foo", false, 1)
+ if err != nil || e.Index() != 1 {
+ t.Fatalf("scan error [/foo] [1] %v", e.Index)
+ }
+
+ e, err = eh.scan("/foo/bar", false, 1)
+
+ if err != nil || e.Index() != 2 {
+ t.Fatalf("scan error [/foo/bar] [2] %v", e.Index)
+ }
+
+ e, err = eh.scan("/foo/bar", true, 3)
+
+ if err != nil || e.Index() != 4 {
+ t.Fatalf("scan error [/foo/bar/bar] [4] %v", e.Index)
+ }
+
+ e, err = eh.scan("/foo/bar", true, 6)
+
+ if e != nil {
+ t.Fatalf("bad index shoud reuturn nil")
+ }
+}
+
+// TestFullEventQueue tests a queue with capacity = 10
+// Add 1000 events into that queue, and test if scanning
+// works still for previous events.
+func TestFullEventQueue(t *testing.T) {
+
+ eh := newEventHistory(10)
+
+ // Add
+ for i := 0; i < 1000; i++ {
+ ce := newEvent(Create, "/foo", uint64(i), uint64(i))
+ eh.addEvent(ce)
+ e, err := eh.scan("/foo", true, uint64(i-1))
+ if i > 0 {
+ if e == nil || err != nil {
+ t.Fatalf("scan error [/foo] [%v] %v", i-1, i)
+ }
+ }
+ }
+}
+
+func TestCloneEvent(t *testing.T) {
+ e1 := &Event{
+ Action: Create,
+ EtcdIndex: 1,
+ Node: nil,
+ PrevNode: nil,
+ }
+ e2 := e1.Clone()
+ if e2.Action != Create {
+ t.Fatalf("Action=%q, want %q", e2.Action, Create)
+ }
+ if e2.EtcdIndex != e1.EtcdIndex {
+ t.Fatalf("EtcdIndex=%d, want %d", e2.EtcdIndex, e1.EtcdIndex)
+ }
+ // Changing the cloned node should not affect the original
+ e2.Action = Delete
+ e2.EtcdIndex = uint64(5)
+ if e1.Action != Create {
+ t.Fatalf("Action=%q, want %q", e1.Action, Create)
+ }
+ if e1.EtcdIndex != uint64(1) {
+ t.Fatalf("EtcdIndex=%d, want %d", e1.EtcdIndex, uint64(1))
+ }
+ if e2.Action != Delete {
+ t.Fatalf("Action=%q, want %q", e2.Action, Delete)
+ }
+ if e2.EtcdIndex != uint64(5) {
+ t.Fatalf("EtcdIndex=%d, want %d", e2.EtcdIndex, uint64(5))
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/heap_test.go b/vendor/github.com/coreos/etcd/store/heap_test.go
new file mode 100644
index 000000000..ad52f4d39
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/heap_test.go
@@ -0,0 +1,94 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+func TestHeapPushPop(t *testing.T) {
+ h := newTtlKeyHeap()
+
+ // add from older expire time to earlier expire time
+ // the path is equal to ttl from now
+ for i := 0; i < 10; i++ {
+ path := fmt.Sprintf("%v", 10-i)
+ m := time.Duration(10 - i)
+ n := newKV(nil, path, path, 0, nil, time.Now().Add(time.Second*m))
+ h.push(n)
+ }
+
+ min := time.Now()
+
+ for i := 0; i < 10; i++ {
+ node := h.pop()
+ if node.ExpireTime.Before(min) {
+ t.Fatal("heap sort wrong!")
+ }
+ min = node.ExpireTime
+ }
+
+}
+
+func TestHeapUpdate(t *testing.T) {
+ h := newTtlKeyHeap()
+
+ kvs := make([]*node, 10)
+
+ // add from older expire time to earlier expire time
+ // the path is equal to ttl from now
+ for i := range kvs {
+ path := fmt.Sprintf("%v", 10-i)
+ m := time.Duration(10 - i)
+ n := newKV(nil, path, path, 0, nil, time.Now().Add(time.Second*m))
+ kvs[i] = n
+ h.push(n)
+ }
+
+ // Path 7
+ kvs[3].ExpireTime = time.Now().Add(time.Second * 11)
+
+ // Path 5
+ kvs[5].ExpireTime = time.Now().Add(time.Second * 12)
+
+ h.update(kvs[3])
+ h.update(kvs[5])
+
+ min := time.Now()
+
+ for i := 0; i < 10; i++ {
+ node := h.pop()
+ if node.ExpireTime.Before(min) {
+ t.Fatal("heap sort wrong!")
+ }
+ min = node.ExpireTime
+
+ if i == 8 {
+ if node.Path != "7" {
+ t.Fatal("heap sort wrong!", node.Path)
+ }
+ }
+
+ if i == 9 {
+ if node.Path != "5" {
+ t.Fatal("heap sort wrong!")
+ }
+ }
+
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/store/metrics.go b/vendor/github.com/coreos/etcd/store/metrics.go
new file mode 100644
index 000000000..91bc803f9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/metrics.go
@@ -0,0 +1,128 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+)
+
+// Set of raw Prometheus metrics.
+// Labels
+// * action = declared in event.go
+// * outcome = Outcome
+// Do not increment directly, use Report* methods.
+var (
+ readCounter = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "reads_total",
+ Help: "Total number of reads action by (get/getRecursive), local to this member.",
+ }, []string{"action"})
+
+ writeCounter = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "writes_total",
+ Help: "Total number of writes (e.g. set/compareAndDelete) seen by this member.",
+ }, []string{"action"})
+
+ readFailedCounter = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "reads_failed_total",
+ Help: "Failed read actions by (get/getRecursive), local to this member.",
+ }, []string{"action"})
+
+ writeFailedCounter = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "writes_failed_total",
+ Help: "Failed write actions (e.g. set/compareAndDelete), seen by this member.",
+ }, []string{"action"})
+
+ expireCounter = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "expires_total",
+ Help: "Total number of expired keys.",
+ })
+
+ watchRequests = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "watch_requests_total",
+ Help: "Total number of incoming watch requests (new or reestablished).",
+ })
+
+ watcherCount = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "store",
+ Name: "watchers",
+ Help: "Count of currently active watchers.",
+ })
+)
+
+const (
+ GetRecursive = "getRecursive"
+)
+
+func init() {
+ prometheus.MustRegister(readCounter)
+ prometheus.MustRegister(writeCounter)
+ prometheus.MustRegister(expireCounter)
+ prometheus.MustRegister(watchRequests)
+ prometheus.MustRegister(watcherCount)
+}
+
+func reportReadSuccess(read_action string) {
+ readCounter.WithLabelValues(read_action).Inc()
+}
+
+func reportReadFailure(read_action string) {
+ readCounter.WithLabelValues(read_action).Inc()
+ readFailedCounter.WithLabelValues(read_action).Inc()
+}
+
+func reportWriteSuccess(write_action string) {
+ writeCounter.WithLabelValues(write_action).Inc()
+}
+
+func reportWriteFailure(write_action string) {
+ writeCounter.WithLabelValues(write_action).Inc()
+ writeFailedCounter.WithLabelValues(write_action).Inc()
+}
+
+func reportExpiredKey() {
+ expireCounter.Inc()
+}
+
+func reportWatchRequest() {
+ watchRequests.Inc()
+}
+
+func reportWatcherAdded() {
+ watcherCount.Inc()
+}
+
+func reportWatcherRemoved() {
+ watcherCount.Dec()
+}
diff --git a/vendor/github.com/coreos/etcd/store/node.go b/vendor/github.com/coreos/etcd/store/node.go
new file mode 100644
index 000000000..0ad9abd4c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/node.go
@@ -0,0 +1,400 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "path"
+ "sort"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ etcdErr "github.com/coreos/etcd/error"
+)
+
+// explanations of Compare function result
+const (
+ CompareMatch = 0
+ CompareIndexNotMatch = 1
+ CompareValueNotMatch = 2
+ CompareNotMatch = 3
+)
+
+var Permanent time.Time
+
+// node is the basic element in the store system.
+// A key-value pair will have a string value
+// A directory will have a children map
+type node struct {
+ Path string
+
+ CreatedIndex uint64
+ ModifiedIndex uint64
+
+ Parent *node `json:"-"` // should not encode this field! avoid circular dependency.
+
+ ExpireTime time.Time
+ Value string // for key-value pair
+ Children map[string]*node // for directory
+
+ // A reference to the store this node is attached to.
+ store *store
+}
+
+// newKV creates a Key-Value pair
+func newKV(store *store, nodePath string, value string, createdIndex uint64, parent *node, expireTime time.Time) *node {
+ return &node{
+ Path: nodePath,
+ CreatedIndex: createdIndex,
+ ModifiedIndex: createdIndex,
+ Parent: parent,
+ store: store,
+ ExpireTime: expireTime,
+ Value: value,
+ }
+}
+
+// newDir creates a directory
+func newDir(store *store, nodePath string, createdIndex uint64, parent *node, expireTime time.Time) *node {
+ return &node{
+ Path: nodePath,
+ CreatedIndex: createdIndex,
+ ModifiedIndex: createdIndex,
+ Parent: parent,
+ ExpireTime: expireTime,
+ Children: make(map[string]*node),
+ store: store,
+ }
+}
+
+// IsHidden function checks if the node is a hidden node. A hidden node
+// will begin with '_'
+// A hidden node will not be shown via get command under a directory
+// For example if we have /foo/_hidden and /foo/notHidden, get "/foo"
+// will only return /foo/notHidden
+func (n *node) IsHidden() bool {
+ _, name := path.Split(n.Path)
+
+ return name[0] == '_'
+}
+
+// IsPermanent function checks if the node is a permanent one.
+func (n *node) IsPermanent() bool {
+ // we use a uninitialized time.Time to indicate the node is a
+ // permanent one.
+ // the uninitialized time.Time should equal zero.
+ return n.ExpireTime.IsZero()
+}
+
+// IsDir function checks whether the node is a directory.
+// If the node is a directory, the function will return true.
+// Otherwise the function will return false.
+func (n *node) IsDir() bool {
+ return !(n.Children == nil)
+}
+
+// Read function gets the value of the node.
+// If the receiver node is not a key-value pair, a "Not A File" error will be returned.
+func (n *node) Read() (string, *etcdErr.Error) {
+ if n.IsDir() {
+ return "", etcdErr.NewError(etcdErr.EcodeNotFile, "", n.store.CurrentIndex)
+ }
+
+ return n.Value, nil
+}
+
+// Write function set the value of the node to the given value.
+// If the receiver node is a directory, a "Not A File" error will be returned.
+func (n *node) Write(value string, index uint64) *etcdErr.Error {
+ if n.IsDir() {
+ return etcdErr.NewError(etcdErr.EcodeNotFile, "", n.store.CurrentIndex)
+ }
+
+ n.Value = value
+ n.ModifiedIndex = index
+
+ return nil
+}
+
+func (n *node) expirationAndTTL(clock clockwork.Clock) (*time.Time, int64) {
+ if !n.IsPermanent() {
+ /* compute ttl as:
+ ceiling( (expireTime - timeNow) / nanosecondsPerSecond )
+ which ranges from 1..n
+ rather than as:
+ ( (expireTime - timeNow) / nanosecondsPerSecond ) + 1
+ which ranges 1..n+1
+ */
+ ttlN := n.ExpireTime.Sub(clock.Now())
+ ttl := ttlN / time.Second
+ if (ttlN % time.Second) > 0 {
+ ttl++
+ }
+ t := n.ExpireTime.UTC()
+ return &t, int64(ttl)
+ }
+ return nil, 0
+}
+
+// List function return a slice of nodes under the receiver node.
+// If the receiver node is not a directory, a "Not A Directory" error will be returned.
+func (n *node) List() ([]*node, *etcdErr.Error) {
+ if !n.IsDir() {
+ return nil, etcdErr.NewError(etcdErr.EcodeNotDir, "", n.store.CurrentIndex)
+ }
+
+ nodes := make([]*node, len(n.Children))
+
+ i := 0
+ for _, node := range n.Children {
+ nodes[i] = node
+ i++
+ }
+
+ return nodes, nil
+}
+
+// GetChild function returns the child node under the directory node.
+// On success, it returns the file node
+func (n *node) GetChild(name string) (*node, *etcdErr.Error) {
+ if !n.IsDir() {
+ return nil, etcdErr.NewError(etcdErr.EcodeNotDir, n.Path, n.store.CurrentIndex)
+ }
+
+ child, ok := n.Children[name]
+
+ if ok {
+ return child, nil
+ }
+
+ return nil, nil
+}
+
+// Add function adds a node to the receiver node.
+// If the receiver is not a directory, a "Not A Directory" error will be returned.
+// If there is an existing node with the same name under the directory, a "Already Exist"
+// error will be returned
+func (n *node) Add(child *node) *etcdErr.Error {
+ if !n.IsDir() {
+ return etcdErr.NewError(etcdErr.EcodeNotDir, "", n.store.CurrentIndex)
+ }
+
+ _, name := path.Split(child.Path)
+
+ _, ok := n.Children[name]
+
+ if ok {
+ return etcdErr.NewError(etcdErr.EcodeNodeExist, "", n.store.CurrentIndex)
+ }
+
+ n.Children[name] = child
+
+ return nil
+}
+
+// Remove function remove the node.
+func (n *node) Remove(dir, recursive bool, callback func(path string)) *etcdErr.Error {
+
+ if n.IsDir() {
+ if !dir {
+ // cannot delete a directory without recursive set to true
+ return etcdErr.NewError(etcdErr.EcodeNotFile, n.Path, n.store.CurrentIndex)
+ }
+
+ if len(n.Children) != 0 && !recursive {
+ // cannot delete a directory if it is not empty and the operation
+ // is not recursive
+ return etcdErr.NewError(etcdErr.EcodeDirNotEmpty, n.Path, n.store.CurrentIndex)
+ }
+ }
+
+ if !n.IsDir() { // key-value pair
+ _, name := path.Split(n.Path)
+
+ // find its parent and remove the node from the map
+ if n.Parent != nil && n.Parent.Children[name] == n {
+ delete(n.Parent.Children, name)
+ }
+
+ if callback != nil {
+ callback(n.Path)
+ }
+
+ if !n.IsPermanent() {
+ n.store.ttlKeyHeap.remove(n)
+ }
+
+ return nil
+ }
+
+ for _, child := range n.Children { // delete all children
+ child.Remove(true, true, callback)
+ }
+
+ // delete self
+ _, name := path.Split(n.Path)
+ if n.Parent != nil && n.Parent.Children[name] == n {
+ delete(n.Parent.Children, name)
+
+ if callback != nil {
+ callback(n.Path)
+ }
+
+ if !n.IsPermanent() {
+ n.store.ttlKeyHeap.remove(n)
+ }
+
+ }
+
+ return nil
+}
+
+func (n *node) Repr(recursive, sorted bool, clock clockwork.Clock) *NodeExtern {
+ if n.IsDir() {
+ node := &NodeExtern{
+ Key: n.Path,
+ Dir: true,
+ ModifiedIndex: n.ModifiedIndex,
+ CreatedIndex: n.CreatedIndex,
+ }
+ node.Expiration, node.TTL = n.expirationAndTTL(clock)
+
+ if !recursive {
+ return node
+ }
+
+ children, _ := n.List()
+ node.Nodes = make(NodeExterns, len(children))
+
+ // we do not use the index in the children slice directly
+ // we need to skip the hidden one
+ i := 0
+
+ for _, child := range children {
+
+ if child.IsHidden() { // get will not list hidden node
+ continue
+ }
+
+ node.Nodes[i] = child.Repr(recursive, sorted, clock)
+
+ i++
+ }
+
+ // eliminate hidden nodes
+ node.Nodes = node.Nodes[:i]
+ if sorted {
+ sort.Sort(node.Nodes)
+ }
+
+ return node
+ }
+
+ // since n.Value could be changed later, so we need to copy the value out
+ value := n.Value
+ node := &NodeExtern{
+ Key: n.Path,
+ Value: &value,
+ ModifiedIndex: n.ModifiedIndex,
+ CreatedIndex: n.CreatedIndex,
+ }
+ node.Expiration, node.TTL = n.expirationAndTTL(clock)
+ return node
+}
+
+func (n *node) UpdateTTL(expireTime time.Time) {
+
+ if !n.IsPermanent() {
+ if expireTime.IsZero() {
+ // from ttl to permanent
+ n.ExpireTime = expireTime
+ // remove from ttl heap
+ n.store.ttlKeyHeap.remove(n)
+ } else {
+ // update ttl
+ n.ExpireTime = expireTime
+ // update ttl heap
+ n.store.ttlKeyHeap.update(n)
+ }
+
+ } else {
+ if !expireTime.IsZero() {
+ // from permanent to ttl
+ n.ExpireTime = expireTime
+ // push into ttl heap
+ n.store.ttlKeyHeap.push(n)
+ }
+ }
+}
+
+// Compare function compares node index and value with provided ones.
+// second result value explains result and equals to one of Compare.. constants
+func (n *node) Compare(prevValue string, prevIndex uint64) (ok bool, which int) {
+ indexMatch := (prevIndex == 0 || n.ModifiedIndex == prevIndex)
+ valueMatch := (prevValue == "" || n.Value == prevValue)
+ ok = valueMatch && indexMatch
+ switch {
+ case valueMatch && indexMatch:
+ which = CompareMatch
+ case indexMatch && !valueMatch:
+ which = CompareValueNotMatch
+ case valueMatch && !indexMatch:
+ which = CompareIndexNotMatch
+ default:
+ which = CompareNotMatch
+ }
+ return
+}
+
+// Clone function clone the node recursively and return the new node.
+// If the node is a directory, it will clone all the content under this directory.
+// If the node is a key-value pair, it will clone the pair.
+func (n *node) Clone() *node {
+ if !n.IsDir() {
+ newkv := newKV(n.store, n.Path, n.Value, n.CreatedIndex, n.Parent, n.ExpireTime)
+ newkv.ModifiedIndex = n.ModifiedIndex
+ return newkv
+ }
+
+ clone := newDir(n.store, n.Path, n.CreatedIndex, n.Parent, n.ExpireTime)
+ clone.ModifiedIndex = n.ModifiedIndex
+
+ for key, child := range n.Children {
+ clone.Children[key] = child.Clone()
+ }
+
+ return clone
+}
+
+// recoverAndclean function help to do recovery.
+// Two things need to be done: 1. recovery structure; 2. delete expired nodes
+
+// If the node is a directory, it will help recover children's parent pointer and recursively
+// call this function on its children.
+// We check the expire last since we need to recover the whole structure first and add all the
+// notifications into the event history.
+func (n *node) recoverAndclean() {
+ if n.IsDir() {
+ for _, child := range n.Children {
+ child.Parent = n
+ child.store = n.store
+ child.recoverAndclean()
+ }
+ }
+
+ if !n.ExpireTime.IsZero() {
+ n.store.ttlKeyHeap.push(n)
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/store/node_extern.go b/vendor/github.com/coreos/etcd/store/node_extern.go
new file mode 100644
index 000000000..e26f857bc
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/node_extern.go
@@ -0,0 +1,115 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "sort"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+)
+
+// NodeExtern is the external representation of the
+// internal node with additional fields
+// PrevValue is the previous value of the node
+// TTL is time to live in second
+type NodeExtern struct {
+ Key string `json:"key,omitempty"`
+ Value *string `json:"value,omitempty"`
+ Dir bool `json:"dir,omitempty"`
+ Expiration *time.Time `json:"expiration,omitempty"`
+ TTL int64 `json:"ttl,omitempty"`
+ Nodes NodeExterns `json:"nodes,omitempty"`
+ ModifiedIndex uint64 `json:"modifiedIndex,omitempty"`
+ CreatedIndex uint64 `json:"createdIndex,omitempty"`
+}
+
+func (eNode *NodeExtern) loadInternalNode(n *node, recursive, sorted bool, clock clockwork.Clock) {
+ if n.IsDir() { // node is a directory
+ eNode.Dir = true
+
+ children, _ := n.List()
+ eNode.Nodes = make(NodeExterns, len(children))
+
+ // we do not use the index in the children slice directly
+ // we need to skip the hidden one
+ i := 0
+
+ for _, child := range children {
+ if child.IsHidden() { // get will not return hidden nodes
+ continue
+ }
+
+ eNode.Nodes[i] = child.Repr(recursive, sorted, clock)
+ i++
+ }
+
+ // eliminate hidden nodes
+ eNode.Nodes = eNode.Nodes[:i]
+
+ if sorted {
+ sort.Sort(eNode.Nodes)
+ }
+
+ } else { // node is a file
+ value, _ := n.Read()
+ eNode.Value = &value
+ }
+
+ eNode.Expiration, eNode.TTL = n.expirationAndTTL(clock)
+}
+
+func (eNode *NodeExtern) Clone() *NodeExtern {
+ if eNode == nil {
+ return nil
+ }
+ nn := &NodeExtern{
+ Key: eNode.Key,
+ Dir: eNode.Dir,
+ TTL: eNode.TTL,
+ ModifiedIndex: eNode.ModifiedIndex,
+ CreatedIndex: eNode.CreatedIndex,
+ }
+ if eNode.Value != nil {
+ s := *eNode.Value
+ nn.Value = &s
+ }
+ if eNode.Expiration != nil {
+ t := *eNode.Expiration
+ nn.Expiration = &t
+ }
+ if eNode.Nodes != nil {
+ nn.Nodes = make(NodeExterns, len(eNode.Nodes))
+ for i, n := range eNode.Nodes {
+ nn.Nodes[i] = n.Clone()
+ }
+ }
+ return nn
+}
+
+type NodeExterns []*NodeExtern
+
+// interfaces for sorting
+func (ns NodeExterns) Len() int {
+ return len(ns)
+}
+
+func (ns NodeExterns) Less(i, j int) bool {
+ return ns[i].Key < ns[j].Key
+}
+
+func (ns NodeExterns) Swap(i, j int) {
+ ns[i], ns[j] = ns[j], ns[i]
+}
diff --git a/vendor/github.com/coreos/etcd/store/node_extern_test.go b/vendor/github.com/coreos/etcd/store/node_extern_test.go
new file mode 100644
index 000000000..d7398ec50
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/node_extern_test.go
@@ -0,0 +1,107 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "reflect"
+ "testing"
+ "time"
+ "unsafe"
+)
+import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
+
+func TestNodeExternClone(t *testing.T) {
+ var eNode *NodeExtern
+ if g := eNode.Clone(); g != nil {
+ t.Fatalf("nil.Clone=%v, want nil", g)
+ }
+
+ const (
+ key string = "/foo/bar"
+ ttl int64 = 123456789
+ ci uint64 = 123
+ mi uint64 = 321
+ )
+ var (
+ val = "some_data"
+ valp = &val
+ exp = time.Unix(12345, 67890)
+ expp = &exp
+ child = NodeExtern{}
+ childp = &child
+ childs = []*NodeExtern{childp}
+ )
+
+ eNode = &NodeExtern{
+ Key: key,
+ TTL: ttl,
+ CreatedIndex: ci,
+ ModifiedIndex: mi,
+ Value: valp,
+ Expiration: expp,
+ Nodes: childs,
+ }
+
+ gNode := eNode.Clone()
+ // Check the clone is as expected
+ assert.Equal(t, gNode.Key, key)
+ assert.Equal(t, gNode.TTL, ttl)
+ assert.Equal(t, gNode.CreatedIndex, ci)
+ assert.Equal(t, gNode.ModifiedIndex, mi)
+ // values should be the same
+ assert.Equal(t, *gNode.Value, val)
+ assert.Equal(t, *gNode.Expiration, exp)
+ assert.Equal(t, len(gNode.Nodes), len(childs))
+ assert.Equal(t, *gNode.Nodes[0], child)
+ // but pointers should differ
+ if gNode.Value == eNode.Value {
+ t.Fatalf("expected value pointers to differ, but got same!")
+ }
+ if gNode.Expiration == eNode.Expiration {
+ t.Fatalf("expected expiration pointers to differ, but got same!")
+ }
+ if sameSlice(gNode.Nodes, eNode.Nodes) {
+ t.Fatalf("expected nodes pointers to differ, but got same!")
+ }
+ // Original should be the same
+ assert.Equal(t, eNode.Key, key)
+ assert.Equal(t, eNode.TTL, ttl)
+ assert.Equal(t, eNode.CreatedIndex, ci)
+ assert.Equal(t, eNode.ModifiedIndex, mi)
+ assert.Equal(t, eNode.Value, valp)
+ assert.Equal(t, eNode.Expiration, expp)
+ if !sameSlice(eNode.Nodes, childs) {
+ t.Fatalf("expected nodes pointer to same, but got different!")
+ }
+ // Change the clone and ensure the original is not affected
+ gNode.Key = "/baz"
+ gNode.TTL = 0
+ gNode.Nodes[0].Key = "uno"
+ assert.Equal(t, eNode.Key, key)
+ assert.Equal(t, eNode.TTL, ttl)
+ assert.Equal(t, eNode.CreatedIndex, ci)
+ assert.Equal(t, eNode.ModifiedIndex, mi)
+ assert.Equal(t, *eNode.Nodes[0], child)
+ // Change the original and ensure the clone is not affected
+ eNode.Key = "/wuf"
+ assert.Equal(t, eNode.Key, "/wuf")
+ assert.Equal(t, gNode.Key, "/baz")
+}
+
+func sameSlice(a, b []*NodeExtern) bool {
+ ah := (*reflect.SliceHeader)(unsafe.Pointer(&a))
+ bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
+ return *ah == *bh
+}
diff --git a/vendor/github.com/coreos/etcd/store/stats.go b/vendor/github.com/coreos/etcd/store/stats.go
new file mode 100644
index 000000000..96926d9c0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/stats.go
@@ -0,0 +1,139 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "encoding/json"
+ "sync/atomic"
+)
+
+const (
+ SetSuccess = iota
+ SetFail
+ DeleteSuccess
+ DeleteFail
+ CreateSuccess
+ CreateFail
+ UpdateSuccess
+ UpdateFail
+ CompareAndSwapSuccess
+ CompareAndSwapFail
+ GetSuccess
+ GetFail
+ ExpireCount
+ CompareAndDeleteSuccess
+ CompareAndDeleteFail
+)
+
+type Stats struct {
+
+ // Number of get requests
+ GetSuccess uint64 `json:"getsSuccess"`
+ GetFail uint64 `json:"getsFail"`
+
+ // Number of sets requests
+ SetSuccess uint64 `json:"setsSuccess"`
+ SetFail uint64 `json:"setsFail"`
+
+ // Number of delete requests
+ DeleteSuccess uint64 `json:"deleteSuccess"`
+ DeleteFail uint64 `json:"deleteFail"`
+
+ // Number of update requests
+ UpdateSuccess uint64 `json:"updateSuccess"`
+ UpdateFail uint64 `json:"updateFail"`
+
+ // Number of create requests
+ CreateSuccess uint64 `json:"createSuccess"`
+ CreateFail uint64 `json:"createFail"`
+
+ // Number of testAndSet requests
+ CompareAndSwapSuccess uint64 `json:"compareAndSwapSuccess"`
+ CompareAndSwapFail uint64 `json:"compareAndSwapFail"`
+
+ // Number of compareAndDelete requests
+ CompareAndDeleteSuccess uint64 `json:"compareAndDeleteSuccess"`
+ CompareAndDeleteFail uint64 `json:"compareAndDeleteFail"`
+
+ ExpireCount uint64 `json:"expireCount"`
+
+ Watchers uint64 `json:"watchers"`
+}
+
+func newStats() *Stats {
+ s := new(Stats)
+ return s
+}
+
+func (s *Stats) clone() *Stats {
+ return &Stats{
+ GetSuccess: s.GetSuccess,
+ GetFail: s.GetFail,
+ SetSuccess: s.SetSuccess,
+ SetFail: s.SetFail,
+ DeleteSuccess: s.DeleteSuccess,
+ DeleteFail: s.DeleteFail,
+ UpdateSuccess: s.UpdateSuccess,
+ UpdateFail: s.UpdateFail,
+ CreateSuccess: s.CreateSuccess,
+ CreateFail: s.CreateFail,
+ CompareAndSwapSuccess: s.CompareAndSwapSuccess,
+ CompareAndSwapFail: s.CompareAndSwapFail,
+ CompareAndDeleteSuccess: s.CompareAndDeleteSuccess,
+ CompareAndDeleteFail: s.CompareAndDeleteFail,
+ ExpireCount: s.ExpireCount,
+ Watchers: s.Watchers,
+ }
+}
+
+func (s *Stats) toJson() []byte {
+ b, _ := json.Marshal(s)
+ return b
+}
+
+func (s *Stats) Inc(field int) {
+ switch field {
+ case SetSuccess:
+ atomic.AddUint64(&s.SetSuccess, 1)
+ case SetFail:
+ atomic.AddUint64(&s.SetFail, 1)
+ case CreateSuccess:
+ atomic.AddUint64(&s.CreateSuccess, 1)
+ case CreateFail:
+ atomic.AddUint64(&s.CreateFail, 1)
+ case DeleteSuccess:
+ atomic.AddUint64(&s.DeleteSuccess, 1)
+ case DeleteFail:
+ atomic.AddUint64(&s.DeleteFail, 1)
+ case GetSuccess:
+ atomic.AddUint64(&s.GetSuccess, 1)
+ case GetFail:
+ atomic.AddUint64(&s.GetFail, 1)
+ case UpdateSuccess:
+ atomic.AddUint64(&s.UpdateSuccess, 1)
+ case UpdateFail:
+ atomic.AddUint64(&s.UpdateFail, 1)
+ case CompareAndSwapSuccess:
+ atomic.AddUint64(&s.CompareAndSwapSuccess, 1)
+ case CompareAndSwapFail:
+ atomic.AddUint64(&s.CompareAndSwapFail, 1)
+ case CompareAndDeleteSuccess:
+ atomic.AddUint64(&s.CompareAndDeleteSuccess, 1)
+ case CompareAndDeleteFail:
+ atomic.AddUint64(&s.CompareAndDeleteFail, 1)
+ case ExpireCount:
+ atomic.AddUint64(&s.ExpireCount, 1)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/stats_test.go b/vendor/github.com/coreos/etcd/store/stats_test.go
new file mode 100644
index 000000000..da1bac077
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/stats_test.go
@@ -0,0 +1,112 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
+)
+
+// Ensure that a successful Get is recorded in the stats.
+func TestStoreStatsGetSuccess(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ s.Get("/foo", false, false)
+ assert.Equal(t, uint64(1), s.Stats.GetSuccess, "")
+}
+
+// Ensure that a failed Get is recorded in the stats.
+func TestStoreStatsGetFail(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ s.Get("/no_such_key", false, false)
+ assert.Equal(t, uint64(1), s.Stats.GetFail, "")
+}
+
+// Ensure that a successful Create is recorded in the stats.
+func TestStoreStatsCreateSuccess(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ assert.Equal(t, uint64(1), s.Stats.CreateSuccess, "")
+}
+
+// Ensure that a failed Create is recorded in the stats.
+func TestStoreStatsCreateFail(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", true, "", false, Permanent)
+ s.Create("/foo", false, "bar", false, Permanent)
+ assert.Equal(t, uint64(1), s.Stats.CreateFail, "")
+}
+
+// Ensure that a successful Update is recorded in the stats.
+func TestStoreStatsUpdateSuccess(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ s.Update("/foo", "baz", Permanent)
+ assert.Equal(t, uint64(1), s.Stats.UpdateSuccess, "")
+}
+
+// Ensure that a failed Update is recorded in the stats.
+func TestStoreStatsUpdateFail(t *testing.T) {
+ s := newStore()
+ s.Update("/foo", "bar", Permanent)
+ assert.Equal(t, uint64(1), s.Stats.UpdateFail, "")
+}
+
+// Ensure that a successful CAS is recorded in the stats.
+func TestStoreStatsCompareAndSwapSuccess(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
+ assert.Equal(t, uint64(1), s.Stats.CompareAndSwapSuccess, "")
+}
+
+// Ensure that a failed CAS is recorded in the stats.
+func TestStoreStatsCompareAndSwapFail(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ s.CompareAndSwap("/foo", "wrong_value", 0, "baz", Permanent)
+ assert.Equal(t, uint64(1), s.Stats.CompareAndSwapFail, "")
+}
+
+// Ensure that a successful Delete is recorded in the stats.
+func TestStoreStatsDeleteSuccess(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ s.Delete("/foo", false, false)
+ assert.Equal(t, uint64(1), s.Stats.DeleteSuccess, "")
+}
+
+// Ensure that a failed Delete is recorded in the stats.
+func TestStoreStatsDeleteFail(t *testing.T) {
+ s := newStore()
+ s.Delete("/foo", false, false)
+ assert.Equal(t, uint64(1), s.Stats.DeleteFail, "")
+}
+
+//Ensure that the number of expirations is recorded in the stats.
+func TestStoreStatsExpireCount(t *testing.T) {
+ s := newStore()
+ fc := newFakeClock()
+ s.clock = fc
+
+ s.Create("/foo", false, "bar", false, fc.Now().Add(500*time.Millisecond))
+ assert.Equal(t, uint64(0), s.Stats.ExpireCount, "")
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+ assert.Equal(t, uint64(1), s.Stats.ExpireCount, "")
+}
diff --git a/vendor/github.com/coreos/etcd/store/store.go b/vendor/github.com/coreos/etcd/store/store.go
new file mode 100644
index 000000000..93e2e6047
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/store.go
@@ -0,0 +1,712 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "encoding/json"
+ "fmt"
+ "path"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ etcdErr "github.com/coreos/etcd/error"
+ "github.com/coreos/etcd/pkg/types"
+)
+
+// The default version to set when the store is first initialized.
+const defaultVersion = 2
+
+var minExpireTime time.Time
+
+func init() {
+ minExpireTime, _ = time.Parse(time.RFC3339, "2000-01-01T00:00:00Z")
+}
+
+type Store interface {
+ Version() int
+ Index() uint64
+
+ Get(nodePath string, recursive, sorted bool) (*Event, error)
+ Set(nodePath string, dir bool, value string, expireTime time.Time) (*Event, error)
+ Update(nodePath string, newValue string, expireTime time.Time) (*Event, error)
+ Create(nodePath string, dir bool, value string, unique bool,
+ expireTime time.Time) (*Event, error)
+ CompareAndSwap(nodePath string, prevValue string, prevIndex uint64,
+ value string, expireTime time.Time) (*Event, error)
+ Delete(nodePath string, dir, recursive bool) (*Event, error)
+ CompareAndDelete(nodePath string, prevValue string, prevIndex uint64) (*Event, error)
+
+ Watch(prefix string, recursive, stream bool, sinceIndex uint64) (Watcher, error)
+
+ Save() ([]byte, error)
+ Recovery(state []byte) error
+
+ Clone() Store
+ SaveNoCopy() ([]byte, error)
+
+ JsonStats() []byte
+ DeleteExpiredKeys(cutoff time.Time)
+}
+
+type store struct {
+ Root *node
+ WatcherHub *watcherHub
+ CurrentIndex uint64
+ Stats *Stats
+ CurrentVersion int
+ ttlKeyHeap *ttlKeyHeap // need to recovery manually
+ worldLock sync.RWMutex // stop the world lock
+ clock clockwork.Clock
+ readonlySet types.Set
+}
+
+// The given namespaces will be created as initial directories in the returned store.
+func New(namespaces ...string) Store {
+ s := newStore(namespaces...)
+ s.clock = clockwork.NewRealClock()
+ return s
+}
+
+func newStore(namespaces ...string) *store {
+ s := new(store)
+ s.CurrentVersion = defaultVersion
+ s.Root = newDir(s, "/", s.CurrentIndex, nil, Permanent)
+ for _, namespace := range namespaces {
+ s.Root.Add(newDir(s, namespace, s.CurrentIndex, s.Root, Permanent))
+ }
+ s.Stats = newStats()
+ s.WatcherHub = newWatchHub(1000)
+ s.ttlKeyHeap = newTtlKeyHeap()
+ s.readonlySet = types.NewUnsafeSet(append(namespaces, "/")...)
+ return s
+}
+
+// Version retrieves current version of the store.
+func (s *store) Version() int {
+ return s.CurrentVersion
+}
+
+// Retrieves current of the store
+func (s *store) Index() uint64 {
+ s.worldLock.RLock()
+ defer s.worldLock.RUnlock()
+ return s.CurrentIndex
+}
+
+// Get returns a get event.
+// If recursive is true, it will return all the content under the node path.
+// If sorted is true, it will sort the content by keys.
+func (s *store) Get(nodePath string, recursive, sorted bool) (*Event, error) {
+ s.worldLock.RLock()
+ defer s.worldLock.RUnlock()
+
+ nodePath = path.Clean(path.Join("/", nodePath))
+
+ n, err := s.internalGet(nodePath)
+
+ if err != nil {
+ s.Stats.Inc(GetFail)
+ if recursive {
+ reportReadFailure(GetRecursive)
+ } else {
+ reportReadFailure(Get)
+ }
+ return nil, err
+ }
+
+ e := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
+ e.EtcdIndex = s.CurrentIndex
+ e.Node.loadInternalNode(n, recursive, sorted, s.clock)
+
+ s.Stats.Inc(GetSuccess)
+ if recursive {
+ reportReadSuccess(GetRecursive)
+ } else {
+ reportReadSuccess(Get)
+ }
+
+ return e, nil
+}
+
+// Create creates the node at nodePath. Create will help to create intermediate directories with no ttl.
+// If the node has already existed, create will fail.
+// If any node on the path is a file, create will fail.
+func (s *store) Create(nodePath string, dir bool, value string, unique bool, expireTime time.Time) (*Event, error) {
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+ e, err := s.internalCreate(nodePath, dir, value, unique, false, expireTime, Create)
+
+ if err == nil {
+ e.EtcdIndex = s.CurrentIndex
+ s.WatcherHub.notify(e)
+ s.Stats.Inc(CreateSuccess)
+ reportWriteSuccess(Create)
+ } else {
+ s.Stats.Inc(CreateFail)
+ reportWriteFailure(Create)
+ }
+
+ return e, err
+}
+
+// Set creates or replace the node at nodePath.
+func (s *store) Set(nodePath string, dir bool, value string, expireTime time.Time) (*Event, error) {
+ var err error
+
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+
+ defer func() {
+ if err == nil {
+ s.Stats.Inc(SetSuccess)
+ reportWriteSuccess(Set)
+ } else {
+ s.Stats.Inc(SetFail)
+ reportWriteFailure(Set)
+ }
+ }()
+
+ // Get prevNode value
+ n, getErr := s.internalGet(nodePath)
+ if getErr != nil && getErr.ErrorCode != etcdErr.EcodeKeyNotFound {
+ err = getErr
+ return nil, err
+ }
+
+ // Set new value
+ e, err := s.internalCreate(nodePath, dir, value, false, true, expireTime, Set)
+ if err != nil {
+ return nil, err
+ }
+ e.EtcdIndex = s.CurrentIndex
+
+ // Put prevNode into event
+ if getErr == nil {
+ prev := newEvent(Get, nodePath, n.ModifiedIndex, n.CreatedIndex)
+ prev.Node.loadInternalNode(n, false, false, s.clock)
+ e.PrevNode = prev.Node
+ }
+
+ s.WatcherHub.notify(e)
+
+ return e, nil
+}
+
+// returns user-readable cause of failed comparison
+func getCompareFailCause(n *node, which int, prevValue string, prevIndex uint64) string {
+ switch which {
+ case CompareIndexNotMatch:
+ return fmt.Sprintf("[%v != %v]", prevIndex, n.ModifiedIndex)
+ case CompareValueNotMatch:
+ return fmt.Sprintf("[%v != %v]", prevValue, n.Value)
+ default:
+ return fmt.Sprintf("[%v != %v] [%v != %v]", prevValue, n.Value, prevIndex, n.ModifiedIndex)
+ }
+}
+
+func (s *store) CompareAndSwap(nodePath string, prevValue string, prevIndex uint64,
+ value string, expireTime time.Time) (*Event, error) {
+
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+
+ nodePath = path.Clean(path.Join("/", nodePath))
+ // we do not allow the user to change "/"
+ if s.readonlySet.Contains(nodePath) {
+ return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", s.CurrentIndex)
+ }
+
+ n, err := s.internalGet(nodePath)
+
+ if err != nil {
+ s.Stats.Inc(CompareAndSwapFail)
+ reportWriteFailure(CompareAndSwap)
+ return nil, err
+ }
+
+ if n.IsDir() { // can only compare and swap file
+ s.Stats.Inc(CompareAndSwapFail)
+ reportWriteFailure(CompareAndSwap)
+ return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, s.CurrentIndex)
+ }
+
+ // If both of the prevValue and prevIndex are given, we will test both of them.
+ // Command will be executed, only if both of the tests are successful.
+ if ok, which := n.Compare(prevValue, prevIndex); !ok {
+ cause := getCompareFailCause(n, which, prevValue, prevIndex)
+ s.Stats.Inc(CompareAndSwapFail)
+ reportWriteFailure(CompareAndSwap)
+ return nil, etcdErr.NewError(etcdErr.EcodeTestFailed, cause, s.CurrentIndex)
+ }
+
+ // update etcd index
+ s.CurrentIndex++
+
+ e := newEvent(CompareAndSwap, nodePath, s.CurrentIndex, n.CreatedIndex)
+ e.EtcdIndex = s.CurrentIndex
+ e.PrevNode = n.Repr(false, false, s.clock)
+ eNode := e.Node
+
+ // if test succeed, write the value
+ n.Write(value, s.CurrentIndex)
+ n.UpdateTTL(expireTime)
+
+ // copy the value for safety
+ valueCopy := value
+ eNode.Value = &valueCopy
+ eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
+
+ s.WatcherHub.notify(e)
+ s.Stats.Inc(CompareAndSwapSuccess)
+ reportWriteSuccess(CompareAndSwap)
+
+ return e, nil
+}
+
+// Delete deletes the node at the given path.
+// If the node is a directory, recursive must be true to delete it.
+func (s *store) Delete(nodePath string, dir, recursive bool) (*Event, error) {
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+
+ nodePath = path.Clean(path.Join("/", nodePath))
+ // we do not allow the user to change "/"
+ if s.readonlySet.Contains(nodePath) {
+ return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", s.CurrentIndex)
+ }
+
+ // recursive implies dir
+ if recursive == true {
+ dir = true
+ }
+
+ n, err := s.internalGet(nodePath)
+
+ if err != nil { // if the node does not exist, return error
+ s.Stats.Inc(DeleteFail)
+ reportWriteFailure(Delete)
+ return nil, err
+ }
+
+ nextIndex := s.CurrentIndex + 1
+ e := newEvent(Delete, nodePath, nextIndex, n.CreatedIndex)
+ e.EtcdIndex = nextIndex
+ e.PrevNode = n.Repr(false, false, s.clock)
+ eNode := e.Node
+
+ if n.IsDir() {
+ eNode.Dir = true
+ }
+
+ callback := func(path string) { // notify function
+ // notify the watchers with deleted set true
+ s.WatcherHub.notifyWatchers(e, path, true)
+ }
+
+ err = n.Remove(dir, recursive, callback)
+
+ if err != nil {
+ s.Stats.Inc(DeleteFail)
+ reportWriteFailure(Delete)
+ return nil, err
+ }
+
+ // update etcd index
+ s.CurrentIndex++
+
+ s.WatcherHub.notify(e)
+
+ s.Stats.Inc(DeleteSuccess)
+ reportWriteSuccess(Delete)
+
+ return e, nil
+}
+
+func (s *store) CompareAndDelete(nodePath string, prevValue string, prevIndex uint64) (*Event, error) {
+ nodePath = path.Clean(path.Join("/", nodePath))
+
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+
+ n, err := s.internalGet(nodePath)
+
+ if err != nil { // if the node does not exist, return error
+ s.Stats.Inc(CompareAndDeleteFail)
+ reportWriteFailure(CompareAndDelete)
+ return nil, err
+ }
+
+ if n.IsDir() { // can only compare and delete file
+ s.Stats.Inc(CompareAndSwapFail)
+ reportWriteFailure(CompareAndDelete)
+ return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, s.CurrentIndex)
+ }
+
+ // If both of the prevValue and prevIndex are given, we will test both of them.
+ // Command will be executed, only if both of the tests are successful.
+ if ok, which := n.Compare(prevValue, prevIndex); !ok {
+ cause := getCompareFailCause(n, which, prevValue, prevIndex)
+ s.Stats.Inc(CompareAndDeleteFail)
+ reportWriteFailure(CompareAndDelete)
+ return nil, etcdErr.NewError(etcdErr.EcodeTestFailed, cause, s.CurrentIndex)
+ }
+
+ // update etcd index
+ s.CurrentIndex++
+
+ e := newEvent(CompareAndDelete, nodePath, s.CurrentIndex, n.CreatedIndex)
+ e.EtcdIndex = s.CurrentIndex
+ e.PrevNode = n.Repr(false, false, s.clock)
+
+ callback := func(path string) { // notify function
+ // notify the watchers with deleted set true
+ s.WatcherHub.notifyWatchers(e, path, true)
+ }
+
+ err = n.Remove(false, false, callback)
+ if err != nil {
+ return nil, err
+ }
+
+ s.WatcherHub.notify(e)
+ s.Stats.Inc(CompareAndDeleteSuccess)
+ reportWriteSuccess(CompareAndDelete)
+
+ return e, nil
+}
+
+func (s *store) Watch(key string, recursive, stream bool, sinceIndex uint64) (Watcher, error) {
+ s.worldLock.RLock()
+ defer s.worldLock.RUnlock()
+
+ key = path.Clean(path.Join("/", key))
+ if sinceIndex == 0 {
+ sinceIndex = s.CurrentIndex + 1
+ }
+ // WatchHub does not know about the current index, so we need to pass it in
+ w, err := s.WatcherHub.watch(key, recursive, stream, sinceIndex, s.CurrentIndex)
+ if err != nil {
+ return nil, err
+ }
+
+ return w, nil
+}
+
+// walk walks all the nodePath and apply the walkFunc on each directory
+func (s *store) walk(nodePath string, walkFunc func(prev *node, component string) (*node, *etcdErr.Error)) (*node, *etcdErr.Error) {
+ components := strings.Split(nodePath, "/")
+
+ curr := s.Root
+ var err *etcdErr.Error
+
+ for i := 1; i < len(components); i++ {
+ if len(components[i]) == 0 { // ignore empty string
+ return curr, nil
+ }
+
+ curr, err = walkFunc(curr, components[i])
+ if err != nil {
+ return nil, err
+ }
+
+ }
+
+ return curr, nil
+}
+
+// Update updates the value/ttl of the node.
+// If the node is a file, the value and the ttl can be updated.
+// If the node is a directory, only the ttl can be updated.
+func (s *store) Update(nodePath string, newValue string, expireTime time.Time) (*Event, error) {
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+
+ nodePath = path.Clean(path.Join("/", nodePath))
+ // we do not allow the user to change "/"
+ if s.readonlySet.Contains(nodePath) {
+ return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", s.CurrentIndex)
+ }
+
+ currIndex, nextIndex := s.CurrentIndex, s.CurrentIndex+1
+
+ n, err := s.internalGet(nodePath)
+
+ if err != nil { // if the node does not exist, return error
+ s.Stats.Inc(UpdateFail)
+ reportWriteFailure(Update)
+ return nil, err
+ }
+
+ e := newEvent(Update, nodePath, nextIndex, n.CreatedIndex)
+ e.EtcdIndex = nextIndex
+ e.PrevNode = n.Repr(false, false, s.clock)
+ eNode := e.Node
+
+ if n.IsDir() && len(newValue) != 0 {
+ // if the node is a directory, we cannot update value to non-empty
+ s.Stats.Inc(UpdateFail)
+ reportWriteFailure(Update)
+ return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
+ }
+
+ n.Write(newValue, nextIndex)
+
+ if n.IsDir() {
+ eNode.Dir = true
+ } else {
+ // copy the value for safety
+ newValueCopy := newValue
+ eNode.Value = &newValueCopy
+ }
+
+ // update ttl
+ n.UpdateTTL(expireTime)
+
+ eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
+
+ s.WatcherHub.notify(e)
+
+ s.Stats.Inc(UpdateSuccess)
+ reportWriteSuccess(Update)
+
+ s.CurrentIndex = nextIndex
+
+ return e, nil
+}
+
+func (s *store) internalCreate(nodePath string, dir bool, value string, unique, replace bool,
+ expireTime time.Time, action string) (*Event, error) {
+
+ currIndex, nextIndex := s.CurrentIndex, s.CurrentIndex+1
+
+ if unique { // append unique item under the node path
+ nodePath += "/" + fmt.Sprintf("%020s", strconv.FormatUint(nextIndex, 10))
+ }
+
+ nodePath = path.Clean(path.Join("/", nodePath))
+
+ // we do not allow the user to change "/"
+ if s.readonlySet.Contains(nodePath) {
+ return nil, etcdErr.NewError(etcdErr.EcodeRootROnly, "/", currIndex)
+ }
+
+ // Assume expire times that are way in the past are
+ // This can occur when the time is serialized to JS
+ if expireTime.Before(minExpireTime) {
+ expireTime = Permanent
+ }
+
+ dirName, nodeName := path.Split(nodePath)
+
+ // walk through the nodePath, create dirs and get the last directory node
+ d, err := s.walk(dirName, s.checkDir)
+
+ if err != nil {
+ s.Stats.Inc(SetFail)
+ reportWriteFailure(action)
+ err.Index = currIndex
+ return nil, err
+ }
+
+ e := newEvent(action, nodePath, nextIndex, nextIndex)
+ eNode := e.Node
+
+ n, _ := d.GetChild(nodeName)
+
+ // force will try to replace an existing file
+ if n != nil {
+ if replace {
+ if n.IsDir() {
+ return nil, etcdErr.NewError(etcdErr.EcodeNotFile, nodePath, currIndex)
+ }
+ e.PrevNode = n.Repr(false, false, s.clock)
+
+ n.Remove(false, false, nil)
+ } else {
+ return nil, etcdErr.NewError(etcdErr.EcodeNodeExist, nodePath, currIndex)
+ }
+ }
+
+ if !dir { // create file
+ // copy the value for safety
+ valueCopy := value
+ eNode.Value = &valueCopy
+
+ n = newKV(s, nodePath, value, nextIndex, d, expireTime)
+
+ } else { // create directory
+ eNode.Dir = true
+
+ n = newDir(s, nodePath, nextIndex, d, expireTime)
+ }
+
+ // we are sure d is a directory and does not have the children with name n.Name
+ d.Add(n)
+
+ // node with TTL
+ if !n.IsPermanent() {
+ s.ttlKeyHeap.push(n)
+
+ eNode.Expiration, eNode.TTL = n.expirationAndTTL(s.clock)
+ }
+
+ s.CurrentIndex = nextIndex
+
+ return e, nil
+}
+
+// InternalGet gets the node of the given nodePath.
+func (s *store) internalGet(nodePath string) (*node, *etcdErr.Error) {
+ nodePath = path.Clean(path.Join("/", nodePath))
+
+ walkFunc := func(parent *node, name string) (*node, *etcdErr.Error) {
+
+ if !parent.IsDir() {
+ err := etcdErr.NewError(etcdErr.EcodeNotDir, parent.Path, s.CurrentIndex)
+ return nil, err
+ }
+
+ child, ok := parent.Children[name]
+ if ok {
+ return child, nil
+ }
+
+ return nil, etcdErr.NewError(etcdErr.EcodeKeyNotFound, path.Join(parent.Path, name), s.CurrentIndex)
+ }
+
+ f, err := s.walk(nodePath, walkFunc)
+
+ if err != nil {
+ return nil, err
+ }
+ return f, nil
+}
+
+// deleteExpiredKyes will delete all
+func (s *store) DeleteExpiredKeys(cutoff time.Time) {
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+
+ for {
+ node := s.ttlKeyHeap.top()
+ if node == nil || node.ExpireTime.After(cutoff) {
+ break
+ }
+
+ s.CurrentIndex++
+ e := newEvent(Expire, node.Path, s.CurrentIndex, node.CreatedIndex)
+ e.EtcdIndex = s.CurrentIndex
+ e.PrevNode = node.Repr(false, false, s.clock)
+
+ callback := func(path string) { // notify function
+ // notify the watchers with deleted set true
+ s.WatcherHub.notifyWatchers(e, path, true)
+ }
+
+ s.ttlKeyHeap.pop()
+ node.Remove(true, true, callback)
+
+ reportExpiredKey()
+ s.Stats.Inc(ExpireCount)
+
+ s.WatcherHub.notify(e)
+ }
+
+}
+
+// checkDir will check whether the component is a directory under parent node.
+// If it is a directory, this function will return the pointer to that node.
+// If it does not exist, this function will create a new directory and return the pointer to that node.
+// If it is a file, this function will return error.
+func (s *store) checkDir(parent *node, dirName string) (*node, *etcdErr.Error) {
+ node, ok := parent.Children[dirName]
+
+ if ok {
+ if node.IsDir() {
+ return node, nil
+ }
+
+ return nil, etcdErr.NewError(etcdErr.EcodeNotDir, node.Path, s.CurrentIndex)
+ }
+
+ n := newDir(s, path.Join(parent.Path, dirName), s.CurrentIndex+1, parent, Permanent)
+
+ parent.Children[dirName] = n
+
+ return n, nil
+}
+
+// Save saves the static state of the store system.
+// It will not be able to save the state of watchers.
+// It will not save the parent field of the node. Or there will
+// be cyclic dependencies issue for the json package.
+func (s *store) Save() ([]byte, error) {
+ b, err := json.Marshal(s.Clone())
+ if err != nil {
+ return nil, err
+ }
+
+ return b, nil
+}
+
+func (s *store) SaveNoCopy() ([]byte, error) {
+ b, err := json.Marshal(s)
+ if err != nil {
+ return nil, err
+ }
+
+ return b, nil
+}
+
+func (s *store) Clone() Store {
+ s.worldLock.Lock()
+
+ clonedStore := newStore()
+ clonedStore.CurrentIndex = s.CurrentIndex
+ clonedStore.Root = s.Root.Clone()
+ clonedStore.WatcherHub = s.WatcherHub.clone()
+ clonedStore.Stats = s.Stats.clone()
+ clonedStore.CurrentVersion = s.CurrentVersion
+
+ s.worldLock.Unlock()
+ return clonedStore
+}
+
+// Recovery recovers the store system from a static state
+// It needs to recover the parent field of the nodes.
+// It needs to delete the expired nodes since the saved time and also
+// needs to create monitoring go routines.
+func (s *store) Recovery(state []byte) error {
+ s.worldLock.Lock()
+ defer s.worldLock.Unlock()
+ err := json.Unmarshal(state, s)
+
+ if err != nil {
+ return err
+ }
+
+ s.ttlKeyHeap = newTtlKeyHeap()
+
+ s.Root.recoverAndclean()
+ return nil
+}
+
+func (s *store) JsonStats() []byte {
+ s.Stats.Watchers = uint64(s.WatcherHub.count)
+ return s.Stats.toJson()
+}
diff --git a/vendor/github.com/coreos/etcd/store/store_bench_test.go b/vendor/github.com/coreos/etcd/store/store_bench_test.go
new file mode 100644
index 000000000..3922116f0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/store_bench_test.go
@@ -0,0 +1,219 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "encoding/json"
+ "fmt"
+ "runtime"
+ "testing"
+)
+
+func BenchmarkStoreSet128Bytes(b *testing.B) {
+ benchStoreSet(b, 128, nil)
+}
+
+func BenchmarkStoreSet1024Bytes(b *testing.B) {
+ benchStoreSet(b, 1024, nil)
+}
+
+func BenchmarkStoreSet4096Bytes(b *testing.B) {
+ benchStoreSet(b, 4096, nil)
+}
+
+func BenchmarkStoreSetWithJson128Bytes(b *testing.B) {
+ benchStoreSet(b, 128, json.Marshal)
+}
+
+func BenchmarkStoreSetWithJson1024Bytes(b *testing.B) {
+ benchStoreSet(b, 1024, json.Marshal)
+}
+
+func BenchmarkStoreSetWithJson4096Bytes(b *testing.B) {
+ benchStoreSet(b, 4096, json.Marshal)
+}
+
+func BenchmarkStoreDelete(b *testing.B) {
+ b.StopTimer()
+
+ s := newStore()
+ kvs, _ := generateNRandomKV(b.N, 128)
+
+ memStats := new(runtime.MemStats)
+ runtime.GC()
+ runtime.ReadMemStats(memStats)
+
+ for i := 0; i < b.N; i++ {
+ _, err := s.Set(kvs[i][0], false, kvs[i][1], Permanent)
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ setMemStats := new(runtime.MemStats)
+ runtime.GC()
+ runtime.ReadMemStats(setMemStats)
+
+ b.StartTimer()
+
+ for i := range kvs {
+ s.Delete(kvs[i][0], false, false)
+ }
+
+ b.StopTimer()
+
+ // clean up
+ e, err := s.Get("/", false, false)
+ if err != nil {
+ panic(err)
+ }
+
+ for _, n := range e.Node.Nodes {
+ _, err := s.Delete(n.Key, true, true)
+ if err != nil {
+ panic(err)
+ }
+ }
+ s.WatcherHub.EventHistory = nil
+
+ deleteMemStats := new(runtime.MemStats)
+ runtime.GC()
+ runtime.ReadMemStats(deleteMemStats)
+
+ fmt.Printf("\nBefore set Alloc: %v; After set Alloc: %v, After delete Alloc: %v\n",
+ memStats.Alloc/1000, setMemStats.Alloc/1000, deleteMemStats.Alloc/1000)
+}
+
+func BenchmarkWatch(b *testing.B) {
+ b.StopTimer()
+ s := newStore()
+ kvs, _ := generateNRandomKV(b.N, 128)
+ b.StartTimer()
+
+ memStats := new(runtime.MemStats)
+ runtime.GC()
+ runtime.ReadMemStats(memStats)
+
+ for i := 0; i < b.N; i++ {
+ w, _ := s.Watch(kvs[i][0], false, false, 0)
+
+ e := newEvent("set", kvs[i][0], uint64(i+1), uint64(i+1))
+ s.WatcherHub.notify(e)
+ <-w.EventChan()
+ s.CurrentIndex++
+ }
+
+ s.WatcherHub.EventHistory = nil
+ afterMemStats := new(runtime.MemStats)
+ runtime.GC()
+ runtime.ReadMemStats(afterMemStats)
+ fmt.Printf("\nBefore Alloc: %v; After Alloc: %v\n",
+ memStats.Alloc/1000, afterMemStats.Alloc/1000)
+}
+
+func BenchmarkWatchWithSet(b *testing.B) {
+ b.StopTimer()
+ s := newStore()
+ kvs, _ := generateNRandomKV(b.N, 128)
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ w, _ := s.Watch(kvs[i][0], false, false, 0)
+
+ s.Set(kvs[i][0], false, "test", Permanent)
+ <-w.EventChan()
+ }
+}
+
+func BenchmarkWatchWithSetBatch(b *testing.B) {
+ b.StopTimer()
+ s := newStore()
+ kvs, _ := generateNRandomKV(b.N, 128)
+ b.StartTimer()
+
+ watchers := make([]Watcher, b.N)
+
+ for i := 0; i < b.N; i++ {
+ watchers[i], _ = s.Watch(kvs[i][0], false, false, 0)
+ }
+
+ for i := 0; i < b.N; i++ {
+ s.Set(kvs[i][0], false, "test", Permanent)
+ }
+
+ for i := 0; i < b.N; i++ {
+ <-watchers[i].EventChan()
+ }
+
+}
+
+func BenchmarkWatchOneKey(b *testing.B) {
+ s := newStore()
+ watchers := make([]Watcher, b.N)
+
+ for i := 0; i < b.N; i++ {
+ watchers[i], _ = s.Watch("/foo", false, false, 0)
+ }
+
+ s.Set("/foo", false, "", Permanent)
+
+ for i := 0; i < b.N; i++ {
+ <-watchers[i].EventChan()
+ }
+}
+
+func benchStoreSet(b *testing.B, valueSize int, process func(interface{}) ([]byte, error)) {
+ s := newStore()
+ b.StopTimer()
+ kvs, size := generateNRandomKV(b.N, valueSize)
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ resp, err := s.Set(kvs[i][0], false, kvs[i][1], Permanent)
+ if err != nil {
+ panic(err)
+ }
+
+ if process != nil {
+ _, err = process(resp)
+ if err != nil {
+ panic(err)
+ }
+ }
+ }
+
+ kvs = nil
+ b.StopTimer()
+ memStats := new(runtime.MemStats)
+ runtime.GC()
+ runtime.ReadMemStats(memStats)
+ fmt.Printf("\nAlloc: %vKB; Data: %vKB; Kvs: %v; Alloc/Data:%v\n",
+ memStats.Alloc/1000, size/1000, b.N, memStats.Alloc/size)
+}
+
+func generateNRandomKV(n int, valueSize int) ([][]string, uint64) {
+ var size uint64
+ kvs := make([][]string, n)
+ bytes := make([]byte, valueSize)
+
+ for i := 0; i < n; i++ {
+ kvs[i] = make([]string, 2)
+ kvs[i][0] = fmt.Sprintf("/%010d/%010d/%010d", n, n, n)
+ kvs[i][1] = string(bytes)
+ size = size + uint64(len(kvs[i][0])) + uint64(len(kvs[i][1]))
+ }
+
+ return kvs, size
+}
diff --git a/vendor/github.com/coreos/etcd/store/store_test.go b/vendor/github.com/coreos/etcd/store/store_test.go
new file mode 100644
index 000000000..42f1807fb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/store_test.go
@@ -0,0 +1,996 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "testing"
+ "time"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/jonboulle/clockwork"
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/stretchr/testify/assert"
+ etcdErr "github.com/coreos/etcd/error"
+)
+
+func TestNewStoreWithNamespaces(t *testing.T) {
+ s := newStore("/0", "/1")
+
+ _, err := s.Get("/0", false, false)
+ assert.Nil(t, err, "")
+ _, err = s.Get("/1", false, false)
+ assert.Nil(t, err, "")
+}
+
+// Ensure that the store can retrieve an existing value.
+func TestStoreGetValue(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", false, "bar", false, Permanent)
+ var eidx uint64 = 1
+ e, err := s.Get("/foo", false, false)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "get", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+}
+
+// Ensure that any TTL <= minExpireTime becomes Permanent
+func TestMinExpireTime(t *testing.T) {
+ s := newStore()
+ fc := clockwork.NewFakeClock()
+ s.clock = fc
+ // FakeClock starts at 0, so minExpireTime should be far in the future.. but just in case
+ assert.True(t, minExpireTime.After(fc.Now()), "minExpireTime should be ahead of FakeClock!")
+ s.Create("/foo", false, "Y", false, fc.Now().Add(3*time.Second))
+ fc.Advance(5 * time.Second)
+ // Ensure it hasn't expired
+ s.DeleteExpiredKeys(fc.Now())
+ var eidx uint64 = 1
+ e, err := s.Get("/foo", true, false)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "get", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.Equal(t, e.Node.TTL, 0)
+}
+
+// Ensure that the store can recrusively retrieve a directory listing.
+// Note that hidden files should not be returned.
+func TestStoreGetDirectory(t *testing.T) {
+ s := newStore()
+ fc := newFakeClock()
+ s.clock = fc
+ s.Create("/foo", true, "", false, Permanent)
+ s.Create("/foo/bar", false, "X", false, Permanent)
+ s.Create("/foo/_hidden", false, "*", false, Permanent)
+ s.Create("/foo/baz", true, "", false, Permanent)
+ s.Create("/foo/baz/bat", false, "Y", false, Permanent)
+ s.Create("/foo/baz/_hidden", false, "*", false, Permanent)
+ s.Create("/foo/baz/ttl", false, "Y", false, fc.Now().Add(time.Second*3))
+ var eidx uint64 = 7
+ e, err := s.Get("/foo", true, false)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "get", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.Equal(t, len(e.Node.Nodes), 2, "")
+ var bazNodes NodeExterns
+ for _, node := range e.Node.Nodes {
+ switch node.Key {
+ case "/foo/bar":
+ assert.Equal(t, *node.Value, "X", "")
+ assert.Equal(t, node.Dir, false, "")
+ case "/foo/baz":
+ assert.Equal(t, node.Dir, true, "")
+ assert.Equal(t, len(node.Nodes), 2, "")
+ bazNodes = node.Nodes
+ default:
+ t.Errorf("key = %s, not matched", node.Key)
+ }
+ }
+ for _, node := range bazNodes {
+ switch node.Key {
+ case "/foo/baz/bat":
+ assert.Equal(t, *node.Value, "Y", "")
+ assert.Equal(t, node.Dir, false, "")
+ case "/foo/baz/ttl":
+ assert.Equal(t, *node.Value, "Y", "")
+ assert.Equal(t, node.Dir, false, "")
+ assert.Equal(t, node.TTL, 3, "")
+ default:
+ t.Errorf("key = %s, not matched", node.Key)
+ }
+ }
+}
+
+// Ensure that the store can retrieve a directory in sorted order.
+func TestStoreGetSorted(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", true, "", false, Permanent)
+ s.Create("/foo/x", false, "0", false, Permanent)
+ s.Create("/foo/z", false, "0", false, Permanent)
+ s.Create("/foo/y", true, "", false, Permanent)
+ s.Create("/foo/y/a", false, "0", false, Permanent)
+ s.Create("/foo/y/b", false, "0", false, Permanent)
+ var eidx uint64 = 6
+ e, err := s.Get("/foo", true, true)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ var yNodes NodeExterns
+ for _, node := range e.Node.Nodes {
+ switch node.Key {
+ case "/foo/x":
+ case "/foo/y":
+ yNodes = node.Nodes
+ case "/foo/z":
+ default:
+ t.Errorf("key = %s, not matched", node.Key)
+ }
+ }
+ for _, node := range yNodes {
+ switch node.Key {
+ case "/foo/y/a":
+ case "/foo/y/b":
+ default:
+ t.Errorf("key = %s, not matched", node.Key)
+ }
+ }
+}
+
+func TestSet(t *testing.T) {
+ s := newStore()
+
+ // Set /foo=""
+ var eidx uint64 = 1
+ e, err := s.Set("/foo", false, "", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "set", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "", "")
+ assert.Nil(t, e.Node.Nodes, "")
+ assert.Nil(t, e.Node.Expiration, "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "")
+
+ // Set /foo="bar"
+ eidx = 2
+ e, err = s.Set("/foo", false, "bar", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "set", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+ assert.Nil(t, e.Node.Nodes, "")
+ assert.Nil(t, e.Node.Expiration, "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
+ // check prevNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "", "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
+ // Set /foo="baz" (for testing prevNode)
+ eidx = 3
+ e, err = s.Set("/foo", false, "baz", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "set", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ assert.Nil(t, e.Node.Nodes, "")
+ assert.Nil(t, e.Node.Expiration, "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "")
+ // check prevNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "")
+
+ // Set /dir as a directory
+ eidx = 4
+ e, err = s.Set("/dir", true, "", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "set", "")
+ assert.Equal(t, e.Node.Key, "/dir", "")
+ assert.True(t, e.Node.Dir, "")
+ assert.Nil(t, e.Node.Value)
+ assert.Nil(t, e.Node.Nodes, "")
+ assert.Nil(t, e.Node.Expiration, "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(4), "")
+}
+
+// Ensure that the store can create a new key if it doesn't already exist.
+func TestStoreCreateValue(t *testing.T) {
+ s := newStore()
+ // Create /foo=bar
+ var eidx uint64 = 1
+ e, err := s.Create("/foo", false, "bar", false, Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+ assert.Nil(t, e.Node.Nodes, "")
+ assert.Nil(t, e.Node.Expiration, "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(1), "")
+
+ // Create /empty=""
+ eidx = 2
+ e, err = s.Create("/empty", false, "", false, Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/empty", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "", "")
+ assert.Nil(t, e.Node.Nodes, "")
+ assert.Nil(t, e.Node.Expiration, "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
+
+}
+
+// Ensure that the store can create a new directory if it doesn't already exist.
+func TestStoreCreateDirectory(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ e, err := s.Create("/foo", true, "", false, Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.True(t, e.Node.Dir, "")
+}
+
+// Ensure that the store fails to create a key if it already exists.
+func TestStoreCreateFailsIfExists(t *testing.T) {
+ s := newStore()
+ // create /foo as dir
+ s.Create("/foo", true, "", false, Permanent)
+
+ // create /foo as dir again
+ e, _err := s.Create("/foo", true, "", false, Permanent)
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeNodeExist, "")
+ assert.Equal(t, err.Message, "Key already exists", "")
+ assert.Equal(t, err.Cause, "/foo", "")
+ assert.Equal(t, err.Index, uint64(1), "")
+ assert.Nil(t, e, 0, "")
+}
+
+// Ensure that the store can update a key if it already exists.
+func TestStoreUpdateValue(t *testing.T) {
+ s := newStore()
+ // create /foo=bar
+ s.Create("/foo", false, "bar", false, Permanent)
+ // update /foo="bzr"
+ var eidx uint64 = 2
+ e, err := s.Update("/foo", "baz", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "update", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(2), "")
+ // check prevNode
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+ assert.Equal(t, e.PrevNode.TTL, 0, "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
+
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+
+ // update /foo=""
+ eidx = 3
+ e, err = s.Update("/foo", "", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "update", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.False(t, e.Node.Dir, "")
+ assert.Equal(t, *e.Node.Value, "", "")
+ assert.Equal(t, e.Node.TTL, 0, "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "")
+ // check prevNode
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "baz", "")
+ assert.Equal(t, e.PrevNode.TTL, 0, "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(2), "")
+
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, *e.Node.Value, "", "")
+}
+
+// Ensure that the store cannot update a directory.
+func TestStoreUpdateFailsIfDirectory(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", true, "", false, Permanent)
+ e, _err := s.Update("/foo", "baz", Permanent)
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "")
+ assert.Equal(t, err.Message, "Not a file", "")
+ assert.Equal(t, err.Cause, "/foo", "")
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store can update the TTL on a value.
+func TestStoreUpdateValueTTL(t *testing.T) {
+ s := newStore()
+ fc := newFakeClock()
+ s.clock = fc
+
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, Permanent)
+ _, err := s.Update("/foo", "baz", fc.Now().Add(500*time.Millisecond))
+ e, _ := s.Get("/foo", false, false)
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+ e, err = s.Get("/foo", false, false)
+ assert.Nil(t, e, "")
+ assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
+}
+
+// Ensure that the store can update the TTL on a directory.
+func TestStoreUpdateDirTTL(t *testing.T) {
+ s := newStore()
+ fc := newFakeClock()
+ s.clock = fc
+
+ var eidx uint64 = 3
+ s.Create("/foo", true, "", false, Permanent)
+ s.Create("/foo/bar", false, "baz", false, Permanent)
+ e, err := s.Update("/foo", "", fc.Now().Add(500*time.Millisecond))
+ assert.Equal(t, e.Node.Dir, true, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ e, _ = s.Get("/foo/bar", false, false)
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+ e, err = s.Get("/foo/bar", false, false)
+ assert.Nil(t, e, "")
+ assert.Equal(t, err.(*etcdErr.Error).ErrorCode, etcdErr.EcodeKeyNotFound, "")
+}
+
+// Ensure that the store can delete a value.
+func TestStoreDeleteValue(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, err := s.Delete("/foo", false, false)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "delete", "")
+ // check prevNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+}
+
+// Ensure that the store can delete a directory if recursive is specified.
+func TestStoreDeleteDiretory(t *testing.T) {
+ s := newStore()
+ // create directory /foo
+ var eidx uint64 = 2
+ s.Create("/foo", true, "", false, Permanent)
+ // delete /foo with dir = true and recursive = false
+ // this should succeed, since the directory is empty
+ e, err := s.Delete("/foo", true, false)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "delete", "")
+ // check prevNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, e.PrevNode.Dir, true, "")
+
+ // create directory /foo and directory /foo/bar
+ s.Create("/foo/bar", true, "", false, Permanent)
+ // delete /foo with dir = true and recursive = false
+ // this should fail, since the directory is not empty
+ _, err = s.Delete("/foo", true, false)
+ assert.NotNil(t, err, "")
+
+ // delete /foo with dir=false and recursive = true
+ // this should succeed, since recursive implies dir=true
+ // and recursively delete should be able to delete all
+ // items under the given directory
+ e, err = s.Delete("/foo", false, true)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.Action, "delete", "")
+
+}
+
+// Ensure that the store cannot delete a directory if both of recursive
+// and dir are not specified.
+func TestStoreDeleteDiretoryFailsIfNonRecursiveAndDir(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", true, "", false, Permanent)
+ e, _err := s.Delete("/foo", false, false)
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "")
+ assert.Equal(t, err.Message, "Not a file", "")
+ assert.Nil(t, e, "")
+}
+
+func TestRootRdOnly(t *testing.T) {
+ s := newStore("/0")
+
+ for _, tt := range []string{"/", "/0"} {
+ _, err := s.Set(tt, true, "", Permanent)
+ assert.NotNil(t, err, "")
+
+ _, err = s.Delete(tt, true, true)
+ assert.NotNil(t, err, "")
+
+ _, err = s.Create(tt, true, "", false, Permanent)
+ assert.NotNil(t, err, "")
+
+ _, err = s.Update(tt, "", Permanent)
+ assert.NotNil(t, err, "")
+
+ _, err = s.CompareAndSwap(tt, "", 0, "", Permanent)
+ assert.NotNil(t, err, "")
+ }
+}
+
+func TestStoreCompareAndDeletePrevValue(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, err := s.CompareAndDelete("/foo", "bar", 0)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "compareAndDelete", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+
+ // check pervNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
+ assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
+}
+
+func TestStoreCompareAndDeletePrevValueFailsIfNotMatch(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, _err := s.CompareAndDelete("/foo", "baz", 0)
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
+ assert.Equal(t, err.Message, "Compare failed", "")
+ assert.Nil(t, e, "")
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+}
+
+func TestStoreCompareAndDeletePrevIndex(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, err := s.CompareAndDelete("/foo", "", 1)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "compareAndDelete", "")
+ // check pervNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
+ assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
+}
+
+func TestStoreCompareAndDeletePrevIndexFailsIfNotMatch(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, _err := s.CompareAndDelete("/foo", "", 100)
+ assert.NotNil(t, _err, "")
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
+ assert.Equal(t, err.Message, "Compare failed", "")
+ assert.Nil(t, e, "")
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+}
+
+// Ensure that the store cannot delete a directory.
+func TestStoreCompareAndDeleteDiretoryFail(t *testing.T) {
+ s := newStore()
+ s.Create("/foo", true, "", false, Permanent)
+ _, _err := s.CompareAndDelete("/foo", "", 0)
+ assert.NotNil(t, _err, "")
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeNotFile, "")
+}
+
+// Ensure that the store can conditionally update a key if it has a previous value.
+func TestStoreCompareAndSwapPrevValue(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, err := s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "compareAndSwap", "")
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ // check pervNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
+ assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
+
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, *e.Node.Value, "baz", "")
+}
+
+// Ensure that the store cannot conditionally update a key if it has the wrong previous value.
+func TestStoreCompareAndSwapPrevValueFailsIfNotMatch(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, _err := s.CompareAndSwap("/foo", "wrong_value", 0, "baz", Permanent)
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
+ assert.Equal(t, err.Message, "Compare failed", "")
+ assert.Nil(t, e, "")
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, *e.Node.Value, "bar", "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+}
+
+// Ensure that the store can conditionally update a key if it has a previous index.
+func TestStoreCompareAndSwapPrevIndex(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, err := s.CompareAndSwap("/foo", "", 1, "baz", Permanent)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "compareAndSwap", "")
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ // check prevNode
+ assert.NotNil(t, e.PrevNode, "")
+ assert.Equal(t, e.PrevNode.Key, "/foo", "")
+ assert.Equal(t, *e.PrevNode.Value, "bar", "")
+ assert.Equal(t, e.PrevNode.ModifiedIndex, uint64(1), "")
+ assert.Equal(t, e.PrevNode.CreatedIndex, uint64(1), "")
+
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+}
+
+// Ensure that the store cannot conditionally update a key if it has the wrong previous index.
+func TestStoreCompareAndSwapPrevIndexFailsIfNotMatch(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ e, _err := s.CompareAndSwap("/foo", "", 100, "baz", Permanent)
+ err := _err.(*etcdErr.Error)
+ assert.Equal(t, err.ErrorCode, etcdErr.EcodeTestFailed, "")
+ assert.Equal(t, err.Message, "Compare failed", "")
+ assert.Nil(t, e, "")
+ e, _ = s.Get("/foo", false, false)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+}
+
+// Ensure that the store can watch for key creation.
+func TestStoreWatchCreate(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 0
+ w, _ := s.Watch("/foo", false, false, 0)
+ c := w.EventChan()
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ s.Create("/foo", false, "bar", false, Permanent)
+ eidx = 1
+ e := nbselect(c)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ e = nbselect(c)
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store can watch for recursive key creation.
+func TestStoreWatchRecursiveCreate(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 0
+ w, _ := s.Watch("/foo", true, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 1
+ s.Create("/foo/bar", false, "baz", false, Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/foo/bar", "")
+}
+
+// Ensure that the store can watch for key updates.
+func TestStoreWatchUpdate(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ w, _ := s.Watch("/foo", false, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 2
+ s.Update("/foo", "baz", Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "update", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+}
+
+// Ensure that the store can watch for recursive key updates.
+func TestStoreWatchRecursiveUpdate(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo/bar", false, "baz", false, Permanent)
+ w, _ := s.Watch("/foo", true, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 2
+ s.Update("/foo/bar", "baz", Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "update", "")
+ assert.Equal(t, e.Node.Key, "/foo/bar", "")
+}
+
+// Ensure that the store can watch for key deletions.
+func TestStoreWatchDelete(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ w, _ := s.Watch("/foo", false, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 2
+ s.Delete("/foo", false, false)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "delete", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+}
+
+// Ensure that the store can watch for recursive key deletions.
+func TestStoreWatchRecursiveDelete(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo/bar", false, "baz", false, Permanent)
+ w, _ := s.Watch("/foo", true, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 2
+ s.Delete("/foo/bar", false, false)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "delete", "")
+ assert.Equal(t, e.Node.Key, "/foo/bar", "")
+}
+
+// Ensure that the store can watch for CAS updates.
+func TestStoreWatchCompareAndSwap(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo", false, "bar", false, Permanent)
+ w, _ := s.Watch("/foo", false, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 2
+ s.CompareAndSwap("/foo", "bar", 0, "baz", Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "compareAndSwap", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+}
+
+// Ensure that the store can watch for recursive CAS updates.
+func TestStoreWatchRecursiveCompareAndSwap(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ s.Create("/foo/bar", false, "baz", false, Permanent)
+ w, _ := s.Watch("/foo", true, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ eidx = 2
+ s.CompareAndSwap("/foo/bar", "baz", 0, "bat", Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "compareAndSwap", "")
+ assert.Equal(t, e.Node.Key, "/foo/bar", "")
+}
+
+// Ensure that the store can watch for key expiration.
+func TestStoreWatchExpire(t *testing.T) {
+ s := newStore()
+ fc := newFakeClock()
+ s.clock = fc
+
+ var eidx uint64 = 2
+ s.Create("/foo", false, "bar", false, fc.Now().Add(500*time.Millisecond))
+ s.Create("/foofoo", false, "barbarbar", false, fc.Now().Add(500*time.Millisecond))
+
+ w, _ := s.Watch("/", true, false, 0)
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ c := w.EventChan()
+ e := nbselect(c)
+ assert.Nil(t, e, "")
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+ eidx = 3
+ e = nbselect(c)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "expire", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ w, _ = s.Watch("/", true, false, 4)
+ eidx = 4
+ assert.Equal(t, w.StartIndex(), eidx, "")
+ e = nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "expire", "")
+ assert.Equal(t, e.Node.Key, "/foofoo", "")
+}
+
+// Ensure that the store can watch in streaming mode.
+func TestStoreWatchStream(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ w, _ := s.Watch("/foo", false, true, 0)
+ // first modification
+ s.Create("/foo", false, "bar", false, Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+ // second modification
+ eidx = 2
+ s.Update("/foo", "baz", Permanent)
+ e = nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "update", "")
+ assert.Equal(t, e.Node.Key, "/foo", "")
+ assert.Equal(t, *e.Node.Value, "baz", "")
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store can recover from a previously saved state.
+func TestStoreRecover(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 4
+ s.Create("/foo", true, "", false, Permanent)
+ s.Create("/foo/x", false, "bar", false, Permanent)
+ s.Update("/foo/x", "barbar", Permanent)
+ s.Create("/foo/y", false, "baz", false, Permanent)
+ b, err := s.Save()
+
+ s2 := newStore()
+ s2.Recovery(b)
+
+ e, err := s.Get("/foo/x", false, false)
+ assert.Equal(t, e.Node.CreatedIndex, uint64(2), "")
+ assert.Equal(t, e.Node.ModifiedIndex, uint64(3), "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Nil(t, err, "")
+ assert.Equal(t, *e.Node.Value, "barbar", "")
+
+ e, err = s.Get("/foo/y", false, false)
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Nil(t, err, "")
+ assert.Equal(t, *e.Node.Value, "baz", "")
+}
+
+// Ensure that the store can recover from a previously saved state that includes an expiring key.
+func TestStoreRecoverWithExpiration(t *testing.T) {
+ s := newStore()
+ s.clock = newFakeClock()
+
+ fc := newFakeClock()
+
+ var eidx uint64 = 4
+ s.Create("/foo", true, "", false, Permanent)
+ s.Create("/foo/x", false, "bar", false, Permanent)
+ s.Create("/foo/y", false, "baz", false, fc.Now().Add(5*time.Millisecond))
+ b, err := s.Save()
+
+ time.Sleep(10 * time.Millisecond)
+
+ s2 := newStore()
+ s2.clock = fc
+
+ s2.Recovery(b)
+
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+
+ e, err := s.Get("/foo/x", false, false)
+ assert.Nil(t, err, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, *e.Node.Value, "bar", "")
+
+ e, err = s.Get("/foo/y", false, false)
+ assert.NotNil(t, err, "")
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store can watch for hidden keys as long as it's an exact path match.
+func TestStoreWatchCreateWithHiddenKey(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ w, _ := s.Watch("/_foo", false, false, 0)
+ s.Create("/_foo", false, "bar", false, Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/_foo", "")
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store doesn't see hidden key creates without an exact path match in recursive mode.
+func TestStoreWatchRecursiveCreateWithHiddenKey(t *testing.T) {
+ s := newStore()
+ w, _ := s.Watch("/foo", true, false, 0)
+ s.Create("/foo/_bar", false, "baz", false, Permanent)
+ e := nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+ w, _ = s.Watch("/foo", true, false, 0)
+ s.Create("/foo/_baz", true, "", false, Permanent)
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+ s.Create("/foo/_baz/quux", false, "quux", false, Permanent)
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store doesn't see hidden key updates.
+func TestStoreWatchUpdateWithHiddenKey(t *testing.T) {
+ s := newStore()
+ s.Create("/_foo", false, "bar", false, Permanent)
+ w, _ := s.Watch("/_foo", false, false, 0)
+ s.Update("/_foo", "baz", Permanent)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.Action, "update", "")
+ assert.Equal(t, e.Node.Key, "/_foo", "")
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store doesn't see hidden key updates without an exact path match in recursive mode.
+func TestStoreWatchRecursiveUpdateWithHiddenKey(t *testing.T) {
+ s := newStore()
+ s.Create("/foo/_bar", false, "baz", false, Permanent)
+ w, _ := s.Watch("/foo", true, false, 0)
+ s.Update("/foo/_bar", "baz", Permanent)
+ e := nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store can watch for key deletions.
+func TestStoreWatchDeleteWithHiddenKey(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 2
+ s.Create("/_foo", false, "bar", false, Permanent)
+ w, _ := s.Watch("/_foo", false, false, 0)
+ s.Delete("/_foo", false, false)
+ e := nbselect(w.EventChan())
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "delete", "")
+ assert.Equal(t, e.Node.Key, "/_foo", "")
+ e = nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store doesn't see hidden key deletes without an exact path match in recursive mode.
+func TestStoreWatchRecursiveDeleteWithHiddenKey(t *testing.T) {
+ s := newStore()
+ s.Create("/foo/_bar", false, "baz", false, Permanent)
+ w, _ := s.Watch("/foo", true, false, 0)
+ s.Delete("/foo/_bar", false, false)
+ e := nbselect(w.EventChan())
+ assert.Nil(t, e, "")
+}
+
+// Ensure that the store doesn't see expirations of hidden keys.
+func TestStoreWatchExpireWithHiddenKey(t *testing.T) {
+ s := newStore()
+ fc := newFakeClock()
+ s.clock = fc
+
+ s.Create("/_foo", false, "bar", false, fc.Now().Add(500*time.Millisecond))
+ s.Create("/foofoo", false, "barbarbar", false, fc.Now().Add(1000*time.Millisecond))
+
+ w, _ := s.Watch("/", true, false, 0)
+ c := w.EventChan()
+ e := nbselect(c)
+ assert.Nil(t, e, "")
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+ e = nbselect(c)
+ assert.Nil(t, e, "")
+ fc.Advance(600 * time.Millisecond)
+ s.DeleteExpiredKeys(fc.Now())
+ e = nbselect(c)
+ assert.Equal(t, e.Action, "expire", "")
+ assert.Equal(t, e.Node.Key, "/foofoo", "")
+}
+
+// Ensure that the store does see hidden key creates if watching deeper than a hidden key in recursive mode.
+func TestStoreWatchRecursiveCreateDeeperThanHiddenKey(t *testing.T) {
+ s := newStore()
+ var eidx uint64 = 1
+ w, _ := s.Watch("/_foo/bar", true, false, 0)
+ s.Create("/_foo/bar/baz", false, "baz", false, Permanent)
+
+ e := nbselect(w.EventChan())
+ assert.NotNil(t, e, "")
+ assert.Equal(t, e.EtcdIndex, eidx, "")
+ assert.Equal(t, e.Action, "create", "")
+ assert.Equal(t, e.Node.Key, "/_foo/bar/baz", "")
+}
+
+// Ensure that slow consumers are handled properly.
+//
+// Since Watcher.EventChan() has a buffer of size 1 we can only queue 1
+// event per watcher. If the consumer cannot consume the event on time and
+// another event arrives, the channel is closed and event is discarded.
+// This test ensures that after closing the channel, the store can continue
+// to operate correctly.
+func TestStoreWatchSlowConsumer(t *testing.T) {
+ s := newStore()
+ s.Watch("/foo", true, true, 0) // stream must be true
+ s.Set("/foo", false, "1", Permanent) // ok
+ s.Set("/foo", false, "2", Permanent) // ok
+ s.Set("/foo", false, "3", Permanent) // must not panic
+}
+
+// Performs a non-blocking select on an event channel.
+func nbselect(c <-chan *Event) *Event {
+ select {
+ case e := <-c:
+ return e
+ default:
+ return nil
+ }
+}
+
+// newFakeClock creates a new FakeClock that has been advanced to at least minExpireTime
+func newFakeClock() clockwork.FakeClock {
+ fc := clockwork.NewFakeClock()
+ for minExpireTime.After(fc.Now()) {
+ fc.Advance((0x1 << 62) * time.Nanosecond)
+ }
+ return fc
+}
diff --git a/vendor/github.com/coreos/etcd/store/ttl_key_heap.go b/vendor/github.com/coreos/etcd/store/ttl_key_heap.go
new file mode 100644
index 000000000..d2dffeccb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/ttl_key_heap.go
@@ -0,0 +1,99 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "container/heap"
+)
+
+// An TTLKeyHeap is a min-heap of TTLKeys order by expiration time
+type ttlKeyHeap struct {
+ array []*node
+ keyMap map[*node]int
+}
+
+func newTtlKeyHeap() *ttlKeyHeap {
+ h := &ttlKeyHeap{keyMap: make(map[*node]int)}
+ heap.Init(h)
+ return h
+}
+
+func (h ttlKeyHeap) Len() int {
+ return len(h.array)
+}
+
+func (h ttlKeyHeap) Less(i, j int) bool {
+ return h.array[i].ExpireTime.Before(h.array[j].ExpireTime)
+}
+
+func (h ttlKeyHeap) Swap(i, j int) {
+ // swap node
+ h.array[i], h.array[j] = h.array[j], h.array[i]
+
+ // update map
+ h.keyMap[h.array[i]] = i
+ h.keyMap[h.array[j]] = j
+}
+
+func (h *ttlKeyHeap) Push(x interface{}) {
+ n, _ := x.(*node)
+ h.keyMap[n] = len(h.array)
+ h.array = append(h.array, n)
+}
+
+func (h *ttlKeyHeap) Pop() interface{} {
+ old := h.array
+ n := len(old)
+ x := old[n-1]
+ // Set slice element to nil, so GC can recycle the node.
+ // This is due to golang GC doesn't support partial recycling:
+ // https://github.com/golang/go/issues/9618
+ old[n-1] = nil
+ h.array = old[0 : n-1]
+ delete(h.keyMap, x)
+ return x
+}
+
+func (h *ttlKeyHeap) top() *node {
+ if h.Len() != 0 {
+ return h.array[0]
+ }
+ return nil
+}
+
+func (h *ttlKeyHeap) pop() *node {
+ x := heap.Pop(h)
+ n, _ := x.(*node)
+ return n
+}
+
+func (h *ttlKeyHeap) push(x interface{}) {
+ heap.Push(h, x)
+}
+
+func (h *ttlKeyHeap) update(n *node) {
+ index, ok := h.keyMap[n]
+ if ok {
+ heap.Remove(h, index)
+ heap.Push(h, n)
+ }
+}
+
+func (h *ttlKeyHeap) remove(n *node) {
+ index, ok := h.keyMap[n]
+ if ok {
+ heap.Remove(h, index)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/watcher.go b/vendor/github.com/coreos/etcd/store/watcher.go
new file mode 100644
index 000000000..3c9f70ec4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/watcher.go
@@ -0,0 +1,87 @@
+// Copyright 2015 CoreOS, 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 store
+
+type Watcher interface {
+ EventChan() chan *Event
+ StartIndex() uint64 // The EtcdIndex at which the Watcher was created
+ Remove()
+}
+
+type watcher struct {
+ eventChan chan *Event
+ stream bool
+ recursive bool
+ sinceIndex uint64
+ startIndex uint64
+ hub *watcherHub
+ removed bool
+ remove func()
+}
+
+func (w *watcher) EventChan() chan *Event {
+ return w.eventChan
+}
+
+func (w *watcher) StartIndex() uint64 {
+ return w.startIndex
+}
+
+// notify function notifies the watcher. If the watcher interests in the given path,
+// the function will return true.
+func (w *watcher) notify(e *Event, originalPath bool, deleted bool) bool {
+ // watcher is interested the path in three cases and under one condition
+ // the condition is that the event happens after the watcher's sinceIndex
+
+ // 1. the path at which the event happens is the path the watcher is watching at.
+ // For example if the watcher is watching at "/foo" and the event happens at "/foo",
+ // the watcher must be interested in that event.
+
+ // 2. the watcher is a recursive watcher, it interests in the event happens after
+ // its watching path. For example if watcher A watches at "/foo" and it is a recursive
+ // one, it will interest in the event happens at "/foo/bar".
+
+ // 3. when we delete a directory, we need to force notify all the watchers who watches
+ // at the file we need to delete.
+ // For example a watcher is watching at "/foo/bar". And we deletes "/foo". The watcher
+ // should get notified even if "/foo" is not the path it is watching.
+ if (w.recursive || originalPath || deleted) && e.Index() >= w.sinceIndex {
+ // We cannot block here if the eventChan capacity is full, otherwise
+ // etcd will hang. eventChan capacity is full when the rate of
+ // notifications are higher than our send rate.
+ // If this happens, we close the channel.
+ select {
+ case w.eventChan <- e:
+ default:
+ // We have missed a notification. Remove the watcher.
+ // Removing the watcher also closes the eventChan.
+ w.remove()
+ }
+ return true
+ }
+ return false
+}
+
+// Remove removes the watcher from watcherHub
+// The actual remove function is guaranteed to only be executed once
+func (w *watcher) Remove() {
+ w.hub.mutex.Lock()
+ defer w.hub.mutex.Unlock()
+
+ close(w.eventChan)
+ if w.remove != nil {
+ w.remove()
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/watcher_hub.go b/vendor/github.com/coreos/etcd/store/watcher_hub.go
new file mode 100644
index 000000000..d573eb331
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/watcher_hub.go
@@ -0,0 +1,195 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "container/list"
+ "path"
+ "strings"
+ "sync"
+ "sync/atomic"
+
+ etcdErr "github.com/coreos/etcd/error"
+)
+
+// A watcherHub contains all subscribed watchers
+// watchers is a map with watched path as key and watcher as value
+// EventHistory keeps the old events for watcherHub. It is used to help
+// watcher to get a continuous event history. Or a watcher might miss the
+// event happens between the end of the first watch command and the start
+// of the second command.
+type watcherHub struct {
+ // count must be the first element to keep 64-bit alignment for atomic
+ // access
+
+ count int64 // current number of watchers.
+
+ mutex sync.Mutex
+ watchers map[string]*list.List
+ EventHistory *EventHistory
+}
+
+// newWatchHub creates a watchHub. The capacity determines how many events we will
+// keep in the eventHistory.
+// Typically, we only need to keep a small size of history[smaller than 20K].
+// Ideally, it should smaller than 20K/s[max throughput] * 2 * 50ms[RTT] = 2000
+func newWatchHub(capacity int) *watcherHub {
+ return &watcherHub{
+ watchers: make(map[string]*list.List),
+ EventHistory: newEventHistory(capacity),
+ }
+}
+
+// Watch function returns a Watcher.
+// If recursive is true, the first change after index under key will be sent to the event channel of the watcher.
+// If recursive is false, the first change after index at key will be sent to the event channel of the watcher.
+// If index is zero, watch will start from the current index + 1.
+func (wh *watcherHub) watch(key string, recursive, stream bool, index, storeIndex uint64) (Watcher, *etcdErr.Error) {
+ reportWatchRequest()
+ event, err := wh.EventHistory.scan(key, recursive, index)
+
+ if err != nil {
+ err.Index = storeIndex
+ return nil, err
+ }
+
+ w := &watcher{
+ eventChan: make(chan *Event, 100), // use a buffered channel
+ recursive: recursive,
+ stream: stream,
+ sinceIndex: index,
+ startIndex: storeIndex,
+ hub: wh,
+ }
+
+ wh.mutex.Lock()
+ defer wh.mutex.Unlock()
+ // If the event exists in the known history, append the EtcdIndex and return immediately
+ if event != nil {
+ event.EtcdIndex = storeIndex
+ w.eventChan <- event
+ return w, nil
+ }
+
+ l, ok := wh.watchers[key]
+
+ var elem *list.Element
+
+ if ok { // add the new watcher to the back of the list
+ elem = l.PushBack(w)
+ } else { // create a new list and add the new watcher
+ l = list.New()
+ elem = l.PushBack(w)
+ wh.watchers[key] = l
+ }
+
+ w.remove = func() {
+ if w.removed { // avoid removing it twice
+ return
+ }
+ w.removed = true
+ l.Remove(elem)
+ atomic.AddInt64(&wh.count, -1)
+ reportWatcherRemoved()
+ if l.Len() == 0 {
+ delete(wh.watchers, key)
+ }
+ }
+
+ atomic.AddInt64(&wh.count, 1)
+ reportWatcherAdded()
+
+ return w, nil
+}
+
+// notify function accepts an event and notify to the watchers.
+func (wh *watcherHub) notify(e *Event) {
+ e = wh.EventHistory.addEvent(e) // add event into the eventHistory
+
+ segments := strings.Split(e.Node.Key, "/")
+
+ currPath := "/"
+
+ // walk through all the segments of the path and notify the watchers
+ // if the path is "/foo/bar", it will notify watchers with path "/",
+ // "/foo" and "/foo/bar"
+
+ for _, segment := range segments {
+ currPath = path.Join(currPath, segment)
+ // notify the watchers who interests in the changes of current path
+ wh.notifyWatchers(e, currPath, false)
+ }
+}
+
+func (wh *watcherHub) notifyWatchers(e *Event, nodePath string, deleted bool) {
+ wh.mutex.Lock()
+ defer wh.mutex.Unlock()
+
+ l, ok := wh.watchers[nodePath]
+ if ok {
+ curr := l.Front()
+
+ for curr != nil {
+ next := curr.Next() // save reference to the next one in the list
+
+ w, _ := curr.Value.(*watcher)
+
+ originalPath := (e.Node.Key == nodePath)
+ if (originalPath || !isHidden(nodePath, e.Node.Key)) && w.notify(e, originalPath, deleted) {
+ if !w.stream { // do not remove the stream watcher
+ // if we successfully notify a watcher
+ // we need to remove the watcher from the list
+ // and decrease the counter
+ w.removed = true
+ l.Remove(curr)
+ atomic.AddInt64(&wh.count, -1)
+ reportWatcherRemoved()
+ }
+ }
+
+ curr = next // update current to the next element in the list
+ }
+
+ if l.Len() == 0 {
+ // if we have notified all watcher in the list
+ // we can delete the list
+ delete(wh.watchers, nodePath)
+ }
+ }
+}
+
+// clone function clones the watcherHub and return the cloned one.
+// only clone the static content. do not clone the current watchers.
+func (wh *watcherHub) clone() *watcherHub {
+ clonedHistory := wh.EventHistory.clone()
+
+ return &watcherHub{
+ EventHistory: clonedHistory,
+ }
+}
+
+// isHidden checks to see if key path is considered hidden to watch path i.e. the
+// last element is hidden or it's within a hidden directory
+func isHidden(watchPath, keyPath string) bool {
+ // When deleting a directory, watchPath might be deeper than the actual keyPath
+ // For example, when deleting /foo we also need to notify watchers on /foo/bar.
+ if len(watchPath) > len(keyPath) {
+ return false
+ }
+ // if watch path is just a "/", after path will start without "/"
+ // add a "/" to deal with the special case when watchPath is "/"
+ afterPath := path.Clean("/" + keyPath[len(watchPath):])
+ return strings.Contains(afterPath, "/_")
+}
diff --git a/vendor/github.com/coreos/etcd/store/watcher_hub_test.go b/vendor/github.com/coreos/etcd/store/watcher_hub_test.go
new file mode 100644
index 000000000..7af749a1c
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/watcher_hub_test.go
@@ -0,0 +1,66 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "testing"
+)
+
+// TestIsHidden tests isHidden functions.
+func TestIsHidden(t *testing.T) {
+ // watch at "/"
+ // key is "/_foo", hidden to "/"
+ // expected: hidden = true
+ watch := "/"
+ key := "/_foo"
+ hidden := isHidden(watch, key)
+ if !hidden {
+ t.Fatalf("%v should be hidden to %v\n", key, watch)
+ }
+
+ // watch at "/_foo"
+ // key is "/_foo", not hidden to "/_foo"
+ // expected: hidden = false
+ watch = "/_foo"
+ hidden = isHidden(watch, key)
+ if hidden {
+ t.Fatalf("%v should not be hidden to %v\n", key, watch)
+ }
+
+ // watch at "/_foo/"
+ // key is "/_foo/foo", not hidden to "/_foo"
+ key = "/_foo/foo"
+ hidden = isHidden(watch, key)
+ if hidden {
+ t.Fatalf("%v should not be hidden to %v\n", key, watch)
+ }
+
+ // watch at "/_foo/"
+ // key is "/_foo/_foo", hidden to "/_foo"
+ key = "/_foo/_foo"
+ hidden = isHidden(watch, key)
+ if !hidden {
+ t.Fatalf("%v should be hidden to %v\n", key, watch)
+ }
+
+ // watch at "/_foo/foo"
+ // key is "/_foo"
+ watch = "_foo/foo"
+ key = "/_foo/"
+ hidden = isHidden(watch, key)
+ if hidden {
+ t.Fatalf("%v should not be hidden to %v\n", key, watch)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/store/watcher_test.go b/vendor/github.com/coreos/etcd/store/watcher_test.go
new file mode 100644
index 000000000..af9a9d160
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/store/watcher_test.go
@@ -0,0 +1,92 @@
+// Copyright 2015 CoreOS, 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 store
+
+import (
+ "testing"
+)
+
+func TestWatcher(t *testing.T) {
+ s := newStore()
+ wh := s.WatcherHub
+ w, err := wh.watch("/foo", true, false, 1, 1)
+ if err != nil {
+ t.Fatalf("%v", err)
+ }
+ c := w.EventChan()
+
+ select {
+ case <-c:
+ t.Fatal("should not receive from channel before send the event")
+ default:
+ // do nothing
+ }
+
+ e := newEvent(Create, "/foo/bar", 1, 1)
+
+ wh.notify(e)
+
+ re := <-c
+
+ if e != re {
+ t.Fatal("recv != send")
+ }
+
+ w, _ = wh.watch("/foo", false, false, 2, 1)
+ c = w.EventChan()
+
+ e = newEvent(Create, "/foo/bar", 2, 2)
+
+ wh.notify(e)
+
+ select {
+ case re = <-c:
+ t.Fatal("should not receive from channel if not recursive ", re)
+ default:
+ // do nothing
+ }
+
+ e = newEvent(Create, "/foo", 3, 3)
+
+ wh.notify(e)
+
+ re = <-c
+
+ if e != re {
+ t.Fatal("recv != send")
+ }
+
+ // ensure we are doing exact matching rather than prefix matching
+ w, _ = wh.watch("/fo", true, false, 1, 1)
+ c = w.EventChan()
+
+ select {
+ case re = <-c:
+ t.Fatal("should not receive from channel:", re)
+ default:
+ // do nothing
+ }
+
+ e = newEvent(Create, "/fo/bar", 3, 3)
+
+ wh.notify(e)
+
+ re = <-c
+
+ if e != re {
+ t.Fatal("recv != send")
+ }
+
+}
diff --git a/vendor/github.com/coreos/etcd/version/version.go b/vendor/github.com/coreos/etcd/version/version.go
new file mode 100644
index 000000000..f3859b6c2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/version/version.go
@@ -0,0 +1,89 @@
+// Copyright 2015 CoreOS, 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 version
+
+import (
+ "fmt"
+ "os"
+ "path"
+ "strings"
+
+ "github.com/coreos/etcd/pkg/fileutil"
+ "github.com/coreos/etcd/pkg/types"
+)
+
+var (
+ // MinClusterVersion is the min cluster version this etcd binary is compatible with.
+ MinClusterVersion = "2.1.0"
+ Version = "2.2.0+git"
+
+ // Git SHA Value will be set during build
+ GitSHA = "Not provided (use ./build instead of go build)"
+)
+
+// WalVersion is an enum for versions of etcd logs.
+type DataDirVersion string
+
+const (
+ DataDirUnknown DataDirVersion = "Unknown WAL"
+ DataDir2_0 DataDirVersion = "2.0.0"
+ DataDir2_0Proxy DataDirVersion = "2.0 proxy"
+ DataDir2_0_1 DataDirVersion = "2.0.1"
+)
+
+type Versions struct {
+ Server string `json:"etcdserver"`
+ Cluster string `json:"etcdcluster"`
+ // TODO: raft state machine version
+}
+
+func DetectDataDir(dirpath string) (DataDirVersion, error) {
+ names, err := fileutil.ReadDir(dirpath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ err = nil
+ }
+ // Error reading the directory
+ return DataDirUnknown, err
+ }
+ nameSet := types.NewUnsafeSet(names...)
+ if nameSet.Contains("member") {
+ ver, err := DetectDataDir(path.Join(dirpath, "member"))
+ if ver == DataDir2_0 {
+ return DataDir2_0_1, nil
+ }
+ return ver, err
+ }
+ if nameSet.ContainsAll([]string{"snap", "wal"}) {
+ // .../wal cannot be empty to exist.
+ walnames, err := fileutil.ReadDir(path.Join(dirpath, "wal"))
+ if err == nil && len(walnames) > 0 {
+ return DataDir2_0, nil
+ }
+ }
+ if nameSet.ContainsAll([]string{"proxy"}) {
+ return DataDir2_0Proxy, nil
+ }
+ return DataDirUnknown, nil
+}
+
+// Cluster only keeps the major.minor.
+func Cluster(v string) string {
+ vs := strings.Split(v, ".")
+ if len(vs) <= 2 {
+ return v
+ }
+ return fmt.Sprintf("%s.%s", vs[0], vs[1])
+}
diff --git a/vendor/github.com/coreos/etcd/version/version_test.go b/vendor/github.com/coreos/etcd/version/version_test.go
new file mode 100644
index 000000000..d99dc98a4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/version/version_test.go
@@ -0,0 +1,67 @@
+// Copyright 2015 CoreOS, 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 version
+
+import (
+ "io/ioutil"
+ "os"
+ "path"
+ "strings"
+ "testing"
+)
+
+func TestDetectDataDir(t *testing.T) {
+ tests := []struct {
+ names []string
+ wver DataDirVersion
+ }{
+ {[]string{"member/", "member/wal/", "member/wal/1", "member/snap/"}, DataDir2_0_1},
+ {[]string{"snap/", "wal/", "wal/1"}, DataDir2_0},
+ {[]string{"weird"}, DataDirUnknown},
+ {[]string{"snap/", "wal/"}, DataDirUnknown},
+ }
+ for i, tt := range tests {
+ p := mustMakeDir(t, tt.names...)
+ ver, err := DetectDataDir(p)
+ if ver != tt.wver {
+ t.Errorf("#%d: version = %s, want %s", i, ver, tt.wver)
+ }
+ if err != nil {
+ t.Errorf("#%d: err = %s, want nil", i, err)
+ }
+ os.RemoveAll(p)
+ }
+}
+
+// mustMakeDir builds the directory that contains files with the given
+// names. If the name ends with '/', it is created as a directory.
+func mustMakeDir(t *testing.T, names ...string) string {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ for _, n := range names {
+ if strings.HasSuffix(n, "/") {
+ if err := os.MkdirAll(path.Join(p, n), 0700); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ if _, err := os.Create(path.Join(p, n)); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+ return p
+}
diff --git a/vendor/github.com/coreos/etcd/wal/decoder.go b/vendor/github.com/coreos/etcd/wal/decoder.go
new file mode 100644
index 000000000..f75c919fb
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/decoder.go
@@ -0,0 +1,103 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "bufio"
+ "encoding/binary"
+ "hash"
+ "io"
+ "sync"
+
+ "github.com/coreos/etcd/pkg/crc"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+type decoder struct {
+ mu sync.Mutex
+ br *bufio.Reader
+
+ c io.Closer
+ crc hash.Hash32
+}
+
+func newDecoder(rc io.ReadCloser) *decoder {
+ return &decoder{
+ br: bufio.NewReader(rc),
+ c: rc,
+ crc: crc.New(0, crcTable),
+ }
+}
+
+func (d *decoder) decode(rec *walpb.Record) error {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+
+ rec.Reset()
+ l, err := readInt64(d.br)
+ if err != nil {
+ return err
+ }
+ data := make([]byte, l)
+ if _, err = io.ReadFull(d.br, data); err != nil {
+ // ReadFull returns io.EOF only if no bytes were read
+ // the decoder should treat this as an ErrUnexpectedEOF instead.
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return err
+ }
+ if err := rec.Unmarshal(data); err != nil {
+ return err
+ }
+ // skip crc checking if the record type is crcType
+ if rec.Type == crcType {
+ return nil
+ }
+ d.crc.Write(rec.Data)
+ return rec.Validate(d.crc.Sum32())
+}
+
+func (d *decoder) updateCRC(prevCrc uint32) {
+ d.crc = crc.New(prevCrc, crcTable)
+}
+
+func (d *decoder) lastCRC() uint32 {
+ return d.crc.Sum32()
+}
+
+func (d *decoder) close() error {
+ return d.c.Close()
+}
+
+func mustUnmarshalEntry(d []byte) raftpb.Entry {
+ var e raftpb.Entry
+ pbutil.MustUnmarshal(&e, d)
+ return e
+}
+
+func mustUnmarshalState(d []byte) raftpb.HardState {
+ var s raftpb.HardState
+ pbutil.MustUnmarshal(&s, d)
+ return s
+}
+
+func readInt64(r io.Reader) (int64, error) {
+ var n int64
+ err := binary.Read(r, binary.LittleEndian, &n)
+ return n, err
+}
diff --git a/vendor/github.com/coreos/etcd/wal/doc.go b/vendor/github.com/coreos/etcd/wal/doc.go
new file mode 100644
index 000000000..769b522f0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/doc.go
@@ -0,0 +1,68 @@
+// Copyright 2015 CoreOS, 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 wal provides an implementation of a write ahead log that is used by
+etcd.
+
+A WAL is created at a particular directory and is made up of a number of
+segmented WAL files. Inside of each file the raft state and entries are appended
+to it with the Save method:
+
+ metadata := []byte{}
+ w, err := wal.Create("/var/lib/etcd", metadata)
+ ...
+ err := w.Save(s, ents)
+
+After saving an raft snapshot to disk, SaveSnapshot method should be called to
+record it. So WAL can match with the saved snapshot when restarting.
+
+ err := w.SaveSnapshot(walpb.Snapshot{Index: 10, Term: 2})
+
+When a user has finished using a WAL it must be closed:
+
+ w.Close()
+
+WAL files are placed inside of the directory in the following format:
+$seq-$index.wal
+
+The first WAL file to be created will be 0000000000000000-0000000000000000.wal
+indicating an initial sequence of 0 and an initial raft index of 0. The first
+entry written to WAL MUST have raft index 0.
+
+WAL will cuts its current wal files if its size exceeds 8MB. This will increment an internal
+sequence number and cause a new file to be created. If the last raft index saved
+was 0x20 and this is the first time cut has been called on this WAL then the sequence will
+increment from 0x0 to 0x1. The new file will be: 0000000000000001-0000000000000021.wal.
+If a second cut issues 0x10 entries with incremental index later then the file will be called:
+0000000000000002-0000000000000031.wal.
+
+At a later time a WAL can be opened at a particular snapshot. If there is no
+snapshot, an empty snapshot should be passed in.
+
+ w, err := wal.Open("/var/lib/etcd", walpb.Snapshot{Index: 10, Term: 2})
+ ...
+
+The snapshot must have been written to the WAL.
+
+Additional items cannot be Saved to this WAL until all of the items from the given
+snapshot to the end of the WAL are read first:
+
+ metadata, state, ents, err := w.ReadAll()
+
+This will give you the metadata, the last raft.State and the slice of
+raft.Entry items in the log.
+
+*/
+package wal
diff --git a/vendor/github.com/coreos/etcd/wal/encoder.go b/vendor/github.com/coreos/etcd/wal/encoder.go
new file mode 100644
index 000000000..959f90ad4
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/encoder.go
@@ -0,0 +1,89 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "bufio"
+ "encoding/binary"
+ "hash"
+ "io"
+ "sync"
+
+ "github.com/coreos/etcd/pkg/crc"
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+type encoder struct {
+ mu sync.Mutex
+ bw *bufio.Writer
+
+ crc hash.Hash32
+ buf []byte
+ uint64buf []byte
+}
+
+func newEncoder(w io.Writer, prevCrc uint32) *encoder {
+ return &encoder{
+ bw: bufio.NewWriter(w),
+ crc: crc.New(prevCrc, crcTable),
+ // 1MB buffer
+ buf: make([]byte, 1024*1024),
+ uint64buf: make([]byte, 8),
+ }
+}
+
+func (e *encoder) encode(rec *walpb.Record) error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+
+ e.crc.Write(rec.Data)
+ rec.Crc = e.crc.Sum32()
+ var (
+ data []byte
+ err error
+ n int
+ )
+
+ if rec.Size() > len(e.buf) {
+ data, err = rec.Marshal()
+ if err != nil {
+ return err
+ }
+ } else {
+ n, err = rec.MarshalTo(e.buf)
+ if err != nil {
+ return err
+ }
+ data = e.buf[:n]
+ }
+ if err := writeInt64(e.bw, int64(len(data)), e.uint64buf); err != nil {
+ return err
+ }
+ _, err = e.bw.Write(data)
+ return err
+}
+
+func (e *encoder) flush() error {
+ e.mu.Lock()
+ defer e.mu.Unlock()
+ return e.bw.Flush()
+}
+
+func writeInt64(w io.Writer, n int64, buf []byte) error {
+ // http://golang.org/src/encoding/binary/binary.go
+ binary.LittleEndian.PutUint64(buf, uint64(n))
+ _, err := w.Write(buf)
+ return err
+}
diff --git a/vendor/github.com/coreos/etcd/wal/metrics.go b/vendor/github.com/coreos/etcd/wal/metrics.go
new file mode 100644
index 000000000..b792c214d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/metrics.go
@@ -0,0 +1,37 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import "github.com/coreos/etcd/Godeps/_workspace/src/github.com/prometheus/client_golang/prometheus"
+
+var (
+ syncDurations = prometheus.NewSummary(prometheus.SummaryOpts{
+ Namespace: "etcd",
+ Subsystem: "wal",
+ Name: "fsync_durations_microseconds",
+ Help: "The latency distributions of fsync called by wal.",
+ })
+ lastIndexSaved = prometheus.NewGauge(prometheus.GaugeOpts{
+ Namespace: "etcd",
+ Subsystem: "wal",
+ Name: "last_index_saved",
+ Help: "The index of the last entry saved by wal.",
+ })
+)
+
+func init() {
+ prometheus.MustRegister(syncDurations)
+ prometheus.MustRegister(lastIndexSaved)
+}
diff --git a/vendor/github.com/coreos/etcd/wal/multi_readcloser.go b/vendor/github.com/coreos/etcd/wal/multi_readcloser.go
new file mode 100644
index 000000000..513c6d17d
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/multi_readcloser.go
@@ -0,0 +1,45 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import "io"
+
+type multiReadCloser struct {
+ closers []io.Closer
+ reader io.Reader
+}
+
+func (mc *multiReadCloser) Close() error {
+ var err error
+ for i := range mc.closers {
+ err = mc.closers[i].Close()
+ }
+ return err
+}
+
+func (mc *multiReadCloser) Read(p []byte) (int, error) {
+ return mc.reader.Read(p)
+}
+
+func MultiReadCloser(readClosers ...io.ReadCloser) io.ReadCloser {
+ cs := make([]io.Closer, len(readClosers))
+ rs := make([]io.Reader, len(readClosers))
+ for i := range readClosers {
+ cs[i] = readClosers[i]
+ rs[i] = readClosers[i]
+ }
+ r := io.MultiReader(rs...)
+ return &multiReadCloser{cs, r}
+}
diff --git a/vendor/github.com/coreos/etcd/wal/record_test.go b/vendor/github.com/coreos/etcd/wal/record_test.go
new file mode 100644
index 000000000..82849c276
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/record_test.go
@@ -0,0 +1,86 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "bytes"
+ "hash/crc32"
+ "io"
+ "io/ioutil"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+var (
+ infoData = []byte("\b\xef\xfd\x02")
+ infoRecord = append([]byte("\x0e\x00\x00\x00\x00\x00\x00\x00\b\x01\x10\x99\xb5\xe4\xd0\x03\x1a\x04"), infoData...)
+)
+
+func TestReadRecord(t *testing.T) {
+ badInfoRecord := make([]byte, len(infoRecord))
+ copy(badInfoRecord, infoRecord)
+ badInfoRecord[len(badInfoRecord)-1] = 'a'
+
+ tests := []struct {
+ data []byte
+ wr *walpb.Record
+ we error
+ }{
+ {infoRecord, &walpb.Record{Type: 1, Crc: crc32.Checksum(infoData, crcTable), Data: infoData}, nil},
+ {[]byte(""), &walpb.Record{}, io.EOF},
+ {infoRecord[:8], &walpb.Record{}, io.ErrUnexpectedEOF},
+ {infoRecord[:len(infoRecord)-len(infoData)-8], &walpb.Record{}, io.ErrUnexpectedEOF},
+ {infoRecord[:len(infoRecord)-len(infoData)], &walpb.Record{}, io.ErrUnexpectedEOF},
+ {infoRecord[:len(infoRecord)-8], &walpb.Record{}, io.ErrUnexpectedEOF},
+ {badInfoRecord, &walpb.Record{}, walpb.ErrCRCMismatch},
+ }
+
+ rec := &walpb.Record{}
+ for i, tt := range tests {
+ buf := bytes.NewBuffer(tt.data)
+ decoder := newDecoder(ioutil.NopCloser(buf))
+ e := decoder.decode(rec)
+ if !reflect.DeepEqual(rec, tt.wr) {
+ t.Errorf("#%d: block = %v, want %v", i, rec, tt.wr)
+ }
+ if !reflect.DeepEqual(e, tt.we) {
+ t.Errorf("#%d: err = %v, want %v", i, e, tt.we)
+ }
+ rec = &walpb.Record{}
+ }
+}
+
+func TestWriteRecord(t *testing.T) {
+ b := &walpb.Record{}
+ typ := int64(0xABCD)
+ d := []byte("Hello world!")
+ buf := new(bytes.Buffer)
+ e := newEncoder(buf, 0)
+ e.encode(&walpb.Record{Type: typ, Data: d})
+ e.flush()
+ decoder := newDecoder(ioutil.NopCloser(buf))
+ err := decoder.decode(b)
+ if err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if b.Type != typ {
+ t.Errorf("type = %d, want %d", b.Type, typ)
+ }
+ if !reflect.DeepEqual(b.Data, d) {
+ t.Errorf("data = %v, want %v", b.Data, d)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/wal/repair.go b/vendor/github.com/coreos/etcd/wal/repair.go
new file mode 100644
index 000000000..d18112553
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/repair.go
@@ -0,0 +1,106 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "io"
+ "os"
+ "path"
+
+ "github.com/coreos/etcd/pkg/fileutil"
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+// Repair tries to repair the unexpectedEOF error in the
+// last wal file by truncating.
+func Repair(dirpath string) bool {
+ f, err := openLast(dirpath)
+ if err != nil {
+ return false
+ }
+ defer f.Close()
+
+ n := 0
+ rec := &walpb.Record{}
+
+ decoder := newDecoder(f)
+ defer decoder.close()
+ for {
+ err := decoder.decode(rec)
+ switch err {
+ case nil:
+ n += 8 + rec.Size()
+ // update crc of the decoder when necessary
+ switch rec.Type {
+ case crcType:
+ crc := decoder.crc.Sum32()
+ // current crc of decoder must match the crc of the record.
+ // do no need to match 0 crc, since the decoder is a new one at this case.
+ if crc != 0 && rec.Validate(crc) != nil {
+ return false
+ }
+ decoder.updateCRC(rec.Crc)
+ }
+ continue
+ case io.EOF:
+ return true
+ case io.ErrUnexpectedEOF:
+ plog.Noticef("repairing %v", f.Name())
+ bf, bferr := os.Create(f.Name() + ".broken")
+ if bferr != nil {
+ plog.Errorf("could not repair %v, failed to create backup file", f.Name())
+ return false
+ }
+ defer bf.Close()
+
+ if _, err = f.Seek(0, os.SEEK_SET); err != nil {
+ plog.Errorf("could not repair %v, failed to read file", f.Name())
+ return false
+ }
+
+ if _, err = io.Copy(bf, f); err != nil {
+ plog.Errorf("could not repair %v, failed to copy file", f.Name())
+ return false
+ }
+
+ if err = f.Truncate(int64(n)); err != nil {
+ plog.Errorf("could not repair %v, failed to truncate file", f.Name())
+ return false
+ }
+ if err = f.Sync(); err != nil {
+ plog.Errorf("could not repair %v, failed to sync file", f.Name())
+ return false
+ }
+ return true
+ default:
+ plog.Errorf("could not repair error (%v)", err)
+ return false
+ }
+ }
+}
+
+// openLast opens the last wal file for read and write.
+func openLast(dirpath string) (*os.File, error) {
+ names, err := fileutil.ReadDir(dirpath)
+ if err != nil {
+ return nil, err
+ }
+ names = checkWalNames(names)
+ if len(names) == 0 {
+ return nil, ErrFileNotFound
+ }
+ last := path.Join(dirpath, names[len(names)-1])
+ return os.OpenFile(last, os.O_RDWR, 0)
+}
diff --git a/vendor/github.com/coreos/etcd/wal/repair_test.go b/vendor/github.com/coreos/etcd/wal/repair_test.go
new file mode 100644
index 000000000..38c03b0c9
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/repair_test.go
@@ -0,0 +1,91 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "io"
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+func TestRepair(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+ // create WAL
+ w, err := Create(p, nil)
+ defer w.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ n := 10
+ for i := 1; i <= n; i++ {
+ es := []raftpb.Entry{{Index: uint64(i)}}
+ if err = w.Save(raftpb.HardState{}, es); err != nil {
+ t.Fatal(err)
+ }
+ }
+ w.Close()
+
+ // break the wal.
+ f, err := openLast(p)
+ if err != nil {
+ t.Fatal(err)
+ }
+ offset, err := f.Seek(-4, os.SEEK_END)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = f.Truncate(offset)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // verify we have broke the wal
+ w, err = Open(p, walpb.Snapshot{})
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, _, _, err = w.ReadAll()
+ if err != io.ErrUnexpectedEOF {
+ t.Fatalf("err = %v, want %v", err, io.ErrUnexpectedEOF)
+ }
+ w.Close()
+
+ // repair the wal
+ ok := Repair(p)
+ if !ok {
+ t.Fatalf("fix = %t, want %t", ok, true)
+ }
+
+ w, err = Open(p, walpb.Snapshot{})
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, _, ents, err := w.ReadAll()
+ if err != nil {
+ t.Fatalf("err = %v, want %v", err, nil)
+ }
+ if len(ents) != n-1 {
+ t.Fatalf("len(ents) = %d, want %d", len(ents), n-1)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/wal/util.go b/vendor/github.com/coreos/etcd/wal/util.go
new file mode 100644
index 000000000..9588b6ec0
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/util.go
@@ -0,0 +1,93 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/coreos/etcd/pkg/fileutil"
+)
+
+var (
+ badWalName = errors.New("bad wal name")
+)
+
+func Exist(dirpath string) bool {
+ names, err := fileutil.ReadDir(dirpath)
+ if err != nil {
+ return false
+ }
+ return len(names) != 0
+}
+
+// searchIndex returns the last array index of names whose raft index section is
+// equal to or smaller than the given index.
+// The given names MUST be sorted.
+func searchIndex(names []string, index uint64) (int, bool) {
+ for i := len(names) - 1; i >= 0; i-- {
+ name := names[i]
+ _, curIndex, err := parseWalName(name)
+ if err != nil {
+ plog.Panicf("parse correct name should never fail: %v", err)
+ }
+ if index >= curIndex {
+ return i, true
+ }
+ }
+ return -1, false
+}
+
+// names should have been sorted based on sequence number.
+// isValidSeq checks whether seq increases continuously.
+func isValidSeq(names []string) bool {
+ var lastSeq uint64
+ for _, name := range names {
+ curSeq, _, err := parseWalName(name)
+ if err != nil {
+ plog.Panicf("parse correct name should never fail: %v", err)
+ }
+ if lastSeq != 0 && lastSeq != curSeq-1 {
+ return false
+ }
+ lastSeq = curSeq
+ }
+ return true
+}
+
+func checkWalNames(names []string) []string {
+ wnames := make([]string, 0)
+ for _, name := range names {
+ if _, _, err := parseWalName(name); err != nil {
+ plog.Warningf("ignored file %v in wal", name)
+ continue
+ }
+ wnames = append(wnames, name)
+ }
+ return wnames
+}
+
+func parseWalName(str string) (seq, index uint64, err error) {
+ if !strings.HasSuffix(str, ".wal") {
+ return 0, 0, badWalName
+ }
+ _, err = fmt.Sscanf(str, "%016x-%016x.wal", &seq, &index)
+ return seq, index, err
+}
+
+func walName(seq, index uint64) string {
+ return fmt.Sprintf("%016x-%016x.wal", seq, index)
+}
diff --git a/vendor/github.com/coreos/etcd/wal/wal.go b/vendor/github.com/coreos/etcd/wal/wal.go
new file mode 100644
index 000000000..d92c0619a
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/wal.go
@@ -0,0 +1,548 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "errors"
+ "fmt"
+ "hash/crc32"
+ "io"
+ "os"
+ "path"
+ "reflect"
+ "sync"
+ "time"
+
+ "github.com/coreos/etcd/pkg/fileutil"
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/raft"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/wal/walpb"
+
+ "github.com/coreos/etcd/Godeps/_workspace/src/github.com/coreos/pkg/capnslog"
+)
+
+const (
+ metadataType int64 = iota + 1
+ entryType
+ stateType
+ crcType
+ snapshotType
+
+ // the owner can make/remove files inside the directory
+ privateDirMode = 0700
+
+ // the expected size of each wal segment file.
+ // the actual size might be bigger than it.
+ segmentSizeBytes = 64 * 1000 * 1000 // 64MB
+)
+
+var (
+ plog = capnslog.NewPackageLogger("github.com/coreos/etcd", "wal")
+
+ ErrMetadataConflict = errors.New("wal: conflicting metadata found")
+ ErrFileNotFound = errors.New("wal: file not found")
+ ErrCRCMismatch = errors.New("wal: crc mismatch")
+ ErrSnapshotMismatch = errors.New("wal: snapshot mismatch")
+ ErrSnapshotNotFound = errors.New("wal: snapshot not found")
+ crcTable = crc32.MakeTable(crc32.Castagnoli)
+)
+
+// WAL is a logical repersentation of the stable storage.
+// WAL is either in read mode or append mode but not both.
+// A newly created WAL is in append mode, and ready for appending records.
+// A just opened WAL is in read mode, and ready for reading records.
+// The WAL will be ready for appending after reading out all the previous records.
+type WAL struct {
+ dir string // the living directory of the underlay files
+ metadata []byte // metadata recorded at the head of each WAL
+ state raftpb.HardState // hardstate recorded at the head of WAL
+
+ start walpb.Snapshot // snapshot to start reading
+ decoder *decoder // decoder to decode records
+
+ mu sync.Mutex
+ f *os.File // underlay file opened for appending, sync
+ seq uint64 // sequence of the wal file currently used for writes
+ enti uint64 // index of the last entry saved to the wal
+ encoder *encoder // encoder to encode records
+
+ locks []fileutil.Lock // the file locks the WAL is holding (the name is increasing)
+}
+
+// Create creates a WAL ready for appending records. The given metadata is
+// recorded at the head of each WAL file, and can be retrieved with ReadAll.
+func Create(dirpath string, metadata []byte) (*WAL, error) {
+ if Exist(dirpath) {
+ return nil, os.ErrExist
+ }
+
+ if err := os.MkdirAll(dirpath, privateDirMode); err != nil {
+ return nil, err
+ }
+
+ p := path.Join(dirpath, walName(0, 0))
+ f, err := os.OpenFile(p, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
+ if err != nil {
+ return nil, err
+ }
+ l, err := fileutil.NewLock(f.Name())
+ if err != nil {
+ return nil, err
+ }
+ err = l.Lock()
+ if err != nil {
+ return nil, err
+ }
+
+ w := &WAL{
+ dir: dirpath,
+ metadata: metadata,
+ seq: 0,
+ f: f,
+ encoder: newEncoder(f, 0),
+ }
+ w.locks = append(w.locks, l)
+ if err := w.saveCrc(0); err != nil {
+ return nil, err
+ }
+ if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil {
+ return nil, err
+ }
+ if err = w.SaveSnapshot(walpb.Snapshot{}); err != nil {
+ return nil, err
+ }
+ return w, nil
+}
+
+// Open opens the WAL at the given snap.
+// The snap SHOULD have been previously saved to the WAL, or the following
+// ReadAll will fail.
+// The returned WAL is ready to read and the first record will be the one after
+// the given snap. The WAL cannot be appended to before reading out all of its
+// previous records.
+func Open(dirpath string, snap walpb.Snapshot) (*WAL, error) {
+ return openAtIndex(dirpath, snap, true)
+}
+
+// OpenForRead only opens the wal files for read.
+// Write on a read only wal panics.
+func OpenForRead(dirpath string, snap walpb.Snapshot) (*WAL, error) {
+ return openAtIndex(dirpath, snap, false)
+}
+
+func openAtIndex(dirpath string, snap walpb.Snapshot, write bool) (*WAL, error) {
+ names, err := fileutil.ReadDir(dirpath)
+ if err != nil {
+ return nil, err
+ }
+ names = checkWalNames(names)
+ if len(names) == 0 {
+ return nil, ErrFileNotFound
+ }
+
+ nameIndex, ok := searchIndex(names, snap.Index)
+ if !ok || !isValidSeq(names[nameIndex:]) {
+ return nil, ErrFileNotFound
+ }
+
+ // open the wal files for reading
+ rcs := make([]io.ReadCloser, 0)
+ ls := make([]fileutil.Lock, 0)
+ for _, name := range names[nameIndex:] {
+ f, err := os.Open(path.Join(dirpath, name))
+ if err != nil {
+ return nil, err
+ }
+ l, err := fileutil.NewLock(f.Name())
+ if err != nil {
+ return nil, err
+ }
+ err = l.TryLock()
+ if err != nil {
+ if write {
+ return nil, err
+ }
+ }
+ rcs = append(rcs, f)
+ ls = append(ls, l)
+ }
+ rc := MultiReadCloser(rcs...)
+
+ // create a WAL ready for reading
+ w := &WAL{
+ dir: dirpath,
+ start: snap,
+ decoder: newDecoder(rc),
+ locks: ls,
+ }
+
+ if write {
+ // open the lastest wal file for appending
+ seq, _, err := parseWalName(names[len(names)-1])
+ if err != nil {
+ rc.Close()
+ return nil, err
+ }
+ last := path.Join(dirpath, names[len(names)-1])
+
+ f, err := os.OpenFile(last, os.O_WRONLY|os.O_APPEND, 0)
+ if err != nil {
+ rc.Close()
+ return nil, err
+ }
+ err = fileutil.Preallocate(f, segmentSizeBytes)
+ if err != nil {
+ rc.Close()
+ plog.Errorf("failed to allocate space when creating new wal file (%v)", err)
+ return nil, err
+ }
+
+ w.f = f
+ w.seq = seq
+ }
+
+ return w, nil
+}
+
+// ReadAll reads out records of the current WAL.
+// If opened in write mode, it must read out all records until EOF. Or an error
+// will be returned.
+// If opened in read mode, it will try to read all records if possible.
+// If it cannot read out the expected snap, it will return ErrSnapshotNotFound.
+// If loaded snap doesn't match with the expected one, it will return
+// all the records and error ErrSnapshotMismatch.
+// TODO: detect not-last-snap error.
+// TODO: maybe loose the checking of match.
+// After ReadAll, the WAL will be ready for appending new records.
+func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.Entry, err error) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ rec := &walpb.Record{}
+ decoder := w.decoder
+
+ var match bool
+ for err = decoder.decode(rec); err == nil; err = decoder.decode(rec) {
+ switch rec.Type {
+ case entryType:
+ e := mustUnmarshalEntry(rec.Data)
+ if e.Index > w.start.Index {
+ ents = append(ents[:e.Index-w.start.Index-1], e)
+ }
+ w.enti = e.Index
+ case stateType:
+ state = mustUnmarshalState(rec.Data)
+ case metadataType:
+ if metadata != nil && !reflect.DeepEqual(metadata, rec.Data) {
+ state.Reset()
+ return nil, state, nil, ErrMetadataConflict
+ }
+ metadata = rec.Data
+ case crcType:
+ crc := decoder.crc.Sum32()
+ // current crc of decoder must match the crc of the record.
+ // do no need to match 0 crc, since the decoder is a new one at this case.
+ if crc != 0 && rec.Validate(crc) != nil {
+ state.Reset()
+ return nil, state, nil, ErrCRCMismatch
+ }
+ decoder.updateCRC(rec.Crc)
+ case snapshotType:
+ var snap walpb.Snapshot
+ pbutil.MustUnmarshal(&snap, rec.Data)
+ if snap.Index == w.start.Index {
+ if snap.Term != w.start.Term {
+ state.Reset()
+ return nil, state, nil, ErrSnapshotMismatch
+ }
+ match = true
+ }
+ default:
+ state.Reset()
+ return nil, state, nil, fmt.Errorf("unexpected block type %d", rec.Type)
+ }
+ }
+
+ switch w.f {
+ case nil:
+ // We do not have to read out all entries in read mode.
+ // The last record maybe a partial written one, so
+ // ErrunexpectedEOF might be returned.
+ if err != io.EOF && err != io.ErrUnexpectedEOF {
+ state.Reset()
+ return nil, state, nil, err
+ }
+ default:
+ // We must read all of the entries if WAL is opened in write mode.
+ if err != io.EOF {
+ state.Reset()
+ return nil, state, nil, err
+ }
+ }
+
+ err = nil
+ if !match {
+ err = ErrSnapshotNotFound
+ }
+
+ // close decoder, disable reading
+ w.decoder.close()
+ w.start = walpb.Snapshot{}
+
+ w.metadata = metadata
+
+ if w.f != nil {
+ // create encoder (chain crc with the decoder), enable appending
+ w.encoder = newEncoder(w.f, w.decoder.lastCRC())
+ w.decoder = nil
+ lastIndexSaved.Set(float64(w.enti))
+ }
+
+ return metadata, state, ents, err
+}
+
+// cut closes current file written and creates a new one ready to append.
+// cut first creates a temp wal file and writes necessary headers into it.
+// Then cut atomtically rename temp wal file to a wal file.
+func (w *WAL) cut() error {
+ // close old wal file
+ if err := w.sync(); err != nil {
+ return err
+ }
+ if err := w.f.Close(); err != nil {
+ return err
+ }
+
+ fpath := path.Join(w.dir, walName(w.seq+1, w.enti+1))
+ ftpath := fpath + ".tmp"
+
+ // create a temp wal file with name sequence + 1, or tuncate the existing one
+ ft, err := os.OpenFile(ftpath, os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC, 0600)
+ if err != nil {
+ return err
+ }
+
+ // update writer and save the previous crc
+ w.f = ft
+ prevCrc := w.encoder.crc.Sum32()
+ w.encoder = newEncoder(w.f, prevCrc)
+ if err := w.saveCrc(prevCrc); err != nil {
+ return err
+ }
+ if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.metadata}); err != nil {
+ return err
+ }
+ if err := w.saveState(&w.state); err != nil {
+ return err
+ }
+ // close temp wal file
+ if err := w.sync(); err != nil {
+ return err
+ }
+ if err := w.f.Close(); err != nil {
+ return err
+ }
+
+ // atomically move temp wal file to wal file
+ if err := os.Rename(ftpath, fpath); err != nil {
+ return err
+ }
+
+ // open the wal file and update writer again
+ f, err := os.OpenFile(fpath, os.O_WRONLY|os.O_APPEND, 0600)
+ if err != nil {
+ return err
+ }
+ err = fileutil.Preallocate(f, segmentSizeBytes)
+ if err != nil {
+ plog.Errorf("failed to allocate space when creating new wal file (%v)", err)
+ return err
+ }
+
+ w.f = f
+ prevCrc = w.encoder.crc.Sum32()
+ w.encoder = newEncoder(w.f, prevCrc)
+
+ // lock the new wal file
+ l, err := fileutil.NewLock(f.Name())
+ if err != nil {
+ return err
+ }
+
+ err = l.Lock()
+ if err != nil {
+ return err
+ }
+ w.locks = append(w.locks, l)
+
+ // increase the wal seq
+ w.seq++
+
+ plog.Infof("segmented wal file %v is created", fpath)
+ return nil
+}
+
+func (w *WAL) sync() error {
+ if w.encoder != nil {
+ if err := w.encoder.flush(); err != nil {
+ return err
+ }
+ }
+ start := time.Now()
+ err := w.f.Sync()
+ syncDurations.Observe(float64(time.Since(start).Nanoseconds() / int64(time.Microsecond)))
+ return err
+}
+
+// ReleaseLockTo releases the locks, which has smaller index than the given index
+// except the largest one among them.
+// For example, if WAL is holding lock 1,2,3,4,5,6, ReleaseLockTo(4) will release
+// lock 1,2 but keep 3. ReleaseLockTo(5) will release 1,2,3 but keep 4.
+func (w *WAL) ReleaseLockTo(index uint64) error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ var smaller int
+ found := false
+
+ for i, l := range w.locks {
+ _, lockIndex, err := parseWalName(path.Base(l.Name()))
+ if err != nil {
+ return err
+ }
+ if lockIndex >= index {
+ smaller = i - 1
+ found = true
+ break
+ }
+ }
+
+ // if no lock index is greater than the release index, we can
+ // release lock upto the last one(excluding).
+ if !found && len(w.locks) != 0 {
+ smaller = len(w.locks) - 1
+ }
+
+ if smaller <= 0 {
+ return nil
+ }
+
+ for i := 0; i < smaller; i++ {
+ w.locks[i].Unlock()
+ w.locks[i].Destroy()
+ }
+ w.locks = w.locks[smaller:]
+
+ return nil
+}
+
+func (w *WAL) Close() error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ if w.f != nil {
+ if err := w.sync(); err != nil {
+ return err
+ }
+ if err := w.f.Close(); err != nil {
+ return err
+ }
+ }
+ for _, l := range w.locks {
+ err := l.Unlock()
+ if err != nil {
+ plog.Errorf("failed to unlock during closing wal: %s", err)
+ }
+ err = l.Destroy()
+ if err != nil {
+ plog.Errorf("failed to destroy lock during closing wal: %s", err)
+ }
+ }
+ return nil
+}
+
+func (w *WAL) saveEntry(e *raftpb.Entry) error {
+ // TODO: add MustMarshalTo to reduce one allocation.
+ b := pbutil.MustMarshal(e)
+ rec := &walpb.Record{Type: entryType, Data: b}
+ if err := w.encoder.encode(rec); err != nil {
+ return err
+ }
+ w.enti = e.Index
+ lastIndexSaved.Set(float64(w.enti))
+ return nil
+}
+
+func (w *WAL) saveState(s *raftpb.HardState) error {
+ if raft.IsEmptyHardState(*s) {
+ return nil
+ }
+ w.state = *s
+ b := pbutil.MustMarshal(s)
+ rec := &walpb.Record{Type: stateType, Data: b}
+ return w.encoder.encode(rec)
+}
+
+func (w *WAL) Save(st raftpb.HardState, ents []raftpb.Entry) error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ // short cut, do not call sync
+ if raft.IsEmptyHardState(st) && len(ents) == 0 {
+ return nil
+ }
+
+ // TODO(xiangli): no more reference operator
+ for i := range ents {
+ if err := w.saveEntry(&ents[i]); err != nil {
+ return err
+ }
+ }
+ if err := w.saveState(&st); err != nil {
+ return err
+ }
+
+ fstat, err := w.f.Stat()
+ if err != nil {
+ return err
+ }
+ if fstat.Size() < segmentSizeBytes {
+ return w.sync()
+ }
+ // TODO: add a test for this code path when refactoring the tests
+ return w.cut()
+}
+
+func (w *WAL) SaveSnapshot(e walpb.Snapshot) error {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+
+ b := pbutil.MustMarshal(&e)
+ rec := &walpb.Record{Type: snapshotType, Data: b}
+ if err := w.encoder.encode(rec); err != nil {
+ return err
+ }
+ // update enti only when snapshot is ahead of last index
+ if w.enti < e.Index {
+ w.enti = e.Index
+ }
+ lastIndexSaved.Set(float64(w.enti))
+ return w.sync()
+}
+
+func (w *WAL) saveCrc(prevCrc uint32) error {
+ return w.encoder.encode(&walpb.Record{Type: crcType, Crc: prevCrc})
+}
diff --git a/vendor/github.com/coreos/etcd/wal/wal_bench_test.go b/vendor/github.com/coreos/etcd/wal/wal_bench_test.go
new file mode 100644
index 000000000..9568399b2
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/wal_bench_test.go
@@ -0,0 +1,68 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/coreos/etcd/raft/raftpb"
+)
+
+func BenchmarkWrite100EntryWithoutBatch(b *testing.B) { benchmarkWriteEntry(b, 100, 0) }
+func BenchmarkWrite100EntryBatch10(b *testing.B) { benchmarkWriteEntry(b, 100, 10) }
+func BenchmarkWrite100EntryBatch100(b *testing.B) { benchmarkWriteEntry(b, 100, 100) }
+func BenchmarkWrite100EntryBatch500(b *testing.B) { benchmarkWriteEntry(b, 100, 500) }
+func BenchmarkWrite100EntryBatch1000(b *testing.B) { benchmarkWriteEntry(b, 100, 1000) }
+
+func BenchmarkWrite1000EntryWithoutBatch(b *testing.B) { benchmarkWriteEntry(b, 1000, 0) }
+func BenchmarkWrite1000EntryBatch10(b *testing.B) { benchmarkWriteEntry(b, 1000, 10) }
+func BenchmarkWrite1000EntryBatch100(b *testing.B) { benchmarkWriteEntry(b, 1000, 100) }
+func BenchmarkWrite1000EntryBatch500(b *testing.B) { benchmarkWriteEntry(b, 1000, 500) }
+func BenchmarkWrite1000EntryBatch1000(b *testing.B) { benchmarkWriteEntry(b, 1000, 1000) }
+
+func benchmarkWriteEntry(b *testing.B, size int, batch int) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ w, err := Create(p, []byte("somedata"))
+ if err != nil {
+ b.Fatalf("err = %v, want nil", err)
+ }
+ data := make([]byte, size)
+ for i := 0; i < len(data); i++ {
+ data[i] = byte(i)
+ }
+ e := &raftpb.Entry{Data: data}
+
+ b.ResetTimer()
+ n := 0
+ b.SetBytes(int64(e.Size()))
+ for i := 0; i < b.N; i++ {
+ err := w.saveEntry(e)
+ if err != nil {
+ b.Fatal(err)
+ }
+ n++
+ if n > batch {
+ w.sync()
+ n = 0
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/wal/wal_test.go b/vendor/github.com/coreos/etcd/wal/wal_test.go
new file mode 100644
index 000000000..9de5efc70
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/wal_test.go
@@ -0,0 +1,523 @@
+// Copyright 2015 CoreOS, 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 wal
+
+import (
+ "bytes"
+ "io/ioutil"
+ "os"
+ "path"
+ "reflect"
+ "testing"
+
+ "github.com/coreos/etcd/pkg/pbutil"
+ "github.com/coreos/etcd/raft/raftpb"
+ "github.com/coreos/etcd/wal/walpb"
+)
+
+func TestNew(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ w, err := Create(p, []byte("somedata"))
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ if g := path.Base(w.f.Name()); g != walName(0, 0) {
+ t.Errorf("name = %+v, want %+v", g, walName(0, 0))
+ }
+ defer w.Close()
+ gd, err := ioutil.ReadFile(w.f.Name())
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+
+ var wb bytes.Buffer
+ e := newEncoder(&wb, 0)
+ err = e.encode(&walpb.Record{Type: crcType, Crc: 0})
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ err = e.encode(&walpb.Record{Type: metadataType, Data: []byte("somedata")})
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ r := &walpb.Record{
+ Type: snapshotType,
+ Data: pbutil.MustMarshal(&walpb.Snapshot{}),
+ }
+ if err = e.encode(r); err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ e.flush()
+ if !reflect.DeepEqual(gd, wb.Bytes()) {
+ t.Errorf("data = %v, want %v", gd, wb.Bytes())
+ }
+}
+
+func TestNewForInitedDir(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ os.Create(path.Join(p, walName(0, 0)))
+ if _, err = Create(p, nil); err == nil || err != os.ErrExist {
+ t.Errorf("err = %v, want %v", err, os.ErrExist)
+ }
+}
+
+func TestOpenAtIndex(t *testing.T) {
+ dir, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ f, err := os.Create(path.Join(dir, walName(0, 0)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ w, err := Open(dir, walpb.Snapshot{})
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ if g := path.Base(w.f.Name()); g != walName(0, 0) {
+ t.Errorf("name = %+v, want %+v", g, walName(0, 0))
+ }
+ if w.seq != 0 {
+ t.Errorf("seq = %d, want %d", w.seq, 0)
+ }
+ w.Close()
+
+ wname := walName(2, 10)
+ f, err = os.Create(path.Join(dir, wname))
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ w, err = Open(dir, walpb.Snapshot{Index: 5})
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ if g := path.Base(w.f.Name()); g != wname {
+ t.Errorf("name = %+v, want %+v", g, wname)
+ }
+ if w.seq != 2 {
+ t.Errorf("seq = %d, want %d", w.seq, 2)
+ }
+ w.Close()
+
+ emptydir, err := ioutil.TempDir(os.TempDir(), "waltestempty")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(emptydir)
+ if _, err = Open(emptydir, walpb.Snapshot{}); err != ErrFileNotFound {
+ t.Errorf("err = %v, want %v", err, ErrFileNotFound)
+ }
+}
+
+// TODO: split it into smaller tests for better readability
+func TestCut(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ w, err := Create(p, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer w.Close()
+
+ state := raftpb.HardState{Term: 1}
+ // TODO(unihorn): remove this when cut can operate on an empty file
+ if err := w.Save(state, []raftpb.Entry{{}}); err != nil {
+ t.Fatal(err)
+ }
+ if err := w.cut(); err != nil {
+ t.Fatal(err)
+ }
+ wname := walName(1, 1)
+ if g := path.Base(w.f.Name()); g != wname {
+ t.Errorf("name = %s, want %s", g, wname)
+ }
+
+ es := []raftpb.Entry{{Index: 1, Term: 1, Data: []byte{1}}}
+ if err := w.Save(raftpb.HardState{}, es); err != nil {
+ t.Fatal(err)
+ }
+ if err := w.cut(); err != nil {
+ t.Fatal(err)
+ }
+ snap := walpb.Snapshot{Index: 2, Term: 1}
+ if err := w.SaveSnapshot(snap); err != nil {
+ t.Fatal(err)
+ }
+ wname = walName(2, 2)
+ if g := path.Base(w.f.Name()); g != wname {
+ t.Errorf("name = %s, want %s", g, wname)
+ }
+
+ // check the state in the last WAL
+ // We do check before closing the WAL to ensure that Cut syncs the data
+ // into the disk.
+ f, err := os.Open(path.Join(p, wname))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ nw := &WAL{
+ decoder: newDecoder(f),
+ start: snap,
+ }
+ _, gst, _, err := nw.ReadAll()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(gst, state) {
+ t.Errorf("state = %+v, want %+v", gst, state)
+ }
+}
+
+func TestRecover(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ w, err := Create(p, []byte("metadata"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := w.SaveSnapshot(walpb.Snapshot{}); err != nil {
+ t.Fatal(err)
+ }
+ ents := []raftpb.Entry{{Index: 1, Term: 1, Data: []byte{1}}, {Index: 2, Term: 2, Data: []byte{2}}}
+ if err = w.Save(raftpb.HardState{}, ents); err != nil {
+ t.Fatal(err)
+ }
+ sts := []raftpb.HardState{{Term: 1, Vote: 1, Commit: 1}, {Term: 2, Vote: 2, Commit: 2}}
+ for _, s := range sts {
+ if err = w.Save(s, nil); err != nil {
+ t.Fatal(err)
+ }
+ }
+ w.Close()
+
+ if w, err = Open(p, walpb.Snapshot{}); err != nil {
+ t.Fatal(err)
+ }
+ metadata, state, entries, err := w.ReadAll()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !reflect.DeepEqual(metadata, []byte("metadata")) {
+ t.Errorf("metadata = %s, want %s", metadata, "metadata")
+ }
+ if !reflect.DeepEqual(entries, ents) {
+ t.Errorf("ents = %+v, want %+v", entries, ents)
+ }
+ // only the latest state is recorded
+ s := sts[len(sts)-1]
+ if !reflect.DeepEqual(state, s) {
+ t.Errorf("state = %+v, want %+v", state, s)
+ }
+ w.Close()
+}
+
+func TestSearchIndex(t *testing.T) {
+ tests := []struct {
+ names []string
+ index uint64
+ widx int
+ wok bool
+ }{
+ {
+ []string{
+ "0000000000000000-0000000000000000.wal",
+ "0000000000000001-0000000000001000.wal",
+ "0000000000000002-0000000000002000.wal",
+ },
+ 0x1000, 1, true,
+ },
+ {
+ []string{
+ "0000000000000001-0000000000004000.wal",
+ "0000000000000002-0000000000003000.wal",
+ "0000000000000003-0000000000005000.wal",
+ },
+ 0x4000, 1, true,
+ },
+ {
+ []string{
+ "0000000000000001-0000000000002000.wal",
+ "0000000000000002-0000000000003000.wal",
+ "0000000000000003-0000000000005000.wal",
+ },
+ 0x1000, -1, false,
+ },
+ }
+ for i, tt := range tests {
+ idx, ok := searchIndex(tt.names, tt.index)
+ if idx != tt.widx {
+ t.Errorf("#%d: idx = %d, want %d", i, idx, tt.widx)
+ }
+ if ok != tt.wok {
+ t.Errorf("#%d: ok = %v, want %v", i, ok, tt.wok)
+ }
+ }
+}
+
+func TestScanWalName(t *testing.T) {
+ tests := []struct {
+ str string
+ wseq, windex uint64
+ wok bool
+ }{
+ {"0000000000000000-0000000000000000.wal", 0, 0, true},
+ {"0000000000000000.wal", 0, 0, false},
+ {"0000000000000000-0000000000000000.snap", 0, 0, false},
+ }
+ for i, tt := range tests {
+ s, index, err := parseWalName(tt.str)
+ if g := err == nil; g != tt.wok {
+ t.Errorf("#%d: ok = %v, want %v", i, g, tt.wok)
+ }
+ if s != tt.wseq {
+ t.Errorf("#%d: seq = %d, want %d", i, s, tt.wseq)
+ }
+ if index != tt.windex {
+ t.Errorf("#%d: index = %d, want %d", i, index, tt.windex)
+ }
+ }
+}
+
+func TestRecoverAfterCut(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ md, err := Create(p, []byte("metadata"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ for i := 0; i < 10; i++ {
+ if err = md.SaveSnapshot(walpb.Snapshot{Index: uint64(i)}); err != nil {
+ t.Fatal(err)
+ }
+ es := []raftpb.Entry{{Index: uint64(i)}}
+ if err = md.Save(raftpb.HardState{}, es); err != nil {
+ t.Fatal(err)
+ }
+ if err = md.cut(); err != nil {
+ t.Fatal(err)
+ }
+ }
+ md.Close()
+
+ if err := os.Remove(path.Join(p, walName(4, 4))); err != nil {
+ t.Fatal(err)
+ }
+
+ for i := 0; i < 10; i++ {
+ w, err := Open(p, walpb.Snapshot{Index: uint64(i)})
+ if err != nil {
+ if i <= 4 {
+ if err != ErrFileNotFound {
+ t.Errorf("#%d: err = %v, want %v", i, err, ErrFileNotFound)
+ }
+ } else {
+ t.Errorf("#%d: err = %v, want nil", i, err)
+ }
+ continue
+ }
+ metadata, _, entries, err := w.ReadAll()
+ if err != nil {
+ t.Errorf("#%d: err = %v, want nil", i, err)
+ continue
+ }
+ if !reflect.DeepEqual(metadata, []byte("metadata")) {
+ t.Errorf("#%d: metadata = %s, want %s", i, metadata, "metadata")
+ }
+ for j, e := range entries {
+ if e.Index != uint64(j+i+1) {
+ t.Errorf("#%d: ents[%d].Index = %+v, want %+v", i, j, e.Index, j+i+1)
+ }
+ }
+ w.Close()
+ }
+}
+
+func TestOpenAtUncommittedIndex(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+
+ w, err := Create(p, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := w.SaveSnapshot(walpb.Snapshot{}); err != nil {
+ t.Fatal(err)
+ }
+ if err := w.Save(raftpb.HardState{}, []raftpb.Entry{{Index: 0}}); err != nil {
+ t.Fatal(err)
+ }
+ w.Close()
+
+ w, err = Open(p, walpb.Snapshot{})
+ if err != nil {
+ t.Fatal(err)
+ }
+ // commit up to index 0, try to read index 1
+ if _, _, _, err := w.ReadAll(); err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ w.Close()
+}
+
+// TestOpenForRead tests that OpenForRead can load all files.
+// The tests creates WAL directory, and cut out multiple WAL files. Then
+// it releases the lock of part of data, and excepts that OpenForRead
+// can read out all files even if some are locked for write.
+func TestOpenForRead(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+ // create WAL
+ w, err := Create(p, nil)
+ defer w.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ // make 10 separate files
+ for i := 0; i < 10; i++ {
+ es := []raftpb.Entry{{Index: uint64(i)}}
+ if err = w.Save(raftpb.HardState{}, es); err != nil {
+ t.Fatal(err)
+ }
+ if err = w.cut(); err != nil {
+ t.Fatal(err)
+ }
+ }
+ // release the lock to 5
+ unlockIndex := uint64(5)
+ w.ReleaseLockTo(unlockIndex)
+
+ // All are available for read
+ w2, err := OpenForRead(p, walpb.Snapshot{})
+ defer w2.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, _, ents, err := w2.ReadAll()
+ if err != nil {
+ t.Fatalf("err = %v, want nil", err)
+ }
+ if g := ents[len(ents)-1].Index; g != 9 {
+ t.Errorf("last index read = %d, want %d", g, 9)
+ }
+}
+
+func TestSaveEmpty(t *testing.T) {
+ var buf bytes.Buffer
+ var est raftpb.HardState
+ w := WAL{
+ encoder: newEncoder(&buf, 0),
+ }
+ if err := w.saveState(&est); err != nil {
+ t.Errorf("err = %v, want nil", err)
+ }
+ if len(buf.Bytes()) != 0 {
+ t.Errorf("buf.Bytes = %d, want 0", len(buf.Bytes()))
+ }
+}
+
+func TestReleaseLockTo(t *testing.T) {
+ p, err := ioutil.TempDir(os.TempDir(), "waltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(p)
+ // create WAL
+ w, err := Create(p, nil)
+ defer w.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ // make 10 separate files
+ for i := 0; i < 10; i++ {
+ es := []raftpb.Entry{{Index: uint64(i)}}
+ if err = w.Save(raftpb.HardState{}, es); err != nil {
+ t.Fatal(err)
+ }
+ if err = w.cut(); err != nil {
+ t.Fatal(err)
+ }
+ }
+ // release the lock to 5
+ unlockIndex := uint64(5)
+ w.ReleaseLockTo(unlockIndex)
+
+ // expected remaining are 4,5,6,7,8,9,10
+ if len(w.locks) != 7 {
+ t.Errorf("len(w.locks) = %d, want %d", len(w.locks), 7)
+ }
+ for i, l := range w.locks {
+ _, lockIndex, err := parseWalName(path.Base(l.Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if lockIndex != uint64(i+4) {
+ t.Errorf("#%d: lockindex = %d, want %d", i, lockIndex, uint64(i+4))
+ }
+ }
+
+ // release the lock to 15
+ unlockIndex = uint64(15)
+ w.ReleaseLockTo(unlockIndex)
+
+ // expected remaining is 10
+ if len(w.locks) != 1 {
+ t.Errorf("len(w.locks) = %d, want %d", len(w.locks), 1)
+ }
+ _, lockIndex, err := parseWalName(path.Base(w.locks[0].Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if lockIndex != uint64(10) {
+ t.Errorf("lockindex = %d, want %d", lockIndex, 10)
+ }
+}
diff --git a/vendor/github.com/coreos/etcd/wal/walpb/record.go b/vendor/github.com/coreos/etcd/wal/walpb/record.go
new file mode 100644
index 000000000..bb5368569
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/walpb/record.go
@@ -0,0 +1,29 @@
+// Copyright 2015 CoreOS, 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 walpb
+
+import "errors"
+
+var (
+ ErrCRCMismatch = errors.New("walpb: crc mismatch")
+)
+
+func (rec *Record) Validate(crc uint32) error {
+ if rec.Crc == crc {
+ return nil
+ }
+ rec.Reset()
+ return ErrCRCMismatch
+}
diff --git a/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go b/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go
new file mode 100644
index 000000000..9749b08e5
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/walpb/record.pb.go
@@ -0,0 +1,447 @@
+// Code generated by protoc-gen-gogo.
+// source: record.proto
+// DO NOT EDIT!
+
+/*
+ Package walpb is a generated protocol buffer package.
+
+ It is generated from these files:
+ record.proto
+
+ It has these top-level messages:
+ Record
+ Snapshot
+*/
+package walpb
+
+import proto "github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
+import math "math"
+
+// discarding unused import gogoproto "github.com/coreos/etcd/Godeps/_workspace/src/gogoproto"
+
+import io "io"
+import fmt "fmt"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = math.Inf
+
+type Record struct {
+ Type int64 `protobuf:"varint,1,opt,name=type" json:"type"`
+ Crc uint32 `protobuf:"varint,2,opt,name=crc" json:"crc"`
+ Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Record) Reset() { *m = Record{} }
+func (m *Record) String() string { return proto.CompactTextString(m) }
+func (*Record) ProtoMessage() {}
+
+type Snapshot struct {
+ Index uint64 `protobuf:"varint,1,opt,name=index" json:"index"`
+ Term uint64 `protobuf:"varint,2,opt,name=term" json:"term"`
+ XXX_unrecognized []byte `json:"-"`
+}
+
+func (m *Snapshot) Reset() { *m = Snapshot{} }
+func (m *Snapshot) String() string { return proto.CompactTextString(m) }
+func (*Snapshot) ProtoMessage() {}
+
+func (m *Record) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Record) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintRecord(data, i, uint64(m.Type))
+ data[i] = 0x10
+ i++
+ i = encodeVarintRecord(data, i, uint64(m.Crc))
+ if m.Data != nil {
+ data[i] = 0x1a
+ i++
+ i = encodeVarintRecord(data, i, uint64(len(m.Data)))
+ i += copy(data[i:], m.Data)
+ }
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func (m *Snapshot) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+func (m *Snapshot) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ data[i] = 0x8
+ i++
+ i = encodeVarintRecord(data, i, uint64(m.Index))
+ data[i] = 0x10
+ i++
+ i = encodeVarintRecord(data, i, uint64(m.Term))
+ if m.XXX_unrecognized != nil {
+ i += copy(data[i:], m.XXX_unrecognized)
+ }
+ return i, nil
+}
+
+func encodeFixed64Record(data []byte, offset int, v uint64) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ data[offset+4] = uint8(v >> 32)
+ data[offset+5] = uint8(v >> 40)
+ data[offset+6] = uint8(v >> 48)
+ data[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Record(data []byte, offset int, v uint32) int {
+ data[offset] = uint8(v)
+ data[offset+1] = uint8(v >> 8)
+ data[offset+2] = uint8(v >> 16)
+ data[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintRecord(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+func (m *Record) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovRecord(uint64(m.Type))
+ n += 1 + sovRecord(uint64(m.Crc))
+ if m.Data != nil {
+ l = len(m.Data)
+ n += 1 + l + sovRecord(uint64(l))
+ }
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func (m *Snapshot) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovRecord(uint64(m.Index))
+ n += 1 + sovRecord(uint64(m.Term))
+ if m.XXX_unrecognized != nil {
+ n += len(m.XXX_unrecognized)
+ }
+ return n
+}
+
+func sovRecord(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozRecord(x uint64) (n int) {
+ return sovRecord(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Record) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ m.Type = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Type |= (int64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Crc", wireType)
+ }
+ m.Crc = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Crc |= (uint32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
+ }
+ var byteLen int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ byteLen |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if byteLen < 0 {
+ return ErrInvalidLengthRecord
+ }
+ postIndex := iNdEx + byteLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Data = append([]byte{}, data[iNdEx:postIndex]...)
+ iNdEx = postIndex
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRecord(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRecord
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func (m *Snapshot) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType)
+ }
+ m.Index = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Index |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Term", wireType)
+ }
+ m.Term = 0
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ m.Term |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ default:
+ var sizeOfWire int
+ for {
+ sizeOfWire++
+ wire >>= 7
+ if wire == 0 {
+ break
+ }
+ }
+ iNdEx -= sizeOfWire
+ skippy, err := skipRecord(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthRecord
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.XXX_unrecognized = append(m.XXX_unrecognized, data[iNdEx:iNdEx+skippy]...)
+ iNdEx += skippy
+ }
+ }
+
+ return nil
+}
+func skipRecord(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthRecord
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipRecord(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthRecord = fmt.Errorf("proto: negative length found during unmarshaling")
+)
diff --git a/vendor/github.com/coreos/etcd/wal/walpb/record.proto b/vendor/github.com/coreos/etcd/wal/walpb/record.proto
new file mode 100644
index 000000000..b694cb233
--- /dev/null
+++ b/vendor/github.com/coreos/etcd/wal/walpb/record.proto
@@ -0,0 +1,20 @@
+syntax = "proto2";
+package walpb;
+
+import "gogoproto/gogo.proto";
+
+option (gogoproto.marshaler_all) = true;
+option (gogoproto.sizer_all) = true;
+option (gogoproto.unmarshaler_all) = true;
+option (gogoproto.goproto_getters_all) = false;
+
+message Record {
+ optional int64 type = 1 [(gogoproto.nullable) = false];
+ optional uint32 crc = 2 [(gogoproto.nullable) = false];
+ optional bytes data = 3;
+}
+
+message Snapshot {
+ optional uint64 index = 1 [(gogoproto.nullable) = false];
+ optional uint64 term = 2 [(gogoproto.nullable) = false];
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/add_child.go b/vendor/github.com/coreos/go-etcd/etcd/add_child.go
new file mode 100644
index 000000000..7122be049
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/add_child.go
@@ -0,0 +1,23 @@
+package etcd
+
+// Add a new directory with a random etcd-generated key under the given path.
+func (c *Client) AddChildDir(key string, ttl uint64) (*Response, error) {
+ raw, err := c.post(key, "", ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// Add a new file with a random etcd-generated key under the given path.
+func (c *Client) AddChild(key string, value string, ttl uint64) (*Response, error) {
+ raw, err := c.post(key, value, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/add_child_test.go b/vendor/github.com/coreos/go-etcd/etcd/add_child_test.go
new file mode 100644
index 000000000..26223ff1c
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/add_child_test.go
@@ -0,0 +1,73 @@
+package etcd
+
+import "testing"
+
+func TestAddChild(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("fooDir", true)
+ c.Delete("nonexistentDir", true)
+ }()
+
+ c.CreateDir("fooDir", 5)
+
+ _, err := c.AddChild("fooDir", "v0", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = c.AddChild("fooDir", "v1", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ resp, err := c.Get("fooDir", true, false)
+ // The child with v0 should proceed the child with v1 because it's added
+ // earlier, so it should have a lower key.
+ if !(len(resp.Node.Nodes) == 2 && (resp.Node.Nodes[0].Value == "v0" && resp.Node.Nodes[1].Value == "v1")) {
+ t.Fatalf("AddChild 1 failed. There should be two chlidren whose values are v0 and v1, respectively."+
+ " The response was: %#v", resp)
+ }
+
+ // Creating a child under a nonexistent directory should succeed.
+ // The directory should be created.
+ resp, err = c.AddChild("nonexistentDir", "foo", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestAddChildDir(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("fooDir", true)
+ c.Delete("nonexistentDir", true)
+ }()
+
+ c.CreateDir("fooDir", 5)
+
+ _, err := c.AddChildDir("fooDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = c.AddChildDir("fooDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ resp, err := c.Get("fooDir", true, false)
+ // The child with v0 should proceed the child with v1 because it's added
+ // earlier, so it should have a lower key.
+ if !(len(resp.Node.Nodes) == 2 && (len(resp.Node.Nodes[0].Nodes) == 0 && len(resp.Node.Nodes[1].Nodes) == 0)) {
+ t.Fatalf("AddChildDir 1 failed. There should be two chlidren whose values are v0 and v1, respectively."+
+ " The response was: %#v", resp)
+ }
+
+ // Creating a child under a nonexistent directory should succeed.
+ // The directory should be created.
+ resp, err = c.AddChildDir("nonexistentDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/client.go b/vendor/github.com/coreos/go-etcd/etcd/client.go
new file mode 100644
index 000000000..60ed762b9
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/client.go
@@ -0,0 +1,476 @@
+package etcd
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/json"
+ "errors"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+ "strings"
+ "time"
+)
+
+// See SetConsistency for how to use these constants.
+const (
+ // Using strings rather than iota because the consistency level
+ // could be persisted to disk, so it'd be better to use
+ // human-readable values.
+ STRONG_CONSISTENCY = "STRONG"
+ WEAK_CONSISTENCY = "WEAK"
+)
+
+const (
+ defaultBufferSize = 10
+)
+
+func init() {
+ rand.Seed(int64(time.Now().Nanosecond()))
+}
+
+type Config struct {
+ CertFile string `json:"certFile"`
+ KeyFile string `json:"keyFile"`
+ CaCertFile []string `json:"caCertFiles"`
+ DialTimeout time.Duration `json:"timeout"`
+ Consistency string `json:"consistency"`
+}
+
+type credentials struct {
+ username string
+ password string
+}
+
+type Client struct {
+ config Config `json:"config"`
+ cluster *Cluster `json:"cluster"`
+ httpClient *http.Client
+ credentials *credentials
+ transport *http.Transport
+ persistence io.Writer
+ cURLch chan string
+ // CheckRetry can be used to control the policy for failed requests
+ // and modify the cluster if needed.
+ // The client calls it before sending requests again, and
+ // stops retrying if CheckRetry returns some error. The cases that
+ // this function needs to handle include no response and unexpected
+ // http status code of response.
+ // If CheckRetry is nil, client will call the default one
+ // `DefaultCheckRetry`.
+ // Argument cluster is the etcd.Cluster object that these requests have been made on.
+ // Argument numReqs is the number of http.Requests that have been made so far.
+ // Argument lastResp is the http.Responses from the last request.
+ // Argument err is the reason of the failure.
+ CheckRetry func(cluster *Cluster, numReqs int,
+ lastResp http.Response, err error) error
+}
+
+// NewClient create a basic client that is configured to be used
+// with the given machine list.
+func NewClient(machines []string) *Client {
+ config := Config{
+ // default timeout is one second
+ DialTimeout: time.Second,
+ Consistency: WEAK_CONSISTENCY,
+ }
+
+ client := &Client{
+ cluster: NewCluster(machines),
+ config: config,
+ }
+
+ client.initHTTPClient()
+ client.saveConfig()
+
+ return client
+}
+
+// NewTLSClient create a basic client with TLS configuration
+func NewTLSClient(machines []string, cert, key, caCert string) (*Client, error) {
+ // overwrite the default machine to use https
+ if len(machines) == 0 {
+ machines = []string{"https://127.0.0.1:4001"}
+ }
+
+ config := Config{
+ // default timeout is one second
+ DialTimeout: time.Second,
+ Consistency: WEAK_CONSISTENCY,
+ CertFile: cert,
+ KeyFile: key,
+ CaCertFile: make([]string, 0),
+ }
+
+ client := &Client{
+ cluster: NewCluster(machines),
+ config: config,
+ }
+
+ err := client.initHTTPSClient(cert, key)
+ if err != nil {
+ return nil, err
+ }
+
+ err = client.AddRootCA(caCert)
+
+ client.saveConfig()
+
+ return client, nil
+}
+
+// NewClientFromFile creates a client from a given file path.
+// The given file is expected to use the JSON format.
+func NewClientFromFile(fpath string) (*Client, error) {
+ fi, err := os.Open(fpath)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err := fi.Close(); err != nil {
+ panic(err)
+ }
+ }()
+
+ return NewClientFromReader(fi)
+}
+
+// NewClientFromReader creates a Client configured from a given reader.
+// The configuration is expected to use the JSON format.
+func NewClientFromReader(reader io.Reader) (*Client, error) {
+ c := new(Client)
+
+ b, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return nil, err
+ }
+
+ err = json.Unmarshal(b, c)
+ if err != nil {
+ return nil, err
+ }
+ if c.config.CertFile == "" {
+ c.initHTTPClient()
+ } else {
+ err = c.initHTTPSClient(c.config.CertFile, c.config.KeyFile)
+ }
+
+ if err != nil {
+ return nil, err
+ }
+
+ for _, caCert := range c.config.CaCertFile {
+ if err := c.AddRootCA(caCert); err != nil {
+ return nil, err
+ }
+ }
+
+ return c, nil
+}
+
+// Override the Client's HTTP Transport object
+func (c *Client) SetTransport(tr *http.Transport) {
+ c.httpClient.Transport = tr
+ c.transport = tr
+}
+
+func (c *Client) SetCredentials(username, password string) {
+ c.credentials = &credentials{username, password}
+}
+
+func (c *Client) Close() {
+ c.transport.DisableKeepAlives = true
+ c.transport.CloseIdleConnections()
+}
+
+// initHTTPClient initializes a HTTP client for etcd client
+func (c *Client) initHTTPClient() {
+ c.transport = &http.Transport{
+ Dial: c.DefaultDial,
+ TLSClientConfig: &tls.Config{
+ InsecureSkipVerify: true,
+ },
+ }
+ c.httpClient = &http.Client{Transport: c.transport}
+}
+
+// initHTTPClient initializes a HTTPS client for etcd client
+func (c *Client) initHTTPSClient(cert, key string) error {
+ if cert == "" || key == "" {
+ return errors.New("Require both cert and key path")
+ }
+
+ tlsCert, err := tls.LoadX509KeyPair(cert, key)
+ if err != nil {
+ return err
+ }
+
+ tlsConfig := &tls.Config{
+ Certificates: []tls.Certificate{tlsCert},
+ InsecureSkipVerify: true,
+ }
+
+ c.transport = &http.Transport{
+ TLSClientConfig: tlsConfig,
+ Dial: c.DefaultDial,
+ }
+
+ c.httpClient = &http.Client{Transport: c.transport}
+ return nil
+}
+
+// SetPersistence sets a writer to which the config will be
+// written every time it's changed.
+func (c *Client) SetPersistence(writer io.Writer) {
+ c.persistence = writer
+}
+
+// SetConsistency changes the consistency level of the client.
+//
+// When consistency is set to STRONG_CONSISTENCY, all requests,
+// including GET, are sent to the leader. This means that, assuming
+// the absence of leader failures, GET requests are guaranteed to see
+// the changes made by previous requests.
+//
+// When consistency is set to WEAK_CONSISTENCY, other requests
+// are still sent to the leader, but GET requests are sent to a
+// random server from the server pool. This reduces the read
+// load on the leader, but it's not guaranteed that the GET requests
+// will see changes made by previous requests (they might have not
+// yet been committed on non-leader servers).
+func (c *Client) SetConsistency(consistency string) error {
+ if !(consistency == STRONG_CONSISTENCY || consistency == WEAK_CONSISTENCY) {
+ return errors.New("The argument must be either STRONG_CONSISTENCY or WEAK_CONSISTENCY.")
+ }
+ c.config.Consistency = consistency
+ return nil
+}
+
+// Sets the DialTimeout value
+func (c *Client) SetDialTimeout(d time.Duration) {
+ c.config.DialTimeout = d
+}
+
+// AddRootCA adds a root CA cert for the etcd client
+func (c *Client) AddRootCA(caCert string) error {
+ if c.httpClient == nil {
+ return errors.New("Client has not been initialized yet!")
+ }
+
+ certBytes, err := ioutil.ReadFile(caCert)
+ if err != nil {
+ return err
+ }
+
+ tr, ok := c.httpClient.Transport.(*http.Transport)
+
+ if !ok {
+ panic("AddRootCA(): Transport type assert should not fail")
+ }
+
+ if tr.TLSClientConfig.RootCAs == nil {
+ caCertPool := x509.NewCertPool()
+ ok = caCertPool.AppendCertsFromPEM(certBytes)
+ if ok {
+ tr.TLSClientConfig.RootCAs = caCertPool
+ }
+ tr.TLSClientConfig.InsecureSkipVerify = false
+ } else {
+ ok = tr.TLSClientConfig.RootCAs.AppendCertsFromPEM(certBytes)
+ }
+
+ if !ok {
+ err = errors.New("Unable to load caCert")
+ }
+
+ c.config.CaCertFile = append(c.config.CaCertFile, caCert)
+ c.saveConfig()
+
+ return err
+}
+
+// SetCluster updates cluster information using the given machine list.
+func (c *Client) SetCluster(machines []string) bool {
+ success := c.internalSyncCluster(machines)
+ return success
+}
+
+func (c *Client) GetCluster() []string {
+ return c.cluster.Machines
+}
+
+// SyncCluster updates the cluster information using the internal machine list.
+// If no members are found, the intenral machine list is left untouched.
+func (c *Client) SyncCluster() bool {
+ return c.internalSyncCluster(c.cluster.Machines)
+}
+
+// internalSyncCluster syncs cluster information using the given machine list.
+func (c *Client) internalSyncCluster(machines []string) bool {
+ // comma-separated list of machines in the cluster.
+ members := ""
+
+ for _, machine := range machines {
+ httpPath := c.createHttpPath(machine, path.Join(version, "members"))
+ resp, err := c.httpClient.Get(httpPath)
+ if err != nil {
+ // try another machine in the cluster
+ continue
+ }
+
+ if resp.StatusCode != http.StatusOK { // fall-back to old endpoint
+ httpPath := c.createHttpPath(machine, path.Join(version, "machines"))
+ resp, err := c.httpClient.Get(httpPath)
+ if err != nil {
+ // try another machine in the cluster
+ continue
+ }
+ b, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ // try another machine in the cluster
+ continue
+ }
+ members = string(b)
+ } else {
+ b, err := ioutil.ReadAll(resp.Body)
+ resp.Body.Close()
+ if err != nil {
+ // try another machine in the cluster
+ continue
+ }
+
+ var mCollection memberCollection
+ if err := json.Unmarshal(b, &mCollection); err != nil {
+ // try another machine
+ continue
+ }
+
+ urls := make([]string, 0)
+ for _, m := range mCollection {
+ urls = append(urls, m.ClientURLs...)
+ }
+
+ members = strings.Join(urls, ",")
+ }
+
+ // We should never do an empty cluster update.
+ if members == "" {
+ continue
+ }
+
+ // update Machines List
+ c.cluster.updateFromStr(members)
+ logger.Debug("sync.machines ", c.cluster.Machines)
+ c.saveConfig()
+ return true
+ }
+
+ return false
+}
+
+// createHttpPath creates a complete HTTP URL.
+// serverName should contain both the host name and a port number, if any.
+func (c *Client) createHttpPath(serverName string, _path string) string {
+ u, err := url.Parse(serverName)
+ if err != nil {
+ panic(err)
+ }
+
+ u.Path = path.Join(u.Path, _path)
+
+ if u.Scheme == "" {
+ u.Scheme = "http"
+ }
+ return u.String()
+}
+
+// DefaultDial attempts to open a TCP connection to the provided address, explicitly
+// enabling keep-alives with a one-second interval.
+func (c *Client) DefaultDial(network, addr string) (net.Conn, error) {
+ dialer := net.Dialer{
+ Timeout: c.config.DialTimeout,
+ KeepAlive: time.Second,
+ }
+
+ return dialer.Dial(network, addr)
+}
+
+func (c *Client) OpenCURL() {
+ c.cURLch = make(chan string, defaultBufferSize)
+}
+
+func (c *Client) CloseCURL() {
+ c.cURLch = nil
+}
+
+func (c *Client) sendCURL(command string) {
+ go func() {
+ select {
+ case c.cURLch <- command:
+ default:
+ }
+ }()
+}
+
+func (c *Client) RecvCURL() string {
+ return <-c.cURLch
+}
+
+// saveConfig saves the current config using c.persistence.
+func (c *Client) saveConfig() error {
+ if c.persistence != nil {
+ b, err := json.Marshal(c)
+ if err != nil {
+ return err
+ }
+
+ _, err = c.persistence.Write(b)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// MarshalJSON implements the Marshaller interface
+// as defined by the standard JSON package.
+func (c *Client) MarshalJSON() ([]byte, error) {
+ b, err := json.Marshal(struct {
+ Config Config `json:"config"`
+ Cluster *Cluster `json:"cluster"`
+ }{
+ Config: c.config,
+ Cluster: c.cluster,
+ })
+
+ if err != nil {
+ return nil, err
+ }
+
+ return b, nil
+}
+
+// UnmarshalJSON implements the Unmarshaller interface
+// as defined by the standard JSON package.
+func (c *Client) UnmarshalJSON(b []byte) error {
+ temp := struct {
+ Config Config `json:"config"`
+ Cluster *Cluster `json:"cluster"`
+ }{}
+ err := json.Unmarshal(b, &temp)
+ if err != nil {
+ return err
+ }
+
+ c.cluster = temp.Cluster
+ c.config = temp.Config
+ return nil
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/client_test.go b/vendor/github.com/coreos/go-etcd/etcd/client_test.go
new file mode 100644
index 000000000..4720d8d69
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/client_test.go
@@ -0,0 +1,108 @@
+package etcd
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+ "net/url"
+ "os"
+ "testing"
+)
+
+// To pass this test, we need to create a cluster of 3 machines
+// The server should be listening on localhost:4001, 4002, 4003
+func TestSync(t *testing.T) {
+ fmt.Println("Make sure there are three nodes at 0.0.0.0:4001-4003")
+
+ // Explicit trailing slash to ensure this doesn't reproduce:
+ // https://github.com/coreos/go-etcd/issues/82
+ c := NewClient([]string{"http://127.0.0.1:4001/"})
+
+ success := c.SyncCluster()
+ if !success {
+ t.Fatal("cannot sync machines")
+ }
+
+ for _, m := range c.GetCluster() {
+ u, err := url.Parse(m)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if u.Scheme != "http" {
+ t.Fatal("scheme must be http")
+ }
+
+ host, _, err := net.SplitHostPort(u.Host)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if host != "localhost" {
+ t.Fatal("Host must be localhost")
+ }
+ }
+
+ badMachines := []string{"abc", "edef"}
+
+ success = c.SetCluster(badMachines)
+
+ if success {
+ t.Fatal("should not sync on bad machines")
+ }
+
+ goodMachines := []string{"127.0.0.1:4002"}
+
+ success = c.SetCluster(goodMachines)
+
+ if !success {
+ t.Fatal("cannot sync machines")
+ } else {
+ fmt.Println(c.cluster.Machines)
+ }
+
+}
+
+func TestPersistence(t *testing.T) {
+ c := NewClient(nil)
+ c.SyncCluster()
+
+ fo, err := os.Create("config.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer func() {
+ if err := fo.Close(); err != nil {
+ panic(err)
+ }
+ }()
+
+ c.SetPersistence(fo)
+ err = c.saveConfig()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ c2, err := NewClientFromFile("config.json")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify that the two clients have the same config
+ b1, _ := json.Marshal(c)
+ b2, _ := json.Marshal(c2)
+
+ if string(b1) != string(b2) {
+ t.Fatalf("The two configs should be equal!")
+ }
+}
+
+func TestClientRetry(t *testing.T) {
+ c := NewClient([]string{"http://strange", "http://127.0.0.1:4001"})
+ // use first endpoint as the picked url
+ c.cluster.picked = 0
+ if _, err := c.Set("foo", "bar", 5); err != nil {
+ t.Fatal(err)
+ }
+ if _, err := c.Delete("foo", true); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/cluster.go b/vendor/github.com/coreos/go-etcd/etcd/cluster.go
new file mode 100644
index 000000000..d0461e17a
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/cluster.go
@@ -0,0 +1,54 @@
+package etcd
+
+import (
+ "math/rand"
+ "strings"
+ "sync"
+)
+
+type Cluster struct {
+ Leader string `json:"leader"`
+ Machines []string `json:"machines"`
+ picked int
+ mu sync.RWMutex
+}
+
+func NewCluster(machines []string) *Cluster {
+ // if an empty slice was sent in then just assume HTTP 4001 on localhost
+ if len(machines) == 0 {
+ machines = []string{"http://127.0.0.1:4001"}
+ }
+
+ machines = shuffleStringSlice(machines)
+ logger.Debug("Shuffle cluster machines", machines)
+ // default leader and machines
+ return &Cluster{
+ Leader: "",
+ Machines: machines,
+ picked: rand.Intn(len(machines)),
+ }
+}
+
+func (cl *Cluster) failure() {
+ cl.mu.Lock()
+ defer cl.mu.Unlock()
+ cl.picked = (cl.picked + 1) % len(cl.Machines)
+}
+
+func (cl *Cluster) pick() string {
+ cl.mu.Lock()
+ defer cl.mu.Unlock()
+ return cl.Machines[cl.picked]
+}
+
+func (cl *Cluster) updateFromStr(machines string) {
+ cl.mu.Lock()
+ defer cl.mu.Unlock()
+
+ cl.Machines = strings.Split(machines, ",")
+ for i := range cl.Machines {
+ cl.Machines[i] = strings.TrimSpace(cl.Machines[i])
+ }
+ cl.Machines = shuffleStringSlice(cl.Machines)
+ cl.picked = rand.Intn(len(cl.Machines))
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/compare_and_delete.go b/vendor/github.com/coreos/go-etcd/etcd/compare_and_delete.go
new file mode 100644
index 000000000..11131bb76
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/compare_and_delete.go
@@ -0,0 +1,34 @@
+package etcd
+
+import "fmt"
+
+func (c *Client) CompareAndDelete(key string, prevValue string, prevIndex uint64) (*Response, error) {
+ raw, err := c.RawCompareAndDelete(key, prevValue, prevIndex)
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+func (c *Client) RawCompareAndDelete(key string, prevValue string, prevIndex uint64) (*RawResponse, error) {
+ if prevValue == "" && prevIndex == 0 {
+ return nil, fmt.Errorf("You must give either prevValue or prevIndex.")
+ }
+
+ options := Options{}
+ if prevValue != "" {
+ options["prevValue"] = prevValue
+ }
+ if prevIndex != 0 {
+ options["prevIndex"] = prevIndex
+ }
+
+ raw, err := c.delete(key, options)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw, err
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/compare_and_delete_test.go b/vendor/github.com/coreos/go-etcd/etcd/compare_and_delete_test.go
new file mode 100644
index 000000000..223e50f29
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/compare_and_delete_test.go
@@ -0,0 +1,46 @@
+package etcd
+
+import (
+ "testing"
+)
+
+func TestCompareAndDelete(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ }()
+
+ c.Set("foo", "bar", 5)
+
+ // This should succeed an correct prevValue
+ resp, err := c.CompareAndDelete("foo", "bar", 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+ t.Fatalf("CompareAndDelete 1 prevNode failed: %#v", resp)
+ }
+
+ resp, _ = c.Set("foo", "bar", 5)
+ // This should fail because it gives an incorrect prevValue
+ _, err = c.CompareAndDelete("foo", "xxx", 0)
+ if err == nil {
+ t.Fatalf("CompareAndDelete 2 should have failed. The response is: %#v", resp)
+ }
+
+ // This should succeed because it gives an correct prevIndex
+ resp, err = c.CompareAndDelete("foo", "", resp.Node.ModifiedIndex)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+ t.Fatalf("CompareAndSwap 3 prevNode failed: %#v", resp)
+ }
+
+ c.Set("foo", "bar", 5)
+ // This should fail because it gives an incorrect prevIndex
+ resp, err = c.CompareAndDelete("foo", "", 29817514)
+ if err == nil {
+ t.Fatalf("CompareAndDelete 4 should have failed. The response is: %#v", resp)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/compare_and_swap.go b/vendor/github.com/coreos/go-etcd/etcd/compare_and_swap.go
new file mode 100644
index 000000000..bb4f90643
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/compare_and_swap.go
@@ -0,0 +1,36 @@
+package etcd
+
+import "fmt"
+
+func (c *Client) CompareAndSwap(key string, value string, ttl uint64,
+ prevValue string, prevIndex uint64) (*Response, error) {
+ raw, err := c.RawCompareAndSwap(key, value, ttl, prevValue, prevIndex)
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+func (c *Client) RawCompareAndSwap(key string, value string, ttl uint64,
+ prevValue string, prevIndex uint64) (*RawResponse, error) {
+ if prevValue == "" && prevIndex == 0 {
+ return nil, fmt.Errorf("You must give either prevValue or prevIndex.")
+ }
+
+ options := Options{}
+ if prevValue != "" {
+ options["prevValue"] = prevValue
+ }
+ if prevIndex != 0 {
+ options["prevIndex"] = prevIndex
+ }
+
+ raw, err := c.put(key, value, ttl, options)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw, err
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go b/vendor/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
new file mode 100644
index 000000000..14a1b00f5
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/compare_and_swap_test.go
@@ -0,0 +1,57 @@
+package etcd
+
+import (
+ "testing"
+)
+
+func TestCompareAndSwap(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ }()
+
+ c.Set("foo", "bar", 5)
+
+ // This should succeed
+ resp, err := c.CompareAndSwap("foo", "bar2", 5, "bar", 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Value == "bar2" && resp.Node.Key == "/foo" && resp.Node.TTL == 5) {
+ t.Fatalf("CompareAndSwap 1 failed: %#v", resp)
+ }
+
+ if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+ t.Fatalf("CompareAndSwap 1 prevNode failed: %#v", resp)
+ }
+
+ // This should fail because it gives an incorrect prevValue
+ resp, err = c.CompareAndSwap("foo", "bar3", 5, "xxx", 0)
+ if err == nil {
+ t.Fatalf("CompareAndSwap 2 should have failed. The response is: %#v", resp)
+ }
+
+ resp, err = c.Set("foo", "bar", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // This should succeed
+ resp, err = c.CompareAndSwap("foo", "bar2", 5, "", resp.Node.ModifiedIndex)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Value == "bar2" && resp.Node.Key == "/foo" && resp.Node.TTL == 5) {
+ t.Fatalf("CompareAndSwap 3 failed: %#v", resp)
+ }
+
+ if !(resp.PrevNode.Value == "bar" && resp.PrevNode.Key == "/foo" && resp.PrevNode.TTL == 5) {
+ t.Fatalf("CompareAndSwap 3 prevNode failed: %#v", resp)
+ }
+
+ // This should fail because it gives an incorrect prevIndex
+ resp, err = c.CompareAndSwap("foo", "bar3", 5, "", 29817514)
+ if err == nil {
+ t.Fatalf("CompareAndSwap 4 should have failed. The response is: %#v", resp)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/debug.go b/vendor/github.com/coreos/go-etcd/etcd/debug.go
new file mode 100644
index 000000000..0f777886b
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/debug.go
@@ -0,0 +1,55 @@
+package etcd
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "strings"
+)
+
+var logger *etcdLogger
+
+func SetLogger(l *log.Logger) {
+ logger = &etcdLogger{l}
+}
+
+func GetLogger() *log.Logger {
+ return logger.log
+}
+
+type etcdLogger struct {
+ log *log.Logger
+}
+
+func (p *etcdLogger) Debug(args ...interface{}) {
+ msg := "DEBUG: " + fmt.Sprint(args...)
+ p.log.Println(msg)
+}
+
+func (p *etcdLogger) Debugf(f string, args ...interface{}) {
+ msg := "DEBUG: " + fmt.Sprintf(f, args...)
+ // Append newline if necessary
+ if !strings.HasSuffix(msg, "\n") {
+ msg = msg + "\n"
+ }
+ p.log.Print(msg)
+}
+
+func (p *etcdLogger) Warning(args ...interface{}) {
+ msg := "WARNING: " + fmt.Sprint(args...)
+ p.log.Println(msg)
+}
+
+func (p *etcdLogger) Warningf(f string, args ...interface{}) {
+ msg := "WARNING: " + fmt.Sprintf(f, args...)
+ // Append newline if necessary
+ if !strings.HasSuffix(msg, "\n") {
+ msg = msg + "\n"
+ }
+ p.log.Print(msg)
+}
+
+func init() {
+ // Default logger uses the go default log.
+ SetLogger(log.New(ioutil.Discard, "go-etcd", log.LstdFlags))
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/debug_test.go b/vendor/github.com/coreos/go-etcd/etcd/debug_test.go
new file mode 100644
index 000000000..97f6d1110
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/debug_test.go
@@ -0,0 +1,28 @@
+package etcd
+
+import (
+ "testing"
+)
+
+type Foo struct{}
+type Bar struct {
+ one string
+ two int
+}
+
+// Tests that logs don't panic with arbitrary interfaces
+func TestDebug(t *testing.T) {
+ f := &Foo{}
+ b := &Bar{"asfd", 3}
+ for _, test := range []interface{}{
+ 1234,
+ "asdf",
+ f,
+ b,
+ } {
+ logger.Debug(test)
+ logger.Debugf("something, %s", test)
+ logger.Warning(test)
+ logger.Warningf("something, %s", test)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/delete.go b/vendor/github.com/coreos/go-etcd/etcd/delete.go
new file mode 100644
index 000000000..b37accd7d
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/delete.go
@@ -0,0 +1,40 @@
+package etcd
+
+// Delete deletes the given key.
+//
+// When recursive set to false, if the key points to a
+// directory the method will fail.
+//
+// When recursive set to true, if the key points to a file,
+// the file will be deleted; if the key points to a directory,
+// then everything under the directory (including all child directories)
+// will be deleted.
+func (c *Client) Delete(key string, recursive bool) (*Response, error) {
+ raw, err := c.RawDelete(key, recursive, false)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// DeleteDir deletes an empty directory or a key value pair
+func (c *Client) DeleteDir(key string) (*Response, error) {
+ raw, err := c.RawDelete(key, false, true)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+func (c *Client) RawDelete(key string, recursive bool, dir bool) (*RawResponse, error) {
+ ops := Options{
+ "recursive": recursive,
+ "dir": dir,
+ }
+
+ return c.delete(key, ops)
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/delete_test.go b/vendor/github.com/coreos/go-etcd/etcd/delete_test.go
new file mode 100644
index 000000000..590497155
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/delete_test.go
@@ -0,0 +1,81 @@
+package etcd
+
+import (
+ "testing"
+)
+
+func TestDelete(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ }()
+
+ c.Set("foo", "bar", 5)
+ resp, err := c.Delete("foo", false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Node.Value == "") {
+ t.Fatalf("Delete failed with %s", resp.Node.Value)
+ }
+
+ if !(resp.PrevNode.Value == "bar") {
+ t.Fatalf("Delete PrevNode failed with %s", resp.Node.Value)
+ }
+
+ resp, err = c.Delete("foo", false)
+ if err == nil {
+ t.Fatalf("Delete should have failed because the key foo did not exist. "+
+ "The response was: %v", resp)
+ }
+}
+
+func TestDeleteAll(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ c.Delete("fooDir", true)
+ }()
+
+ c.SetDir("foo", 5)
+ // test delete an empty dir
+ resp, err := c.DeleteDir("foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Node.Value == "") {
+ t.Fatalf("DeleteAll 1 failed: %#v", resp)
+ }
+
+ if !(resp.PrevNode.Dir == true && resp.PrevNode.Value == "") {
+ t.Fatalf("DeleteAll 1 PrevNode failed: %#v", resp)
+ }
+
+ c.CreateDir("fooDir", 5)
+ c.Set("fooDir/foo", "bar", 5)
+ _, err = c.DeleteDir("fooDir")
+ if err == nil {
+ t.Fatal("should not able to delete a non-empty dir with deletedir")
+ }
+
+ resp, err = c.Delete("fooDir", true)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Node.Value == "") {
+ t.Fatalf("DeleteAll 2 failed: %#v", resp)
+ }
+
+ if !(resp.PrevNode.Dir == true && resp.PrevNode.Value == "") {
+ t.Fatalf("DeleteAll 2 PrevNode failed: %#v", resp)
+ }
+
+ resp, err = c.Delete("foo", true)
+ if err == nil {
+ t.Fatalf("DeleteAll should have failed because the key foo did not exist. "+
+ "The response was: %v", resp)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/error.go b/vendor/github.com/coreos/go-etcd/etcd/error.go
new file mode 100644
index 000000000..66dca54b5
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/error.go
@@ -0,0 +1,49 @@
+package etcd
+
+import (
+ "encoding/json"
+ "fmt"
+)
+
+const (
+ ErrCodeEtcdNotReachable = 501
+ ErrCodeUnhandledHTTPStatus = 502
+)
+
+var (
+ errorMap = map[int]string{
+ ErrCodeEtcdNotReachable: "All the given peers are not reachable",
+ }
+)
+
+type EtcdError struct {
+ ErrorCode int `json:"errorCode"`
+ Message string `json:"message"`
+ Cause string `json:"cause,omitempty"`
+ Index uint64 `json:"index"`
+}
+
+func (e EtcdError) Error() string {
+ return fmt.Sprintf("%v: %v (%v) [%v]", e.ErrorCode, e.Message, e.Cause, e.Index)
+}
+
+func newError(errorCode int, cause string, index uint64) *EtcdError {
+ return &EtcdError{
+ ErrorCode: errorCode,
+ Message: errorMap[errorCode],
+ Cause: cause,
+ Index: index,
+ }
+}
+
+func handleError(b []byte) error {
+ etcdErr := new(EtcdError)
+
+ err := json.Unmarshal(b, etcdErr)
+ if err != nil {
+ logger.Warningf("cannot unmarshal etcd error: %v", err)
+ return err
+ }
+
+ return etcdErr
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/get.go b/vendor/github.com/coreos/go-etcd/etcd/get.go
new file mode 100644
index 000000000..09fe641c2
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/get.go
@@ -0,0 +1,32 @@
+package etcd
+
+// Get gets the file or directory associated with the given key.
+// If the key points to a directory, files and directories under
+// it will be returned in sorted or unsorted order, depending on
+// the sort flag.
+// If recursive is set to false, contents under child directories
+// will not be returned.
+// If recursive is set to true, all the contents will be returned.
+func (c *Client) Get(key string, sort, recursive bool) (*Response, error) {
+ raw, err := c.RawGet(key, sort, recursive)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+func (c *Client) RawGet(key string, sort, recursive bool) (*RawResponse, error) {
+ var q bool
+ if c.config.Consistency == STRONG_CONSISTENCY {
+ q = true
+ }
+ ops := Options{
+ "recursive": recursive,
+ "sorted": sort,
+ "quorum": q,
+ }
+
+ return c.get(key, ops)
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/get_test.go b/vendor/github.com/coreos/go-etcd/etcd/get_test.go
new file mode 100644
index 000000000..279c4e26f
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/get_test.go
@@ -0,0 +1,131 @@
+package etcd
+
+import (
+ "reflect"
+ "testing"
+)
+
+// cleanNode scrubs Expiration, ModifiedIndex and CreatedIndex of a node.
+func cleanNode(n *Node) {
+ n.Expiration = nil
+ n.ModifiedIndex = 0
+ n.CreatedIndex = 0
+}
+
+// cleanResult scrubs a result object two levels deep of Expiration,
+// ModifiedIndex and CreatedIndex.
+func cleanResult(result *Response) {
+ // TODO(philips): make this recursive.
+ cleanNode(result.Node)
+ for i, _ := range result.Node.Nodes {
+ cleanNode(result.Node.Nodes[i])
+ for j, _ := range result.Node.Nodes[i].Nodes {
+ cleanNode(result.Node.Nodes[i].Nodes[j])
+ }
+ }
+}
+
+func TestGet(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ }()
+
+ c.Set("foo", "bar", 5)
+
+ result, err := c.Get("foo", false, false)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if result.Node.Key != "/foo" || result.Node.Value != "bar" {
+ t.Fatalf("Get failed with %s %s %v", result.Node.Key, result.Node.Value, result.Node.TTL)
+ }
+
+ result, err = c.Get("goo", false, false)
+ if err == nil {
+ t.Fatalf("should not be able to get non-exist key")
+ }
+}
+
+func TestGetAll(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("fooDir", true)
+ }()
+
+ c.CreateDir("fooDir", 5)
+ c.Set("fooDir/k0", "v0", 5)
+ c.Set("fooDir/k1", "v1", 5)
+
+ // Return kv-pairs in sorted order
+ result, err := c.Get("fooDir", true, false)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := Nodes{
+ &Node{
+ Key: "/fooDir/k0",
+ Value: "v0",
+ TTL: 5,
+ },
+ &Node{
+ Key: "/fooDir/k1",
+ Value: "v1",
+ TTL: 5,
+ },
+ }
+
+ cleanResult(result)
+
+ if !reflect.DeepEqual(result.Node.Nodes, expected) {
+ t.Fatalf("(actual) %v != (expected) %v", result.Node.Nodes, expected)
+ }
+
+ // Test the `recursive` option
+ c.CreateDir("fooDir/childDir", 5)
+ c.Set("fooDir/childDir/k2", "v2", 5)
+
+ // Return kv-pairs in sorted order
+ result, err = c.Get("fooDir", true, true)
+
+ cleanResult(result)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected = Nodes{
+ &Node{
+ Key: "/fooDir/childDir",
+ Dir: true,
+ Nodes: Nodes{
+ &Node{
+ Key: "/fooDir/childDir/k2",
+ Value: "v2",
+ TTL: 5,
+ },
+ },
+ TTL: 5,
+ },
+ &Node{
+ Key: "/fooDir/k0",
+ Value: "v0",
+ TTL: 5,
+ },
+ &Node{
+ Key: "/fooDir/k1",
+ Value: "v1",
+ TTL: 5,
+ },
+ }
+
+ cleanResult(result)
+
+ if !reflect.DeepEqual(result.Node.Nodes, expected) {
+ t.Fatalf("(actual) %v != (expected) %v", result.Node.Nodes, expected)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/member.go b/vendor/github.com/coreos/go-etcd/etcd/member.go
new file mode 100644
index 000000000..5b13b28e1
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/member.go
@@ -0,0 +1,30 @@
+package etcd
+
+import "encoding/json"
+
+type Member struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ PeerURLs []string `json:"peerURLs"`
+ ClientURLs []string `json:"clientURLs"`
+}
+
+type memberCollection []Member
+
+func (c *memberCollection) UnmarshalJSON(data []byte) error {
+ d := struct {
+ Members []Member
+ }{}
+
+ if err := json.Unmarshal(data, &d); err != nil {
+ return err
+ }
+
+ if d.Members == nil {
+ *c = make([]Member, 0)
+ return nil
+ }
+
+ *c = d.Members
+ return nil
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/member_test.go b/vendor/github.com/coreos/go-etcd/etcd/member_test.go
new file mode 100644
index 000000000..53ebdd4bf
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/member_test.go
@@ -0,0 +1,71 @@
+package etcd
+
+import (
+ "encoding/json"
+ "reflect"
+ "testing"
+)
+
+func TestMemberCollectionUnmarshal(t *testing.T) {
+ tests := []struct {
+ body []byte
+ want memberCollection
+ }{
+ {
+ body: []byte(`{"members":[]}`),
+ want: memberCollection([]Member{}),
+ },
+ {
+ body: []byte(`{"members":[{"id":"2745e2525fce8fe","peerURLs":["http://127.0.0.1:7003"],"name":"node3","clientURLs":["http://127.0.0.1:4003"]},{"id":"42134f434382925","peerURLs":["http://127.0.0.1:2380","http://127.0.0.1:7001"],"name":"node1","clientURLs":["http://127.0.0.1:2379","http://127.0.0.1:4001"]},{"id":"94088180e21eb87b","peerURLs":["http://127.0.0.1:7002"],"name":"node2","clientURLs":["http://127.0.0.1:4002"]}]}`),
+ want: memberCollection(
+ []Member{
+ {
+ ID: "2745e2525fce8fe",
+ Name: "node3",
+ PeerURLs: []string{
+ "http://127.0.0.1:7003",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:4003",
+ },
+ },
+ {
+ ID: "42134f434382925",
+ Name: "node1",
+ PeerURLs: []string{
+ "http://127.0.0.1:2380",
+ "http://127.0.0.1:7001",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:2379",
+ "http://127.0.0.1:4001",
+ },
+ },
+ {
+ ID: "94088180e21eb87b",
+ Name: "node2",
+ PeerURLs: []string{
+ "http://127.0.0.1:7002",
+ },
+ ClientURLs: []string{
+ "http://127.0.0.1:4002",
+ },
+ },
+ },
+ ),
+ },
+ }
+
+ for i, tt := range tests {
+ var got memberCollection
+ err := json.Unmarshal(tt.body, &got)
+ if err != nil {
+ t.Errorf("#%d: unexpected error: %v", i, err)
+ continue
+ }
+
+ if !reflect.DeepEqual(tt.want, got) {
+ t.Errorf("#%d: incorrect output: want=%#v, got=%#v", i, tt.want, got)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/options.go b/vendor/github.com/coreos/go-etcd/etcd/options.go
new file mode 100644
index 000000000..d21c96f08
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/options.go
@@ -0,0 +1,72 @@
+package etcd
+
+import (
+ "fmt"
+ "net/url"
+ "reflect"
+)
+
+type Options map[string]interface{}
+
+// An internally-used data structure that represents a mapping
+// between valid options and their kinds
+type validOptions map[string]reflect.Kind
+
+// Valid options for GET, PUT, POST, DELETE
+// Using CAPITALIZED_UNDERSCORE to emphasize that these
+// values are meant to be used as constants.
+var (
+ VALID_GET_OPTIONS = validOptions{
+ "recursive": reflect.Bool,
+ "quorum": reflect.Bool,
+ "sorted": reflect.Bool,
+ "wait": reflect.Bool,
+ "waitIndex": reflect.Uint64,
+ }
+
+ VALID_PUT_OPTIONS = validOptions{
+ "prevValue": reflect.String,
+ "prevIndex": reflect.Uint64,
+ "prevExist": reflect.Bool,
+ "dir": reflect.Bool,
+ }
+
+ VALID_POST_OPTIONS = validOptions{}
+
+ VALID_DELETE_OPTIONS = validOptions{
+ "recursive": reflect.Bool,
+ "dir": reflect.Bool,
+ "prevValue": reflect.String,
+ "prevIndex": reflect.Uint64,
+ }
+)
+
+// Convert options to a string of HTML parameters
+func (ops Options) toParameters(validOps validOptions) (string, error) {
+ p := "?"
+ values := url.Values{}
+
+ if ops == nil {
+ return "", nil
+ }
+
+ for k, v := range ops {
+ // Check if the given option is valid (that it exists)
+ kind := validOps[k]
+ if kind == reflect.Invalid {
+ return "", fmt.Errorf("Invalid option: %v", k)
+ }
+
+ // Check if the given option is of the valid type
+ t := reflect.TypeOf(v)
+ if kind != t.Kind() {
+ return "", fmt.Errorf("Option %s should be of %v kind, not of %v kind.",
+ k, kind, t.Kind())
+ }
+
+ values.Set(k, fmt.Sprintf("%v", v))
+ }
+
+ p += values.Encode()
+ return p, nil
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/requests.go b/vendor/github.com/coreos/go-etcd/etcd/requests.go
new file mode 100644
index 000000000..8f720f6f4
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/requests.go
@@ -0,0 +1,403 @@
+package etcd
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+ "sync"
+ "time"
+)
+
+// Errors introduced by handling requests
+var (
+ ErrRequestCancelled = errors.New("sending request is cancelled")
+)
+
+type RawRequest struct {
+ Method string
+ RelativePath string
+ Values url.Values
+ Cancel <-chan bool
+}
+
+// NewRawRequest returns a new RawRequest
+func NewRawRequest(method, relativePath string, values url.Values, cancel <-chan bool) *RawRequest {
+ return &RawRequest{
+ Method: method,
+ RelativePath: relativePath,
+ Values: values,
+ Cancel: cancel,
+ }
+}
+
+// getCancelable issues a cancelable GET request
+func (c *Client) getCancelable(key string, options Options,
+ cancel <-chan bool) (*RawResponse, error) {
+ logger.Debugf("get %s [%s]", key, c.cluster.pick())
+ p := keyToPath(key)
+
+ str, err := options.toParameters(VALID_GET_OPTIONS)
+ if err != nil {
+ return nil, err
+ }
+ p += str
+
+ req := NewRawRequest("GET", p, nil, cancel)
+ resp, err := c.SendRequest(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return resp, nil
+}
+
+// get issues a GET request
+func (c *Client) get(key string, options Options) (*RawResponse, error) {
+ return c.getCancelable(key, options, nil)
+}
+
+// put issues a PUT request
+func (c *Client) put(key string, value string, ttl uint64,
+ options Options) (*RawResponse, error) {
+
+ logger.Debugf("put %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.pick())
+ p := keyToPath(key)
+
+ str, err := options.toParameters(VALID_PUT_OPTIONS)
+ if err != nil {
+ return nil, err
+ }
+ p += str
+
+ req := NewRawRequest("PUT", p, buildValues(value, ttl), nil)
+ resp, err := c.SendRequest(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return resp, nil
+}
+
+// post issues a POST request
+func (c *Client) post(key string, value string, ttl uint64) (*RawResponse, error) {
+ logger.Debugf("post %s, %s, ttl: %d, [%s]", key, value, ttl, c.cluster.pick())
+ p := keyToPath(key)
+
+ req := NewRawRequest("POST", p, buildValues(value, ttl), nil)
+ resp, err := c.SendRequest(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return resp, nil
+}
+
+// delete issues a DELETE request
+func (c *Client) delete(key string, options Options) (*RawResponse, error) {
+ logger.Debugf("delete %s [%s]", key, c.cluster.pick())
+ p := keyToPath(key)
+
+ str, err := options.toParameters(VALID_DELETE_OPTIONS)
+ if err != nil {
+ return nil, err
+ }
+ p += str
+
+ req := NewRawRequest("DELETE", p, nil, nil)
+ resp, err := c.SendRequest(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return resp, nil
+}
+
+// SendRequest sends a HTTP request and returns a Response as defined by etcd
+func (c *Client) SendRequest(rr *RawRequest) (*RawResponse, error) {
+ var req *http.Request
+ var resp *http.Response
+ var httpPath string
+ var err error
+ var respBody []byte
+
+ var numReqs = 1
+
+ checkRetry := c.CheckRetry
+ if checkRetry == nil {
+ checkRetry = DefaultCheckRetry
+ }
+
+ cancelled := make(chan bool, 1)
+ reqLock := new(sync.Mutex)
+
+ if rr.Cancel != nil {
+ cancelRoutine := make(chan bool)
+ defer close(cancelRoutine)
+
+ go func() {
+ select {
+ case <-rr.Cancel:
+ cancelled <- true
+ logger.Debug("send.request is cancelled")
+ case <-cancelRoutine:
+ return
+ }
+
+ // Repeat canceling request until this thread is stopped
+ // because we have no idea about whether it succeeds.
+ for {
+ reqLock.Lock()
+ c.httpClient.Transport.(*http.Transport).CancelRequest(req)
+ reqLock.Unlock()
+
+ select {
+ case <-time.After(100 * time.Millisecond):
+ case <-cancelRoutine:
+ return
+ }
+ }
+ }()
+ }
+
+ // If we connect to a follower and consistency is required, retry until
+ // we connect to a leader
+ sleep := 25 * time.Millisecond
+ maxSleep := time.Second
+
+ for attempt := 0; ; attempt++ {
+ if attempt > 0 {
+ select {
+ case <-cancelled:
+ return nil, ErrRequestCancelled
+ case <-time.After(sleep):
+ sleep = sleep * 2
+ if sleep > maxSleep {
+ sleep = maxSleep
+ }
+ }
+ }
+
+ logger.Debug("Connecting to etcd: attempt ", attempt+1, " for ", rr.RelativePath)
+
+ // get httpPath if not set
+ if httpPath == "" {
+ httpPath = c.getHttpPath(rr.RelativePath)
+ }
+
+ // Return a cURL command if curlChan is set
+ if c.cURLch != nil {
+ command := fmt.Sprintf("curl -X %s %s", rr.Method, httpPath)
+ for key, value := range rr.Values {
+ command += fmt.Sprintf(" -d %s=%s", key, value[0])
+ }
+ if c.credentials != nil {
+ command += fmt.Sprintf(" -u %s", c.credentials.username)
+ }
+ c.sendCURL(command)
+ }
+
+ logger.Debug("send.request.to ", httpPath, " | method ", rr.Method)
+
+ req, err := func() (*http.Request, error) {
+ reqLock.Lock()
+ defer reqLock.Unlock()
+
+ if rr.Values == nil {
+ if req, err = http.NewRequest(rr.Method, httpPath, nil); err != nil {
+ return nil, err
+ }
+ } else {
+ body := strings.NewReader(rr.Values.Encode())
+ if req, err = http.NewRequest(rr.Method, httpPath, body); err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Content-Type",
+ "application/x-www-form-urlencoded; param=value")
+ }
+ return req, nil
+ }()
+
+ if err != nil {
+ return nil, err
+ }
+
+ if c.credentials != nil {
+ req.SetBasicAuth(c.credentials.username, c.credentials.password)
+ }
+
+ resp, err = c.httpClient.Do(req)
+ // clear previous httpPath
+ httpPath = ""
+ defer func() {
+ if resp != nil {
+ resp.Body.Close()
+ }
+ }()
+
+ // If the request was cancelled, return ErrRequestCancelled directly
+ select {
+ case <-cancelled:
+ return nil, ErrRequestCancelled
+ default:
+ }
+
+ numReqs++
+
+ // network error, change a machine!
+ if err != nil {
+ logger.Debug("network error: ", err.Error())
+ lastResp := http.Response{}
+ if checkErr := checkRetry(c.cluster, numReqs, lastResp, err); checkErr != nil {
+ return nil, checkErr
+ }
+
+ c.cluster.failure()
+ continue
+ }
+
+ // if there is no error, it should receive response
+ logger.Debug("recv.response.from ", httpPath)
+
+ if validHttpStatusCode[resp.StatusCode] {
+ // try to read byte code and break the loop
+ respBody, err = ioutil.ReadAll(resp.Body)
+ if err == nil {
+ logger.Debug("recv.success ", httpPath)
+ break
+ }
+ // ReadAll error may be caused due to cancel request
+ select {
+ case <-cancelled:
+ return nil, ErrRequestCancelled
+ default:
+ }
+
+ if err == io.ErrUnexpectedEOF {
+ // underlying connection was closed prematurely, probably by timeout
+ // TODO: empty body or unexpectedEOF can cause http.Transport to get hosed;
+ // this allows the client to detect that and take evasive action. Need
+ // to revisit once code.google.com/p/go/issues/detail?id=8648 gets fixed.
+ respBody = []byte{}
+ break
+ }
+ }
+
+ if resp.StatusCode == http.StatusTemporaryRedirect {
+ u, err := resp.Location()
+
+ if err != nil {
+ logger.Warning(err)
+ } else {
+ // set httpPath for following redirection
+ httpPath = u.String()
+ }
+ resp.Body.Close()
+ continue
+ }
+
+ if checkErr := checkRetry(c.cluster, numReqs, *resp,
+ errors.New("Unexpected HTTP status code")); checkErr != nil {
+ return nil, checkErr
+ }
+ resp.Body.Close()
+ }
+
+ r := &RawResponse{
+ StatusCode: resp.StatusCode,
+ Body: respBody,
+ Header: resp.Header,
+ }
+
+ return r, nil
+}
+
+// DefaultCheckRetry defines the retrying behaviour for bad HTTP requests
+// If we have retried 2 * machine number, stop retrying.
+// If status code is InternalServerError, sleep for 200ms.
+func DefaultCheckRetry(cluster *Cluster, numReqs int, lastResp http.Response,
+ err error) error {
+
+ if numReqs > 2*len(cluster.Machines) {
+ errStr := fmt.Sprintf("failed to propose on members %v twice [last error: %v]", cluster.Machines, err)
+ return newError(ErrCodeEtcdNotReachable, errStr, 0)
+ }
+
+ if isEmptyResponse(lastResp) {
+ // always retry if it failed to get response from one machine
+ return nil
+ }
+ if !shouldRetry(lastResp) {
+ body := []byte("nil")
+ if lastResp.Body != nil {
+ if b, err := ioutil.ReadAll(lastResp.Body); err == nil {
+ body = b
+ }
+ }
+ errStr := fmt.Sprintf("unhandled http status [%s] with body [%s]", http.StatusText(lastResp.StatusCode), body)
+ return newError(ErrCodeUnhandledHTTPStatus, errStr, 0)
+ }
+ // sleep some time and expect leader election finish
+ time.Sleep(time.Millisecond * 200)
+ logger.Warning("bad response status code ", lastResp.StatusCode)
+ return nil
+}
+
+func isEmptyResponse(r http.Response) bool { return r.StatusCode == 0 }
+
+// shouldRetry returns whether the reponse deserves retry.
+func shouldRetry(r http.Response) bool {
+ // TODO: only retry when the cluster is in leader election
+ // We cannot do it exactly because etcd doesn't support it well.
+ return r.StatusCode == http.StatusInternalServerError
+}
+
+func (c *Client) getHttpPath(s ...string) string {
+ fullPath := c.cluster.pick() + "/" + version
+ for _, seg := range s {
+ fullPath = fullPath + "/" + seg
+ }
+ return fullPath
+}
+
+// buildValues builds a url.Values map according to the given value and ttl
+func buildValues(value string, ttl uint64) url.Values {
+ v := url.Values{}
+
+ if value != "" {
+ v.Set("value", value)
+ }
+
+ if ttl > 0 {
+ v.Set("ttl", fmt.Sprintf("%v", ttl))
+ }
+
+ return v
+}
+
+// convert key string to http path exclude version, including URL escaping
+// for example: key[foo] -> path[keys/foo]
+// key[/%z] -> path[keys/%25z]
+// key[/] -> path[keys/]
+func keyToPath(key string) string {
+ // URL-escape our key, except for slashes
+ p := strings.Replace(url.QueryEscape(path.Join("keys", key)), "%2F", "/", -1)
+
+ // corner case: if key is "/" or "//" ect
+ // path join will clear the tailing "/"
+ // we need to add it back
+ if p == "keys" {
+ p = "keys/"
+ }
+
+ return p
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/requests_test.go b/vendor/github.com/coreos/go-etcd/etcd/requests_test.go
new file mode 100644
index 000000000..7a2bd190a
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/requests_test.go
@@ -0,0 +1,22 @@
+package etcd
+
+import "testing"
+
+func TestKeyToPath(t *testing.T) {
+ tests := []struct {
+ key string
+ wpath string
+ }{
+ {"", "keys/"},
+ {"foo", "keys/foo"},
+ {"foo/bar", "keys/foo/bar"},
+ {"%z", "keys/%25z"},
+ {"/", "keys/"},
+ }
+ for i, tt := range tests {
+ path := keyToPath(tt.key)
+ if path != tt.wpath {
+ t.Errorf("#%d: path = %s, want %s", i, path, tt.wpath)
+ }
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/response.generated.go b/vendor/github.com/coreos/go-etcd/etcd/response.generated.go
new file mode 100644
index 000000000..0701dc09d
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/response.generated.go
@@ -0,0 +1,1459 @@
+// ************************************************************
+// DO NOT EDIT.
+// THIS FILE IS AUTO-GENERATED BY codecgen.
+// ************************************************************
+
+package etcd
+
+import (
+ "errors"
+ "fmt"
+ codec1978 "github.com/ugorji/go/codec"
+ pkg1_http "net/http"
+ "reflect"
+ "runtime"
+ time "time"
+)
+
+const (
+ codecSelferC_UTF81978 = 1
+ codecSelferC_RAW1978 = 0
+ codecSelferValueTypeArray1978 = 10
+ codecSelferValueTypeMap1978 = 9
+)
+
+var (
+ codecSelferBitsize1978 = uint8(reflect.TypeOf(uint(0)).Bits())
+ codecSelferOnlyMapOrArrayEncodeToStructErr1978 = errors.New(`only encoded map or array can be decoded into a struct`)
+)
+
+type codecSelfer1978 struct{}
+
+func init() {
+ if codec1978.GenVersion != 4 {
+ _, file, _, _ := runtime.Caller(0)
+ err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v",
+ 4, codec1978.GenVersion, file)
+ panic(err)
+ }
+ if false { // reference the types, but skip this branch at build/run time
+ var v0 pkg1_http.Header
+ var v1 time.Time
+ _, _ = v0, v1
+ }
+}
+
+func (x responseType) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ yym1 := z.EncBinary()
+ _ = yym1
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ r.EncodeInt(int64(x))
+ }
+}
+
+func (x *responseType) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym2 := z.DecBinary()
+ _ = yym2
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ *((*int)(x)) = int(r.DecodeInt(codecSelferBitsize1978))
+ }
+}
+
+func (x *RawResponse) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym3 := z.EncBinary()
+ _ = yym3
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ yysep4 := !z.EncBinary()
+ yy2arr4 := z.EncBasicHandle().StructToArray
+ var yyq4 [3]bool
+ _, _, _ = yysep4, yyq4, yy2arr4
+ const yyr4 bool = false
+ if yyr4 || yy2arr4 {
+ r.EncodeArrayStart(3)
+ } else {
+ var yynn4 int = 3
+ for _, b := range yyq4 {
+ if b {
+ yynn4++
+ }
+ }
+ r.EncodeMapStart(yynn4)
+ }
+ if yyr4 || yy2arr4 {
+ yym6 := z.EncBinary()
+ _ = yym6
+ if false {
+ } else {
+ r.EncodeInt(int64(x.StatusCode))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("StatusCode"))
+ yym7 := z.EncBinary()
+ _ = yym7
+ if false {
+ } else {
+ r.EncodeInt(int64(x.StatusCode))
+ }
+ }
+ if yyr4 || yy2arr4 {
+ if x.Body == nil {
+ r.EncodeNil()
+ } else {
+ yym9 := z.EncBinary()
+ _ = yym9
+ if false {
+ } else {
+ r.EncodeStringBytes(codecSelferC_RAW1978, []byte(x.Body))
+ }
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("Body"))
+ if x.Body == nil {
+ r.EncodeNil()
+ } else {
+ yym10 := z.EncBinary()
+ _ = yym10
+ if false {
+ } else {
+ r.EncodeStringBytes(codecSelferC_RAW1978, []byte(x.Body))
+ }
+ }
+ }
+ if yyr4 || yy2arr4 {
+ if x.Header == nil {
+ r.EncodeNil()
+ } else {
+ yym12 := z.EncBinary()
+ _ = yym12
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x.Header) {
+ } else {
+ h.enchttp_Header((pkg1_http.Header)(x.Header), e)
+ }
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("Header"))
+ if x.Header == nil {
+ r.EncodeNil()
+ } else {
+ yym13 := z.EncBinary()
+ _ = yym13
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x.Header) {
+ } else {
+ h.enchttp_Header((pkg1_http.Header)(x.Header), e)
+ }
+ }
+ }
+ if yysep4 {
+ r.EncodeEnd()
+ }
+ }
+ }
+}
+
+func (x *RawResponse) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym14 := z.DecBinary()
+ _ = yym14
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ if r.IsContainerType(codecSelferValueTypeMap1978) {
+ yyl15 := r.ReadMapStart()
+ if yyl15 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromMap(yyl15, d)
+ }
+ } else if r.IsContainerType(codecSelferValueTypeArray1978) {
+ yyl15 := r.ReadArrayStart()
+ if yyl15 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromArray(yyl15, d)
+ }
+ } else {
+ panic(codecSelferOnlyMapOrArrayEncodeToStructErr1978)
+ }
+ }
+}
+
+func (x *RawResponse) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yys16Slc = z.DecScratchBuffer() // default slice to decode into
+ _ = yys16Slc
+ var yyhl16 bool = l >= 0
+ for yyj16 := 0; ; yyj16++ {
+ if yyhl16 {
+ if yyj16 >= l {
+ break
+ }
+ } else {
+ if r.CheckBreak() {
+ break
+ }
+ }
+ yys16Slc = r.DecodeBytes(yys16Slc, true, true)
+ yys16 := string(yys16Slc)
+ switch yys16 {
+ case "StatusCode":
+ if r.TryDecodeAsNil() {
+ x.StatusCode = 0
+ } else {
+ x.StatusCode = int(r.DecodeInt(codecSelferBitsize1978))
+ }
+ case "Body":
+ if r.TryDecodeAsNil() {
+ x.Body = nil
+ } else {
+ yyv18 := &x.Body
+ yym19 := z.DecBinary()
+ _ = yym19
+ if false {
+ } else {
+ *yyv18 = r.DecodeBytes(*(*[]byte)(yyv18), false, false)
+ }
+ }
+ case "Header":
+ if r.TryDecodeAsNil() {
+ x.Header = nil
+ } else {
+ yyv20 := &x.Header
+ yym21 := z.DecBinary()
+ _ = yym21
+ if false {
+ } else if z.HasExtensions() && z.DecExt(yyv20) {
+ } else {
+ h.dechttp_Header((*pkg1_http.Header)(yyv20), d)
+ }
+ }
+ default:
+ z.DecStructFieldNotFound(-1, yys16)
+ } // end switch yys16
+ } // end for yyj16
+ if !yyhl16 {
+ r.ReadEnd()
+ }
+}
+
+func (x *RawResponse) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yyj22 int
+ var yyb22 bool
+ var yyhl22 bool = l >= 0
+ yyj22++
+ if yyhl22 {
+ yyb22 = yyj22 > l
+ } else {
+ yyb22 = r.CheckBreak()
+ }
+ if yyb22 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.StatusCode = 0
+ } else {
+ x.StatusCode = int(r.DecodeInt(codecSelferBitsize1978))
+ }
+ yyj22++
+ if yyhl22 {
+ yyb22 = yyj22 > l
+ } else {
+ yyb22 = r.CheckBreak()
+ }
+ if yyb22 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Body = nil
+ } else {
+ yyv24 := &x.Body
+ yym25 := z.DecBinary()
+ _ = yym25
+ if false {
+ } else {
+ *yyv24 = r.DecodeBytes(*(*[]byte)(yyv24), false, false)
+ }
+ }
+ yyj22++
+ if yyhl22 {
+ yyb22 = yyj22 > l
+ } else {
+ yyb22 = r.CheckBreak()
+ }
+ if yyb22 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Header = nil
+ } else {
+ yyv26 := &x.Header
+ yym27 := z.DecBinary()
+ _ = yym27
+ if false {
+ } else if z.HasExtensions() && z.DecExt(yyv26) {
+ } else {
+ h.dechttp_Header((*pkg1_http.Header)(yyv26), d)
+ }
+ }
+ for {
+ yyj22++
+ if yyhl22 {
+ yyb22 = yyj22 > l
+ } else {
+ yyb22 = r.CheckBreak()
+ }
+ if yyb22 {
+ break
+ }
+ z.DecStructFieldNotFound(yyj22-1, "")
+ }
+ r.ReadEnd()
+}
+
+func (x *Response) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym28 := z.EncBinary()
+ _ = yym28
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ yysep29 := !z.EncBinary()
+ yy2arr29 := z.EncBasicHandle().StructToArray
+ var yyq29 [6]bool
+ _, _, _ = yysep29, yyq29, yy2arr29
+ const yyr29 bool = false
+ yyq29[2] = x.PrevNode != nil
+ if yyr29 || yy2arr29 {
+ r.EncodeArrayStart(6)
+ } else {
+ var yynn29 int = 5
+ for _, b := range yyq29 {
+ if b {
+ yynn29++
+ }
+ }
+ r.EncodeMapStart(yynn29)
+ }
+ if yyr29 || yy2arr29 {
+ yym31 := z.EncBinary()
+ _ = yym31
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(x.Action))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("action"))
+ yym32 := z.EncBinary()
+ _ = yym32
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(x.Action))
+ }
+ }
+ if yyr29 || yy2arr29 {
+ if x.Node == nil {
+ r.EncodeNil()
+ } else {
+ x.Node.CodecEncodeSelf(e)
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("node"))
+ if x.Node == nil {
+ r.EncodeNil()
+ } else {
+ x.Node.CodecEncodeSelf(e)
+ }
+ }
+ if yyr29 || yy2arr29 {
+ if yyq29[2] {
+ if x.PrevNode == nil {
+ r.EncodeNil()
+ } else {
+ x.PrevNode.CodecEncodeSelf(e)
+ }
+ } else {
+ r.EncodeNil()
+ }
+ } else {
+ if yyq29[2] {
+ r.EncodeString(codecSelferC_UTF81978, string("prevNode"))
+ if x.PrevNode == nil {
+ r.EncodeNil()
+ } else {
+ x.PrevNode.CodecEncodeSelf(e)
+ }
+ }
+ }
+ if yyr29 || yy2arr29 {
+ yym36 := z.EncBinary()
+ _ = yym36
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.EtcdIndex))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("etcdIndex"))
+ yym37 := z.EncBinary()
+ _ = yym37
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.EtcdIndex))
+ }
+ }
+ if yyr29 || yy2arr29 {
+ yym39 := z.EncBinary()
+ _ = yym39
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.RaftIndex))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("raftIndex"))
+ yym40 := z.EncBinary()
+ _ = yym40
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.RaftIndex))
+ }
+ }
+ if yyr29 || yy2arr29 {
+ yym42 := z.EncBinary()
+ _ = yym42
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.RaftTerm))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("raftTerm"))
+ yym43 := z.EncBinary()
+ _ = yym43
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.RaftTerm))
+ }
+ }
+ if yysep29 {
+ r.EncodeEnd()
+ }
+ }
+ }
+}
+
+func (x *Response) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym44 := z.DecBinary()
+ _ = yym44
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ if r.IsContainerType(codecSelferValueTypeMap1978) {
+ yyl45 := r.ReadMapStart()
+ if yyl45 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromMap(yyl45, d)
+ }
+ } else if r.IsContainerType(codecSelferValueTypeArray1978) {
+ yyl45 := r.ReadArrayStart()
+ if yyl45 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromArray(yyl45, d)
+ }
+ } else {
+ panic(codecSelferOnlyMapOrArrayEncodeToStructErr1978)
+ }
+ }
+}
+
+func (x *Response) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yys46Slc = z.DecScratchBuffer() // default slice to decode into
+ _ = yys46Slc
+ var yyhl46 bool = l >= 0
+ for yyj46 := 0; ; yyj46++ {
+ if yyhl46 {
+ if yyj46 >= l {
+ break
+ }
+ } else {
+ if r.CheckBreak() {
+ break
+ }
+ }
+ yys46Slc = r.DecodeBytes(yys46Slc, true, true)
+ yys46 := string(yys46Slc)
+ switch yys46 {
+ case "action":
+ if r.TryDecodeAsNil() {
+ x.Action = ""
+ } else {
+ x.Action = string(r.DecodeString())
+ }
+ case "node":
+ if r.TryDecodeAsNil() {
+ if x.Node != nil {
+ x.Node = nil
+ }
+ } else {
+ if x.Node == nil {
+ x.Node = new(Node)
+ }
+ x.Node.CodecDecodeSelf(d)
+ }
+ case "prevNode":
+ if r.TryDecodeAsNil() {
+ if x.PrevNode != nil {
+ x.PrevNode = nil
+ }
+ } else {
+ if x.PrevNode == nil {
+ x.PrevNode = new(Node)
+ }
+ x.PrevNode.CodecDecodeSelf(d)
+ }
+ case "etcdIndex":
+ if r.TryDecodeAsNil() {
+ x.EtcdIndex = 0
+ } else {
+ x.EtcdIndex = uint64(r.DecodeUint(64))
+ }
+ case "raftIndex":
+ if r.TryDecodeAsNil() {
+ x.RaftIndex = 0
+ } else {
+ x.RaftIndex = uint64(r.DecodeUint(64))
+ }
+ case "raftTerm":
+ if r.TryDecodeAsNil() {
+ x.RaftTerm = 0
+ } else {
+ x.RaftTerm = uint64(r.DecodeUint(64))
+ }
+ default:
+ z.DecStructFieldNotFound(-1, yys46)
+ } // end switch yys46
+ } // end for yyj46
+ if !yyhl46 {
+ r.ReadEnd()
+ }
+}
+
+func (x *Response) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yyj53 int
+ var yyb53 bool
+ var yyhl53 bool = l >= 0
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Action = ""
+ } else {
+ x.Action = string(r.DecodeString())
+ }
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ if x.Node != nil {
+ x.Node = nil
+ }
+ } else {
+ if x.Node == nil {
+ x.Node = new(Node)
+ }
+ x.Node.CodecDecodeSelf(d)
+ }
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ if x.PrevNode != nil {
+ x.PrevNode = nil
+ }
+ } else {
+ if x.PrevNode == nil {
+ x.PrevNode = new(Node)
+ }
+ x.PrevNode.CodecDecodeSelf(d)
+ }
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.EtcdIndex = 0
+ } else {
+ x.EtcdIndex = uint64(r.DecodeUint(64))
+ }
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.RaftIndex = 0
+ } else {
+ x.RaftIndex = uint64(r.DecodeUint(64))
+ }
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.RaftTerm = 0
+ } else {
+ x.RaftTerm = uint64(r.DecodeUint(64))
+ }
+ for {
+ yyj53++
+ if yyhl53 {
+ yyb53 = yyj53 > l
+ } else {
+ yyb53 = r.CheckBreak()
+ }
+ if yyb53 {
+ break
+ }
+ z.DecStructFieldNotFound(yyj53-1, "")
+ }
+ r.ReadEnd()
+}
+
+func (x *Node) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym60 := z.EncBinary()
+ _ = yym60
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ yysep61 := !z.EncBinary()
+ yy2arr61 := z.EncBasicHandle().StructToArray
+ var yyq61 [8]bool
+ _, _, _ = yysep61, yyq61, yy2arr61
+ const yyr61 bool = false
+ yyq61[1] = x.Value != ""
+ yyq61[2] = x.Dir != false
+ yyq61[3] = x.Expiration != nil
+ yyq61[4] = x.TTL != 0
+ yyq61[5] = len(x.Nodes) != 0
+ yyq61[6] = x.ModifiedIndex != 0
+ yyq61[7] = x.CreatedIndex != 0
+ if yyr61 || yy2arr61 {
+ r.EncodeArrayStart(8)
+ } else {
+ var yynn61 int = 1
+ for _, b := range yyq61 {
+ if b {
+ yynn61++
+ }
+ }
+ r.EncodeMapStart(yynn61)
+ }
+ if yyr61 || yy2arr61 {
+ yym63 := z.EncBinary()
+ _ = yym63
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(x.Key))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string("key"))
+ yym64 := z.EncBinary()
+ _ = yym64
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(x.Key))
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[1] {
+ yym66 := z.EncBinary()
+ _ = yym66
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(x.Value))
+ }
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, "")
+ }
+ } else {
+ if yyq61[1] {
+ r.EncodeString(codecSelferC_UTF81978, string("value"))
+ yym67 := z.EncBinary()
+ _ = yym67
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(x.Value))
+ }
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[2] {
+ yym69 := z.EncBinary()
+ _ = yym69
+ if false {
+ } else {
+ r.EncodeBool(bool(x.Dir))
+ }
+ } else {
+ r.EncodeBool(false)
+ }
+ } else {
+ if yyq61[2] {
+ r.EncodeString(codecSelferC_UTF81978, string("dir"))
+ yym70 := z.EncBinary()
+ _ = yym70
+ if false {
+ } else {
+ r.EncodeBool(bool(x.Dir))
+ }
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[3] {
+ if x.Expiration == nil {
+ r.EncodeNil()
+ } else {
+ yym72 := z.EncBinary()
+ _ = yym72
+ if false {
+ } else if yym73 := z.TimeRtidIfBinc(); yym73 != 0 {
+ r.EncodeBuiltin(yym73, x.Expiration)
+ } else if z.HasExtensions() && z.EncExt(x.Expiration) {
+ } else if yym72 {
+ z.EncBinaryMarshal(x.Expiration)
+ } else if !yym72 && z.IsJSONHandle() {
+ z.EncJSONMarshal(x.Expiration)
+ } else {
+ z.EncFallback(x.Expiration)
+ }
+ }
+ } else {
+ r.EncodeNil()
+ }
+ } else {
+ if yyq61[3] {
+ r.EncodeString(codecSelferC_UTF81978, string("expiration"))
+ if x.Expiration == nil {
+ r.EncodeNil()
+ } else {
+ yym74 := z.EncBinary()
+ _ = yym74
+ if false {
+ } else if yym75 := z.TimeRtidIfBinc(); yym75 != 0 {
+ r.EncodeBuiltin(yym75, x.Expiration)
+ } else if z.HasExtensions() && z.EncExt(x.Expiration) {
+ } else if yym74 {
+ z.EncBinaryMarshal(x.Expiration)
+ } else if !yym74 && z.IsJSONHandle() {
+ z.EncJSONMarshal(x.Expiration)
+ } else {
+ z.EncFallback(x.Expiration)
+ }
+ }
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[4] {
+ yym77 := z.EncBinary()
+ _ = yym77
+ if false {
+ } else {
+ r.EncodeInt(int64(x.TTL))
+ }
+ } else {
+ r.EncodeInt(0)
+ }
+ } else {
+ if yyq61[4] {
+ r.EncodeString(codecSelferC_UTF81978, string("ttl"))
+ yym78 := z.EncBinary()
+ _ = yym78
+ if false {
+ } else {
+ r.EncodeInt(int64(x.TTL))
+ }
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[5] {
+ if x.Nodes == nil {
+ r.EncodeNil()
+ } else {
+ x.Nodes.CodecEncodeSelf(e)
+ }
+ } else {
+ r.EncodeNil()
+ }
+ } else {
+ if yyq61[5] {
+ r.EncodeString(codecSelferC_UTF81978, string("nodes"))
+ if x.Nodes == nil {
+ r.EncodeNil()
+ } else {
+ x.Nodes.CodecEncodeSelf(e)
+ }
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[6] {
+ yym81 := z.EncBinary()
+ _ = yym81
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.ModifiedIndex))
+ }
+ } else {
+ r.EncodeUint(0)
+ }
+ } else {
+ if yyq61[6] {
+ r.EncodeString(codecSelferC_UTF81978, string("modifiedIndex"))
+ yym82 := z.EncBinary()
+ _ = yym82
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.ModifiedIndex))
+ }
+ }
+ }
+ if yyr61 || yy2arr61 {
+ if yyq61[7] {
+ yym84 := z.EncBinary()
+ _ = yym84
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.CreatedIndex))
+ }
+ } else {
+ r.EncodeUint(0)
+ }
+ } else {
+ if yyq61[7] {
+ r.EncodeString(codecSelferC_UTF81978, string("createdIndex"))
+ yym85 := z.EncBinary()
+ _ = yym85
+ if false {
+ } else {
+ r.EncodeUint(uint64(x.CreatedIndex))
+ }
+ }
+ }
+ if yysep61 {
+ r.EncodeEnd()
+ }
+ }
+ }
+}
+
+func (x *Node) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym86 := z.DecBinary()
+ _ = yym86
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ if r.IsContainerType(codecSelferValueTypeMap1978) {
+ yyl87 := r.ReadMapStart()
+ if yyl87 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromMap(yyl87, d)
+ }
+ } else if r.IsContainerType(codecSelferValueTypeArray1978) {
+ yyl87 := r.ReadArrayStart()
+ if yyl87 == 0 {
+ r.ReadEnd()
+ } else {
+ x.codecDecodeSelfFromArray(yyl87, d)
+ }
+ } else {
+ panic(codecSelferOnlyMapOrArrayEncodeToStructErr1978)
+ }
+ }
+}
+
+func (x *Node) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yys88Slc = z.DecScratchBuffer() // default slice to decode into
+ _ = yys88Slc
+ var yyhl88 bool = l >= 0
+ for yyj88 := 0; ; yyj88++ {
+ if yyhl88 {
+ if yyj88 >= l {
+ break
+ }
+ } else {
+ if r.CheckBreak() {
+ break
+ }
+ }
+ yys88Slc = r.DecodeBytes(yys88Slc, true, true)
+ yys88 := string(yys88Slc)
+ switch yys88 {
+ case "key":
+ if r.TryDecodeAsNil() {
+ x.Key = ""
+ } else {
+ x.Key = string(r.DecodeString())
+ }
+ case "value":
+ if r.TryDecodeAsNil() {
+ x.Value = ""
+ } else {
+ x.Value = string(r.DecodeString())
+ }
+ case "dir":
+ if r.TryDecodeAsNil() {
+ x.Dir = false
+ } else {
+ x.Dir = bool(r.DecodeBool())
+ }
+ case "expiration":
+ if r.TryDecodeAsNil() {
+ if x.Expiration != nil {
+ x.Expiration = nil
+ }
+ } else {
+ if x.Expiration == nil {
+ x.Expiration = new(time.Time)
+ }
+ yym93 := z.DecBinary()
+ _ = yym93
+ if false {
+ } else if yym94 := z.TimeRtidIfBinc(); yym94 != 0 {
+ r.DecodeBuiltin(yym94, x.Expiration)
+ } else if z.HasExtensions() && z.DecExt(x.Expiration) {
+ } else if yym93 {
+ z.DecBinaryUnmarshal(x.Expiration)
+ } else if !yym93 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(x.Expiration)
+ } else {
+ z.DecFallback(x.Expiration, false)
+ }
+ }
+ case "ttl":
+ if r.TryDecodeAsNil() {
+ x.TTL = 0
+ } else {
+ x.TTL = int64(r.DecodeInt(64))
+ }
+ case "nodes":
+ if r.TryDecodeAsNil() {
+ x.Nodes = nil
+ } else {
+ yyv96 := &x.Nodes
+ yyv96.CodecDecodeSelf(d)
+ }
+ case "modifiedIndex":
+ if r.TryDecodeAsNil() {
+ x.ModifiedIndex = 0
+ } else {
+ x.ModifiedIndex = uint64(r.DecodeUint(64))
+ }
+ case "createdIndex":
+ if r.TryDecodeAsNil() {
+ x.CreatedIndex = 0
+ } else {
+ x.CreatedIndex = uint64(r.DecodeUint(64))
+ }
+ default:
+ z.DecStructFieldNotFound(-1, yys88)
+ } // end switch yys88
+ } // end for yyj88
+ if !yyhl88 {
+ r.ReadEnd()
+ }
+}
+
+func (x *Node) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ var yyj99 int
+ var yyb99 bool
+ var yyhl99 bool = l >= 0
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Key = ""
+ } else {
+ x.Key = string(r.DecodeString())
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Value = ""
+ } else {
+ x.Value = string(r.DecodeString())
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Dir = false
+ } else {
+ x.Dir = bool(r.DecodeBool())
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ if x.Expiration != nil {
+ x.Expiration = nil
+ }
+ } else {
+ if x.Expiration == nil {
+ x.Expiration = new(time.Time)
+ }
+ yym104 := z.DecBinary()
+ _ = yym104
+ if false {
+ } else if yym105 := z.TimeRtidIfBinc(); yym105 != 0 {
+ r.DecodeBuiltin(yym105, x.Expiration)
+ } else if z.HasExtensions() && z.DecExt(x.Expiration) {
+ } else if yym104 {
+ z.DecBinaryUnmarshal(x.Expiration)
+ } else if !yym104 && z.IsJSONHandle() {
+ z.DecJSONUnmarshal(x.Expiration)
+ } else {
+ z.DecFallback(x.Expiration, false)
+ }
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.TTL = 0
+ } else {
+ x.TTL = int64(r.DecodeInt(64))
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.Nodes = nil
+ } else {
+ yyv107 := &x.Nodes
+ yyv107.CodecDecodeSelf(d)
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.ModifiedIndex = 0
+ } else {
+ x.ModifiedIndex = uint64(r.DecodeUint(64))
+ }
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ r.ReadEnd()
+ return
+ }
+ if r.TryDecodeAsNil() {
+ x.CreatedIndex = 0
+ } else {
+ x.CreatedIndex = uint64(r.DecodeUint(64))
+ }
+ for {
+ yyj99++
+ if yyhl99 {
+ yyb99 = yyj99 > l
+ } else {
+ yyb99 = r.CheckBreak()
+ }
+ if yyb99 {
+ break
+ }
+ z.DecStructFieldNotFound(yyj99-1, "")
+ }
+ r.ReadEnd()
+}
+
+func (x Nodes) CodecEncodeSelf(e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ if x == nil {
+ r.EncodeNil()
+ } else {
+ yym110 := z.EncBinary()
+ _ = yym110
+ if false {
+ } else if z.HasExtensions() && z.EncExt(x) {
+ } else {
+ h.encNodes((Nodes)(x), e)
+ }
+ }
+}
+
+func (x *Nodes) CodecDecodeSelf(d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+ yym111 := z.DecBinary()
+ _ = yym111
+ if false {
+ } else if z.HasExtensions() && z.DecExt(x) {
+ } else {
+ h.decNodes((*Nodes)(x), d)
+ }
+}
+
+func (x codecSelfer1978) enchttp_Header(v pkg1_http.Header, e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ r.EncodeMapStart(len(v))
+ for yyk112, yyv112 := range v {
+ yym113 := z.EncBinary()
+ _ = yym113
+ if false {
+ } else {
+ r.EncodeString(codecSelferC_UTF81978, string(yyk112))
+ }
+ if yyv112 == nil {
+ r.EncodeNil()
+ } else {
+ yym114 := z.EncBinary()
+ _ = yym114
+ if false {
+ } else {
+ z.F.EncSliceStringV(yyv112, false, e)
+ }
+ }
+ }
+ r.EncodeEnd()
+}
+
+func (x codecSelfer1978) dechttp_Header(v *pkg1_http.Header, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+
+ yyv115 := *v
+ yyl115 := r.ReadMapStart()
+ if yyv115 == nil {
+ yyrl115, _ := z.DecInferLen(yyl115, z.DecBasicHandle().MaxInitLen, 40)
+ yyv115 = make(map[string][]string, yyrl115)
+ *v = yyv115
+ }
+ if yyl115 > 0 {
+ for yyj115 := 0; yyj115 < yyl115; yyj115++ {
+ var yymk115 string
+ if r.TryDecodeAsNil() {
+ yymk115 = ""
+ } else {
+ yymk115 = string(r.DecodeString())
+ }
+
+ yymv115 := yyv115[yymk115]
+ if r.TryDecodeAsNil() {
+ yymv115 = nil
+ } else {
+ yyv117 := &yymv115
+ yym118 := z.DecBinary()
+ _ = yym118
+ if false {
+ } else {
+ z.F.DecSliceStringX(yyv117, false, d)
+ }
+ }
+
+ if yyv115 != nil {
+ yyv115[yymk115] = yymv115
+ }
+ }
+ } else if yyl115 < 0 {
+ for yyj115 := 0; !r.CheckBreak(); yyj115++ {
+ var yymk115 string
+ if r.TryDecodeAsNil() {
+ yymk115 = ""
+ } else {
+ yymk115 = string(r.DecodeString())
+ }
+
+ yymv115 := yyv115[yymk115]
+ if r.TryDecodeAsNil() {
+ yymv115 = nil
+ } else {
+ yyv120 := &yymv115
+ yym121 := z.DecBinary()
+ _ = yym121
+ if false {
+ } else {
+ z.F.DecSliceStringX(yyv120, false, d)
+ }
+ }
+
+ if yyv115 != nil {
+ yyv115[yymk115] = yymv115
+ }
+ }
+ r.ReadEnd()
+ } // else len==0: TODO: Should we clear map entries?
+}
+
+func (x codecSelfer1978) encNodes(v Nodes, e *codec1978.Encoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperEncoder(e)
+ _, _, _ = h, z, r
+ r.EncodeArrayStart(len(v))
+ for _, yyv122 := range v {
+ if yyv122 == nil {
+ r.EncodeNil()
+ } else {
+ yyv122.CodecEncodeSelf(e)
+ }
+ }
+ r.EncodeEnd()
+}
+
+func (x codecSelfer1978) decNodes(v *Nodes, d *codec1978.Decoder) {
+ var h codecSelfer1978
+ z, r := codec1978.GenHelperDecoder(d)
+ _, _, _ = h, z, r
+
+ yyv123 := *v
+ yyh123, yyl123 := z.DecSliceHelperStart()
+
+ var yyrr123, yyrl123 int
+ var yyc123, yyrt123 bool
+ _, _, _ = yyc123, yyrt123, yyrl123
+ yyrr123 = yyl123
+
+ if yyv123 == nil {
+ if yyrl123, yyrt123 = z.DecInferLen(yyl123, z.DecBasicHandle().MaxInitLen, 8); yyrt123 {
+ yyrr123 = yyrl123
+ }
+ yyv123 = make(Nodes, yyrl123)
+ yyc123 = true
+ }
+
+ if yyl123 == 0 {
+ if len(yyv123) != 0 {
+ yyv123 = yyv123[:0]
+ yyc123 = true
+ }
+ } else if yyl123 > 0 {
+
+ if yyl123 > cap(yyv123) {
+ yyrl123, yyrt123 = z.DecInferLen(yyl123, z.DecBasicHandle().MaxInitLen, 8)
+ yyv123 = make([]*Node, yyrl123)
+ yyc123 = true
+
+ yyrr123 = len(yyv123)
+ } else if yyl123 != len(yyv123) {
+ yyv123 = yyv123[:yyl123]
+ yyc123 = true
+ }
+ yyj123 := 0
+ for ; yyj123 < yyrr123; yyj123++ {
+ if r.TryDecodeAsNil() {
+ if yyv123[yyj123] != nil {
+ *yyv123[yyj123] = Node{}
+ }
+ } else {
+ if yyv123[yyj123] == nil {
+ yyv123[yyj123] = new(Node)
+ }
+ yyw124 := yyv123[yyj123]
+ yyw124.CodecDecodeSelf(d)
+ }
+
+ }
+ if yyrt123 {
+ for ; yyj123 < yyl123; yyj123++ {
+ yyv123 = append(yyv123, nil)
+ if r.TryDecodeAsNil() {
+ if yyv123[yyj123] != nil {
+ *yyv123[yyj123] = Node{}
+ }
+ } else {
+ if yyv123[yyj123] == nil {
+ yyv123[yyj123] = new(Node)
+ }
+ yyw125 := yyv123[yyj123]
+ yyw125.CodecDecodeSelf(d)
+ }
+
+ }
+ }
+
+ } else {
+ for yyj123 := 0; !r.CheckBreak(); yyj123++ {
+ if yyj123 >= len(yyv123) {
+ yyv123 = append(yyv123, nil) // var yyz123 *Node
+ yyc123 = true
+ }
+
+ if yyj123 < len(yyv123) {
+ if r.TryDecodeAsNil() {
+ if yyv123[yyj123] != nil {
+ *yyv123[yyj123] = Node{}
+ }
+ } else {
+ if yyv123[yyj123] == nil {
+ yyv123[yyj123] = new(Node)
+ }
+ yyw126 := yyv123[yyj123]
+ yyw126.CodecDecodeSelf(d)
+ }
+
+ } else {
+ z.DecSwallow()
+ }
+
+ }
+ yyh123.End()
+ }
+ if yyc123 {
+ *v = yyv123
+ }
+
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/response.go b/vendor/github.com/coreos/go-etcd/etcd/response.go
new file mode 100644
index 000000000..69b38be46
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/response.go
@@ -0,0 +1,93 @@
+package etcd
+
+//go:generate codecgen -d 1978 -o response.generated.go response.go
+
+import (
+ "net/http"
+ "strconv"
+ "time"
+
+ "github.com/ugorji/go/codec"
+)
+
+const (
+ rawResponse = iota
+ normalResponse
+)
+
+type responseType int
+
+type RawResponse struct {
+ StatusCode int
+ Body []byte
+ Header http.Header
+}
+
+var (
+ validHttpStatusCode = map[int]bool{
+ http.StatusCreated: true,
+ http.StatusOK: true,
+ http.StatusBadRequest: true,
+ http.StatusNotFound: true,
+ http.StatusPreconditionFailed: true,
+ http.StatusForbidden: true,
+ http.StatusUnauthorized: true,
+ }
+)
+
+// Unmarshal parses RawResponse and stores the result in Response
+func (rr *RawResponse) Unmarshal() (*Response, error) {
+ if rr.StatusCode != http.StatusOK && rr.StatusCode != http.StatusCreated {
+ return nil, handleError(rr.Body)
+ }
+
+ resp := new(Response)
+
+ err := codec.NewDecoderBytes(rr.Body, new(codec.JsonHandle)).Decode(resp)
+
+ if err != nil {
+ return nil, err
+ }
+
+ // attach index and term to response
+ resp.EtcdIndex, _ = strconv.ParseUint(rr.Header.Get("X-Etcd-Index"), 10, 64)
+ resp.RaftIndex, _ = strconv.ParseUint(rr.Header.Get("X-Raft-Index"), 10, 64)
+ resp.RaftTerm, _ = strconv.ParseUint(rr.Header.Get("X-Raft-Term"), 10, 64)
+
+ return resp, nil
+}
+
+type Response struct {
+ Action string `json:"action"`
+ Node *Node `json:"node"`
+ PrevNode *Node `json:"prevNode,omitempty"`
+ EtcdIndex uint64 `json:"etcdIndex"`
+ RaftIndex uint64 `json:"raftIndex"`
+ RaftTerm uint64 `json:"raftTerm"`
+}
+
+type Node struct {
+ Key string `json:"key, omitempty"`
+ Value string `json:"value,omitempty"`
+ Dir bool `json:"dir,omitempty"`
+ Expiration *time.Time `json:"expiration,omitempty"`
+ TTL int64 `json:"ttl,omitempty"`
+ Nodes Nodes `json:"nodes,omitempty"`
+ ModifiedIndex uint64 `json:"modifiedIndex,omitempty"`
+ CreatedIndex uint64 `json:"createdIndex,omitempty"`
+}
+
+type Nodes []*Node
+
+// interfaces for sorting
+func (ns Nodes) Len() int {
+ return len(ns)
+}
+
+func (ns Nodes) Less(i, j int) bool {
+ return ns[i].Key < ns[j].Key
+}
+
+func (ns Nodes) Swap(i, j int) {
+ ns[i], ns[j] = ns[j], ns[i]
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/response_test.go b/vendor/github.com/coreos/go-etcd/etcd/response_test.go
new file mode 100644
index 000000000..23e0c56eb
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/response_test.go
@@ -0,0 +1,75 @@
+package etcd
+
+import (
+ "net/http"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/ugorji/go/codec"
+)
+
+func createTestNode(size int) *Node {
+ return &Node{
+ Key: strings.Repeat("a", 30),
+ Value: strings.Repeat("a", size),
+ TTL: 123456789,
+ ModifiedIndex: 123456,
+ CreatedIndex: 123456,
+ }
+}
+
+func createTestNodeWithChildren(children, size int) *Node {
+ node := createTestNode(size)
+ for i := 0; i < children; i++ {
+ node.Nodes = append(node.Nodes, createTestNode(size))
+ }
+ return node
+}
+
+func createTestResponse(children, size int) *Response {
+ return &Response{
+ Action: "aaaaa",
+ Node: createTestNodeWithChildren(children, size),
+ PrevNode: nil,
+ EtcdIndex: 123456,
+ RaftIndex: 123456,
+ RaftTerm: 123456,
+ }
+}
+
+func benchmarkResponseUnmarshalling(b *testing.B, children, size int) {
+ response := createTestResponse(children, size)
+
+ rr := RawResponse{http.StatusOK, make([]byte, 0), http.Header{}}
+ codec.NewEncoderBytes(&rr.Body, new(codec.JsonHandle)).Encode(response)
+
+ b.ResetTimer()
+ newResponse := new(Response)
+ var err error
+ for i := 0; i < b.N; i++ {
+ if newResponse, err = rr.Unmarshal(); err != nil {
+ b.Errorf("Error: %v", err)
+ }
+
+ }
+ if !reflect.DeepEqual(response.Node, newResponse.Node) {
+ b.Errorf("Unexpected difference in a parsed response: \n%+v\n%+v", response, newResponse)
+ }
+}
+
+func BenchmarkSmallResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 30, 20)
+}
+
+func BenchmarkManySmallResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 3000, 20)
+}
+
+func BenchmarkMediumResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 300, 200)
+}
+
+func BenchmarkLargeResponseUnmarshal(b *testing.B) {
+ benchmarkResponseUnmarshalling(b, 3000, 2000)
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go b/vendor/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
new file mode 100644
index 000000000..87c86b830
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/set_curl_chan_test.go
@@ -0,0 +1,42 @@
+package etcd
+
+import (
+ "fmt"
+ "testing"
+)
+
+func TestSetCurlChan(t *testing.T) {
+ c := NewClient(nil)
+ c.OpenCURL()
+
+ defer func() {
+ c.Delete("foo", true)
+ }()
+
+ _, err := c.Set("foo", "bar", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected := fmt.Sprintf("curl -X PUT %s/v2/keys/foo -d value=bar -d ttl=5",
+ c.cluster.pick())
+ actual := c.RecvCURL()
+ if expected != actual {
+ t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
+ actual, expected)
+ }
+
+ c.SetConsistency(STRONG_CONSISTENCY)
+ _, err = c.Get("foo", false, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ expected = fmt.Sprintf("curl -X GET %s/v2/keys/foo?quorum=true&recursive=false&sorted=false",
+ c.cluster.pick())
+ actual = c.RecvCURL()
+ if expected != actual {
+ t.Fatalf(`Command "%s" is not equal to expected value "%s"`,
+ actual, expected)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/set_update_create.go b/vendor/github.com/coreos/go-etcd/etcd/set_update_create.go
new file mode 100644
index 000000000..e2840cf35
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/set_update_create.go
@@ -0,0 +1,137 @@
+package etcd
+
+// Set sets the given key to the given value.
+// It will create a new key value pair or replace the old one.
+// It will not replace a existing directory.
+func (c *Client) Set(key string, value string, ttl uint64) (*Response, error) {
+ raw, err := c.RawSet(key, value, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// SetDir sets the given key to a directory.
+// It will create a new directory or replace the old key value pair by a directory.
+// It will not replace a existing directory.
+func (c *Client) SetDir(key string, ttl uint64) (*Response, error) {
+ raw, err := c.RawSetDir(key, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// CreateDir creates a directory. It succeeds only if
+// the given key does not yet exist.
+func (c *Client) CreateDir(key string, ttl uint64) (*Response, error) {
+ raw, err := c.RawCreateDir(key, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// UpdateDir updates the given directory. It succeeds only if the
+// given key already exists.
+func (c *Client) UpdateDir(key string, ttl uint64) (*Response, error) {
+ raw, err := c.RawUpdateDir(key, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// Create creates a file with the given value under the given key. It succeeds
+// only if the given key does not yet exist.
+func (c *Client) Create(key string, value string, ttl uint64) (*Response, error) {
+ raw, err := c.RawCreate(key, value, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// CreateInOrder creates a file with a key that's guaranteed to be higher than other
+// keys in the given directory. It is useful for creating queues.
+func (c *Client) CreateInOrder(dir string, value string, ttl uint64) (*Response, error) {
+ raw, err := c.RawCreateInOrder(dir, value, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+// Update updates the given key to the given value. It succeeds only if the
+// given key already exists.
+func (c *Client) Update(key string, value string, ttl uint64) (*Response, error) {
+ raw, err := c.RawUpdate(key, value, ttl)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+}
+
+func (c *Client) RawUpdateDir(key string, ttl uint64) (*RawResponse, error) {
+ ops := Options{
+ "prevExist": true,
+ "dir": true,
+ }
+
+ return c.put(key, "", ttl, ops)
+}
+
+func (c *Client) RawCreateDir(key string, ttl uint64) (*RawResponse, error) {
+ ops := Options{
+ "prevExist": false,
+ "dir": true,
+ }
+
+ return c.put(key, "", ttl, ops)
+}
+
+func (c *Client) RawSet(key string, value string, ttl uint64) (*RawResponse, error) {
+ return c.put(key, value, ttl, nil)
+}
+
+func (c *Client) RawSetDir(key string, ttl uint64) (*RawResponse, error) {
+ ops := Options{
+ "dir": true,
+ }
+
+ return c.put(key, "", ttl, ops)
+}
+
+func (c *Client) RawUpdate(key string, value string, ttl uint64) (*RawResponse, error) {
+ ops := Options{
+ "prevExist": true,
+ }
+
+ return c.put(key, value, ttl, ops)
+}
+
+func (c *Client) RawCreate(key string, value string, ttl uint64) (*RawResponse, error) {
+ ops := Options{
+ "prevExist": false,
+ }
+
+ return c.put(key, value, ttl, ops)
+}
+
+func (c *Client) RawCreateInOrder(dir string, value string, ttl uint64) (*RawResponse, error) {
+ return c.post(dir, value, ttl)
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/set_update_create_test.go b/vendor/github.com/coreos/go-etcd/etcd/set_update_create_test.go
new file mode 100644
index 000000000..ced0f06e7
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/set_update_create_test.go
@@ -0,0 +1,241 @@
+package etcd
+
+import (
+ "testing"
+)
+
+func TestSet(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ }()
+
+ resp, err := c.Set("foo", "bar", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if resp.Node.Key != "/foo" || resp.Node.Value != "bar" || resp.Node.TTL != 5 {
+ t.Fatalf("Set 1 failed: %#v", resp)
+ }
+ if resp.PrevNode != nil {
+ t.Fatalf("Set 1 PrevNode failed: %#v", resp)
+ }
+
+ resp, err = c.Set("foo", "bar2", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/foo" && resp.Node.Value == "bar2" && resp.Node.TTL == 5) {
+ t.Fatalf("Set 2 failed: %#v", resp)
+ }
+ if resp.PrevNode.Key != "/foo" || resp.PrevNode.Value != "bar" || resp.Node.TTL != 5 {
+ t.Fatalf("Set 2 PrevNode failed: %#v", resp)
+ }
+}
+
+func TestUpdate(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ c.Delete("nonexistent", true)
+ }()
+
+ resp, err := c.Set("foo", "bar", 5)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // This should succeed.
+ resp, err = c.Update("foo", "wakawaka", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Action == "update" && resp.Node.Key == "/foo" && resp.Node.TTL == 5) {
+ t.Fatalf("Update 1 failed: %#v", resp)
+ }
+ if !(resp.PrevNode.Key == "/foo" && resp.PrevNode.Value == "bar" && resp.Node.TTL == 5) {
+ t.Fatalf("Update 1 prevValue failed: %#v", resp)
+ }
+
+ // This should fail because the key does not exist.
+ resp, err = c.Update("nonexistent", "whatever", 5)
+ if err == nil {
+ t.Fatalf("The key %v did not exist, so the update should have failed."+
+ "The response was: %#v", resp.Node.Key, resp)
+ }
+}
+
+func TestCreate(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("newKey", true)
+ }()
+
+ newKey := "/newKey"
+ newValue := "/newValue"
+
+ // This should succeed
+ resp, err := c.Create(newKey, newValue, 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Action == "create" && resp.Node.Key == newKey &&
+ resp.Node.Value == newValue && resp.Node.TTL == 5) {
+ t.Fatalf("Create 1 failed: %#v", resp)
+ }
+ if resp.PrevNode != nil {
+ t.Fatalf("Create 1 PrevNode failed: %#v", resp)
+ }
+
+ // This should fail, because the key is already there
+ resp, err = c.Create(newKey, newValue, 5)
+ if err == nil {
+ t.Fatalf("The key %v did exist, so the creation should have failed."+
+ "The response was: %#v", resp.Node.Key, resp)
+ }
+}
+
+func TestCreateInOrder(t *testing.T) {
+ c := NewClient(nil)
+ dir := "/queue"
+ defer func() {
+ c.DeleteDir(dir)
+ }()
+
+ var firstKey, secondKey string
+
+ resp, err := c.CreateInOrder(dir, "1", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Action == "create" && resp.Node.Value == "1" && resp.Node.TTL == 5) {
+ t.Fatalf("Create 1 failed: %#v", resp)
+ }
+
+ firstKey = resp.Node.Key
+
+ resp, err = c.CreateInOrder(dir, "2", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Action == "create" && resp.Node.Value == "2" && resp.Node.TTL == 5) {
+ t.Fatalf("Create 2 failed: %#v", resp)
+ }
+
+ secondKey = resp.Node.Key
+
+ if firstKey >= secondKey {
+ t.Fatalf("Expected first key to be greater than second key, but %s is not greater than %s",
+ firstKey, secondKey)
+ }
+}
+
+func TestSetDir(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("foo", true)
+ c.Delete("fooDir", true)
+ }()
+
+ resp, err := c.CreateDir("fooDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/fooDir" && resp.Node.Value == "" && resp.Node.TTL == 5) {
+ t.Fatalf("SetDir 1 failed: %#v", resp)
+ }
+ if resp.PrevNode != nil {
+ t.Fatalf("SetDir 1 PrevNode failed: %#v", resp)
+ }
+
+ // This should fail because /fooDir already points to a directory
+ resp, err = c.CreateDir("/fooDir", 5)
+ if err == nil {
+ t.Fatalf("fooDir already points to a directory, so SetDir should have failed."+
+ "The response was: %#v", resp)
+ }
+
+ _, err = c.Set("foo", "bar", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // This should succeed
+ // It should replace the key
+ resp, err = c.SetDir("foo", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/foo" && resp.Node.Value == "" && resp.Node.TTL == 5) {
+ t.Fatalf("SetDir 2 failed: %#v", resp)
+ }
+ if !(resp.PrevNode.Key == "/foo" && resp.PrevNode.Value == "bar" && resp.PrevNode.TTL == 5) {
+ t.Fatalf("SetDir 2 failed: %#v", resp)
+ }
+}
+
+func TestUpdateDir(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("fooDir", true)
+ }()
+
+ resp, err := c.CreateDir("fooDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // This should succeed.
+ resp, err = c.UpdateDir("fooDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Action == "update" && resp.Node.Key == "/fooDir" &&
+ resp.Node.Value == "" && resp.Node.TTL == 5) {
+ t.Fatalf("UpdateDir 1 failed: %#v", resp)
+ }
+ if !(resp.PrevNode.Key == "/fooDir" && resp.PrevNode.Dir == true && resp.PrevNode.TTL == 5) {
+ t.Fatalf("UpdateDir 1 PrevNode failed: %#v", resp)
+ }
+
+ // This should fail because the key does not exist.
+ resp, err = c.UpdateDir("nonexistentDir", 5)
+ if err == nil {
+ t.Fatalf("The key %v did not exist, so the update should have failed."+
+ "The response was: %#v", resp.Node.Key, resp)
+ }
+}
+
+func TestCreateDir(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("fooDir", true)
+ }()
+
+ // This should succeed
+ resp, err := c.CreateDir("fooDir", 5)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !(resp.Action == "create" && resp.Node.Key == "/fooDir" &&
+ resp.Node.Value == "" && resp.Node.TTL == 5) {
+ t.Fatalf("CreateDir 1 failed: %#v", resp)
+ }
+ if resp.PrevNode != nil {
+ t.Fatalf("CreateDir 1 PrevNode failed: %#v", resp)
+ }
+
+ // This should fail, because the key is already there
+ resp, err = c.CreateDir("fooDir", 5)
+ if err == nil {
+ t.Fatalf("The key %v did exist, so the creation should have failed."+
+ "The response was: %#v", resp.Node.Key, resp)
+ }
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/shuffle.go b/vendor/github.com/coreos/go-etcd/etcd/shuffle.go
new file mode 100644
index 000000000..c26ddac30
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/shuffle.go
@@ -0,0 +1,19 @@
+package etcd
+
+import (
+ "math/rand"
+)
+
+func shuffleStringSlice(cards []string) []string {
+ size := len(cards)
+ //Do not need to copy if nothing changed
+ if size <= 1 {
+ return cards
+ }
+ shuffled := make([]string, size)
+ index := rand.Perm(size)
+ for i := range cards {
+ shuffled[index[i]] = cards[i]
+ }
+ return shuffled
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/version.go b/vendor/github.com/coreos/go-etcd/etcd/version.go
new file mode 100644
index 000000000..b1e9ed271
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/version.go
@@ -0,0 +1,6 @@
+package etcd
+
+const (
+ version = "v2"
+ packageVersion = "v2.0.0+git"
+)
diff --git a/vendor/github.com/coreos/go-etcd/etcd/watch.go b/vendor/github.com/coreos/go-etcd/etcd/watch.go
new file mode 100644
index 000000000..aa8d3df30
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/watch.go
@@ -0,0 +1,103 @@
+package etcd
+
+import (
+ "errors"
+)
+
+// Errors introduced by the Watch command.
+var (
+ ErrWatchStoppedByUser = errors.New("Watch stopped by the user via stop channel")
+)
+
+// If recursive is set to true the watch returns the first change under the given
+// prefix since the given index.
+//
+// If recursive is set to false the watch returns the first change to the given key
+// since the given index.
+//
+// To watch for the latest change, set waitIndex = 0.
+//
+// If a receiver channel is given, it will be a long-term watch. Watch will block at the
+//channel. After someone receives the channel, it will go on to watch that
+// prefix. If a stop channel is given, the client can close long-term watch using
+// the stop channel.
+func (c *Client) Watch(prefix string, waitIndex uint64, recursive bool,
+ receiver chan *Response, stop chan bool) (*Response, error) {
+ logger.Debugf("watch %s [%s]", prefix, c.cluster.Leader)
+ if receiver == nil {
+ raw, err := c.watchOnce(prefix, waitIndex, recursive, stop)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return raw.Unmarshal()
+ }
+ defer close(receiver)
+
+ for {
+ raw, err := c.watchOnce(prefix, waitIndex, recursive, stop)
+
+ if err != nil {
+ return nil, err
+ }
+
+ resp, err := raw.Unmarshal()
+
+ if err != nil {
+ return nil, err
+ }
+
+ waitIndex = resp.Node.ModifiedIndex + 1
+ receiver <- resp
+ }
+}
+
+func (c *Client) RawWatch(prefix string, waitIndex uint64, recursive bool,
+ receiver chan *RawResponse, stop chan bool) (*RawResponse, error) {
+
+ logger.Debugf("rawWatch %s [%s]", prefix, c.cluster.Leader)
+ if receiver == nil {
+ return c.watchOnce(prefix, waitIndex, recursive, stop)
+ }
+
+ for {
+ raw, err := c.watchOnce(prefix, waitIndex, recursive, stop)
+
+ if err != nil {
+ return nil, err
+ }
+
+ resp, err := raw.Unmarshal()
+
+ if err != nil {
+ return nil, err
+ }
+
+ waitIndex = resp.Node.ModifiedIndex + 1
+ receiver <- raw
+ }
+}
+
+// helper func
+// return when there is change under the given prefix
+func (c *Client) watchOnce(key string, waitIndex uint64, recursive bool, stop chan bool) (*RawResponse, error) {
+
+ options := Options{
+ "wait": true,
+ }
+ if waitIndex > 0 {
+ options["waitIndex"] = waitIndex
+ }
+ if recursive {
+ options["recursive"] = true
+ }
+
+ resp, err := c.getCancelable(key, options, stop)
+
+ if err == ErrRequestCancelled {
+ return nil, ErrWatchStoppedByUser
+ }
+
+ return resp, err
+}
diff --git a/vendor/github.com/coreos/go-etcd/etcd/watch_test.go b/vendor/github.com/coreos/go-etcd/etcd/watch_test.go
new file mode 100644
index 000000000..43e1dfeb8
--- /dev/null
+++ b/vendor/github.com/coreos/go-etcd/etcd/watch_test.go
@@ -0,0 +1,119 @@
+package etcd
+
+import (
+ "fmt"
+ "runtime"
+ "testing"
+ "time"
+)
+
+func TestWatch(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("watch_foo", true)
+ }()
+
+ go setHelper("watch_foo", "bar", c)
+
+ resp, err := c.Watch("watch_foo", 0, false, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/watch_foo" && resp.Node.Value == "bar") {
+ t.Fatalf("Watch 1 failed: %#v", resp)
+ }
+
+ go setHelper("watch_foo", "bar", c)
+
+ resp, err = c.Watch("watch_foo", resp.Node.ModifiedIndex+1, false, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/watch_foo" && resp.Node.Value == "bar") {
+ t.Fatalf("Watch 2 failed: %#v", resp)
+ }
+
+ routineNum := runtime.NumGoroutine()
+
+ ch := make(chan *Response, 10)
+ stop := make(chan bool, 1)
+
+ go setLoop("watch_foo", "bar", c)
+
+ go receiver(ch, stop)
+
+ _, err = c.Watch("watch_foo", 0, false, ch, stop)
+ if err != ErrWatchStoppedByUser {
+ t.Fatalf("Watch returned a non-user stop error")
+ }
+
+ if newRoutineNum := runtime.NumGoroutine(); newRoutineNum != routineNum {
+ t.Fatalf("Routine numbers differ after watch stop: %v, %v", routineNum, newRoutineNum)
+ }
+}
+
+func TestWatchAll(t *testing.T) {
+ c := NewClient(nil)
+ defer func() {
+ c.Delete("watch_foo", true)
+ }()
+
+ go setHelper("watch_foo/foo", "bar", c)
+
+ resp, err := c.Watch("watch_foo", 0, true, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/watch_foo/foo" && resp.Node.Value == "bar") {
+ t.Fatalf("WatchAll 1 failed: %#v", resp)
+ }
+
+ go setHelper("watch_foo/foo", "bar", c)
+
+ resp, err = c.Watch("watch_foo", resp.Node.ModifiedIndex+1, true, nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !(resp.Node.Key == "/watch_foo/foo" && resp.Node.Value == "bar") {
+ t.Fatalf("WatchAll 2 failed: %#v", resp)
+ }
+
+ ch := make(chan *Response, 10)
+ stop := make(chan bool, 1)
+
+ routineNum := runtime.NumGoroutine()
+
+ go setLoop("watch_foo/foo", "bar", c)
+
+ go receiver(ch, stop)
+
+ _, err = c.Watch("watch_foo", 0, true, ch, stop)
+ if err != ErrWatchStoppedByUser {
+ t.Fatalf("Watch returned a non-user stop error")
+ }
+
+ if newRoutineNum := runtime.NumGoroutine(); newRoutineNum != routineNum {
+ t.Fatalf("Routine numbers differ after watch stop: %v, %v", routineNum, newRoutineNum)
+ }
+}
+
+func setHelper(key, value string, c *Client) {
+ time.Sleep(time.Second)
+ c.Set(key, value, 100)
+}
+
+func setLoop(key, value string, c *Client) {
+ time.Sleep(time.Second)
+ for i := 0; i < 10; i++ {
+ newValue := fmt.Sprintf("%s_%v", value, i)
+ c.Set(key, newValue, 100)
+ time.Sleep(time.Second / 10)
+ }
+}
+
+func receiver(c chan *Response, stop chan bool) {
+ for i := 0; i < 10; i++ {
+ <-c
+ }
+ stop <- true
+}
diff --git a/vendor/github.com/cpuguy83/go-md2man/md2man/md2man.go b/vendor/github.com/cpuguy83/go-md2man/md2man/md2man.go
new file mode 100644
index 000000000..8f44fa155
--- /dev/null
+++ b/vendor/github.com/cpuguy83/go-md2man/md2man/md2man.go
@@ -0,0 +1,19 @@
+package md2man
+
+import (
+ "github.com/russross/blackfriday"
+)
+
+func Render(doc []byte) []byte {
+ renderer := RoffRenderer(0)
+ extensions := 0
+ extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
+ extensions |= blackfriday.EXTENSION_TABLES
+ extensions |= blackfriday.EXTENSION_FENCED_CODE
+ extensions |= blackfriday.EXTENSION_AUTOLINK
+ extensions |= blackfriday.EXTENSION_SPACE_HEADERS
+ extensions |= blackfriday.EXTENSION_FOOTNOTES
+ extensions |= blackfriday.EXTENSION_TITLEBLOCK
+
+ return blackfriday.Markdown(doc, renderer, extensions)
+}
diff --git a/vendor/github.com/cpuguy83/go-md2man/md2man/roff.go b/vendor/github.com/cpuguy83/go-md2man/md2man/roff.go
new file mode 100644
index 000000000..4478786b7
--- /dev/null
+++ b/vendor/github.com/cpuguy83/go-md2man/md2man/roff.go
@@ -0,0 +1,269 @@
+package md2man
+
+import (
+ "bytes"
+ "fmt"
+ "html"
+ "strings"
+
+ "github.com/russross/blackfriday"
+)
+
+type roffRenderer struct{}
+
+func RoffRenderer(flags int) blackfriday.Renderer {
+ return &roffRenderer{}
+}
+
+func (r *roffRenderer) GetFlags() int {
+ return 0
+}
+
+func (r *roffRenderer) TitleBlock(out *bytes.Buffer, text []byte) {
+ out.WriteString(".TH ")
+
+ splitText := bytes.Split(text, []byte("\n"))
+ for i, line := range splitText {
+ line = bytes.TrimPrefix(line, []byte("% "))
+ if i == 0 {
+ line = bytes.Replace(line, []byte("("), []byte("\" \""), 1)
+ line = bytes.Replace(line, []byte(")"), []byte("\" \""), 1)
+ }
+ line = append([]byte("\""), line...)
+ line = append(line, []byte("\" ")...)
+ out.Write(line)
+ }
+
+ out.WriteString(" \"\"\n")
+}
+
+func (r *roffRenderer) BlockCode(out *bytes.Buffer, text []byte, lang string) {
+ out.WriteString("\n.PP\n.RS\n\n.nf\n")
+ escapeSpecialChars(out, text)
+ out.WriteString("\n.fi\n.RE\n")
+}
+
+func (r *roffRenderer) BlockQuote(out *bytes.Buffer, text []byte) {
+ out.WriteString("\n.PP\n.RS\n")
+ out.Write(text)
+ out.WriteString("\n.RE\n")
+}
+
+func (r *roffRenderer) BlockHtml(out *bytes.Buffer, text []byte) {
+ out.Write(text)
+}
+
+func (r *roffRenderer) Header(out *bytes.Buffer, text func() bool, level int, id string) {
+ marker := out.Len()
+
+ switch {
+ case marker == 0:
+ // This is the doc header
+ out.WriteString(".TH ")
+ case level == 1:
+ out.WriteString("\n\n.SH ")
+ case level == 2:
+ out.WriteString("\n.SH ")
+ default:
+ out.WriteString("\n.SS ")
+ }
+
+ if !text() {
+ out.Truncate(marker)
+ return
+ }
+}
+
+func (r *roffRenderer) HRule(out *bytes.Buffer) {
+ out.WriteString("\n.ti 0\n\\l'\\n(.lu'\n")
+}
+
+func (r *roffRenderer) List(out *bytes.Buffer, text func() bool, flags int) {
+ marker := out.Len()
+ out.WriteString(".IP ")
+ if flags&blackfriday.LIST_TYPE_ORDERED != 0 {
+ out.WriteString("\\(bu 2")
+ } else {
+ out.WriteString("\\n+[step" + string(flags) + "]")
+ }
+ out.WriteString("\n")
+ if !text() {
+ out.Truncate(marker)
+ return
+ }
+
+}
+
+func (r *roffRenderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
+ out.WriteString("\n\\item ")
+ out.Write(text)
+}
+
+func (r *roffRenderer) Paragraph(out *bytes.Buffer, text func() bool) {
+ marker := out.Len()
+ out.WriteString("\n.PP\n")
+ if !text() {
+ out.Truncate(marker)
+ return
+ }
+ if marker != 0 {
+ out.WriteString("\n")
+ }
+}
+
+// TODO: This might now work
+func (r *roffRenderer) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
+ out.WriteString(".TS\nallbox;\n")
+
+ out.Write(header)
+ out.Write(body)
+ out.WriteString("\n.TE\n")
+}
+
+func (r *roffRenderer) TableRow(out *bytes.Buffer, text []byte) {
+ if out.Len() > 0 {
+ out.WriteString("\n")
+ }
+ out.Write(text)
+ out.WriteString("\n")
+}
+
+func (r *roffRenderer) TableHeaderCell(out *bytes.Buffer, text []byte, align int) {
+ if out.Len() > 0 {
+ out.WriteString(" ")
+ }
+ out.Write(text)
+ out.WriteString(" ")
+}
+
+// TODO: This is probably broken
+func (r *roffRenderer) TableCell(out *bytes.Buffer, text []byte, align int) {
+ if out.Len() > 0 {
+ out.WriteString("\t")
+ }
+ out.Write(text)
+ out.WriteString("\t")
+}
+
+func (r *roffRenderer) Footnotes(out *bytes.Buffer, text func() bool) {
+
+}
+
+func (r *roffRenderer) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
+
+}
+
+func (r *roffRenderer) AutoLink(out *bytes.Buffer, link []byte, kind int) {
+ out.WriteString("\n\\[la]")
+ out.Write(link)
+ out.WriteString("\\[ra]")
+}
+
+func (r *roffRenderer) CodeSpan(out *bytes.Buffer, text []byte) {
+ out.WriteString("\\fB\\fC")
+ escapeSpecialChars(out, text)
+ out.WriteString("\\fR")
+}
+
+func (r *roffRenderer) DoubleEmphasis(out *bytes.Buffer, text []byte) {
+ out.WriteString("\\fB")
+ out.Write(text)
+ out.WriteString("\\fP")
+}
+
+func (r *roffRenderer) Emphasis(out *bytes.Buffer, text []byte) {
+ out.WriteString("\\fI")
+ out.Write(text)
+ out.WriteString("\\fP")
+}
+
+func (r *roffRenderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
+}
+
+func (r *roffRenderer) LineBreak(out *bytes.Buffer) {
+ out.WriteString("\n.br\n")
+}
+
+func (r *roffRenderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
+ r.AutoLink(out, link, 0)
+}
+
+func (r *roffRenderer) RawHtmlTag(out *bytes.Buffer, tag []byte) {
+ out.Write(tag)
+}
+
+func (r *roffRenderer) TripleEmphasis(out *bytes.Buffer, text []byte) {
+ out.WriteString("\\s+2")
+ out.Write(text)
+ out.WriteString("\\s-2")
+}
+
+func (r *roffRenderer) StrikeThrough(out *bytes.Buffer, text []byte) {
+}
+
+func (r *roffRenderer) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
+
+}
+
+func (r *roffRenderer) Entity(out *bytes.Buffer, entity []byte) {
+ out.WriteString(html.UnescapeString(string(entity)))
+}
+
+func processFooterText(text []byte) []byte {
+ text = bytes.TrimPrefix(text, []byte("% "))
+ newText := []byte{}
+ textArr := strings.Split(string(text), ") ")
+
+ for i, w := range textArr {
+ if i == 0 {
+ w = strings.Replace(w, "(", "\" \"", 1)
+ w = fmt.Sprintf("\"%s\"", w)
+ } else {
+ w = fmt.Sprintf(" \"%s\"", w)
+ }
+ newText = append(newText, []byte(w)...)
+ }
+ newText = append(newText, []byte(" \"\"")...)
+
+ return newText
+}
+
+func (r *roffRenderer) NormalText(out *bytes.Buffer, text []byte) {
+ escapeSpecialChars(out, text)
+}
+
+func (r *roffRenderer) DocumentHeader(out *bytes.Buffer) {
+}
+
+func (r *roffRenderer) DocumentFooter(out *bytes.Buffer) {
+}
+
+func needsBackslash(c byte) bool {
+ for _, r := range []byte("-_&\\~") {
+ if c == r {
+ return true
+ }
+ }
+ return false
+}
+
+func escapeSpecialChars(out *bytes.Buffer, text []byte) {
+ for i := 0; i < len(text); i++ {
+ // directly copy normal characters
+ org := i
+
+ for i < len(text) && !needsBackslash(text[i]) {
+ i++
+ }
+ if i > org {
+ out.Write(text[org:i])
+ }
+
+ // escape a character
+ if i >= len(text) {
+ break
+ }
+ out.WriteByte('\\')
+ out.WriteByte(text[i])
+ }
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go
new file mode 100644
index 000000000..a8d27a3f6
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go
@@ -0,0 +1,136 @@
+// Copyright (c) 2015 Dave Collins
+//
+// 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.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when the code is not running on Google App Engine and "-tags disableunsafe"
+// is not added to the go build command line.
+// +build !appengine,!disableunsafe
+
+package spew
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = false
+
+ // ptrSize is the size of a pointer on the current arch.
+ ptrSize = unsafe.Sizeof((*byte)(nil))
+)
+
+var (
+ // offsetPtr, offsetScalar, and offsetFlag are the offsets for the
+ // internal reflect.Value fields. These values are valid before golang
+ // commit ecccf07e7f9d which changed the format. The are also valid
+ // after commit 82f48826c6c7 which changed the format again to mirror
+ // the original format. Code in the init function updates these offsets
+ // as necessary.
+ offsetPtr = uintptr(ptrSize)
+ offsetScalar = uintptr(0)
+ offsetFlag = uintptr(ptrSize * 2)
+
+ // flagKindWidth and flagKindShift indicate various bits that the
+ // reflect package uses internally to track kind information.
+ //
+ // flagRO indicates whether or not the value field of a reflect.Value is
+ // read-only.
+ //
+ // flagIndir indicates whether the value field of a reflect.Value is
+ // the actual data or a pointer to the data.
+ //
+ // These values are valid before golang commit 90a7c3c86944 which
+ // changed their positions. Code in the init function updates these
+ // flags as necessary.
+ flagKindWidth = uintptr(5)
+ flagKindShift = uintptr(flagKindWidth - 1)
+ flagRO = uintptr(1 << 0)
+ flagIndir = uintptr(1 << 1)
+)
+
+func init() {
+ // Older versions of reflect.Value stored small integers directly in the
+ // ptr field (which is named val in the older versions). Versions
+ // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
+ // scalar for this purpose which unfortunately came before the flag
+ // field, so the offset of the flag field is different for those
+ // versions.
+ //
+ // This code constructs a new reflect.Value from a known small integer
+ // and checks if the size of the reflect.Value struct indicates it has
+ // the scalar field. When it does, the offsets are updated accordingly.
+ vv := reflect.ValueOf(0xf00)
+ if unsafe.Sizeof(vv) == (ptrSize * 4) {
+ offsetScalar = ptrSize * 2
+ offsetFlag = ptrSize * 3
+ }
+
+ // Commit 90a7c3c86944 changed the flag positions such that the low
+ // order bits are the kind. This code extracts the kind from the flags
+ // field and ensures it's the correct type. When it's not, the flag
+ // order has been changed to the newer format, so the flags are updated
+ // accordingly.
+ upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
+ upfv := *(*uintptr)(upf)
+ flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) {
+ flagKindShift = 0
+ flagRO = 1 << 5
+ flagIndir = 1 << 6
+ }
+}
+
+// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
+// the typical safety restrictions preventing access to unaddressable and
+// unexported data. It works by digging the raw pointer to the underlying
+// value out of the protected value and generating a new unprotected (unsafe)
+// reflect.Value to it.
+//
+// This allows us to check for implementations of the Stringer and error
+// interfaces to be used for pretty printing ordinarily unaddressable and
+// inaccessible values such as unexported struct fields.
+func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
+ indirects := 1
+ vt := v.Type()
+ upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
+ rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
+ if rvf&flagIndir != 0 {
+ vt = reflect.PtrTo(v.Type())
+ indirects++
+ } else if offsetScalar != 0 {
+ // The value is in the scalar field when it's not one of the
+ // reference types.
+ switch vt.Kind() {
+ case reflect.Uintptr:
+ case reflect.Chan:
+ case reflect.Func:
+ case reflect.Map:
+ case reflect.Ptr:
+ case reflect.UnsafePointer:
+ default:
+ upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
+ offsetScalar)
+ }
+ }
+
+ pv := reflect.NewAt(vt, upv)
+ rv = pv
+ for i := 0; i < indirects; i++ {
+ rv = rv.Elem()
+ }
+ return rv
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
new file mode 100644
index 000000000..457e41235
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2015 Dave Collins
+//
+// 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.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when either the code is running on Google App Engine or "-tags disableunsafe"
+// is added to the go build command line.
+// +build appengine disableunsafe
+
+package spew
+
+import "reflect"
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = true
+)
+
+// unsafeReflectValue typically converts the passed reflect.Value into a one
+// that bypasses the typical safety restrictions preventing access to
+// unaddressable and unexported data. However, doing this relies on access to
+// the unsafe package. This is a stub version which simply returns the passed
+// reflect.Value when the unsafe package is not available.
+func unsafeReflectValue(v reflect.Value) reflect.Value {
+ return v
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go
new file mode 100644
index 000000000..14f02dc15
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/common.go
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2013 Dave Collins
+ *
+ * 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 (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+)
+
+// Some constants in the form of bytes to avoid string overhead. This mirrors
+// the technique used in the fmt package.
+var (
+ panicBytes = []byte("(PANIC=")
+ plusBytes = []byte("+")
+ iBytes = []byte("i")
+ trueBytes = []byte("true")
+ falseBytes = []byte("false")
+ interfaceBytes = []byte("(interface {})")
+ commaNewlineBytes = []byte(",\n")
+ newlineBytes = []byte("\n")
+ openBraceBytes = []byte("{")
+ openBraceNewlineBytes = []byte("{\n")
+ closeBraceBytes = []byte("}")
+ asteriskBytes = []byte("*")
+ colonBytes = []byte(":")
+ colonSpaceBytes = []byte(": ")
+ openParenBytes = []byte("(")
+ closeParenBytes = []byte(")")
+ spaceBytes = []byte(" ")
+ pointerChainBytes = []byte("->")
+ nilAngleBytes = []byte("")
+ maxNewlineBytes = []byte("\n")
+ maxShortBytes = []byte("")
+ circularBytes = []byte("")
+ circularShortBytes = []byte("")
+ invalidAngleBytes = []byte("")
+ openBracketBytes = []byte("[")
+ closeBracketBytes = []byte("]")
+ percentBytes = []byte("%")
+ precisionBytes = []byte(".")
+ openAngleBytes = []byte("<")
+ closeAngleBytes = []byte(">")
+ openMapBytes = []byte("map[")
+ closeMapBytes = []byte("]")
+ lenEqualsBytes = []byte("len=")
+ capEqualsBytes = []byte("cap=")
+)
+
+// hexDigits is used to map a decimal value to a hex digit.
+var hexDigits = "0123456789abcdef"
+
+// catchPanic handles any panics that might occur during the handleMethods
+// calls.
+func catchPanic(w io.Writer, v reflect.Value) {
+ if err := recover(); err != nil {
+ w.Write(panicBytes)
+ fmt.Fprintf(w, "%v", err)
+ w.Write(closeParenBytes)
+ }
+}
+
+// handleMethods attempts to call the Error and String methods on the underlying
+// type the passed reflect.Value represents and outputes the result to Writer w.
+//
+// It handles panics in any called methods by catching and displaying the error
+// as the formatted value.
+func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
+ // We need an interface to check if the type implements the error or
+ // Stringer interface. However, the reflect package won't give us an
+ // interface on certain things like unexported struct fields in order
+ // to enforce visibility rules. We use unsafe, when it's available,
+ // to bypass these restrictions since this package does not mutate the
+ // values.
+ if !v.CanInterface() {
+ if UnsafeDisabled {
+ return false
+ }
+
+ v = unsafeReflectValue(v)
+ }
+
+ // Choose whether or not to do error and Stringer interface lookups against
+ // the base type or a pointer to the base type depending on settings.
+ // Technically calling one of these methods with a pointer receiver can
+ // mutate the value, however, types which choose to satisify an error or
+ // Stringer interface with a pointer receiver should not be mutating their
+ // state inside these interface methods.
+ if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
+ v = unsafeReflectValue(v)
+ }
+ if v.CanAddr() {
+ v = v.Addr()
+ }
+
+ // Is it an error or Stringer?
+ switch iface := v.Interface().(type) {
+ case error:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.Error()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+
+ w.Write([]byte(iface.Error()))
+ return true
+
+ case fmt.Stringer:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.String()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+ w.Write([]byte(iface.String()))
+ return true
+ }
+ return false
+}
+
+// printBool outputs a boolean value as true or false to Writer w.
+func printBool(w io.Writer, val bool) {
+ if val {
+ w.Write(trueBytes)
+ } else {
+ w.Write(falseBytes)
+ }
+}
+
+// printInt outputs a signed integer value to Writer w.
+func printInt(w io.Writer, val int64, base int) {
+ w.Write([]byte(strconv.FormatInt(val, base)))
+}
+
+// printUint outputs an unsigned integer value to Writer w.
+func printUint(w io.Writer, val uint64, base int) {
+ w.Write([]byte(strconv.FormatUint(val, base)))
+}
+
+// printFloat outputs a floating point value using the specified precision,
+// which is expected to be 32 or 64bit, to Writer w.
+func printFloat(w io.Writer, val float64, precision int) {
+ w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
+}
+
+// printComplex outputs a complex value using the specified float precision
+// for the real and imaginary parts to Writer w.
+func printComplex(w io.Writer, c complex128, floatPrecision int) {
+ r := real(c)
+ w.Write(openParenBytes)
+ w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
+ i := imag(c)
+ if i >= 0 {
+ w.Write(plusBytes)
+ }
+ w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
+ w.Write(iBytes)
+ w.Write(closeParenBytes)
+}
+
+// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
+// prefix to Writer w.
+func printHexPtr(w io.Writer, p uintptr) {
+ // Null pointer.
+ num := uint64(p)
+ if num == 0 {
+ w.Write(nilAngleBytes)
+ return
+ }
+
+ // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
+ buf := make([]byte, 18)
+
+ // It's simpler to construct the hex string right to left.
+ base := uint64(16)
+ i := len(buf) - 1
+ for num >= base {
+ buf[i] = hexDigits[num%base]
+ num /= base
+ i--
+ }
+ buf[i] = hexDigits[num]
+
+ // Add '0x' prefix.
+ i--
+ buf[i] = 'x'
+ i--
+ buf[i] = '0'
+
+ // Strip unused leading bytes.
+ buf = buf[i:]
+ w.Write(buf)
+}
+
+// valuesSorter implements sort.Interface to allow a slice of reflect.Value
+// elements to be sorted.
+type valuesSorter struct {
+ values []reflect.Value
+ strings []string // either nil or same len and values
+ cs *ConfigState
+}
+
+// newValuesSorter initializes a valuesSorter instance, which holds a set of
+// surrogate keys on which the data should be sorted. It uses flags in
+// ConfigState to decide if and how to populate those surrogate keys.
+func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
+ vs := &valuesSorter{values: values, cs: cs}
+ if canSortSimply(vs.values[0].Kind()) {
+ return vs
+ }
+ if !cs.DisableMethods {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ b := bytes.Buffer{}
+ if !handleMethods(cs, &b, vs.values[i]) {
+ vs.strings = nil
+ break
+ }
+ vs.strings[i] = b.String()
+ }
+ }
+ if vs.strings == nil && cs.SpewKeys {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
+ }
+ }
+ return vs
+}
+
+// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
+// directly, or whether it should be considered for sorting by surrogate keys
+// (if the ConfigState allows it).
+func canSortSimply(kind reflect.Kind) bool {
+ // This switch parallels valueSortLess, except for the default case.
+ switch kind {
+ case reflect.Bool:
+ return true
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return true
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return true
+ case reflect.Float32, reflect.Float64:
+ return true
+ case reflect.String:
+ return true
+ case reflect.Uintptr:
+ return true
+ case reflect.Array:
+ return true
+ }
+ return false
+}
+
+// Len returns the number of values in the slice. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Len() int {
+ return len(s.values)
+}
+
+// Swap swaps the values at the passed indices. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ if s.strings != nil {
+ s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
+ }
+}
+
+// valueSortLess returns whether the first value should sort before the second
+// value. It is used by valueSorter.Less as part of the sort.Interface
+// implementation.
+func valueSortLess(a, b reflect.Value) bool {
+ switch a.Kind() {
+ case reflect.Bool:
+ return !a.Bool() && b.Bool()
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return a.Int() < b.Int()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return a.Uint() < b.Uint()
+ case reflect.Float32, reflect.Float64:
+ return a.Float() < b.Float()
+ case reflect.String:
+ return a.String() < b.String()
+ case reflect.Uintptr:
+ return a.Uint() < b.Uint()
+ case reflect.Array:
+ // Compare the contents of both arrays.
+ l := a.Len()
+ for i := 0; i < l; i++ {
+ av := a.Index(i)
+ bv := b.Index(i)
+ if av.Interface() == bv.Interface() {
+ continue
+ }
+ return valueSortLess(av, bv)
+ }
+ }
+ return a.String() < b.String()
+}
+
+// Less returns whether the value at index i should sort before the
+// value at index j. It is part of the sort.Interface implementation.
+func (s *valuesSorter) Less(i, j int) bool {
+ if s.strings == nil {
+ return valueSortLess(s.values[i], s.values[j])
+ }
+ return s.strings[i] < s.strings[j]
+}
+
+// sortValues is a sort function that handles both native types and any type that
+// can be converted to error or Stringer. Other inputs are sorted according to
+// their Value.String() value to ensure display stability.
+func sortValues(values []reflect.Value, cs *ConfigState) {
+ if len(values) == 0 {
+ return
+ }
+ sort.Sort(newValuesSorter(values, cs))
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/common_test.go b/vendor/github.com/davecgh/go-spew/spew/common_test.go
new file mode 100644
index 000000000..39b7525b3
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/common_test.go
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2013 Dave Collins
+ *
+ * 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_test
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// custom type to test Stinger interface on non-pointer receiver.
+type stringer string
+
+// String implements the Stringer interface for testing invocation of custom
+// stringers on types with non-pointer receivers.
+func (s stringer) String() string {
+ return "stringer " + string(s)
+}
+
+// custom type to test Stinger interface on pointer receiver.
+type pstringer string
+
+// String implements the Stringer interface for testing invocation of custom
+// stringers on types with only pointer receivers.
+func (s *pstringer) String() string {
+ return "stringer " + string(*s)
+}
+
+// xref1 and xref2 are cross referencing structs for testing circular reference
+// detection.
+type xref1 struct {
+ ps2 *xref2
+}
+type xref2 struct {
+ ps1 *xref1
+}
+
+// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
+// reference for testing detection.
+type indirCir1 struct {
+ ps2 *indirCir2
+}
+type indirCir2 struct {
+ ps3 *indirCir3
+}
+type indirCir3 struct {
+ ps1 *indirCir1
+}
+
+// embed is used to test embedded structures.
+type embed struct {
+ a string
+}
+
+// embedwrap is used to test embedded structures.
+type embedwrap struct {
+ *embed
+ e *embed
+}
+
+// panicer is used to intentionally cause a panic for testing spew properly
+// handles them
+type panicer int
+
+func (p panicer) String() string {
+ panic("test panic")
+}
+
+// customError is used to test custom error interface invocation.
+type customError int
+
+func (e customError) Error() string {
+ return fmt.Sprintf("error: %d", int(e))
+}
+
+// stringizeWants converts a slice of wanted test output into a format suitable
+// for a test error message.
+func stringizeWants(wants []string) string {
+ s := ""
+ for i, want := range wants {
+ if i > 0 {
+ s += fmt.Sprintf("want%d: %s", i+1, want)
+ } else {
+ s += "want: " + want
+ }
+ }
+ return s
+}
+
+// testFailed returns whether or not a test failed by checking if the result
+// of the test is in the slice of wanted strings.
+func testFailed(result string, wants []string) bool {
+ for _, want := range wants {
+ if result == want {
+ return false
+ }
+ }
+ return true
+}
+
+type sortableStruct struct {
+ x int
+}
+
+func (ss sortableStruct) String() string {
+ return fmt.Sprintf("ss.%d", ss.x)
+}
+
+type unsortableStruct struct {
+ x int
+}
+
+type sortTestCase struct {
+ input []reflect.Value
+ expected []reflect.Value
+}
+
+func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
+ getInterfaces := func(values []reflect.Value) []interface{} {
+ interfaces := []interface{}{}
+ for _, v := range values {
+ interfaces = append(interfaces, v.Interface())
+ }
+ return interfaces
+ }
+
+ for _, test := range tests {
+ spew.SortValues(test.input, cs)
+ // reflect.DeepEqual cannot really make sense of reflect.Value,
+ // probably because of all the pointer tricks. For instance,
+ // v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
+ // instead.
+ input := getInterfaces(test.input)
+ expected := getInterfaces(test.expected)
+ if !reflect.DeepEqual(input, expected) {
+ t.Errorf("Sort mismatch:\n %v != %v", input, expected)
+ }
+ }
+}
+
+// TestSortValues ensures the sort functionality for relect.Value based sorting
+// works as intended.
+func TestSortValues(t *testing.T) {
+ v := reflect.ValueOf
+
+ a := v("a")
+ b := v("b")
+ c := v("c")
+ embedA := v(embed{"a"})
+ embedB := v(embed{"b"})
+ embedC := v(embed{"c"})
+ tests := []sortTestCase{
+ // No values.
+ {
+ []reflect.Value{},
+ []reflect.Value{},
+ },
+ // Bools.
+ {
+ []reflect.Value{v(false), v(true), v(false)},
+ []reflect.Value{v(false), v(false), v(true)},
+ },
+ // Ints.
+ {
+ []reflect.Value{v(2), v(1), v(3)},
+ []reflect.Value{v(1), v(2), v(3)},
+ },
+ // Uints.
+ {
+ []reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
+ []reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
+ },
+ // Floats.
+ {
+ []reflect.Value{v(2.0), v(1.0), v(3.0)},
+ []reflect.Value{v(1.0), v(2.0), v(3.0)},
+ },
+ // Strings.
+ {
+ []reflect.Value{b, a, c},
+ []reflect.Value{a, b, c},
+ },
+ // Array
+ {
+ []reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
+ []reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
+ },
+ // Uintptrs.
+ {
+ []reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
+ []reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
+ },
+ // SortableStructs.
+ {
+ // Note: not sorted - DisableMethods is set.
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ },
+ // UnsortableStructs.
+ {
+ // Note: not sorted - SpewKeys is false.
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ },
+ // Invalid.
+ {
+ []reflect.Value{embedB, embedA, embedC},
+ []reflect.Value{embedB, embedA, embedC},
+ },
+ }
+ cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
+ helpTestSortValues(tests, &cs, t)
+}
+
+// TestSortValuesWithMethods ensures the sort functionality for relect.Value
+// based sorting works as intended when using string methods.
+func TestSortValuesWithMethods(t *testing.T) {
+ v := reflect.ValueOf
+
+ a := v("a")
+ b := v("b")
+ c := v("c")
+ tests := []sortTestCase{
+ // Ints.
+ {
+ []reflect.Value{v(2), v(1), v(3)},
+ []reflect.Value{v(1), v(2), v(3)},
+ },
+ // Strings.
+ {
+ []reflect.Value{b, a, c},
+ []reflect.Value{a, b, c},
+ },
+ // SortableStructs.
+ {
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
+ },
+ // UnsortableStructs.
+ {
+ // Note: not sorted - SpewKeys is false.
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ },
+ }
+ cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
+ helpTestSortValues(tests, &cs, t)
+}
+
+// TestSortValuesWithSpew ensures the sort functionality for relect.Value
+// based sorting works as intended when using spew to stringify keys.
+func TestSortValuesWithSpew(t *testing.T) {
+ v := reflect.ValueOf
+
+ a := v("a")
+ b := v("b")
+ c := v("c")
+ tests := []sortTestCase{
+ // Ints.
+ {
+ []reflect.Value{v(2), v(1), v(3)},
+ []reflect.Value{v(1), v(2), v(3)},
+ },
+ // Strings.
+ {
+ []reflect.Value{b, a, c},
+ []reflect.Value{a, b, c},
+ },
+ // SortableStructs.
+ {
+ []reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
+ []reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
+ },
+ // UnsortableStructs.
+ {
+ []reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
+ []reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
+ },
+ }
+ cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
+ helpTestSortValues(tests, &cs, t)
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/davecgh/go-spew/spew/config.go
new file mode 100644
index 000000000..ee1ab07b3
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/config.go
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2013 Dave Collins
+ *
+ * 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 (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+)
+
+// ConfigState houses the configuration options used by spew to format and
+// display values. There is a global instance, Config, that is used to control
+// all top-level Formatter and Dump functionality. Each ConfigState instance
+// provides methods equivalent to the top-level functions.
+//
+// The zero value for ConfigState provides no indentation. You would typically
+// want to set it to a space or a tab.
+//
+// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
+// with default settings. See the documentation of NewDefaultConfig for default
+// values.
+type ConfigState struct {
+ // Indent specifies the string to use for each indentation level. The
+ // global config instance that all top-level functions use set this to a
+ // single space by default. If you would like more indentation, you might
+ // set this to a tab with "\t" or perhaps two spaces with " ".
+ Indent string
+
+ // MaxDepth controls the maximum number of levels to descend into nested
+ // data structures. The default, 0, means there is no limit.
+ //
+ // NOTE: Circular data structures are properly detected, so it is not
+ // necessary to set this value unless you specifically want to limit deeply
+ // nested data structures.
+ MaxDepth int
+
+ // DisableMethods specifies whether or not error and Stringer interfaces are
+ // invoked for types that implement them.
+ DisableMethods bool
+
+ // DisablePointerMethods specifies whether or not to check for and invoke
+ // error and Stringer interfaces on types which only accept a pointer
+ // receiver when the current type is not a pointer.
+ //
+ // NOTE: This might be an unsafe action since calling one of these methods
+ // with a pointer receiver could technically mutate the value, however,
+ // in practice, types which choose to satisify an error or Stringer
+ // interface with a pointer receiver should not be mutating their state
+ // inside these interface methods. As a result, this option relies on
+ // access to the unsafe package, so it will not have any effect when
+ // running in environments without access to the unsafe package such as
+ // Google App Engine or with the "disableunsafe" build tag specified.
+ DisablePointerMethods bool
+
+ // ContinueOnMethod specifies whether or not recursion should continue once
+ // a custom error or Stringer interface is invoked. The default, false,
+ // means it will print the results of invoking the custom error or Stringer
+ // interface and return immediately instead of continuing to recurse into
+ // the internals of the data type.
+ //
+ // NOTE: This flag does not have any effect if method invocation is disabled
+ // via the DisableMethods or DisablePointerMethods options.
+ ContinueOnMethod bool
+
+ // SortKeys specifies map keys should be sorted before being printed. Use
+ // this to have a more deterministic, diffable output. Note that only
+ // native types (bool, int, uint, floats, uintptr and string) and types
+ // that support the error or Stringer interfaces (if methods are
+ // enabled) are supported, with other types sorted according to the
+ // reflect.Value.String() output which guarantees display stability.
+ SortKeys bool
+
+ // SpewKeys specifies that, as a last resort attempt, map keys should
+ // be spewed to strings and sorted by those strings. This is only
+ // considered if SortKeys is true.
+ SpewKeys bool
+}
+
+// Config is the active configuration of the top-level functions.
+// The configuration can be changed by modifying the contents of spew.Config.
+var Config = ConfigState{Indent: " "}
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a Formatter interface returned by c.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, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, c.convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.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, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, c.convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.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, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, c.convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a Formatter interface returned by c.NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, c.convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a Formatter interface returned by c.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(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(c.convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a Formatter interface returned by c.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, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, c.convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a Formatter interface returned by c.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(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(c.convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprint(a ...interface{}) string {
+ return fmt.Sprint(c.convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, c.convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a Formatter interface returned by c.NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(c.convertArgs(a)...)
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+c.Printf, c.Println, or c.Printf.
+*/
+func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(c, v)
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
+ fdump(c, w, a...)
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by modifying the public members
+of c. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func (c *ConfigState) Dump(a ...interface{}) {
+ fdump(c, os.Stdout, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func (c *ConfigState) Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(c, &buf, a...)
+ return buf.String()
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a spew Formatter interface using
+// the ConfigState associated with s.
+func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = newFormatter(c, arg)
+ }
+ return formatters
+}
+
+// NewDefaultConfig returns a ConfigState with the following default settings.
+//
+// Indent: " "
+// MaxDepth: 0
+// DisableMethods: false
+// DisablePointerMethods: false
+// ContinueOnMethod: false
+// SortKeys: false
+func NewDefaultConfig() *ConfigState {
+ return &ConfigState{Indent: " "}
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/davecgh/go-spew/spew/doc.go
new file mode 100644
index 000000000..5be0c4060
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/doc.go
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2013 Dave Collins
+ *
+ * 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 implements a deep pretty printer for Go data structures to aid in
+debugging.
+
+A quick overview of the additional features spew provides over the built-in
+printing facilities for Go data types are as follows:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output (only when using
+ Dump style)
+
+There are two different approaches spew allows for dumping Go data structures:
+
+ * Dump style which prints with newlines, customizable indentation,
+ and additional debug information such as types and all pointer addresses
+ used to indirect to the final value
+ * A custom Formatter interface that integrates cleanly with the standard fmt
+ package and replaces %v, %+v, %#v, and %#+v to provide inline printing
+ similar to the default %v while providing the additional functionality
+ outlined above and passing unsupported format verbs such as %x and %q
+ along to fmt
+
+Quick Start
+
+This section demonstrates how to quickly get started with spew. See the
+sections below for further details on formatting and configuration options.
+
+To dump a variable with full newlines, indentation, type, and pointer
+information use Dump, Fdump, or Sdump:
+ spew.Dump(myVar1, myVar2, ...)
+ spew.Fdump(someWriter, myVar1, myVar2, ...)
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Alternatively, if you would prefer to use format strings with a compacted inline
+printing style, use the convenience wrappers Printf, Fprintf, etc with
+%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
+%#+v (adds types and pointer addresses):
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+Configuration Options
+
+Configuration of spew is handled by fields in the ConfigState type. For
+convenience, all of the top-level functions use a global state available
+via the spew.Config global.
+
+It is also possible to create a ConfigState instance that provides methods
+equivalent to the top-level functions. This allows concurrent configuration
+options. See the ConfigState documentation for more details.
+
+The following configuration options are available:
+ * Indent
+ String to use for each indentation level for Dump functions.
+ It is a single space by default. A popular alternative is "\t".
+
+ * MaxDepth
+ Maximum number of levels to descend into nested data structures.
+ There is no limit by default.
+
+ * DisableMethods
+ Disables invocation of error and Stringer interface methods.
+ Method invocation is enabled by default.
+
+ * DisablePointerMethods
+ Disables invocation of error and Stringer interface methods on types
+ which only accept pointer receivers from non-pointer variables.
+ Pointer method invocation is enabled by default.
+
+ * ContinueOnMethod
+ Enables recursion into types after invoking error and Stringer interface
+ methods. Recursion after method invocation is disabled by default.
+
+ * SortKeys
+ Specifies map keys should be sorted before being printed. Use
+ this to have a more deterministic, diffable output. Note that
+ only native types (bool, int, uint, floats, uintptr and string)
+ and types which implement error or Stringer interfaces are
+ supported with other types sorted according to the
+ reflect.Value.String() output which guarantees display
+ stability. Natural map order is used by default.
+
+ * SpewKeys
+ Specifies that, as a last resort attempt, map keys should be
+ spewed to strings and sorted by those strings. This is only
+ considered if SortKeys is true.
+
+Dump Usage
+
+Simply call spew.Dump with a list of variables you want to dump:
+
+ spew.Dump(myVar1, myVar2, ...)
+
+You may also call spew.Fdump if you would prefer to output to an arbitrary
+io.Writer. For example, to dump to standard error:
+
+ spew.Fdump(os.Stderr, myVar1, myVar2, ...)
+
+A third option is to call spew.Sdump to get the formatted output as a string:
+
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Sample Dump Output
+
+See the Dump example for details on the setup of the types and variables being
+shown here.
+
+ (main.Foo) {
+ unexportedField: (*main.Bar)(0xf84002e210)({
+ flag: (main.Flag) flagTwo,
+ data: (uintptr)
+ }),
+ ExportedField: (map[interface {}]interface {}) (len=1) {
+ (string) (len=3) "one": (bool) true
+ }
+ }
+
+Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
+command as shown.
+ ([]uint8) (len=32 cap=32) {
+ 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
+ 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
+ 00000020 31 32 |12|
+ }
+
+Custom Formatter
+
+Spew provides a custom formatter that implements the fmt.Formatter interface
+so that it integrates cleanly with standard fmt package printing functions. The
+formatter is useful for inline printing of smaller data types similar to the
+standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Custom Formatter Usage
+
+The simplest way to make use of the spew custom formatter is to call one of the
+convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
+functions have syntax you are most likely already familiar with:
+
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Println(myVar, myVar2)
+ spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+See the Index for the full list convenience functions.
+
+Sample Formatter Output
+
+Double pointer to a uint8:
+ %v: <**>5
+ %+v: <**>(0xf8400420d0->0xf8400420c8)5
+ %#v: (**uint8)5
+ %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
+
+Pointer to circular struct with a uint8 field and a pointer to itself:
+ %v: <*>{1 <*>}
+ %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)}
+ %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)}
+ %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)}
+
+See the Printf example for details on the setup of variables being shown
+here.
+
+Errors
+
+Since it is possible for custom Stringer/error interfaces to panic, spew
+detects them and handles them internally by printing the panic information
+inline with the output. Since spew is intended to provide deep pretty printing
+capabilities on structures, it intentionally does not return any errors.
+*/
+package spew
diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go
new file mode 100644
index 000000000..36a2b6cc9
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/dump.go
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2013 Dave Collins
+ *
+ * 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 (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ // uint8Type is a reflect.Type representing a uint8. It is used to
+ // convert cgo types to uint8 slices for hexdumping.
+ uint8Type = reflect.TypeOf(uint8(0))
+
+ // cCharRE is a regular expression that matches a cgo char.
+ // It is used to detect character arrays to hexdump them.
+ cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
+
+ // cUnsignedCharRE is a regular expression that matches a cgo unsigned
+ // char. It is used to detect unsigned character arrays to hexdump
+ // them.
+ cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
+
+ // cUint8tCharRE is a regular expression that matches a cgo uint8_t.
+ // It is used to detect uint8_t arrays to hexdump them.
+ cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
+)
+
+// dumpState contains information about the state of a dump operation.
+type dumpState struct {
+ w io.Writer
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ ignoreNextIndent bool
+ cs *ConfigState
+}
+
+// indent performs indentation according to the depth level and cs.Indent
+// option.
+func (d *dumpState) indent() {
+ if d.ignoreNextIndent {
+ d.ignoreNextIndent = false
+ return
+ }
+ d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v
+}
+
+// dumpPtr handles formatting of pointers by indirecting them as necessary.
+func (d *dumpState) dumpPtr(v reflect.Value) {
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range d.pointers {
+ if depth >= d.depth {
+ delete(d.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by dereferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := d.pointers[addr]; ok && pd < d.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ d.pointers[addr] = d.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type information.
+ d.w.Write(openParenBytes)
+ d.w.Write(bytes.Repeat(asteriskBytes, indirects))
+ d.w.Write([]byte(ve.Type().String()))
+ d.w.Write(closeParenBytes)
+
+ // Display pointer information.
+ if len(pointerChain) > 0 {
+ d.w.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ d.w.Write(pointerChainBytes)
+ }
+ printHexPtr(d.w, addr)
+ }
+ d.w.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ d.w.Write(openParenBytes)
+ switch {
+ case nilFound == true:
+ d.w.Write(nilAngleBytes)
+
+ case cycleFound == true:
+ d.w.Write(circularBytes)
+
+ default:
+ d.ignoreNextType = true
+ d.dump(ve)
+ }
+ d.w.Write(closeParenBytes)
+}
+
+// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
+// reflection) arrays and slices are dumped in hexdump -C fashion.
+func (d *dumpState) dumpSlice(v reflect.Value) {
+ // Determine whether this type should be hex dumped or not. Also,
+ // for types which should be hexdumped, try to use the underlying data
+ // first, then fall back to trying to convert them to a uint8 slice.
+ var buf []uint8
+ doConvert := false
+ doHexDump := false
+ numEntries := v.Len()
+ if numEntries > 0 {
+ vt := v.Index(0).Type()
+ vts := vt.String()
+ switch {
+ // C types that need to be converted.
+ case cCharRE.MatchString(vts):
+ fallthrough
+ case cUnsignedCharRE.MatchString(vts):
+ fallthrough
+ case cUint8tCharRE.MatchString(vts):
+ doConvert = true
+
+ // Try to use existing uint8 slices and fall back to converting
+ // and copying if that fails.
+ case vt.Kind() == reflect.Uint8:
+ // TODO(davec): Fix up the disableUnsafe bits...
+
+ // We need an addressable interface to convert the type
+ // to a byte slice. However, the reflect package won't
+ // give us an interface on certain things like
+ // unexported struct fields in order to enforce
+ // visibility rules. We use unsafe, when available, to
+ // bypass these restrictions since this package does not
+ // mutate the values.
+ vs := v
+ if !vs.CanInterface() || !vs.CanAddr() {
+ vs = unsafeReflectValue(vs)
+ }
+ if !UnsafeDisabled {
+ vs = vs.Slice(0, numEntries)
+
+ // Use the existing uint8 slice if it can be
+ // type asserted.
+ iface := vs.Interface()
+ if slice, ok := iface.([]uint8); ok {
+ buf = slice
+ doHexDump = true
+ break
+ }
+ }
+
+ // The underlying data needs to be converted if it can't
+ // be type asserted to a uint8 slice.
+ doConvert = true
+ }
+
+ // Copy and convert the underlying type if needed.
+ if doConvert && vt.ConvertibleTo(uint8Type) {
+ // Convert and copy each element into a uint8 byte
+ // slice.
+ buf = make([]uint8, numEntries)
+ for i := 0; i < numEntries; i++ {
+ vv := v.Index(i)
+ buf[i] = uint8(vv.Convert(uint8Type).Uint())
+ }
+ doHexDump = true
+ }
+ }
+
+ // Hexdump the entire slice as needed.
+ if doHexDump {
+ indent := strings.Repeat(d.cs.Indent, d.depth)
+ str := indent + hex.Dump(buf)
+ str = strings.Replace(str, "\n", "\n"+indent, -1)
+ str = strings.TrimRight(str, d.cs.Indent)
+ d.w.Write([]byte(str))
+ return
+ }
+
+ // Recursively call dump for each item.
+ for i := 0; i < numEntries; i++ {
+ d.dump(d.unpackValue(v.Index(i)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+}
+
+// dump is the main workhorse for dumping a value. It uses the passed reflect
+// value to figure out what kind of object we are dealing with and formats it
+// appropriately. It is a recursive function, however circular data structures
+// are detected and handled properly.
+func (d *dumpState) dump(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ d.w.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ d.indent()
+ d.dumpPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !d.ignoreNextType {
+ d.indent()
+ d.w.Write(openParenBytes)
+ d.w.Write([]byte(v.Type().String()))
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+ d.ignoreNextType = false
+
+ // Display length and capacity if the built-in len and cap functions
+ // work with the value's kind and the len/cap itself is non-zero.
+ valueLen, valueCap := 0, 0
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Chan:
+ valueLen, valueCap = v.Len(), v.Cap()
+ case reflect.Map, reflect.String:
+ valueLen = v.Len()
+ }
+ if valueLen != 0 || valueCap != 0 {
+ d.w.Write(openParenBytes)
+ if valueLen != 0 {
+ d.w.Write(lenEqualsBytes)
+ printInt(d.w, int64(valueLen), 10)
+ }
+ if valueCap != 0 {
+ if valueLen != 0 {
+ d.w.Write(spaceBytes)
+ }
+ d.w.Write(capEqualsBytes)
+ printInt(d.w, int64(valueCap), 10)
+ }
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+
+ // Call Stringer/error interfaces if they exist and the handle methods flag
+ // is enabled
+ if !d.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(d.cs, d.w, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(d.w, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(d.w, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(d.w, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(d.w, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(d.w, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(d.w, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(d.w, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ d.dumpSlice(v)
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.String:
+ d.w.Write([]byte(strconv.Quote(v.String())))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ numEntries := v.Len()
+ keys := v.MapKeys()
+ if d.cs.SortKeys {
+ sortValues(keys, d.cs)
+ }
+ for i, key := range keys {
+ d.dump(d.unpackValue(key))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.MapIndex(key)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Struct:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ vt := v.Type()
+ numFields := v.NumField()
+ for i := 0; i < numFields; i++ {
+ d.indent()
+ vtf := vt.Field(i)
+ d.w.Write([]byte(vtf.Name))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.Field(i)))
+ if i < (numFields - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(d.w, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(d.w, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it in case any new
+ // types are added.
+ default:
+ if v.CanInterface() {
+ fmt.Fprintf(d.w, "%v", v.Interface())
+ } else {
+ fmt.Fprintf(d.w, "%v", v.String())
+ }
+ }
+}
+
+// fdump is a helper function to consolidate the logic from the various public
+// methods which take varying writers and config states.
+func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
+ for _, arg := range a {
+ if arg == nil {
+ w.Write(interfaceBytes)
+ w.Write(spaceBytes)
+ w.Write(nilAngleBytes)
+ w.Write(newlineBytes)
+ continue
+ }
+
+ d := dumpState{w: w, cs: cs}
+ d.pointers = make(map[uintptr]int)
+ d.dump(reflect.ValueOf(arg))
+ d.w.Write(newlineBytes)
+ }
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func Fdump(w io.Writer, a ...interface{}) {
+ fdump(&Config, w, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(&Config, &buf, a...)
+ return buf.String()
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by an exported package global,
+spew.Config. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func Dump(a ...interface{}) {
+ fdump(&Config, os.Stdout, a...)
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/dump_test.go b/vendor/github.com/davecgh/go-spew/spew/dump_test.go
new file mode 100644
index 000000000..2b320401d
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/dump_test.go
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (c) 2013 Dave Collins
+ *
+ * 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.
+ */
+
+/*
+Test Summary:
+NOTE: For each test, a nil pointer, a single pointer and double pointer to the
+base test element are also tested to ensure proper indirection across all types.
+
+- Max int8, int16, int32, int64, int
+- Max uint8, uint16, uint32, uint64, uint
+- Boolean true and false
+- Standard complex64 and complex128
+- Array containing standard ints
+- Array containing type with custom formatter on pointer receiver only
+- Array containing interfaces
+- Array containing bytes
+- Slice containing standard float32 values
+- Slice containing type with custom formatter on pointer receiver only
+- Slice containing interfaces
+- Slice containing bytes
+- Nil slice
+- Standard string
+- Nil interface
+- Sub-interface
+- Map with string keys and int vals
+- Map with custom formatter type on pointer receiver only keys and vals
+- Map with interface keys and values
+- Map with nil interface value
+- Struct with primitives
+- Struct that contains another struct
+- Struct that contains custom type with Stringer pointer interface via both
+ exported and unexported fields
+- Struct that contains embedded struct and field to same struct
+- Uintptr to 0 (null pointer)
+- Uintptr address of real variable
+- Unsafe.Pointer to 0 (null pointer)
+- Unsafe.Pointer to address of real variable
+- Nil channel
+- Standard int channel
+- Function with no params and no returns
+- Function with param and no returns
+- Function with multiple params and multiple returns
+- Struct that is circular through self referencing
+- Structs that are circular through cross referencing
+- Structs that are indirectly circular
+- Type that panics in its Stringer interface
+*/
+
+package spew_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+ "unsafe"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// dumpTest is used to describe a test to be perfomed against the Dump method.
+type dumpTest struct {
+ in interface{}
+ wants []string
+}
+
+// dumpTests houses all of the tests to be performed against the Dump method.
+var dumpTests = make([]dumpTest, 0)
+
+// addDumpTest is a helper method to append the passed input and desired result
+// to dumpTests
+func addDumpTest(in interface{}, wants ...string) {
+ test := dumpTest{in, wants}
+ dumpTests = append(dumpTests, test)
+}
+
+func addIntDumpTests() {
+ // Max int8.
+ v := int8(127)
+ nv := (*int8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int8"
+ vs := "127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Max int16.
+ v2 := int16(32767)
+ nv2 := (*int16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "int16"
+ v2s := "32767"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Max int32.
+ v3 := int32(2147483647)
+ nv3 := (*int32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "int32"
+ v3s := "2147483647"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Max int64.
+ v4 := int64(9223372036854775807)
+ nv4 := (*int64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "int64"
+ v4s := "9223372036854775807"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+
+ // Max int.
+ v5 := int(2147483647)
+ nv5 := (*int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "int"
+ v5s := "2147483647"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")()\n")
+}
+
+func addUintDumpTests() {
+ // Max uint8.
+ v := uint8(255)
+ nv := (*uint8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uint8"
+ vs := "255"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Max uint16.
+ v2 := uint16(65535)
+ nv2 := (*uint16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Max uint32.
+ v3 := uint32(4294967295)
+ nv3 := (*uint32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "uint32"
+ v3s := "4294967295"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Max uint64.
+ v4 := uint64(18446744073709551615)
+ nv4 := (*uint64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "uint64"
+ v4s := "18446744073709551615"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+
+ // Max uint.
+ v5 := uint(4294967295)
+ nv5 := (*uint)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "uint"
+ v5s := "4294967295"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")()\n")
+}
+
+func addBoolDumpTests() {
+ // Boolean true.
+ v := bool(true)
+ nv := (*bool)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "bool"
+ vs := "true"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Boolean false.
+ v2 := bool(false)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "bool"
+ v2s := "false"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addFloatDumpTests() {
+ // Standard float32.
+ v := float32(3.1415)
+ nv := (*float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "3.1415"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Standard float64.
+ v2 := float64(3.1415926)
+ nv2 := (*float64)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "float64"
+ v2s := "3.1415926"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+}
+
+func addComplexDumpTests() {
+ // Standard complex64.
+ v := complex(float32(6), -2)
+ nv := (*complex64)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "complex64"
+ vs := "(6-2i)"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Standard complex128.
+ v2 := complex(float64(-6), 2)
+ nv2 := (*complex128)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "complex128"
+ v2s := "(-6+2i)"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+}
+
+func addArrayDumpTests() {
+ // Array containing standard ints.
+ v := [3]int{1, 2, 3}
+ vLen := fmt.Sprintf("%d", len(v))
+ vCap := fmt.Sprintf("%d", cap(v))
+ nv := (*[3]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int"
+ vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 1,\n (" +
+ vt + ") 2,\n (" + vt + ") 3\n}"
+ addDumpTest(v, "([3]"+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*[3]"+vt+")()\n")
+
+ // Array containing type with custom formatter on pointer receiver only.
+ v2i0 := pstringer("1")
+ v2i1 := pstringer("2")
+ v2i2 := pstringer("3")
+ v2 := [3]pstringer{v2i0, v2i1, v2i2}
+ v2i0Len := fmt.Sprintf("%d", len(v2i0))
+ v2i1Len := fmt.Sprintf("%d", len(v2i1))
+ v2i2Len := fmt.Sprintf("%d", len(v2i2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ v2Cap := fmt.Sprintf("%d", cap(v2))
+ nv2 := (*[3]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.pstringer"
+ v2sp := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t +
+ ") (len=" + v2i0Len + ") stringer 1,\n (" + v2t +
+ ") (len=" + v2i1Len + ") stringer 2,\n (" + v2t +
+ ") (len=" + v2i2Len + ") " + "stringer 3\n}"
+ v2s := v2sp
+ if spew.UnsafeDisabled {
+ v2s = "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t +
+ ") (len=" + v2i0Len + ") \"1\",\n (" + v2t + ") (len=" +
+ v2i1Len + ") \"2\",\n (" + v2t + ") (len=" + v2i2Len +
+ ") " + "\"3\"\n}"
+ }
+ addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2sp+")\n")
+ addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2sp+")\n")
+ addDumpTest(nv2, "(*[3]"+v2t+")()\n")
+
+ // Array containing interfaces.
+ v3i0 := "one"
+ v3 := [3]interface{}{v3i0, int(2), uint(3)}
+ v3i0Len := fmt.Sprintf("%d", len(v3i0))
+ v3Len := fmt.Sprintf("%d", len(v3))
+ v3Cap := fmt.Sprintf("%d", cap(v3))
+ nv3 := (*[3]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[3]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " +
+ "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" +
+ v3t4 + ") 3\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Array containing bytes.
+ v4 := [34]byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+ v4Len := fmt.Sprintf("%d", len(v4))
+ v4Cap := fmt.Sprintf("%d", cap(v4))
+ nv4 := (*[34]byte)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[34]uint8"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
+ " |............... |\n" +
+ " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
+ " |!\"#$%&'()*+,-./0|\n" +
+ " 00000020 31 32 " +
+ " |12|\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+}
+
+func addSliceDumpTests() {
+ // Slice containing standard float32 values.
+ v := []float32{3.14, 6.28, 12.56}
+ vLen := fmt.Sprintf("%d", len(v))
+ vCap := fmt.Sprintf("%d", cap(v))
+ nv := (*[]float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 3.14,\n (" +
+ vt + ") 6.28,\n (" + vt + ") 12.56\n}"
+ addDumpTest(v, "([]"+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*[]"+vt+")()\n")
+
+ // Slice containing type with custom formatter on pointer receiver only.
+ v2i0 := pstringer("1")
+ v2i1 := pstringer("2")
+ v2i2 := pstringer("3")
+ v2 := []pstringer{v2i0, v2i1, v2i2}
+ v2i0Len := fmt.Sprintf("%d", len(v2i0))
+ v2i1Len := fmt.Sprintf("%d", len(v2i1))
+ v2i2Len := fmt.Sprintf("%d", len(v2i2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ v2Cap := fmt.Sprintf("%d", cap(v2))
+ nv2 := (*[]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.pstringer"
+ v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" +
+ v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len +
+ ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " +
+ "stringer 3\n}"
+ addDumpTest(v2, "([]"+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*[]"+v2t+")()\n")
+
+ // Slice containing interfaces.
+ v3i0 := "one"
+ v3 := []interface{}{v3i0, int(2), uint(3), nil}
+ v3i0Len := fmt.Sprintf("%d", len(v3i0))
+ v3Len := fmt.Sprintf("%d", len(v3))
+ v3Cap := fmt.Sprintf("%d", cap(v3))
+ nv3 := (*[]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3t5 := "interface {}"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " +
+ "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" +
+ v3t4 + ") 3,\n (" + v3t5 + ") \n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Slice containing bytes.
+ v4 := []byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+ v4Len := fmt.Sprintf("%d", len(v4))
+ v4Cap := fmt.Sprintf("%d", cap(v4))
+ nv4 := (*[]byte)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[]uint8"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
+ " |............... |\n" +
+ " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
+ " |!\"#$%&'()*+,-./0|\n" +
+ " 00000020 31 32 " +
+ " |12|\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+
+ // Nil slice.
+ v5 := []int(nil)
+ nv5 := (*[]int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "[]int"
+ v5s := ""
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")()\n")
+}
+
+func addStringDumpTests() {
+ // Standard string.
+ v := "test"
+ vLen := fmt.Sprintf("%d", len(v))
+ nv := (*string)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "string"
+ vs := "(len=" + vLen + ") \"test\""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+}
+
+func addInterfaceDumpTests() {
+ // Nil interface.
+ var v interface{}
+ nv := (*interface{})(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "interface {}"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Sub-interface.
+ v2 := interface{}(uint16(65535))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addMapDumpTests() {
+ // Map with string keys and int vals.
+ k := "one"
+ kk := "two"
+ m := map[string]int{k: 1, kk: 2}
+ klen := fmt.Sprintf("%d", len(k)) // not kLen to shut golint up
+ kkLen := fmt.Sprintf("%d", len(kk))
+ mLen := fmt.Sprintf("%d", len(m))
+ nilMap := map[string]int(nil)
+ nm := (*map[string]int)(nil)
+ pm := &m
+ mAddr := fmt.Sprintf("%p", pm)
+ pmAddr := fmt.Sprintf("%p", &pm)
+ mt := "map[string]int"
+ mt1 := "string"
+ mt2 := "int"
+ ms := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + klen + ") " +
+ "\"one\": (" + mt2 + ") 1,\n (" + mt1 + ") (len=" + kkLen +
+ ") \"two\": (" + mt2 + ") 2\n}"
+ ms2 := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + kkLen + ") " +
+ "\"two\": (" + mt2 + ") 2,\n (" + mt1 + ") (len=" + klen +
+ ") \"one\": (" + mt2 + ") 1\n}"
+ addDumpTest(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n")
+ addDumpTest(pm, "(*"+mt+")("+mAddr+")("+ms+")\n",
+ "(*"+mt+")("+mAddr+")("+ms2+")\n")
+ addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n",
+ "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n")
+ addDumpTest(nm, "(*"+mt+")()\n")
+ addDumpTest(nilMap, "("+mt+") \n")
+
+ // Map with custom formatter type on pointer receiver only keys and vals.
+ k2 := pstringer("one")
+ v2 := pstringer("1")
+ m2 := map[pstringer]pstringer{k2: v2}
+ k2Len := fmt.Sprintf("%d", len(k2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ m2Len := fmt.Sprintf("%d", len(m2))
+ nilMap2 := map[pstringer]pstringer(nil)
+ nm2 := (*map[pstringer]pstringer)(nil)
+ pm2 := &m2
+ m2Addr := fmt.Sprintf("%p", pm2)
+ pm2Addr := fmt.Sprintf("%p", &pm2)
+ m2t := "map[spew_test.pstringer]spew_test.pstringer"
+ m2t1 := "spew_test.pstringer"
+ m2t2 := "spew_test.pstringer"
+ m2s := "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + ") " +
+ "stringer one: (" + m2t2 + ") (len=" + v2Len + ") stringer 1\n}"
+ if spew.UnsafeDisabled {
+ m2s = "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len +
+ ") " + "\"one\": (" + m2t2 + ") (len=" + v2Len +
+ ") \"1\"\n}"
+ }
+ addDumpTest(m2, "("+m2t+") "+m2s+"\n")
+ addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n")
+ addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n")
+ addDumpTest(nm2, "(*"+m2t+")()\n")
+ addDumpTest(nilMap2, "("+m2t+") \n")
+
+ // Map with interface keys and values.
+ k3 := "one"
+ k3Len := fmt.Sprintf("%d", len(k3))
+ m3 := map[interface{}]interface{}{k3: 1}
+ m3Len := fmt.Sprintf("%d", len(m3))
+ nilMap3 := map[interface{}]interface{}(nil)
+ nm3 := (*map[interface{}]interface{})(nil)
+ pm3 := &m3
+ m3Addr := fmt.Sprintf("%p", pm3)
+ pm3Addr := fmt.Sprintf("%p", &pm3)
+ m3t := "map[interface {}]interface {}"
+ m3t1 := "string"
+ m3t2 := "int"
+ m3s := "(len=" + m3Len + ") {\n (" + m3t1 + ") (len=" + k3Len + ") " +
+ "\"one\": (" + m3t2 + ") 1\n}"
+ addDumpTest(m3, "("+m3t+") "+m3s+"\n")
+ addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n")
+ addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n")
+ addDumpTest(nm3, "(*"+m3t+")()\n")
+ addDumpTest(nilMap3, "("+m3t+") \n")
+
+ // Map with nil interface value.
+ k4 := "nil"
+ k4Len := fmt.Sprintf("%d", len(k4))
+ m4 := map[string]interface{}{k4: nil}
+ m4Len := fmt.Sprintf("%d", len(m4))
+ nilMap4 := map[string]interface{}(nil)
+ nm4 := (*map[string]interface{})(nil)
+ pm4 := &m4
+ m4Addr := fmt.Sprintf("%p", pm4)
+ pm4Addr := fmt.Sprintf("%p", &pm4)
+ m4t := "map[string]interface {}"
+ m4t1 := "string"
+ m4t2 := "interface {}"
+ m4s := "(len=" + m4Len + ") {\n (" + m4t1 + ") (len=" + k4Len + ")" +
+ " \"nil\": (" + m4t2 + ") \n}"
+ addDumpTest(m4, "("+m4t+") "+m4s+"\n")
+ addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n")
+ addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n")
+ addDumpTest(nm4, "(*"+m4t+")()\n")
+ addDumpTest(nilMap4, "("+m4t+") \n")
+}
+
+func addStructDumpTests() {
+ // Struct with primitives.
+ type s1 struct {
+ a int8
+ b uint8
+ }
+ v := s1{127, 255}
+ nv := (*s1)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.s1"
+ vt2 := "int8"
+ vt3 := "uint8"
+ vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Struct that contains another struct.
+ type s2 struct {
+ s1 s1
+ b bool
+ }
+ v2 := s2{s1{127, 255}, true}
+ nv2 := (*s2)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.s2"
+ v2t2 := "spew_test.s1"
+ v2t3 := "int8"
+ v2t4 := "uint8"
+ v2t5 := "bool"
+ v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" +
+ v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+
+ // Struct that contains custom type with Stringer pointer interface via both
+ // exported and unexported fields.
+ type s3 struct {
+ s pstringer
+ S pstringer
+ }
+ v3 := s3{"test", "test2"}
+ nv3 := (*s3)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.s3"
+ v3t2 := "spew_test.pstringer"
+ v3s := "{\n s: (" + v3t2 + ") (len=4) stringer test,\n S: (" + v3t2 +
+ ") (len=5) stringer test2\n}"
+ v3sp := v3s
+ if spew.UnsafeDisabled {
+ v3s = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" +
+ v3t2 + ") (len=5) \"test2\"\n}"
+ v3sp = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" +
+ v3t2 + ") (len=5) stringer test2\n}"
+ }
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3sp+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3sp+")\n")
+ addDumpTest(nv3, "(*"+v3t+")()\n")
+
+ // Struct that contains embedded struct and field to same struct.
+ e := embed{"embedstr"}
+ eLen := fmt.Sprintf("%d", len("embedstr"))
+ v4 := embedwrap{embed: &e, e: &e}
+ nv4 := (*embedwrap)(nil)
+ pv4 := &v4
+ eAddr := fmt.Sprintf("%p", &e)
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "spew_test.embedwrap"
+ v4t2 := "spew_test.embed"
+ v4t3 := "string"
+ v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 +
+ ") (len=" + eLen + ") \"embedstr\"\n }),\n e: (*" + v4t2 +
+ ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ")" +
+ " \"embedstr\"\n })\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")()\n")
+}
+
+func addUintptrDumpTests() {
+ // Null pointer.
+ v := uintptr(0)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uintptr"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+
+ // Address of real variable.
+ i := 1
+ v2 := uintptr(unsafe.Pointer(&i))
+ nv2 := (*uintptr)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uintptr"
+ v2s := fmt.Sprintf("%p", &i)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")()\n")
+}
+
+func addUnsafePointerDumpTests() {
+ // Null pointer.
+ v := unsafe.Pointer(uintptr(0))
+ nv := (*unsafe.Pointer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "unsafe.Pointer"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+
+ // Address of real variable.
+ i := 1
+ v2 := unsafe.Pointer(&i)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "unsafe.Pointer"
+ v2s := fmt.Sprintf("%p", &i)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv, "(*"+vt+")()\n")
+}
+
+func addChanDumpTests() {
+ // Nil channel.
+ var v chan int
+ pv := &v
+ nv := (*chan int)(nil)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "chan int"
+ vs := ""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(