Browse Source

lib/protocol, lib/discover, lib/db: Use protocol buffer serialization (fixes #3080)

This changes the BEP protocol to use protocol buffer serialization
instead of XDR, and therefore also the database format. The local
discovery protocol is also updated to be protocol buffer format.

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/3276
LGTM: AudriusButkevicius
Jakob Borg 9 năm trước cách đây
mục cha
commit
fa0101bd60
100 tập tin đã thay đổi với 16779 bổ sung4145 xóa
  1. 32 18
      build.go
  2. 13 25
      cmd/stdisco/main.go
  3. 32 21
      cmd/stindex/dump.go
  4. 21 16
      cmd/stindex/dumpsize.go
  5. 3 8
      cmd/stindex/main.go
  6. 22 15
      cmd/syncthing/gui.go
  7. 1 1
      cmd/syncthing/locations.go
  8. 1 1
      cmd/syncthing/main.go
  9. 11 2
      lib/connections/service.go
  10. 1 1
      lib/connections/structs.go
  11. 17 9
      lib/db/blockmap_test.go
  12. 4 16
      lib/db/leveldb.go
  13. 0 114
      lib/db/leveldb_convert.go
  14. 0 136
      lib/db/leveldb_convert_test.go
  15. 39 96
      lib/db/leveldb_dbinstance.go
  16. 41 29
      lib/db/leveldb_transactions.go
  17. 0 142
      lib/db/leveldb_xdr.go
  18. 6 5
      lib/db/set.go
  19. 89 89
      lib/db/set_test.go
  20. 57 0
      lib/db/structs.go
  21. 914 0
      lib/db/structs.pb.go
  22. 35 0
      lib/db/structs.proto
  23. BIN
      lib/db/testdata/oldformat.db.zip
  24. 0 52
      lib/db/truncated.go
  25. 45 27
      lib/discover/local.go
  26. 368 0
      lib/discover/local.pb.go
  27. 14 0
      lib/discover/local.proto
  28. 0 29
      lib/discover/localpackets.go
  29. 0 246
      lib/discover/localpackets_xdr.go
  30. 58 97
      lib/model/model.go
  31. 37 76
      lib/model/model_test.go
  32. 1 1
      lib/model/progressemitter.go
  33. 3 4
      lib/model/progressemitter_test.go
  34. 7 7
      lib/model/rwfolder.go
  35. 2 4
      lib/model/rwfolder_test.go
  36. 10 4
      lib/model/sharedpullerstate.go
  37. 7 7
      lib/protocol/benchmark_test.go
  38. 3999 0
      lib/protocol/bep.pb.go
  39. 189 0
      lib/protocol/bep.proto
  40. 96 0
      lib/protocol/bep_extensions.go
  41. 59 0
      lib/protocol/bufferpool.go
  42. 15 17
      lib/protocol/common_test.go
  43. 0 14
      lib/protocol/compression.go
  44. 2 2
      lib/protocol/conflict_test.go
  45. 0 4
      lib/protocol/debug.go
  46. 14 21
      lib/protocol/errors.go
  47. 0 70
      lib/protocol/fuzz.go
  48. 0 89
      lib/protocol/fuzz_test.go
  49. 0 44
      lib/protocol/header.go
  50. 31 6
      lib/protocol/hello.go
  51. 50 3
      lib/protocol/hello_test.go
  52. 0 186
      lib/protocol/message.go
  53. 0 1309
      lib/protocol/message_xdr.go
  54. 6 6
      lib/protocol/nativemodel_darwin.go
  55. 7 7
      lib/protocol/nativemodel_windows.go
  56. 356 349
      lib/protocol/protocol.go
  57. 90 223
      lib/protocol/protocol_test.go
  58. 112 31
      lib/protocol/vector.go
  59. 0 89
      lib/protocol/vector_compare.go
  60. 0 249
      lib/protocol/vector_compare_test.go
  61. 264 30
      lib/protocol/vector_test.go
  62. 0 35
      lib/protocol/vector_xdr.go
  63. 4 4
      lib/protocol/wireformat.go
  64. 1 1
      lib/scanner/blockqueue.go
  65. 29 35
      lib/scanner/walk.go
  66. 19 19
      lib/scanner/walk_test.go
  67. 3 4
      lib/symlinks/symlink_windows.go
  68. 80 0
      script/protofmt.go
  69. 36 0
      vendor/github.com/gogo/protobuf/LICENSE
  70. 89 0
      vendor/github.com/gogo/protobuf/codec/codec.go
  71. 168 0
      vendor/github.com/gogo/protobuf/gogoproto/doc.go
  72. 661 0
      vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go
  73. 308 0
      vendor/github.com/gogo/protobuf/gogoproto/helper.go
  74. 102 0
      vendor/github.com/gogo/protobuf/io/full.go
  75. 70 0
      vendor/github.com/gogo/protobuf/io/io.go
  76. 126 0
      vendor/github.com/gogo/protobuf/io/uint32.go
  77. 134 0
      vendor/github.com/gogo/protobuf/io/varint.go
  78. 608 0
      vendor/github.com/gogo/protobuf/jsonpb/jsonpb.go
  79. 120 0
      vendor/github.com/gogo/protobuf/jsonpb/jsonpb_test_proto/more_test_objects.pb.go
  80. 586 0
      vendor/github.com/gogo/protobuf/jsonpb/jsonpb_test_proto/test_objects.pb.go
  81. 520 0
      vendor/github.com/gogo/protobuf/plugin/compare/compare.go
  82. 105 0
      vendor/github.com/gogo/protobuf/plugin/compare/comparetest.go
  83. 131 0
      vendor/github.com/gogo/protobuf/plugin/defaultcheck/defaultcheck.go
  84. 199 0
      vendor/github.com/gogo/protobuf/plugin/description/description.go
  85. 71 0
      vendor/github.com/gogo/protobuf/plugin/description/descriptiontest.go
  86. 197 0
      vendor/github.com/gogo/protobuf/plugin/embedcheck/embedcheck.go
  87. 102 0
      vendor/github.com/gogo/protobuf/plugin/enumstringer/enumstringer.go
  88. 602 0
      vendor/github.com/gogo/protobuf/plugin/equal/equal.go
  89. 94 0
      vendor/github.com/gogo/protobuf/plugin/equal/equaltest.go
  90. 231 0
      vendor/github.com/gogo/protobuf/plugin/face/face.go
  91. 80 0
      vendor/github.com/gogo/protobuf/plugin/face/facetest.go
  92. 360 0
      vendor/github.com/gogo/protobuf/plugin/gostring/gostring.go
  93. 88 0
      vendor/github.com/gogo/protobuf/plugin/gostring/gostringtest.go
  94. 462 0
      vendor/github.com/gogo/protobuf/plugin/grpc/grpc.go
  95. 1323 0
      vendor/github.com/gogo/protobuf/plugin/marshalto/marshalto.go
  96. 91 0
      vendor/github.com/gogo/protobuf/plugin/oneofcheck/oneofcheck.go
  97. 774 0
      vendor/github.com/gogo/protobuf/plugin/populate/populate.go
  98. 599 0
      vendor/github.com/gogo/protobuf/plugin/size/size.go
  99. 132 0
      vendor/github.com/gogo/protobuf/plugin/size/sizetest.go
  100. 293 0
      vendor/github.com/gogo/protobuf/plugin/stringer/stringer.go

+ 32 - 18
build.go

@@ -238,8 +238,8 @@ func runCommand(cmd string, target target) {
 	case "assets":
 	case "assets":
 		rebuildAssets()
 		rebuildAssets()
 
 
-	case "xdr":
-		xdr()
+	case "proto":
+		proto()
 
 
 	case "translate":
 	case "translate":
 		translate()
 		translate()
@@ -271,9 +271,12 @@ func runCommand(cmd string, target target) {
 	case "metalint":
 	case "metalint":
 		if isGometalinterInstalled() {
 		if isGometalinterInstalled() {
 			dirs := []string{".", "./cmd/...", "./lib/..."}
 			dirs := []string{".", "./cmd/...", "./lib/..."}
-			gometalinter("deadcode", dirs, "test/util.go")
-			gometalinter("structcheck", dirs)
-			gometalinter("varcheck", dirs)
+			ok := gometalinter("deadcode", dirs, "test/util.go")
+			ok = gometalinter("structcheck", dirs) && ok
+			ok = gometalinter("varcheck", dirs) && ok
+			if !ok {
+				os.Exit(1)
+			}
 		}
 		}
 
 
 	default:
 	default:
@@ -576,8 +579,8 @@ func shouldRebuildAssets() bool {
 	return assetsAreNewer
 	return assetsAreNewer
 }
 }
 
 
-func xdr() {
-	runPrint("go", "generate", "./lib/discover", "./lib/db", "./lib/protocol", "./lib/relay/protocol")
+func proto() {
+	runPrint("go", "generate", "./lib/...")
 }
 }
 
 
 func translate() {
 func translate() {
@@ -956,13 +959,17 @@ func lint(pkg string) {
 	}
 	}
 
 
 	analCommentPolicy := regexp.MustCompile(`exported (function|method|const|type|var) [^\s]+ should have comment`)
 	analCommentPolicy := regexp.MustCompile(`exported (function|method|const|type|var) [^\s]+ should have comment`)
-	for _, line := range bytes.Split(bs, []byte("\n")) {
-		if analCommentPolicy.Match(line) {
+	for _, line := range strings.Split(string(bs), "\n") {
+		if line == "" {
+			continue
+		}
+		if analCommentPolicy.MatchString(line) {
 			continue
 			continue
 		}
 		}
-		if len(line) > 0 {
-			log.Printf("%s", line)
+		if strings.Contains(line, ".pb.go:") {
+			continue
 		}
 		}
+		log.Println(line)
 	}
 	}
 }
 }
 
 
@@ -1003,7 +1010,7 @@ func isGometalinterInstalled() bool {
 	return true
 	return true
 }
 }
 
 
-func gometalinter(linter string, dirs []string, excludes ...string) {
+func gometalinter(linter string, dirs []string, excludes ...string) bool {
 	params := []string{"--disable-all"}
 	params := []string{"--disable-all"}
 	params = append(params, fmt.Sprintf("--deadline=%ds", 60))
 	params = append(params, fmt.Sprintf("--deadline=%ds", 60))
 	params = append(params, "--enable="+linter)
 	params = append(params, "--enable="+linter)
@@ -1016,12 +1023,19 @@ func gometalinter(linter string, dirs []string, excludes ...string) {
 		params = append(params, dir)
 		params = append(params, dir)
 	}
 	}
 
 
-	bs, err := runError("gometalinter", params...)
+	bs, _ := runError("gometalinter", params...)
 
 
-	if len(bs) > 0 {
-		log.Printf("%s", bs)
-	}
-	if err != nil {
-		log.Printf("%v", err)
+	nerr := 0
+	for _, line := range strings.Split(string(bs), "\n") {
+		if line == "" {
+			continue
+		}
+		if strings.Contains(line, ".pb.go:") {
+			continue
+		}
+		log.Println(line)
+		nerr++
 	}
 	}
+
+	return nerr == 0
 }
 }

+ 13 - 25
cmd/stdisco/main.go

@@ -9,6 +9,7 @@ package main
 import (
 import (
 	"bytes"
 	"bytes"
 	"crypto/rand"
 	"crypto/rand"
+	"encoding/binary"
 	"flag"
 	"flag"
 	"log"
 	"log"
 	"strings"
 	"strings"
@@ -66,24 +67,25 @@ func recv(bc beacon.Interface) {
 	seen := make(map[string]bool)
 	seen := make(map[string]bool)
 	for {
 	for {
 		data, src := bc.Recv()
 		data, src := bc.Recv()
+		if m := binary.BigEndian.Uint32(data); m != discover.Magic {
+			log.Printf("Incorrect magic %x in announcement from %v", m, src)
+			continue
+		}
+
 		var ann discover.Announce
 		var ann discover.Announce
-		ann.UnmarshalXDR(data)
+		ann.Unmarshal(data[4:])
 
 
-		if bytes.Equal(ann.This.ID, myID) {
+		if bytes.Equal(ann.ID, myID) {
 			// This is one of our own fake packets, don't print it.
 			// This is one of our own fake packets, don't print it.
 			continue
 			continue
 		}
 		}
 
 
 		// Print announcement details for the first packet from a given
 		// Print announcement details for the first packet from a given
 		// device ID and source address, or if -all was given.
 		// device ID and source address, or if -all was given.
-		key := string(ann.This.ID) + src.String()
+		key := string(ann.ID) + src.String()
 		if all || !seen[key] {
 		if all || !seen[key] {
 			log.Printf("Announcement from %v\n", src)
 			log.Printf("Announcement from %v\n", src)
-			log.Printf(" %v at %s\n", protocol.DeviceIDFromBytes(ann.This.ID), strings.Join(addrStrs(ann.This), ", "))
-
-			for _, dev := range ann.Extra {
-				log.Printf(" %v at %s\n", protocol.DeviceIDFromBytes(dev.ID), strings.Join(addrStrs(dev), ", "))
-			}
+			log.Printf(" %v at %s\n", protocol.DeviceIDFromBytes(ann.ID), strings.Join(ann.Addresses, ", "))
 			seen[key] = true
 			seen[key] = true
 		}
 		}
 	}
 	}
@@ -92,15 +94,10 @@ func recv(bc beacon.Interface) {
 // sends fake discovery announcements once every second
 // sends fake discovery announcements once every second
 func send(bc beacon.Interface) {
 func send(bc beacon.Interface) {
 	ann := discover.Announce{
 	ann := discover.Announce{
-		Magic: discover.AnnouncementMagic,
-		This: discover.Device{
-			ID: myID,
-			Addresses: []discover.Address{
-				{URL: "tcp://fake.example.com:12345"},
-			},
-		},
+		ID:        myID,
+		Addresses: []string{"tcp://fake.example.com:12345"},
 	}
 	}
-	bs, _ := ann.MarshalXDR()
+	bs, _ := ann.Marshal()
 
 
 	for {
 	for {
 		bc.Send(bs)
 		bc.Send(bs)
@@ -108,15 +105,6 @@ func send(bc beacon.Interface) {
 	}
 	}
 }
 }
 
 
-// returns the list of address URLs
-func addrStrs(dev discover.Device) []string {
-	ss := make([]string, len(dev.Addresses))
-	for i, addr := range dev.Addresses {
-		ss[i] = addr.URL
-	}
-	return ss
-}
-
 // returns a random but recognizable device ID
 // returns a random but recognizable device ID
 func randomDeviceID() []byte {
 func randomDeviceID() []byte {
 	var id [32]byte
 	var id [32]byte

+ 32 - 21
cmd/stindex/dump.go

@@ -13,50 +13,61 @@ import (
 
 
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
-	"github.com/syndtr/goleveldb/leveldb"
 )
 )
 
 
-func dump(ldb *leveldb.DB) {
+func dump(ldb *db.Instance) {
 	it := ldb.NewIterator(nil, nil)
 	it := ldb.NewIterator(nil, nil)
-	var dev protocol.DeviceID
 	for it.Next() {
 	for it.Next() {
 		key := it.Key()
 		key := it.Key()
 		switch key[0] {
 		switch key[0] {
 		case db.KeyTypeDevice:
 		case db.KeyTypeDevice:
-			folder := nulString(key[1 : 1+64])
-			devBytes := key[1+64 : 1+64+32]
-			name := nulString(key[1+64+32:])
-			copy(dev[:], devBytes)
-			fmt.Printf("[device] F:%q N:%q D:%v\n", folder, name, dev)
+			folder := binary.BigEndian.Uint32(key[1:])
+			device := binary.BigEndian.Uint32(key[1+4:])
+			name := nulString(key[1+4+4:])
+			fmt.Printf("[device] F:%d D:%d N:%q", folder, device, name)
 
 
 			var f protocol.FileInfo
 			var f protocol.FileInfo
-			err := f.UnmarshalXDR(it.Value())
+			err := f.Unmarshal(it.Value())
 			if err != nil {
 			if err != nil {
 				log.Fatal(err)
 				log.Fatal(err)
 			}
 			}
-			fmt.Printf("  N:%q\n  F:%#o\n  M:%d\n  V:%v\n  S:%d\n  B:%d\n", f.Name, f.Flags, f.Modified, f.Version, f.Size(), len(f.Blocks))
+			fmt.Printf(" V:%v\n", f)
 
 
 		case db.KeyTypeGlobal:
 		case db.KeyTypeGlobal:
-			folder := nulString(key[1 : 1+64])
-			name := nulString(key[1+64:])
+			folder := binary.BigEndian.Uint32(key[1:])
+			name := nulString(key[1+4:])
 			var flv db.VersionList
 			var flv db.VersionList
-			flv.UnmarshalXDR(it.Value())
-			fmt.Printf("[global] F:%q N:%q V: %s\n", folder, name, flv)
+			flv.Unmarshal(it.Value())
+			fmt.Printf("[global] F:%d N:%q V:%s\n", folder, name, flv)
 
 
 		case db.KeyTypeBlock:
 		case db.KeyTypeBlock:
-			folder := nulString(key[1 : 1+64])
-			hash := key[1+64 : 1+64+32]
-			name := nulString(key[1+64+32:])
-			fmt.Printf("[block] F:%q H:%x N:%q I:%d\n", folder, hash, name, binary.BigEndian.Uint32(it.Value()))
+			folder := binary.BigEndian.Uint32(key[1:])
+			hash := key[1+4 : 1+4+32]
+			name := nulString(key[1+4+32:])
+			fmt.Printf("[block] F:%d H:%x N:%q I:%d\n", folder, hash, name, binary.BigEndian.Uint32(it.Value()))
 
 
 		case db.KeyTypeDeviceStatistic:
 		case db.KeyTypeDeviceStatistic:
-			fmt.Printf("[dstat]\n  %x\n  %x\n", it.Key(), it.Value())
+			fmt.Printf("[dstat] K:%x V:%x\n", it.Key(), it.Value())
 
 
 		case db.KeyTypeFolderStatistic:
 		case db.KeyTypeFolderStatistic:
-			fmt.Printf("[fstat]\n  %x\n  %x\n", it.Key(), it.Value())
+			fmt.Printf("[fstat] K:%x V:%x\n", it.Key(), it.Value())
 
 
 		case db.KeyTypeVirtualMtime:
 		case db.KeyTypeVirtualMtime:
-			fmt.Printf("[mtime]\n  %x\n  %x\n", it.Key(), it.Value())
+			fmt.Printf("[mtime] K:%x V:%x\n", it.Key(), it.Value())
+
+		case db.KeyTypeFolderIdx:
+			key := binary.BigEndian.Uint32(it.Key()[1:])
+			fmt.Printf("[folderidx] K:%d V:%q\n", key, it.Value())
+
+		case db.KeyTypeDeviceIdx:
+			key := binary.BigEndian.Uint32(it.Key()[1:])
+			val := it.Value()
+			if len(val) == 0 {
+				fmt.Printf("[deviceidx] K:%d V:<nil>\n", key)
+			} else {
+				dev := protocol.DeviceIDFromBytes(val)
+				fmt.Printf("[deviceidx] K:%d V:%s\n", key, dev)
+			}
 
 
 		default:
 		default:
 			fmt.Printf("[???]\n  %x\n  %x\n", it.Key(), it.Value())
 			fmt.Printf("[???]\n  %x\n  %x\n", it.Key(), it.Value())

+ 21 - 16
cmd/stindex/dumpsize.go

@@ -8,11 +8,10 @@ package main
 
 
 import (
 import (
 	"container/heap"
 	"container/heap"
+	"encoding/binary"
 	"fmt"
 	"fmt"
 
 
 	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/db"
-	"github.com/syncthing/syncthing/lib/protocol"
-	"github.com/syndtr/goleveldb/leveldb"
 )
 )
 
 
 type SizedElement struct {
 type SizedElement struct {
@@ -38,33 +37,31 @@ func (h *ElementHeap) Pop() interface{} {
 	return x
 	return x
 }
 }
 
 
-func dumpsize(ldb *leveldb.DB) {
+func dumpsize(ldb *db.Instance) {
 	h := &ElementHeap{}
 	h := &ElementHeap{}
 	heap.Init(h)
 	heap.Init(h)
 
 
 	it := ldb.NewIterator(nil, nil)
 	it := ldb.NewIterator(nil, nil)
-	var dev protocol.DeviceID
 	var ele SizedElement
 	var ele SizedElement
 	for it.Next() {
 	for it.Next() {
 		key := it.Key()
 		key := it.Key()
 		switch key[0] {
 		switch key[0] {
 		case db.KeyTypeDevice:
 		case db.KeyTypeDevice:
-			folder := nulString(key[1 : 1+64])
-			devBytes := key[1+64 : 1+64+32]
-			name := nulString(key[1+64+32:])
-			copy(dev[:], devBytes)
-			ele.key = fmt.Sprintf("DEVICE:%s:%s:%s", dev, folder, name)
+			folder := binary.BigEndian.Uint32(key[1:])
+			device := binary.BigEndian.Uint32(key[1+4:])
+			name := nulString(key[1+4+4:])
+			ele.key = fmt.Sprintf("DEVICE:%d:%d:%s", folder, device, name)
 
 
 		case db.KeyTypeGlobal:
 		case db.KeyTypeGlobal:
-			folder := nulString(key[1 : 1+64])
-			name := nulString(key[1+64:])
-			ele.key = fmt.Sprintf("GLOBAL:%s:%s", folder, name)
+			folder := binary.BigEndian.Uint32(key[1:])
+			name := nulString(key[1+4:])
+			ele.key = fmt.Sprintf("GLOBAL:%d:%s", folder, name)
 
 
 		case db.KeyTypeBlock:
 		case db.KeyTypeBlock:
-			folder := nulString(key[1 : 1+64])
-			hash := key[1+64 : 1+64+32]
-			name := nulString(key[1+64+32:])
-			ele.key = fmt.Sprintf("BLOCK:%s:%x:%s", folder, hash, name)
+			folder := binary.BigEndian.Uint32(key[1:])
+			hash := key[1+4 : 1+4+32]
+			name := nulString(key[1+4+32:])
+			ele.key = fmt.Sprintf("BLOCK:%d:%x:%s", folder, hash, name)
 
 
 		case db.KeyTypeDeviceStatistic:
 		case db.KeyTypeDeviceStatistic:
 			ele.key = fmt.Sprintf("DEVICESTATS:%s", key[1:])
 			ele.key = fmt.Sprintf("DEVICESTATS:%s", key[1:])
@@ -75,6 +72,14 @@ func dumpsize(ldb *leveldb.DB) {
 		case db.KeyTypeVirtualMtime:
 		case db.KeyTypeVirtualMtime:
 			ele.key = fmt.Sprintf("MTIME:%s", key[1:])
 			ele.key = fmt.Sprintf("MTIME:%s", key[1:])
 
 
+		case db.KeyTypeFolderIdx:
+			id := binary.BigEndian.Uint32(key[1:])
+			ele.key = fmt.Sprintf("FOLDERIDX:%d", id)
+
+		case db.KeyTypeDeviceIdx:
+			id := binary.BigEndian.Uint32(key[1:])
+			ele.key = fmt.Sprintf("DEVICEIDX:%d", id)
+
 		default:
 		default:
 			ele.key = fmt.Sprintf("UNKNOWN:%x", key)
 			ele.key = fmt.Sprintf("UNKNOWN:%x", key)
 		}
 		}

+ 3 - 8
cmd/stindex/main.go

@@ -13,8 +13,7 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 
 
-	"github.com/syndtr/goleveldb/leveldb"
-	"github.com/syndtr/goleveldb/leveldb/opt"
+	"github.com/syncthing/syncthing/lib/db"
 )
 )
 
 
 func main() {
 func main() {
@@ -28,16 +27,12 @@ func main() {
 
 
 	path := flag.Arg(0)
 	path := flag.Arg(0)
 	if path == "" {
 	if path == "" {
-		path = filepath.Join(defaultConfigDir(), "index-v0.11.0.db")
+		path = filepath.Join(defaultConfigDir(), "index-v0.14.0.db")
 	}
 	}
 
 
 	fmt.Println("Path:", path)
 	fmt.Println("Path:", path)
 
 
-	ldb, err := leveldb.OpenFile(path, &opt.Options{
-		ErrorIfMissing:         true,
-		Strict:                 opt.StrictAll,
-		OpenFilesCacheCapacity: 100,
-	})
+	ldb, err := db.Open(path)
 	if err != nil {
 	if err != nil {
 		log.Fatal(err)
 		log.Fatal(err)
 	}
 	}

+ 22 - 15
cmd/syncthing/gui.go

@@ -1173,13 +1173,17 @@ type jsonFileInfo protocol.FileInfo
 
 
 func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
 func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
 	return json.Marshal(map[string]interface{}{
 	return json.Marshal(map[string]interface{}{
-		"name":         f.Name,
-		"size":         protocol.FileInfo(f).Size(),
-		"flags":        fmt.Sprintf("%#o", f.Flags),
-		"modified":     time.Unix(f.Modified, 0),
-		"localVersion": f.LocalVersion,
-		"numBlocks":    len(f.Blocks),
-		"version":      jsonVersionVector(f.Version),
+		"name":          f.Name,
+		"type":          f.Type,
+		"size":          f.Size,
+		"permissions":   fmt.Sprintf("%#o", f.Permissions),
+		"deleted":       f.Deleted,
+		"invalid":       f.Invalid,
+		"noPermissions": f.NoPermissions,
+		"modified":      time.Unix(f.Modified, 0),
+		"localVersion":  f.LocalVersion,
+		"numBlocks":     len(f.Blocks),
+		"version":       jsonVersionVector(f.Version),
 	})
 	})
 }
 }
 
 
@@ -1187,20 +1191,23 @@ type jsonDBFileInfo db.FileInfoTruncated
 
 
 func (f jsonDBFileInfo) MarshalJSON() ([]byte, error) {
 func (f jsonDBFileInfo) MarshalJSON() ([]byte, error) {
 	return json.Marshal(map[string]interface{}{
 	return json.Marshal(map[string]interface{}{
-		"name":         f.Name,
-		"size":         db.FileInfoTruncated(f).Size(),
-		"flags":        fmt.Sprintf("%#o", f.Flags),
-		"modified":     time.Unix(f.Modified, 0),
-		"localVersion": f.LocalVersion,
-		"version":      jsonVersionVector(f.Version),
+		"name":          f.Name,
+		"type":          f.Type,
+		"size":          f.Size,
+		"permissions":   fmt.Sprintf("%#o", f.Permissions),
+		"deleted":       f.Deleted,
+		"invalid":       f.Invalid,
+		"noPermissions": f.NoPermissions,
+		"modified":      time.Unix(f.Modified, 0),
+		"localVersion":  f.LocalVersion,
 	})
 	})
 }
 }
 
 
 type jsonVersionVector protocol.Vector
 type jsonVersionVector protocol.Vector
 
 
 func (v jsonVersionVector) MarshalJSON() ([]byte, error) {
 func (v jsonVersionVector) MarshalJSON() ([]byte, error) {
-	res := make([]string, len(v))
-	for i, c := range v {
+	res := make([]string, len(v.Counters))
+	for i, c := range v.Counters {
 		res[i] = fmt.Sprintf("%v:%d", c.ID, c.Value)
 		res[i] = fmt.Sprintf("%v:%d", c.ID, c.Value)
 	}
 	}
 	return json.Marshal(res)
 	return json.Marshal(res)

+ 1 - 1
cmd/syncthing/locations.go

@@ -48,7 +48,7 @@ var locations = map[locationEnum]string{
 	locKeyFile:       "${config}/key.pem",
 	locKeyFile:       "${config}/key.pem",
 	locHTTPSCertFile: "${config}/https-cert.pem",
 	locHTTPSCertFile: "${config}/https-cert.pem",
 	locHTTPSKeyFile:  "${config}/https-key.pem",
 	locHTTPSKeyFile:  "${config}/https-key.pem",
-	locDatabase:      "${config}/index-v0.13.0.db",
+	locDatabase:      "${config}/index-v0.14.0.db",
 	locLogFile:       "${config}/syncthing.log", // -logfile on Windows
 	locLogFile:       "${config}/syncthing.log", // -logfile on Windows
 	locCsrfTokens:    "${config}/csrftokens.txt",
 	locCsrfTokens:    "${config}/csrftokens.txt",
 	locPanicLog:      "${config}/panic-${timestamp}.log",
 	locPanicLog:      "${config}/panic-${timestamp}.log",

+ 1 - 1
cmd/syncthing/main.go

@@ -690,7 +690,7 @@ func syncthingMain(runtimeOptions RuntimeOptions) {
 			if device == myID {
 			if device == myID {
 				continue
 				continue
 			}
 			}
-			m.Index(device, folderCfg.ID, nil, 0, nil)
+			m.Index(device, folderCfg.ID, nil)
 		}
 		}
 		m.StartFolder(folderCfg.ID)
 		m.StartFolder(folderCfg.ID)
 	}
 	}

+ 11 - 2
lib/connections/service.go

@@ -162,8 +162,17 @@ next:
 		if err != nil {
 		if err != nil {
 			if protocol.IsVersionMismatch(err) {
 			if protocol.IsVersionMismatch(err) {
 				// The error will be a relatively user friendly description
 				// The error will be a relatively user friendly description
-				// of what's wrong with the version compatibility
-				msg := fmt.Sprintf("Connecting to %s (%s): %s", remoteID, c.RemoteAddr(), err)
+				// of what's wrong with the version compatibility. By
+				// default identify the other side by device ID and IP.
+				remote := fmt.Sprintf("%v (%v)", remoteID, c.RemoteAddr())
+				if hello.DeviceName != "" {
+					// If the name was set in the hello return, use that to
+					// give the user more info about which device is the
+					// affected one. It probably says more than the remote
+					// IP.
+					remote = fmt.Sprintf("%q (%s %s, %v)", hello.DeviceName, hello.ClientName, hello.ClientVersion, remoteID)
+				}
+				msg := fmt.Sprintf("Connecting to %s: %s", remote, err)
 				warningFor(remoteID, msg)
 				warningFor(remoteID, msg)
 			} else {
 			} else {
 				// It's something else - connection reset or whatever
 				// It's something else - connection reset or whatever

+ 1 - 1
lib/connections/structs.go

@@ -70,7 +70,7 @@ type Model interface {
 	ConnectedTo(remoteID protocol.DeviceID) bool
 	ConnectedTo(remoteID protocol.DeviceID) bool
 	IsPaused(remoteID protocol.DeviceID) bool
 	IsPaused(remoteID protocol.DeviceID) bool
 	OnHello(protocol.DeviceID, net.Addr, protocol.HelloResult)
 	OnHello(protocol.DeviceID, net.Addr, protocol.HelloResult)
-	GetHello(protocol.DeviceID) protocol.Version13HelloMessage
+	GetHello(protocol.DeviceID) protocol.HelloIntf
 }
 }
 
 
 // serviceFunc wraps a function to create a suture.Service without stop
 // serviceFunc wraps a function to create a suture.Service without stop

+ 17 - 9
lib/db/blockmap_test.go

@@ -73,7 +73,7 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
 
 
 	m := NewBlockMap(db, db.folderIdx.ID([]byte("folder1")))
 	m := NewBlockMap(db, db.folderIdx.ID([]byte("folder1")))
 
 
-	f3.Flags |= protocol.FlagDirectory
+	f3.Type = protocol.FileInfoTypeDirectory
 
 
 	err := m.Add([]protocol.FileInfo{f1, f2, f3})
 	err := m.Add([]protocol.FileInfo{f1, f2, f3})
 	if err != nil {
 	if err != nil {
@@ -99,9 +99,11 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
 		return true
 		return true
 	})
 	})
 
 
-	f3.Flags = f1.Flags
-	f1.Flags |= protocol.FlagDeleted
-	f2.Flags |= protocol.FlagInvalid
+	f3.Permissions = f1.Permissions
+	f3.Deleted = f1.Deleted
+	f3.Invalid = f1.Invalid
+	f1.Deleted = true
+	f2.Invalid = true
 
 
 	// Should remove
 	// Should remove
 	err = m.Update([]protocol.FileInfo{f1, f2, f3})
 	err = m.Update([]protocol.FileInfo{f1, f2, f3})
@@ -145,9 +147,15 @@ func TestBlockMapAddUpdateWipe(t *testing.T) {
 		t.Fatal("db not empty")
 		t.Fatal("db not empty")
 	}
 	}
 
 
-	f1.Flags = 0
-	f2.Flags = 0
-	f3.Flags = 0
+	f1.Deleted = false
+	f1.Invalid = false
+	f1.Permissions = 0
+	f2.Deleted = false
+	f2.Invalid = false
+	f2.Permissions = 0
+	f3.Deleted = false
+	f3.Invalid = false
+	f3.Permissions = 0
 }
 }
 
 
 func TestBlockFinderLookup(t *testing.T) {
 func TestBlockFinderLookup(t *testing.T) {
@@ -187,7 +195,7 @@ func TestBlockFinderLookup(t *testing.T) {
 		t.Fatal("Incorrect count", counter)
 		t.Fatal("Incorrect count", counter)
 	}
 	}
 
 
-	f1.Flags |= protocol.FlagDeleted
+	f1.Deleted = true
 
 
 	err = m1.Update([]protocol.FileInfo{f1})
 	err = m1.Update([]protocol.FileInfo{f1})
 	if err != nil {
 	if err != nil {
@@ -212,7 +220,7 @@ func TestBlockFinderLookup(t *testing.T) {
 		t.Fatal("Incorrect count")
 		t.Fatal("Incorrect count")
 	}
 	}
 
 
-	f1.Flags = 0
+	f1.Deleted = false
 }
 }
 
 
 func TestBlockFinderFix(t *testing.T) {
 func TestBlockFinderFix(t *testing.T) {

+ 4 - 16
lib/db/leveldb.go

@@ -4,9 +4,6 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-//go:generate -command genxdr go run ../../vendor/github.com/calmh/xdr/cmd/genxdr/main.go
-//go:generate genxdr -o leveldb_xdr.go leveldb.go
-
 package db
 package db
 
 
 import (
 import (
@@ -46,25 +43,16 @@ const (
 	KeyTypeDeviceIdx
 	KeyTypeDeviceIdx
 )
 )
 
 
-type fileVersion struct {
-	version protocol.Vector
-	device  []byte
-}
-
-type VersionList struct {
-	versions []fileVersion
-}
-
 func (l VersionList) String() string {
 func (l VersionList) String() string {
 	var b bytes.Buffer
 	var b bytes.Buffer
 	var id protocol.DeviceID
 	var id protocol.DeviceID
 	b.WriteString("{")
 	b.WriteString("{")
-	for i, v := range l.versions {
+	for i, v := range l.Versions {
 		if i > 0 {
 		if i > 0 {
 			b.WriteString(", ")
 			b.WriteString(", ")
 		}
 		}
-		copy(id[:], v.device)
-		fmt.Fprintf(&b, "{%d, %v}", v.version, id)
+		copy(id[:], v.Device)
+		fmt.Fprintf(&b, "{%d, %v}", v.Version, id)
 	}
 	}
 	b.WriteString("}")
 	b.WriteString("}")
 	return b.String()
 	return b.String()
@@ -101,7 +89,7 @@ func getFile(db dbReader, key []byte) (protocol.FileInfo, bool) {
 	}
 	}
 
 
 	var f protocol.FileInfo
 	var f protocol.FileInfo
-	err = f.UnmarshalXDR(bs)
+	err = f.Unmarshal(bs)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}

+ 0 - 114
lib/db/leveldb_convert.go

@@ -1,114 +0,0 @@
-// Copyright (C) 2015 The Syncthing Authors.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-package db
-
-import (
-	"bytes"
-
-	"github.com/syndtr/goleveldb/leveldb"
-)
-
-// convertKeyFormat converts from the v0.12 to the v0.13 database format, to
-// avoid having to do rescan. The change is in the key format for folder
-// labels, so we basically just iterate over the database rewriting keys as
-// necessary.
-func convertKeyFormat(from, to *leveldb.DB) error {
-	l.Infoln("Converting database key format")
-	blocks, files, globals, unchanged := 0, 0, 0, 0
-
-	dbi := newDBInstance(to)
-	i := from.NewIterator(nil, nil)
-	for i.Next() {
-		key := i.Key()
-		switch key[0] {
-		case KeyTypeBlock:
-			folder, file := oldFromBlockKey(key)
-			folderIdx := dbi.folderIdx.ID([]byte(folder))
-			hash := key[1+64:]
-			newKey := blockKeyInto(nil, hash, folderIdx, file)
-			if err := to.Put(newKey, i.Value(), nil); err != nil {
-				return err
-			}
-			blocks++
-
-		case KeyTypeDevice:
-			newKey := dbi.deviceKey(oldDeviceKeyFolder(key), oldDeviceKeyDevice(key), oldDeviceKeyName(key))
-			if err := to.Put(newKey, i.Value(), nil); err != nil {
-				return err
-			}
-			files++
-
-		case KeyTypeGlobal:
-			newKey := dbi.globalKey(oldGlobalKeyFolder(key), oldGlobalKeyName(key))
-			if err := to.Put(newKey, i.Value(), nil); err != nil {
-				return err
-			}
-			globals++
-
-		case KeyTypeVirtualMtime:
-			// Cannot be converted, we drop it instead :(
-
-		default:
-			if err := to.Put(key, i.Value(), nil); err != nil {
-				return err
-			}
-			unchanged++
-		}
-	}
-
-	l.Infof("Converted %d blocks, %d files, %d globals (%d unchanged).", blocks, files, globals, unchanged)
-
-	return nil
-}
-
-func oldDeviceKeyFolder(key []byte) []byte {
-	folder := key[1 : 1+64]
-	izero := bytes.IndexByte(folder, 0)
-	if izero < 0 {
-		return folder
-	}
-	return folder[:izero]
-}
-
-func oldDeviceKeyDevice(key []byte) []byte {
-	return key[1+64 : 1+64+32]
-}
-
-func oldDeviceKeyName(key []byte) []byte {
-	return key[1+64+32:]
-}
-
-func oldGlobalKeyName(key []byte) []byte {
-	return key[1+64:]
-}
-
-func oldGlobalKeyFolder(key []byte) []byte {
-	folder := key[1 : 1+64]
-	izero := bytes.IndexByte(folder, 0)
-	if izero < 0 {
-		return folder
-	}
-	return folder[:izero]
-}
-
-func oldFromBlockKey(data []byte) (string, string) {
-	if len(data) < 1+64+32+1 {
-		panic("Incorrect key length")
-	}
-	if data[0] != KeyTypeBlock {
-		panic("Incorrect key type")
-	}
-
-	file := string(data[1+64+32:])
-
-	slice := data[1 : 1+64]
-	izero := bytes.IndexByte(slice, 0)
-	if izero > -1 {
-		return string(slice[:izero]), file
-	}
-	return string(slice), file
-}

+ 0 - 136
lib/db/leveldb_convert_test.go

@@ -1,136 +0,0 @@
-// Copyright (C) 2015 The Syncthing Authors.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-package db
-
-import (
-	"archive/zip"
-	"io"
-	"os"
-	"path/filepath"
-	"testing"
-
-	"github.com/syndtr/goleveldb/leveldb"
-)
-
-func TestLabelConversion(t *testing.T) {
-	os.RemoveAll("testdata/oldformat.db")
-	defer os.RemoveAll("testdata/oldformat.db")
-	os.RemoveAll("testdata/newformat.db")
-	defer os.RemoveAll("testdata/newformat.db")
-
-	if err := unzip("testdata/oldformat.db.zip", "testdata"); err != nil {
-		t.Fatal(err)
-	}
-
-	odb, err := leveldb.OpenFile("testdata/oldformat.db", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	ldb, err := leveldb.OpenFile("testdata/newformat.db", nil)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if err = convertKeyFormat(odb, ldb); err != nil {
-		t.Fatal(err)
-	}
-	ldb.Close()
-	odb.Close()
-
-	inst, err := Open("testdata/newformat.db")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	fs := NewFileSet("default", inst)
-	files, deleted, _ := fs.GlobalSize()
-	if files+deleted != 953 {
-		// Expected number of global entries determined by
-		// ../../bin/stindex testdata/oldformat.db/ | grep global | grep -c default
-		t.Errorf("Conversion error, global list differs (%d != 953)", files+deleted)
-	}
-
-	files, deleted, _ = fs.LocalSize()
-	if files+deleted != 953 {
-		t.Errorf("Conversion error, device list differs (%d != 953)", files+deleted)
-	}
-
-	f := NewBlockFinder(inst)
-	// [block] F:"default" H:1c25dea9003cc16216e2a22900be1ec1cc5aaf270442904e2f9812c314e929d8 N:"f/f2/f25f1b3e6e029231b933531b2138796d" I:3
-	h := []byte{0x1c, 0x25, 0xde, 0xa9, 0x00, 0x3c, 0xc1, 0x62, 0x16, 0xe2, 0xa2, 0x29, 0x00, 0xbe, 0x1e, 0xc1, 0xcc, 0x5a, 0xaf, 0x27, 0x04, 0x42, 0x90, 0x4e, 0x2f, 0x98, 0x12, 0xc3, 0x14, 0xe9, 0x29, 0xd8}
-	found := 0
-	f.Iterate([]string{"default"}, h, func(folder, file string, idx int32) bool {
-		if folder == "default" && file == filepath.FromSlash("f/f2/f25f1b3e6e029231b933531b2138796d") && idx == 3 {
-			found++
-		}
-		return true
-	})
-	if found != 1 {
-		t.Errorf("Found %d blocks instead of expected 1", found)
-	}
-
-	inst.Close()
-}
-
-func unzip(src, dest string) error {
-	r, err := zip.OpenReader(src)
-	if err != nil {
-		return err
-	}
-	defer func() {
-		if err := r.Close(); err != nil {
-			panic(err)
-		}
-	}()
-
-	os.MkdirAll(dest, 0755)
-
-	// Closure to address file descriptors issue with all the deferred .Close() methods
-	extractAndWriteFile := func(f *zip.File) error {
-		rc, err := f.Open()
-		if err != nil {
-			return err
-		}
-		defer func() {
-			if err := rc.Close(); err != nil {
-				panic(err)
-			}
-		}()
-
-		path := filepath.Join(dest, f.Name)
-
-		if f.FileInfo().IsDir() {
-			os.MkdirAll(path, f.Mode())
-		} else {
-			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
-			if err != nil {
-				return err
-			}
-			defer func() {
-				if err := f.Close(); err != nil {
-					panic(err)
-				}
-			}()
-
-			_, err = io.Copy(f, rc)
-			if err != nil {
-				return err
-			}
-		}
-		return nil
-	}
-
-	for _, f := range r.File {
-		err := extractAndWriteFile(f)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}

+ 39 - 96
lib/db/leveldb_dbinstance.go

@@ -10,12 +10,10 @@ import (
 	"bytes"
 	"bytes"
 	"encoding/binary"
 	"encoding/binary"
 	"os"
 	"os"
-	"path/filepath"
 	"sort"
 	"sort"
 	"strings"
 	"strings"
 	"sync/atomic"
 	"sync/atomic"
 
 
-	"github.com/syncthing/syncthing/lib/osutil"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syndtr/goleveldb/leveldb"
 	"github.com/syndtr/goleveldb/leveldb"
@@ -48,16 +46,6 @@ func Open(file string) (*Instance, error) {
 		WriteBuffer:            4 << 20,
 		WriteBuffer:            4 << 20,
 	}
 	}
 
 
-	if _, err := os.Stat(file); os.IsNotExist(err) {
-		// The file we are looking to open does not exist. This may be the
-		// first launch so we should look for an old version and try to
-		// convert it.
-		if err := checkConvertDatabase(file); err != nil {
-			l.Infoln("Converting old database:", err)
-			l.Infoln("Will rescan from scratch.")
-		}
-	}
-
 	db, err := leveldb.OpenFile(file, opts)
 	db, err := leveldb.OpenFile(file, opts)
 	if leveldbIsCorrupted(err) {
 	if leveldbIsCorrupted(err) {
 		db, err = leveldb.RecoverFile(file, opts)
 		db, err = leveldb.RecoverFile(file, opts)
@@ -151,12 +139,12 @@ func (db *Instance) genericReplace(folder, device []byte, fs []protocol.FileInfo
 
 
 		case moreFs && moreDb && cmp == 0:
 		case moreFs && moreDb && cmp == 0:
 			// File exists on both sides - compare versions. We might get an
 			// File exists on both sides - compare versions. We might get an
-			// update with the same version and different flags if a device has
-			// marked a file as invalid, so handle that too.
+			// update with the same version if a device has marked a file as
+			// invalid, so handle that too.
 			l.Debugln("generic replace; exists - compare")
 			l.Debugln("generic replace; exists - compare")
 			var ef FileInfoTruncated
 			var ef FileInfoTruncated
-			ef.UnmarshalXDR(dbi.Value())
-			if !fs[fsi].Version.Equal(ef.Version) || fs[fsi].Flags != ef.Flags {
+			ef.Unmarshal(dbi.Value())
+			if !fs[fsi].Version.Equal(ef.Version) || fs[fsi].Invalid != ef.Invalid {
 				l.Debugln("generic replace; differs - insert")
 				l.Debugln("generic replace; differs - insert")
 				if lv := t.insertFile(folder, device, fs[fsi]); lv > maxLocalVer {
 				if lv := t.insertFile(folder, device, fs[fsi]); lv > maxLocalVer {
 					maxLocalVer = lv
 					maxLocalVer = lv
@@ -232,13 +220,12 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, l
 		}
 		}
 
 
 		var ef FileInfoTruncated
 		var ef FileInfoTruncated
-		err = ef.UnmarshalXDR(bs)
+		err = ef.Unmarshal(bs)
 		if err != nil {
 		if err != nil {
 			panic(err)
 			panic(err)
 		}
 		}
-		// Flags might change without the version being bumped when we set the
-		// invalid flag on an existing file.
-		if !ef.Version.Equal(f.Version) || ef.Flags != f.Flags {
+		// The Invalid flag might change without the version being bumped.
+		if !ef.Version.Equal(f.Version) || ef.Invalid != f.Invalid {
 			if isLocalDevice {
 			if isLocalDevice {
 				localSize.removeFile(ef)
 				localSize.removeFile(ef)
 				localSize.addFile(f)
 				localSize.addFile(f)
@@ -308,7 +295,7 @@ func (db *Instance) withAllFolderTruncated(folder []byte, fn func(device []byte,
 		// struct, which in turn references the buffer it was unmarshalled
 		// struct, which in turn references the buffer it was unmarshalled
 		// from. dbi.Value() just returns an internal slice that it reuses, so
 		// from. dbi.Value() just returns an internal slice that it reuses, so
 		// we need to copy it.
 		// we need to copy it.
-		err := f.UnmarshalXDR(append([]byte{}, dbi.Value()...))
+		err := f.Unmarshal(append([]byte{}, dbi.Value()...))
 		if err != nil {
 		if err != nil {
 			panic(err)
 			panic(err)
 		}
 		}
@@ -347,16 +334,16 @@ func (db *Instance) getGlobal(folder, file []byte, truncate bool) (FileIntf, boo
 	}
 	}
 
 
 	var vl VersionList
 	var vl VersionList
-	err = vl.UnmarshalXDR(bs)
+	err = vl.Unmarshal(bs)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
-	if len(vl.versions) == 0 {
+	if len(vl.Versions) == 0 {
 		l.Debugln(k)
 		l.Debugln(k)
 		panic("no versions?")
 		panic("no versions?")
 	}
 	}
 
 
-	k = db.deviceKey(folder, vl.versions[0].device, file)
+	k = db.deviceKey(folder, vl.Versions[0].Device, file)
 	bs, err = t.Get(k, nil)
 	bs, err = t.Get(k, nil)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
@@ -384,11 +371,11 @@ func (db *Instance) withGlobal(folder, prefix []byte, truncate bool, fn Iterator
 	var fk []byte
 	var fk []byte
 	for dbi.Next() {
 	for dbi.Next() {
 		var vl VersionList
 		var vl VersionList
-		err := vl.UnmarshalXDR(dbi.Value())
+		err := vl.Unmarshal(dbi.Value())
 		if err != nil {
 		if err != nil {
 			panic(err)
 			panic(err)
 		}
 		}
-		if len(vl.versions) == 0 {
+		if len(vl.Versions) == 0 {
 			l.Debugln(dbi.Key())
 			l.Debugln(dbi.Key())
 			panic("no versions?")
 			panic("no versions?")
 		}
 		}
@@ -398,13 +385,13 @@ func (db *Instance) withGlobal(folder, prefix []byte, truncate bool, fn Iterator
 			return
 			return
 		}
 		}
 
 
-		fk = db.deviceKeyInto(fk[:cap(fk)], folder, vl.versions[0].device, name)
+		fk = db.deviceKeyInto(fk[:cap(fk)], folder, vl.Versions[0].Device, name)
 		bs, err := t.Get(fk, nil)
 		bs, err := t.Get(fk, nil)
 		if err != nil {
 		if err != nil {
 			l.Debugf("folder: %q (%x)", folder, folder)
 			l.Debugf("folder: %q (%x)", folder, folder)
 			l.Debugf("key: %q (%x)", dbi.Key(), dbi.Key())
 			l.Debugf("key: %q (%x)", dbi.Key(), dbi.Key())
 			l.Debugf("vl: %v", vl)
 			l.Debugf("vl: %v", vl)
-			l.Debugf("vl.versions[0].device: %x", vl.versions[0].device)
+			l.Debugf("vl.Versions[0].Device: %x", vl.Versions[0].Device)
 			l.Debugf("name: %q (%x)", name, name)
 			l.Debugf("name: %q (%x)", name, name)
 			l.Debugf("fk: %q", fk)
 			l.Debugf("fk: %q", fk)
 			l.Debugf("fk: %x %x %x",
 			l.Debugf("fk: %x %x %x",
@@ -436,17 +423,17 @@ func (db *Instance) availability(folder, file []byte) []protocol.DeviceID {
 	}
 	}
 
 
 	var vl VersionList
 	var vl VersionList
-	err = vl.UnmarshalXDR(bs)
+	err = vl.Unmarshal(bs)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
 
 
 	var devices []protocol.DeviceID
 	var devices []protocol.DeviceID
-	for _, v := range vl.versions {
-		if !v.version.Equal(vl.versions[0].version) {
+	for _, v := range vl.Versions {
+		if !v.Version.Equal(vl.Versions[0].Version) {
 			break
 			break
 		}
 		}
-		n := protocol.DeviceIDFromBytes(v.device)
+		n := protocol.DeviceIDFromBytes(v.Device)
 		devices = append(devices, n)
 		devices = append(devices, n)
 	}
 	}
 
 
@@ -464,11 +451,11 @@ func (db *Instance) withNeed(folder, device []byte, truncate bool, fn Iterator)
 nextFile:
 nextFile:
 	for dbi.Next() {
 	for dbi.Next() {
 		var vl VersionList
 		var vl VersionList
-		err := vl.UnmarshalXDR(dbi.Value())
+		err := vl.Unmarshal(dbi.Value())
 		if err != nil {
 		if err != nil {
 			panic(err)
 			panic(err)
 		}
 		}
-		if len(vl.versions) == 0 {
+		if len(vl.Versions) == 0 {
 			l.Debugln(dbi.Key())
 			l.Debugln(dbi.Key())
 			panic("no versions?")
 			panic("no versions?")
 		}
 		}
@@ -476,29 +463,29 @@ nextFile:
 		have := false // If we have the file, any version
 		have := false // If we have the file, any version
 		need := false // If we have a lower version of the file
 		need := false // If we have a lower version of the file
 		var haveVersion protocol.Vector
 		var haveVersion protocol.Vector
-		for _, v := range vl.versions {
-			if bytes.Equal(v.device, device) {
+		for _, v := range vl.Versions {
+			if bytes.Equal(v.Device, device) {
 				have = true
 				have = true
-				haveVersion = v.version
+				haveVersion = v.Version
 				// XXX: This marks Concurrent (i.e. conflicting) changes as
 				// XXX: This marks Concurrent (i.e. conflicting) changes as
 				// needs. Maybe we should do that, but it needs special
 				// needs. Maybe we should do that, but it needs special
 				// handling in the puller.
 				// handling in the puller.
-				need = !v.version.GreaterEqual(vl.versions[0].version)
+				need = !v.Version.GreaterEqual(vl.Versions[0].Version)
 				break
 				break
 			}
 			}
 		}
 		}
 
 
 		if need || !have {
 		if need || !have {
 			name := db.globalKeyName(dbi.Key())
 			name := db.globalKeyName(dbi.Key())
-			needVersion := vl.versions[0].version
+			needVersion := vl.Versions[0].Version
 
 
 		nextVersion:
 		nextVersion:
-			for i := range vl.versions {
-				if !vl.versions[i].version.Equal(needVersion) {
+			for i := range vl.Versions {
+				if !vl.Versions[i].Version.Equal(needVersion) {
 					// We haven't found a valid copy of the file with the needed version.
 					// We haven't found a valid copy of the file with the needed version.
 					continue nextFile
 					continue nextFile
 				}
 				}
-				fk = db.deviceKeyInto(fk[:cap(fk)], folder, vl.versions[i].device, name)
+				fk = db.deviceKeyInto(fk[:cap(fk)], folder, vl.Versions[i].Device, name)
 				bs, err := t.Get(fk, nil)
 				bs, err := t.Get(fk, nil)
 				if err != nil {
 				if err != nil {
 					var id protocol.DeviceID
 					var id protocol.DeviceID
@@ -528,7 +515,7 @@ nextFile:
 					continue nextFile
 					continue nextFile
 				}
 				}
 
 
-				l.Debugf("need folder=%q device=%v name=%q need=%v have=%v haveV=%d globalV=%d", folder, protocol.DeviceIDFromBytes(device), name, need, have, haveVersion, vl.versions[0].version)
+				l.Debugf("need folder=%q device=%v name=%q need=%v have=%v haveV=%d globalV=%d", folder, protocol.DeviceIDFromBytes(device), name, need, have, haveVersion, vl.Versions[0].Version)
 
 
 				if cont := fn(gf); !cont {
 				if cont := fn(gf); !cont {
 					return
 					return
@@ -601,7 +588,7 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
 	for dbi.Next() {
 	for dbi.Next() {
 		gk := dbi.Key()
 		gk := dbi.Key()
 		var vl VersionList
 		var vl VersionList
-		err := vl.UnmarshalXDR(dbi.Value())
+		err := vl.Unmarshal(dbi.Value())
 		if err != nil {
 		if err != nil {
 			panic(err)
 			panic(err)
 		}
 		}
@@ -613,8 +600,8 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
 
 
 		name := db.globalKeyName(gk)
 		name := db.globalKeyName(gk)
 		var newVL VersionList
 		var newVL VersionList
-		for i, version := range vl.versions {
-			fk = db.deviceKeyInto(fk[:cap(fk)], folder, version.device, name)
+		for i, version := range vl.Versions {
+			fk = db.deviceKeyInto(fk[:cap(fk)], folder, version.Device, name)
 
 
 			_, err := t.Get(fk, nil)
 			_, err := t.Get(fk, nil)
 			if err == leveldb.ErrNotFound {
 			if err == leveldb.ErrNotFound {
@@ -623,10 +610,10 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
 			if err != nil {
 			if err != nil {
 				panic(err)
 				panic(err)
 			}
 			}
-			newVL.versions = append(newVL.versions, version)
+			newVL.Versions = append(newVL.Versions, version)
 
 
 			if i == 0 {
 			if i == 0 {
-				fi, ok := t.getFile(folder, version.device, name)
+				fi, ok := t.getFile(folder, version.Device, name)
 				if !ok {
 				if !ok {
 					panic("nonexistent global master file")
 					panic("nonexistent global master file")
 				}
 				}
@@ -634,8 +621,8 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
 			}
 			}
 		}
 		}
 
 
-		if len(newVL.versions) != len(vl.versions) {
-			t.Put(dbi.Key(), newVL.MustMarshalXDR())
+		if len(newVL.Versions) != len(vl.Versions) {
+			t.Put(dbi.Key(), mustMarshal(&newVL))
 			t.checkFlush()
 			t.checkFlush()
 		}
 		}
 	}
 	}
@@ -715,12 +702,12 @@ func (db *Instance) globalKeyFolder(key []byte) []byte {
 func unmarshalTrunc(bs []byte, truncate bool) (FileIntf, error) {
 func unmarshalTrunc(bs []byte, truncate bool) (FileIntf, error) {
 	if truncate {
 	if truncate {
 		var tf FileInfoTruncated
 		var tf FileInfoTruncated
-		err := tf.UnmarshalXDR(bs)
+		err := tf.Unmarshal(bs)
 		return tf, err
 		return tf, err
 	}
 	}
 
 
 	var tf protocol.FileInfo
 	var tf protocol.FileInfo
-	err := tf.UnmarshalXDR(bs)
+	err := tf.Unmarshal(bs)
 	return tf, err
 	return tf, err
 }
 }
 
 
@@ -740,50 +727,6 @@ func leveldbIsCorrupted(err error) bool {
 	return false
 	return false
 }
 }
 
 
-// checkConvertDatabase tries to convert an existing old (v0.11) database to
-// new (v0.13) format.
-func checkConvertDatabase(dbFile string) error {
-	oldLoc := filepath.Join(filepath.Dir(dbFile), "index-v0.11.0.db")
-	if _, err := os.Stat(oldLoc); os.IsNotExist(err) {
-		// The old database file does not exist; that's ok, continue as if
-		// everything succeeded.
-		return nil
-	} else if err != nil {
-		// Any other error is weird.
-		return err
-	}
-
-	// There exists a database in the old format. We run a one time
-	// conversion from old to new.
-
-	fromDb, err := leveldb.OpenFile(oldLoc, nil)
-	if err != nil {
-		return err
-	}
-
-	toDb, err := leveldb.OpenFile(dbFile, nil)
-	if err != nil {
-		return err
-	}
-
-	err = convertKeyFormat(fromDb, toDb)
-	if err != nil {
-		return err
-	}
-
-	err = toDb.Close()
-	if err != nil {
-		return err
-	}
-
-	// We've done this one, we don't want to do it again (if the user runs
-	// -reset or so). We don't care too much about errors any more at this stage.
-	fromDb.Close()
-	osutil.Rename(oldLoc, oldLoc+".converted")
-
-	return nil
-}
-
 // A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
 // A smallIndex is an in memory bidirectional []byte to uint32 map. It gives
 // fast lookups in both directions and persists to the database. Don't use for
 // fast lookups in both directions and persists to the database. Don't use for
 // storing more items than fit comfortably in RAM.
 // storing more items than fit comfortably in RAM.

+ 41 - 29
lib/db/leveldb_transactions.go

@@ -83,7 +83,7 @@ func (t readWriteTransaction) insertFile(folder, device []byte, file protocol.Fi
 
 
 	name := []byte(file.Name)
 	name := []byte(file.Name)
 	nk := t.db.deviceKey(folder, device, name)
 	nk := t.db.deviceKey(folder, device, name)
-	t.Put(nk, file.MustMarshalXDR())
+	t.Put(nk, mustMarshal(&file))
 
 
 	return file.LocalVersion
 	return file.LocalVersion
 }
 }
@@ -105,14 +105,14 @@ func (t readWriteTransaction) updateGlobal(folder, device []byte, file protocol.
 	var hasOldFile bool
 	var hasOldFile bool
 	// Remove the device from the current version list
 	// Remove the device from the current version list
 	if len(svl) != 0 {
 	if len(svl) != 0 {
-		err = fl.UnmarshalXDR(svl)
+		err = fl.Unmarshal(svl)
 		if err != nil {
 		if err != nil {
 			panic(err)
 			panic(err)
 		}
 		}
 
 
-		for i := range fl.versions {
-			if bytes.Equal(fl.versions[i].device, device) {
-				if fl.versions[i].version.Equal(file.Version) {
+		for i := range fl.Versions {
+			if bytes.Equal(fl.Versions[i].Device, device) {
+				if fl.Versions[i].Version.Equal(file.Version) {
 					// No need to do anything
 					// No need to do anything
 					return false
 					return false
 				}
 				}
@@ -120,29 +120,29 @@ func (t readWriteTransaction) updateGlobal(folder, device []byte, file protocol.
 				if i == 0 {
 				if i == 0 {
 					// Keep the current newest file around so we can subtract it from
 					// Keep the current newest file around so we can subtract it from
 					// the globalSize if we replace it.
 					// the globalSize if we replace it.
-					oldFile, hasOldFile = t.getFile(folder, fl.versions[0].device, name)
+					oldFile, hasOldFile = t.getFile(folder, fl.Versions[0].Device, name)
 				}
 				}
 
 
-				fl.versions = append(fl.versions[:i], fl.versions[i+1:]...)
+				fl.Versions = append(fl.Versions[:i], fl.Versions[i+1:]...)
 				break
 				break
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	nv := fileVersion{
-		device:  device,
-		version: file.Version,
+	nv := FileVersion{
+		Device:  device,
+		Version: file.Version,
 	}
 	}
 
 
 	insertedAt := -1
 	insertedAt := -1
 	// Find a position in the list to insert this file. The file at the front
 	// Find a position in the list to insert this file. The file at the front
 	// of the list is the newer, the "global".
 	// of the list is the newer, the "global".
-	for i := range fl.versions {
-		switch fl.versions[i].version.Compare(file.Version) {
+	for i := range fl.Versions {
+		switch fl.Versions[i].Version.Compare(file.Version) {
 		case protocol.Equal, protocol.Lesser:
 		case protocol.Equal, protocol.Lesser:
 			// The version at this point in the list is equal to or lesser
 			// The version at this point in the list is equal to or lesser
 			// ("older") than us. We insert ourselves in front of it.
 			// ("older") than us. We insert ourselves in front of it.
-			fl.versions = insertVersion(fl.versions, i, nv)
+			fl.Versions = insertVersion(fl.Versions, i, nv)
 			insertedAt = i
 			insertedAt = i
 			goto done
 			goto done
 
 
@@ -153,12 +153,12 @@ func (t readWriteTransaction) updateGlobal(folder, device []byte, file protocol.
 			// "Greater" in the condition above is just based on the device
 			// "Greater" in the condition above is just based on the device
 			// IDs in the version vector, which is not the only thing we use
 			// IDs in the version vector, which is not the only thing we use
 			// to determine the winner.)
 			// to determine the winner.)
-			of, ok := t.getFile(folder, fl.versions[i].device, name)
+			of, ok := t.getFile(folder, fl.Versions[i].Device, name)
 			if !ok {
 			if !ok {
 				panic("file referenced in version list does not exist")
 				panic("file referenced in version list does not exist")
 			}
 			}
 			if file.WinsConflict(of) {
 			if file.WinsConflict(of) {
-				fl.versions = insertVersion(fl.versions, i, nv)
+				fl.Versions = insertVersion(fl.Versions, i, nv)
 				insertedAt = i
 				insertedAt = i
 				goto done
 				goto done
 			}
 			}
@@ -166,8 +166,8 @@ func (t readWriteTransaction) updateGlobal(folder, device []byte, file protocol.
 	}
 	}
 
 
 	// We didn't find a position for an insert above, so append to the end.
 	// We didn't find a position for an insert above, so append to the end.
-	fl.versions = append(fl.versions, nv)
-	insertedAt = len(fl.versions) - 1
+	fl.Versions = append(fl.Versions, nv)
+	insertedAt = len(fl.Versions) - 1
 
 
 done:
 done:
 	if insertedAt == 0 {
 	if insertedAt == 0 {
@@ -178,9 +178,9 @@ done:
 			if hasOldFile {
 			if hasOldFile {
 				// We have the old file that was removed at the head of the list.
 				// We have the old file that was removed at the head of the list.
 				globalSize.removeFile(oldFile)
 				globalSize.removeFile(oldFile)
-			} else if len(fl.versions) > 1 {
+			} else if len(fl.Versions) > 1 {
 				// The previous newest version is now at index 1, grab it from there.
 				// The previous newest version is now at index 1, grab it from there.
-				oldFile, ok := t.getFile(folder, fl.versions[1].device, name)
+				oldFile, ok := t.getFile(folder, fl.Versions[1].Device, name)
 				if !ok {
 				if !ok {
 					panic("file referenced in version list does not exist")
 					panic("file referenced in version list does not exist")
 				}
 				}
@@ -190,7 +190,7 @@ done:
 	}
 	}
 
 
 	l.Debugf("new global after update: %v", fl)
 	l.Debugf("new global after update: %v", fl)
-	t.Put(gk, fl.MustMarshalXDR())
+	t.Put(gk, mustMarshal(&fl))
 
 
 	return true
 	return true
 }
 }
@@ -210,14 +210,14 @@ func (t readWriteTransaction) removeFromGlobal(folder, device, file []byte, glob
 	}
 	}
 
 
 	var fl VersionList
 	var fl VersionList
-	err = fl.UnmarshalXDR(svl)
+	err = fl.Unmarshal(svl)
 	if err != nil {
 	if err != nil {
 		panic(err)
 		panic(err)
 	}
 	}
 
 
 	removed := false
 	removed := false
-	for i := range fl.versions {
-		if bytes.Equal(fl.versions[i].device, device) {
+	for i := range fl.Versions {
+		if bytes.Equal(fl.Versions[i].Device, device) {
 			if i == 0 && globalSize != nil {
 			if i == 0 && globalSize != nil {
 				f, ok := t.getFile(folder, device, file)
 				f, ok := t.getFile(folder, device, file)
 				if !ok {
 				if !ok {
@@ -226,18 +226,18 @@ func (t readWriteTransaction) removeFromGlobal(folder, device, file []byte, glob
 				globalSize.removeFile(f)
 				globalSize.removeFile(f)
 				removed = true
 				removed = true
 			}
 			}
-			fl.versions = append(fl.versions[:i], fl.versions[i+1:]...)
+			fl.Versions = append(fl.Versions[:i], fl.Versions[i+1:]...)
 			break
 			break
 		}
 		}
 	}
 	}
 
 
-	if len(fl.versions) == 0 {
+	if len(fl.Versions) == 0 {
 		t.Delete(gk)
 		t.Delete(gk)
 	} else {
 	} else {
 		l.Debugf("new global after remove: %v", fl)
 		l.Debugf("new global after remove: %v", fl)
-		t.Put(gk, fl.MustMarshalXDR())
+		t.Put(gk, mustMarshal(&fl))
 		if removed {
 		if removed {
-			f, ok := t.getFile(folder, fl.versions[0].device, file)
+			f, ok := t.getFile(folder, fl.Versions[0].Device, file)
 			if !ok {
 			if !ok {
 				panic("new global is nonexistent file")
 				panic("new global is nonexistent file")
 			}
 			}
@@ -246,9 +246,21 @@ func (t readWriteTransaction) removeFromGlobal(folder, device, file []byte, glob
 	}
 	}
 }
 }
 
 
-func insertVersion(vl []fileVersion, i int, v fileVersion) []fileVersion {
-	t := append(vl, fileVersion{})
+func insertVersion(vl []FileVersion, i int, v FileVersion) []FileVersion {
+	t := append(vl, FileVersion{})
 	copy(t[i+1:], t[i:])
 	copy(t[i+1:], t[i:])
 	t[i] = v
 	t[i] = v
 	return t
 	return t
 }
 }
+
+type marshaller interface {
+	Marshal() ([]byte, error)
+}
+
+func mustMarshal(f marshaller) []byte {
+	bs, err := f.Marshal()
+	if err != nil {
+		panic(err)
+	}
+	return bs
+}

+ 0 - 142
lib/db/leveldb_xdr.go

@@ -1,142 +0,0 @@
-// ************************************************************
-// This file is automatically generated by genxdr. Do not edit.
-// ************************************************************
-
-package db
-
-import (
-	"github.com/calmh/xdr"
-)
-
-/*
-
-fileVersion Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                       Vector Structure                        \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 device (length + padded data)                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct fileVersion {
-	Vector version;
-	opaque device<>;
-}
-
-*/
-
-func (o fileVersion) XDRSize() int {
-	return o.version.XDRSize() +
-		4 + len(o.device) + xdr.Padding(len(o.device))
-}
-
-func (o fileVersion) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o fileVersion) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o fileVersion) MarshalXDRInto(m *xdr.Marshaller) error {
-	if err := o.version.MarshalXDRInto(m); err != nil {
-		return err
-	}
-	m.MarshalBytes(o.device)
-	return m.Error
-}
-
-func (o *fileVersion) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *fileVersion) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	(&o.version).UnmarshalXDRFrom(u)
-	o.device = u.UnmarshalBytes()
-	return u.Error
-}
-
-/*
-
-VersionList Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                      Number of versions                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\              Zero or more fileVersion Structures              \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct VersionList {
-	fileVersion versions<>;
-}
-
-*/
-
-func (o VersionList) XDRSize() int {
-	return 4 + xdr.SizeOfSlice(o.versions)
-}
-
-func (o VersionList) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o VersionList) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o VersionList) MarshalXDRInto(m *xdr.Marshaller) error {
-	m.MarshalUint32(uint32(len(o.versions)))
-	for i := range o.versions {
-		if err := o.versions[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *VersionList) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *VersionList) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	_versionsSize := int(u.UnmarshalUint32())
-	if _versionsSize < 0 {
-		return xdr.ElementSizeExceeded("versions", _versionsSize, 0)
-	} else if _versionsSize == 0 {
-		o.versions = nil
-	} else {
-		if _versionsSize <= len(o.versions) {
-			o.versions = o.versions[:_versionsSize]
-		} else {
-			o.versions = make([]fileVersion, _versionsSize)
-		}
-		for i := range o.versions {
-			(&o.versions[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}

+ 6 - 5
lib/db/set.go

@@ -31,9 +31,10 @@ type FileSet struct {
 }
 }
 
 
 // FileIntf is the set of methods implemented by both protocol.FileInfo and
 // FileIntf is the set of methods implemented by both protocol.FileInfo and
-// protocol.FileInfoTruncated.
+// FileInfoTruncated.
 type FileIntf interface {
 type FileIntf interface {
-	Size() int64
+	FileSize() int64
+	FileName() string
 	IsDeleted() bool
 	IsDeleted() bool
 	IsInvalid() bool
 	IsInvalid() bool
 	IsDirectory() bool
 	IsDirectory() bool
@@ -42,7 +43,7 @@ type FileIntf interface {
 }
 }
 
 
 // The Iterator is called with either a protocol.FileInfo or a
 // The Iterator is called with either a protocol.FileInfo or a
-// protocol.FileInfoTruncated (depending on the method) and returns true to
+// FileInfoTruncated (depending on the method) and returns true to
 // continue iteration, false to stop.
 // continue iteration, false to stop.
 type Iterator func(f FileIntf) bool
 type Iterator func(f FileIntf) bool
 
 
@@ -64,7 +65,7 @@ func (s *sizeTracker) addFile(f FileIntf) {
 	} else {
 	} else {
 		s.files++
 		s.files++
 	}
 	}
-	s.bytes += f.Size()
+	s.bytes += f.FileSize()
 	s.mut.Unlock()
 	s.mut.Unlock()
 }
 }
 
 
@@ -79,7 +80,7 @@ func (s *sizeTracker) removeFile(f FileIntf) {
 	} else {
 	} else {
 		s.files--
 		s.files--
 	}
 	}
-	s.bytes -= f.Size()
+	s.bytes -= f.FileSize()
 	if s.deleted < 0 || s.files < 0 {
 	if s.deleted < 0 || s.files < 0 {
 		panic("bug: removed more than added")
 		panic("bug: removed more than added")
 	}
 	}

+ 89 - 89
lib/db/set_test.go

@@ -88,7 +88,7 @@ func (l fileList) String() string {
 	var b bytes.Buffer
 	var b bytes.Buffer
 	b.WriteString("[]protocol.FileList{\n")
 	b.WriteString("[]protocol.FileList{\n")
 	for _, f := range l {
 	for _, f := range l {
-		fmt.Fprintf(&b, "  %q: #%d, %d bytes, %d blocks, flags=%o\n", f.Name, f.Version, f.Size(), len(f.Blocks), f.Flags)
+		fmt.Fprintf(&b, "  %q: #%d, %d bytes, %d blocks, perms=%o\n", f.Name, f.Version, f.Size, len(f.Blocks), f.Permissions)
 	}
 	}
 	b.WriteString("}")
 	b.WriteString("}")
 	return b.String()
 	return b.String()
@@ -100,35 +100,35 @@ func TestGlobalSet(t *testing.T) {
 	m := db.NewFileSet("test", ldb)
 	m := db.NewFileSet("test", ldb)
 
 
 	local0 := fileList{
 	local0 := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(3)},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "z", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(8)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)},
 	}
 	}
 	local1 := fileList{
 	local1 := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(3)},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "z", Version: protocol.Vector{{ID: myID, Value: 1001}}, Flags: protocol.FlagDeleted},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Deleted: true},
 	}
 	}
 	localTot := fileList{
 	localTot := fileList{
 		local0[0],
 		local0[0],
 		local0[1],
 		local0[1],
 		local0[2],
 		local0[2],
 		local0[3],
 		local0[3],
-		protocol.FileInfo{Name: "z", Version: protocol.Vector{{ID: myID, Value: 1001}}, Flags: protocol.FlagDeleted},
+		protocol.FileInfo{Name: "z", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Deleted: true},
 	}
 	}
 
 
 	remote0 := fileList{
 	remote0 := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(5)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(5)},
 	}
 	}
 	remote1 := fileList{
 	remote1 := fileList{
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(6)},
-		protocol.FileInfo{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(6)},
+		protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(7)},
 	}
 	}
 	remoteTot := fileList{
 	remoteTot := fileList{
 		remote0[0],
 		remote0[0],
@@ -178,7 +178,7 @@ func TestGlobalSet(t *testing.T) {
 		} else {
 		} else {
 			globalFiles++
 			globalFiles++
 		}
 		}
-		globalBytes += f.Size()
+		globalBytes += f.FileSize()
 	}
 	}
 	gsFiles, gsDeleted, gsBytes := m.GlobalSize()
 	gsFiles, gsDeleted, gsBytes := m.GlobalSize()
 	if gsFiles != globalFiles {
 	if gsFiles != globalFiles {
@@ -208,7 +208,7 @@ func TestGlobalSet(t *testing.T) {
 		} else {
 		} else {
 			haveFiles++
 			haveFiles++
 		}
 		}
-		haveBytes += f.Size()
+		haveBytes += f.FileSize()
 	}
 	}
 	lsFiles, lsDeleted, lsBytes := m.LocalSize()
 	lsFiles, lsDeleted, lsBytes := m.LocalSize()
 	if lsFiles != haveFiles {
 	if lsFiles != haveFiles {
@@ -303,23 +303,23 @@ func TestNeedWithInvalid(t *testing.T) {
 	s := db.NewFileSet("test", ldb)
 	s := db.NewFileSet("test", ldb)
 
 
 	localHave := fileList{
 	localHave := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
 	}
 	}
 	remote0Have := fileList{
 	remote0Have := fileList{
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
 	}
 	}
 	remote1Have := fileList{
 	remote1Have := fileList{
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(7)},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
-		protocol.FileInfo{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1004}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true},
+		protocol.FileInfo{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
 	}
 	}
 
 
 	expectedNeed := fileList{
 	expectedNeed := fileList{
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(7)},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
 	}
 	}
 
 
 	s.Replace(protocol.LocalDeviceID, localHave)
 	s.Replace(protocol.LocalDeviceID, localHave)
@@ -340,10 +340,10 @@ func TestUpdateToInvalid(t *testing.T) {
 	s := db.NewFileSet("test", ldb)
 	s := db.NewFileSet("test", ldb)
 
 
 	localHave := fileList{
 	localHave := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
 	}
 	}
 
 
 	s.Replace(protocol.LocalDeviceID, localHave)
 	s.Replace(protocol.LocalDeviceID, localHave)
@@ -355,7 +355,7 @@ func TestUpdateToInvalid(t *testing.T) {
 		t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, localHave)
 		t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, localHave)
 	}
 	}
 
 
-	localHave[1] = protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}, Flags: protocol.FlagInvalid}
+	localHave[1] = protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Invalid: true}
 	s.Update(protocol.LocalDeviceID, localHave[1:2])
 	s.Update(protocol.LocalDeviceID, localHave[1:2])
 
 
 	have = fileList(haveList(s, protocol.LocalDeviceID))
 	have = fileList(haveList(s, protocol.LocalDeviceID))
@@ -372,16 +372,16 @@ func TestInvalidAvailability(t *testing.T) {
 	s := db.NewFileSet("test", ldb)
 	s := db.NewFileSet("test", ldb)
 
 
 	remote0Have := fileList{
 	remote0Have := fileList{
-		protocol.FileInfo{Name: "both", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "r1only", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
-		protocol.FileInfo{Name: "r0only", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(7)},
-		protocol.FileInfo{Name: "none", Version: protocol.Vector{{ID: myID, Value: 1004}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
+		protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(5), Invalid: true},
+		protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
 	}
 	}
 	remote1Have := fileList{
 	remote1Have := fileList{
-		protocol.FileInfo{Name: "both", Version: protocol.Vector{{ID: myID, Value: 1001}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "r1only", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(7)},
-		protocol.FileInfo{Name: "r0only", Version: protocol.Vector{{ID: myID, Value: 1003}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
-		protocol.FileInfo{Name: "none", Version: protocol.Vector{{ID: myID, Value: 1004}}, Blocks: genBlocks(5), Flags: protocol.FlagInvalid},
+		protocol.FileInfo{Name: "both", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "r1only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(7)},
+		protocol.FileInfo{Name: "r0only", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1003}}}, Blocks: genBlocks(5), Invalid: true},
+		protocol.FileInfo{Name: "none", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1004}}}, Blocks: genBlocks(5), Invalid: true},
 	}
 	}
 
 
 	s.Replace(remoteDevice0, remote0Have)
 	s.Replace(remoteDevice0, remote0Have)
@@ -410,17 +410,17 @@ func TestGlobalReset(t *testing.T) {
 	m := db.NewFileSet("test", ldb)
 	m := db.NewFileSet("test", ldb)
 
 
 	local := []protocol.FileInfo{
 	local := []protocol.FileInfo{
-		{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	remote := []protocol.FileInfo{
 	remote := []protocol.FileInfo{
-		{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}},
-		{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
+		{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	m.Replace(protocol.LocalDeviceID, local)
 	m.Replace(protocol.LocalDeviceID, local)
@@ -448,23 +448,23 @@ func TestNeed(t *testing.T) {
 	m := db.NewFileSet("test", ldb)
 	m := db.NewFileSet("test", ldb)
 
 
 	local := []protocol.FileInfo{
 	local := []protocol.FileInfo{
-		{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	remote := []protocol.FileInfo{
 	remote := []protocol.FileInfo{
-		{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}},
-		{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
+		{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	shouldNeed := []protocol.FileInfo{
 	shouldNeed := []protocol.FileInfo{
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1001}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}},
-		{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1001}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
+		{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	m.Replace(protocol.LocalDeviceID, local)
 	m.Replace(protocol.LocalDeviceID, local)
@@ -486,18 +486,18 @@ func TestLocalVersion(t *testing.T) {
 	m := db.NewFileSet("test", ldb)
 	m := db.NewFileSet("test", ldb)
 
 
 	local1 := []protocol.FileInfo{
 	local1 := []protocol.FileInfo{
-		{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	local2 := []protocol.FileInfo{
 	local2 := []protocol.FileInfo{
 		local1[0],
 		local1[0],
 		// [1] deleted
 		// [1] deleted
 		local1[2],
 		local1[2],
-		{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1002}}},
-		{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
+		{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	m.Replace(protocol.LocalDeviceID, local1)
 	m.Replace(protocol.LocalDeviceID, local1)
@@ -515,17 +515,17 @@ func TestListDropFolder(t *testing.T) {
 
 
 	s0 := db.NewFileSet("test0", ldb)
 	s0 := db.NewFileSet("test0", ldb)
 	local1 := []protocol.FileInfo{
 	local1 := []protocol.FileInfo{
-		{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}},
-		{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
+		{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 	s0.Replace(protocol.LocalDeviceID, local1)
 	s0.Replace(protocol.LocalDeviceID, local1)
 
 
 	s1 := db.NewFileSet("test1", ldb)
 	s1 := db.NewFileSet("test1", ldb)
 	local2 := []protocol.FileInfo{
 	local2 := []protocol.FileInfo{
-		{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1002}}},
-		{Name: "e", Version: protocol.Vector{{ID: myID, Value: 1002}}},
-		{Name: "f", Version: protocol.Vector{{ID: myID, Value: 1002}}},
+		{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
+		{Name: "e", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
+		{Name: "f", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}},
 	}
 	}
 	s1.Replace(remoteDevice0, local2)
 	s1.Replace(remoteDevice0, local2)
 
 
@@ -566,24 +566,24 @@ func TestGlobalNeedWithInvalid(t *testing.T) {
 	s := db.NewFileSet("test1", ldb)
 	s := db.NewFileSet("test1", ldb)
 
 
 	rem0 := fileList{
 	rem0 := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1002}}, Flags: protocol.FlagInvalid},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
 	}
 	}
 	s.Replace(remoteDevice0, rem0)
 	s.Replace(remoteDevice0, rem0)
 
 
 	rem1 := fileList{
 	rem1 := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Flags: protocol.FlagInvalid},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Invalid: true},
 	}
 	}
 	s.Replace(remoteDevice1, rem1)
 	s.Replace(remoteDevice1, rem1)
 
 
 	total := fileList{
 	total := fileList{
 		// There's a valid copy of each file, so it should be merged
 		// There's a valid copy of each file, so it should be merged
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1002}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1002}}}, Blocks: genBlocks(4)},
 	}
 	}
 
 
 	need := fileList(needList(s, protocol.LocalDeviceID))
 	need := fileList(needList(s, protocol.LocalDeviceID))
@@ -609,7 +609,7 @@ func TestLongPath(t *testing.T) {
 	name := b.String() // 5000 characters
 	name := b.String() // 5000 characters
 
 
 	local := []protocol.FileInfo{
 	local := []protocol.FileInfo{
-		{Name: string(name), Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: string(name), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	s.Replace(protocol.LocalDeviceID, local)
 	s.Replace(protocol.LocalDeviceID, local)
@@ -633,7 +633,7 @@ func TestCommitted(t *testing.T) {
 	s := db.NewFileSet("test", ldb)
 	s := db.NewFileSet("test", ldb)
 
 
 	local := []protocol.FileInfo{
 	local := []protocol.FileInfo{
-		{Name: string("file"), Version: protocol.Vector{{ID: myID, Value: 1000}}},
+		{Name: string("file"), Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}},
 	}
 	}
 
 
 	// Adding a file should increase the counter
 	// Adding a file should increase the counter
@@ -659,12 +659,12 @@ func TestCommitted(t *testing.T) {
 
 
 func BenchmarkUpdateOneFile(b *testing.B) {
 func BenchmarkUpdateOneFile(b *testing.B) {
 	local0 := fileList{
 	local0 := fileList{
-		protocol.FileInfo{Name: "a", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(1)},
-		protocol.FileInfo{Name: "b", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(2)},
-		protocol.FileInfo{Name: "c", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(3)},
-		protocol.FileInfo{Name: "d", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(4)},
+		protocol.FileInfo{Name: "a", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(1)},
+		protocol.FileInfo{Name: "b", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(2)},
+		protocol.FileInfo{Name: "c", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(3)},
+		protocol.FileInfo{Name: "d", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(4)},
 		// A longer name is more realistic and causes more allocations
 		// A longer name is more realistic and causes more allocations
-		protocol.FileInfo{Name: "zajksdhaskjdh/askjdhaskjdashkajshd/kasjdhaskjdhaskdjhaskdjash/dkjashdaksjdhaskdjahskdjh", Version: protocol.Vector{{ID: myID, Value: 1000}}, Blocks: genBlocks(8)},
+		protocol.FileInfo{Name: "zajksdhaskjdh/askjdhaskjdashkajshd/kasjdhaskjdhaskdjhaskdjash/dkjashdaksjdhaskdjahskdjh", Version: protocol.Vector{Counters: []protocol.Counter{{ID: myID, Value: 1000}}}, Blocks: genBlocks(8)},
 	}
 	}
 
 
 	ldb, err := db.Open("testdata/benchmarkupdate.db")
 	ldb, err := db.Open("testdata/benchmarkupdate.db")

+ 57 - 0
lib/db/structs.go

@@ -0,0 +1,57 @@
+// Copyright (C) 2014 The Syncthing Authors.
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this file,
+// You can obtain one at http://mozilla.org/MPL/2.0/.
+
+//go:generate go run ../../script/protofmt.go structs.proto
+//go:generate protoc --proto_path=../../../../../:../../../../gogo/protobuf/protobuf:. --gogofast_out=. structs.proto
+
+package db
+
+import (
+	"fmt"
+
+	"github.com/syncthing/syncthing/lib/protocol"
+)
+
+func (f FileInfoTruncated) String() string {
+	return fmt.Sprintf("File{Name:%q, Permissions:0%o, Modified:%d, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v}",
+		f.Name, f.Permissions, f.Modified, f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions)
+}
+
+func (f FileInfoTruncated) IsDeleted() bool {
+	return f.Deleted
+}
+
+func (f FileInfoTruncated) IsInvalid() bool {
+	return f.Invalid
+}
+
+func (f FileInfoTruncated) IsDirectory() bool {
+	return f.Type == protocol.FileInfoTypeDirectory
+}
+
+func (f FileInfoTruncated) IsSymlink() bool {
+	switch f.Type {
+	case protocol.FileInfoTypeSymlinkDirectory, protocol.FileInfoTypeSymlinkFile, protocol.FileInfoTypeSymlinkUnknown:
+		return true
+	default:
+		return false
+	}
+}
+
+func (f FileInfoTruncated) HasPermissionBits() bool {
+	return !f.NoPermissions
+}
+
+func (f FileInfoTruncated) FileSize() int64 {
+	if f.IsDirectory() || f.IsDeleted() {
+		return 128
+	}
+	return f.Size
+}
+
+func (f FileInfoTruncated) FileName() string {
+	return f.Name
+}

+ 914 - 0
lib/db/structs.pb.go

@@ -0,0 +1,914 @@
+// Code generated by protoc-gen-gogo.
+// source: structs.proto
+// DO NOT EDIT!
+
+/*
+	Package db is a generated protocol buffer package.
+
+	It is generated from these files:
+		structs.proto
+
+	It has these top-level messages:
+		FileVersion
+		VersionList
+		FileInfoTruncated
+*/
+package db
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import _ "github.com/gogo/protobuf/gogoproto"
+import protocol "github.com/syncthing/syncthing/lib/protocol"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+type FileVersion struct {
+	Version protocol.Vector `protobuf:"bytes,1,opt,name=version" json:"version"`
+	Device  []byte          `protobuf:"bytes,2,opt,name=device,proto3" json:"device,omitempty"`
+}
+
+func (m *FileVersion) Reset()                    { *m = FileVersion{} }
+func (m *FileVersion) String() string            { return proto.CompactTextString(m) }
+func (*FileVersion) ProtoMessage()               {}
+func (*FileVersion) Descriptor() ([]byte, []int) { return fileDescriptorStructs, []int{0} }
+
+type VersionList struct {
+	Versions []FileVersion `protobuf:"bytes,1,rep,name=versions" json:"versions"`
+}
+
+func (m *VersionList) Reset()                    { *m = VersionList{} }
+func (*VersionList) ProtoMessage()               {}
+func (*VersionList) Descriptor() ([]byte, []int) { return fileDescriptorStructs, []int{1} }
+
+// Must be the same as FileInfo but without the blocks field
+type FileInfoTruncated struct {
+	Name          string                `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Type          protocol.FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=protocol.FileInfoType" json:"type,omitempty"`
+	Size          int64                 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
+	Permissions   uint32                `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions,omitempty"`
+	Modified      int64                 `protobuf:"varint,5,opt,name=modified,proto3" json:"modified,omitempty"`
+	Deleted       bool                  `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"`
+	Invalid       bool                  `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"`
+	NoPermissions bool                  `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"no_permissions,omitempty"`
+	Version       protocol.Vector       `protobuf:"bytes,9,opt,name=version" json:"version"`
+	LocalVersion  int64                 `protobuf:"varint,10,opt,name=local_version,json=localVersion,proto3" json:"local_version,omitempty"`
+}
+
+func (m *FileInfoTruncated) Reset()                    { *m = FileInfoTruncated{} }
+func (*FileInfoTruncated) ProtoMessage()               {}
+func (*FileInfoTruncated) Descriptor() ([]byte, []int) { return fileDescriptorStructs, []int{2} }
+
+func init() {
+	proto.RegisterType((*FileVersion)(nil), "db.FileVersion")
+	proto.RegisterType((*VersionList)(nil), "db.VersionList")
+	proto.RegisterType((*FileInfoTruncated)(nil), "db.FileInfoTruncated")
+}
+func (m *FileVersion) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *FileVersion) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	data[i] = 0xa
+	i++
+	i = encodeVarintStructs(data, i, uint64(m.Version.ProtoSize()))
+	n1, err := m.Version.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
+	}
+	i += n1
+	if len(m.Device) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintStructs(data, i, uint64(len(m.Device)))
+		i += copy(data[i:], m.Device)
+	}
+	return i, nil
+}
+
+func (m *VersionList) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *VersionList) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Versions) > 0 {
+		for _, msg := range m.Versions {
+			data[i] = 0xa
+			i++
+			i = encodeVarintStructs(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *FileInfoTruncated) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *FileInfoTruncated) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Name) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintStructs(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
+	if m.Type != 0 {
+		data[i] = 0x10
+		i++
+		i = encodeVarintStructs(data, i, uint64(m.Type))
+	}
+	if m.Size != 0 {
+		data[i] = 0x18
+		i++
+		i = encodeVarintStructs(data, i, uint64(m.Size))
+	}
+	if m.Permissions != 0 {
+		data[i] = 0x20
+		i++
+		i = encodeVarintStructs(data, i, uint64(m.Permissions))
+	}
+	if m.Modified != 0 {
+		data[i] = 0x28
+		i++
+		i = encodeVarintStructs(data, i, uint64(m.Modified))
+	}
+	if m.Deleted {
+		data[i] = 0x30
+		i++
+		if m.Deleted {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.Invalid {
+		data[i] = 0x38
+		i++
+		if m.Invalid {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.NoPermissions {
+		data[i] = 0x40
+		i++
+		if m.NoPermissions {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	data[i] = 0x4a
+	i++
+	i = encodeVarintStructs(data, i, uint64(m.Version.ProtoSize()))
+	n2, err := m.Version.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
+	}
+	i += n2
+	if m.LocalVersion != 0 {
+		data[i] = 0x50
+		i++
+		i = encodeVarintStructs(data, i, uint64(m.LocalVersion))
+	}
+	return i, nil
+}
+
+func encodeFixed64Structs(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 encodeFixed32Structs(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 encodeVarintStructs(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 *FileVersion) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = m.Version.ProtoSize()
+	n += 1 + l + sovStructs(uint64(l))
+	l = len(m.Device)
+	if l > 0 {
+		n += 1 + l + sovStructs(uint64(l))
+	}
+	return n
+}
+
+func (m *VersionList) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if len(m.Versions) > 0 {
+		for _, e := range m.Versions {
+			l = e.ProtoSize()
+			n += 1 + l + sovStructs(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *FileInfoTruncated) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovStructs(uint64(l))
+	}
+	if m.Type != 0 {
+		n += 1 + sovStructs(uint64(m.Type))
+	}
+	if m.Size != 0 {
+		n += 1 + sovStructs(uint64(m.Size))
+	}
+	if m.Permissions != 0 {
+		n += 1 + sovStructs(uint64(m.Permissions))
+	}
+	if m.Modified != 0 {
+		n += 1 + sovStructs(uint64(m.Modified))
+	}
+	if m.Deleted {
+		n += 2
+	}
+	if m.Invalid {
+		n += 2
+	}
+	if m.NoPermissions {
+		n += 2
+	}
+	l = m.Version.ProtoSize()
+	n += 1 + l + sovStructs(uint64(l))
+	if m.LocalVersion != 0 {
+		n += 1 + sovStructs(uint64(m.LocalVersion))
+	}
+	return n
+}
+
+func sovStructs(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozStructs(x uint64) (n int) {
+	return sovStructs(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *FileVersion) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowStructs
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: FileVersion: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: FileVersion: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthStructs
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Version.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthStructs
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Device = append(m.Device[:0], data[iNdEx:postIndex]...)
+			if m.Device == nil {
+				m.Device = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipStructs(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthStructs
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *VersionList) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowStructs
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: VersionList: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: VersionList: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Versions", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthStructs
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Versions = append(m.Versions, FileVersion{})
+			if err := m.Versions[len(m.Versions)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipStructs(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthStructs
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *FileInfoTruncated) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowStructs
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: FileInfoTruncated: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: FileInfoTruncated: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				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 ErrInvalidLengthStructs
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		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 shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Type |= (protocol.FileInfoType(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType)
+			}
+			m.Size = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Size |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType)
+			}
+			m.Permissions = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Permissions |= (uint32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Modified", wireType)
+			}
+			m.Modified = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Modified |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Deleted = bool(v != 0)
+		case 7:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Invalid", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Invalid = bool(v != 0)
+		case 8:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field NoPermissions", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.NoPermissions = bool(v != 0)
+		case 9:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthStructs
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Version.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 10:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVersion", wireType)
+			}
+			m.LocalVersion = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowStructs
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.LocalVersion |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipStructs(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthStructs
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipStructs(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowStructs
+			}
+			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 shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowStructs
+				}
+				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 shift >= 64 {
+					return 0, ErrIntOverflowStructs
+				}
+				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, ErrInvalidLengthStructs
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start int = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowStructs
+					}
+					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 := skipStructs(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 (
+	ErrInvalidLengthStructs = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowStructs   = fmt.Errorf("proto: integer overflow")
+)
+
+var fileDescriptorStructs = []byte{
+	// 401 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x51, 0x4f, 0xcb, 0xd3, 0x30,
+	0x1c, 0x6e, 0xb7, 0xba, 0xf5, 0x4d, 0xdf, 0x4e, 0x0d, 0x32, 0xca, 0x0e, 0xdd, 0x98, 0x08, 0x22,
+	0xd8, 0xe9, 0xc4, 0x8b, 0xc7, 0x1d, 0x06, 0x82, 0x07, 0x29, 0x32, 0x8f, 0xa3, 0x4d, 0xb2, 0x2e,
+	0xd0, 0x26, 0xa5, 0x49, 0x07, 0xf3, 0x93, 0x78, 0xdc, 0xc7, 0xd9, 0xd1, 0x2f, 0xa0, 0xe8, 0xfc,
+	0x22, 0x66, 0x49, 0x3b, 0x7b, 0x7c, 0x0f, 0x81, 0xdf, 0x93, 0xe7, 0xcf, 0xef, 0x21, 0x01, 0xbe,
+	0x90, 0x55, 0x8d, 0xa4, 0x88, 0xca, 0x8a, 0x4b, 0x0e, 0x7b, 0x38, 0x9d, 0xbc, 0xce, 0xa8, 0xdc,
+	0xd7, 0x69, 0x84, 0x78, 0xb1, 0xc8, 0x78, 0xc6, 0x17, 0x9a, 0x4a, 0xeb, 0x9d, 0x46, 0x1a, 0xe8,
+	0xc9, 0x58, 0x26, 0xef, 0x3b, 0x72, 0x71, 0x64, 0x48, 0xee, 0x29, 0xcb, 0x3a, 0x53, 0x4e, 0x53,
+	0x93, 0x80, 0x78, 0xbe, 0x48, 0x49, 0x69, 0x6c, 0xf3, 0xaf, 0xc0, 0x5b, 0xd3, 0x9c, 0x6c, 0x48,
+	0x25, 0x28, 0x67, 0xf0, 0x0d, 0x18, 0x1e, 0xcc, 0x18, 0xd8, 0x33, 0xfb, 0xa5, 0xb7, 0x7c, 0x12,
+	0xb5, 0xa6, 0x68, 0x43, 0x90, 0xe4, 0xd5, 0xca, 0x39, 0xff, 0x9a, 0x5a, 0x71, 0x2b, 0x83, 0x63,
+	0x30, 0xc0, 0xe4, 0x40, 0x11, 0x09, 0x7a, 0xca, 0x70, 0x1f, 0x37, 0x68, 0xbe, 0x06, 0x5e, 0x13,
+	0xfa, 0x89, 0x0a, 0x09, 0xdf, 0x02, 0xb7, 0x71, 0x08, 0x95, 0xdc, 0x57, 0xc9, 0x8f, 0x23, 0x9c,
+	0x46, 0x9d, 0xdd, 0x4d, 0xf0, 0x4d, 0xf6, 0xc1, 0xf9, 0x7e, 0x9a, 0x5a, 0xf3, 0x9f, 0x3d, 0xf0,
+	0xf4, 0xaa, 0xfa, 0xc8, 0x76, 0xfc, 0x4b, 0x55, 0x33, 0x94, 0x48, 0x82, 0x21, 0x04, 0x0e, 0x4b,
+	0x0a, 0xa2, 0x4b, 0xde, 0xc5, 0x7a, 0x86, 0xaf, 0x80, 0x23, 0x8f, 0xa5, 0xe9, 0x31, 0x5a, 0x8e,
+	0xff, 0x17, 0xbf, 0xd9, 0x15, 0x1b, 0x6b, 0xcd, 0xd5, 0x2f, 0xe8, 0x37, 0x12, 0xf4, 0x95, 0xb6,
+	0x1f, 0xeb, 0x19, 0xce, 0x80, 0x57, 0x92, 0xaa, 0xa0, 0xc2, 0xb4, 0x74, 0x14, 0xe5, 0xc7, 0xdd,
+	0x2b, 0x38, 0x01, 0x6e, 0xc1, 0x31, 0xdd, 0x51, 0x82, 0x83, 0x47, 0xda, 0x79, 0xc3, 0x30, 0x00,
+	0x43, 0x4c, 0x72, 0xa2, 0xca, 0x05, 0x03, 0x45, 0xb9, 0x71, 0x0b, 0xaf, 0x0c, 0x65, 0x87, 0x24,
+	0xa7, 0x38, 0x18, 0x1a, 0xa6, 0x81, 0xf0, 0x05, 0x18, 0x31, 0xbe, 0xed, 0x2e, 0x75, 0xb5, 0xc0,
+	0x67, 0xfc, 0x73, 0x67, 0x6d, 0xe7, 0x53, 0xee, 0x1e, 0xf6, 0x29, 0xcf, 0x81, 0x9f, 0x73, 0x94,
+	0xe4, 0xdb, 0xd6, 0x07, 0x74, 0xdb, 0x7b, 0x7d, 0xd9, 0xbc, 0xb7, 0x79, 0xdf, 0xd5, 0xb3, 0xf3,
+	0x9f, 0xd0, 0x3a, 0x5f, 0x42, 0xfb, 0x87, 0x3a, 0xbf, 0x2f, 0xa1, 0x75, 0xfa, 0x1b, 0xda, 0xe9,
+	0x40, 0x2f, 0x78, 0xf7, 0x2f, 0x00, 0x00, 0xff, 0xff, 0xe4, 0xb1, 0x7f, 0x07, 0x98, 0x02, 0x00,
+	0x00,
+}

+ 35 - 0
lib/db/structs.proto

@@ -0,0 +1,35 @@
+syntax = "proto3";
+
+package db;
+
+import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+import "github.com/syncthing/syncthing/lib/protocol/bep.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.sizer_all) = false;
+option (gogoproto.protosizer_all) = true;
+
+message FileVersion {
+    protocol.Vector version = 1 [(gogoproto.nullable) = false];
+    bytes           device  = 2;
+}
+
+message VersionList {
+    option (gogoproto.goproto_stringer) = false;
+    repeated FileVersion versions = 1 [(gogoproto.nullable) = false];
+}
+
+// Must be the same as FileInfo but without the blocks field
+message FileInfoTruncated {
+    option (gogoproto.goproto_stringer) = false;
+    string                name           = 1;
+    protocol.FileInfoType type           = 2;
+    int64                 size           = 3;
+    uint32                permissions    = 4;
+    int64                 modified       = 5;
+    bool                  deleted        = 6;
+    bool                  invalid        = 7;
+    bool                  no_permissions = 8;
+    protocol.Vector       version        = 9 [(gogoproto.nullable) = false];
+    int64                 local_version  = 10;
+}

BIN
lib/db/testdata/oldformat.db.zip


+ 0 - 52
lib/db/truncated.go

@@ -1,52 +0,0 @@
-// Copyright (C) 2014 The Syncthing Authors.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-package db
-
-import (
-	"github.com/calmh/xdr"
-	"github.com/syncthing/syncthing/lib/protocol"
-)
-
-type FileInfoTruncated struct {
-	protocol.FileInfo
-}
-
-func (o *FileInfoTruncated) UnmarshalXDR(bs []byte) error {
-	return o.UnmarshalXDRFrom(&xdr.Unmarshaller{Data: bs})
-}
-
-func (o *FileInfoTruncated) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Name = u.UnmarshalStringMax(8192)
-	o.Flags = u.UnmarshalUint32()
-	o.Modified = int64(u.UnmarshalUint64())
-	(&o.Version).UnmarshalXDRFrom(u)
-	o.LocalVersion = int64(u.UnmarshalUint64())
-	_BlocksSize := int(u.UnmarshalUint32())
-	if _BlocksSize < 0 {
-		return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 10000000)
-	} else if _BlocksSize == 0 {
-		o.Blocks = nil
-	} else {
-		if _BlocksSize > 10000000 {
-			return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 10000000)
-		}
-		for i := 0; i < _BlocksSize; i++ {
-			size := int64(u.UnmarshalUint32())
-			o.CachedSize += size
-			u.UnmarshalBytes()
-		}
-	}
-
-	return u.Error
-}
-
-func BlocksToSize(num int) int64 {
-	if num < 2 {
-		return protocol.BlockSize / 2
-	}
-	return int64(num-1)*protocol.BlockSize + protocol.BlockSize/2
-}

+ 45 - 27
lib/discover/local.go

@@ -4,10 +4,14 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
+//go:generate go run ../../script/protofmt.go local.proto
+//go:generate protoc --proto_path=../../../../../:../../../../gogo/protobuf/protobuf:. --gogofast_out=. local.proto
+
 package discover
 package discover
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"encoding/binary"
 	"encoding/hex"
 	"encoding/hex"
 	"io"
 	"io"
 	"net"
 	"net"
@@ -38,6 +42,8 @@ type localClient struct {
 const (
 const (
 	BroadcastInterval = 30 * time.Second
 	BroadcastInterval = 30 * time.Second
 	CacheLifeTime     = 3 * BroadcastInterval
 	CacheLifeTime     = 3 * BroadcastInterval
+	Magic             = uint32(0x2EA7D90B) // same as in BEP
+	v13Magic          = uint32(0x7D79BC40) // previous version
 )
 )
 
 
 func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
 func NewLocal(id protocol.DeviceID, addr string, addrList AddressLister) (FinderService, error) {
@@ -107,25 +113,19 @@ func (c *localClient) Error() error {
 }
 }
 
 
 func (c *localClient) announcementPkt() Announce {
 func (c *localClient) announcementPkt() Announce {
-	var addrs []Address
-	for _, addr := range c.addrList.AllAddresses() {
-		addrs = append(addrs, Address{
-			URL: addr,
-		})
-	}
-
 	return Announce{
 	return Announce{
-		Magic: AnnouncementMagic,
-		This: Device{
-			ID:        c.myID[:],
-			Addresses: addrs,
-		},
+		ID:        c.myID[:],
+		Addresses: c.addrList.AllAddresses(),
 	}
 	}
 }
 }
 
 
 func (c *localClient) sendLocalAnnouncements() {
 func (c *localClient) sendLocalAnnouncements() {
+	msg := make([]byte, 4)
+	binary.BigEndian.PutUint32(msg, Magic)
+
 	var pkt = c.announcementPkt()
 	var pkt = c.announcementPkt()
-	msg := pkt.MustMarshalXDR()
+	bs, _ := pkt.Marshal()
+	msg = append(msg, bs...)
 
 
 	for {
 	for {
 		c.beacon.Send(msg)
 		c.beacon.Send(msg)
@@ -138,26 +138,44 @@ func (c *localClient) sendLocalAnnouncements() {
 }
 }
 
 
 func (c *localClient) recvAnnouncements(b beacon.Interface) {
 func (c *localClient) recvAnnouncements(b beacon.Interface) {
+	warnedAbout := make(map[string]bool)
 	for {
 	for {
 		buf, addr := b.Recv()
 		buf, addr := b.Recv()
+		if len(buf) < 4 {
+			l.Debugf("discover: short packet from %s")
+			continue
+		}
 
 
-		var pkt Announce
-		err := pkt.UnmarshalXDR(buf)
-		if err != nil && err != io.EOF {
-			l.Debugf("discover: Failed to unmarshal local announcement from %s:\n%s", addr, hex.Dump(buf))
+		magic := binary.BigEndian.Uint32(buf)
+		switch magic {
+		case Magic:
+			// All good
+
+		case v13Magic:
+			// Old version
+			if !warnedAbout[addr.String()] {
+				l.Warnf("Incompatible (v0.13) local discovery packet from %v - upgrade that device to connect", addr)
+				warnedAbout[addr.String()] = true
+			}
+			continue
+
+		default:
+			l.Debugf("discover: Incorrect magic %x from %s", magic, addr)
 			continue
 			continue
 		}
 		}
 
 
-		if pkt.Magic != AnnouncementMagic {
-			l.Debugf("discover: Incorrect magic from %s: %s != %s", addr, pkt.Magic, AnnouncementMagic)
+		var pkt Announce
+		err := pkt.Unmarshal(buf[4:])
+		if err != nil && err != io.EOF {
+			l.Debugf("discover: Failed to unmarshal local announcement from %s:\n%s", addr, hex.Dump(buf))
 			continue
 			continue
 		}
 		}
 
 
-		l.Debugf("discover: Received local announcement from %s for %s", addr, protocol.DeviceIDFromBytes(pkt.This.ID))
+		l.Debugf("discover: Received local announcement from %s for %s", addr, protocol.DeviceIDFromBytes(pkt.ID))
 
 
 		var newDevice bool
 		var newDevice bool
-		if !bytes.Equal(pkt.This.ID, c.myID[:]) {
-			newDevice = c.registerDevice(addr, pkt.This)
+		if !bytes.Equal(pkt.ID, c.myID[:]) {
+			newDevice = c.registerDevice(addr, pkt)
 		}
 		}
 
 
 		if newDevice {
 		if newDevice {
@@ -171,7 +189,7 @@ func (c *localClient) recvAnnouncements(b beacon.Interface) {
 	}
 	}
 }
 }
 
 
-func (c *localClient) registerDevice(src net.Addr, device Device) bool {
+func (c *localClient) registerDevice(src net.Addr, device Announce) bool {
 	var id protocol.DeviceID
 	var id protocol.DeviceID
 	copy(id[:], device.ID)
 	copy(id[:], device.ID)
 
 
@@ -186,7 +204,7 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
 	l.Debugln("discover: Registering addresses for", id)
 	l.Debugln("discover: Registering addresses for", id)
 	var validAddresses []string
 	var validAddresses []string
 	for _, addr := range device.Addresses {
 	for _, addr := range device.Addresses {
-		u, err := url.Parse(addr.URL)
+		u, err := url.Parse(addr)
 		if err != nil {
 		if err != nil {
 			continue
 			continue
 		}
 		}
@@ -219,10 +237,10 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
 			u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
 			u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
 			l.Debugf("discover: Reconstructed URL is %#v", u)
 			l.Debugf("discover: Reconstructed URL is %#v", u)
 			validAddresses = append(validAddresses, u.String())
 			validAddresses = append(validAddresses, u.String())
-			l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr.URL, u.String())
+			l.Debugf("discover: Replaced address %v in %s to get %s", tcpAddr.IP, addr, u.String())
 		} else {
 		} else {
-			validAddresses = append(validAddresses, addr.URL)
-			l.Debugf("discover: Accepted address %s verbatim", addr.URL)
+			validAddresses = append(validAddresses, addr)
+			l.Debugf("discover: Accepted address %s verbatim", addr)
 		}
 		}
 	}
 	}
 
 

+ 368 - 0
lib/discover/local.pb.go

@@ -0,0 +1,368 @@
+// Code generated by protoc-gen-gogo.
+// source: local.proto
+// DO NOT EDIT!
+
+/*
+	Package discover is a generated protocol buffer package.
+
+	It is generated from these files:
+		local.proto
+
+	It has these top-level messages:
+		Announce
+*/
+package discover
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import _ "github.com/gogo/protobuf/gogoproto"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+type Announce struct {
+	ID        []byte   `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	Addresses []string `protobuf:"bytes,2,rep,name=addresses" json:"addresses,omitempty"`
+}
+
+func (m *Announce) Reset()                    { *m = Announce{} }
+func (m *Announce) String() string            { return proto.CompactTextString(m) }
+func (*Announce) ProtoMessage()               {}
+func (*Announce) Descriptor() ([]byte, []int) { return fileDescriptorLocal, []int{0} }
+
+func init() {
+	proto.RegisterType((*Announce)(nil), "discover.Announce")
+}
+func (m *Announce) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Announce) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.ID) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintLocal(data, i, uint64(len(m.ID)))
+		i += copy(data[i:], m.ID)
+	}
+	if len(m.Addresses) > 0 {
+		for _, s := range m.Addresses {
+			data[i] = 0x12
+			i++
+			l = len(s)
+			for l >= 1<<7 {
+				data[i] = uint8(uint64(l)&0x7f | 0x80)
+				l >>= 7
+				i++
+			}
+			data[i] = uint8(l)
+			i++
+			i += copy(data[i:], s)
+		}
+	}
+	return i, nil
+}
+
+func encodeFixed64Local(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 encodeFixed32Local(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 encodeVarintLocal(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 *Announce) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.ID)
+	if l > 0 {
+		n += 1 + l + sovLocal(uint64(l))
+	}
+	if len(m.Addresses) > 0 {
+		for _, s := range m.Addresses {
+			l = len(s)
+			n += 1 + l + sovLocal(uint64(l))
+		}
+	}
+	return n
+}
+
+func sovLocal(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozLocal(x uint64) (n int) {
+	return sovLocal(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Announce) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowLocal
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Announce: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Announce: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLocal
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthLocal
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ID = append(m.ID[:0], data[iNdEx:postIndex]...)
+			if m.ID == nil {
+				m.ID = []byte{}
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowLocal
+				}
+				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 ErrInvalidLengthLocal
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Addresses = append(m.Addresses, string(data[iNdEx:postIndex]))
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipLocal(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthLocal
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipLocal(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowLocal
+			}
+			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 shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowLocal
+				}
+				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 shift >= 64 {
+					return 0, ErrIntOverflowLocal
+				}
+				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, ErrInvalidLengthLocal
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start int = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowLocal
+					}
+					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 := skipLocal(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 (
+	ErrInvalidLengthLocal = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowLocal   = fmt.Errorf("proto: integer overflow")
+)
+
+var fileDescriptorLocal = []byte{
+	// 161 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0xc9, 0x4f, 0x4e,
+	0xcc, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x48, 0xc9, 0x2c, 0x4e, 0xce, 0x2f, 0x4b,
+	0x2d, 0x92, 0xd2, 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf,
+	0x4f, 0xcf, 0xd7, 0x07, 0x2b, 0x48, 0x2a, 0x4d, 0x03, 0xf3, 0xc0, 0x1c, 0x30, 0x0b, 0xa2, 0x51,
+	0xc9, 0x81, 0x8b, 0xc3, 0x31, 0x2f, 0x2f, 0xbf, 0x34, 0x2f, 0x39, 0x55, 0x48, 0x8c, 0x8b, 0x29,
+	0x33, 0x45, 0x82, 0x51, 0x81, 0x51, 0x83, 0xc7, 0x89, 0xed, 0xd1, 0x3d, 0x79, 0x26, 0x4f, 0x97,
+	0x20, 0xa0, 0x88, 0x90, 0x0c, 0x17, 0x67, 0x62, 0x4a, 0x4a, 0x51, 0x6a, 0x71, 0x71, 0x6a, 0xb1,
+	0x04, 0x93, 0x02, 0xb3, 0x06, 0x67, 0x10, 0x42, 0xc0, 0x49, 0xe4, 0xc4, 0x43, 0x39, 0x86, 0x13,
+	0x8f, 0xe4, 0x18, 0x2f, 0x00, 0xf1, 0x83, 0x47, 0x72, 0x0c, 0x0b, 0x1e, 0xcb, 0x31, 0x26, 0xb1,
+	0x81, 0x8d, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc9, 0xec, 0xea, 0xbc, 0xa6, 0x00, 0x00,
+	0x00,
+}

+ 14 - 0
lib/discover/local.proto

@@ -0,0 +1,14 @@
+syntax = "proto3";
+
+package discover;
+
+import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.sizer_all) = false;
+option (gogoproto.protosizer_all) = true;
+
+message Announce {
+    bytes           id        = 1 [(gogoproto.customname) = "ID"];
+    repeated string addresses = 2;
+}

+ 0 - 29
lib/discover/localpackets.go

@@ -1,29 +0,0 @@
-// Copyright (C) 2014 The Syncthing Authors.
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this file,
-// You can obtain one at http://mozilla.org/MPL/2.0/.
-
-//go:generate -command genxdr go run ../../vendor/github.com/calmh/xdr/cmd/genxdr/main.go
-//go:generate genxdr -o localpackets_xdr.go localpackets.go
-
-package discover
-
-const (
-	AnnouncementMagic = 0x7D79BC40
-)
-
-type Announce struct {
-	Magic uint32
-	This  Device
-	Extra []Device // max:16
-}
-
-type Device struct {
-	ID        []byte    // max:32
-	Addresses []Address // max:16
-}
-
-type Address struct {
-	URL string // max:2083
-}

+ 0 - 246
lib/discover/localpackets_xdr.go

@@ -1,246 +0,0 @@
-// ************************************************************
-// This file is automatically generated by genxdr. Do not edit.
-// ************************************************************
-
-package discover
-
-import (
-	"github.com/calmh/xdr"
-)
-
-/*
-
-Announce Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Magic                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                       Device Structure                        \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                        Number of Extra                        |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Device Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct Announce {
-	unsigned int Magic;
-	Device This;
-	Device Extra<16>;
-}
-
-*/
-
-func (o Announce) XDRSize() int {
-	return 4 +
-		o.This.XDRSize() +
-		4 + xdr.SizeOfSlice(o.Extra)
-}
-
-func (o Announce) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o Announce) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o Announce) MarshalXDRInto(m *xdr.Marshaller) error {
-	m.MarshalUint32(o.Magic)
-	if err := o.This.MarshalXDRInto(m); err != nil {
-		return err
-	}
-	if l := len(o.Extra); l > 16 {
-		return xdr.ElementSizeExceeded("Extra", l, 16)
-	}
-	m.MarshalUint32(uint32(len(o.Extra)))
-	for i := range o.Extra {
-		if err := o.Extra[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *Announce) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *Announce) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Magic = u.UnmarshalUint32()
-	(&o.This).UnmarshalXDRFrom(u)
-	_ExtraSize := int(u.UnmarshalUint32())
-	if _ExtraSize < 0 {
-		return xdr.ElementSizeExceeded("Extra", _ExtraSize, 16)
-	} else if _ExtraSize == 0 {
-		o.Extra = nil
-	} else {
-		if _ExtraSize > 16 {
-			return xdr.ElementSizeExceeded("Extra", _ExtraSize, 16)
-		}
-		if _ExtraSize <= len(o.Extra) {
-			o.Extra = o.Extra[:_ExtraSize]
-		} else {
-			o.Extra = make([]Device, _ExtraSize)
-		}
-		for i := range o.Extra {
-			(&o.Extra[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-Device Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                   ID (length + padded data)                   \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                      Number of Addresses                      |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Address Structures                \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct Device {
-	opaque ID<32>;
-	Address Addresses<16>;
-}
-
-*/
-
-func (o Device) XDRSize() int {
-	return 4 + len(o.ID) + xdr.Padding(len(o.ID)) +
-		4 + xdr.SizeOfSlice(o.Addresses)
-}
-
-func (o Device) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o Device) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o Device) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.ID); l > 32 {
-		return xdr.ElementSizeExceeded("ID", l, 32)
-	}
-	m.MarshalBytes(o.ID)
-	if l := len(o.Addresses); l > 16 {
-		return xdr.ElementSizeExceeded("Addresses", l, 16)
-	}
-	m.MarshalUint32(uint32(len(o.Addresses)))
-	for i := range o.Addresses {
-		if err := o.Addresses[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *Device) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *Device) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.ID = u.UnmarshalBytesMax(32)
-	_AddressesSize := int(u.UnmarshalUint32())
-	if _AddressesSize < 0 {
-		return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
-	} else if _AddressesSize == 0 {
-		o.Addresses = nil
-	} else {
-		if _AddressesSize > 16 {
-			return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 16)
-		}
-		if _AddressesSize <= len(o.Addresses) {
-			o.Addresses = o.Addresses[:_AddressesSize]
-		} else {
-			o.Addresses = make([]Address, _AddressesSize)
-		}
-		for i := range o.Addresses {
-			(&o.Addresses[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-Address Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  URL (length + padded data)                   \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct Address {
-	string URL<2083>;
-}
-
-*/
-
-func (o Address) XDRSize() int {
-	return 4 + len(o.URL) + xdr.Padding(len(o.URL))
-}
-
-func (o Address) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o Address) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o Address) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.URL); l > 2083 {
-		return xdr.ElementSizeExceeded("URL", l, 2083)
-	}
-	m.MarshalString(o.URL)
-	return m.Error
-}
-
-func (o *Address) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *Address) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.URL = u.UnmarshalStringMax(2083)
-	return u.Error
-}

+ 58 - 97
lib/model/model.go

@@ -40,10 +40,8 @@ import (
 
 
 // How many files to send in each Index/IndexUpdate message.
 // How many files to send in each Index/IndexUpdate message.
 const (
 const (
-	indexTargetSize   = 250 * 1024 // Aim for making index messages no larger than 250 KiB (uncompressed)
-	indexPerFileSize  = 250        // Each FileInfo is approximately this big, in bytes, excluding BlockInfos
-	indexPerBlockSize = 40         // Each BlockInfo is approximately this big
-	indexBatchSize    = 1000       // Either way, don't include more files than this
+	indexTargetSize = 250 * 1024 // Aim for making index messages no larger than 250 KiB (uncompressed)
+	indexBatchSize  = 1000       // Either way, don't include more files than this
 )
 )
 
 
 type service interface {
 type service interface {
@@ -95,7 +93,7 @@ type Model struct {
 
 
 	conn              map[protocol.DeviceID]connections.Connection
 	conn              map[protocol.DeviceID]connections.Connection
 	helloMessages     map[protocol.DeviceID]protocol.HelloResult
 	helloMessages     map[protocol.DeviceID]protocol.HelloResult
-	deviceClusterConf map[protocol.DeviceID]protocol.ClusterConfigMessage
+	deviceClusterConf map[protocol.DeviceID]protocol.ClusterConfig
 	devicePaused      map[protocol.DeviceID]bool
 	devicePaused      map[protocol.DeviceID]bool
 	deviceDownloads   map[protocol.DeviceID]*deviceDownloadState
 	deviceDownloads   map[protocol.DeviceID]*deviceDownloadState
 	pmut              sync.RWMutex // protects the above
 	pmut              sync.RWMutex // protects the above
@@ -149,7 +147,7 @@ func NewModel(cfg *config.Wrapper, id protocol.DeviceID, deviceName, clientName,
 		folderStatRefs:     make(map[string]*stats.FolderStatisticsReference),
 		folderStatRefs:     make(map[string]*stats.FolderStatisticsReference),
 		conn:               make(map[protocol.DeviceID]connections.Connection),
 		conn:               make(map[protocol.DeviceID]connections.Connection),
 		helloMessages:      make(map[protocol.DeviceID]protocol.HelloResult),
 		helloMessages:      make(map[protocol.DeviceID]protocol.HelloResult),
-		deviceClusterConf:  make(map[protocol.DeviceID]protocol.ClusterConfigMessage),
+		deviceClusterConf:  make(map[protocol.DeviceID]protocol.ClusterConfig),
 		devicePaused:       make(map[protocol.DeviceID]bool),
 		devicePaused:       make(map[protocol.DeviceID]bool),
 		deviceDownloads:    make(map[protocol.DeviceID]*deviceDownloadState),
 		deviceDownloads:    make(map[protocol.DeviceID]*deviceDownloadState),
 		fmut:               sync.NewRWMutex(),
 		fmut:               sync.NewRWMutex(),
@@ -414,7 +412,7 @@ func (m *Model) Completion(device protocol.DeviceID, folder string) float64 {
 		// This might might be more than it really is, because some blocks can be of a smaller size.
 		// This might might be more than it really is, because some blocks can be of a smaller size.
 		downloaded = int64(counts[ft.Name] * protocol.BlockSize)
 		downloaded = int64(counts[ft.Name] * protocol.BlockSize)
 
 
-		fileNeed = ft.Size() - downloaded
+		fileNeed = ft.Size - downloaded
 		if fileNeed < 0 {
 		if fileNeed < 0 {
 			fileNeed = 0
 			fileNeed = 0
 		}
 		}
@@ -436,7 +434,7 @@ func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {
 	} else {
 	} else {
 		deleted++
 		deleted++
 	}
 	}
-	bytes += f.Size()
+	bytes += f.FileSize()
 	return
 	return
 }
 }
 
 
@@ -548,12 +546,7 @@ func (m *Model) NeedFolderFiles(folder string, page, perpage int) ([]db.FileInfo
 
 
 // Index is called when a new device is connected and we receive their full index.
 // Index is called when a new device is connected and we receive their full index.
 // Implements the protocol.Model interface.
 // Implements the protocol.Model interface.
-func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, flags uint32, options []protocol.Option) {
-	if flags != 0 {
-		l.Warnln("protocol error: unknown flags 0x%x in Index message", flags)
-		return
-	}
-
+func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) {
 	l.Debugf("IDX(in): %s %q: %d files", deviceID, folder, len(fs))
 	l.Debugf("IDX(in): %s %q: %d files", deviceID, folder, len(fs))
 
 
 	if !m.folderSharedWith(folder, deviceID) {
 	if !m.folderSharedWith(folder, deviceID) {
@@ -595,12 +588,7 @@ func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.F
 
 
 // IndexUpdate is called for incremental updates to connected devices' indexes.
 // IndexUpdate is called for incremental updates to connected devices' indexes.
 // Implements the protocol.Model interface.
 // Implements the protocol.Model interface.
-func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo, flags uint32, options []protocol.Option) {
-	if flags != 0 {
-		l.Warnln("protocol error: unknown flags 0x%x in IndexUpdate message", flags)
-		return
-	}
-
+func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []protocol.FileInfo) {
 	l.Debugf("%v IDXUP(in): %s / %q: %d files", m, deviceID, folder, len(fs))
 	l.Debugf("%v IDXUP(in): %s / %q: %d files", m, deviceID, folder, len(fs))
 
 
 	if !m.folderSharedWith(folder, deviceID) {
 	if !m.folderSharedWith(folder, deviceID) {
@@ -651,7 +639,7 @@ func (m *Model) folderSharedWithUnlocked(folder string, deviceID protocol.Device
 	return false
 	return false
 }
 }
 
 
-func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfigMessage) {
+func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterConfig) {
 	// Check the peer device's announced folders against our own. Emits events
 	// Check the peer device's announced folders against our own. Emits events
 	// for folders that we don't expect (unknown or not shared).
 	// for folders that we don't expect (unknown or not shared).
 	// Also, collect a list of folders we do share, and if he's interested in
 	// Also, collect a list of folders we do share, and if he's interested in
@@ -659,19 +647,9 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 
 
 	tempIndexFolders := make([]string, 0, len(cm.Folders))
 	tempIndexFolders := make([]string, 0, len(cm.Folders))
 
 
+	m.fmut.Lock()
 	for _, folder := range cm.Folders {
 	for _, folder := range cm.Folders {
-		if folder.Flags&^protocol.FlagFolderAll != 0 {
-			// There are flags set that we don't know what they mean. Fatal!
-			l.Warnf("Device %v: unknown flags for folder %s", deviceID, folder.ID)
-			m.fmut.Unlock()
-			m.Close(deviceID, fmt.Errorf("Unknown folder flags from device %v", deviceID))
-			return
-		}
-
-		m.fmut.Lock()
-		shared := m.folderSharedWithUnlocked(folder.ID, deviceID)
-		m.fmut.Unlock()
-		if !shared {
+		if !m.folderSharedWithUnlocked(folder.ID, deviceID) {
 			events.Default.Log(events.FolderRejected, map[string]string{
 			events.Default.Log(events.FolderRejected, map[string]string{
 				"folder":      folder.ID,
 				"folder":      folder.ID,
 				"folderLabel": folder.Label,
 				"folderLabel": folder.Label,
@@ -680,10 +658,11 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 			l.Infof("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder.ID, deviceID)
 			l.Infof("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder.ID, deviceID)
 			continue
 			continue
 		}
 		}
-		if folder.Flags&protocol.FlagFolderDisabledTempIndexes == 0 {
+		if !folder.DisableTempIndexes {
 			tempIndexFolders = append(tempIndexFolders, folder.ID)
 			tempIndexFolders = append(tempIndexFolders, folder.ID)
 		}
 		}
 	}
 	}
+	m.fmut.Unlock()
 
 
 	// This breaks if we send multiple CM messages during the same connection.
 	// This breaks if we send multiple CM messages during the same connection.
 	if len(tempIndexFolders) > 0 {
 	if len(tempIndexFolders) > 0 {
@@ -733,7 +712,7 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 					}
 					}
 
 
 					// The introducers' introducers are also our introducers.
 					// The introducers' introducers are also our introducers.
-					if device.Flags&protocol.FlagIntroducer != 0 {
+					if device.Introducer {
 						l.Infof("Device %v is now also an introducer", id)
 						l.Infof("Device %v is now also an introducer", id)
 						newDeviceCfg.Introducer = true
 						newDeviceCfg.Introducer = true
 					}
 					}
@@ -804,7 +783,7 @@ func (m *Model) Close(device protocol.DeviceID, err error) {
 
 
 // Request returns the specified data segment by reading it from local disk.
 // Request returns the specified data segment by reading it from local disk.
 // Implements the protocol.Model interface.
 // Implements the protocol.Model interface.
-func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset int64, hash []byte, flags uint32, options []protocol.Option, buf []byte) error {
+func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
 	if offset < 0 {
 	if offset < 0 {
 		return protocol.ErrInvalid
 		return protocol.ErrInvalid
 	}
 	}
@@ -813,14 +792,8 @@ func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset
 		l.Warnf("Request from %s for file %s in unshared folder %q", deviceID, name, folder)
 		l.Warnf("Request from %s for file %s in unshared folder %q", deviceID, name, folder)
 		return protocol.ErrNoSuchFile
 		return protocol.ErrNoSuchFile
 	}
 	}
-
-	if flags != 0 && flags != protocol.FlagFromTemporary {
-		// We currently support only no flags, or FromTemporary flag.
-		return fmt.Errorf("protocol error: unknown flags 0x%x in Request message", flags)
-	}
-
 	if deviceID != protocol.LocalDeviceID {
 	if deviceID != protocol.LocalDeviceID {
-		l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d f=%d", m, deviceID, folder, name, offset, len(buf), flags)
+		l.Debugf("%v REQ(in): %s: %q / %q o=%d s=%d t=%v", m, deviceID, folder, name, offset, len(buf), fromTemporary)
 	}
 	}
 	m.fmut.RLock()
 	m.fmut.RLock()
 	folderCfg := m.folderCfgs[folder]
 	folderCfg := m.folderCfgs[folder]
@@ -880,7 +853,7 @@ func (m *Model) Request(deviceID protocol.DeviceID, folder, name string, offset
 
 
 	// Only check temp files if the flag is set, and if we are set to advertise
 	// Only check temp files if the flag is set, and if we are set to advertise
 	// the temp indexes.
 	// the temp indexes.
-	if flags&protocol.FlagFromTemporary != 0 && !folderCfg.DisableTempIndexes {
+	if fromTemporary && !folderCfg.DisableTempIndexes {
 		tempFn := filepath.Join(folderPath, defTempNamer.TempName(name))
 		tempFn := filepath.Join(folderPath, defTempNamer.TempName(name))
 		if err := readOffsetIntoBuf(tempFn, offset, buf); err == nil {
 		if err := readOffsetIntoBuf(tempFn, offset, buf); err == nil {
 			return nil
 			return nil
@@ -1027,8 +1000,8 @@ func (m *Model) OnHello(remoteID protocol.DeviceID, addr net.Addr, hello protoco
 }
 }
 
 
 // GetHello is called when we are about to connect to some remote device.
 // GetHello is called when we are about to connect to some remote device.
-func (m *Model) GetHello(protocol.DeviceID) protocol.Version13HelloMessage {
-	return protocol.Version13HelloMessage{
+func (m *Model) GetHello(protocol.DeviceID) protocol.HelloIntf {
+	return &protocol.Hello{
 		DeviceName:    m.deviceName,
 		DeviceName:    m.deviceName,
 		ClientName:    m.clientName,
 		ClientName:    m.clientName,
 		ClientVersion: m.clientVersion,
 		ClientVersion: m.clientVersion,
@@ -1101,7 +1074,7 @@ func (m *Model) PauseDevice(device protocol.DeviceID) {
 	events.Default.Log(events.DevicePaused, map[string]string{"device": device.String()})
 	events.Default.Log(events.DevicePaused, map[string]string{"device": device.String()})
 }
 }
 
 
-func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate, flags uint32, options []protocol.Option) {
+func (m *Model) DownloadProgress(device protocol.DeviceID, folder string, updates []protocol.FileDownloadProgressUpdate) {
 	if !m.folderSharedWith(folder, device) {
 	if !m.folderSharedWith(folder, device) {
 		return
 		return
 	}
 	}
@@ -1238,13 +1211,13 @@ func sendIndexTo(initial bool, minLocalVer int64, conn protocol.Connection, fold
 
 
 		if len(batch) == indexBatchSize || currentBatchSize > indexTargetSize {
 		if len(batch) == indexBatchSize || currentBatchSize > indexTargetSize {
 			if initial {
 			if initial {
-				if err = conn.Index(folder, batch, 0, nil); err != nil {
+				if err = conn.Index(folder, batch); err != nil {
 					return false
 					return false
 				}
 				}
 				l.Debugf("sendIndexes for %s-%s/%q: %d files (<%d bytes) (initial index)", deviceID, name, folder, len(batch), currentBatchSize)
 				l.Debugf("sendIndexes for %s-%s/%q: %d files (<%d bytes) (initial index)", deviceID, name, folder, len(batch), currentBatchSize)
 				initial = false
 				initial = false
 			} else {
 			} else {
-				if err = conn.IndexUpdate(folder, batch, 0, nil); err != nil {
+				if err = conn.IndexUpdate(folder, batch); err != nil {
 					return false
 					return false
 				}
 				}
 				l.Debugf("sendIndexes for %s-%s/%q: %d files (<%d bytes) (batched update)", deviceID, name, folder, len(batch), currentBatchSize)
 				l.Debugf("sendIndexes for %s-%s/%q: %d files (<%d bytes) (batched update)", deviceID, name, folder, len(batch), currentBatchSize)
@@ -1255,17 +1228,17 @@ func sendIndexTo(initial bool, minLocalVer int64, conn protocol.Connection, fold
 		}
 		}
 
 
 		batch = append(batch, f)
 		batch = append(batch, f)
-		currentBatchSize += indexPerFileSize + len(f.Blocks)*indexPerBlockSize
+		currentBatchSize += f.ProtoSize()
 		return true
 		return true
 	})
 	})
 
 
 	if initial && err == nil {
 	if initial && err == nil {
-		err = conn.Index(folder, batch, 0, nil)
+		err = conn.Index(folder, batch)
 		if err == nil {
 		if err == nil {
 			l.Debugf("sendIndexes for %s-%s/%q: %d files (small initial index)", deviceID, name, folder, len(batch))
 			l.Debugf("sendIndexes for %s-%s/%q: %d files (small initial index)", deviceID, name, folder, len(batch))
 		}
 		}
 	} else if len(batch) > 0 && err == nil {
 	} else if len(batch) > 0 && err == nil {
-		err = conn.IndexUpdate(folder, batch, 0, nil)
+		err = conn.IndexUpdate(folder, batch)
 		if err == nil {
 		if err == nil {
 			l.Debugf("sendIndexes for %s-%s/%q: %d files (last batch)", deviceID, name, folder, len(batch))
 			l.Debugf("sendIndexes for %s-%s/%q: %d files (last batch)", deviceID, name, folder, len(batch))
 		}
 		}
@@ -1320,11 +1293,14 @@ func (m *Model) localChangeDetected(folder, path string, files []protocol.FileIn
 		objType := "file"
 		objType := "file"
 		action := "modified"
 		action := "modified"
 
 
-		// If our local vector is verison 1 AND it is the only version vector so far seen for this file then
-		// it is a new file.  Else if it is > 1 it's not new, and if it is 1 but another shortId version vector
-		// exists then it is new for us but created elsewhere so the file is still not new but modified by us.
-		// Only if it is truly new do we change this to 'added', else we leave it as 'modified'.
-		if len(file.Version) == 1 && file.Version[0].Value == 1 {
+		// If our local vector is verison 1 AND it is the only version
+		// vector so far seen for this file then it is a new file.  Else if
+		// it is > 1 it's not new, and if it is 1 but another shortId
+		// version vector exists then it is new for us but created elsewhere
+		// so the file is still not new but modified by us. Only if it is
+		// truly new do we change this to 'added', else we leave it as
+		// 'modified'.
+		if len(file.Version.Counters) == 1 && file.Version.Counters[0].Value == 1 {
 			action = "added"
 			action = "added"
 		}
 		}
 
 
@@ -1579,10 +1555,14 @@ func (m *Model) internalScanFolderSubdirs(folder string, subDirs []string) error
 					// File has been ignored or an unsupported symlink. Set invalid bit.
 					// File has been ignored or an unsupported symlink. Set invalid bit.
 					l.Debugln("setting invalid bit on ignored", f)
 					l.Debugln("setting invalid bit on ignored", f)
 					nf := protocol.FileInfo{
 					nf := protocol.FileInfo{
-						Name:     f.Name,
-						Flags:    f.Flags | protocol.FlagInvalid,
-						Modified: f.Modified,
-						Version:  f.Version, // The file is still the same, so don't bump version
+						Name:          f.Name,
+						Type:          f.Type,
+						Size:          f.Size,
+						Modified:      f.Modified,
+						Permissions:   f.Permissions,
+						NoPermissions: f.NoPermissions,
+						Invalid:       true,
+						Version:       f.Version, // The file is still the same, so don't bump version
 					}
 					}
 					batch = append(batch, nf)
 					batch = append(batch, nf)
 				} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
 				} else if _, err := osutil.Lstat(filepath.Join(folderCfg.Path(), f.Name)); err != nil {
@@ -1597,16 +1577,13 @@ func (m *Model) internalScanFolderSubdirs(folder string, subDirs []string) error
 
 
 					nf := protocol.FileInfo{
 					nf := protocol.FileInfo{
 						Name:     f.Name,
 						Name:     f.Name,
-						Flags:    f.Flags | protocol.FlagDeleted,
+						Type:     f.Type,
+						Size:     f.Size,
 						Modified: f.Modified,
 						Modified: f.Modified,
+						Deleted:  true,
 						Version:  f.Version.Update(m.shortID),
 						Version:  f.Version.Update(m.shortID),
 					}
 					}
 
 
-					// The deleted file might have been ignored at some
-					// point, but it currently isn't so we make sure to
-					// clear the invalid bit.
-					nf.Flags &^= protocol.FlagInvalid
-
 					batch = append(batch, nf)
 					batch = append(batch, nf)
 				}
 				}
 			}
 			}
@@ -1672,30 +1649,21 @@ func (m *Model) numHashers(folder string) int {
 
 
 // generateClusterConfig returns a ClusterConfigMessage that is correct for
 // generateClusterConfig returns a ClusterConfigMessage that is correct for
 // the given peer device
 // the given peer device
-func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfigMessage {
-	var message protocol.ClusterConfigMessage
+func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.ClusterConfig {
+	var message protocol.ClusterConfig
 
 
 	m.fmut.RLock()
 	m.fmut.RLock()
 	for _, folder := range m.deviceFolders[device] {
 	for _, folder := range m.deviceFolders[device] {
 		folderCfg := m.cfg.Folders()[folder]
 		folderCfg := m.cfg.Folders()[folder]
 		protocolFolder := protocol.Folder{
 		protocolFolder := protocol.Folder{
-			ID:    folder,
-			Label: folderCfg.Label,
-		}
-		var flags uint32
-		if folderCfg.Type == config.FolderTypeReadOnly {
-			flags |= protocol.FlagFolderReadOnly
-		}
-		if folderCfg.IgnorePerms {
-			flags |= protocol.FlagFolderIgnorePerms
+			ID:                 folder,
+			Label:              folderCfg.Label,
+			ReadOnly:           folderCfg.Type == config.FolderTypeReadOnly,
+			IgnorePermissions:  folderCfg.IgnorePerms,
+			IgnoreDelete:       folderCfg.IgnoreDelete,
+			DisableTempIndexes: folderCfg.DisableTempIndexes,
 		}
 		}
-		if folderCfg.IgnoreDelete {
-			flags |= protocol.FlagFolderIgnoreDelete
-		}
-		if folderCfg.DisableTempIndexes {
-			flags |= protocol.FlagFolderDisabledTempIndexes
-		}
-		protocolFolder.Flags = flags
+
 		for _, device := range m.folderDevices[folder] {
 		for _, device := range m.folderDevices[folder] {
 			// DeviceID is a value type, but with an underlying array. Copy it
 			// DeviceID is a value type, but with an underlying array. Copy it
 			// so we don't grab aliases to the same array later on in device[:]
 			// so we don't grab aliases to the same array later on in device[:]
@@ -1707,14 +1675,11 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
 				ID:          device[:],
 				ID:          device[:],
 				Name:        deviceCfg.Name,
 				Name:        deviceCfg.Name,
 				Addresses:   deviceCfg.Addresses,
 				Addresses:   deviceCfg.Addresses,
-				Compression: uint32(deviceCfg.Compression),
+				Compression: deviceCfg.Compression,
 				CertName:    deviceCfg.CertName,
 				CertName:    deviceCfg.CertName,
-				Flags:       protocol.FlagShareTrusted,
+				Introducer:  deviceCfg.Introducer,
 			}
 			}
 
 
-			if deviceCfg.Introducer {
-				protocolDevice.Flags |= protocol.FlagIntroducer
-			}
 			protocolFolder.Devices = append(protocolFolder.Devices, protocolDevice)
 			protocolFolder.Devices = append(protocolFolder.Devices, protocolDevice)
 		}
 		}
 		message.Folders = append(message.Folders, protocolFolder)
 		message.Folders = append(message.Folders, protocolFolder)
@@ -1759,7 +1724,7 @@ func (m *Model) Override(folder string) {
 		have, ok := fs.Get(protocol.LocalDeviceID, need.Name)
 		have, ok := fs.Get(protocol.LocalDeviceID, need.Name)
 		if !ok || have.Name != need.Name {
 		if !ok || have.Name != need.Name {
 			// We are missing the file
 			// We are missing the file
-			need.Flags |= protocol.FlagDeleted
+			need.Deleted = true
 			need.Blocks = nil
 			need.Blocks = nil
 			need.Version = need.Version.Update(m.shortID)
 			need.Version = need.Version.Update(m.shortID)
 		} else {
 		} else {
@@ -1868,7 +1833,7 @@ func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly
 
 
 		if !dirsonly && base != "" {
 		if !dirsonly && base != "" {
 			last[base] = []interface{}{
 			last[base] = []interface{}{
-				time.Unix(f.Modified, 0), f.Size(),
+				time.Unix(f.Modified, 0), f.FileSize(),
 			}
 			}
 		}
 		}
 
 
@@ -2181,11 +2146,7 @@ func mapDeviceCfgs(devices []config.DeviceConfiguration) map[protocol.DeviceID]s
 
 
 func filterIndex(folder string, fs []protocol.FileInfo, dropDeletes bool, ignores *ignore.Matcher) []protocol.FileInfo {
 func filterIndex(folder string, fs []protocol.FileInfo, dropDeletes bool, ignores *ignore.Matcher) []protocol.FileInfo {
 	for i := 0; i < len(fs); {
 	for i := 0; i < len(fs); {
-		if fs[i].Flags&^protocol.FlagsAll != 0 {
-			l.Debugln("dropping update for file with unknown bits set", fs[i])
-			fs[i] = fs[len(fs)-1]
-			fs = fs[:len(fs)-1]
-		} else if fs[i].IsDeleted() && dropDeletes {
+		if fs[i].IsDeleted() && dropDeletes {
 			l.Debugln("dropping update for undesired delete", fs[i])
 			l.Debugln("dropping update for undesired delete", fs[i])
 			fs[i] = fs[len(fs)-1]
 			fs[i] = fs[len(fs)-1]
 			fs = fs[:len(fs)-1]
 			fs = fs[:len(fs)-1]

+ 37 - 76
lib/model/model_test.go

@@ -55,19 +55,19 @@ func init() {
 var testDataExpected = map[string]protocol.FileInfo{
 var testDataExpected = map[string]protocol.FileInfo{
 	"foo": {
 	"foo": {
 		Name:     "foo",
 		Name:     "foo",
-		Flags:    0,
+		Type:     protocol.FileInfoTypeFile,
 		Modified: 0,
 		Modified: 0,
 		Blocks:   []protocol.BlockInfo{{Offset: 0x0, Size: 0x7, Hash: []uint8{0xae, 0xc0, 0x70, 0x64, 0x5f, 0xe5, 0x3e, 0xe3, 0xb3, 0x76, 0x30, 0x59, 0x37, 0x61, 0x34, 0xf0, 0x58, 0xcc, 0x33, 0x72, 0x47, 0xc9, 0x78, 0xad, 0xd1, 0x78, 0xb6, 0xcc, 0xdf, 0xb0, 0x1, 0x9f}}},
 		Blocks:   []protocol.BlockInfo{{Offset: 0x0, Size: 0x7, Hash: []uint8{0xae, 0xc0, 0x70, 0x64, 0x5f, 0xe5, 0x3e, 0xe3, 0xb3, 0x76, 0x30, 0x59, 0x37, 0x61, 0x34, 0xf0, 0x58, 0xcc, 0x33, 0x72, 0x47, 0xc9, 0x78, 0xad, 0xd1, 0x78, 0xb6, 0xcc, 0xdf, 0xb0, 0x1, 0x9f}}},
 	},
 	},
 	"empty": {
 	"empty": {
 		Name:     "empty",
 		Name:     "empty",
-		Flags:    0,
+		Type:     protocol.FileInfoTypeFile,
 		Modified: 0,
 		Modified: 0,
 		Blocks:   []protocol.BlockInfo{{Offset: 0x0, Size: 0x0, Hash: []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}},
 		Blocks:   []protocol.BlockInfo{{Offset: 0x0, Size: 0x0, Hash: []uint8{0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}}},
 	},
 	},
 	"bar": {
 	"bar": {
 		Name:     "bar",
 		Name:     "bar",
-		Flags:    0,
+		Type:     protocol.FileInfoTypeFile,
 		Modified: 0,
 		Modified: 0,
 		Blocks:   []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}},
 		Blocks:   []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}},
 	},
 	},
@@ -77,8 +77,9 @@ func init() {
 	// Fix expected test data to match reality
 	// Fix expected test data to match reality
 	for n, f := range testDataExpected {
 	for n, f := range testDataExpected {
 		fi, _ := os.Stat("testdata/" + n)
 		fi, _ := os.Stat("testdata/" + n)
-		f.Flags = uint32(fi.Mode())
+		f.Permissions = uint32(fi.Mode())
 		f.Modified = fi.ModTime().Unix()
 		f.Modified = fi.ModTime().Unix()
+		f.Size = fi.Size()
 		testDataExpected[n] = f
 		testDataExpected[n] = f
 	}
 	}
 }
 }
@@ -98,7 +99,7 @@ func TestRequest(t *testing.T) {
 
 
 	// Existing, shared file
 	// Existing, shared file
 	bs = bs[:6]
 	bs = bs[:6]
-	err := m.Request(device1, "default", "foo", 0, nil, 0, nil, bs)
+	err := m.Request(device1, "default", "foo", 0, nil, false, bs)
 	if err != nil {
 	if err != nil {
 		t.Error(err)
 		t.Error(err)
 	}
 	}
@@ -107,32 +108,32 @@ func TestRequest(t *testing.T) {
 	}
 	}
 
 
 	// Existing, nonshared file
 	// Existing, nonshared file
-	err = m.Request(device2, "default", "foo", 0, nil, 0, nil, bs)
+	err = m.Request(device2, "default", "foo", 0, nil, false, bs)
 	if err == nil {
 	if err == nil {
 		t.Error("Unexpected nil error on insecure file read")
 		t.Error("Unexpected nil error on insecure file read")
 	}
 	}
 
 
 	// Nonexistent file
 	// Nonexistent file
-	err = m.Request(device1, "default", "nonexistent", 0, nil, 0, nil, bs)
+	err = m.Request(device1, "default", "nonexistent", 0, nil, false, bs)
 	if err == nil {
 	if err == nil {
 		t.Error("Unexpected nil error on insecure file read")
 		t.Error("Unexpected nil error on insecure file read")
 	}
 	}
 
 
 	// Shared folder, but disallowed file name
 	// Shared folder, but disallowed file name
-	err = m.Request(device1, "default", "../walk.go", 0, nil, 0, nil, bs)
+	err = m.Request(device1, "default", "../walk.go", 0, nil, false, bs)
 	if err == nil {
 	if err == nil {
 		t.Error("Unexpected nil error on insecure file read")
 		t.Error("Unexpected nil error on insecure file read")
 	}
 	}
 
 
 	// Negative offset
 	// Negative offset
-	err = m.Request(device1, "default", "foo", -4, nil, 0, nil, bs[:0])
+	err = m.Request(device1, "default", "foo", -4, nil, false, bs[:0])
 	if err == nil {
 	if err == nil {
 		t.Error("Unexpected nil error on insecure file read")
 		t.Error("Unexpected nil error on insecure file read")
 	}
 	}
 
 
 	// Larger block than available
 	// Larger block than available
 	bs = bs[:42]
 	bs = bs[:42]
-	err = m.Request(device1, "default", "foo", 0, nil, 0, nil, bs)
+	err = m.Request(device1, "default", "foo", 0, nil, false, bs)
 	if err == nil {
 	if err == nil {
 		t.Error("Unexpected nil error on insecure file read")
 		t.Error("Unexpected nil error on insecure file read")
 	}
 	}
@@ -168,11 +169,11 @@ func benchmarkIndex(b *testing.B, nfiles int) {
 	m.ServeBackground()
 	m.ServeBackground()
 
 
 	files := genFiles(nfiles)
 	files := genFiles(nfiles)
-	m.Index(device1, "default", files, 0, nil)
+	m.Index(device1, "default", files)
 
 
 	b.ResetTimer()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
-		m.Index(device1, "default", files, 0, nil)
+		m.Index(device1, "default", files)
 	}
 	}
 	b.ReportAllocs()
 	b.ReportAllocs()
 }
 }
@@ -199,11 +200,11 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
 	files := genFiles(nfiles)
 	files := genFiles(nfiles)
 	ufiles := genFiles(nufiles)
 	ufiles := genFiles(nufiles)
 
 
-	m.Index(device1, "default", files, 0, nil)
+	m.Index(device1, "default", files)
 
 
 	b.ResetTimer()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
-		m.IndexUpdate(device1, "default", ufiles, 0, nil)
+		m.IndexUpdate(device1, "default", ufiles)
 	}
 	}
 	b.ReportAllocs()
 	b.ReportAllocs()
 }
 }
@@ -211,8 +212,6 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
 type downloadProgressMessage struct {
 type downloadProgressMessage struct {
 	folder  string
 	folder  string
 	updates []protocol.FileDownloadProgressUpdate
 	updates []protocol.FileDownloadProgressUpdate
-	flags   uint32
-	options []protocol.Option
 }
 }
 
 
 type FakeConnection struct {
 type FakeConnection struct {
@@ -240,11 +239,11 @@ func (f FakeConnection) Option(string) string {
 	return ""
 	return ""
 }
 }
 
 
-func (FakeConnection) Index(string, []protocol.FileInfo, uint32, []protocol.Option) error {
+func (FakeConnection) Index(string, []protocol.FileInfo) error {
 	return nil
 	return nil
 }
 }
 
 
-func (FakeConnection) IndexUpdate(string, []protocol.FileInfo, uint32, []protocol.Option) error {
+func (FakeConnection) IndexUpdate(string, []protocol.FileInfo) error {
 	return nil
 	return nil
 }
 }
 
 
@@ -252,7 +251,7 @@ func (f FakeConnection) Request(folder, name string, offset int64, size int, has
 	return f.requestData, nil
 	return f.requestData, nil
 }
 }
 
 
-func (FakeConnection) ClusterConfig(protocol.ClusterConfigMessage) {}
+func (FakeConnection) ClusterConfig(protocol.ClusterConfig) {}
 
 
 func (FakeConnection) Ping() bool {
 func (FakeConnection) Ping() bool {
 	return true
 	return true
@@ -266,12 +265,10 @@ func (FakeConnection) Statistics() protocol.Statistics {
 	return protocol.Statistics{}
 	return protocol.Statistics{}
 }
 }
 
 
-func (f *FakeConnection) DownloadProgress(folder string, updates []protocol.FileDownloadProgressUpdate, flags uint32, options []protocol.Option) {
+func (f *FakeConnection) DownloadProgress(folder string, updates []protocol.FileDownloadProgressUpdate) {
 	f.downloadProgressMessages = append(f.downloadProgressMessages, downloadProgressMessage{
 	f.downloadProgressMessages = append(f.downloadProgressMessages, downloadProgressMessage{
 		folder:  folder,
 		folder:  folder,
 		updates: updates,
 		updates: updates,
-		flags:   flags,
-		options: options,
 	})
 	})
 }
 }
 
 
@@ -305,7 +302,7 @@ func BenchmarkRequest(b *testing.B) {
 		},
 		},
 		Connection: fc,
 		Connection: fc,
 	}, protocol.HelloResult{})
 	}, protocol.HelloResult{})
-	m.Index(device1, "default", files, 0, nil)
+	m.Index(device1, "default", files)
 
 
 	b.ResetTimer()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
@@ -451,13 +448,13 @@ func TestClusterConfig(t *testing.T) {
 	if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
 	if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
 		t.Errorf("Incorrect device ID %x != %x", id, device1)
 		t.Errorf("Incorrect device ID %x != %x", id, device1)
 	}
 	}
-	if r.Devices[0].Flags&protocol.FlagIntroducer == 0 {
+	if !r.Devices[0].Introducer {
 		t.Error("Device1 should be flagged as Introducer")
 		t.Error("Device1 should be flagged as Introducer")
 	}
 	}
 	if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
 	if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
 		t.Errorf("Incorrect device ID %x != %x", id, device2)
 		t.Errorf("Incorrect device ID %x != %x", id, device2)
 	}
 	}
-	if r.Devices[1].Flags&protocol.FlagIntroducer != 0 {
+	if r.Devices[1].Introducer {
 		t.Error("Device2 should not be flagged as Introducer")
 		t.Error("Device2 should not be flagged as Introducer")
 	}
 	}
 
 
@@ -471,13 +468,13 @@ func TestClusterConfig(t *testing.T) {
 	if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
 	if id := r.Devices[0].ID; !bytes.Equal(id, device1[:]) {
 		t.Errorf("Incorrect device ID %x != %x", id, device1)
 		t.Errorf("Incorrect device ID %x != %x", id, device1)
 	}
 	}
-	if r.Devices[0].Flags&protocol.FlagIntroducer == 0 {
+	if !r.Devices[0].Introducer {
 		t.Error("Device1 should be flagged as Introducer")
 		t.Error("Device1 should be flagged as Introducer")
 	}
 	}
 	if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
 	if id := r.Devices[1].ID; !bytes.Equal(id, device2[:]) {
 		t.Errorf("Incorrect device ID %x != %x", id, device2)
 		t.Errorf("Incorrect device ID %x != %x", id, device2)
 	}
 	}
-	if r.Devices[1].Flags&protocol.FlagIntroducer != 0 {
+	if r.Devices[1].Introducer {
 		t.Error("Device2 should not be flagged as Introducer")
 		t.Error("Device2 should not be flagged as Introducer")
 	}
 	}
 }
 }
@@ -572,44 +569,6 @@ func TestIgnores(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestRefuseUnknownBits(t *testing.T) {
-	db := db.OpenMemory()
-	m := NewModel(defaultConfig, protocol.LocalDeviceID, "device", "syncthing", "dev", db, nil)
-	m.AddFolder(defaultFolderConfig)
-	m.ServeBackground()
-
-	m.ScanFolder("default")
-	m.Index(device1, "default", []protocol.FileInfo{
-		{
-			Name:  "invalid1",
-			Flags: (protocol.FlagsAll + 1) &^ protocol.FlagInvalid,
-		},
-		{
-			Name:  "invalid2",
-			Flags: (protocol.FlagsAll + 2) &^ protocol.FlagInvalid,
-		},
-		{
-			Name:  "invalid3",
-			Flags: (1 << 31) &^ protocol.FlagInvalid,
-		},
-		{
-			Name:  "valid",
-			Flags: protocol.FlagsAll &^ (protocol.FlagInvalid | protocol.FlagSymlink),
-		},
-	}, 0, nil)
-
-	for _, name := range []string{"invalid1", "invalid2", "invalid3"} {
-		f, ok := m.CurrentGlobalFile("default", name)
-		if ok || f.Name == name {
-			t.Error("Invalid file found or name match")
-		}
-	}
-	f, ok := m.CurrentGlobalFile("default", "valid")
-	if !ok || f.Name != "valid" {
-		t.Error("Valid file not found or name mismatch", ok, f)
-	}
-}
-
 func TestROScanRecovery(t *testing.T) {
 func TestROScanRecovery(t *testing.T) {
 	ldb := db.OpenMemory()
 	ldb := db.OpenMemory()
 	set := db.NewFileSet("default", ldb)
 	set := db.NewFileSet("default", ldb)
@@ -789,17 +748,18 @@ func TestGlobalDirectoryTree(t *testing.T) {
 	m.ServeBackground()
 	m.ServeBackground()
 
 
 	b := func(isfile bool, path ...string) protocol.FileInfo {
 	b := func(isfile bool, path ...string) protocol.FileInfo {
-		flags := uint32(protocol.FlagDirectory)
+		typ := protocol.FileInfoTypeDirectory
 		blocks := []protocol.BlockInfo{}
 		blocks := []protocol.BlockInfo{}
 		if isfile {
 		if isfile {
-			flags = 0
+			typ = protocol.FileInfoTypeFile
 			blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
 			blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
 		}
 		}
 		return protocol.FileInfo{
 		return protocol.FileInfo{
 			Name:     filepath.Join(path...),
 			Name:     filepath.Join(path...),
-			Flags:    flags,
+			Type:     typ,
 			Modified: 0x666,
 			Modified: 0x666,
 			Blocks:   blocks,
 			Blocks:   blocks,
+			Size:     0xa,
 		}
 		}
 	}
 	}
 
 
@@ -871,7 +831,7 @@ func TestGlobalDirectoryTree(t *testing.T) {
 		return string(bytes)
 		return string(bytes)
 	}
 	}
 
 
-	m.Index(device1, "default", testdata, 0, nil)
+	m.Index(device1, "default", testdata)
 
 
 	result := m.GlobalDirectoryTree("default", "", -1, false)
 	result := m.GlobalDirectoryTree("default", "", -1, false)
 
 
@@ -1039,17 +999,18 @@ func TestGlobalDirectorySelfFixing(t *testing.T) {
 	m.ServeBackground()
 	m.ServeBackground()
 
 
 	b := func(isfile bool, path ...string) protocol.FileInfo {
 	b := func(isfile bool, path ...string) protocol.FileInfo {
-		flags := uint32(protocol.FlagDirectory)
+		typ := protocol.FileInfoTypeDirectory
 		blocks := []protocol.BlockInfo{}
 		blocks := []protocol.BlockInfo{}
 		if isfile {
 		if isfile {
-			flags = 0
+			typ = protocol.FileInfoTypeFile
 			blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
 			blocks = []protocol.BlockInfo{{Offset: 0x0, Size: 0xa, Hash: []uint8{0x2f, 0x72, 0xcc, 0x11, 0xa6, 0xfc, 0xd0, 0x27, 0x1e, 0xce, 0xf8, 0xc6, 0x10, 0x56, 0xee, 0x1e, 0xb1, 0x24, 0x3b, 0xe3, 0x80, 0x5b, 0xf9, 0xa9, 0xdf, 0x98, 0xf9, 0x2f, 0x76, 0x36, 0xb0, 0x5c}}}
 		}
 		}
 		return protocol.FileInfo{
 		return protocol.FileInfo{
 			Name:     filepath.Join(path...),
 			Name:     filepath.Join(path...),
-			Flags:    flags,
+			Type:     typ,
 			Modified: 0x666,
 			Modified: 0x666,
 			Blocks:   blocks,
 			Blocks:   blocks,
+			Size:     0xa,
 		}
 		}
 	}
 	}
 
 
@@ -1130,7 +1091,7 @@ func TestGlobalDirectorySelfFixing(t *testing.T) {
 		return string(bytes)
 		return string(bytes)
 	}
 	}
 
 
-	m.Index(device1, "default", testdata, 0, nil)
+	m.Index(device1, "default", testdata)
 
 
 	result := m.GlobalDirectoryTree("default", "", -1, false)
 	result := m.GlobalDirectoryTree("default", "", -1, false)
 
 
@@ -1215,7 +1176,7 @@ func benchmarkTree(b *testing.B, n1, n2 int) {
 	m.ScanFolder("default")
 	m.ScanFolder("default")
 	files := genDeepFiles(n1, n2)
 	files := genDeepFiles(n1, n2)
 
 
-	m.Index(device1, "default", files, 0, nil)
+	m.Index(device1, "default", files)
 
 
 	b.ResetTimer()
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 	for i := 0; i < b.N; i++ {
@@ -1244,12 +1205,12 @@ func TestIgnoreDelete(t *testing.T) {
 	}
 	}
 
 
 	// Mark it for deletion
 	// Mark it for deletion
-	f.Flags = protocol.FlagDeleted
+	f.Deleted = true
 	f.Version = f.Version.Update(142) // arbitrary short remote ID
 	f.Version = f.Version.Update(142) // arbitrary short remote ID
 	f.Blocks = nil
 	f.Blocks = nil
 
 
 	// Send the index
 	// Send the index
-	m.Index(device1, "default", []protocol.FileInfo{f}, 0, nil)
+	m.Index(device1, "default", []protocol.FileInfo{f})
 
 
 	// Make sure we ignored it
 	// Make sure we ignored it
 	f, ok = m.CurrentGlobalFile("default", "foo")
 	f, ok = m.CurrentGlobalFile("default", "foo")

+ 1 - 1
lib/model/progressemitter.go

@@ -137,7 +137,7 @@ func (t *ProgressEmitter) sendDownloadProgressMessages() {
 			updates := state.update(folder, activePullers)
 			updates := state.update(folder, activePullers)
 
 
 			if len(updates) > 0 {
 			if len(updates) > 0 {
-				conn.DownloadProgress(folder, updates, 0, nil)
+				conn.DownloadProgress(folder, updates)
 			}
 			}
 		}
 		}
 	}
 	}

+ 3 - 4
lib/model/progressemitter_test.go

@@ -100,7 +100,6 @@ func TestProgressEmitter(t *testing.T) {
 }
 }
 
 
 func TestSendDownloadProgressMessages(t *testing.T) {
 func TestSendDownloadProgressMessages(t *testing.T) {
-
 	c := config.Wrap("/tmp/test", config.Configuration{})
 	c := config.Wrap("/tmp/test", config.Configuration{})
 	c.SetOptions(config.OptionsConfiguration{
 	c.SetOptions(config.OptionsConfiguration{
 		ProgressUpdateIntervalS: 0,
 		ProgressUpdateIntervalS: 0,
@@ -112,7 +111,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
 	p := NewProgressEmitter(c)
 	p := NewProgressEmitter(c)
 	p.temporaryIndexSubscribe(fc, []string{"folder", "folder2"})
 	p.temporaryIndexSubscribe(fc, []string{"folder", "folder2"})
 
 
-	expect := func(updateIdx int, state *sharedPullerState, updateType uint32, version protocol.Vector, blocks []int32, remove bool) {
+	expect := func(updateIdx int, state *sharedPullerState, updateType protocol.FileDownloadProgressUpdateType, version protocol.Vector, blocks []int32, remove bool) {
 		messageIdx := -1
 		messageIdx := -1
 		for i, msg := range fc.downloadProgressMessages {
 		for i, msg := range fc.downloadProgressMessages {
 			if msg.folder == state.folder {
 			if msg.folder == state.folder {
@@ -346,7 +345,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
 		file: protocol.FileInfo{
 		file: protocol.FileInfo{
 			Name:    "state5",
 			Name:    "state5",
 			Version: v1,
 			Version: v1,
-			Flags:   protocol.FlagDirectory,
+			Type:    protocol.FileInfoTypeDirectory,
 			Blocks:  blocks,
 			Blocks:  blocks,
 		},
 		},
 		mut:              sync.NewRWMutex(),
 		mut:              sync.NewRWMutex(),
@@ -359,7 +358,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
 		file: protocol.FileInfo{
 		file: protocol.FileInfo{
 			Name:    "state6",
 			Name:    "state6",
 			Version: v1,
 			Version: v1,
-			Flags:   protocol.FlagSymlink,
+			Type:    protocol.FileInfoTypeSymlinkUnknown,
 		},
 		},
 		mut:              sync.NewRWMutex(),
 		mut:              sync.NewRWMutex(),
 		available:        []int32{1, 2, 3},
 		available:        []int32{1, 2, 3},

+ 7 - 7
lib/model/rwfolder.go

@@ -163,7 +163,7 @@ func (f *rwFolder) configureCopiersAndPullers(config config.FolderConfiguration)
 // set on the local host or the FlagNoPermBits has been set on the file/dir
 // set on the local host or the FlagNoPermBits has been set on the file/dir
 // which is being pulled.
 // which is being pulled.
 func (f *rwFolder) ignorePermissions(file protocol.FileInfo) bool {
 func (f *rwFolder) ignorePermissions(file protocol.FileInfo) bool {
-	return f.ignorePerms || file.Flags&protocol.FlagNoPermBits != 0
+	return f.ignorePerms || file.NoPermissions
 }
 }
 
 
 // Serve will run scans and pulls. It will return when Stop()ed or on a
 // Serve will run scans and pulls. It will return when Stop()ed or on a
@@ -435,7 +435,7 @@ func (f *rwFolder) pullerIteration(ignores *ignore.Matcher) int {
 		if !handleFile(file) {
 		if !handleFile(file) {
 			// A new or changed file or symlink. This is the only case where we
 			// A new or changed file or symlink. This is the only case where we
 			// do stuff concurrently in the background
 			// do stuff concurrently in the background
-			f.queue.Push(file.Name, file.Size(), file.Modified)
+			f.queue.Push(file.Name, file.Size, file.Modified)
 		}
 		}
 
 
 		changed++
 		changed++
@@ -569,7 +569,7 @@ func (f *rwFolder) handleDir(file protocol.FileInfo) {
 	}()
 	}()
 
 
 	realName := filepath.Join(f.dir, file.Name)
 	realName := filepath.Join(f.dir, file.Name)
-	mode := os.FileMode(file.Flags & 0777)
+	mode := os.FileMode(file.Permissions & 0777)
 	if f.ignorePermissions(file) {
 	if f.ignorePermissions(file) {
 		mode = 0777
 		mode = 0777
 	}
 	}
@@ -909,7 +909,7 @@ func (f *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocks
 		// touching the file. If we can't stat the file we'll just pull it.
 		// touching the file. If we can't stat the file we'll just pull it.
 		if info, err := osutil.Lstat(realName); err == nil {
 		if info, err := osutil.Lstat(realName); err == nil {
 			mtime := f.virtualMtimeRepo.GetMtime(file.Name, info.ModTime())
 			mtime := f.virtualMtimeRepo.GetMtime(file.Name, info.ModTime())
-			if mtime.Unix() != curFile.Modified || info.Size() != curFile.Size() {
+			if mtime.Unix() != curFile.Modified || info.Size() != curFile.Size {
 				l.Debugln("file modified but not rescanned; not pulling:", realName)
 				l.Debugln("file modified but not rescanned; not pulling:", realName)
 				// Scan() is synchronous (i.e. blocks until the scan is
 				// Scan() is synchronous (i.e. blocks until the scan is
 				// completed and returns an error), but a scan can't happen
 				// completed and returns an error), but a scan can't happen
@@ -965,7 +965,7 @@ func (f *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocks
 	} else {
 	} else {
 		// Copy the blocks, as we don't want to shuffle them on the FileInfo
 		// Copy the blocks, as we don't want to shuffle them on the FileInfo
 		blocks = append(blocks, file.Blocks...)
 		blocks = append(blocks, file.Blocks...)
-		blocksSize = file.Size()
+		blocksSize = file.Size
 	}
 	}
 
 
 	if f.checkFreeSpace {
 	if f.checkFreeSpace {
@@ -1021,7 +1021,7 @@ func (f *rwFolder) handleFile(file protocol.FileInfo, copyChan chan<- copyBlocks
 func (f *rwFolder) shortcutFile(file protocol.FileInfo) error {
 func (f *rwFolder) shortcutFile(file protocol.FileInfo) error {
 	realName := filepath.Join(f.dir, file.Name)
 	realName := filepath.Join(f.dir, file.Name)
 	if !f.ignorePermissions(file) {
 	if !f.ignorePermissions(file) {
-		if err := os.Chmod(realName, os.FileMode(file.Flags&0777)); err != nil {
+		if err := os.Chmod(realName, os.FileMode(file.Permissions&0777)); err != nil {
 			l.Infof("Puller (folder %q, file %q): shortcut: chmod: %v", f.folderID, file.Name, err)
 			l.Infof("Puller (folder %q, file %q): shortcut: chmod: %v", f.folderID, file.Name, err)
 			f.newError(file.Name, err)
 			f.newError(file.Name, err)
 			return err
 			return err
@@ -1235,7 +1235,7 @@ func (f *rwFolder) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPul
 func (f *rwFolder) performFinish(state *sharedPullerState) error {
 func (f *rwFolder) performFinish(state *sharedPullerState) error {
 	// Set the correct permission bits on the new file
 	// Set the correct permission bits on the new file
 	if !f.ignorePermissions(state.file) {
 	if !f.ignorePermissions(state.file) {
-		if err := os.Chmod(state.tempName, os.FileMode(state.file.Flags&0777)); err != nil {
+		if err := os.Chmod(state.tempName, os.FileMode(state.file.Permissions&0777)); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 2 - 4
lib/model/rwfolder_test.go

@@ -50,10 +50,8 @@ func setUpFile(filename string, blockNumbers []int) protocol.FileInfo {
 	}
 	}
 
 
 	return protocol.FileInfo{
 	return protocol.FileInfo{
-		Name:     filename,
-		Flags:    0,
-		Modified: 0,
-		Blocks:   existingBlocks,
+		Name:   filename,
+		Blocks: existingBlocks,
 	}
 	}
 }
 }
 
 

+ 10 - 4
lib/model/sharedpullerstate.go

@@ -12,7 +12,6 @@ import (
 	"path/filepath"
 	"path/filepath"
 	"time"
 	"time"
 
 
-	"github.com/syncthing/syncthing/lib/db"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/protocol"
 	"github.com/syncthing/syncthing/lib/sync"
 	"github.com/syncthing/syncthing/lib/sync"
 )
 )
@@ -150,7 +149,7 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
 	if s.sparse && !s.file.IsSymlink() {
 	if s.sparse && !s.file.IsSymlink() {
 		// Truncate sets the size of the file. This creates a sparse file or a
 		// Truncate sets the size of the file. This creates a sparse file or a
 		// space reservation, depending on the underlying filesystem.
 		// space reservation, depending on the underlying filesystem.
-		if err := fd.Truncate(s.file.Size()); err != nil {
+		if err := fd.Truncate(s.file.Size); err != nil {
 			s.failLocked("dst truncate", err)
 			s.failLocked("dst truncate", err)
 			return nil, err
 			return nil, err
 		}
 		}
@@ -293,8 +292,8 @@ func (s *sharedPullerState) Progress() *pullerProgress {
 		CopiedFromElsewhere: s.copyTotal - s.copyNeeded - s.copyOrigin,
 		CopiedFromElsewhere: s.copyTotal - s.copyNeeded - s.copyOrigin,
 		Pulled:              s.pullTotal - s.pullNeeded,
 		Pulled:              s.pullTotal - s.pullNeeded,
 		Pulling:             s.pullNeeded,
 		Pulling:             s.pullNeeded,
-		BytesTotal:          db.BlocksToSize(total),
-		BytesDone:           db.BlocksToSize(done),
+		BytesTotal:          blocksToSize(total),
+		BytesDone:           blocksToSize(done),
 	}
 	}
 }
 }
 
 
@@ -321,3 +320,10 @@ func (s *sharedPullerState) Available() []int32 {
 	s.mut.RUnlock()
 	s.mut.RUnlock()
 	return blocks
 	return blocks
 }
 }
+
+func blocksToSize(num int) int64 {
+	if num < 2 {
+		return protocol.BlockSize / 2
+	}
+	return int64(num-1)*protocol.BlockSize + protocol.BlockSize/2
+}

+ 7 - 7
lib/protocol/benchmark_test.go

@@ -63,8 +63,8 @@ func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
 	c1.Start()
 	c1.Start()
 
 
 	// Satisfy the assertions in the protocol by sending an initial cluster config
 	// Satisfy the assertions in the protocol by sending an initial cluster config
-	c0.ClusterConfig(ClusterConfigMessage{})
-	c1.ClusterConfig(ClusterConfigMessage{})
+	c0.ClusterConfig(ClusterConfig{})
+	c1.ClusterConfig(ClusterConfig{})
 
 
 	// Report some useful stats and reset the timer for the actual test
 	// Report some useful stats and reset the timer for the actual test
 	b.ReportAllocs()
 	b.ReportAllocs()
@@ -164,13 +164,13 @@ func negotiateTLS(cert tls.Certificate, conn0, conn1 net.Conn) (net.Conn, net.Co
 
 
 type fakeModel struct{}
 type fakeModel struct{}
 
 
-func (m *fakeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (m *fakeModel) Index(deviceID DeviceID, folder string, files []FileInfo) {
 }
 }
 
 
-func (m *fakeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (m *fakeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) {
 }
 }
 
 
-func (m *fakeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error {
+func (m *fakeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
 	// We write the offset to the end of the buffer, so the receiver
 	// We write the offset to the end of the buffer, so the receiver
 	// can verify that it did in fact get some data back over the
 	// can verify that it did in fact get some data back over the
 	// connection.
 	// connection.
@@ -178,11 +178,11 @@ func (m *fakeModel) Request(deviceID DeviceID, folder string, name string, offse
 	return nil
 	return nil
 }
 }
 
 
-func (m *fakeModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
+func (m *fakeModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) {
 }
 }
 
 
 func (m *fakeModel) Close(deviceID DeviceID, err error) {
 func (m *fakeModel) Close(deviceID DeviceID, err error) {
 }
 }
 
 
-func (m *fakeModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate, flags uint32, options []Option) {
+func (m *fakeModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) {
 }
 }

+ 3999 - 0
lib/protocol/bep.pb.go

@@ -0,0 +1,3999 @@
+// Code generated by protoc-gen-gogo.
+// source: bep.proto
+// DO NOT EDIT!
+
+/*
+	Package protocol is a generated protocol buffer package.
+
+	It is generated from these files:
+		bep.proto
+
+	It has these top-level messages:
+		Hello
+		Header
+		ClusterConfig
+		Folder
+		Device
+		Index
+		IndexUpdate
+		FileInfo
+		BlockInfo
+		Vector
+		Counter
+		Request
+		Response
+		DownloadProgress
+		FileDownloadProgressUpdate
+		Ping
+		Close
+*/
+package protocol
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import _ "github.com/gogo/protobuf/gogoproto"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+type MessageType int32
+
+const (
+	messageTypeClusterConfig    MessageType = 0
+	messageTypeIndex            MessageType = 1
+	messageTypeIndexUpdate      MessageType = 2
+	messageTypeRequest          MessageType = 3
+	messageTypeResponse         MessageType = 4
+	messageTypeDownloadProgress MessageType = 5
+	messageTypePing             MessageType = 6
+	messageTypeClose            MessageType = 7
+)
+
+var MessageType_name = map[int32]string{
+	0: "CLUSTER_CONFIG",
+	1: "INDEX",
+	2: "INDEX_UPDATE",
+	3: "REQUEST",
+	4: "RESPONSE",
+	5: "DOWNLOAD_PROGRESS",
+	6: "PING",
+	7: "CLOSE",
+}
+var MessageType_value = map[string]int32{
+	"CLUSTER_CONFIG":    0,
+	"INDEX":             1,
+	"INDEX_UPDATE":      2,
+	"REQUEST":           3,
+	"RESPONSE":          4,
+	"DOWNLOAD_PROGRESS": 5,
+	"PING":              6,
+	"CLOSE":             7,
+}
+
+func (x MessageType) String() string {
+	return proto.EnumName(MessageType_name, int32(x))
+}
+func (MessageType) EnumDescriptor() ([]byte, []int) { return fileDescriptorBep, []int{0} }
+
+type MessageCompression int32
+
+const (
+	MessageCompressionNone MessageCompression = 0
+	MessageCompressionLZ4  MessageCompression = 1
+)
+
+var MessageCompression_name = map[int32]string{
+	0: "NONE",
+	1: "LZ4",
+}
+var MessageCompression_value = map[string]int32{
+	"NONE": 0,
+	"LZ4":  1,
+}
+
+func (x MessageCompression) String() string {
+	return proto.EnumName(MessageCompression_name, int32(x))
+}
+func (MessageCompression) EnumDescriptor() ([]byte, []int) { return fileDescriptorBep, []int{1} }
+
+type Compression int32
+
+const (
+	CompressMetadata Compression = 0
+	CompressNever    Compression = 1
+	CompressAlways   Compression = 2
+)
+
+var Compression_name = map[int32]string{
+	0: "METADATA",
+	1: "NEVER",
+	2: "ALWAYS",
+}
+var Compression_value = map[string]int32{
+	"METADATA": 0,
+	"NEVER":    1,
+	"ALWAYS":   2,
+}
+
+func (x Compression) String() string {
+	return proto.EnumName(Compression_name, int32(x))
+}
+func (Compression) EnumDescriptor() ([]byte, []int) { return fileDescriptorBep, []int{2} }
+
+type FileInfoType int32
+
+const (
+	FileInfoTypeFile             FileInfoType = 0
+	FileInfoTypeDirectory        FileInfoType = 1
+	FileInfoTypeSymlinkFile      FileInfoType = 2
+	FileInfoTypeSymlinkDirectory FileInfoType = 3
+	FileInfoTypeSymlinkUnknown   FileInfoType = 4
+)
+
+var FileInfoType_name = map[int32]string{
+	0: "FILE",
+	1: "DIRECTORY",
+	2: "SYMLINK_FILE",
+	3: "SYMLINK_DIRECTORY",
+	4: "SYMLINK_UNKNOWN",
+}
+var FileInfoType_value = map[string]int32{
+	"FILE":              0,
+	"DIRECTORY":         1,
+	"SYMLINK_FILE":      2,
+	"SYMLINK_DIRECTORY": 3,
+	"SYMLINK_UNKNOWN":   4,
+}
+
+func (x FileInfoType) String() string {
+	return proto.EnumName(FileInfoType_name, int32(x))
+}
+func (FileInfoType) EnumDescriptor() ([]byte, []int) { return fileDescriptorBep, []int{3} }
+
+type ErrorCode int32
+
+const (
+	ErrorCodeNoError     ErrorCode = 0
+	ErrorCodeGeneric     ErrorCode = 1
+	ErrorCodeNoSuchFile  ErrorCode = 2
+	ErrorCodeInvalidFile ErrorCode = 3
+)
+
+var ErrorCode_name = map[int32]string{
+	0: "NO_ERROR",
+	1: "GENERIC",
+	2: "NO_SUCH_FILE",
+	3: "INVALID_FILE",
+}
+var ErrorCode_value = map[string]int32{
+	"NO_ERROR":     0,
+	"GENERIC":      1,
+	"NO_SUCH_FILE": 2,
+	"INVALID_FILE": 3,
+}
+
+func (x ErrorCode) String() string {
+	return proto.EnumName(ErrorCode_name, int32(x))
+}
+func (ErrorCode) EnumDescriptor() ([]byte, []int) { return fileDescriptorBep, []int{4} }
+
+type FileDownloadProgressUpdateType int32
+
+const (
+	UpdateTypeAppend FileDownloadProgressUpdateType = 0
+	UpdateTypeForget FileDownloadProgressUpdateType = 1
+)
+
+var FileDownloadProgressUpdateType_name = map[int32]string{
+	0: "APPEND",
+	1: "FORGET",
+}
+var FileDownloadProgressUpdateType_value = map[string]int32{
+	"APPEND": 0,
+	"FORGET": 1,
+}
+
+func (x FileDownloadProgressUpdateType) String() string {
+	return proto.EnumName(FileDownloadProgressUpdateType_name, int32(x))
+}
+func (FileDownloadProgressUpdateType) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptorBep, []int{5}
+}
+
+type Hello struct {
+	DeviceName    string `protobuf:"bytes,1,opt,name=device_name,json=deviceName,proto3" json:"device_name,omitempty"`
+	ClientName    string `protobuf:"bytes,2,opt,name=client_name,json=clientName,proto3" json:"client_name,omitempty"`
+	ClientVersion string `protobuf:"bytes,3,opt,name=client_version,json=clientVersion,proto3" json:"client_version,omitempty"`
+}
+
+func (m *Hello) Reset()                    { *m = Hello{} }
+func (m *Hello) String() string            { return proto.CompactTextString(m) }
+func (*Hello) ProtoMessage()               {}
+func (*Hello) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{0} }
+
+type Header struct {
+	Type        MessageType        `protobuf:"varint,1,opt,name=type,proto3,enum=protocol.MessageType" json:"type,omitempty"`
+	Compression MessageCompression `protobuf:"varint,2,opt,name=compression,proto3,enum=protocol.MessageCompression" json:"compression,omitempty"`
+}
+
+func (m *Header) Reset()                    { *m = Header{} }
+func (m *Header) String() string            { return proto.CompactTextString(m) }
+func (*Header) ProtoMessage()               {}
+func (*Header) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{1} }
+
+type ClusterConfig struct {
+	Folders []Folder `protobuf:"bytes,1,rep,name=folders" json:"folders"`
+}
+
+func (m *ClusterConfig) Reset()                    { *m = ClusterConfig{} }
+func (m *ClusterConfig) String() string            { return proto.CompactTextString(m) }
+func (*ClusterConfig) ProtoMessage()               {}
+func (*ClusterConfig) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{2} }
+
+type Folder struct {
+	ID                 string   `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	Label              string   `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"`
+	ReadOnly           bool     `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+	IgnorePermissions  bool     `protobuf:"varint,4,opt,name=ignore_permissions,json=ignorePermissions,proto3" json:"ignore_permissions,omitempty"`
+	IgnoreDelete       bool     `protobuf:"varint,5,opt,name=ignore_delete,json=ignoreDelete,proto3" json:"ignore_delete,omitempty"`
+	DisableTempIndexes bool     `protobuf:"varint,6,opt,name=disable_temp_indexes,json=disableTempIndexes,proto3" json:"disable_temp_indexes,omitempty"`
+	Devices            []Device `protobuf:"bytes,16,rep,name=devices" json:"devices"`
+}
+
+func (m *Folder) Reset()                    { *m = Folder{} }
+func (m *Folder) String() string            { return proto.CompactTextString(m) }
+func (*Folder) ProtoMessage()               {}
+func (*Folder) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{3} }
+
+type Device struct {
+	ID              []byte      `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+	Name            string      `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Addresses       []string    `protobuf:"bytes,3,rep,name=addresses" json:"addresses,omitempty"`
+	Compression     Compression `protobuf:"varint,4,opt,name=compression,proto3,enum=protocol.Compression" json:"compression,omitempty"`
+	CertName        string      `protobuf:"bytes,5,opt,name=cert_name,json=certName,proto3" json:"cert_name,omitempty"`
+	MaxLocalVersion int64       `protobuf:"varint,6,opt,name=max_local_version,json=maxLocalVersion,proto3" json:"max_local_version,omitempty"`
+	Introducer      bool        `protobuf:"varint,7,opt,name=introducer,proto3" json:"introducer,omitempty"`
+}
+
+func (m *Device) Reset()                    { *m = Device{} }
+func (m *Device) String() string            { return proto.CompactTextString(m) }
+func (*Device) ProtoMessage()               {}
+func (*Device) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{4} }
+
+type Index struct {
+	Folder string     `protobuf:"bytes,1,opt,name=folder,proto3" json:"folder,omitempty"`
+	Files  []FileInfo `protobuf:"bytes,2,rep,name=files" json:"files"`
+}
+
+func (m *Index) Reset()                    { *m = Index{} }
+func (m *Index) String() string            { return proto.CompactTextString(m) }
+func (*Index) ProtoMessage()               {}
+func (*Index) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{5} }
+
+type IndexUpdate struct {
+	Folder string     `protobuf:"bytes,1,opt,name=folder,proto3" json:"folder,omitempty"`
+	Files  []FileInfo `protobuf:"bytes,2,rep,name=files" json:"files"`
+}
+
+func (m *IndexUpdate) Reset()                    { *m = IndexUpdate{} }
+func (m *IndexUpdate) String() string            { return proto.CompactTextString(m) }
+func (*IndexUpdate) ProtoMessage()               {}
+func (*IndexUpdate) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{6} }
+
+type FileInfo struct {
+	Name          string       `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Type          FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=protocol.FileInfoType" json:"type,omitempty"`
+	Size          int64        `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"`
+	Permissions   uint32       `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions,omitempty"`
+	Modified      int64        `protobuf:"varint,5,opt,name=modified,proto3" json:"modified,omitempty"`
+	Deleted       bool         `protobuf:"varint,6,opt,name=deleted,proto3" json:"deleted,omitempty"`
+	Invalid       bool         `protobuf:"varint,7,opt,name=invalid,proto3" json:"invalid,omitempty"`
+	NoPermissions bool         `protobuf:"varint,8,opt,name=no_permissions,json=noPermissions,proto3" json:"no_permissions,omitempty"`
+	Version       Vector       `protobuf:"bytes,9,opt,name=version" json:"version"`
+	LocalVersion  int64        `protobuf:"varint,10,opt,name=local_version,json=localVersion,proto3" json:"local_version,omitempty"`
+	Blocks        []BlockInfo  `protobuf:"bytes,16,rep,name=Blocks,json=blocks" json:"Blocks"`
+}
+
+func (m *FileInfo) Reset()                    { *m = FileInfo{} }
+func (*FileInfo) ProtoMessage()               {}
+func (*FileInfo) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{7} }
+
+type BlockInfo struct {
+	Offset int64  `protobuf:"varint,1,opt,name=offset,proto3" json:"offset,omitempty"`
+	Size   int32  `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
+	Hash   []byte `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"`
+}
+
+func (m *BlockInfo) Reset()                    { *m = BlockInfo{} }
+func (*BlockInfo) ProtoMessage()               {}
+func (*BlockInfo) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{8} }
+
+type Vector struct {
+	Counters []Counter `protobuf:"bytes,1,rep,name=counters" json:"counters"`
+}
+
+func (m *Vector) Reset()                    { *m = Vector{} }
+func (m *Vector) String() string            { return proto.CompactTextString(m) }
+func (*Vector) ProtoMessage()               {}
+func (*Vector) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{9} }
+
+type Counter struct {
+	ID    ShortID `protobuf:"varint,1,opt,name=id,proto3,customtype=ShortID" json:"id"`
+	Value uint64  `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
+}
+
+func (m *Counter) Reset()                    { *m = Counter{} }
+func (m *Counter) String() string            { return proto.CompactTextString(m) }
+func (*Counter) ProtoMessage()               {}
+func (*Counter) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{10} }
+
+type Request struct {
+	ID            int32  `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Folder        string `protobuf:"bytes,2,opt,name=folder,proto3" json:"folder,omitempty"`
+	Name          string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+	Offset        int64  `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"`
+	Size          int32  `protobuf:"varint,5,opt,name=size,proto3" json:"size,omitempty"`
+	Hash          []byte `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash,omitempty"`
+	FromTemporary bool   `protobuf:"varint,7,opt,name=from_temporary,json=fromTemporary,proto3" json:"from_temporary,omitempty"`
+}
+
+func (m *Request) Reset()                    { *m = Request{} }
+func (m *Request) String() string            { return proto.CompactTextString(m) }
+func (*Request) ProtoMessage()               {}
+func (*Request) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{11} }
+
+type Response struct {
+	ID   int32     `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
+	Data []byte    `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
+	Code ErrorCode `protobuf:"varint,3,opt,name=code,proto3,enum=protocol.ErrorCode" json:"code,omitempty"`
+}
+
+func (m *Response) Reset()                    { *m = Response{} }
+func (m *Response) String() string            { return proto.CompactTextString(m) }
+func (*Response) ProtoMessage()               {}
+func (*Response) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{12} }
+
+type DownloadProgress struct {
+	Folder  string                       `protobuf:"bytes,1,opt,name=folder,proto3" json:"folder,omitempty"`
+	Updates []FileDownloadProgressUpdate `protobuf:"bytes,2,rep,name=updates" json:"updates"`
+}
+
+func (m *DownloadProgress) Reset()                    { *m = DownloadProgress{} }
+func (m *DownloadProgress) String() string            { return proto.CompactTextString(m) }
+func (*DownloadProgress) ProtoMessage()               {}
+func (*DownloadProgress) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{13} }
+
+type FileDownloadProgressUpdate struct {
+	UpdateType   FileDownloadProgressUpdateType `protobuf:"varint,1,opt,name=update_type,json=updateType,proto3,enum=protocol.FileDownloadProgressUpdateType" json:"update_type,omitempty"`
+	Name         string                         `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
+	Version      Vector                         `protobuf:"bytes,3,opt,name=version" json:"version"`
+	BlockIndexes []int32                        `protobuf:"varint,4,rep,name=block_indexes,json=blockIndexes" json:"block_indexes,omitempty"`
+}
+
+func (m *FileDownloadProgressUpdate) Reset()                    { *m = FileDownloadProgressUpdate{} }
+func (m *FileDownloadProgressUpdate) String() string            { return proto.CompactTextString(m) }
+func (*FileDownloadProgressUpdate) ProtoMessage()               {}
+func (*FileDownloadProgressUpdate) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{14} }
+
+type Ping struct {
+}
+
+func (m *Ping) Reset()                    { *m = Ping{} }
+func (m *Ping) String() string            { return proto.CompactTextString(m) }
+func (*Ping) ProtoMessage()               {}
+func (*Ping) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{15} }
+
+type Close struct {
+	Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"`
+}
+
+func (m *Close) Reset()                    { *m = Close{} }
+func (m *Close) String() string            { return proto.CompactTextString(m) }
+func (*Close) ProtoMessage()               {}
+func (*Close) Descriptor() ([]byte, []int) { return fileDescriptorBep, []int{16} }
+
+func init() {
+	proto.RegisterType((*Hello)(nil), "protocol.Hello")
+	proto.RegisterType((*Header)(nil), "protocol.Header")
+	proto.RegisterType((*ClusterConfig)(nil), "protocol.ClusterConfig")
+	proto.RegisterType((*Folder)(nil), "protocol.Folder")
+	proto.RegisterType((*Device)(nil), "protocol.Device")
+	proto.RegisterType((*Index)(nil), "protocol.Index")
+	proto.RegisterType((*IndexUpdate)(nil), "protocol.IndexUpdate")
+	proto.RegisterType((*FileInfo)(nil), "protocol.FileInfo")
+	proto.RegisterType((*BlockInfo)(nil), "protocol.BlockInfo")
+	proto.RegisterType((*Vector)(nil), "protocol.Vector")
+	proto.RegisterType((*Counter)(nil), "protocol.Counter")
+	proto.RegisterType((*Request)(nil), "protocol.Request")
+	proto.RegisterType((*Response)(nil), "protocol.Response")
+	proto.RegisterType((*DownloadProgress)(nil), "protocol.DownloadProgress")
+	proto.RegisterType((*FileDownloadProgressUpdate)(nil), "protocol.FileDownloadProgressUpdate")
+	proto.RegisterType((*Ping)(nil), "protocol.Ping")
+	proto.RegisterType((*Close)(nil), "protocol.Close")
+	proto.RegisterEnum("protocol.MessageType", MessageType_name, MessageType_value)
+	proto.RegisterEnum("protocol.MessageCompression", MessageCompression_name, MessageCompression_value)
+	proto.RegisterEnum("protocol.Compression", Compression_name, Compression_value)
+	proto.RegisterEnum("protocol.FileInfoType", FileInfoType_name, FileInfoType_value)
+	proto.RegisterEnum("protocol.ErrorCode", ErrorCode_name, ErrorCode_value)
+	proto.RegisterEnum("protocol.FileDownloadProgressUpdateType", FileDownloadProgressUpdateType_name, FileDownloadProgressUpdateType_value)
+}
+func (m *Hello) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Hello) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.DeviceName) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.DeviceName)))
+		i += copy(data[i:], m.DeviceName)
+	}
+	if len(m.ClientName) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.ClientName)))
+		i += copy(data[i:], m.ClientName)
+	}
+	if len(m.ClientVersion) > 0 {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.ClientVersion)))
+		i += copy(data[i:], m.ClientVersion)
+	}
+	return i, nil
+}
+
+func (m *Header) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Header) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Type != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Type))
+	}
+	if m.Compression != 0 {
+		data[i] = 0x10
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Compression))
+	}
+	return i, nil
+}
+
+func (m *ClusterConfig) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *ClusterConfig) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Folders) > 0 {
+		for _, msg := range m.Folders {
+			data[i] = 0xa
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *Folder) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Folder) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.ID) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.ID)))
+		i += copy(data[i:], m.ID)
+	}
+	if len(m.Label) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Label)))
+		i += copy(data[i:], m.Label)
+	}
+	if m.ReadOnly {
+		data[i] = 0x18
+		i++
+		if m.ReadOnly {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.IgnorePermissions {
+		data[i] = 0x20
+		i++
+		if m.IgnorePermissions {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.IgnoreDelete {
+		data[i] = 0x28
+		i++
+		if m.IgnoreDelete {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.DisableTempIndexes {
+		data[i] = 0x30
+		i++
+		if m.DisableTempIndexes {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if len(m.Devices) > 0 {
+		for _, msg := range m.Devices {
+			data[i] = 0x82
+			i++
+			data[i] = 0x1
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *Device) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Device) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.ID) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.ID)))
+		i += copy(data[i:], m.ID)
+	}
+	if len(m.Name) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
+	if len(m.Addresses) > 0 {
+		for _, s := range m.Addresses {
+			data[i] = 0x1a
+			i++
+			l = len(s)
+			for l >= 1<<7 {
+				data[i] = uint8(uint64(l)&0x7f | 0x80)
+				l >>= 7
+				i++
+			}
+			data[i] = uint8(l)
+			i++
+			i += copy(data[i:], s)
+		}
+	}
+	if m.Compression != 0 {
+		data[i] = 0x20
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Compression))
+	}
+	if len(m.CertName) > 0 {
+		data[i] = 0x2a
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.CertName)))
+		i += copy(data[i:], m.CertName)
+	}
+	if m.MaxLocalVersion != 0 {
+		data[i] = 0x30
+		i++
+		i = encodeVarintBep(data, i, uint64(m.MaxLocalVersion))
+	}
+	if m.Introducer {
+		data[i] = 0x38
+		i++
+		if m.Introducer {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	return i, nil
+}
+
+func (m *Index) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Index) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Folder) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Folder)))
+		i += copy(data[i:], m.Folder)
+	}
+	if len(m.Files) > 0 {
+		for _, msg := range m.Files {
+			data[i] = 0x12
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *IndexUpdate) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *IndexUpdate) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Folder) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Folder)))
+		i += copy(data[i:], m.Folder)
+	}
+	if len(m.Files) > 0 {
+		for _, msg := range m.Files {
+			data[i] = 0x12
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *FileInfo) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *FileInfo) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Name) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
+	if m.Type != 0 {
+		data[i] = 0x10
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Type))
+	}
+	if m.Size != 0 {
+		data[i] = 0x18
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Size))
+	}
+	if m.Permissions != 0 {
+		data[i] = 0x20
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Permissions))
+	}
+	if m.Modified != 0 {
+		data[i] = 0x28
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Modified))
+	}
+	if m.Deleted {
+		data[i] = 0x30
+		i++
+		if m.Deleted {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.Invalid {
+		data[i] = 0x38
+		i++
+		if m.Invalid {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	if m.NoPermissions {
+		data[i] = 0x40
+		i++
+		if m.NoPermissions {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	data[i] = 0x4a
+	i++
+	i = encodeVarintBep(data, i, uint64(m.Version.ProtoSize()))
+	n1, err := m.Version.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
+	}
+	i += n1
+	if m.LocalVersion != 0 {
+		data[i] = 0x50
+		i++
+		i = encodeVarintBep(data, i, uint64(m.LocalVersion))
+	}
+	if len(m.Blocks) > 0 {
+		for _, msg := range m.Blocks {
+			data[i] = 0x82
+			i++
+			data[i] = 0x1
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *BlockInfo) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *BlockInfo) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.Offset != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Offset))
+	}
+	if m.Size != 0 {
+		data[i] = 0x10
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Size))
+	}
+	if len(m.Hash) > 0 {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Hash)))
+		i += copy(data[i:], m.Hash)
+	}
+	return i, nil
+}
+
+func (m *Vector) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Vector) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Counters) > 0 {
+		for _, msg := range m.Counters {
+			data[i] = 0xa
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *Counter) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Counter) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.ID != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintBep(data, i, uint64(m.ID))
+	}
+	if m.Value != 0 {
+		data[i] = 0x10
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Value))
+	}
+	return i, nil
+}
+
+func (m *Request) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	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
+	if m.ID != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintBep(data, i, uint64(m.ID))
+	}
+	if len(m.Folder) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Folder)))
+		i += copy(data[i:], m.Folder)
+	}
+	if len(m.Name) > 0 {
+		data[i] = 0x1a
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
+	if m.Offset != 0 {
+		data[i] = 0x20
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Offset))
+	}
+	if m.Size != 0 {
+		data[i] = 0x28
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Size))
+	}
+	if len(m.Hash) > 0 {
+		data[i] = 0x32
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Hash)))
+		i += copy(data[i:], m.Hash)
+	}
+	if m.FromTemporary {
+		data[i] = 0x38
+		i++
+		if m.FromTemporary {
+			data[i] = 1
+		} else {
+			data[i] = 0
+		}
+		i++
+	}
+	return i, nil
+}
+
+func (m *Response) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Response) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.ID != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintBep(data, i, uint64(m.ID))
+	}
+	if len(m.Data) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Data)))
+		i += copy(data[i:], m.Data)
+	}
+	if m.Code != 0 {
+		data[i] = 0x18
+		i++
+		i = encodeVarintBep(data, i, uint64(m.Code))
+	}
+	return i, nil
+}
+
+func (m *DownloadProgress) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *DownloadProgress) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Folder) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Folder)))
+		i += copy(data[i:], m.Folder)
+	}
+	if len(m.Updates) > 0 {
+		for _, msg := range m.Updates {
+			data[i] = 0x12
+			i++
+			i = encodeVarintBep(data, i, uint64(msg.ProtoSize()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	return i, nil
+}
+
+func (m *FileDownloadProgressUpdate) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *FileDownloadProgressUpdate) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if m.UpdateType != 0 {
+		data[i] = 0x8
+		i++
+		i = encodeVarintBep(data, i, uint64(m.UpdateType))
+	}
+	if len(m.Name) > 0 {
+		data[i] = 0x12
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Name)))
+		i += copy(data[i:], m.Name)
+	}
+	data[i] = 0x1a
+	i++
+	i = encodeVarintBep(data, i, uint64(m.Version.ProtoSize()))
+	n2, err := m.Version.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
+	}
+	i += n2
+	if len(m.BlockIndexes) > 0 {
+		for _, num := range m.BlockIndexes {
+			data[i] = 0x20
+			i++
+			i = encodeVarintBep(data, i, uint64(num))
+		}
+	}
+	return i, nil
+}
+
+func (m *Ping) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Ping) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	return i, nil
+}
+
+func (m *Close) Marshal() (data []byte, err error) {
+	size := m.ProtoSize()
+	data = make([]byte, size)
+	n, err := m.MarshalTo(data)
+	if err != nil {
+		return nil, err
+	}
+	return data[:n], nil
+}
+
+func (m *Close) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	if len(m.Reason) > 0 {
+		data[i] = 0xa
+		i++
+		i = encodeVarintBep(data, i, uint64(len(m.Reason)))
+		i += copy(data[i:], m.Reason)
+	}
+	return i, nil
+}
+
+func encodeFixed64Bep(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 encodeFixed32Bep(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 encodeVarintBep(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 *Hello) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.DeviceName)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	l = len(m.ClientName)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	l = len(m.ClientVersion)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	return n
+}
+
+func (m *Header) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if m.Type != 0 {
+		n += 1 + sovBep(uint64(m.Type))
+	}
+	if m.Compression != 0 {
+		n += 1 + sovBep(uint64(m.Compression))
+	}
+	return n
+}
+
+func (m *ClusterConfig) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if len(m.Folders) > 0 {
+		for _, e := range m.Folders {
+			l = e.ProtoSize()
+			n += 1 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *Folder) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.ID)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	l = len(m.Label)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if m.ReadOnly {
+		n += 2
+	}
+	if m.IgnorePermissions {
+		n += 2
+	}
+	if m.IgnoreDelete {
+		n += 2
+	}
+	if m.DisableTempIndexes {
+		n += 2
+	}
+	if len(m.Devices) > 0 {
+		for _, e := range m.Devices {
+			l = e.ProtoSize()
+			n += 2 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *Device) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.ID)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if len(m.Addresses) > 0 {
+		for _, s := range m.Addresses {
+			l = len(s)
+			n += 1 + l + sovBep(uint64(l))
+		}
+	}
+	if m.Compression != 0 {
+		n += 1 + sovBep(uint64(m.Compression))
+	}
+	l = len(m.CertName)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if m.MaxLocalVersion != 0 {
+		n += 1 + sovBep(uint64(m.MaxLocalVersion))
+	}
+	if m.Introducer {
+		n += 2
+	}
+	return n
+}
+
+func (m *Index) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Folder)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if len(m.Files) > 0 {
+		for _, e := range m.Files {
+			l = e.ProtoSize()
+			n += 1 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *IndexUpdate) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Folder)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if len(m.Files) > 0 {
+		for _, e := range m.Files {
+			l = e.ProtoSize()
+			n += 1 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *FileInfo) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if m.Type != 0 {
+		n += 1 + sovBep(uint64(m.Type))
+	}
+	if m.Size != 0 {
+		n += 1 + sovBep(uint64(m.Size))
+	}
+	if m.Permissions != 0 {
+		n += 1 + sovBep(uint64(m.Permissions))
+	}
+	if m.Modified != 0 {
+		n += 1 + sovBep(uint64(m.Modified))
+	}
+	if m.Deleted {
+		n += 2
+	}
+	if m.Invalid {
+		n += 2
+	}
+	if m.NoPermissions {
+		n += 2
+	}
+	l = m.Version.ProtoSize()
+	n += 1 + l + sovBep(uint64(l))
+	if m.LocalVersion != 0 {
+		n += 1 + sovBep(uint64(m.LocalVersion))
+	}
+	if len(m.Blocks) > 0 {
+		for _, e := range m.Blocks {
+			l = e.ProtoSize()
+			n += 2 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *BlockInfo) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if m.Offset != 0 {
+		n += 1 + sovBep(uint64(m.Offset))
+	}
+	if m.Size != 0 {
+		n += 1 + sovBep(uint64(m.Size))
+	}
+	l = len(m.Hash)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	return n
+}
+
+func (m *Vector) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if len(m.Counters) > 0 {
+		for _, e := range m.Counters {
+			l = e.ProtoSize()
+			n += 1 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *Counter) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if m.ID != 0 {
+		n += 1 + sovBep(uint64(m.ID))
+	}
+	if m.Value != 0 {
+		n += 1 + sovBep(uint64(m.Value))
+	}
+	return n
+}
+
+func (m *Request) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if m.ID != 0 {
+		n += 1 + sovBep(uint64(m.ID))
+	}
+	l = len(m.Folder)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if m.Offset != 0 {
+		n += 1 + sovBep(uint64(m.Offset))
+	}
+	if m.Size != 0 {
+		n += 1 + sovBep(uint64(m.Size))
+	}
+	l = len(m.Hash)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if m.FromTemporary {
+		n += 2
+	}
+	return n
+}
+
+func (m *Response) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if m.ID != 0 {
+		n += 1 + sovBep(uint64(m.ID))
+	}
+	l = len(m.Data)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if m.Code != 0 {
+		n += 1 + sovBep(uint64(m.Code))
+	}
+	return n
+}
+
+func (m *DownloadProgress) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Folder)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	if len(m.Updates) > 0 {
+		for _, e := range m.Updates {
+			l = e.ProtoSize()
+			n += 1 + l + sovBep(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *FileDownloadProgressUpdate) ProtoSize() (n int) {
+	var l int
+	_ = l
+	if m.UpdateType != 0 {
+		n += 1 + sovBep(uint64(m.UpdateType))
+	}
+	l = len(m.Name)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	l = m.Version.ProtoSize()
+	n += 1 + l + sovBep(uint64(l))
+	if len(m.BlockIndexes) > 0 {
+		for _, e := range m.BlockIndexes {
+			n += 1 + sovBep(uint64(e))
+		}
+	}
+	return n
+}
+
+func (m *Ping) ProtoSize() (n int) {
+	var l int
+	_ = l
+	return n
+}
+
+func (m *Close) ProtoSize() (n int) {
+	var l int
+	_ = l
+	l = len(m.Reason)
+	if l > 0 {
+		n += 1 + l + sovBep(uint64(l))
+	}
+	return n
+}
+
+func sovBep(x uint64) (n int) {
+	for {
+		n++
+		x >>= 7
+		if x == 0 {
+			break
+		}
+	}
+	return n
+}
+func sozBep(x uint64) (n int) {
+	return sovBep(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Hello) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Hello: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Hello: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DeviceName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DeviceName = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ClientName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ClientName = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ClientVersion", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ClientVersion = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Header) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Header: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Header: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		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 shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 Compression", wireType)
+			}
+			m.Compression = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Compression |= (MessageCompression(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *ClusterConfig) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: ClusterConfig: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: ClusterConfig: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Folders", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Folders = append(m.Folders, Folder{})
+			if err := m.Folders[len(m.Folders)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Folder) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Folder: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Folder: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ID = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Label = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ReadOnly", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.ReadOnly = bool(v != 0)
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field IgnorePermissions", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.IgnorePermissions = bool(v != 0)
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field IgnoreDelete", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.IgnoreDelete = bool(v != 0)
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DisableTempIndexes", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.DisableTempIndexes = bool(v != 0)
+		case 16:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Devices = append(m.Devices, Device{})
+			if err := m.Devices[len(m.Devices)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Device) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Device: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Device: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.ID = append(m.ID[:0], data[iNdEx:postIndex]...)
+			if m.ID == nil {
+				m.ID = []byte{}
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Addresses = append(m.Addresses, string(data[iNdEx:postIndex]))
+			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Compression", wireType)
+			}
+			m.Compression = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Compression |= (Compression(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field CertName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.CertName = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MaxLocalVersion", wireType)
+			}
+			m.MaxLocalVersion = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.MaxLocalVersion |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 7:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Introducer", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Introducer = bool(v != 0)
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Index) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Index: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Index: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Folder", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Folder = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Files = append(m.Files, FileInfo{})
+			if err := m.Files[len(m.Files)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *IndexUpdate) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: IndexUpdate: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: IndexUpdate: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Folder", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Folder = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Files = append(m.Files, FileInfo{})
+			if err := m.Files[len(m.Files)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *FileInfo) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: FileInfo: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: FileInfo: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		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 shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Type |= (FileInfoType(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType)
+			}
+			m.Size = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Size |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Permissions", wireType)
+			}
+			m.Permissions = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Permissions |= (uint32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Modified", wireType)
+			}
+			m.Modified = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Modified |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Deleted", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Deleted = bool(v != 0)
+		case 7:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Invalid", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.Invalid = bool(v != 0)
+		case 8:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field NoPermissions", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.NoPermissions = bool(v != 0)
+		case 9:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Version.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 10:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field LocalVersion", wireType)
+			}
+			m.LocalVersion = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.LocalVersion |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 16:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Blocks", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Blocks = append(m.Blocks, BlockInfo{})
+			if err := m.Blocks[len(m.Blocks)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *BlockInfo) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: BlockInfo: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: BlockInfo: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType)
+			}
+			m.Offset = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Offset |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType)
+			}
+			m.Size = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Size |= (int32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Hash = append(m.Hash[:0], data[iNdEx:postIndex]...)
+			if m.Hash == nil {
+				m.Hash = []byte{}
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Vector) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Vector: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Vector: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Counters", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Counters = append(m.Counters, Counter{})
+			if err := m.Counters[len(m.Counters)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Counter) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Counter: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Counter: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		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 shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.ID |= (ShortID(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
+			}
+			m.Value = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Value |= (uint64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Request) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Request: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Request: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		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 shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.ID |= (int32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Folder", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Folder = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType)
+			}
+			m.Offset = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Offset |= (int64(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 5:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType)
+			}
+			m.Size = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Size |= (int32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 6:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
+			}
+			var byteLen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Hash = append(m.Hash[:0], data[iNdEx:postIndex]...)
+			if m.Hash == nil {
+				m.Hash = []byte{}
+			}
+			iNdEx = postIndex
+		case 7:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FromTemporary", wireType)
+			}
+			var v int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.FromTemporary = bool(v != 0)
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Response) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Response: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Response: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		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 shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.ID |= (int32(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 shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				byteLen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if byteLen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + byteLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Data = append(m.Data[:0], data[iNdEx:postIndex]...)
+			if m.Data == nil {
+				m.Data = []byte{}
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType)
+			}
+			m.Code = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.Code |= (ErrorCode(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *DownloadProgress) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: DownloadProgress: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: DownloadProgress: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Folder", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Folder = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Updates", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Updates = append(m.Updates, FileDownloadProgressUpdate{})
+			if err := m.Updates[len(m.Updates)-1].Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *FileDownloadProgressUpdate) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: FileDownloadProgressUpdate: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: FileDownloadProgressUpdate: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field UpdateType", wireType)
+			}
+			m.UpdateType = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				m.UpdateType |= (FileDownloadProgressUpdateType(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Name = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				msglen |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + msglen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Version.Unmarshal(data[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 4:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BlockIndexes", wireType)
+			}
+			var v int32
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := data[iNdEx]
+				iNdEx++
+				v |= (int32(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			m.BlockIndexes = append(m.BlockIndexes, v)
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Ping) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Ping: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Ping: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *Close) Unmarshal(data []byte) error {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowBep
+			}
+			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)
+		if wireType == 4 {
+			return fmt.Errorf("proto: Close: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Close: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowBep
+				}
+				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 ErrInvalidLengthBep
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Reason = string(data[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipBep(data[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if skippy < 0 {
+				return ErrInvalidLengthBep
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipBep(data []byte) (n int, err error) {
+	l := len(data)
+	iNdEx := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowBep
+			}
+			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 shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowBep
+				}
+				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 shift >= 64 {
+					return 0, ErrIntOverflowBep
+				}
+				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, ErrInvalidLengthBep
+			}
+			return iNdEx, nil
+		case 3:
+			for {
+				var innerWire uint64
+				var start int = iNdEx
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return 0, ErrIntOverflowBep
+					}
+					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 := skipBep(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 (
+	ErrInvalidLengthBep = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowBep   = fmt.Errorf("proto: integer overflow")
+)
+
+var fileDescriptorBep = []byte{
+	// 1543 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x56, 0x41, 0x6f, 0x1a, 0xc7,
+	0x17, 0x37, 0xb0, 0x2c, 0x30, 0xc6, 0x0e, 0x9e, 0x38, 0x0e, 0xff, 0x8d, 0xff, 0xb6, 0xbb, 0x49,
+	0x54, 0x17, 0x35, 0x4e, 0x9b, 0x54, 0x8d, 0x54, 0xa9, 0x95, 0x30, 0xac, 0x1d, 0x14, 0xbc, 0x90,
+	0x05, 0x9c, 0xa6, 0x87, 0xa2, 0x85, 0x1d, 0xf0, 0x2a, 0xcb, 0x0e, 0xdd, 0x5d, 0x92, 0xb8, 0x5f,
+	0xa0, 0x52, 0xfb, 0x05, 0x7a, 0xa9, 0x94, 0x6b, 0xef, 0xfd, 0x10, 0x39, 0x46, 0x39, 0xf6, 0x10,
+	0xb5, 0xe9, 0xa5, 0x1f, 0xa0, 0xbd, 0xf7, 0xcd, 0xcc, 0x2e, 0x2c, 0xc6, 0xae, 0x72, 0xe8, 0x01,
+	0x31, 0xf3, 0xde, 0x6f, 0xde, 0xcc, 0xfc, 0xde, 0xef, 0xbd, 0x59, 0x94, 0xeb, 0x91, 0xf1, 0xde,
+	0xd8, 0xa3, 0x01, 0xc5, 0x59, 0xfe, 0xd7, 0xa7, 0x8e, 0x72, 0x6b, 0x68, 0x07, 0x27, 0x93, 0xde,
+	0x5e, 0x9f, 0x8e, 0x6e, 0x0f, 0xe9, 0x90, 0xde, 0xe6, 0x9e, 0xde, 0x64, 0xc0, 0x67, 0x7c, 0xc2,
+	0x47, 0x62, 0xa1, 0x3a, 0x46, 0xe9, 0xfb, 0xc4, 0x71, 0x28, 0xde, 0x46, 0xcb, 0x16, 0x79, 0x6a,
+	0xf7, 0x49, 0xd7, 0x35, 0x47, 0xa4, 0x98, 0xd8, 0x49, 0xec, 0xe6, 0x0c, 0x24, 0x4c, 0x3a, 0x58,
+	0x18, 0xa0, 0xef, 0xd8, 0xc4, 0x0d, 0x04, 0x20, 0x29, 0x00, 0xc2, 0xc4, 0x01, 0x37, 0xd1, 0x6a,
+	0x08, 0x78, 0x4a, 0x3c, 0xdf, 0xa6, 0x6e, 0x31, 0xc5, 0x31, 0x2b, 0xc2, 0x7a, 0x2c, 0x8c, 0xaa,
+	0x8f, 0xe4, 0xfb, 0xc4, 0xb4, 0x88, 0x87, 0x3f, 0x40, 0x52, 0x70, 0x3a, 0x16, 0x7b, 0xad, 0xde,
+	0xb9, 0xb2, 0x17, 0xdd, 0x61, 0xef, 0x88, 0xf8, 0xbe, 0x39, 0x24, 0x6d, 0x70, 0x1a, 0x1c, 0x82,
+	0xbf, 0x80, 0xcd, 0xe9, 0x68, 0xec, 0x81, 0x83, 0x05, 0x4e, 0xf2, 0x15, 0x9b, 0x0b, 0x2b, 0x2a,
+	0x33, 0x8c, 0x11, 0x5f, 0xa0, 0x96, 0xd1, 0x4a, 0xc5, 0x99, 0xf8, 0x01, 0xf1, 0x2a, 0xd4, 0x1d,
+	0xd8, 0x43, 0xfc, 0x11, 0xca, 0x0c, 0xa8, 0x03, 0xa7, 0xf0, 0x61, 0xfb, 0xd4, 0xee, 0xf2, 0x9d,
+	0xc2, 0x2c, 0xd8, 0x01, 0x77, 0xec, 0x4b, 0x2f, 0xdf, 0x6c, 0x2f, 0x19, 0x11, 0x4c, 0xfd, 0x21,
+	0x89, 0x64, 0xe1, 0xc1, 0x1b, 0x28, 0x69, 0x5b, 0x82, 0xa2, 0x7d, 0xf9, 0xed, 0x9b, 0xed, 0x64,
+	0xad, 0x6a, 0x80, 0x05, 0xaf, 0xa3, 0xb4, 0x63, 0xf6, 0x88, 0x13, 0x92, 0x23, 0x26, 0xf8, 0x1a,
+	0xca, 0x79, 0x70, 0xe1, 0x2e, 0x75, 0x9d, 0x53, 0x4e, 0x49, 0xd6, 0xc8, 0x32, 0x43, 0x03, 0xe6,
+	0xf8, 0x16, 0xc2, 0xf6, 0xd0, 0xa5, 0x1e, 0xe9, 0x8e, 0x89, 0x37, 0xb2, 0xf9, 0x69, 0xfd, 0xa2,
+	0xc4, 0x51, 0x6b, 0xc2, 0xd3, 0x9c, 0x39, 0xf0, 0x75, 0xb4, 0x12, 0xc2, 0x2d, 0xe2, 0x90, 0x80,
+	0x14, 0xd3, 0x1c, 0x99, 0x17, 0xc6, 0x2a, 0xb7, 0xc1, 0xdd, 0xd6, 0x2d, 0xdb, 0x37, 0x7b, 0x0e,
+	0xe9, 0x06, 0x64, 0x34, 0xee, 0xda, 0xae, 0x45, 0x9e, 0x13, 0xbf, 0x28, 0x73, 0x2c, 0x0e, 0x7d,
+	0x6d, 0x70, 0xd5, 0x84, 0x87, 0xb1, 0x21, 0x32, 0xed, 0x17, 0x0b, 0x67, 0xd9, 0xa8, 0x72, 0x47,
+	0xc4, 0x46, 0x08, 0x53, 0xff, 0x4a, 0x20, 0x59, 0x78, 0x62, 0x6c, 0xe4, 0xe7, 0xd8, 0xc0, 0x48,
+	0x8a, 0x29, 0x85, 0x8f, 0xf1, 0x26, 0xca, 0x99, 0x96, 0xc5, 0xb2, 0x02, 0x5b, 0xa5, 0x60, 0xab,
+	0x9c, 0x31, 0x33, 0xe0, 0x7b, 0xf3, 0x59, 0x96, 0xce, 0xea, 0xe2, 0xa2, 0xf4, 0x32, 0x8a, 0xfb,
+	0xc4, 0x0b, 0x95, 0x99, 0xe6, 0xfb, 0x65, 0x99, 0x81, 0xeb, 0xb2, 0x84, 0xd6, 0x46, 0xe6, 0xf3,
+	0xae, 0x43, 0xfb, 0xa6, 0x33, 0x95, 0x26, 0xe3, 0x22, 0x65, 0x5c, 0x02, 0x47, 0x9d, 0xd9, 0x43,
+	0x71, 0xe2, 0x2d, 0x84, 0x6c, 0x37, 0xf0, 0xa8, 0x35, 0x81, 0xe5, 0xc5, 0x0c, 0x27, 0x2c, 0x66,
+	0x51, 0x1b, 0x28, 0xcd, 0x39, 0x83, 0x4b, 0xcb, 0x42, 0x18, 0x61, 0xa5, 0x84, 0x33, 0xbc, 0x87,
+	0xd2, 0x03, 0xdb, 0x81, 0xcb, 0x25, 0x39, 0x8f, 0x38, 0xa6, 0x2a, 0x30, 0xd7, 0xdc, 0x01, 0x0d,
+	0x99, 0x14, 0x30, 0xb5, 0x83, 0x96, 0x79, 0xc0, 0xce, 0xd8, 0x32, 0x03, 0xf2, 0x9f, 0x85, 0xfd,
+	0x2e, 0x85, 0xb2, 0x91, 0x67, 0x9a, 0x88, 0x44, 0x2c, 0x11, 0xa5, 0xb0, 0xf6, 0x44, 0x25, 0x6d,
+	0x2c, 0xc6, 0x8b, 0x15, 0x1f, 0xac, 0xf7, 0xed, 0x6f, 0x09, 0xd7, 0x6e, 0xca, 0xe0, 0x63, 0xbc,
+	0x83, 0x96, 0xcf, 0x0a, 0x76, 0xc5, 0x88, 0x9b, 0xb0, 0x82, 0xb2, 0x23, 0x6a, 0xd9, 0x03, 0x9b,
+	0x58, 0x3c, 0x25, 0x29, 0x63, 0x3a, 0xc7, 0x45, 0xa6, 0x37, 0xa6, 0x55, 0x2b, 0x14, 0x65, 0x34,
+	0x65, 0x1e, 0xdb, 0x7d, 0x6a, 0x3a, 0xa0, 0x28, 0xc1, 0x7e, 0x34, 0x65, 0xed, 0xc5, 0xa5, 0x73,
+	0x55, 0x92, 0xe5, 0x80, 0x15, 0x97, 0xc6, 0x2b, 0x04, 0xa4, 0x1c, 0xe5, 0x38, 0x07, 0xfe, 0x39,
+	0x29, 0x1f, 0x93, 0x7e, 0x40, 0xa7, 0x85, 0x1d, 0xc2, 0x58, 0x4d, 0xcd, 0x6b, 0x03, 0xf1, 0xd3,
+	0xe6, 0x9d, 0xb8, 0x30, 0x3e, 0x46, 0xf2, 0x3e, 0x18, 0x9e, 0x44, 0x05, 0x72, 0x79, 0x16, 0x95,
+	0xdb, 0x63, 0x29, 0x90, 0x7b, 0x1c, 0xf8, 0x99, 0xf4, 0xe3, 0x8b, 0xed, 0x25, 0xf5, 0x21, 0xca,
+	0x4d, 0x01, 0x2c, 0xbd, 0x74, 0x30, 0xf0, 0x49, 0xc0, 0x73, 0x91, 0x32, 0xc2, 0xd9, 0x94, 0x61,
+	0x96, 0x8d, 0x74, 0xc8, 0x30, 0xd8, 0x4e, 0x4c, 0xff, 0x84, 0xb3, 0x9e, 0x37, 0xf8, 0x38, 0x0c,
+	0xf9, 0x39, 0x92, 0xc5, 0x4d, 0xf0, 0x5d, 0x94, 0xed, 0xd3, 0x89, 0x1b, 0xcc, 0xda, 0xd8, 0x5a,
+	0xbc, 0x5a, 0xb8, 0x27, 0x3c, 0xd5, 0x14, 0xa8, 0x1e, 0xa0, 0x4c, 0xe8, 0x02, 0x4e, 0xa3, 0xd2,
+	0x95, 0xf6, 0xaf, 0x30, 0xd8, 0xaf, 0x6f, 0xb6, 0x33, 0xad, 0x13, 0xea, 0x05, 0xb5, 0xea, 0x7c,
+	0x5f, 0x83, 0x1c, 0x4c, 0xc4, 0xf9, 0x24, 0x43, 0x4c, 0xd4, 0x5f, 0x12, 0x28, 0x63, 0x90, 0x6f,
+	0x26, 0xc4, 0x0f, 0x62, 0x3d, 0x20, 0x3d, 0xd7, 0x03, 0x66, 0x7a, 0x4e, 0xce, 0xe9, 0x39, 0x92,
+	0x64, 0x2a, 0x26, 0xc9, 0x19, 0x39, 0xd2, 0xb9, 0xe4, 0xa4, 0xcf, 0x21, 0x47, 0x9e, 0x91, 0xc3,
+	0x04, 0x32, 0xf0, 0xe8, 0x88, 0xf7, 0x3c, 0xea, 0x99, 0xde, 0x69, 0xa8, 0xa0, 0x15, 0x66, 0x6d,
+	0x47, 0x46, 0xb5, 0x8b, 0xb2, 0x06, 0xf1, 0xc7, 0xa0, 0x15, 0x72, 0xe1, 0xb1, 0x21, 0x3c, 0x94,
+	0xa3, 0xc9, 0x0f, 0x0d, 0xe1, 0xd9, 0x18, 0xbf, 0x8f, 0xa4, 0x3e, 0xb5, 0xc4, 0x91, 0x57, 0xe3,
+	0xf9, 0xd7, 0x3c, 0x8f, 0xc2, 0xb3, 0x62, 0x41, 0xb9, 0x30, 0x00, 0x3c, 0xa9, 0x85, 0x2a, 0x7d,
+	0xe6, 0x3a, 0xd4, 0xb4, 0x9a, 0x1e, 0x1d, 0xb2, 0x1e, 0x75, 0x61, 0x5d, 0x57, 0x51, 0x66, 0xc2,
+	0x2b, 0x3f, 0xaa, 0xec, 0x1b, 0xf3, 0x95, 0x78, 0x36, 0x90, 0x68, 0x13, 0x91, 0x82, 0xc3, 0xa5,
+	0xea, 0xeb, 0x04, 0x52, 0x2e, 0x46, 0xe3, 0x1a, 0x5a, 0x16, 0xc8, 0x6e, 0xec, 0xb9, 0xdd, 0x7d,
+	0x97, 0x8d, 0x78, 0x13, 0x40, 0x93, 0xe9, 0xf8, 0xdc, 0x9e, 0x1e, 0xab, 0xb8, 0xd4, 0x3b, 0x57,
+	0x1c, 0xaf, 0x91, 0xe9, 0xcb, 0x24, 0xc1, 0xdd, 0xd3, 0x46, 0xbe, 0x27, 0x0a, 0x85, 0xdb, 0x54,
+	0x19, 0x49, 0x4d, 0xdb, 0x1d, 0xaa, 0xdb, 0x28, 0x5d, 0x71, 0x28, 0x4f, 0x96, 0x0c, 0xcf, 0xa6,
+	0x0f, 0xdb, 0x84, 0x1c, 0x8a, 0x59, 0xe9, 0x75, 0x12, 0x2d, 0xc7, 0xbe, 0x18, 0xe0, 0x3c, 0xab,
+	0x95, 0x7a, 0xa7, 0xd5, 0xd6, 0x8c, 0x6e, 0xa5, 0xa1, 0x1f, 0xd4, 0x0e, 0x0b, 0x4b, 0xca, 0xe6,
+	0xf7, 0x3f, 0xed, 0x14, 0x47, 0x33, 0xd0, 0xfc, 0xc7, 0x00, 0x6c, 0x51, 0xd3, 0xab, 0xda, 0x97,
+	0x85, 0x84, 0xb2, 0x0e, 0xc0, 0x42, 0x0c, 0x28, 0xba, 0xfd, 0x87, 0x28, 0xcf, 0x01, 0xdd, 0x4e,
+	0xb3, 0x5a, 0x6e, 0x6b, 0x85, 0xa4, 0xa2, 0x00, 0x6e, 0xe3, 0x2c, 0x2e, 0xe4, 0xfb, 0x3a, 0xd4,
+	0x85, 0xf6, 0xb0, 0xa3, 0xb5, 0xda, 0x85, 0x94, 0xb2, 0x01, 0x40, 0x1c, 0x03, 0x46, 0x15, 0x73,
+	0x13, 0x64, 0xa8, 0xb5, 0x9a, 0x0d, 0xbd, 0xa5, 0x15, 0x24, 0xe5, 0x2a, 0xa0, 0x2e, 0xcf, 0xa1,
+	0x42, 0x85, 0x7e, 0x8a, 0xd6, 0xaa, 0x8d, 0x47, 0x7a, 0xbd, 0x51, 0xae, 0x76, 0x9b, 0x46, 0xe3,
+	0x10, 0xd6, 0xb4, 0x0a, 0x69, 0x65, 0x1b, 0xf0, 0xd7, 0x62, 0xf8, 0x05, 0xc1, 0xfd, 0x1f, 0xd8,
+	0xab, 0xe9, 0x87, 0x05, 0x59, 0xb9, 0x0c, 0xd0, 0x4b, 0x31, 0x28, 0x23, 0x95, 0xdd, 0xb8, 0x52,
+	0x6f, 0xc0, 0xd6, 0x99, 0x85, 0x1b, 0x73, 0xb2, 0x4b, 0x5f, 0x23, 0xbc, 0xf8, 0x4d, 0x85, 0x6f,
+	0x20, 0x49, 0x6f, 0xe8, 0x1a, 0x10, 0xca, 0xef, 0xbf, 0x88, 0xd0, 0xa9, 0x4b, 0xb0, 0x8a, 0x52,
+	0xf5, 0xaf, 0x3e, 0x01, 0x32, 0xff, 0x07, 0xa0, 0x2b, 0x8b, 0x20, 0x70, 0x96, 0x28, 0x5a, 0x8e,
+	0x07, 0x56, 0x51, 0xf6, 0x48, 0x6b, 0x97, 0x81, 0xdc, 0x32, 0x04, 0xe7, 0x47, 0x8a, 0xdc, 0x47,
+	0x24, 0x30, 0x79, 0x01, 0x6e, 0xa2, 0xb4, 0xae, 0x1d, 0x6b, 0x06, 0x04, 0x5e, 0x03, 0xc0, 0x4a,
+	0x04, 0xd0, 0x09, 0xe8, 0x0a, 0x5e, 0x6e, 0xb9, 0x5c, 0x7f, 0x54, 0x7e, 0xdc, 0x82, 0xe4, 0x60,
+	0x70, 0xaf, 0x46, 0xee, 0xb2, 0xf3, 0xcc, 0x3c, 0xf5, 0x4b, 0x7f, 0x27, 0x50, 0x3e, 0xfe, 0xb6,
+	0xc1, 0x02, 0xe9, 0xa0, 0x56, 0xd7, 0xa2, 0xed, 0xe2, 0x3e, 0x36, 0xc6, 0xbb, 0x28, 0x57, 0xad,
+	0x19, 0x5a, 0xa5, 0xdd, 0x30, 0x1e, 0x47, 0x77, 0x89, 0x83, 0xaa, 0xb6, 0xc7, 0xc5, 0xcd, 0xbe,
+	0xe1, 0xf2, 0xad, 0xc7, 0x47, 0xf5, 0x9a, 0xfe, 0xa0, 0xcb, 0x23, 0x26, 0x95, 0x6b, 0x00, 0xbe,
+	0x1a, 0x07, 0xb7, 0x4e, 0x47, 0x8e, 0xed, 0x3e, 0xe1, 0x81, 0xef, 0xa1, 0xb5, 0x08, 0x3e, 0xdb,
+	0x20, 0xa5, 0xec, 0xc0, 0x9a, 0xcd, 0x73, 0xd6, 0xcc, 0xf6, 0xb9, 0x8b, 0x2e, 0x45, 0x0b, 0x3b,
+	0xfa, 0x03, 0x1d, 0x64, 0x01, 0xca, 0xd9, 0x82, 0x65, 0xca, 0x39, 0xcb, 0x3a, 0xee, 0x13, 0x17,
+	0x44, 0x51, 0xfa, 0x39, 0x81, 0x72, 0xd3, 0x0e, 0xc5, 0x78, 0xd6, 0x1b, 0x5d, 0xcd, 0x30, 0x1a,
+	0x46, 0x74, 0xf1, 0xa9, 0x53, 0xa7, 0x7c, 0x88, 0xdf, 0x43, 0x99, 0x43, 0x4d, 0xd7, 0x8c, 0x5a,
+	0x25, 0xaa, 0x87, 0x29, 0xe4, 0x90, 0xb8, 0xc4, 0xb3, 0xfb, 0xf0, 0xe5, 0x9e, 0x87, 0x30, 0xad,
+	0x4e, 0xe5, 0x7e, 0x74, 0x63, 0x2e, 0xe0, 0x58, 0xa8, 0xd6, 0xa4, 0x7f, 0xc2, 0x6f, 0x5b, 0x62,
+	0xa5, 0x73, 0x5c, 0xae, 0xd7, 0xaa, 0x02, 0x9a, 0x52, 0x8a, 0x00, 0x5d, 0x9f, 0x42, 0x6b, 0xe2,
+	0x79, 0x67, 0xd8, 0x92, 0x85, 0xb6, 0xfe, 0xbd, 0x17, 0xc1, 0x67, 0x87, 0x5c, 0x6e, 0x36, 0x35,
+	0xbd, 0x1a, 0x9d, 0x7e, 0xe6, 0x2b, 0x8f, 0xc7, 0xc4, 0xb5, 0x18, 0xe2, 0xa0, 0x61, 0x1c, 0x6a,
+	0xed, 0xe8, 0xf0, 0x33, 0xc4, 0x01, 0xf5, 0x86, 0x24, 0xd8, 0xdf, 0x7c, 0xf9, 0xfb, 0xd6, 0xd2,
+	0x2b, 0xf8, 0xbd, 0x7c, 0xbb, 0x95, 0x78, 0x05, 0xbf, 0xdf, 0xde, 0x6e, 0x2d, 0xfd, 0x09, 0xff,
+	0x2f, 0xfe, 0xd8, 0x4a, 0xf4, 0x64, 0xde, 0xbb, 0xee, 0xfe, 0x13, 0x00, 0x00, 0xff, 0xff, 0x88,
+	0x06, 0x3b, 0x0f, 0x5d, 0x0d, 0x00, 0x00,
+}

+ 189 - 0
lib/protocol/bep.proto

@@ -0,0 +1,189 @@
+// protoc --proto_path=../../../../../:../../../../gogo/protobuf/protobuf:. --gogofast_out=. message.proto
+
+syntax = "proto3";
+
+package protocol;
+
+import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+
+option (gogoproto.goproto_getters_all) = false;
+option (gogoproto.sizer_all) = false;
+option (gogoproto.protosizer_all) = true;
+option (gogoproto.goproto_enum_stringer_all) = true;
+option (gogoproto.goproto_enum_prefix_all) = false;
+
+// --- Pre-auth ---
+
+message Hello {
+    string device_name    = 1;
+    string client_name    = 2;
+    string client_version = 3;
+}
+
+// --- Header ---
+
+message Header {
+    MessageType        type        = 1;
+    MessageCompression compression = 2;
+}
+
+enum MessageType {
+    CLUSTER_CONFIG    = 0 [(gogoproto.enumvalue_customname) = "messageTypeClusterConfig"];
+    INDEX             = 1 [(gogoproto.enumvalue_customname) = "messageTypeIndex"];
+    INDEX_UPDATE      = 2 [(gogoproto.enumvalue_customname) = "messageTypeIndexUpdate"];
+    REQUEST           = 3 [(gogoproto.enumvalue_customname) = "messageTypeRequest"];
+    RESPONSE          = 4 [(gogoproto.enumvalue_customname) = "messageTypeResponse"];
+    DOWNLOAD_PROGRESS = 5 [(gogoproto.enumvalue_customname) = "messageTypeDownloadProgress"];
+    PING              = 6 [(gogoproto.enumvalue_customname) = "messageTypePing"];
+    CLOSE             = 7 [(gogoproto.enumvalue_customname) = "messageTypeClose"];
+}
+
+enum MessageCompression {
+    NONE = 0 [(gogoproto.enumvalue_customname) = "MessageCompressionNone"];
+    LZ4  = 1 [(gogoproto.enumvalue_customname) = "MessageCompressionLZ4"];
+}
+
+// --- Actual messages ---
+
+// Cluster Config
+
+message ClusterConfig {
+    repeated Folder folders = 1 [(gogoproto.nullable) = false];
+}
+
+message Folder {
+    string id                   = 1 [(gogoproto.customname) = "ID"];
+    string label                = 2;
+    bool   read_only            = 3;
+    bool   ignore_permissions   = 4;
+    bool   ignore_delete        = 5;
+    bool   disable_temp_indexes = 6;
+
+    repeated Device devices = 16 [(gogoproto.nullable) = false];
+}
+
+message Device {
+    bytes           id                = 1 [(gogoproto.customname) = "ID"];
+    string          name              = 2;
+    repeated string addresses         = 3;
+    Compression     compression       = 4;
+    string          cert_name         = 5;
+    int64           max_local_version = 6;
+    bool            introducer        = 7;
+}
+
+enum Compression {
+    METADATA = 0 [(gogoproto.enumvalue_customname) = "CompressMetadata"];
+    NEVER    = 1 [(gogoproto.enumvalue_customname) = "CompressNever"];
+    ALWAYS   = 2 [(gogoproto.enumvalue_customname) = "CompressAlways"];
+}
+
+// Index and Index Update
+
+message Index {
+    string            folder = 1;
+    repeated FileInfo files  = 2 [(gogoproto.nullable) = false];
+}
+
+message IndexUpdate {
+    string            folder = 1;
+    repeated FileInfo files  = 2 [(gogoproto.nullable) = false];
+}
+
+message FileInfo {
+    option (gogoproto.goproto_stringer) = false;
+    string       name           = 1;
+    FileInfoType type           = 2;
+    int64        size           = 3;
+    uint32       permissions    = 4;
+    int64        modified       = 5;
+    bool         deleted        = 6;
+    bool         invalid        = 7;
+    bool         no_permissions = 8;
+    Vector       version        = 9 [(gogoproto.nullable) = false];
+    int64        local_version  = 10;
+
+    repeated BlockInfo Blocks = 16 [(gogoproto.nullable) = false];
+}
+
+enum FileInfoType {
+    FILE              = 0 [(gogoproto.enumvalue_customname) = "FileInfoTypeFile"];
+    DIRECTORY         = 1 [(gogoproto.enumvalue_customname) = "FileInfoTypeDirectory"];
+    SYMLINK_FILE      = 2 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlinkFile"];
+    SYMLINK_DIRECTORY = 3 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlinkDirectory"];
+    SYMLINK_UNKNOWN   = 4 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlinkUnknown"];
+}
+
+message BlockInfo {
+    option (gogoproto.goproto_stringer) = false;
+    int64 offset = 1;
+    int32 size   = 2;
+    bytes hash   = 3;
+}
+
+message Vector {
+    repeated Counter counters = 1 [(gogoproto.nullable) = false];
+}
+
+message Counter {
+    uint64 id    = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false];
+    uint64 value = 2;
+}
+
+// Request
+
+message Request {
+    int32  id             = 1 [(gogoproto.customname) = "ID"];
+    string folder         = 2;
+    string name           = 3;
+    int64  offset         = 4;
+    int32  size           = 5;
+    bytes  hash           = 6;
+    bool   from_temporary = 7;
+}
+
+// Response
+
+message Response {
+    int32     id   = 1 [(gogoproto.customname) = "ID"];
+    bytes     data = 2;
+    ErrorCode code = 3;
+}
+
+enum ErrorCode {
+    NO_ERROR     = 0 [(gogoproto.enumvalue_customname) = "ErrorCodeNoError"];
+    GENERIC      = 1 [(gogoproto.enumvalue_customname) = "ErrorCodeGeneric"];
+    NO_SUCH_FILE = 2 [(gogoproto.enumvalue_customname) = "ErrorCodeNoSuchFile"];
+    INVALID_FILE = 3 [(gogoproto.enumvalue_customname) = "ErrorCodeInvalidFile"];
+}
+
+// DownloadProgress
+
+message DownloadProgress {
+    string                              folder  = 1;
+    repeated FileDownloadProgressUpdate updates = 2 [(gogoproto.nullable) = false];
+}
+
+message FileDownloadProgressUpdate {
+    FileDownloadProgressUpdateType update_type   = 1;
+    string                         name          = 2;
+    Vector                         version       = 3 [(gogoproto.nullable) = false];
+    repeated int32                 block_indexes = 4;
+}
+
+enum FileDownloadProgressUpdateType {
+    APPEND = 0 [(gogoproto.enumvalue_customname) = "UpdateTypeAppend"];
+    FORGET = 1 [(gogoproto.enumvalue_customname) = "UpdateTypeForget"];
+}
+
+// Ping
+
+message Ping {
+}
+
+// Close
+
+message Close {
+    string reason = 1;
+}
+

+ 96 - 0
lib/protocol/bep_extensions.go

@@ -0,0 +1,96 @@
+// Copyright (C) 2014 The Protocol Authors.
+
+//go:generate go run ../../script/protofmt.go bep.proto
+//go:generate protoc --proto_path=../../../../../:../../../../gogo/protobuf/protobuf:. --gogofast_out=. bep.proto
+
+package protocol
+
+import (
+	"bytes"
+	"crypto/sha256"
+	"fmt"
+)
+
+var (
+	sha256OfEmptyBlock = sha256.Sum256(make([]byte, BlockSize))
+	HelloMessageMagic  = uint32(0x2EA7D90B)
+)
+
+func (m Hello) Magic() uint32 {
+	return HelloMessageMagic
+}
+
+func (f FileInfo) String() string {
+	return fmt.Sprintf("File{Name:%q, Permissions:0%o, Modified:%d, Version:%v, Length:%d, Deleted:%v, Invalid:%v, NoPermissions:%v, Blocks:%v}",
+		f.Name, f.Permissions, f.Modified, f.Version, f.Size, f.Deleted, f.Invalid, f.NoPermissions, f.Blocks)
+}
+
+func (f FileInfo) IsDeleted() bool {
+	return f.Deleted
+}
+
+func (f FileInfo) IsInvalid() bool {
+	return f.Invalid
+}
+
+func (f FileInfo) IsDirectory() bool {
+	return f.Type == FileInfoTypeDirectory
+}
+
+func (f FileInfo) IsSymlink() bool {
+	switch f.Type {
+	case FileInfoTypeSymlinkDirectory, FileInfoTypeSymlinkFile, FileInfoTypeSymlinkUnknown:
+		return true
+	default:
+		return false
+	}
+}
+
+func (f FileInfo) HasPermissionBits() bool {
+	return !f.NoPermissions
+}
+
+func (f FileInfo) FileSize() int64 {
+	if f.IsDirectory() || f.IsDeleted() {
+		return 128
+	}
+	return f.Size
+}
+
+func (f FileInfo) FileName() string {
+	return f.Name
+}
+
+// WinsConflict returns true if "f" is the one to choose when it is in
+// conflict with "other".
+func (f FileInfo) WinsConflict(other FileInfo) bool {
+	// If a modification is in conflict with a delete, we pick the
+	// modification.
+	if !f.IsDeleted() && other.IsDeleted() {
+		return true
+	}
+	if f.IsDeleted() && !other.IsDeleted() {
+		return false
+	}
+
+	// The one with the newer modification time wins.
+	if f.Modified > other.Modified {
+		return true
+	}
+	if f.Modified < other.Modified {
+		return false
+	}
+
+	// The modification times were equal. Use the device ID in the version
+	// vector as tie breaker.
+	return f.Version.Compare(other.Version) == ConcurrentGreater
+}
+
+func (b BlockInfo) String() string {
+	return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash)
+}
+
+// IsEmpty returns true if the block is a full block of zeroes.
+func (b BlockInfo) IsEmpty() bool {
+	return b.Size == BlockSize && bytes.Equal(b.Hash, sha256OfEmptyBlock[:])
+}

+ 59 - 0
lib/protocol/bufferpool.go

@@ -0,0 +1,59 @@
+// Copyright (C) 2016 The Protocol Authors.
+
+package protocol
+
+import "sync"
+
+type bufferPool struct {
+	minSize int
+	pool    sync.Pool
+}
+
+// get returns a new buffer of the requested size
+func (p *bufferPool) get(size int) []byte {
+	intf := p.pool.Get()
+	if intf == nil {
+		// Pool is empty, must allocate.
+		return p.new(size)
+	}
+
+	bs := intf.([]byte)
+	if cap(bs) < size {
+		// Buffer was too small, leave it for someone else and allocate.
+		p.put(bs)
+		return p.new(size)
+	}
+
+	return bs[:size]
+}
+
+// upgrade grows the buffer to the requested size, while attempting to reuse
+// it if possible.
+func (p *bufferPool) upgrade(bs []byte, size int) []byte {
+	if cap(bs) >= size {
+		// Reslicing is enough, lets go!
+		return bs[:size]
+	}
+
+	// It was too small. But it pack into the pool and try to get another
+	// buffer.
+	p.put(bs)
+	return p.get(size)
+}
+
+// put returns the buffer to the pool
+func (p *bufferPool) put(bs []byte) {
+	p.pool.Put(bs)
+}
+
+// new creates a new buffer of the requested size, taking the minimum
+// allocation count into account. For internal use only.
+func (p *bufferPool) new(size int) []byte {
+	allocSize := size
+	if allocSize < p.minSize {
+		// Avoid allocating tiny buffers that we won't be able to reuse for
+		// anything useful.
+		allocSize = p.minSize
+	}
+	return make([]byte, allocSize)[:size]
+}

+ 15 - 17
lib/protocol/common_test.go

@@ -5,16 +5,15 @@ package protocol
 import "time"
 import "time"
 
 
 type TestModel struct {
 type TestModel struct {
-	data      []byte
-	folder    string
-	name      string
-	offset    int64
-	size      int
-	hash      []byte
-	flags     uint32
-	options   []Option
-	closedCh  chan struct{}
-	closedErr error
+	data          []byte
+	folder        string
+	name          string
+	offset        int64
+	size          int
+	hash          []byte
+	fromTemporary bool
+	closedCh      chan struct{}
+	closedErr     error
 }
 }
 
 
 func newTestModel() *TestModel {
 func newTestModel() *TestModel {
@@ -23,20 +22,19 @@ func newTestModel() *TestModel {
 	}
 	}
 }
 }
 
 
-func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (t *TestModel) Index(deviceID DeviceID, folder string, files []FileInfo) {
 }
 }
 
 
-func (t *TestModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (t *TestModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) {
 }
 }
 
 
-func (t *TestModel) Request(deviceID DeviceID, folder, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error {
+func (t *TestModel) Request(deviceID DeviceID, folder, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
 	t.folder = folder
 	t.folder = folder
 	t.name = name
 	t.name = name
 	t.offset = offset
 	t.offset = offset
 	t.size = len(buf)
 	t.size = len(buf)
 	t.hash = hash
 	t.hash = hash
-	t.flags = flags
-	t.options = options
+	t.fromTemporary = fromTemporary
 	copy(buf, t.data)
 	copy(buf, t.data)
 	return nil
 	return nil
 }
 }
@@ -46,10 +44,10 @@ func (t *TestModel) Close(deviceID DeviceID, err error) {
 	close(t.closedCh)
 	close(t.closedCh)
 }
 }
 
 
-func (t *TestModel) ClusterConfig(deviceID DeviceID, config ClusterConfigMessage) {
+func (t *TestModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) {
 }
 }
 
 
-func (t *TestModel) DownloadProgress(DeviceID, string, []FileDownloadProgressUpdate, uint32, []Option) {
+func (t *TestModel) DownloadProgress(DeviceID, string, []FileDownloadProgressUpdate) {
 }
 }
 
 
 func (t *TestModel) closedError() error {
 func (t *TestModel) closedError() error {

+ 0 - 14
lib/protocol/compression.go

@@ -4,13 +4,7 @@ package protocol
 
 
 import "fmt"
 import "fmt"
 
 
-type Compression int
-
 const (
 const (
-	CompressMetadata Compression = iota // zero value is the default, default should be "metadata"
-	CompressNever
-	CompressAlways
-
 	compressionThreshold = 128 // don't bother compressing messages smaller than this many bytes
 	compressionThreshold = 128 // don't bother compressing messages smaller than this many bytes
 )
 )
 
 
@@ -31,14 +25,6 @@ var compressionUnmarshal = map[string]Compression{
 	"always":   CompressAlways,
 	"always":   CompressAlways,
 }
 }
 
 
-func (c Compression) String() string {
-	s, ok := compressionMarshal[c]
-	if !ok {
-		return fmt.Sprintf("unknown:%d", c)
-	}
-	return s
-}
-
 func (c Compression) GoString() string {
 func (c Compression) GoString() string {
 	return fmt.Sprintf("%q", c.String())
 	return fmt.Sprintf("%q", c.String())
 }
 }

+ 2 - 2
lib/protocol/conflict_test.go

@@ -8,8 +8,8 @@ func TestWinsConflict(t *testing.T) {
 	testcases := [][2]FileInfo{
 	testcases := [][2]FileInfo{
 		// The first should always win over the second
 		// The first should always win over the second
 		{{Modified: 42}, {Modified: 41}},
 		{{Modified: 42}, {Modified: 41}},
-		{{Modified: 41}, {Modified: 42, Flags: FlagDeleted}},
-		{{Modified: 41, Version: Vector{{42, 2}, {43, 1}}}, {Modified: 41, Version: Vector{{42, 1}, {43, 2}}}},
+		{{Modified: 41}, {Modified: 42, Deleted: true}},
+		{{Modified: 41, Version: Vector{[]Counter{{42, 2}, {43, 1}}}}, {Modified: 41, Version: Vector{[]Counter{{42, 1}, {43, 2}}}}},
 	}
 	}
 
 
 	for _, tc := range testcases {
 	for _, tc := range testcases {

+ 0 - 4
lib/protocol/debug.go

@@ -16,7 +16,3 @@ var (
 func init() {
 func init() {
 	l.SetDebug("protocol", strings.Contains(os.Getenv("STTRACE"), "protocol") || os.Getenv("STTRACE") == "all")
 	l.SetDebug("protocol", strings.Contains(os.Getenv("STTRACE"), "protocol") || os.Getenv("STTRACE") == "all")
 }
 }
-
-func shouldDebug() bool {
-	return l.ShouldDebug("protocol")
-}

+ 14 - 21
lib/protocol/errors.go

@@ -6,13 +6,6 @@ import (
 	"errors"
 	"errors"
 )
 )
 
 
-const (
-	ecNoError int32 = iota
-	ecGeneric
-	ecNoSuchFile
-	ecInvalid
-)
-
 var (
 var (
 	ErrNoError    error
 	ErrNoError    error
 	ErrGeneric    = errors.New("generic error")
 	ErrGeneric    = errors.New("generic error")
@@ -20,32 +13,32 @@ var (
 	ErrInvalid    = errors.New("file is invalid")
 	ErrInvalid    = errors.New("file is invalid")
 )
 )
 
 
-var lookupError = map[int32]error{
-	ecNoError:    ErrNoError,
-	ecGeneric:    ErrGeneric,
-	ecNoSuchFile: ErrNoSuchFile,
-	ecInvalid:    ErrInvalid,
+var lookupError = map[ErrorCode]error{
+	ErrorCodeNoError:     ErrNoError,
+	ErrorCodeGeneric:     ErrGeneric,
+	ErrorCodeNoSuchFile:  ErrNoSuchFile,
+	ErrorCodeInvalidFile: ErrInvalid,
 }
 }
 
 
-var lookupCode = map[error]int32{
-	ErrNoError:    ecNoError,
-	ErrGeneric:    ecGeneric,
-	ErrNoSuchFile: ecNoSuchFile,
-	ErrInvalid:    ecInvalid,
+var lookupCode = map[error]ErrorCode{
+	ErrNoError:    ErrorCodeNoError,
+	ErrGeneric:    ErrorCodeGeneric,
+	ErrNoSuchFile: ErrorCodeNoSuchFile,
+	ErrInvalid:    ErrorCodeInvalidFile,
 }
 }
 
 
-func codeToError(errcode int32) error {
-	err, ok := lookupError[errcode]
+func codeToError(code ErrorCode) error {
+	err, ok := lookupError[code]
 	if !ok {
 	if !ok {
 		return ErrGeneric
 		return ErrGeneric
 	}
 	}
 	return err
 	return err
 }
 }
 
 
-func errorToCode(err error) int32 {
+func errorToCode(err error) ErrorCode {
 	code, ok := lookupCode[err]
 	code, ok := lookupCode[err]
 	if !ok {
 	if !ok {
-		return ecGeneric
+		return ErrorCodeGeneric
 	}
 	}
 	return code
 	return code
 }
 }

+ 0 - 70
lib/protocol/fuzz.go

@@ -1,70 +0,0 @@
-// Copyright (C) 2015 The Protocol Authors.
-
-// +build gofuzz
-
-package protocol
-
-import (
-	"bytes"
-	"encoding/binary"
-	"encoding/hex"
-	"fmt"
-	"reflect"
-	"sync"
-)
-
-func Fuzz(data []byte) int {
-	// Regenerate the length, or we'll most commonly exit quickly due to an
-	// unexpected eof which is unintestering.
-	if len(data) > 8 {
-		binary.BigEndian.PutUint32(data[4:], uint32(len(data))-8)
-	}
-
-	// Setup a rawConnection we'll use to parse the message.
-	c := rawConnection{
-		cr:     &countingReader{Reader: bytes.NewReader(data)},
-		closed: make(chan struct{}),
-		pool: sync.Pool{
-			New: func() interface{} {
-				return make([]byte, BlockSize)
-			},
-		},
-	}
-
-	// Attempt to parse the message.
-	hdr, msg, err := c.readMessage()
-	if err != nil {
-		return 0
-	}
-
-	// If parsing worked, attempt to encode it again.
-	newBs, err := msg.AppendXDR(nil)
-	if err != nil {
-		panic("not encodable")
-	}
-
-	// Create an appriate header for the re-encoding.
-	newMsg := make([]byte, 8)
-	binary.BigEndian.PutUint32(newMsg, encodeHeader(hdr))
-	binary.BigEndian.PutUint32(newMsg[4:], uint32(len(newBs)))
-	newMsg = append(newMsg, newBs...)
-
-	// Use the rawConnection to parse the re-encoding.
-	c.cr = &countingReader{Reader: bytes.NewReader(newMsg)}
-	hdr2, msg2, err := c.readMessage()
-	if err != nil {
-		fmt.Println("Initial:\n" + hex.Dump(data))
-		fmt.Println("New:\n" + hex.Dump(newMsg))
-		panic("not parseable after re-encode: " + err.Error())
-	}
-
-	// Make sure the data is the same as it was before.
-	if hdr != hdr2 {
-		panic("headers differ")
-	}
-	if !reflect.DeepEqual(msg, msg2) {
-		panic("contents differ")
-	}
-
-	return 1
-}

+ 0 - 89
lib/protocol/fuzz_test.go

@@ -1,89 +0,0 @@
-// Copyright (C) 2015 The Protocol Authors.
-
-// +build gofuzz
-
-package protocol
-
-import (
-	"encoding/binary"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"strings"
-	"testing"
-	"testing/quick"
-)
-
-// This can be used to generate a corpus of valid messages as a starting point
-// for the fuzzer.
-func TestGenerateCorpus(t *testing.T) {
-	t.Skip("Use to generate initial corpus only")
-
-	n := 0
-	check := func(idx IndexMessage) bool {
-		for i := range idx.Options {
-			if len(idx.Options[i].Key) > 64 {
-				idx.Options[i].Key = idx.Options[i].Key[:64]
-			}
-		}
-		hdr := header{
-			version:     0,
-			msgID:       42,
-			msgType:     messageTypeIndex,
-			compression: false,
-		}
-
-		msgBs := idx.MustMarshalXDR()
-
-		buf := make([]byte, 8)
-		binary.BigEndian.PutUint32(buf, encodeHeader(hdr))
-		binary.BigEndian.PutUint32(buf[4:], uint32(len(msgBs)))
-		buf = append(buf, msgBs...)
-
-		ioutil.WriteFile(fmt.Sprintf("testdata/corpus/test-%03d.xdr", n), buf, 0644)
-		n++
-		return true
-	}
-
-	if err := quick.Check(check, &quick.Config{MaxCount: 1000}); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// Tests any crashers found by the fuzzer, for closer investigation.
-func TestCrashers(t *testing.T) {
-	testFiles(t, "testdata/crashers")
-}
-
-// Tests the entire corpus, which should PASS before the fuzzer starts
-// fuzzing.
-func TestCorpus(t *testing.T) {
-	testFiles(t, "testdata/corpus")
-}
-
-func testFiles(t *testing.T, dir string) {
-	fd, err := os.Open(dir)
-	if err != nil {
-		t.Fatal(err)
-	}
-	crashers, err := fd.Readdirnames(-1)
-	if err != nil {
-		t.Fatal(err)
-	}
-	for _, name := range crashers {
-		if strings.HasSuffix(name, ".output") {
-			continue
-		}
-		if strings.HasSuffix(name, ".quoted") {
-			continue
-		}
-
-		t.Log(name)
-		crasher, err := ioutil.ReadFile(dir + "/" + name)
-		if err != nil {
-			t.Fatal(err)
-		}
-
-		Fuzz(crasher)
-	}
-}

+ 0 - 44
lib/protocol/header.go

@@ -1,44 +0,0 @@
-// Copyright (C) 2014 The Protocol Authors.
-
-package protocol
-
-import "github.com/calmh/xdr"
-
-type header struct {
-	version     int
-	msgID       int
-	msgType     int
-	compression bool
-}
-
-func (h header) MarshalXDRInto(m *xdr.Marshaller) error {
-	v := encodeHeader(h)
-	m.MarshalUint32(v)
-	return m.Error
-}
-
-func (h *header) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	v := u.UnmarshalUint32()
-	*h = decodeHeader(v)
-	return u.Error
-}
-
-func encodeHeader(h header) uint32 {
-	var isComp uint32
-	if h.compression {
-		isComp = 1 << 0 // the zeroth bit is the compression bit
-	}
-	return uint32(h.version&0xf)<<28 +
-		uint32(h.msgID&0xfff)<<16 +
-		uint32(h.msgType&0xff)<<8 +
-		isComp
-}
-
-func decodeHeader(u uint32) header {
-	return header{
-		version:     int(u>>28) & 0xf,
-		msgID:       int(u>>16) & 0xfff,
-		msgType:     int(u>>8) & 0xff,
-		compression: u&1 == 1,
-	}
-}

+ 31 - 6
lib/protocol/hello.go

@@ -9,10 +9,10 @@ import (
 	"io"
 	"io"
 )
 )
 
 
-// The HelloMessage interface is implemented by the version specific hello
+// The HelloIntf interface is implemented by the version specific hello
 // message. It knows its magic number and how to serialize itself to a byte
 // message. It knows its magic number and how to serialize itself to a byte
 // buffer.
 // buffer.
-type HelloMessage interface {
+type HelloIntf interface {
 	Magic() uint32
 	Magic() uint32
 	Marshal() ([]byte, error)
 	Marshal() ([]byte, error)
 }
 }
@@ -29,12 +29,15 @@ var (
 	// ErrTooOldVersion12 is returned by ExchangeHello when the other side
 	// ErrTooOldVersion12 is returned by ExchangeHello when the other side
 	// speaks the older, incompatible version 0.12 of the protocol.
 	// speaks the older, incompatible version 0.12 of the protocol.
 	ErrTooOldVersion12 = errors.New("the remote device speaks an older version of the protocol (v0.12) not compatible with this version")
 	ErrTooOldVersion12 = errors.New("the remote device speaks an older version of the protocol (v0.12) not compatible with this version")
+	// ErrTooOldVersion13 is returned by ExchangeHello when the other side
+	// speaks the older, incompatible version 0.12 of the protocol.
+	ErrTooOldVersion13 = errors.New("the remote device speaks an older version of the protocol (v0.13) not compatible with this version")
 	// ErrUnknownMagic is returned by ExchangeHellow when the other side
 	// ErrUnknownMagic is returned by ExchangeHellow when the other side
 	// speaks something entirely unknown.
 	// speaks something entirely unknown.
 	ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
 	ErrUnknownMagic = errors.New("the remote device speaks an unknown (newer?) version of the protocol")
 )
 )
 
 
-func ExchangeHello(c io.ReadWriter, h HelloMessage) (HelloResult, error) {
+func ExchangeHello(c io.ReadWriter, h HelloIntf) (HelloResult, error) {
 	if err := writeHello(c, h); err != nil {
 	if err := writeHello(c, h); err != nil {
 		return HelloResult{}, err
 		return HelloResult{}, err
 	}
 	}
@@ -45,7 +48,7 @@ func ExchangeHello(c io.ReadWriter, h HelloMessage) (HelloResult, error) {
 // version mismatch that we might want to alert the user about.
 // version mismatch that we might want to alert the user about.
 func IsVersionMismatch(err error) bool {
 func IsVersionMismatch(err error) bool {
 	switch err {
 	switch err {
-	case ErrTooOldVersion12, ErrUnknownMagic:
+	case ErrTooOldVersion12, ErrTooOldVersion13, ErrUnknownMagic:
 		return true
 		return true
 	default:
 	default:
 		return false
 		return false
@@ -59,6 +62,28 @@ func readHello(c io.Reader) (HelloResult, error) {
 	}
 	}
 
 
 	switch binary.BigEndian.Uint32(header[:4]) {
 	switch binary.BigEndian.Uint32(header[:4]) {
+	case HelloMessageMagic:
+		// This is a v0.14 Hello message in proto format
+		msgSize := binary.BigEndian.Uint32(header[4:])
+		if msgSize > 1024 {
+			return HelloResult{}, fmt.Errorf("hello message too big")
+		}
+		buf := make([]byte, msgSize)
+		if _, err := io.ReadFull(c, buf); err != nil {
+			return HelloResult{}, err
+		}
+
+		var hello Hello
+		if err := hello.Unmarshal(buf); err != nil {
+			return HelloResult{}, err
+		}
+		res := HelloResult{
+			DeviceName:    hello.DeviceName,
+			ClientName:    hello.ClientName,
+			ClientVersion: hello.ClientVersion,
+		}
+		return res, nil
+
 	case Version13HelloMagic:
 	case Version13HelloMagic:
 		// This is a v0.13 Hello message in XDR format
 		// This is a v0.13 Hello message in XDR format
 		msgSize := binary.BigEndian.Uint32(header[4:])
 		msgSize := binary.BigEndian.Uint32(header[4:])
@@ -79,7 +104,7 @@ func readHello(c io.Reader) (HelloResult, error) {
 			ClientName:    hello.ClientName,
 			ClientName:    hello.ClientName,
 			ClientVersion: hello.ClientVersion,
 			ClientVersion: hello.ClientVersion,
 		}
 		}
-		return res, nil
+		return res, ErrTooOldVersion13
 
 
 	case 0x00010001, 0x00010000:
 	case 0x00010001, 0x00010000:
 		// This is the first word of a v0.12 cluster config message.
 		// This is the first word of a v0.12 cluster config message.
@@ -90,7 +115,7 @@ func readHello(c io.Reader) (HelloResult, error) {
 	return HelloResult{}, ErrUnknownMagic
 	return HelloResult{}, ErrUnknownMagic
 }
 }
 
 
-func writeHello(c io.Writer, h HelloMessage) error {
+func writeHello(c io.Writer, h HelloIntf) error {
 	msg, err := h.Marshal()
 	msg, err := h.Marshal()
 	if err != nil {
 	if err != nil {
 		return err
 		return err

+ 50 - 3
lib/protocol/hello_test.go

@@ -13,6 +13,53 @@ import (
 
 
 var spaceRe = regexp.MustCompile(`\s`)
 var spaceRe = regexp.MustCompile(`\s`)
 
 
+func TestVersion14Hello(t *testing.T) {
+	// Tests that we can send and receive a version 0.14 hello message.
+
+	expected := Hello{
+		DeviceName:    "test device",
+		ClientName:    "syncthing",
+		ClientVersion: "v0.14.5",
+	}
+	msgBuf, err := expected.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	hdrBuf := make([]byte, 8)
+	binary.BigEndian.PutUint32(hdrBuf, HelloMessageMagic)
+	binary.BigEndian.PutUint32(hdrBuf[4:], uint32(len(msgBuf)))
+
+	outBuf := new(bytes.Buffer)
+	outBuf.Write(hdrBuf)
+	outBuf.Write(msgBuf)
+
+	inBuf := new(bytes.Buffer)
+
+	conn := &readWriter{outBuf, inBuf}
+
+	send := &Hello{
+		DeviceName:    "this device",
+		ClientName:    "other client",
+		ClientVersion: "v0.14.6",
+	}
+
+	res, err := ExchangeHello(conn, send)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if res.ClientName != expected.ClientName {
+		t.Errorf("incorrect ClientName %q != expected %q", res.ClientName, expected.ClientName)
+	}
+	if res.ClientVersion != expected.ClientVersion {
+		t.Errorf("incorrect ClientVersion %q != expected %q", res.ClientVersion, expected.ClientVersion)
+	}
+	if res.DeviceName != expected.DeviceName {
+		t.Errorf("incorrect DeviceName %q != expected %q", res.DeviceName, expected.DeviceName)
+	}
+}
+
 func TestVersion13Hello(t *testing.T) {
 func TestVersion13Hello(t *testing.T) {
 	// Tests that we can send and receive a version 0.13 hello message.
 	// Tests that we can send and receive a version 0.13 hello message.
 
 
@@ -42,8 +89,8 @@ func TestVersion13Hello(t *testing.T) {
 	}
 	}
 
 
 	res, err := ExchangeHello(conn, send)
 	res, err := ExchangeHello(conn, send)
-	if err != nil {
-		t.Fatal(err)
+	if err != ErrTooOldVersion13 {
+		t.Errorf("unexpected error %v != ErrTooOldVersion13", err)
 	}
 	}
 
 
 	if res.ClientName != expected.ClientName {
 	if res.ClientName != expected.ClientName {
@@ -94,7 +141,7 @@ func TestVersion12Hello(t *testing.T) {
 
 
 	_, err := ExchangeHello(conn, send)
 	_, err := ExchangeHello(conn, send)
 	if err != ErrTooOldVersion12 {
 	if err != ErrTooOldVersion12 {
-		t.Errorf("unexpected error %v != ErrTooOld", err)
+		t.Errorf("unexpected error %v != ErrTooOldVersion12", err)
 	}
 	}
 }
 }
 
 

+ 0 - 186
lib/protocol/message.go

@@ -1,186 +0,0 @@
-// Copyright (C) 2014 The Protocol Authors.
-
-//go:generate -command genxdr go run ../../vendor/github.com/calmh/xdr/cmd/genxdr/main.go
-//go:generate genxdr -o message_xdr.go message.go
-
-package protocol
-
-import (
-	"bytes"
-	"crypto/sha256"
-	"fmt"
-)
-
-var (
-	sha256OfEmptyBlock = sha256.Sum256(make([]byte, BlockSize))
-)
-
-type IndexMessage struct {
-	Folder  string     // max:256
-	Files   []FileInfo // max:1000000
-	Flags   uint32
-	Options []Option // max:64
-}
-
-type FileInfo struct {
-	Name         string // max:8192
-	Flags        uint32
-	Modified     int64
-	Version      Vector
-	LocalVersion int64
-	CachedSize   int64       // noencode (cache only)
-	Blocks       []BlockInfo // max:10000000
-}
-
-func (f FileInfo) String() string {
-	return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%v, Size:%d, Blocks:%v}",
-		f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.Blocks)
-}
-
-func (f FileInfo) Size() (bytes int64) {
-	if f.IsDeleted() || f.IsDirectory() {
-		return 128
-	}
-	if f.CachedSize > 0 {
-		return f.CachedSize
-	}
-	for _, b := range f.Blocks {
-		bytes += int64(b.Size)
-	}
-	f.CachedSize = bytes
-	return
-}
-
-func (f FileInfo) IsDeleted() bool {
-	return f.Flags&FlagDeleted != 0
-}
-
-func (f FileInfo) IsInvalid() bool {
-	return f.Flags&FlagInvalid != 0
-}
-
-func (f FileInfo) IsDirectory() bool {
-	return f.Flags&FlagDirectory != 0
-}
-
-func (f FileInfo) IsSymlink() bool {
-	return f.Flags&FlagSymlink != 0
-}
-
-func (f FileInfo) HasPermissionBits() bool {
-	return f.Flags&FlagNoPermBits == 0
-}
-
-// WinsConflict returns true if "f" is the one to choose when it is in
-// conflict with "other".
-func (f FileInfo) WinsConflict(other FileInfo) bool {
-	// If a modification is in conflict with a delete, we pick the
-	// modification.
-	if !f.IsDeleted() && other.IsDeleted() {
-		return true
-	}
-	if f.IsDeleted() && !other.IsDeleted() {
-		return false
-	}
-
-	// The one with the newer modification time wins.
-	if f.Modified > other.Modified {
-		return true
-	}
-	if f.Modified < other.Modified {
-		return false
-	}
-
-	// The modification times were equal. Use the device ID in the version
-	// vector as tie breaker.
-	return f.Version.Compare(other.Version) == ConcurrentGreater
-}
-
-type BlockInfo struct {
-	Offset int64 // noencode (cache only)
-	Size   int32
-	Hash   []byte // max:64
-}
-
-func (b BlockInfo) String() string {
-	return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash)
-}
-
-// IsEmpty returns true if the block is a full block of zeroes.
-func (b BlockInfo) IsEmpty() bool {
-	return b.Size == BlockSize && bytes.Equal(b.Hash, sha256OfEmptyBlock[:])
-}
-
-type RequestMessage struct {
-	Folder  string // max:256
-	Name    string // max:8192
-	Offset  int64
-	Size    int32
-	Hash    []byte // max:64
-	Flags   uint32
-	Options []Option // max:64
-}
-
-type ResponseMessage struct {
-	Data []byte
-	Code int32
-}
-
-type ClusterConfigMessage struct {
-	Folders []Folder // max:1000000
-	Options []Option // max:64
-}
-
-type DownloadProgressMessage struct {
-	Folder  string                       // max:64
-	Updates []FileDownloadProgressUpdate // max:1000000
-	Flags   uint32
-	Options []Option // max:64
-}
-
-func (o *ClusterConfigMessage) GetOption(key string) string {
-	for _, option := range o.Options {
-		if option.Key == key {
-			return option.Value
-		}
-	}
-	return ""
-}
-
-type Folder struct {
-	ID      string   // max:256
-	Label   string   // max:256
-	Devices []Device // max:1000000
-	Flags   uint32
-	Options []Option // max:64
-}
-
-type Device struct {
-	ID              []byte   // max:32
-	Name            string   // max:64
-	Addresses       []string // max:64,2083
-	Compression     uint32
-	CertName        string // max:64
-	MaxLocalVersion int64
-	Flags           uint32
-	Options         []Option // max:64
-}
-
-type FileDownloadProgressUpdate struct {
-	UpdateType   uint32
-	Name         string // max:8192
-	Version      Vector
-	BlockIndexes []int32 // max:1000000
-}
-
-type Option struct {
-	Key   string // max:64
-	Value string // max:1024
-}
-
-type CloseMessage struct {
-	Reason string // max:1024
-	Code   int32
-}
-
-type EmptyMessage struct{}

+ 0 - 1309
lib/protocol/message_xdr.go

@@ -1,1309 +0,0 @@
-// ************************************************************
-// This file is automatically generated by genxdr. Do not edit.
-// ************************************************************
-
-package protocol
-
-import (
-	"github.com/calmh/xdr"
-)
-
-/*
-
-IndexMessage Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 Folder (length + padded data)                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                        Number of Files                        |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\               Zero or more FileInfo Structures                \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Flags                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Options                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Option Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct IndexMessage {
-	string Folder<256>;
-	FileInfo Files<1000000>;
-	unsigned int Flags;
-	Option Options<64>;
-}
-
-*/
-
-func (o IndexMessage) XDRSize() int {
-	return 4 + len(o.Folder) + xdr.Padding(len(o.Folder)) +
-		4 + xdr.SizeOfSlice(o.Files) + 4 +
-		4 + xdr.SizeOfSlice(o.Options)
-}
-
-func (o IndexMessage) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o IndexMessage) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o IndexMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Folder); l > 256 {
-		return xdr.ElementSizeExceeded("Folder", l, 256)
-	}
-	m.MarshalString(o.Folder)
-	if l := len(o.Files); l > 1000000 {
-		return xdr.ElementSizeExceeded("Files", l, 1000000)
-	}
-	m.MarshalUint32(uint32(len(o.Files)))
-	for i := range o.Files {
-		if err := o.Files[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	m.MarshalUint32(o.Flags)
-	if l := len(o.Options); l > 64 {
-		return xdr.ElementSizeExceeded("Options", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Options)))
-	for i := range o.Options {
-		if err := o.Options[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *IndexMessage) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *IndexMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Folder = u.UnmarshalStringMax(256)
-	_FilesSize := int(u.UnmarshalUint32())
-	if _FilesSize < 0 {
-		return xdr.ElementSizeExceeded("Files", _FilesSize, 1000000)
-	} else if _FilesSize == 0 {
-		o.Files = nil
-	} else {
-		if _FilesSize > 1000000 {
-			return xdr.ElementSizeExceeded("Files", _FilesSize, 1000000)
-		}
-		if _FilesSize <= len(o.Files) {
-			o.Files = o.Files[:_FilesSize]
-		} else {
-			o.Files = make([]FileInfo, _FilesSize)
-		}
-		for i := range o.Files {
-			(&o.Files[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	o.Flags = u.UnmarshalUint32()
-	_OptionsSize := int(u.UnmarshalUint32())
-	if _OptionsSize < 0 {
-		return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-	} else if _OptionsSize == 0 {
-		o.Options = nil
-	} else {
-		if _OptionsSize > 64 {
-			return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-		}
-		if _OptionsSize <= len(o.Options) {
-			o.Options = o.Options[:_OptionsSize]
-		} else {
-			o.Options = make([]Option, _OptionsSize)
-		}
-		for i := range o.Options {
-			(&o.Options[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-FileInfo Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Name (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Flags                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                                                               |
-+                      Modified (64 bits)                       +
-|                                                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                       Vector Structure                        \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                                                               |
-+                    Local Version (64 bits)                    +
-|                                                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Blocks                        |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\               Zero or more BlockInfo Structures               \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct FileInfo {
-	string Name<8192>;
-	unsigned int Flags;
-	hyper Modified;
-	Vector Version;
-	hyper LocalVersion;
-	BlockInfo Blocks<10000000>;
-}
-
-*/
-
-func (o FileInfo) XDRSize() int {
-	return 4 + len(o.Name) + xdr.Padding(len(o.Name)) + 4 + 8 +
-		o.Version.XDRSize() + 8 +
-		4 + xdr.SizeOfSlice(o.Blocks)
-}
-
-func (o FileInfo) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o FileInfo) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o FileInfo) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Name); l > 8192 {
-		return xdr.ElementSizeExceeded("Name", l, 8192)
-	}
-	m.MarshalString(o.Name)
-	m.MarshalUint32(o.Flags)
-	m.MarshalUint64(uint64(o.Modified))
-	if err := o.Version.MarshalXDRInto(m); err != nil {
-		return err
-	}
-	m.MarshalUint64(uint64(o.LocalVersion))
-	if l := len(o.Blocks); l > 10000000 {
-		return xdr.ElementSizeExceeded("Blocks", l, 10000000)
-	}
-	m.MarshalUint32(uint32(len(o.Blocks)))
-	for i := range o.Blocks {
-		if err := o.Blocks[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *FileInfo) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *FileInfo) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Name = u.UnmarshalStringMax(8192)
-	o.Flags = u.UnmarshalUint32()
-	o.Modified = int64(u.UnmarshalUint64())
-	(&o.Version).UnmarshalXDRFrom(u)
-	o.LocalVersion = int64(u.UnmarshalUint64())
-	_BlocksSize := int(u.UnmarshalUint32())
-	if _BlocksSize < 0 {
-		return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 10000000)
-	} else if _BlocksSize == 0 {
-		o.Blocks = nil
-	} else {
-		if _BlocksSize > 10000000 {
-			return xdr.ElementSizeExceeded("Blocks", _BlocksSize, 10000000)
-		}
-		if _BlocksSize <= len(o.Blocks) {
-			o.Blocks = o.Blocks[:_BlocksSize]
-		} else {
-			o.Blocks = make([]BlockInfo, _BlocksSize)
-		}
-		for i := range o.Blocks {
-			(&o.Blocks[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-BlockInfo Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Size                              |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Hash (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct BlockInfo {
-	int Size;
-	opaque Hash<64>;
-}
-
-*/
-
-func (o BlockInfo) XDRSize() int {
-	return 4 +
-		4 + len(o.Hash) + xdr.Padding(len(o.Hash))
-}
-
-func (o BlockInfo) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o BlockInfo) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o BlockInfo) MarshalXDRInto(m *xdr.Marshaller) error {
-	m.MarshalUint32(uint32(o.Size))
-	if l := len(o.Hash); l > 64 {
-		return xdr.ElementSizeExceeded("Hash", l, 64)
-	}
-	m.MarshalBytes(o.Hash)
-	return m.Error
-}
-
-func (o *BlockInfo) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *BlockInfo) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Size = int32(u.UnmarshalUint32())
-	o.Hash = u.UnmarshalBytesMax(64)
-	return u.Error
-}
-
-/*
-
-RequestMessage Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 Folder (length + padded data)                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Name (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                                                               |
-+                       Offset (64 bits)                        +
-|                                                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Size                              |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Hash (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Flags                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Options                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Option Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct RequestMessage {
-	string Folder<256>;
-	string Name<8192>;
-	hyper Offset;
-	int Size;
-	opaque Hash<64>;
-	unsigned int Flags;
-	Option Options<64>;
-}
-
-*/
-
-func (o RequestMessage) XDRSize() int {
-	return 4 + len(o.Folder) + xdr.Padding(len(o.Folder)) +
-		4 + len(o.Name) + xdr.Padding(len(o.Name)) + 8 + 4 +
-		4 + len(o.Hash) + xdr.Padding(len(o.Hash)) + 4 +
-		4 + xdr.SizeOfSlice(o.Options)
-}
-
-func (o RequestMessage) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o RequestMessage) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o RequestMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Folder); l > 256 {
-		return xdr.ElementSizeExceeded("Folder", l, 256)
-	}
-	m.MarshalString(o.Folder)
-	if l := len(o.Name); l > 8192 {
-		return xdr.ElementSizeExceeded("Name", l, 8192)
-	}
-	m.MarshalString(o.Name)
-	m.MarshalUint64(uint64(o.Offset))
-	m.MarshalUint32(uint32(o.Size))
-	if l := len(o.Hash); l > 64 {
-		return xdr.ElementSizeExceeded("Hash", l, 64)
-	}
-	m.MarshalBytes(o.Hash)
-	m.MarshalUint32(o.Flags)
-	if l := len(o.Options); l > 64 {
-		return xdr.ElementSizeExceeded("Options", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Options)))
-	for i := range o.Options {
-		if err := o.Options[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *RequestMessage) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *RequestMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Folder = u.UnmarshalStringMax(256)
-	o.Name = u.UnmarshalStringMax(8192)
-	o.Offset = int64(u.UnmarshalUint64())
-	o.Size = int32(u.UnmarshalUint32())
-	o.Hash = u.UnmarshalBytesMax(64)
-	o.Flags = u.UnmarshalUint32()
-	_OptionsSize := int(u.UnmarshalUint32())
-	if _OptionsSize < 0 {
-		return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-	} else if _OptionsSize == 0 {
-		o.Options = nil
-	} else {
-		if _OptionsSize > 64 {
-			return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-		}
-		if _OptionsSize <= len(o.Options) {
-			o.Options = o.Options[:_OptionsSize]
-		} else {
-			o.Options = make([]Option, _OptionsSize)
-		}
-		for i := range o.Options {
-			(&o.Options[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-ResponseMessage Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Data (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Code                              |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct ResponseMessage {
-	opaque Data<>;
-	int Code;
-}
-
-*/
-
-func (o ResponseMessage) XDRSize() int {
-	return 4 + len(o.Data) + xdr.Padding(len(o.Data)) + 4
-}
-
-func (o ResponseMessage) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o ResponseMessage) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o ResponseMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	m.MarshalBytes(o.Data)
-	m.MarshalUint32(uint32(o.Code))
-	return m.Error
-}
-
-func (o *ResponseMessage) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *ResponseMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Data = u.UnmarshalBytes()
-	o.Code = int32(u.UnmarshalUint32())
-	return u.Error
-}
-
-/*
-
-ClusterConfigMessage Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Folders                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Folder Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Options                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Option Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct ClusterConfigMessage {
-	Folder Folders<1000000>;
-	Option Options<64>;
-}
-
-*/
-
-func (o ClusterConfigMessage) XDRSize() int {
-	return 4 + xdr.SizeOfSlice(o.Folders) +
-		4 + xdr.SizeOfSlice(o.Options)
-}
-
-func (o ClusterConfigMessage) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o ClusterConfigMessage) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o ClusterConfigMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Folders); l > 1000000 {
-		return xdr.ElementSizeExceeded("Folders", l, 1000000)
-	}
-	m.MarshalUint32(uint32(len(o.Folders)))
-	for i := range o.Folders {
-		if err := o.Folders[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	if l := len(o.Options); l > 64 {
-		return xdr.ElementSizeExceeded("Options", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Options)))
-	for i := range o.Options {
-		if err := o.Options[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *ClusterConfigMessage) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *ClusterConfigMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	_FoldersSize := int(u.UnmarshalUint32())
-	if _FoldersSize < 0 {
-		return xdr.ElementSizeExceeded("Folders", _FoldersSize, 1000000)
-	} else if _FoldersSize == 0 {
-		o.Folders = nil
-	} else {
-		if _FoldersSize > 1000000 {
-			return xdr.ElementSizeExceeded("Folders", _FoldersSize, 1000000)
-		}
-		if _FoldersSize <= len(o.Folders) {
-			o.Folders = o.Folders[:_FoldersSize]
-		} else {
-			o.Folders = make([]Folder, _FoldersSize)
-		}
-		for i := range o.Folders {
-			(&o.Folders[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	_OptionsSize := int(u.UnmarshalUint32())
-	if _OptionsSize < 0 {
-		return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-	} else if _OptionsSize == 0 {
-		o.Options = nil
-	} else {
-		if _OptionsSize > 64 {
-			return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-		}
-		if _OptionsSize <= len(o.Options) {
-			o.Options = o.Options[:_OptionsSize]
-		} else {
-			o.Options = make([]Option, _OptionsSize)
-		}
-		for i := range o.Options {
-			(&o.Options[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-DownloadProgressMessage Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 Folder (length + padded data)                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Updates                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\      Zero or more FileDownloadProgressUpdate Structures       \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Flags                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Options                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Option Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct DownloadProgressMessage {
-	string Folder<64>;
-	FileDownloadProgressUpdate Updates<1000000>;
-	unsigned int Flags;
-	Option Options<64>;
-}
-
-*/
-
-func (o DownloadProgressMessage) XDRSize() int {
-	return 4 + len(o.Folder) + xdr.Padding(len(o.Folder)) +
-		4 + xdr.SizeOfSlice(o.Updates) + 4 +
-		4 + xdr.SizeOfSlice(o.Options)
-}
-
-func (o DownloadProgressMessage) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o DownloadProgressMessage) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o DownloadProgressMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Folder); l > 64 {
-		return xdr.ElementSizeExceeded("Folder", l, 64)
-	}
-	m.MarshalString(o.Folder)
-	if l := len(o.Updates); l > 1000000 {
-		return xdr.ElementSizeExceeded("Updates", l, 1000000)
-	}
-	m.MarshalUint32(uint32(len(o.Updates)))
-	for i := range o.Updates {
-		if err := o.Updates[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	m.MarshalUint32(o.Flags)
-	if l := len(o.Options); l > 64 {
-		return xdr.ElementSizeExceeded("Options", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Options)))
-	for i := range o.Options {
-		if err := o.Options[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *DownloadProgressMessage) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *DownloadProgressMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Folder = u.UnmarshalStringMax(64)
-	_UpdatesSize := int(u.UnmarshalUint32())
-	if _UpdatesSize < 0 {
-		return xdr.ElementSizeExceeded("Updates", _UpdatesSize, 1000000)
-	} else if _UpdatesSize == 0 {
-		o.Updates = nil
-	} else {
-		if _UpdatesSize > 1000000 {
-			return xdr.ElementSizeExceeded("Updates", _UpdatesSize, 1000000)
-		}
-		if _UpdatesSize <= len(o.Updates) {
-			o.Updates = o.Updates[:_UpdatesSize]
-		} else {
-			o.Updates = make([]FileDownloadProgressUpdate, _UpdatesSize)
-		}
-		for i := range o.Updates {
-			(&o.Updates[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	o.Flags = u.UnmarshalUint32()
-	_OptionsSize := int(u.UnmarshalUint32())
-	if _OptionsSize < 0 {
-		return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-	} else if _OptionsSize == 0 {
-		o.Options = nil
-	} else {
-		if _OptionsSize > 64 {
-			return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-		}
-		if _OptionsSize <= len(o.Options) {
-			o.Options = o.Options[:_OptionsSize]
-		} else {
-			o.Options = make([]Option, _OptionsSize)
-		}
-		for i := range o.Options {
-			(&o.Options[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-Folder Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                   ID (length + padded data)                   \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 Label (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Devices                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Device Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Flags                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Options                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Option Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct Folder {
-	string ID<256>;
-	string Label<256>;
-	Device Devices<1000000>;
-	unsigned int Flags;
-	Option Options<64>;
-}
-
-*/
-
-func (o Folder) XDRSize() int {
-	return 4 + len(o.ID) + xdr.Padding(len(o.ID)) +
-		4 + len(o.Label) + xdr.Padding(len(o.Label)) +
-		4 + xdr.SizeOfSlice(o.Devices) + 4 +
-		4 + xdr.SizeOfSlice(o.Options)
-}
-
-func (o Folder) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o Folder) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o Folder) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.ID); l > 256 {
-		return xdr.ElementSizeExceeded("ID", l, 256)
-	}
-	m.MarshalString(o.ID)
-	if l := len(o.Label); l > 256 {
-		return xdr.ElementSizeExceeded("Label", l, 256)
-	}
-	m.MarshalString(o.Label)
-	if l := len(o.Devices); l > 1000000 {
-		return xdr.ElementSizeExceeded("Devices", l, 1000000)
-	}
-	m.MarshalUint32(uint32(len(o.Devices)))
-	for i := range o.Devices {
-		if err := o.Devices[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	m.MarshalUint32(o.Flags)
-	if l := len(o.Options); l > 64 {
-		return xdr.ElementSizeExceeded("Options", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Options)))
-	for i := range o.Options {
-		if err := o.Options[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *Folder) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *Folder) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.ID = u.UnmarshalStringMax(256)
-	o.Label = u.UnmarshalStringMax(256)
-	_DevicesSize := int(u.UnmarshalUint32())
-	if _DevicesSize < 0 {
-		return xdr.ElementSizeExceeded("Devices", _DevicesSize, 1000000)
-	} else if _DevicesSize == 0 {
-		o.Devices = nil
-	} else {
-		if _DevicesSize > 1000000 {
-			return xdr.ElementSizeExceeded("Devices", _DevicesSize, 1000000)
-		}
-		if _DevicesSize <= len(o.Devices) {
-			o.Devices = o.Devices[:_DevicesSize]
-		} else {
-			o.Devices = make([]Device, _DevicesSize)
-		}
-		for i := range o.Devices {
-			(&o.Devices[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	o.Flags = u.UnmarshalUint32()
-	_OptionsSize := int(u.UnmarshalUint32())
-	if _OptionsSize < 0 {
-		return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-	} else if _OptionsSize == 0 {
-		o.Options = nil
-	} else {
-		if _OptionsSize > 64 {
-			return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-		}
-		if _OptionsSize <= len(o.Options) {
-			o.Options = o.Options[:_OptionsSize]
-		} else {
-			o.Options = make([]Option, _OptionsSize)
-		}
-		for i := range o.Options {
-			(&o.Options[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-Device Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                   ID (length + padded data)                   \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Name (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                      Number of Addresses                      |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-/                                                               /
-\               Addresses (length + padded data)                \
-/                                                               /
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                          Compression                          |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\               Cert Name (length + padded data)                \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                                                               |
-+                  Max Local Version (64 bits)                  +
-|                                                               |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Flags                             |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                       Number of Options                       |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                Zero or more Option Structures                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct Device {
-	opaque ID<32>;
-	string Name<64>;
-	string Addresses<64>;
-	unsigned int Compression;
-	string CertName<64>;
-	hyper MaxLocalVersion;
-	unsigned int Flags;
-	Option Options<64>;
-}
-
-*/
-
-func (o Device) XDRSize() int {
-	return 4 + len(o.ID) + xdr.Padding(len(o.ID)) +
-		4 + len(o.Name) + xdr.Padding(len(o.Name)) +
-		4 + xdr.SizeOfSlice(o.Addresses) + 4 +
-		4 + len(o.CertName) + xdr.Padding(len(o.CertName)) + 8 + 4 +
-		4 + xdr.SizeOfSlice(o.Options)
-}
-
-func (o Device) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o Device) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o Device) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.ID); l > 32 {
-		return xdr.ElementSizeExceeded("ID", l, 32)
-	}
-	m.MarshalBytes(o.ID)
-	if l := len(o.Name); l > 64 {
-		return xdr.ElementSizeExceeded("Name", l, 64)
-	}
-	m.MarshalString(o.Name)
-	if l := len(o.Addresses); l > 64 {
-		return xdr.ElementSizeExceeded("Addresses", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Addresses)))
-	for i := range o.Addresses {
-		m.MarshalString(o.Addresses[i])
-	}
-	m.MarshalUint32(o.Compression)
-	if l := len(o.CertName); l > 64 {
-		return xdr.ElementSizeExceeded("CertName", l, 64)
-	}
-	m.MarshalString(o.CertName)
-	m.MarshalUint64(uint64(o.MaxLocalVersion))
-	m.MarshalUint32(o.Flags)
-	if l := len(o.Options); l > 64 {
-		return xdr.ElementSizeExceeded("Options", l, 64)
-	}
-	m.MarshalUint32(uint32(len(o.Options)))
-	for i := range o.Options {
-		if err := o.Options[i].MarshalXDRInto(m); err != nil {
-			return err
-		}
-	}
-	return m.Error
-}
-
-func (o *Device) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *Device) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.ID = u.UnmarshalBytesMax(32)
-	o.Name = u.UnmarshalStringMax(64)
-	_AddressesSize := int(u.UnmarshalUint32())
-	if _AddressesSize < 0 {
-		return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 64)
-	} else if _AddressesSize == 0 {
-		o.Addresses = nil
-	} else {
-		if _AddressesSize > 64 {
-			return xdr.ElementSizeExceeded("Addresses", _AddressesSize, 64)
-		}
-		if _AddressesSize <= len(o.Addresses) {
-			for i := _AddressesSize; i < len(o.Addresses); i++ {
-				o.Addresses[i] = ""
-			}
-			o.Addresses = o.Addresses[:_AddressesSize]
-		} else {
-			o.Addresses = make([]string, _AddressesSize)
-		}
-		for i := range o.Addresses {
-			o.Addresses[i] = u.UnmarshalStringMax(2083)
-		}
-	}
-	o.Compression = u.UnmarshalUint32()
-	o.CertName = u.UnmarshalStringMax(64)
-	o.MaxLocalVersion = int64(u.UnmarshalUint64())
-	o.Flags = u.UnmarshalUint32()
-	_OptionsSize := int(u.UnmarshalUint32())
-	if _OptionsSize < 0 {
-		return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-	} else if _OptionsSize == 0 {
-		o.Options = nil
-	} else {
-		if _OptionsSize > 64 {
-			return xdr.ElementSizeExceeded("Options", _OptionsSize, 64)
-		}
-		if _OptionsSize <= len(o.Options) {
-			o.Options = o.Options[:_OptionsSize]
-		} else {
-			o.Options = make([]Option, _OptionsSize)
-		}
-		for i := range o.Options {
-			(&o.Options[i]).UnmarshalXDRFrom(u)
-		}
-	}
-	return u.Error
-}
-
-/*
-
-FileDownloadProgressUpdate Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                          Update Type                          |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Name (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                       Vector Structure                        \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                    Number of Block Indexes                    |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-|                    Block Indexes (n items)                    |
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct FileDownloadProgressUpdate {
-	unsigned int UpdateType;
-	string Name<8192>;
-	Vector Version;
-	int BlockIndexes<1000000>;
-}
-
-*/
-
-func (o FileDownloadProgressUpdate) XDRSize() int {
-	return 4 +
-		4 + len(o.Name) + xdr.Padding(len(o.Name)) +
-		o.Version.XDRSize() +
-		4 + len(o.BlockIndexes)*4
-}
-
-func (o FileDownloadProgressUpdate) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o FileDownloadProgressUpdate) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o FileDownloadProgressUpdate) MarshalXDRInto(m *xdr.Marshaller) error {
-	m.MarshalUint32(o.UpdateType)
-	if l := len(o.Name); l > 8192 {
-		return xdr.ElementSizeExceeded("Name", l, 8192)
-	}
-	m.MarshalString(o.Name)
-	if err := o.Version.MarshalXDRInto(m); err != nil {
-		return err
-	}
-	if l := len(o.BlockIndexes); l > 1000000 {
-		return xdr.ElementSizeExceeded("BlockIndexes", l, 1000000)
-	}
-	m.MarshalUint32(uint32(len(o.BlockIndexes)))
-	for i := range o.BlockIndexes {
-		m.MarshalUint32(uint32(o.BlockIndexes[i]))
-	}
-	return m.Error
-}
-
-func (o *FileDownloadProgressUpdate) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *FileDownloadProgressUpdate) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.UpdateType = u.UnmarshalUint32()
-	o.Name = u.UnmarshalStringMax(8192)
-	(&o.Version).UnmarshalXDRFrom(u)
-	_BlockIndexesSize := int(u.UnmarshalUint32())
-	if _BlockIndexesSize < 0 {
-		return xdr.ElementSizeExceeded("BlockIndexes", _BlockIndexesSize, 1000000)
-	} else if _BlockIndexesSize == 0 {
-		o.BlockIndexes = nil
-	} else {
-		if _BlockIndexesSize > 1000000 {
-			return xdr.ElementSizeExceeded("BlockIndexes", _BlockIndexesSize, 1000000)
-		}
-		if _BlockIndexesSize <= len(o.BlockIndexes) {
-			o.BlockIndexes = o.BlockIndexes[:_BlockIndexesSize]
-		} else {
-			o.BlockIndexes = make([]int32, _BlockIndexesSize)
-		}
-		for i := range o.BlockIndexes {
-			o.BlockIndexes[i] = int32(u.UnmarshalUint32())
-		}
-	}
-	return u.Error
-}
-
-/*
-
-Option Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                  Key (length + padded data)                   \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 Value (length + padded data)                  \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct Option {
-	string Key<64>;
-	string Value<1024>;
-}
-
-*/
-
-func (o Option) XDRSize() int {
-	return 4 + len(o.Key) + xdr.Padding(len(o.Key)) +
-		4 + len(o.Value) + xdr.Padding(len(o.Value))
-}
-
-func (o Option) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o Option) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o Option) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Key); l > 64 {
-		return xdr.ElementSizeExceeded("Key", l, 64)
-	}
-	m.MarshalString(o.Key)
-	if l := len(o.Value); l > 1024 {
-		return xdr.ElementSizeExceeded("Value", l, 1024)
-	}
-	m.MarshalString(o.Value)
-	return m.Error
-}
-
-func (o *Option) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *Option) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Key = u.UnmarshalStringMax(64)
-	o.Value = u.UnmarshalStringMax(1024)
-	return u.Error
-}
-
-/*
-
-CloseMessage Structure:
-
- 0                   1                   2                   3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-/                                                               /
-\                 Reason (length + padded data)                 \
-/                                                               /
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-|                             Code                              |
-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-
-struct CloseMessage {
-	string Reason<1024>;
-	int Code;
-}
-
-*/
-
-func (o CloseMessage) XDRSize() int {
-	return 4 + len(o.Reason) + xdr.Padding(len(o.Reason)) + 4
-}
-
-func (o CloseMessage) MarshalXDR() ([]byte, error) {
-	buf := make([]byte, o.XDRSize())
-	m := &xdr.Marshaller{Data: buf}
-	return buf, o.MarshalXDRInto(m)
-}
-
-func (o CloseMessage) MustMarshalXDR() []byte {
-	bs, err := o.MarshalXDR()
-	if err != nil {
-		panic(err)
-	}
-	return bs
-}
-
-func (o CloseMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	if l := len(o.Reason); l > 1024 {
-		return xdr.ElementSizeExceeded("Reason", l, 1024)
-	}
-	m.MarshalString(o.Reason)
-	m.MarshalUint32(uint32(o.Code))
-	return m.Error
-}
-
-func (o *CloseMessage) UnmarshalXDR(bs []byte) error {
-	u := &xdr.Unmarshaller{Data: bs}
-	return o.UnmarshalXDRFrom(u)
-}
-func (o *CloseMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	o.Reason = u.UnmarshalStringMax(1024)
-	o.Code = int32(u.UnmarshalUint32())
-	return u.Error
-}
-
-/*
-
-EmptyMessage Structure:
-(contains no fields)
-
-
-struct EmptyMessage {
-}
-
-*/
-
-func (o EmptyMessage) XDRSize() int {
-	return 0
-}
-func (o EmptyMessage) MarshalXDR() ([]byte, error) {
-	return nil, nil
-}
-
-func (o EmptyMessage) MustMarshalXDR() []byte {
-	return nil
-}
-
-func (o EmptyMessage) MarshalXDRInto(m *xdr.Marshaller) error {
-	return nil
-}
-
-func (o *EmptyMessage) UnmarshalXDR(bs []byte) error {
-	return nil
-}
-
-func (o *EmptyMessage) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	return nil
-}

+ 6 - 6
lib/protocol/nativemodel_darwin.go

@@ -12,21 +12,21 @@ type nativeModel struct {
 	Model
 	Model
 }
 }
 
 
-func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo) {
 	for i := range files {
 	for i := range files {
 		files[i].Name = norm.NFD.String(files[i].Name)
 		files[i].Name = norm.NFD.String(files[i].Name)
 	}
 	}
-	m.Model.Index(deviceID, folder, files, flags, options)
+	m.Model.Index(deviceID, folder, files)
 }
 }
 
 
-func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) {
 	for i := range files {
 	for i := range files {
 		files[i].Name = norm.NFD.String(files[i].Name)
 		files[i].Name = norm.NFD.String(files[i].Name)
 	}
 	}
-	m.Model.IndexUpdate(deviceID, folder, files, flags, options)
+	m.Model.IndexUpdate(deviceID, folder, files)
 }
 }
 
 
-func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error {
+func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
 	name = norm.NFD.String(name)
 	name = norm.NFD.String(name)
-	return m.Model.Request(deviceID, folder, name, offset, hash, flags, options, buf)
+	return m.Model.Request(deviceID, folder, name, offset, hash, fromTemporary, buf)
 }
 }

+ 7 - 7
lib/protocol/nativemodel_windows.go

@@ -24,19 +24,19 @@ type nativeModel struct {
 	Model
 	Model
 }
 }
 
 
-func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (m nativeModel) Index(deviceID DeviceID, folder string, files []FileInfo) {
 	fixupFiles(folder, files)
 	fixupFiles(folder, files)
-	m.Model.Index(deviceID, folder, files, flags, options)
+	m.Model.Index(deviceID, folder, files)
 }
 }
 
 
-func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option) {
+func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) {
 	fixupFiles(folder, files)
 	fixupFiles(folder, files)
-	m.Model.IndexUpdate(deviceID, folder, files, flags, options)
+	m.Model.IndexUpdate(deviceID, folder, files)
 }
 }
 
 
-func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error {
+func (m nativeModel) Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error {
 	name = filepath.FromSlash(name)
 	name = filepath.FromSlash(name)
-	return m.Model.Request(deviceID, folder, name, offset, hash, flags, options, buf)
+	return m.Model.Request(deviceID, folder, name, offset, hash, fromTemporary, buf)
 }
 }
 
 
 func fixupFiles(folder string, files []FileInfo) {
 func fixupFiles(folder string, files []FileInfo) {
@@ -47,7 +47,7 @@ func fixupFiles(folder string, files []FileInfo) {
 				// can't possibly exist here anyway.
 				// can't possibly exist here anyway.
 				continue
 				continue
 			}
 			}
-			files[i].Flags |= FlagInvalid
+			files[i].Invalid = true
 			l.Warnf("File name %q (folder %q) contains invalid characters; marked as invalid.", f.Name, folder)
 			l.Warnf("File name %q (folder %q) contains invalid characters; marked as invalid.", f.Name, folder)
 		}
 		}
 		files[i].Name = filepath.FromSlash(files[i].Name)
 		files[i].Name = filepath.FromSlash(files[i].Name)

+ 356 - 349
lib/protocol/protocol.go

@@ -4,7 +4,6 @@ package protocol
 
 
 import (
 import (
 	"encoding/binary"
 	"encoding/binary"
-	"encoding/hex"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
@@ -12,26 +11,16 @@ import (
 	"time"
 	"time"
 
 
 	lz4 "github.com/bkaradzic/go-lz4"
 	lz4 "github.com/bkaradzic/go-lz4"
-	"github.com/calmh/xdr"
 )
 )
 
 
 const (
 const (
 	// BlockSize is the standard ata block size (128 KiB)
 	// BlockSize is the standard ata block size (128 KiB)
 	BlockSize = 128 << 10
 	BlockSize = 128 << 10
 
 
-	// MaxMessageLen is the largest message size allowed on the wire. (512 MiB)
-	MaxMessageLen = 64 << 23
-)
+	// MaxMessageLen is the largest message size allowed on the wire. (500 MB)
+	MaxMessageLen = 500 * 1000 * 1000
 
 
-const (
-	messageTypeClusterConfig    = 0
-	messageTypeIndex            = 1
-	messageTypeRequest          = 2
-	messageTypeResponse         = 3
-	messageTypePing             = 4
-	messageTypeIndexUpdate      = 6
-	messageTypeClose            = 7
-	messageTypeDownloadProgress = 8
+	hdrSize = 6
 )
 )
 
 
 const (
 const (
@@ -39,31 +28,11 @@ const (
 	stateReady
 	stateReady
 )
 )
 
 
-// FileInfo flags
-const (
-	FlagDeleted              uint32 = 1 << 12 // bit 19 in MSB order with the first bit being #0
-	FlagInvalid                     = 1 << 13 // bit 18
-	FlagDirectory                   = 1 << 14 // bit 17
-	FlagNoPermBits                  = 1 << 15 // bit 16
-	FlagSymlink                     = 1 << 16 // bit 15
-	FlagSymlinkMissingTarget        = 1 << 17 // bit 14
-
-	FlagsAll = (1 << 18) - 1
-
-	SymlinkTypeMask = FlagDirectory | FlagSymlinkMissingTarget
-)
-
 // Request message flags
 // Request message flags
 const (
 const (
 	FlagFromTemporary uint32 = 1 << iota
 	FlagFromTemporary uint32 = 1 << iota
 )
 )
 
 
-// FileDownloadProgressUpdate update types
-const (
-	UpdateTypeAppend uint32 = iota
-	UpdateTypeForget
-)
-
 // ClusterConfigMessage.Folders flags
 // ClusterConfigMessage.Folders flags
 const (
 const (
 	FlagFolderReadOnly            uint32 = 1 << 0
 	FlagFolderReadOnly            uint32 = 1 << 0
@@ -85,35 +54,33 @@ var (
 	ErrClosed               = errors.New("connection closed")
 	ErrClosed               = errors.New("connection closed")
 	ErrTimeout              = errors.New("read timeout")
 	ErrTimeout              = errors.New("read timeout")
 	ErrSwitchingConnections = errors.New("switching connections")
 	ErrSwitchingConnections = errors.New("switching connections")
+	errUnknownMessage       = errors.New("unknown message")
 )
 )
 
 
-// Specific variants of empty messages...
-type pingMessage struct{ EmptyMessage }
-
 type Model interface {
 type Model interface {
 	// An index was received from the peer device
 	// An index was received from the peer device
-	Index(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option)
+	Index(deviceID DeviceID, folder string, files []FileInfo)
 	// An index update was received from the peer device
 	// An index update was received from the peer device
-	IndexUpdate(deviceID DeviceID, folder string, files []FileInfo, flags uint32, options []Option)
+	IndexUpdate(deviceID DeviceID, folder string, files []FileInfo)
 	// A request was made by the peer device
 	// A request was made by the peer device
-	Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, flags uint32, options []Option, buf []byte) error
+	Request(deviceID DeviceID, folder string, name string, offset int64, hash []byte, fromTemporary bool, buf []byte) error
 	// A cluster configuration message was received
 	// A cluster configuration message was received
-	ClusterConfig(deviceID DeviceID, config ClusterConfigMessage)
+	ClusterConfig(deviceID DeviceID, config ClusterConfig)
 	// The peer device closed the connection
 	// The peer device closed the connection
 	Close(deviceID DeviceID, err error)
 	Close(deviceID DeviceID, err error)
 	// The peer device sent progress updates for the files it is currently downloading
 	// The peer device sent progress updates for the files it is currently downloading
-	DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate, flags uint32, options []Option)
+	DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate)
 }
 }
 
 
 type Connection interface {
 type Connection interface {
 	Start()
 	Start()
 	ID() DeviceID
 	ID() DeviceID
 	Name() string
 	Name() string
-	Index(folder string, files []FileInfo, flags uint32, options []Option) error
-	IndexUpdate(folder string, files []FileInfo, flags uint32, options []Option) error
+	Index(folder string, files []FileInfo) error
+	IndexUpdate(folder string, files []FileInfo) error
 	Request(folder string, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error)
 	Request(folder string, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error)
-	ClusterConfig(config ClusterConfigMessage)
-	DownloadProgress(folder string, updates []FileDownloadProgressUpdate, flags uint32, options []Option)
+	ClusterConfig(config ClusterConfig)
+	DownloadProgress(folder string, updates []FileDownloadProgressUpdate)
 	Statistics() Statistics
 	Statistics() Statistics
 	Closed() bool
 	Closed() bool
 }
 }
@@ -126,19 +93,19 @@ type rawConnection struct {
 	cr *countingReader
 	cr *countingReader
 	cw *countingWriter
 	cw *countingWriter
 
 
-	awaiting    [4096]chan asyncResult
+	awaiting    map[int32]chan asyncResult
 	awaitingMut sync.Mutex
 	awaitingMut sync.Mutex
 
 
 	idxMut sync.Mutex // ensures serialization of Index calls
 	idxMut sync.Mutex // ensures serialization of Index calls
 
 
-	nextID      chan int
-	outbox      chan hdrMsg
+	nextID    int32
+	nextIDMut sync.Mutex
+
+	outbox      chan asyncMessage
 	closed      chan struct{}
 	closed      chan struct{}
 	once        sync.Once
 	once        sync.Once
 	pool        sync.Pool
 	pool        sync.Pool
 	compression Compression
 	compression Compression
-
-	readerBuf []byte // used & reused by readMessage
 }
 }
 
 
 type asyncResult struct {
 type asyncResult struct {
@@ -146,19 +113,16 @@ type asyncResult struct {
 	err error
 	err error
 }
 }
 
 
-type hdrMsg struct {
-	hdr  header
-	msg  encodable
-	done chan struct{}
-}
-
-type encodable interface {
-	MarshalXDRInto(m *xdr.Marshaller) error
-	XDRSize() int
+type message interface {
+	ProtoSize() int
+	Marshal() ([]byte, error)
+	MarshalTo([]byte) (int, error)
+	Unmarshal([]byte) error
 }
 }
 
 
-type isEofer interface {
-	IsEOF() bool
+type asyncMessage struct {
+	msg  message
+	done chan struct{} // done closes when we're done marshalling the message and it's contents can be reused
 }
 }
 
 
 const (
 const (
@@ -170,6 +134,12 @@ const (
 	ReceiveTimeout = 300 * time.Second
 	ReceiveTimeout = 300 * time.Second
 )
 )
 
 
+// A buffer pool for global use. We don't allocate smaller buffers than 64k,
+// in the hope of being able to reuse them later.
+var buffers = bufferPool{
+	minSize: 64 << 10,
+}
+
 func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) Connection {
 func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) Connection {
 	cr := &countingReader{Reader: reader}
 	cr := &countingReader{Reader: reader}
 	cw := &countingWriter{Writer: writer}
 	cw := &countingWriter{Writer: writer}
@@ -180,8 +150,8 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiv
 		receiver: nativeModel{receiver},
 		receiver: nativeModel{receiver},
 		cr:       cr,
 		cr:       cr,
 		cw:       cw,
 		cw:       cw,
-		outbox:   make(chan hdrMsg),
-		nextID:   make(chan int),
+		awaiting: make(map[int32]chan asyncResult),
+		outbox:   make(chan asyncMessage),
 		closed:   make(chan struct{}),
 		closed:   make(chan struct{}),
 		pool: sync.Pool{
 		pool: sync.Pool{
 			New: func() interface{} {
 			New: func() interface{} {
@@ -201,7 +171,6 @@ func (c *rawConnection) Start() {
 	go c.writerLoop()
 	go c.writerLoop()
 	go c.pingSender()
 	go c.pingSender()
 	go c.pingReceiver()
 	go c.pingReceiver()
-	go c.idGenerator()
 }
 }
 
 
 func (c *rawConnection) ID() DeviceID {
 func (c *rawConnection) ID() DeviceID {
@@ -213,36 +182,32 @@ func (c *rawConnection) Name() string {
 }
 }
 
 
 // Index writes the list of file information to the connected peer device
 // Index writes the list of file information to the connected peer device
-func (c *rawConnection) Index(folder string, idx []FileInfo, flags uint32, options []Option) error {
+func (c *rawConnection) Index(folder string, idx []FileInfo) error {
 	select {
 	select {
 	case <-c.closed:
 	case <-c.closed:
 		return ErrClosed
 		return ErrClosed
 	default:
 	default:
 	}
 	}
 	c.idxMut.Lock()
 	c.idxMut.Lock()
-	c.send(-1, messageTypeIndex, IndexMessage{
-		Folder:  folder,
-		Files:   idx,
-		Flags:   flags,
-		Options: options,
+	c.send(&Index{
+		Folder: folder,
+		Files:  idx,
 	}, nil)
 	}, nil)
 	c.idxMut.Unlock()
 	c.idxMut.Unlock()
 	return nil
 	return nil
 }
 }
 
 
 // IndexUpdate writes the list of file information to the connected peer device as an update
 // IndexUpdate writes the list of file information to the connected peer device as an update
-func (c *rawConnection) IndexUpdate(folder string, idx []FileInfo, flags uint32, options []Option) error {
+func (c *rawConnection) IndexUpdate(folder string, idx []FileInfo) error {
 	select {
 	select {
 	case <-c.closed:
 	case <-c.closed:
 		return ErrClosed
 		return ErrClosed
 	default:
 	default:
 	}
 	}
 	c.idxMut.Lock()
 	c.idxMut.Lock()
-	c.send(-1, messageTypeIndexUpdate, IndexMessage{
-		Folder:  folder,
-		Files:   idx,
-		Flags:   flags,
-		Options: options,
+	c.send(&IndexUpdate{
+		Folder: folder,
+		Files:  idx,
 	}, nil)
 	}, nil)
 	c.idxMut.Unlock()
 	c.idxMut.Unlock()
 	return nil
 	return nil
@@ -250,35 +215,27 @@ func (c *rawConnection) IndexUpdate(folder string, idx []FileInfo, flags uint32,
 
 
 // Request returns the bytes for the specified block after fetching them from the connected peer.
 // Request returns the bytes for the specified block after fetching them from the connected peer.
 func (c *rawConnection) Request(folder string, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) {
 func (c *rawConnection) Request(folder string, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) {
-	var id int
-	select {
-	case id = <-c.nextID:
-	case <-c.closed:
-		return nil, ErrClosed
-	}
-
-	var flags uint32
-
-	if fromTemporary {
-		flags |= FlagFromTemporary
-	}
+	c.nextIDMut.Lock()
+	id := c.nextID
+	c.nextID++
+	c.nextIDMut.Unlock()
 
 
 	c.awaitingMut.Lock()
 	c.awaitingMut.Lock()
-	if ch := c.awaiting[id]; ch != nil {
+	if _, ok := c.awaiting[id]; ok {
 		panic("id taken")
 		panic("id taken")
 	}
 	}
 	rc := make(chan asyncResult, 1)
 	rc := make(chan asyncResult, 1)
 	c.awaiting[id] = rc
 	c.awaiting[id] = rc
 	c.awaitingMut.Unlock()
 	c.awaitingMut.Unlock()
 
 
-	ok := c.send(id, messageTypeRequest, RequestMessage{
-		Folder:  folder,
-		Name:    name,
-		Offset:  offset,
-		Size:    int32(size),
-		Hash:    hash,
-		Flags:   flags,
-		Options: nil,
+	ok := c.send(&Request{
+		ID:            id,
+		Folder:        folder,
+		Name:          name,
+		Offset:        offset,
+		Size:          int32(size),
+		Hash:          hash,
+		FromTemporary: fromTemporary,
 	}, nil)
 	}, nil)
 	if !ok {
 	if !ok {
 		return nil, ErrClosed
 		return nil, ErrClosed
@@ -292,8 +249,8 @@ func (c *rawConnection) Request(folder string, name string, offset int64, size i
 }
 }
 
 
 // ClusterConfig send the cluster configuration message to the peer and returns any error
 // ClusterConfig send the cluster configuration message to the peer and returns any error
-func (c *rawConnection) ClusterConfig(config ClusterConfigMessage) {
-	c.send(-1, messageTypeClusterConfig, config, nil)
+func (c *rawConnection) ClusterConfig(config ClusterConfig) {
+	c.send(&config, nil)
 }
 }
 
 
 func (c *rawConnection) Closed() bool {
 func (c *rawConnection) Closed() bool {
@@ -306,24 +263,15 @@ func (c *rawConnection) Closed() bool {
 }
 }
 
 
 // DownloadProgress sends the progress updates for the files that are currently being downloaded.
 // DownloadProgress sends the progress updates for the files that are currently being downloaded.
-func (c *rawConnection) DownloadProgress(folder string, updates []FileDownloadProgressUpdate, flags uint32, options []Option) {
-	c.send(-1, messageTypeDownloadProgress, DownloadProgressMessage{
+func (c *rawConnection) DownloadProgress(folder string, updates []FileDownloadProgressUpdate) {
+	c.send(&DownloadProgress{
 		Folder:  folder,
 		Folder:  folder,
 		Updates: updates,
 		Updates: updates,
-		Flags:   flags,
-		Options: options,
 	}, nil)
 	}, nil)
 }
 }
 
 
 func (c *rawConnection) ping() bool {
 func (c *rawConnection) ping() bool {
-	var id int
-	select {
-	case id = <-c.nextID:
-	case <-c.closed:
-		return false
-	}
-
-	return c.send(id, messageTypePing, nil, nil)
+	return c.send(&Ping{}, nil)
 }
 }
 
 
 func (c *rawConnection) readerLoop() (err error) {
 func (c *rawConnection) readerLoop() (err error) {
@@ -339,199 +287,172 @@ func (c *rawConnection) readerLoop() (err error) {
 		default:
 		default:
 		}
 		}
 
 
-		hdr, msg, err := c.readMessage()
+		msg, err := c.readMessage()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 
 
 		switch msg := msg.(type) {
 		switch msg := msg.(type) {
-		case ClusterConfigMessage:
+		case *ClusterConfig:
+			l.Debugln("read ClusterConfig message")
 			if state != stateInitial {
 			if state != stateInitial {
 				return fmt.Errorf("protocol error: cluster config message in state %d", state)
 				return fmt.Errorf("protocol error: cluster config message in state %d", state)
 			}
 			}
-			go c.receiver.ClusterConfig(c.id, msg)
+			go c.receiver.ClusterConfig(c.id, *msg)
 			state = stateReady
 			state = stateReady
 
 
-		case IndexMessage:
-			switch hdr.msgType {
-			case messageTypeIndex:
-				if state != stateReady {
-					return fmt.Errorf("protocol error: index message in state %d", state)
-				}
-				c.handleIndex(msg)
-				state = stateReady
-
-			case messageTypeIndexUpdate:
-				if state != stateReady {
-					return fmt.Errorf("protocol error: index update message in state %d", state)
-				}
-				c.handleIndexUpdate(msg)
-				state = stateReady
+		case *Index:
+			l.Debugln("read Index message")
+			if state != stateReady {
+				return fmt.Errorf("protocol error: index message in state %d", state)
 			}
 			}
+			c.handleIndex(*msg)
+			state = stateReady
+
+		case *IndexUpdate:
+			l.Debugln("read IndexUpdate message")
+			if state != stateReady {
+				return fmt.Errorf("protocol error: index update message in state %d", state)
+			}
+			c.handleIndexUpdate(*msg)
+			state = stateReady
 
 
-		case RequestMessage:
+		case *Request:
+			l.Debugln("read Request message")
 			if state != stateReady {
 			if state != stateReady {
 				return fmt.Errorf("protocol error: request message in state %d", state)
 				return fmt.Errorf("protocol error: request message in state %d", state)
 			}
 			}
 			// Requests are handled asynchronously
 			// Requests are handled asynchronously
-			go c.handleRequest(hdr.msgID, msg)
+			go c.handleRequest(*msg)
 
 
-		case ResponseMessage:
+		case *Response:
+			l.Debugln("read Response message")
 			if state != stateReady {
 			if state != stateReady {
 				return fmt.Errorf("protocol error: response message in state %d", state)
 				return fmt.Errorf("protocol error: response message in state %d", state)
 			}
 			}
-			c.handleResponse(hdr.msgID, msg)
+			c.handleResponse(*msg)
 
 
-		case DownloadProgressMessage:
+		case *DownloadProgress:
+			l.Debugln("read DownloadProgress message")
 			if state != stateReady {
 			if state != stateReady {
 				return fmt.Errorf("protocol error: response message in state %d", state)
 				return fmt.Errorf("protocol error: response message in state %d", state)
 			}
 			}
-			c.receiver.DownloadProgress(c.id, msg.Folder, msg.Updates, msg.Flags, msg.Options)
+			c.receiver.DownloadProgress(c.id, msg.Folder, msg.Updates)
 
 
-		case pingMessage:
+		case *Ping:
+			l.Debugln("read Ping message")
 			if state != stateReady {
 			if state != stateReady {
 				return fmt.Errorf("protocol error: ping message in state %d", state)
 				return fmt.Errorf("protocol error: ping message in state %d", state)
 			}
 			}
 			// Nothing
 			// Nothing
 
 
-		case CloseMessage:
+		case *Close:
+			l.Debugln("read Close message")
 			return errors.New(msg.Reason)
 			return errors.New(msg.Reason)
 
 
 		default:
 		default:
-			return fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType)
+			l.Debugf("read unknown message: %+T", msg)
+			return fmt.Errorf("protocol error: %s: unknown or empty message", c.id)
 		}
 		}
 	}
 	}
 }
 }
 
 
-func (c *rawConnection) readMessage() (hdr header, msg encodable, err error) {
-	hdrBuf := make([]byte, 8)
-	_, err = io.ReadFull(c.cr, hdrBuf)
+func (c *rawConnection) readMessage() (message, error) {
+	hdr, err := c.readHeader()
 	if err != nil {
 	if err != nil {
-		return
+		return nil, err
 	}
 	}
 
 
-	hdr = decodeHeader(binary.BigEndian.Uint32(hdrBuf[:4]))
-	msglen := int(binary.BigEndian.Uint32(hdrBuf[4:]))
+	return c.readMessageAfterHeader(hdr)
+}
 
 
-	l.Debugf("read header %v (msglen=%d)", hdr, msglen)
+func (c *rawConnection) readMessageAfterHeader(hdr Header) (message, error) {
+	// First comes a 4 byte message length
 
 
-	if msglen > MaxMessageLen {
-		err = fmt.Errorf("message length %d exceeds maximum %d", msglen, MaxMessageLen)
-		return
+	buf := buffers.get(4)
+	if _, err := io.ReadFull(c.cr, buf); err != nil {
+		return nil, fmt.Errorf("reading message length: %v", err)
 	}
 	}
-
-	if hdr.version != 0 {
-		err = fmt.Errorf("unknown protocol version 0x%x", hdr.version)
-		return
+	msgLen := int32(binary.BigEndian.Uint32(buf))
+	if msgLen < 0 {
+		return nil, fmt.Errorf("negative message length %d", msgLen)
 	}
 	}
 
 
-	// c.readerBuf contains a buffer we can reuse. But once we've unmarshalled
-	// a message from the buffer we can't reuse it again as the unmarshalled
-	// message refers to the contents of the buffer. The only case we a buffer
-	// ends up in readerBuf for reuse is when the message is compressed, as we
-	// then decompress into a new buffer instead.
-
-	var msgBuf []byte
-	if cap(c.readerBuf) >= msglen {
-		// If we have a buffer ready in rdbuf we just use that.
-		msgBuf = c.readerBuf[:msglen]
-	} else {
-		// Otherwise we allocate a new buffer.
-		msgBuf = make([]byte, msglen)
-	}
+	// Then comes the message
 
 
-	_, err = io.ReadFull(c.cr, msgBuf)
-	if err != nil {
-		return
+	buf = buffers.upgrade(buf, int(msgLen))
+	if _, err := io.ReadFull(c.cr, buf); err != nil {
+		return nil, fmt.Errorf("reading message: %v", err)
 	}
 	}
 
 
-	l.Debugf("read %d bytes", len(msgBuf))
+	// ... which might be compressed
 
 
-	if hdr.compression && msglen > 0 {
-		// We're going to decompress msgBuf into a different newly allocated
-		// buffer, so keep msgBuf around for reuse on the next message.
-		c.readerBuf = msgBuf
+	switch hdr.Compression {
+	case MessageCompressionNone:
+		// Nothing
 
 
-		msgBuf, err = lz4.Decode(nil, msgBuf)
+	case MessageCompressionLZ4:
+		decomp, err := c.lz4Decompress(buf)
+		buffers.put(buf)
 		if err != nil {
 		if err != nil {
-			return
+			return nil, fmt.Errorf("decompressing message: %v", err)
 		}
 		}
-		l.Debugf("decompressed to %d bytes", len(msgBuf))
-	} else {
-		c.readerBuf = nil
-	}
+		buf = decomp
 
 
-	if shouldDebug() {
-		if len(msgBuf) > 1024 {
-			l.Debugf("message data:\n%s", hex.Dump(msgBuf[:1024]))
-		} else {
-			l.Debugf("message data:\n%s", hex.Dump(msgBuf))
-		}
+	default:
+		return nil, fmt.Errorf("unknown message compression %d", hdr.Compression)
 	}
 	}
 
 
-	switch hdr.msgType {
-	case messageTypeIndex, messageTypeIndexUpdate:
-		var idx IndexMessage
-		err = idx.UnmarshalXDR(msgBuf)
-		msg = idx
-
-	case messageTypeRequest:
-		var req RequestMessage
-		err = req.UnmarshalXDR(msgBuf)
-		msg = req
+	// ... and is then unmarshalled
 
 
-	case messageTypeResponse:
-		var resp ResponseMessage
-		err = resp.UnmarshalXDR(msgBuf)
-		msg = resp
+	msg, err := c.newMessage(hdr.Type)
+	if err != nil {
+		return nil, err
+	}
+	if err := msg.Unmarshal(buf); err != nil {
+		return nil, fmt.Errorf("unmarshalling message: %v", err)
+	}
+	buffers.put(buf)
 
 
-	case messageTypePing:
-		msg = pingMessage{}
+	return msg, nil
+}
 
 
-	case messageTypeClusterConfig:
-		var cc ClusterConfigMessage
-		err = cc.UnmarshalXDR(msgBuf)
-		msg = cc
+func (c *rawConnection) readHeader() (Header, error) {
+	// First comes a 2 byte header length
 
 
-	case messageTypeClose:
-		var cm CloseMessage
-		err = cm.UnmarshalXDR(msgBuf)
-		msg = cm
+	buf := buffers.get(2)
+	if _, err := io.ReadFull(c.cr, buf); err != nil {
+		return Header{}, fmt.Errorf("reading length: %v", err)
+	}
+	hdrLen := int16(binary.BigEndian.Uint16(buf))
+	if hdrLen < 0 {
+		return Header{}, fmt.Errorf("negative header length %d", hdrLen)
+	}
 
 
-	case messageTypeDownloadProgress:
-		var dp DownloadProgressMessage
-		err := dp.UnmarshalXDR(msgBuf)
-		if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
-			err = nil
-		}
-		msg = dp
+	// Then comes the header
 
 
-	default:
-		err = fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType)
+	buf = buffers.upgrade(buf, int(hdrLen))
+	if _, err := io.ReadFull(c.cr, buf); err != nil {
+		return Header{}, fmt.Errorf("reading header: %v", err)
 	}
 	}
 
 
-	// We check the returned error for the XDRError.IsEOF() method.
-	// IsEOF()==true here means that the message contained fewer fields than
-	// expected. It does not signify an EOF on the socket, because we've
-	// successfully read a size value and then that many bytes from the wire.
-	// New fields we expected but the other peer didn't send should be
-	// interpreted as zero/nil, and if that's not valid we'll verify it
-	// somewhere else.
-	if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
-		err = nil
+	var hdr Header
+	if err := hdr.Unmarshal(buf); err != nil {
+		return Header{}, fmt.Errorf("unmarshalling header: %v", err)
 	}
 	}
 
 
-	return
+	buffers.put(buf)
+	return hdr, nil
 }
 }
 
 
-func (c *rawConnection) handleIndex(im IndexMessage) {
-	l.Debugf("Index(%v, %v, %d file, flags %x, opts: %s)", c.id, im.Folder, len(im.Files), im.Flags, im.Options)
-	c.receiver.Index(c.id, im.Folder, filterIndexMessageFiles(im.Files), im.Flags, im.Options)
+func (c *rawConnection) handleIndex(im Index) {
+	l.Debugf("Index(%v, %v, %d file)", c.id, im.Folder, len(im.Files))
+	c.receiver.Index(c.id, im.Folder, filterIndexMessageFiles(im.Files))
 }
 }
 
 
-func (c *rawConnection) handleIndexUpdate(im IndexMessage) {
-	l.Debugf("queueing IndexUpdate(%v, %v, %d files, flags %x, opts: %s)", c.id, im.Folder, len(im.Files), im.Flags, im.Options)
-	c.receiver.IndexUpdate(c.id, im.Folder, filterIndexMessageFiles(im.Files), im.Flags, im.Options)
+func (c *rawConnection) handleIndexUpdate(im IndexUpdate) {
+	l.Debugf("queueing IndexUpdate(%v, %v, %d files)", c.id, im.Folder, len(im.Files))
+	c.receiver.IndexUpdate(c.id, im.Folder, filterIndexMessageFiles(im.Files))
 }
 }
 
 
 func filterIndexMessageFiles(fs []FileInfo) []FileInfo {
 func filterIndexMessageFiles(fs []FileInfo) []FileInfo {
@@ -560,7 +481,7 @@ func filterIndexMessageFiles(fs []FileInfo) []FileInfo {
 	return fs
 	return fs
 }
 }
 
 
-func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
+func (c *rawConnection) handleRequest(req Request) {
 	size := int(req.Size)
 	size := int(req.Size)
 	usePool := size <= BlockSize
 	usePool := size <= BlockSize
 
 
@@ -574,14 +495,16 @@ func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
 		buf = make([]byte, size)
 		buf = make([]byte, size)
 	}
 	}
 
 
-	err := c.receiver.Request(c.id, req.Folder, req.Name, int64(req.Offset), req.Hash, req.Flags, req.Options, buf)
+	err := c.receiver.Request(c.id, req.Folder, req.Name, int64(req.Offset), req.Hash, req.FromTemporary, buf)
 	if err != nil {
 	if err != nil {
-		c.send(msgID, messageTypeResponse, ResponseMessage{
+		c.send(&Response{
+			ID:   req.ID,
 			Data: nil,
 			Data: nil,
 			Code: errorToCode(err),
 			Code: errorToCode(err),
 		}, done)
 		}, done)
 	} else {
 	} else {
-		c.send(msgID, messageTypeResponse, ResponseMessage{
+		c.send(&Response{
+			ID:   req.ID,
 			Data: buf,
 			Data: buf,
 			Code: errorToCode(err),
 			Code: errorToCode(err),
 		}, done)
 		}, done)
@@ -593,34 +516,19 @@ func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
 	}
 	}
 }
 }
 
 
-func (c *rawConnection) handleResponse(msgID int, resp ResponseMessage) {
+func (c *rawConnection) handleResponse(resp Response) {
 	c.awaitingMut.Lock()
 	c.awaitingMut.Lock()
-	if rc := c.awaiting[msgID]; rc != nil {
-		c.awaiting[msgID] = nil
+	if rc := c.awaiting[resp.ID]; rc != nil {
+		delete(c.awaiting, resp.ID)
 		rc <- asyncResult{resp.Data, codeToError(resp.Code)}
 		rc <- asyncResult{resp.Data, codeToError(resp.Code)}
 		close(rc)
 		close(rc)
 	}
 	}
 	c.awaitingMut.Unlock()
 	c.awaitingMut.Unlock()
 }
 }
 
 
-func (c *rawConnection) send(msgID int, msgType int, msg encodable, done chan struct{}) bool {
-	if msgID < 0 {
-		select {
-		case id := <-c.nextID:
-			msgID = id
-		case <-c.closed:
-			return false
-		}
-	}
-
-	hdr := header{
-		version: 0,
-		msgID:   msgID,
-		msgType: msgType,
-	}
-
+func (c *rawConnection) send(msg message, done chan struct{}) bool {
 	select {
 	select {
-	case c.outbox <- hdrMsg{hdr, msg, done}:
+	case c.outbox <- asyncMessage{msg, done}:
 		return true
 		return true
 	case <-c.closed:
 	case <-c.closed:
 		return false
 		return false
@@ -628,94 +536,181 @@ func (c *rawConnection) send(msgID int, msgType int, msg encodable, done chan st
 }
 }
 
 
 func (c *rawConnection) writerLoop() {
 func (c *rawConnection) writerLoop() {
-	var msgBuf = make([]byte, 8) // buffer for wire format message, kept and reused
-	var uncBuf []byte            // buffer for uncompressed message, kept and reused
 	for {
 	for {
-		var tempBuf []byte
-		var err error
-
 		select {
 		select {
 		case hm := <-c.outbox:
 		case hm := <-c.outbox:
-			if hm.msg != nil {
-				// Uncompressed message in uncBuf
-				msgLen := hm.msg.XDRSize()
-				if cap(uncBuf) >= msgLen {
-					uncBuf = uncBuf[:msgLen]
-				} else {
-					uncBuf = make([]byte, msgLen)
-				}
-				m := &xdr.Marshaller{Data: uncBuf}
-				err = hm.msg.MarshalXDRInto(m)
-				if hm.done != nil {
-					close(hm.done)
-				}
-				if err != nil {
-					c.close(err)
-					return
-				}
-
-				compress := false
-				switch c.compression {
-				case CompressAlways:
-					compress = true
-				case CompressMetadata:
-					compress = hm.hdr.msgType != messageTypeResponse
-				}
-
-				if compress && len(uncBuf) >= compressionThreshold {
-					// Use compression for large messages
-					hm.hdr.compression = true
-
-					// Make sure we have enough space for the compressed message plus header in msgBug
-					msgBuf = msgBuf[:cap(msgBuf)]
-					if maxLen := lz4.CompressBound(len(uncBuf)) + 8; maxLen > len(msgBuf) {
-						msgBuf = make([]byte, maxLen)
-					}
-
-					// Compressed is written to msgBuf, we keep tb for the length only
-					tempBuf, err = lz4.Encode(msgBuf[8:], uncBuf)
-					binary.BigEndian.PutUint32(msgBuf[4:8], uint32(len(tempBuf)))
-					msgBuf = msgBuf[0 : len(tempBuf)+8]
-
-					l.Debugf("write compressed message; %v (len=%d)", hm.hdr, len(tempBuf))
-				} else {
-					// No point in compressing very short messages
-					hm.hdr.compression = false
-
-					msgBuf = msgBuf[:cap(msgBuf)]
-					if l := len(uncBuf) + 8; l > len(msgBuf) {
-						msgBuf = make([]byte, l)
-					}
-
-					binary.BigEndian.PutUint32(msgBuf[4:8], uint32(len(uncBuf)))
-					msgBuf = msgBuf[0 : len(uncBuf)+8]
-					copy(msgBuf[8:], uncBuf)
-
-					l.Debugf("write uncompressed message; %v (len=%d)", hm.hdr, len(uncBuf))
-				}
-			} else {
-				l.Debugf("write empty message; %v", hm.hdr)
-				binary.BigEndian.PutUint32(msgBuf[4:8], 0)
-				msgBuf = msgBuf[:8]
-			}
-
-			binary.BigEndian.PutUint32(msgBuf[0:4], encodeHeader(hm.hdr))
-
-			if err == nil {
-				var n int
-				n, err = c.cw.Write(msgBuf)
-				l.Debugf("wrote %d bytes on the wire", n)
-			}
-			if err != nil {
+			if err := c.writeMessage(hm); err != nil {
 				c.close(err)
 				c.close(err)
 				return
 				return
 			}
 			}
+
 		case <-c.closed:
 		case <-c.closed:
 			return
 			return
 		}
 		}
 	}
 	}
 }
 }
 
 
+func (c *rawConnection) writeMessage(hm asyncMessage) error {
+	if c.shouldCompressMessage(hm.msg) {
+		return c.writeCompressedMessage(hm)
+	}
+	return c.writeUncompressedMessage(hm)
+}
+
+func (c *rawConnection) writeCompressedMessage(hm asyncMessage) error {
+	size := hm.msg.ProtoSize()
+	buf := buffers.get(size)
+	if _, err := hm.msg.MarshalTo(buf); err != nil {
+		return fmt.Errorf("marshalling message: %v", err)
+	}
+	if hm.done != nil {
+		close(hm.done)
+	}
+
+	compressed, err := c.lz4Compress(buf)
+	if err != nil {
+		return fmt.Errorf("compressing message: %v", err)
+	}
+
+	hdr := Header{
+		Type:        c.typeOf(hm.msg),
+		Compression: MessageCompressionLZ4,
+	}
+	hdrSize := hdr.ProtoSize()
+	if hdrSize > 1<<16-1 {
+		panic("impossibly large header")
+	}
+
+	totSize := 2 + hdrSize + 4 + len(compressed)
+	buf = buffers.upgrade(buf, totSize)
+
+	// Header length
+	binary.BigEndian.PutUint16(buf, uint16(hdrSize))
+	// Header
+	if _, err := hdr.MarshalTo(buf[2:]); err != nil {
+		return fmt.Errorf("marshalling header: %v", err)
+	}
+	// Message length
+	binary.BigEndian.PutUint32(buf[2+hdrSize:], uint32(len(compressed)))
+	// Message
+	copy(buf[2+hdrSize+4:], compressed)
+	buffers.put(compressed)
+
+	n, err := c.cw.Write(buf)
+	buffers.put(buf)
+
+	l.Debugf("wrote %d bytes on the wire (2 bytes length, %d bytes header, 4 bytes message length, %d bytes message (%d uncompressed)), err=%v", n, hdrSize, len(compressed), size, err)
+	if err != nil {
+		return fmt.Errorf("writing message: %v", err)
+	}
+	return nil
+}
+
+func (c *rawConnection) writeUncompressedMessage(hm asyncMessage) error {
+	size := hm.msg.ProtoSize()
+
+	hdr := Header{
+		Type: c.typeOf(hm.msg),
+	}
+	hdrSize := hdr.ProtoSize()
+	if hdrSize > 1<<16-1 {
+		panic("impossibly large header")
+	}
+
+	totSize := 2 + hdrSize + 4 + size
+	buf := buffers.get(totSize)
+
+	// Header length
+	binary.BigEndian.PutUint16(buf, uint16(hdrSize))
+	// Header
+	if _, err := hdr.MarshalTo(buf[2:]); err != nil {
+		return fmt.Errorf("marshalling header: %v", err)
+	}
+	// Message length
+	binary.BigEndian.PutUint32(buf[2+hdrSize:], uint32(size))
+	// Message
+	if _, err := hm.msg.MarshalTo(buf[2+hdrSize+4:]); err != nil {
+		return fmt.Errorf("marshalling message: %v", err)
+	}
+	if hm.done != nil {
+		close(hm.done)
+	}
+
+	n, err := c.cw.Write(buf[:totSize])
+	buffers.put(buf)
+
+	l.Debugf("wrote %d bytes on the wire (2 bytes length, %d bytes header, 4 bytes message length, %d bytes message), err=%v", n, hdrSize, size, err)
+	if err != nil {
+		return fmt.Errorf("writing message: %v", err)
+	}
+	return nil
+}
+
+func (c *rawConnection) typeOf(msg message) MessageType {
+	switch msg.(type) {
+	case *ClusterConfig:
+		return messageTypeClusterConfig
+	case *Index:
+		return messageTypeIndex
+	case *IndexUpdate:
+		return messageTypeIndexUpdate
+	case *Request:
+		return messageTypeRequest
+	case *Response:
+		return messageTypeResponse
+	case *DownloadProgress:
+		return messageTypeDownloadProgress
+	case *Ping:
+		return messageTypePing
+	case *Close:
+		return messageTypeClose
+	default:
+		panic("bug: unknown message type")
+	}
+}
+
+func (c *rawConnection) newMessage(t MessageType) (message, error) {
+	switch t {
+	case messageTypeClusterConfig:
+		return new(ClusterConfig), nil
+	case messageTypeIndex:
+		return new(Index), nil
+	case messageTypeIndexUpdate:
+		return new(IndexUpdate), nil
+	case messageTypeRequest:
+		return new(Request), nil
+	case messageTypeResponse:
+		return new(Response), nil
+	case messageTypeDownloadProgress:
+		return new(DownloadProgress), nil
+	case messageTypePing:
+		return new(Ping), nil
+	case messageTypeClose:
+		return new(Close), nil
+	default:
+		return nil, errUnknownMessage
+	}
+}
+
+func (c *rawConnection) shouldCompressMessage(msg message) bool {
+	switch c.compression {
+	case CompressNever:
+		return false
+
+	case CompressAlways:
+		// Use compression for large enough messages
+		return msg.ProtoSize() >= compressionThreshold
+
+	case CompressMetadata:
+		_, isResponse := msg.(*Response)
+		// Compress if it's large enough and not a response message
+		return !isResponse && msg.ProtoSize() >= compressionThreshold
+
+	default:
+		panic("unknown compression setting")
+	}
+}
+
 func (c *rawConnection) close(err error) {
 func (c *rawConnection) close(err error) {
 	c.once.Do(func() {
 	c.once.Do(func() {
 		l.Debugln("close due to", err)
 		l.Debugln("close due to", err)
@@ -725,7 +720,7 @@ func (c *rawConnection) close(err error) {
 		for i, ch := range c.awaiting {
 		for i, ch := range c.awaiting {
 			if ch != nil {
 			if ch != nil {
 				close(ch)
 				close(ch)
-				c.awaiting[i] = nil
+				delete(c.awaiting, i)
 			}
 			}
 		}
 		}
 		c.awaitingMut.Unlock()
 		c.awaitingMut.Unlock()
@@ -734,18 +729,6 @@ func (c *rawConnection) close(err error) {
 	})
 	})
 }
 }
 
 
-func (c *rawConnection) idGenerator() {
-	nextID := 0
-	for {
-		nextID = (nextID + 1) & 0xfff
-		select {
-		case c.nextID <- nextID:
-		case <-c.closed:
-			return
-		}
-	}
-}
-
 // The pingSender makes sure that we've sent a message within the last
 // The pingSender makes sure that we've sent a message within the last
 // PingSendInterval. If we already have something sent in the last
 // PingSendInterval. If we already have something sent in the last
 // PingSendInterval/2, we do nothing. Otherwise we send a ping message. This
 // PingSendInterval/2, we do nothing. Otherwise we send a ping message. This
@@ -808,3 +791,27 @@ func (c *rawConnection) Statistics() Statistics {
 		OutBytesTotal: c.cw.Tot(),
 		OutBytesTotal: c.cw.Tot(),
 	}
 	}
 }
 }
+
+func (c *rawConnection) lz4Compress(src []byte) ([]byte, error) {
+	var err error
+	buf := buffers.get(len(src))
+	buf, err = lz4.Encode(buf, src)
+	if err != nil {
+		return nil, err
+	}
+
+	binary.BigEndian.PutUint32(buf, binary.LittleEndian.Uint32(buf))
+	return buf, nil
+}
+
+func (c *rawConnection) lz4Decompress(src []byte) ([]byte, error) {
+	size := binary.BigEndian.Uint32(src)
+	binary.LittleEndian.PutUint32(src, size)
+	var err error
+	buf := buffers.get(int(size))
+	buf, err = lz4.Decode(buf, src)
+	if err != nil {
+		return nil, err
+	}
+	return buf, nil
+}

+ 90 - 223
lib/protocol/protocol_test.go

@@ -3,21 +3,17 @@
 package protocol
 package protocol
 
 
 import (
 import (
-	"encoding/binary"
-	"encoding/hex"
+	"bytes"
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
-	"fmt"
 	"io"
 	"io"
 	"io/ioutil"
 	"io/ioutil"
-	"os"
-	"reflect"
 	"strings"
 	"strings"
 	"testing"
 	"testing"
 	"testing/quick"
 	"testing/quick"
 	"time"
 	"time"
 
 
-	"github.com/calmh/xdr"
+	"github.com/syncthing/syncthing/lib/rand"
 )
 )
 
 
 var (
 var (
@@ -26,64 +22,6 @@ var (
 	quickCfg = &quick.Config{}
 	quickCfg = &quick.Config{}
 )
 )
 
 
-func TestHeaderEncodeDecode(t *testing.T) {
-	f := func(ver, id, typ int) bool {
-		ver = int(uint(ver) % 16)
-		id = int(uint(id) % 4096)
-		typ = int(uint(typ) % 256)
-		h0 := header{version: ver, msgID: id, msgType: typ}
-		h1 := decodeHeader(encodeHeader(h0))
-		return h0 == h1
-	}
-	if err := quick.Check(f, nil); err != nil {
-		t.Error(err)
-	}
-}
-
-func TestHeaderMarshalUnmarshal(t *testing.T) {
-	f := func(ver, id, typ int) bool {
-		ver = int(uint(ver) % 16)
-		id = int(uint(id) % 4096)
-		typ = int(uint(typ) % 256)
-		buf := make([]byte, 4)
-
-		h0 := header{version: ver, msgID: id, msgType: typ}
-		h0.MarshalXDRInto(&xdr.Marshaller{Data: buf})
-
-		var h1 header
-		h1.UnmarshalXDRFrom(&xdr.Unmarshaller{Data: buf})
-		return h0 == h1
-	}
-	if err := quick.Check(f, nil); err != nil {
-		t.Error(err)
-	}
-}
-
-func TestHeaderLayout(t *testing.T) {
-	var e, a uint32
-
-	// Version are the first four bits
-	e = 0xf0000000
-	a = encodeHeader(header{version: 0xf})
-	if a != e {
-		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
-	}
-
-	// Message ID are the following 12 bits
-	e = 0x0fff0000
-	a = encodeHeader(header{msgID: 0xfff})
-	if a != e {
-		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
-	}
-
-	// Type are the last 8 bits before reserved
-	e = 0x0000ff00
-	a = encodeHeader(header{msgType: 0xff})
-	if a != e {
-		t.Errorf("Header layout incorrect; %08x != %08x", a, e)
-	}
-}
-
 func TestPing(t *testing.T) {
 func TestPing(t *testing.T) {
 	ar, aw := io.Pipe()
 	ar, aw := io.Pipe()
 	br, bw := io.Pipe()
 	br, bw := io.Pipe()
@@ -92,8 +30,8 @@ func TestPing(t *testing.T) {
 	c0.Start()
 	c0.Start()
 	c1 := NewConnection(c1ID, br, aw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
 	c1 := NewConnection(c1ID, br, aw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
 	c1.Start()
 	c1.Start()
-	c0.ClusterConfig(ClusterConfigMessage{})
-	c1.ClusterConfig(ClusterConfigMessage{})
+	c0.ClusterConfig(ClusterConfig{})
+	c1.ClusterConfig(ClusterConfig{})
 
 
 	if ok := c0.ping(); !ok {
 	if ok := c0.ping(); !ok {
 		t.Error("c0 ping failed")
 		t.Error("c0 ping failed")
@@ -103,56 +41,6 @@ func TestPing(t *testing.T) {
 	}
 	}
 }
 }
 
 
-func TestVersionErr(t *testing.T) {
-	m0 := newTestModel()
-	m1 := newTestModel()
-
-	ar, aw := io.Pipe()
-	br, bw := io.Pipe()
-
-	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
-	c0.Start()
-	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
-	c1.Start()
-	c0.ClusterConfig(ClusterConfigMessage{})
-	c1.ClusterConfig(ClusterConfigMessage{})
-
-	timeoutWriteHeader(c0.cw, header{
-		version: 2, // higher than supported
-		msgID:   0,
-		msgType: messageTypeIndex,
-	})
-
-	if err := m1.closedError(); err == nil || !strings.Contains(err.Error(), "unknown protocol version") {
-		t.Error("Connection should close due to unknown version, not", err)
-	}
-}
-
-func TestTypeErr(t *testing.T) {
-	m0 := newTestModel()
-	m1 := newTestModel()
-
-	ar, aw := io.Pipe()
-	br, bw := io.Pipe()
-
-	c0 := NewConnection(c0ID, ar, bw, m0, "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
-	c0.Start()
-	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
-	c1.Start()
-	c0.ClusterConfig(ClusterConfigMessage{})
-	c1.ClusterConfig(ClusterConfigMessage{})
-
-	timeoutWriteHeader(c0.cw, header{
-		version: 0,
-		msgID:   0,
-		msgType: 42, // unknown type
-	})
-
-	if err := m1.closedError(); err == nil || !strings.Contains(err.Error(), "unknown message type") {
-		t.Error("Connection should close due to unknown message type, not", err)
-	}
-}
-
 func TestClose(t *testing.T) {
 func TestClose(t *testing.T) {
 	m0 := newTestModel()
 	m0 := newTestModel()
 	m1 := newTestModel()
 	m1 := newTestModel()
@@ -164,8 +52,8 @@ func TestClose(t *testing.T) {
 	c0.Start()
 	c0.Start()
 	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
 	c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
 	c1.Start()
 	c1.Start()
-	c0.ClusterConfig(ClusterConfigMessage{})
-	c1.ClusterConfig(ClusterConfigMessage{})
+	c0.ClusterConfig(ClusterConfig{})
+	c1.ClusterConfig(ClusterConfig{})
 
 
 	c0.close(errors.New("manual close"))
 	c0.close(errors.New("manual close"))
 
 
@@ -180,8 +68,8 @@ func TestClose(t *testing.T) {
 		t.Error("Ping should not return true")
 		t.Error("Ping should not return true")
 	}
 	}
 
 
-	c0.Index("default", nil, 0, nil)
-	c0.Index("default", nil, 0, nil)
+	c0.Index("default", nil)
+	c0.Index("default", nil)
 
 
 	if _, err := c0.Request("default", "foo", 0, 0, nil, false); err == nil {
 	if _, err := c0.Request("default", "foo", 0, 0, nil, false); err == nil {
 		t.Error("Request should return an error")
 		t.Error("Request should return an error")
@@ -193,15 +81,11 @@ func TestMarshalIndexMessage(t *testing.T) {
 		quickCfg.MaxCount = 10
 		quickCfg.MaxCount = 10
 	}
 	}
 
 
-	f := func(m1 IndexMessage) bool {
-		if len(m1.Options) == 0 {
-			m1.Options = nil
-		}
+	f := func(m1 Index) bool {
 		if len(m1.Files) == 0 {
 		if len(m1.Files) == 0 {
 			m1.Files = nil
 			m1.Files = nil
 		}
 		}
 		for i, f := range m1.Files {
 		for i, f := range m1.Files {
-			m1.Files[i].CachedSize = 0
 			if len(f.Blocks) == 0 {
 			if len(f.Blocks) == 0 {
 				m1.Files[i].Blocks = nil
 				m1.Files[i].Blocks = nil
 			} else {
 			} else {
@@ -212,9 +96,12 @@ func TestMarshalIndexMessage(t *testing.T) {
 					}
 					}
 				}
 				}
 			}
 			}
+			if len(f.Version.Counters) == 0 {
+				m1.Files[i].Version.Counters = nil
+			}
 		}
 		}
 
 
-		return testMarshal(t, "index", &m1, &IndexMessage{})
+		return testMarshal(t, "index", &m1, &Index{})
 	}
 	}
 
 
 	if err := quick.Check(f, quickCfg); err != nil {
 	if err := quick.Check(f, quickCfg); err != nil {
@@ -227,14 +114,11 @@ func TestMarshalRequestMessage(t *testing.T) {
 		quickCfg.MaxCount = 10
 		quickCfg.MaxCount = 10
 	}
 	}
 
 
-	f := func(m1 RequestMessage) bool {
-		if len(m1.Options) == 0 {
-			m1.Options = nil
-		}
+	f := func(m1 Request) bool {
 		if len(m1.Hash) == 0 {
 		if len(m1.Hash) == 0 {
 			m1.Hash = nil
 			m1.Hash = nil
 		}
 		}
-		return testMarshal(t, "request", &m1, &RequestMessage{})
+		return testMarshal(t, "request", &m1, &Request{})
 	}
 	}
 
 
 	if err := quick.Check(f, quickCfg); err != nil {
 	if err := quick.Check(f, quickCfg); err != nil {
@@ -247,11 +131,11 @@ func TestMarshalResponseMessage(t *testing.T) {
 		quickCfg.MaxCount = 10
 		quickCfg.MaxCount = 10
 	}
 	}
 
 
-	f := func(m1 ResponseMessage) bool {
+	f := func(m1 Response) bool {
 		if len(m1.Data) == 0 {
 		if len(m1.Data) == 0 {
 			m1.Data = nil
 			m1.Data = nil
 		}
 		}
-		return testMarshal(t, "response", &m1, &ResponseMessage{})
+		return testMarshal(t, "response", &m1, &Response{})
 	}
 	}
 
 
 	if err := quick.Check(f, quickCfg); err != nil {
 	if err := quick.Check(f, quickCfg); err != nil {
@@ -264,10 +148,7 @@ func TestMarshalClusterConfigMessage(t *testing.T) {
 		quickCfg.MaxCount = 10
 		quickCfg.MaxCount = 10
 	}
 	}
 
 
-	f := func(m1 ClusterConfigMessage) bool {
-		if len(m1.Options) == 0 {
-			m1.Options = nil
-		}
+	f := func(m1 ClusterConfig) bool {
 		if len(m1.Folders) == 0 {
 		if len(m1.Folders) == 0 {
 			m1.Folders = nil
 			m1.Folders = nil
 		}
 		}
@@ -275,11 +156,8 @@ func TestMarshalClusterConfigMessage(t *testing.T) {
 			if len(m1.Folders[i].Devices) == 0 {
 			if len(m1.Folders[i].Devices) == 0 {
 				m1.Folders[i].Devices = nil
 				m1.Folders[i].Devices = nil
 			}
 			}
-			if len(m1.Folders[i].Options) == 0 {
-				m1.Folders[i].Options = nil
-			}
 		}
 		}
-		return testMarshal(t, "clusterconfig", &m1, &ClusterConfigMessage{})
+		return testMarshal(t, "clusterconfig", &m1, &ClusterConfig{})
 	}
 	}
 
 
 	if err := quick.Check(f, quickCfg); err != nil {
 	if err := quick.Check(f, quickCfg); err != nil {
@@ -292,8 +170,8 @@ func TestMarshalCloseMessage(t *testing.T) {
 		quickCfg.MaxCount = 10
 		quickCfg.MaxCount = 10
 	}
 	}
 
 
-	f := func(m1 CloseMessage) bool {
-		return testMarshal(t, "close", &m1, &CloseMessage{})
+	f := func(m1 Close) bool {
+		return testMarshal(t, "close", &m1, &Close{})
 	}
 	}
 
 
 	if err := quick.Check(f, quickCfg); err != nil {
 	if err := quick.Check(f, quickCfg); err != nil {
@@ -301,108 +179,97 @@ func TestMarshalCloseMessage(t *testing.T) {
 	}
 	}
 }
 }
 
 
-type message interface {
-	MarshalXDR() ([]byte, error)
-	UnmarshalXDR([]byte) error
-}
-
 func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
 func testMarshal(t *testing.T, prefix string, m1, m2 message) bool {
-	failed := func(bc []byte) {
-		bs, _ := json.MarshalIndent(m1, "", "  ")
-		ioutil.WriteFile(prefix+"-1.txt", bs, 0644)
-		bs, _ = json.MarshalIndent(m2, "", "  ")
-		ioutil.WriteFile(prefix+"-2.txt", bs, 0644)
-		if len(bc) > 0 {
-			f, _ := os.Create(prefix + "-data.txt")
-			fmt.Fprint(f, hex.Dump(bc))
-			f.Close()
-		}
-	}
-
-	buf, err := m1.MarshalXDR()
-	if err != nil && strings.Contains(err.Error(), "exceeds size") {
-		return true
-	}
+	buf, err := m1.Marshal()
 	if err != nil {
 	if err != nil {
-		failed(nil)
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	err = m2.UnmarshalXDR(buf)
+	err = m2.Unmarshal(buf)
 	if err != nil {
 	if err != nil {
-		failed(buf)
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 
-	ok := reflect.DeepEqual(m1, m2)
-	if !ok {
-		failed(buf)
+	bs1, _ := json.MarshalIndent(m1, "", "  ")
+	bs2, _ := json.MarshalIndent(m2, "", "  ")
+	if !bytes.Equal(bs1, bs2) {
+		ioutil.WriteFile(prefix+"-1.txt", bs1, 0644)
+		ioutil.WriteFile(prefix+"-2.txt", bs1, 0644)
+		return false
 	}
 	}
-	return ok
-}
 
 
-func timeoutWriteHeader(w io.Writer, hdr header) {
-	// This tries to write a message header to w, but times out after a while.
-	// This is useful because in testing, with a PipeWriter, it will block
-	// forever if the other side isn't reading any more. On the other hand we
-	// can't just "go" it into the background, because if the other side is
-	// still there we should wait for the write to complete. Yay.
-
-	var buf [8]byte // header and message length
-	binary.BigEndian.PutUint32(buf[:], encodeHeader(hdr))
-	binary.BigEndian.PutUint32(buf[4:], 0) // zero message length, explicitly
-
-	done := make(chan struct{})
-	go func() {
-		w.Write(buf[:])
-		close(done)
-	}()
-	select {
-	case <-done:
-	case <-time.After(250 * time.Millisecond):
-	}
+	return true
 }
 }
 
 
-func TestFileInfoSize(t *testing.T) {
-	fi := FileInfo{
-		Blocks: []BlockInfo{
-			{Size: 42},
-			{Offset: 42, Size: 23},
-			{Offset: 42 + 23, Size: 34},
-		},
+func TestMarshalledIndexMessageSize(t *testing.T) {
+	// We should be able to handle a 1 TiB file without
+	// blowing the default max message size.
+
+	if testing.Short() {
+		t.Skip("this test requires a lot of memory")
+		return
 	}
 	}
 
 
-	size := fi.Size()
-	want := int64(42 + 23 + 34)
-	if size != want {
-		t.Errorf("Incorrect size reported, got %d, want %d", size, want)
+	const (
+		maxMessageSize = MaxMessageLen
+		fileSize       = 1 << 40
+		blockSize      = BlockSize
+	)
+
+	f := FileInfo{
+		Name:        "a normal length file name withoout any weird stuff.txt",
+		Type:        FileInfoTypeFile,
+		Size:        fileSize,
+		Permissions: 0666,
+		Modified:    time.Now().Unix(),
+		Version:     Vector{Counters: []Counter{{ID: 1 << 60, Value: 1}, {ID: 2 << 60, Value: 1}}},
+		Blocks:      make([]BlockInfo, fileSize/blockSize),
 	}
 	}
 
 
-	size = fi.Size() // Cached, this time
-	if size != want {
-		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
+	for i := 0; i < fileSize/blockSize; i++ {
+		f.Blocks[i].Offset = int64(i) * blockSize
+		f.Blocks[i].Size = blockSize
+		f.Blocks[i].Hash = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 30, 1, 2}
 	}
 	}
 
 
-	fi.CachedSize = 8
-	want = 8
-	size = fi.Size() // Ensure it came from the cache
-	if size != want {
-		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
+	idx := Index{
+		Folder: "some folder ID",
+		Files:  []FileInfo{f},
 	}
 	}
 
 
-	fi.CachedSize = 0
-	fi.Flags = FlagDirectory
-	want = 128
-	size = fi.Size() // Directories are 128 bytes large
-	if size != want {
-		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
+	msgSize := idx.ProtoSize()
+	if msgSize > maxMessageSize {
+		t.Errorf("Message size %d bytes is larger than max %d", msgSize, maxMessageSize)
 	}
 	}
+}
+
+func TestLZ4Compression(t *testing.T) {
+	c := new(rawConnection)
 
 
-	fi.CachedSize = 0
-	fi.Flags = FlagDeleted
-	want = 128
-	size = fi.Size() // Also deleted files
-	if size != want {
-		t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
+	for i := 0; i < 10; i++ {
+		dataLen := 150 + rand.Intn(150)
+		data := make([]byte, dataLen)
+		_, err := io.ReadFull(rand.Reader, data[100:])
+		if err != nil {
+			t.Fatal(err)
+		}
+		comp, err := c.lz4Compress(data)
+		if err != nil {
+			t.Errorf("compressing %d bytes: %v", dataLen, err)
+			continue
+		}
+
+		res, err := c.lz4Decompress(comp)
+		if err != nil {
+			t.Errorf("decompressing %d bytes to %d: %v", len(comp), dataLen, err)
+			continue
+		}
+		if len(res) != len(data) {
+			t.Errorf("Incorrect len %d != expected %d", len(res), len(data))
+		}
+		if !bytes.Equal(data, res) {
+			t.Error("Incorrect decompressed data")
+		}
+		t.Logf("OK #%d, %d -> %d -> %d", i, dataLen, len(comp), dataLen)
 	}
 	}
 }
 }

+ 112 - 31
lib/protocol/vector.go

@@ -6,35 +6,30 @@ package protocol
 // version vector. The vector has slice semantics and some operations on it
 // version vector. The vector has slice semantics and some operations on it
 // are "append-like" in that they may return the same vector modified, or v
 // are "append-like" in that they may return the same vector modified, or v
 // new allocated Vector with the modified contents.
 // new allocated Vector with the modified contents.
-type Vector []Counter
 
 
 // Counter represents a single counter in the version vector.
 // Counter represents a single counter in the version vector.
-type Counter struct {
-	ID    ShortID
-	Value uint64
-}
 
 
 // Update returns a Vector with the index for the specific ID incremented by
 // Update returns a Vector with the index for the specific ID incremented by
 // one. If it is possible, the vector v is updated and returned. If it is not,
 // one. If it is possible, the vector v is updated and returned. If it is not,
 // a copy will be created, updated and returned.
 // a copy will be created, updated and returned.
 func (v Vector) Update(id ShortID) Vector {
 func (v Vector) Update(id ShortID) Vector {
-	for i := range v {
-		if v[i].ID == id {
+	for i := range v.Counters {
+		if v.Counters[i].ID == id {
 			// Update an existing index
 			// Update an existing index
-			v[i].Value++
+			v.Counters[i].Value++
 			return v
 			return v
-		} else if v[i].ID > id {
+		} else if v.Counters[i].ID > id {
 			// Insert a new index
 			// Insert a new index
-			nv := make(Vector, len(v)+1)
-			copy(nv, v[:i])
+			nv := make([]Counter, len(v.Counters)+1)
+			copy(nv, v.Counters[:i])
 			nv[i].ID = id
 			nv[i].ID = id
 			nv[i].Value = 1
 			nv[i].Value = 1
-			copy(nv[i+1:], v[i:])
-			return nv
+			copy(nv[i+1:], v.Counters[i:])
+			return Vector{nv}
 		}
 		}
 	}
 	}
 	// Append a new index
 	// Append a new index
-	return append(v, Counter{id, 1})
+	return Vector{append(v.Counters, Counter{id, 1})}
 }
 }
 
 
 // Merge returns the vector containing the maximum indexes from v and b. If it
 // Merge returns the vector containing the maximum indexes from v and b. If it
@@ -42,28 +37,28 @@ func (v Vector) Update(id ShortID) Vector {
 // will be created, updated and returned.
 // will be created, updated and returned.
 func (v Vector) Merge(b Vector) Vector {
 func (v Vector) Merge(b Vector) Vector {
 	var vi, bi int
 	var vi, bi int
-	for bi < len(b) {
-		if vi == len(v) {
+	for bi < len(b.Counters) {
+		if vi == len(v.Counters) {
 			// We've reach the end of v, all that remains are appends
 			// We've reach the end of v, all that remains are appends
-			return append(v, b[bi:]...)
+			return Vector{append(v.Counters, b.Counters[bi:]...)}
 		}
 		}
 
 
-		if v[vi].ID > b[bi].ID {
+		if v.Counters[vi].ID > b.Counters[bi].ID {
 			// The index from b should be inserted here
 			// The index from b should be inserted here
-			n := make(Vector, len(v)+1)
-			copy(n, v[:vi])
-			n[vi] = b[bi]
-			copy(n[vi+1:], v[vi:])
-			v = n
+			n := make([]Counter, len(v.Counters)+1)
+			copy(n, v.Counters[:vi])
+			n[vi] = b.Counters[bi]
+			copy(n[vi+1:], v.Counters[vi:])
+			v.Counters = n
 		}
 		}
 
 
-		if v[vi].ID == b[bi].ID {
-			if val := b[bi].Value; val > v[vi].Value {
-				v[vi].Value = val
+		if v.Counters[vi].ID == b.Counters[bi].ID {
+			if val := b.Counters[bi].Value; val > v.Counters[vi].Value {
+				v.Counters[vi].Value = val
 			}
 			}
 		}
 		}
 
 
-		if bi < len(b) && v[vi].ID == b[bi].ID {
+		if bi < len(b.Counters) && v.Counters[vi].ID == b.Counters[bi].ID {
 			bi++
 			bi++
 		}
 		}
 		vi++
 		vi++
@@ -74,9 +69,9 @@ func (v Vector) Merge(b Vector) Vector {
 
 
 // Copy returns an identical vector that is not shared with v.
 // Copy returns an identical vector that is not shared with v.
 func (v Vector) Copy() Vector {
 func (v Vector) Copy() Vector {
-	nv := make(Vector, len(v))
-	copy(nv, v)
-	return nv
+	nv := make([]Counter, len(v.Counters))
+	copy(nv, v.Counters)
+	return Vector{nv}
 }
 }
 
 
 // Equal returns true when the two vectors are equivalent.
 // Equal returns true when the two vectors are equivalent.
@@ -106,10 +101,96 @@ func (v Vector) Concurrent(b Vector) bool {
 
 
 // Counter returns the current value of the given counter ID.
 // Counter returns the current value of the given counter ID.
 func (v Vector) Counter(id ShortID) uint64 {
 func (v Vector) Counter(id ShortID) uint64 {
-	for _, c := range v {
+	for _, c := range v.Counters {
 		if c.ID == id {
 		if c.ID == id {
 			return c.Value
 			return c.Value
 		}
 		}
 	}
 	}
 	return 0
 	return 0
 }
 }
+
+// Ordering represents the relationship between two Vectors.
+type Ordering int
+
+const (
+	Equal Ordering = iota
+	Greater
+	Lesser
+	ConcurrentLesser
+	ConcurrentGreater
+)
+
+// There's really no such thing as "concurrent lesser" and "concurrent
+// greater" in version vectors, just "concurrent". But it's useful to be able
+// to get a strict ordering between versions for stable sorts and so on, so we
+// return both variants. The convenience method Concurrent() can be used to
+// check for either case.
+
+// Compare returns the Ordering that describes a's relation to b.
+func (v Vector) Compare(b Vector) Ordering {
+	var ai, bi int     // index into a and b
+	var av, bv Counter // value at current index
+
+	result := Equal
+
+	for ai < len(v.Counters) || bi < len(b.Counters) {
+		var aMissing, bMissing bool
+
+		if ai < len(v.Counters) {
+			av = v.Counters[ai]
+		} else {
+			av = Counter{}
+			aMissing = true
+		}
+
+		if bi < len(b.Counters) {
+			bv = b.Counters[bi]
+		} else {
+			bv = Counter{}
+			bMissing = true
+		}
+
+		switch {
+		case av.ID == bv.ID:
+			// We have a counter value for each side
+			if av.Value > bv.Value {
+				if result == Lesser {
+					return ConcurrentLesser
+				}
+				result = Greater
+			} else if av.Value < bv.Value {
+				if result == Greater {
+					return ConcurrentGreater
+				}
+				result = Lesser
+			}
+
+		case !aMissing && av.ID < bv.ID || bMissing:
+			// Value is missing on the b side
+			if av.Value > 0 {
+				if result == Lesser {
+					return ConcurrentLesser
+				}
+				result = Greater
+			}
+
+		case !bMissing && bv.ID < av.ID || aMissing:
+			// Value is missing on the a side
+			if bv.Value > 0 {
+				if result == Greater {
+					return ConcurrentGreater
+				}
+				result = Lesser
+			}
+		}
+
+		if ai < len(v.Counters) && (av.ID <= bv.ID || bMissing) {
+			ai++
+		}
+		if bi < len(b.Counters) && (bv.ID <= av.ID || aMissing) {
+			bi++
+		}
+	}
+
+	return result
+}

+ 0 - 89
lib/protocol/vector_compare.go

@@ -1,89 +0,0 @@
-// Copyright (C) 2015 The Protocol Authors.
-
-package protocol
-
-// Ordering represents the relationship between two Vectors.
-type Ordering int
-
-const (
-	Equal Ordering = iota
-	Greater
-	Lesser
-	ConcurrentLesser
-	ConcurrentGreater
-)
-
-// There's really no such thing as "concurrent lesser" and "concurrent
-// greater" in version vectors, just "concurrent". But it's useful to be able
-// to get a strict ordering between versions for stable sorts and so on, so we
-// return both variants. The convenience method Concurrent() can be used to
-// check for either case.
-
-// Compare returns the Ordering that describes a's relation to b.
-func (a Vector) Compare(b Vector) Ordering {
-	var ai, bi int     // index into a and b
-	var av, bv Counter // value at current index
-
-	result := Equal
-
-	for ai < len(a) || bi < len(b) {
-		var aMissing, bMissing bool
-
-		if ai < len(a) {
-			av = a[ai]
-		} else {
-			av = Counter{}
-			aMissing = true
-		}
-
-		if bi < len(b) {
-			bv = b[bi]
-		} else {
-			bv = Counter{}
-			bMissing = true
-		}
-
-		switch {
-		case av.ID == bv.ID:
-			// We have a counter value for each side
-			if av.Value > bv.Value {
-				if result == Lesser {
-					return ConcurrentLesser
-				}
-				result = Greater
-			} else if av.Value < bv.Value {
-				if result == Greater {
-					return ConcurrentGreater
-				}
-				result = Lesser
-			}
-
-		case !aMissing && av.ID < bv.ID || bMissing:
-			// Value is missing on the b side
-			if av.Value > 0 {
-				if result == Lesser {
-					return ConcurrentLesser
-				}
-				result = Greater
-			}
-
-		case !bMissing && bv.ID < av.ID || aMissing:
-			// Value is missing on the a side
-			if bv.Value > 0 {
-				if result == Greater {
-					return ConcurrentGreater
-				}
-				result = Lesser
-			}
-		}
-
-		if ai < len(a) && (av.ID <= bv.ID || bMissing) {
-			ai++
-		}
-		if bi < len(b) && (bv.ID <= av.ID || aMissing) {
-			bi++
-		}
-	}
-
-	return result
-}

+ 0 - 249
lib/protocol/vector_compare_test.go

@@ -1,249 +0,0 @@
-// Copyright (C) 2015 The Protocol Authors.
-
-package protocol
-
-import (
-	"math"
-	"testing"
-)
-
-func TestCompare(t *testing.T) {
-	testcases := []struct {
-		a, b Vector
-		r    Ordering
-	}{
-		// Empty vectors are identical
-		{Vector{}, Vector{}, Equal},
-		{Vector{}, nil, Equal},
-		{nil, Vector{}, Equal},
-		{nil, Vector{Counter{42, 0}}, Equal},
-		{Vector{}, Vector{Counter{42, 0}}, Equal},
-		{Vector{Counter{42, 0}}, nil, Equal},
-		{Vector{Counter{42, 0}}, Vector{}, Equal},
-
-		// Zero is the implied value for a missing Counter
-		{
-			Vector{Counter{42, 0}},
-			Vector{Counter{77, 0}},
-			Equal,
-		},
-
-		// Equal vectors are equal
-		{
-			Vector{Counter{42, 33}},
-			Vector{Counter{42, 33}},
-			Equal,
-		},
-		{
-			Vector{Counter{42, 33}, Counter{77, 24}},
-			Vector{Counter{42, 33}, Counter{77, 24}},
-			Equal,
-		},
-
-		// These a-vectors are all greater than the b-vector
-		{
-			Vector{Counter{42, 1}},
-			nil,
-			Greater,
-		},
-		{
-			Vector{Counter{42, 1}},
-			Vector{},
-			Greater,
-		},
-		{
-			Vector{Counter{0, 1}},
-			Vector{Counter{0, 0}},
-			Greater,
-		},
-		{
-			Vector{Counter{42, 1}},
-			Vector{Counter{42, 0}},
-			Greater,
-		},
-		{
-			Vector{Counter{math.MaxUint64, 1}},
-			Vector{Counter{math.MaxUint64, 0}},
-			Greater,
-		},
-		{
-			Vector{Counter{0, math.MaxUint64}},
-			Vector{Counter{0, 0}},
-			Greater,
-		},
-		{
-			Vector{Counter{42, math.MaxUint64}},
-			Vector{Counter{42, 0}},
-			Greater,
-		},
-		{
-			Vector{Counter{math.MaxUint64, math.MaxUint64}},
-			Vector{Counter{math.MaxUint64, 0}},
-			Greater,
-		},
-		{
-			Vector{Counter{0, math.MaxUint64}},
-			Vector{Counter{0, math.MaxUint64 - 1}},
-			Greater,
-		},
-		{
-			Vector{Counter{42, math.MaxUint64}},
-			Vector{Counter{42, math.MaxUint64 - 1}},
-			Greater,
-		},
-		{
-			Vector{Counter{math.MaxUint64, math.MaxUint64}},
-			Vector{Counter{math.MaxUint64, math.MaxUint64 - 1}},
-			Greater,
-		},
-		{
-			Vector{Counter{42, 2}},
-			Vector{Counter{42, 1}},
-			Greater,
-		},
-		{
-			Vector{Counter{22, 22}, Counter{42, 2}},
-			Vector{Counter{22, 22}, Counter{42, 1}},
-			Greater,
-		},
-		{
-			Vector{Counter{42, 2}, Counter{77, 3}},
-			Vector{Counter{42, 1}, Counter{77, 3}},
-			Greater,
-		},
-		{
-			Vector{Counter{22, 22}, Counter{42, 2}, Counter{77, 3}},
-			Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
-			Greater,
-		},
-		{
-			Vector{Counter{22, 23}, Counter{42, 2}, Counter{77, 4}},
-			Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
-			Greater,
-		},
-
-		// These a-vectors are all lesser than the b-vector
-		{nil, Vector{Counter{42, 1}}, Lesser},
-		{Vector{}, Vector{Counter{42, 1}}, Lesser},
-		{
-			Vector{Counter{42, 0}},
-			Vector{Counter{42, 1}},
-			Lesser,
-		},
-		{
-			Vector{Counter{42, 1}},
-			Vector{Counter{42, 2}},
-			Lesser,
-		},
-		{
-			Vector{Counter{22, 22}, Counter{42, 1}},
-			Vector{Counter{22, 22}, Counter{42, 2}},
-			Lesser,
-		},
-		{
-			Vector{Counter{42, 1}, Counter{77, 3}},
-			Vector{Counter{42, 2}, Counter{77, 3}},
-			Lesser,
-		},
-		{
-			Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
-			Vector{Counter{22, 22}, Counter{42, 2}, Counter{77, 3}},
-			Lesser,
-		},
-		{
-			Vector{Counter{22, 22}, Counter{42, 1}, Counter{77, 3}},
-			Vector{Counter{22, 23}, Counter{42, 2}, Counter{77, 4}},
-			Lesser,
-		},
-
-		// These are all in conflict
-		{
-			Vector{Counter{42, 2}},
-			Vector{Counter{43, 1}},
-			ConcurrentGreater,
-		},
-		{
-			Vector{Counter{43, 1}},
-			Vector{Counter{42, 2}},
-			ConcurrentLesser,
-		},
-		{
-			Vector{Counter{22, 23}, Counter{42, 1}},
-			Vector{Counter{22, 22}, Counter{42, 2}},
-			ConcurrentGreater,
-		},
-		{
-			Vector{Counter{22, 21}, Counter{42, 2}},
-			Vector{Counter{22, 22}, Counter{42, 1}},
-			ConcurrentLesser,
-		},
-		{
-			Vector{Counter{22, 21}, Counter{42, 2}, Counter{43, 1}},
-			Vector{Counter{20, 1}, Counter{22, 22}, Counter{42, 1}},
-			ConcurrentLesser,
-		},
-	}
-
-	for i, tc := range testcases {
-		// Test real Compare
-		if r := tc.a.Compare(tc.b); r != tc.r {
-			t.Errorf("%d: %+v.Compare(%+v) == %v (expected %v)", i, tc.a, tc.b, r, tc.r)
-		}
-
-		// Test convenience functions
-		switch tc.r {
-		case Greater:
-			if tc.a.Equal(tc.b) {
-				t.Errorf("%+v == %+v", tc.a, tc.b)
-			}
-			if tc.a.Concurrent(tc.b) {
-				t.Errorf("%+v concurrent %+v", tc.a, tc.b)
-			}
-			if !tc.a.GreaterEqual(tc.b) {
-				t.Errorf("%+v not >= %+v", tc.a, tc.b)
-			}
-			if tc.a.LesserEqual(tc.b) {
-				t.Errorf("%+v <= %+v", tc.a, tc.b)
-			}
-		case Lesser:
-			if tc.a.Concurrent(tc.b) {
-				t.Errorf("%+v concurrent %+v", tc.a, tc.b)
-			}
-			if tc.a.Equal(tc.b) {
-				t.Errorf("%+v == %+v", tc.a, tc.b)
-			}
-			if tc.a.GreaterEqual(tc.b) {
-				t.Errorf("%+v >= %+v", tc.a, tc.b)
-			}
-			if !tc.a.LesserEqual(tc.b) {
-				t.Errorf("%+v not <= %+v", tc.a, tc.b)
-			}
-		case Equal:
-			if tc.a.Concurrent(tc.b) {
-				t.Errorf("%+v concurrent %+v", tc.a, tc.b)
-			}
-			if !tc.a.Equal(tc.b) {
-				t.Errorf("%+v not == %+v", tc.a, tc.b)
-			}
-			if !tc.a.GreaterEqual(tc.b) {
-				t.Errorf("%+v not <= %+v", tc.a, tc.b)
-			}
-			if !tc.a.LesserEqual(tc.b) {
-				t.Errorf("%+v not <= %+v", tc.a, tc.b)
-			}
-		case ConcurrentLesser, ConcurrentGreater:
-			if !tc.a.Concurrent(tc.b) {
-				t.Errorf("%+v not concurrent %+v", tc.a, tc.b)
-			}
-			if tc.a.Equal(tc.b) {
-				t.Errorf("%+v == %+v", tc.a, tc.b)
-			}
-			if tc.a.GreaterEqual(tc.b) {
-				t.Errorf("%+v >= %+v", tc.a, tc.b)
-			}
-			if tc.a.LesserEqual(tc.b) {
-				t.Errorf("%+v <= %+v", tc.a, tc.b)
-			}
-		}
-	}
-}

+ 264 - 30
lib/protocol/vector_test.go

@@ -2,7 +2,10 @@
 
 
 package protocol
 package protocol
 
 
-import "testing"
+import (
+	"math"
+	"testing"
+)
 
 
 func TestUpdate(t *testing.T) {
 func TestUpdate(t *testing.T) {
 	var v Vector
 	var v Vector
@@ -10,7 +13,7 @@ func TestUpdate(t *testing.T) {
 	// Append
 	// Append
 
 
 	v = v.Update(42)
 	v = v.Update(42)
-	expected := Vector{Counter{42, 1}}
+	expected := Vector{[]Counter{{42, 1}}}
 
 
 	if v.Compare(expected) != Equal {
 	if v.Compare(expected) != Equal {
 		t.Errorf("Update error, %+v != %+v", v, expected)
 		t.Errorf("Update error, %+v != %+v", v, expected)
@@ -19,7 +22,7 @@ func TestUpdate(t *testing.T) {
 	// Insert at front
 	// Insert at front
 
 
 	v = v.Update(36)
 	v = v.Update(36)
-	expected = Vector{Counter{36, 1}, Counter{42, 1}}
+	expected = Vector{[]Counter{{36, 1}, {42, 1}}}
 
 
 	if v.Compare(expected) != Equal {
 	if v.Compare(expected) != Equal {
 		t.Errorf("Update error, %+v != %+v", v, expected)
 		t.Errorf("Update error, %+v != %+v", v, expected)
@@ -28,7 +31,7 @@ func TestUpdate(t *testing.T) {
 	// Insert in moddle
 	// Insert in moddle
 
 
 	v = v.Update(37)
 	v = v.Update(37)
-	expected = Vector{Counter{36, 1}, Counter{37, 1}, Counter{42, 1}}
+	expected = Vector{[]Counter{{36, 1}, {37, 1}, {42, 1}}}
 
 
 	if v.Compare(expected) != Equal {
 	if v.Compare(expected) != Equal {
 		t.Errorf("Update error, %+v != %+v", v, expected)
 		t.Errorf("Update error, %+v != %+v", v, expected)
@@ -37,7 +40,7 @@ func TestUpdate(t *testing.T) {
 	// Update existing
 	// Update existing
 
 
 	v = v.Update(37)
 	v = v.Update(37)
-	expected = Vector{Counter{36, 1}, Counter{37, 2}, Counter{42, 1}}
+	expected = Vector{[]Counter{{36, 1}, {37, 2}, {42, 1}}}
 
 
 	if v.Compare(expected) != Equal {
 	if v.Compare(expected) != Equal {
 		t.Errorf("Update error, %+v != %+v", v, expected)
 		t.Errorf("Update error, %+v != %+v", v, expected)
@@ -45,7 +48,7 @@ func TestUpdate(t *testing.T) {
 }
 }
 
 
 func TestCopy(t *testing.T) {
 func TestCopy(t *testing.T) {
-	v0 := Vector{Counter{42, 1}}
+	v0 := Vector{[]Counter{{42, 1}}}
 	v1 := v0.Copy()
 	v1 := v0.Copy()
 	v1.Update(42)
 	v1.Update(42)
 	if v0.Compare(v1) != Lesser {
 	if v0.Compare(v1) != Lesser {
@@ -64,52 +67,52 @@ func TestMerge(t *testing.T) {
 			Vector{},
 			Vector{},
 		},
 		},
 		{
 		{
-			Vector{Counter{22, 1}, Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
 		},
 		},
 
 
 		// Appends
 		// Appends
 		{
 		{
 			Vector{},
 			Vector{},
-			Vector{Counter{22, 1}, Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
 		},
 		},
 		{
 		{
-			Vector{Counter{22, 1}},
-			Vector{Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
+			Vector{[]Counter{{22, 1}}},
+			Vector{[]Counter{{42, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
 		},
 		},
 		{
 		{
-			Vector{Counter{22, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
+			Vector{[]Counter{{22, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
 		},
 		},
 
 
 		// Insert
 		// Insert
 		{
 		{
-			Vector{Counter{22, 1}, Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{23, 2}, Counter{42, 1}},
-			Vector{Counter{22, 1}, Counter{23, 2}, Counter{42, 1}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
+			Vector{[]Counter{{22, 1}, {23, 2}, {42, 1}}},
+			Vector{[]Counter{{22, 1}, {23, 2}, {42, 1}}},
 		},
 		},
 		{
 		{
-			Vector{Counter{42, 1}},
-			Vector{Counter{22, 1}},
-			Vector{Counter{22, 1}, Counter{42, 1}},
+			Vector{[]Counter{{42, 1}}},
+			Vector{[]Counter{{22, 1}}},
+			Vector{[]Counter{{22, 1}, {42, 1}}},
 		},
 		},
 
 
 		// Update
 		// Update
 		{
 		{
-			Vector{Counter{22, 1}, Counter{42, 2}},
-			Vector{Counter{22, 2}, Counter{42, 1}},
-			Vector{Counter{22, 2}, Counter{42, 2}},
+			Vector{[]Counter{{22, 1}, {42, 2}}},
+			Vector{[]Counter{{22, 2}, {42, 1}}},
+			Vector{[]Counter{{22, 2}, {42, 2}}},
 		},
 		},
 
 
 		// All of the above
 		// All of the above
 		{
 		{
-			Vector{Counter{10, 1}, Counter{20, 2}, Counter{30, 1}},
-			Vector{Counter{5, 1}, Counter{10, 2}, Counter{15, 1}, Counter{20, 1}, Counter{25, 1}, Counter{35, 1}},
-			Vector{Counter{5, 1}, Counter{10, 2}, Counter{15, 1}, Counter{20, 2}, Counter{25, 1}, Counter{30, 1}, Counter{35, 1}},
+			Vector{[]Counter{{10, 1}, {20, 2}, {30, 1}}},
+			Vector{[]Counter{{5, 1}, {10, 2}, {15, 1}, {20, 1}, {25, 1}, {35, 1}}},
+			Vector{[]Counter{{5, 1}, {10, 2}, {15, 1}, {20, 2}, {25, 1}, {30, 1}, {35, 1}}},
 		},
 		},
 	}
 	}
 
 
@@ -121,7 +124,7 @@ func TestMerge(t *testing.T) {
 }
 }
 
 
 func TestCounterValue(t *testing.T) {
 func TestCounterValue(t *testing.T) {
-	v0 := Vector{Counter{42, 1}, Counter{64, 5}}
+	v0 := Vector{[]Counter{{42, 1}, {64, 5}}}
 	if v0.Counter(42) != 1 {
 	if v0.Counter(42) != 1 {
 		t.Errorf("Counter error, %d != %d", v0.Counter(42), 1)
 		t.Errorf("Counter error, %d != %d", v0.Counter(42), 1)
 	}
 	}
@@ -132,3 +135,234 @@ func TestCounterValue(t *testing.T) {
 		t.Errorf("Counter error, %d != %d", v0.Counter(72), 0)
 		t.Errorf("Counter error, %d != %d", v0.Counter(72), 0)
 	}
 	}
 }
 }
+
+func TestCompare(t *testing.T) {
+	testcases := []struct {
+		a, b Vector
+		r    Ordering
+	}{
+		// Empty vectors are identical
+		{Vector{}, Vector{}, Equal},
+		{Vector{}, Vector{[]Counter{{42, 0}}}, Equal},
+		{Vector{[]Counter{Counter{42, 0}}}, Vector{}, Equal},
+
+		// Zero is the implied value for a missing Counter
+		{
+			Vector{[]Counter{{42, 0}}},
+			Vector{[]Counter{{77, 0}}},
+			Equal,
+		},
+
+		// Equal vectors are equal
+		{
+			Vector{[]Counter{{42, 33}}},
+			Vector{[]Counter{{42, 33}}},
+			Equal,
+		},
+		{
+			Vector{[]Counter{{42, 33}, {77, 24}}},
+			Vector{[]Counter{{42, 33}, {77, 24}}},
+			Equal,
+		},
+
+		// These a-vectors are all greater than the b-vector
+		{
+			Vector{[]Counter{{42, 1}}},
+			Vector{},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{0, 1}}},
+			Vector{[]Counter{{0, 0}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{42, 1}}},
+			Vector{[]Counter{{42, 0}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{math.MaxUint64, 1}}},
+			Vector{[]Counter{{math.MaxUint64, 0}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{0, math.MaxUint64}}},
+			Vector{[]Counter{{0, 0}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{42, math.MaxUint64}}},
+			Vector{[]Counter{{42, 0}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{math.MaxUint64, math.MaxUint64}}},
+			Vector{[]Counter{{math.MaxUint64, 0}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{0, math.MaxUint64}}},
+			Vector{[]Counter{{0, math.MaxUint64 - 1}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{42, math.MaxUint64}}},
+			Vector{[]Counter{{42, math.MaxUint64 - 1}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{math.MaxUint64, math.MaxUint64}}},
+			Vector{[]Counter{{math.MaxUint64, math.MaxUint64 - 1}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{42, 2}}},
+			Vector{[]Counter{{42, 1}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{22, 22}, {42, 2}}},
+			Vector{[]Counter{{22, 22}, {42, 1}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{42, 2}, {77, 3}}},
+			Vector{[]Counter{{42, 1}, {77, 3}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{22, 22}, {42, 2}, {77, 3}}},
+			Vector{[]Counter{{22, 22}, {42, 1}, {77, 3}}},
+			Greater,
+		},
+		{
+			Vector{[]Counter{{22, 23}, {42, 2}, {77, 4}}},
+			Vector{[]Counter{{22, 22}, {42, 1}, {77, 3}}},
+			Greater,
+		},
+
+		// These a-vectors are all lesser than the b-vector
+		{Vector{}, Vector{[]Counter{{42, 1}}}, Lesser},
+		{
+			Vector{[]Counter{{42, 0}}},
+			Vector{[]Counter{{42, 1}}},
+			Lesser,
+		},
+		{
+			Vector{[]Counter{{42, 1}}},
+			Vector{[]Counter{{42, 2}}},
+			Lesser,
+		},
+		{
+			Vector{[]Counter{{22, 22}, {42, 1}}},
+			Vector{[]Counter{{22, 22}, {42, 2}}},
+			Lesser,
+		},
+		{
+			Vector{[]Counter{{42, 1}, {77, 3}}},
+			Vector{[]Counter{{42, 2}, {77, 3}}},
+			Lesser,
+		},
+		{
+			Vector{[]Counter{{22, 22}, {42, 1}, {77, 3}}},
+			Vector{[]Counter{{22, 22}, {42, 2}, {77, 3}}},
+			Lesser,
+		},
+		{
+			Vector{[]Counter{{22, 22}, {42, 1}, {77, 3}}},
+			Vector{[]Counter{{22, 23}, {42, 2}, {77, 4}}},
+			Lesser,
+		},
+
+		// These are all in conflict
+		{
+			Vector{[]Counter{{42, 2}}},
+			Vector{[]Counter{{43, 1}}},
+			ConcurrentGreater,
+		},
+		{
+			Vector{[]Counter{{43, 1}}},
+			Vector{[]Counter{{42, 2}}},
+			ConcurrentLesser,
+		},
+		{
+			Vector{[]Counter{{22, 23}, {42, 1}}},
+			Vector{[]Counter{{22, 22}, {42, 2}}},
+			ConcurrentGreater,
+		},
+		{
+			Vector{[]Counter{{22, 21}, {42, 2}}},
+			Vector{[]Counter{{22, 22}, {42, 1}}},
+			ConcurrentLesser,
+		},
+		{
+			Vector{[]Counter{{22, 21}, {42, 2}, {43, 1}}},
+			Vector{[]Counter{{20, 1}, {22, 22}, {42, 1}}},
+			ConcurrentLesser,
+		},
+	}
+
+	for i, tc := range testcases {
+		// Test real Compare
+		if r := tc.a.Compare(tc.b); r != tc.r {
+			t.Errorf("%d: %+v.Compare(%+v) == %v (expected %v)", i, tc.a, tc.b, r, tc.r)
+		}
+
+		// Test convenience functions
+		switch tc.r {
+		case Greater:
+			if tc.a.Equal(tc.b) {
+				t.Errorf("%+v == %+v", tc.a, tc.b)
+			}
+			if tc.a.Concurrent(tc.b) {
+				t.Errorf("%+v concurrent %+v", tc.a, tc.b)
+			}
+			if !tc.a.GreaterEqual(tc.b) {
+				t.Errorf("%+v not >= %+v", tc.a, tc.b)
+			}
+			if tc.a.LesserEqual(tc.b) {
+				t.Errorf("%+v <= %+v", tc.a, tc.b)
+			}
+		case Lesser:
+			if tc.a.Concurrent(tc.b) {
+				t.Errorf("%+v concurrent %+v", tc.a, tc.b)
+			}
+			if tc.a.Equal(tc.b) {
+				t.Errorf("%+v == %+v", tc.a, tc.b)
+			}
+			if tc.a.GreaterEqual(tc.b) {
+				t.Errorf("%+v >= %+v", tc.a, tc.b)
+			}
+			if !tc.a.LesserEqual(tc.b) {
+				t.Errorf("%+v not <= %+v", tc.a, tc.b)
+			}
+		case Equal:
+			if tc.a.Concurrent(tc.b) {
+				t.Errorf("%+v concurrent %+v", tc.a, tc.b)
+			}
+			if !tc.a.Equal(tc.b) {
+				t.Errorf("%+v not == %+v", tc.a, tc.b)
+			}
+			if !tc.a.GreaterEqual(tc.b) {
+				t.Errorf("%+v not <= %+v", tc.a, tc.b)
+			}
+			if !tc.a.LesserEqual(tc.b) {
+				t.Errorf("%+v not <= %+v", tc.a, tc.b)
+			}
+		case ConcurrentLesser, ConcurrentGreater:
+			if !tc.a.Concurrent(tc.b) {
+				t.Errorf("%+v not concurrent %+v", tc.a, tc.b)
+			}
+			if tc.a.Equal(tc.b) {
+				t.Errorf("%+v == %+v", tc.a, tc.b)
+			}
+			if tc.a.GreaterEqual(tc.b) {
+				t.Errorf("%+v >= %+v", tc.a, tc.b)
+			}
+			if tc.a.LesserEqual(tc.b) {
+				t.Errorf("%+v <= %+v", tc.a, tc.b)
+			}
+		}
+	}
+}

+ 0 - 35
lib/protocol/vector_xdr.go

@@ -1,35 +0,0 @@
-// Copyright (C) 2015 The Protocol Authors.
-
-package protocol
-
-import "github.com/calmh/xdr"
-
-// This stuff is hacked up manually because genxdr doesn't support 'type
-// Vector []Counter' declarations and it was tricky when I tried to add it...
-
-func (v Vector) MarshalXDRInto(m *xdr.Marshaller) error {
-	m.MarshalUint32(uint32(len(v)))
-	for i := range v {
-		m.MarshalUint64(uint64(v[i].ID))
-		m.MarshalUint64(v[i].Value)
-	}
-	return m.Error
-}
-
-func (v *Vector) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
-	l := int(u.UnmarshalUint32())
-	if l > 1e6 {
-		return xdr.ElementSizeExceeded("number of counters", l, 1e6)
-	}
-	n := make(Vector, l)
-	for i := range n {
-		n[i].ID = ShortID(u.UnmarshalUint64())
-		n[i].Value = u.UnmarshalUint64()
-	}
-	*v = n
-	return u.Error
-}
-
-func (v Vector) XDRSize() int {
-	return 4 + 16*len(v)
-}

+ 4 - 4
lib/protocol/wireformat.go

@@ -12,7 +12,7 @@ type wireFormatConnection struct {
 	Connection
 	Connection
 }
 }
 
 
-func (c wireFormatConnection) Index(folder string, fs []FileInfo, flags uint32, options []Option) error {
+func (c wireFormatConnection) Index(folder string, fs []FileInfo) error {
 	var myFs = make([]FileInfo, len(fs))
 	var myFs = make([]FileInfo, len(fs))
 	copy(myFs, fs)
 	copy(myFs, fs)
 
 
@@ -20,10 +20,10 @@ func (c wireFormatConnection) Index(folder string, fs []FileInfo, flags uint32,
 		myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
 		myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
 	}
 	}
 
 
-	return c.Connection.Index(folder, myFs, flags, options)
+	return c.Connection.Index(folder, myFs)
 }
 }
 
 
-func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo, flags uint32, options []Option) error {
+func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo) error {
 	var myFs = make([]FileInfo, len(fs))
 	var myFs = make([]FileInfo, len(fs))
 	copy(myFs, fs)
 	copy(myFs, fs)
 
 
@@ -31,7 +31,7 @@ func (c wireFormatConnection) IndexUpdate(folder string, fs []FileInfo, flags ui
 		myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
 		myFs[i].Name = norm.NFC.String(filepath.ToSlash(myFs[i].Name))
 	}
 	}
 
 
-	return c.Connection.IndexUpdate(folder, myFs, flags, options)
+	return c.Connection.IndexUpdate(folder, myFs)
 }
 }
 
 
 func (c wireFormatConnection) Request(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) {
 func (c wireFormatConnection) Request(folder, name string, offset int64, size int, hash []byte, fromTemporary bool) ([]byte, error) {

+ 1 - 1
lib/scanner/blockqueue.go

@@ -71,7 +71,7 @@ func hashFiles(dir string, blockSize int, outbox, inbox chan protocol.FileInfo,
 				panic("Bug. Asked to hash a directory or a deleted file.")
 				panic("Bug. Asked to hash a directory or a deleted file.")
 			}
 			}
 
 
-			blocks, err := HashFile(filepath.Join(dir, f.Name), blockSize, f.CachedSize, counter)
+			blocks, err := HashFile(filepath.Join(dir, f.Name), blockSize, f.Size, counter)
 			if err != nil {
 			if err != nil {
 				l.Debugln("hash error:", f.Name, err)
 				l.Debugln("hash error:", f.Name, err)
 				continue
 				continue

+ 29 - 35
lib/scanner/walk.go

@@ -168,7 +168,7 @@ func (w *walker) walk() (chan protocol.FileInfo, error) {
 
 
 		for file := range toHashChan {
 		for file := range toHashChan {
 			filesToHash = append(filesToHash, file)
 			filesToHash = append(filesToHash, file)
-			total += int64(file.CachedSize)
+			total += int64(file.Size)
 		}
 		}
 
 
 		realToHashChan := make(chan protocol.FileInfo)
 		realToHashChan := make(chan protocol.FileInfo)
@@ -309,25 +309,22 @@ func (w *walker) walkRegular(relPath string, info os.FileInfo, mtime time.Time,
 	//  - was not invalid (since it looks valid now)
 	//  - was not invalid (since it looks valid now)
 	//  - has the same size as previously
 	//  - has the same size as previously
 	cf, ok := w.CurrentFiler.CurrentFile(relPath)
 	cf, ok := w.CurrentFiler.CurrentFile(relPath)
-	permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, curMode)
+	permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Permissions, curMode)
 	if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == mtime.Unix() && !cf.IsDirectory() &&
 	if ok && permUnchanged && !cf.IsDeleted() && cf.Modified == mtime.Unix() && !cf.IsDirectory() &&
-		!cf.IsSymlink() && !cf.IsInvalid() && cf.Size() == info.Size() {
+		!cf.IsSymlink() && !cf.IsInvalid() && cf.Size == info.Size() {
 		return nil
 		return nil
 	}
 	}
 
 
 	l.Debugln("rescan:", cf, mtime.Unix(), info.Mode()&os.ModePerm)
 	l.Debugln("rescan:", cf, mtime.Unix(), info.Mode()&os.ModePerm)
 
 
-	var flags = curMode & uint32(maskModePerm)
-	if w.IgnorePerms {
-		flags = protocol.FlagNoPermBits | 0666
-	}
-
 	f := protocol.FileInfo{
 	f := protocol.FileInfo{
-		Name:       relPath,
-		Version:    cf.Version.Update(w.ShortID),
-		Flags:      flags,
-		Modified:   mtime.Unix(),
-		CachedSize: info.Size(),
+		Name:          relPath,
+		Type:          protocol.FileInfoTypeFile,
+		Version:       cf.Version.Update(w.ShortID),
+		Permissions:   curMode & uint32(maskModePerm),
+		NoPermissions: w.IgnorePerms,
+		Modified:      mtime.Unix(),
+		Size:          info.Size(),
 	}
 	}
 	l.Debugln("to hash:", relPath, f)
 	l.Debugln("to hash:", relPath, f)
 
 
@@ -349,22 +346,18 @@ func (w *walker) walkDir(relPath string, info os.FileInfo, mtime time.Time, dcha
 	//  - was not a symlink (since it's a directory now)
 	//  - was not a symlink (since it's a directory now)
 	//  - was not invalid (since it looks valid now)
 	//  - was not invalid (since it looks valid now)
 	cf, ok := w.CurrentFiler.CurrentFile(relPath)
 	cf, ok := w.CurrentFiler.CurrentFile(relPath)
-	permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Flags, uint32(info.Mode()))
+	permUnchanged := w.IgnorePerms || !cf.HasPermissionBits() || PermsEqual(cf.Permissions, uint32(info.Mode()))
 	if ok && permUnchanged && !cf.IsDeleted() && cf.IsDirectory() && !cf.IsSymlink() && !cf.IsInvalid() {
 	if ok && permUnchanged && !cf.IsDeleted() && cf.IsDirectory() && !cf.IsSymlink() && !cf.IsInvalid() {
 		return nil
 		return nil
 	}
 	}
 
 
-	flags := uint32(protocol.FlagDirectory)
-	if w.IgnorePerms {
-		flags |= protocol.FlagNoPermBits | 0777
-	} else {
-		flags |= uint32(info.Mode() & maskModePerm)
-	}
 	f := protocol.FileInfo{
 	f := protocol.FileInfo{
-		Name:     relPath,
-		Version:  cf.Version.Update(w.ShortID),
-		Flags:    flags,
-		Modified: mtime.Unix(),
+		Name:          relPath,
+		Type:          protocol.FileInfoTypeDirectory,
+		Version:       cf.Version.Update(w.ShortID),
+		Permissions:   uint32(info.Mode() & maskModePerm),
+		NoPermissions: w.IgnorePerms,
+		Modified:      mtime.Unix(),
 	}
 	}
 	l.Debugln("dir:", relPath, f)
 	l.Debugln("dir:", relPath, f)
 
 
@@ -420,11 +413,12 @@ func (w *walker) walkSymlink(absPath, relPath string, dchan chan protocol.FileIn
 	}
 	}
 
 
 	f := protocol.FileInfo{
 	f := protocol.FileInfo{
-		Name:     relPath,
-		Version:  cf.Version.Update(w.ShortID),
-		Flags:    uint32(protocol.FlagSymlink | protocol.FlagNoPermBits | 0666 | SymlinkFlags(targetType)),
-		Modified: 0,
-		Blocks:   blocks,
+		Name:          relPath,
+		Type:          SymlinkType(targetType),
+		Version:       cf.Version.Update(w.ShortID),
+		Modified:      0,
+		NoPermissions: true, // Symlinks don't have permissions of their own
+		Blocks:        blocks,
 	}
 	}
 
 
 	l.Debugln("symlink changedb:", absPath, f)
 	l.Debugln("symlink changedb:", absPath, f)
@@ -519,21 +513,21 @@ func SymlinkTypeEqual(disk symlinks.TargetType, f protocol.FileInfo) bool {
 	case symlinks.TargetUnknown:
 	case symlinks.TargetUnknown:
 		return true
 		return true
 	case symlinks.TargetDirectory:
 	case symlinks.TargetDirectory:
-		return f.IsDirectory() && f.Flags&protocol.FlagSymlinkMissingTarget == 0
+		return f.Type == protocol.FileInfoTypeSymlinkDirectory
 	case symlinks.TargetFile:
 	case symlinks.TargetFile:
-		return !f.IsDirectory() && f.Flags&protocol.FlagSymlinkMissingTarget == 0
+		return f.Type == protocol.FileInfoTypeSymlinkFile
 	}
 	}
 	panic("unknown symlink TargetType")
 	panic("unknown symlink TargetType")
 }
 }
 
 
-func SymlinkFlags(t symlinks.TargetType) uint32 {
+func SymlinkType(t symlinks.TargetType) protocol.FileInfoType {
 	switch t {
 	switch t {
 	case symlinks.TargetFile:
 	case symlinks.TargetFile:
-		return 0
+		return protocol.FileInfoTypeSymlinkFile
 	case symlinks.TargetDirectory:
 	case symlinks.TargetDirectory:
-		return protocol.FlagDirectory
+		return protocol.FileInfoTypeSymlinkDirectory
 	case symlinks.TargetUnknown:
 	case symlinks.TargetUnknown:
-		return protocol.FlagSymlinkMissingTarget
+		return protocol.FileInfoTypeSymlinkUnknown
 	}
 	}
 	panic("unknown symlink TargetType")
 	panic("unknown symlink TargetType")
 }
 }

+ 19 - 19
lib/scanner/walk_test.go

@@ -28,9 +28,9 @@ import (
 )
 )
 
 
 type testfile struct {
 type testfile struct {
-	name string
-	size int
-	hash string
+	name   string
+	length int64
+	hash   string
 }
 }
 
 
 type testfileList []testfile
 type testfileList []testfile
@@ -322,7 +322,7 @@ func (l fileList) testfiles() testfileList {
 		if len(f.Blocks) > 1 {
 		if len(f.Blocks) > 1 {
 			panic("simple test case stuff only supports a single block per file")
 			panic("simple test case stuff only supports a single block per file")
 		}
 		}
-		testfiles[i] = testfile{name: f.Name, size: int(f.Size())}
+		testfiles[i] = testfile{name: f.Name, length: f.FileSize()}
 		if len(f.Blocks) == 1 {
 		if len(f.Blocks) == 1 {
 			testfiles[i].hash = fmt.Sprintf("%x", f.Blocks[0].Hash)
 			testfiles[i].hash = fmt.Sprintf("%x", f.Blocks[0].Hash)
 		}
 		}
@@ -334,7 +334,7 @@ func (l testfileList) String() string {
 	var b bytes.Buffer
 	var b bytes.Buffer
 	b.WriteString("{\n")
 	b.WriteString("{\n")
 	for _, f := range l {
 	for _, f := range l {
-		fmt.Fprintf(&b, "  %s (%d bytes): %s\n", f.name, f.size, f.hash)
+		fmt.Fprintf(&b, "  %s (%d bytes): %s\n", f.name, f.length, f.hash)
 	}
 	}
 	b.WriteString("}")
 	b.WriteString("}")
 	return b.String()
 	return b.String()
@@ -342,28 +342,28 @@ func (l testfileList) String() string {
 
 
 func TestSymlinkTypeEqual(t *testing.T) {
 func TestSymlinkTypeEqual(t *testing.T) {
 	testcases := []struct {
 	testcases := []struct {
-		onDiskType   symlinks.TargetType
-		inIndexFlags uint32
-		equal        bool
+		onDiskType symlinks.TargetType
+		fiType     protocol.FileInfoType
+		equal      bool
 	}{
 	}{
 		// File is only equal to file
 		// File is only equal to file
-		{symlinks.TargetFile, 0, true},
-		{symlinks.TargetFile, protocol.FlagDirectory, false},
-		{symlinks.TargetFile, protocol.FlagSymlinkMissingTarget, false},
+		{symlinks.TargetFile, protocol.FileInfoTypeSymlinkFile, true},
+		{symlinks.TargetFile, protocol.FileInfoTypeSymlinkDirectory, false},
+		{symlinks.TargetFile, protocol.FileInfoTypeSymlinkUnknown, false},
 		// Directory is only equal to directory
 		// Directory is only equal to directory
-		{symlinks.TargetDirectory, 0, false},
-		{symlinks.TargetDirectory, protocol.FlagDirectory, true},
-		{symlinks.TargetDirectory, protocol.FlagSymlinkMissingTarget, false},
+		{symlinks.TargetDirectory, protocol.FileInfoTypeSymlinkFile, false},
+		{symlinks.TargetDirectory, protocol.FileInfoTypeSymlinkDirectory, true},
+		{symlinks.TargetDirectory, protocol.FileInfoTypeSymlinkUnknown, false},
 		// Unknown is equal to anything
 		// Unknown is equal to anything
-		{symlinks.TargetUnknown, 0, true},
-		{symlinks.TargetUnknown, protocol.FlagDirectory, true},
-		{symlinks.TargetUnknown, protocol.FlagSymlinkMissingTarget, true},
+		{symlinks.TargetUnknown, protocol.FileInfoTypeSymlinkFile, true},
+		{symlinks.TargetUnknown, protocol.FileInfoTypeSymlinkDirectory, true},
+		{symlinks.TargetUnknown, protocol.FileInfoTypeSymlinkUnknown, true},
 	}
 	}
 
 
 	for _, tc := range testcases {
 	for _, tc := range testcases {
-		res := SymlinkTypeEqual(tc.onDiskType, protocol.FileInfo{Flags: tc.inIndexFlags})
+		res := SymlinkTypeEqual(tc.onDiskType, protocol.FileInfo{Type: tc.fiType})
 		if res != tc.equal {
 		if res != tc.equal {
-			t.Errorf("Incorrect result %v for %v, %v", res, tc.onDiskType, tc.inIndexFlags)
+			t.Errorf("Incorrect result %v for %v, %v", res, tc.onDiskType, tc.fiType)
 		}
 		}
 	}
 	}
 }
 }

+ 3 - 4
lib/symlinks/symlink_windows.go

@@ -13,7 +13,6 @@ import (
 	"path/filepath"
 	"path/filepath"
 
 
 	"github.com/syncthing/syncthing/lib/osutil"
 	"github.com/syncthing/syncthing/lib/osutil"
-	"github.com/syncthing/syncthing/lib/protocol"
 
 
 	"syscall"
 	"syscall"
 	"unicode/utf16"
 	"unicode/utf16"
@@ -55,7 +54,7 @@ func init() {
 	path := filepath.Join(base, "symlinktest")
 	path := filepath.Join(base, "symlinktest")
 	defer os.Remove(path)
 	defer os.Remove(path)
 
 
-	err := Create(path, base, protocol.FlagDirectory)
+	err := Create(path, base, TargetDirectory)
 	if err != nil {
 	if err != nil {
 		return
 		return
 	}
 	}
@@ -65,8 +64,8 @@ func init() {
 		return
 		return
 	}
 	}
 
 
-	target, flags, err := Read(path)
-	if err != nil || osutil.NativeFilename(target) != base || flags&protocol.FlagDirectory == 0 {
+	target, tt, err := Read(path)
+	if err != nil || osutil.NativeFilename(target) != base || tt != TargetDirectory {
 		return
 		return
 	}
 	}
 	Supported = true
 	Supported = true

+ 80 - 0
script/protofmt.go

@@ -0,0 +1,80 @@
+package main
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"regexp"
+	"strings"
+	"text/tabwriter"
+)
+
+func main() {
+	flag.Parse()
+	file := flag.Arg(0)
+	in, err := os.Open(file)
+	if err != nil {
+		log.Fatal(err)
+	}
+	out, err := os.Create(file + ".tmp")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if err := formatProto(in, out); err != nil {
+		log.Fatal(err)
+	}
+	if err := os.Rename(file+".tmp", file); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func formatProto(in io.Reader, out io.Writer) error {
+	sc := bufio.NewScanner(in)
+	lineExp := regexp.MustCompile(`([^=]+)\s+([^=\s]+?)\s*=(.+)`)
+	var tw *tabwriter.Writer
+	for sc.Scan() {
+		line := sc.Text()
+		if strings.HasPrefix(line, "//") {
+			if _, err := fmt.Fprintln(out, line); err != nil {
+				return err
+			}
+			continue
+		}
+
+		ms := lineExp.FindStringSubmatch(line)
+		for i := range ms {
+			ms[i] = strings.TrimSpace(ms[i])
+		}
+		if len(ms) == 4 && ms[1] != "option" {
+			typ := strings.Join(strings.Fields(ms[1]), " ")
+			name := ms[2]
+			id := ms[3]
+			if tw == nil {
+				tw = tabwriter.NewWriter(out, 4, 4, 1, ' ', 0)
+			}
+			if typ == "" {
+				// We're in an enum
+				fmt.Fprintf(tw, "\t%s\t= %s\n", name, id)
+			} else {
+				// Message
+				fmt.Fprintf(tw, "\t%s\t%s\t= %s\n", typ, name, id)
+			}
+		} else {
+			if tw != nil {
+				if err := tw.Flush(); err != nil {
+					return err
+				}
+				tw = nil
+			}
+			if _, err := fmt.Fprintln(out, line); err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}

+ 36 - 0
vendor/github.com/gogo/protobuf/LICENSE

@@ -0,0 +1,36 @@
+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.
+

+ 89 - 0
vendor/github.com/gogo/protobuf/codec/codec.go

@@ -0,0 +1,89 @@
+// Copyright (c) 2015, 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 codec
+
+import (
+	"github.com/gogo/protobuf/proto"
+)
+
+type Codec interface {
+	Marshal(v interface{}) ([]byte, error)
+	Unmarshal(data []byte, v interface{}) error
+	String() string
+}
+
+type marshaler interface {
+	MarshalTo(data []byte) (n int, err error)
+}
+
+func getSize(v interface{}) (int, bool) {
+	if sz, ok := v.(interface {
+		Size() (n int)
+	}); ok {
+		return sz.Size(), true
+	} else if sz, ok := v.(interface {
+		ProtoSize() (n int)
+	}); ok {
+		return sz.ProtoSize(), true
+	} else {
+		return 0, false
+	}
+}
+
+type codec struct {
+	buf []byte
+}
+
+func (this *codec) String() string {
+	return "proto"
+}
+
+func New(size int) Codec {
+	return &codec{make([]byte, size)}
+}
+
+func (this *codec) Marshal(v interface{}) ([]byte, error) {
+	if m, ok := v.(marshaler); ok {
+		n, ok := getSize(v)
+		if !ok {
+			return proto.Marshal(v.(proto.Message))
+		}
+		if n > len(this.buf) {
+			this.buf = make([]byte, n)
+		}
+		_, err := m.MarshalTo(this.buf)
+		if err != nil {
+			return nil, err
+		}
+		return this.buf[:n], nil
+	}
+	return proto.Marshal(v.(proto.Message))
+}
+
+func (this *codec) Unmarshal(data []byte, v interface{}) error {
+	return proto.Unmarshal(data, v.(proto.Message))
+}

+ 168 - 0
vendor/github.com/gogo/protobuf/gogoproto/doc.go

@@ -0,0 +1,168 @@
+// 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
+//
+// 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 gogoproto provides extensions for protocol buffers to achieve:
+
+  - fast marshalling and unmarshalling.
+  - peace of mind by optionally generating test and benchmark code.
+  - more canonical Go structures.
+  - less typing by optionally generating extra helper code.
+  - goprotobuf compatibility
+
+More Canonical Go Structures
+
+A lot of time working with a goprotobuf struct will lead you to a place where you create another struct that is easier to work with and then have a function to copy the values between the two structs.
+You might also find that basic structs that started their life as part of an API need to be sent over the wire. With gob, you could just send it. With goprotobuf, you need to make a parallel struct.
+Gogoprotobuf tries to fix these problems with the nullable, embed, customtype and customname field extensions.
+
+  - nullable, if false, a field is generated without a pointer (see warning below).
+  - embed, if true, the field is generated as an embedded field.
+  - customtype, It works with the Marshal and Unmarshal methods, to allow you to have your own types in your struct, but marshal to bytes. For example, custom.Uuid or custom.Fixed128
+  - customname (beta), Changes the generated fieldname. This is especially useful when generated methods conflict with fieldnames.
+  - casttype (beta), Changes the generated fieldtype.  All generated code assumes that this type is castable to the protocol buffer field type.  It does not work for structs or enums.
+  - castkey (beta), Changes the generated fieldtype for a map key.  All generated code assumes that this type is castable to the protocol buffer field type.  Only supported on maps.
+  - castvalue (beta), Changes the generated fieldtype for a map value.  All generated code assumes that this type is castable to the protocol buffer field type.  Only supported on maps.
+
+Warning about nullable: According to the Protocol Buffer specification, you should be able to tell whether a field is set or unset. With the option nullable=false this feature is lost, since your non-nullable fields will always be set. It can be seen as a layer on top of Protocol Buffers, where before and after marshalling all non-nullable fields are set and they cannot be unset.
+
+Let us look at:
+
+	github.com/gogo/protobuf/test/example/example.proto
+
+for a quicker overview.
+
+The following message:
+
+  package test;
+
+  import "github.com/gogo/protobuf/gogoproto/gogo.proto";
+
+	message A {
+		optional string Description = 1 [(gogoproto.nullable) = false];
+		optional int64 Number = 2 [(gogoproto.nullable) = false];
+		optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
+	}
+
+Will generate a go struct which looks a lot like this:
+
+	type A struct {
+		Description string
+		Number      int64
+		Id          github_com_gogo_protobuf_test_custom.Uuid
+	}
+
+You will see there are no pointers, since all fields are non-nullable.
+You will also see a custom type which marshals to a string.
+Be warned it is your responsibility to test your custom types thoroughly.
+You should think of every possible empty and nil case for your marshaling, unmarshaling and size methods.
+
+Next we will embed the message A in message B.
+
+	message B {
+		optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
+		repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
+	}
+
+See below that A is embedded in B.
+
+	type B struct {
+		A
+		G []github_com_gogo_protobuf_test_custom.Uint128
+	}
+
+Also see the repeated custom type.
+
+	type Uint128 [2]uint64
+
+Next we will create a custom name for one of our fields.
+
+	message C {
+		optional int64 size = 1 [(gogoproto.customname) = "MySize"];
+	}
+
+See below that the field's name is MySize and not Size.
+
+	type C struct {
+		MySize		*int64
+	}
+
+The is useful when having a protocol buffer message with a field name which conflicts with a generated method.
+As an example, having a field name size and using the sizer plugin to generate a Size method will cause a go compiler error.
+Using customname you can fix this error without changing the field name.
+This is typically useful when working with a protocol buffer that was designed before these methods and/or the go language were avialable.
+
+Gogoprotobuf also has some more subtle changes, these could be changed back:
+
+  - the generated package name for imports do not have the extra /filename.pb,
+  but are actually the imports specified in the .proto file.
+
+Gogoprotobuf also has lost some features which should be brought back with time:
+
+  - Marshalling and unmarshalling with reflect and without the unsafe package,
+  this requires work in pointer_reflect.go
+
+Why does nullable break protocol buffer specifications:
+
+The protocol buffer specification states, somewhere, that you should be able to tell whether a
+field is set or unset.  With the option nullable=false this feature is lost,
+since your non-nullable fields will always be set.  It can be seen as a layer on top of
+protocol buffers, where before and after marshalling all non-nullable fields are set
+and they cannot be unset.
+
+Goprotobuf Compatibility:
+
+Gogoprotobuf is compatible with Goprotobuf, because it is compatible with protocol buffers.
+Gogoprotobuf generates the same code as goprotobuf if no extensions are used.
+The enumprefix, getters and stringer extensions can be used to remove some of the unnecessary code generated by goprotobuf:
+
+  - gogoproto_import, if false, the generated code imports github.com/golang/protobuf/proto instead of github.com/gogo/protobuf/proto.
+  - goproto_enum_prefix, if false, generates the enum constant names without the messagetype prefix
+  - goproto_enum_stringer (experimental), if false, the enum is generated without the default string method, this is useful for rather using enum_stringer, or allowing you to write your own string method.
+  - goproto_getters, if false, the message is generated without get methods, this is useful when you would rather want to use face
+  - goproto_stringer, if false, the message is generated without the default string method, this is useful for rather using stringer, or allowing you to write your own string method.
+  - goproto_extensions_map (beta), if false, the extensions field is generated as type []byte instead of type map[int32]proto.Extension
+  - goproto_unrecognized (beta), if false, XXX_unrecognized field is not generated. This is useful in conjunction with gogoproto.nullable=false, to generate structures completely devoid of pointers and reduce GC pressure at the cost of losing information about unrecognized fields.
+
+Less Typing and Peace of Mind is explained in their specific plugin folders godoc:
+
+	- github.com/gogo/protobuf/plugin/<extension_name>
+
+If you do not use any of these extension the code that is generated
+will be the same as if goprotobuf has generated it.
+
+The most complete way to see examples is to look at
+
+	github.com/gogo/protobuf/test/thetest.proto
+
+Gogoprototest is a seperate project,
+because we want to keep gogoprotobuf independant of goprotobuf,
+but we still want to test it thoroughly.
+
+*/
+package gogoproto

+ 661 - 0
vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go

@@ -0,0 +1,661 @@
+// Code generated by protoc-gen-gogo.
+// source: gogo.proto
+// DO NOT EDIT!
+
+/*
+Package gogoproto is a generated protocol buffer package.
+
+It is generated from these files:
+	gogo.proto
+
+It has these top-level messages:
+*/
+package gogoproto
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+var E_GoprotoEnumPrefix = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.EnumOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         62001,
+	Name:          "gogoproto.goproto_enum_prefix",
+	Tag:           "varint,62001,opt,name=goproto_enum_prefix,json=goprotoEnumPrefix",
+}
+
+var E_GoprotoEnumStringer = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.EnumOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         62021,
+	Name:          "gogoproto.goproto_enum_stringer",
+	Tag:           "varint,62021,opt,name=goproto_enum_stringer,json=goprotoEnumStringer",
+}
+
+var E_EnumStringer = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.EnumOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         62022,
+	Name:          "gogoproto.enum_stringer",
+	Tag:           "varint,62022,opt,name=enum_stringer,json=enumStringer",
+}
+
+var E_EnumCustomname = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.EnumOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         62023,
+	Name:          "gogoproto.enum_customname",
+	Tag:           "bytes,62023,opt,name=enum_customname,json=enumCustomname",
+}
+
+var E_EnumvalueCustomname = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.EnumValueOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         66001,
+	Name:          "gogoproto.enumvalue_customname",
+	Tag:           "bytes,66001,opt,name=enumvalue_customname,json=enumvalueCustomname",
+}
+
+var E_GoprotoGettersAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63001,
+	Name:          "gogoproto.goproto_getters_all",
+	Tag:           "varint,63001,opt,name=goproto_getters_all,json=goprotoGettersAll",
+}
+
+var E_GoprotoEnumPrefixAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63002,
+	Name:          "gogoproto.goproto_enum_prefix_all",
+	Tag:           "varint,63002,opt,name=goproto_enum_prefix_all,json=goprotoEnumPrefixAll",
+}
+
+var E_GoprotoStringerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63003,
+	Name:          "gogoproto.goproto_stringer_all",
+	Tag:           "varint,63003,opt,name=goproto_stringer_all,json=goprotoStringerAll",
+}
+
+var E_VerboseEqualAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63004,
+	Name:          "gogoproto.verbose_equal_all",
+	Tag:           "varint,63004,opt,name=verbose_equal_all,json=verboseEqualAll",
+}
+
+var E_FaceAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63005,
+	Name:          "gogoproto.face_all",
+	Tag:           "varint,63005,opt,name=face_all,json=faceAll",
+}
+
+var E_GostringAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63006,
+	Name:          "gogoproto.gostring_all",
+	Tag:           "varint,63006,opt,name=gostring_all,json=gostringAll",
+}
+
+var E_PopulateAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63007,
+	Name:          "gogoproto.populate_all",
+	Tag:           "varint,63007,opt,name=populate_all,json=populateAll",
+}
+
+var E_StringerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63008,
+	Name:          "gogoproto.stringer_all",
+	Tag:           "varint,63008,opt,name=stringer_all,json=stringerAll",
+}
+
+var E_OnlyoneAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63009,
+	Name:          "gogoproto.onlyone_all",
+	Tag:           "varint,63009,opt,name=onlyone_all,json=onlyoneAll",
+}
+
+var E_EqualAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63013,
+	Name:          "gogoproto.equal_all",
+	Tag:           "varint,63013,opt,name=equal_all,json=equalAll",
+}
+
+var E_DescriptionAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63014,
+	Name:          "gogoproto.description_all",
+	Tag:           "varint,63014,opt,name=description_all,json=descriptionAll",
+}
+
+var E_TestgenAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63015,
+	Name:          "gogoproto.testgen_all",
+	Tag:           "varint,63015,opt,name=testgen_all,json=testgenAll",
+}
+
+var E_BenchgenAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63016,
+	Name:          "gogoproto.benchgen_all",
+	Tag:           "varint,63016,opt,name=benchgen_all,json=benchgenAll",
+}
+
+var E_MarshalerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63017,
+	Name:          "gogoproto.marshaler_all",
+	Tag:           "varint,63017,opt,name=marshaler_all,json=marshalerAll",
+}
+
+var E_UnmarshalerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63018,
+	Name:          "gogoproto.unmarshaler_all",
+	Tag:           "varint,63018,opt,name=unmarshaler_all,json=unmarshalerAll",
+}
+
+var E_StableMarshalerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63019,
+	Name:          "gogoproto.stable_marshaler_all",
+	Tag:           "varint,63019,opt,name=stable_marshaler_all,json=stableMarshalerAll",
+}
+
+var E_SizerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63020,
+	Name:          "gogoproto.sizer_all",
+	Tag:           "varint,63020,opt,name=sizer_all,json=sizerAll",
+}
+
+var E_GoprotoEnumStringerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63021,
+	Name:          "gogoproto.goproto_enum_stringer_all",
+	Tag:           "varint,63021,opt,name=goproto_enum_stringer_all,json=goprotoEnumStringerAll",
+}
+
+var E_EnumStringerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63022,
+	Name:          "gogoproto.enum_stringer_all",
+	Tag:           "varint,63022,opt,name=enum_stringer_all,json=enumStringerAll",
+}
+
+var E_UnsafeMarshalerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63023,
+	Name:          "gogoproto.unsafe_marshaler_all",
+	Tag:           "varint,63023,opt,name=unsafe_marshaler_all,json=unsafeMarshalerAll",
+}
+
+var E_UnsafeUnmarshalerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63024,
+	Name:          "gogoproto.unsafe_unmarshaler_all",
+	Tag:           "varint,63024,opt,name=unsafe_unmarshaler_all,json=unsafeUnmarshalerAll",
+}
+
+var E_GoprotoExtensionsMapAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63025,
+	Name:          "gogoproto.goproto_extensions_map_all",
+	Tag:           "varint,63025,opt,name=goproto_extensions_map_all,json=goprotoExtensionsMapAll",
+}
+
+var E_GoprotoUnrecognizedAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63026,
+	Name:          "gogoproto.goproto_unrecognized_all",
+	Tag:           "varint,63026,opt,name=goproto_unrecognized_all,json=goprotoUnrecognizedAll",
+}
+
+var E_GogoprotoImport = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63027,
+	Name:          "gogoproto.gogoproto_import",
+	Tag:           "varint,63027,opt,name=gogoproto_import,json=gogoprotoImport",
+}
+
+var E_ProtosizerAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63028,
+	Name:          "gogoproto.protosizer_all",
+	Tag:           "varint,63028,opt,name=protosizer_all,json=protosizerAll",
+}
+
+var E_CompareAll = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FileOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         63029,
+	Name:          "gogoproto.compare_all",
+	Tag:           "varint,63029,opt,name=compare_all,json=compareAll",
+}
+
+var E_GoprotoGetters = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64001,
+	Name:          "gogoproto.goproto_getters",
+	Tag:           "varint,64001,opt,name=goproto_getters,json=goprotoGetters",
+}
+
+var E_GoprotoStringer = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64003,
+	Name:          "gogoproto.goproto_stringer",
+	Tag:           "varint,64003,opt,name=goproto_stringer,json=goprotoStringer",
+}
+
+var E_VerboseEqual = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64004,
+	Name:          "gogoproto.verbose_equal",
+	Tag:           "varint,64004,opt,name=verbose_equal,json=verboseEqual",
+}
+
+var E_Face = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64005,
+	Name:          "gogoproto.face",
+	Tag:           "varint,64005,opt,name=face",
+}
+
+var E_Gostring = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64006,
+	Name:          "gogoproto.gostring",
+	Tag:           "varint,64006,opt,name=gostring",
+}
+
+var E_Populate = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64007,
+	Name:          "gogoproto.populate",
+	Tag:           "varint,64007,opt,name=populate",
+}
+
+var E_Stringer = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         67008,
+	Name:          "gogoproto.stringer",
+	Tag:           "varint,67008,opt,name=stringer",
+}
+
+var E_Onlyone = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64009,
+	Name:          "gogoproto.onlyone",
+	Tag:           "varint,64009,opt,name=onlyone",
+}
+
+var E_Equal = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64013,
+	Name:          "gogoproto.equal",
+	Tag:           "varint,64013,opt,name=equal",
+}
+
+var E_Description = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64014,
+	Name:          "gogoproto.description",
+	Tag:           "varint,64014,opt,name=description",
+}
+
+var E_Testgen = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64015,
+	Name:          "gogoproto.testgen",
+	Tag:           "varint,64015,opt,name=testgen",
+}
+
+var E_Benchgen = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64016,
+	Name:          "gogoproto.benchgen",
+	Tag:           "varint,64016,opt,name=benchgen",
+}
+
+var E_Marshaler = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64017,
+	Name:          "gogoproto.marshaler",
+	Tag:           "varint,64017,opt,name=marshaler",
+}
+
+var E_Unmarshaler = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64018,
+	Name:          "gogoproto.unmarshaler",
+	Tag:           "varint,64018,opt,name=unmarshaler",
+}
+
+var E_StableMarshaler = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64019,
+	Name:          "gogoproto.stable_marshaler",
+	Tag:           "varint,64019,opt,name=stable_marshaler,json=stableMarshaler",
+}
+
+var E_Sizer = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64020,
+	Name:          "gogoproto.sizer",
+	Tag:           "varint,64020,opt,name=sizer",
+}
+
+var E_UnsafeMarshaler = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64023,
+	Name:          "gogoproto.unsafe_marshaler",
+	Tag:           "varint,64023,opt,name=unsafe_marshaler,json=unsafeMarshaler",
+}
+
+var E_UnsafeUnmarshaler = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64024,
+	Name:          "gogoproto.unsafe_unmarshaler",
+	Tag:           "varint,64024,opt,name=unsafe_unmarshaler,json=unsafeUnmarshaler",
+}
+
+var E_GoprotoExtensionsMap = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64025,
+	Name:          "gogoproto.goproto_extensions_map",
+	Tag:           "varint,64025,opt,name=goproto_extensions_map,json=goprotoExtensionsMap",
+}
+
+var E_GoprotoUnrecognized = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64026,
+	Name:          "gogoproto.goproto_unrecognized",
+	Tag:           "varint,64026,opt,name=goproto_unrecognized,json=goprotoUnrecognized",
+}
+
+var E_Protosizer = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64028,
+	Name:          "gogoproto.protosizer",
+	Tag:           "varint,64028,opt,name=protosizer",
+}
+
+var E_Compare = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.MessageOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         64029,
+	Name:          "gogoproto.compare",
+	Tag:           "varint,64029,opt,name=compare",
+}
+
+var E_Nullable = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         65001,
+	Name:          "gogoproto.nullable",
+	Tag:           "varint,65001,opt,name=nullable",
+}
+
+var E_Embed = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*bool)(nil),
+	Field:         65002,
+	Name:          "gogoproto.embed",
+	Tag:           "varint,65002,opt,name=embed",
+}
+
+var E_Customtype = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65003,
+	Name:          "gogoproto.customtype",
+	Tag:           "bytes,65003,opt,name=customtype",
+}
+
+var E_Customname = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65004,
+	Name:          "gogoproto.customname",
+	Tag:           "bytes,65004,opt,name=customname",
+}
+
+var E_Jsontag = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65005,
+	Name:          "gogoproto.jsontag",
+	Tag:           "bytes,65005,opt,name=jsontag",
+}
+
+var E_Moretags = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65006,
+	Name:          "gogoproto.moretags",
+	Tag:           "bytes,65006,opt,name=moretags",
+}
+
+var E_Casttype = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65007,
+	Name:          "gogoproto.casttype",
+	Tag:           "bytes,65007,opt,name=casttype",
+}
+
+var E_Castkey = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65008,
+	Name:          "gogoproto.castkey",
+	Tag:           "bytes,65008,opt,name=castkey",
+}
+
+var E_Castvalue = &proto.ExtensionDesc{
+	ExtendedType:  (*google_protobuf.FieldOptions)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         65009,
+	Name:          "gogoproto.castvalue",
+	Tag:           "bytes,65009,opt,name=castvalue",
+}
+
+func init() {
+	proto.RegisterExtension(E_GoprotoEnumPrefix)
+	proto.RegisterExtension(E_GoprotoEnumStringer)
+	proto.RegisterExtension(E_EnumStringer)
+	proto.RegisterExtension(E_EnumCustomname)
+	proto.RegisterExtension(E_EnumvalueCustomname)
+	proto.RegisterExtension(E_GoprotoGettersAll)
+	proto.RegisterExtension(E_GoprotoEnumPrefixAll)
+	proto.RegisterExtension(E_GoprotoStringerAll)
+	proto.RegisterExtension(E_VerboseEqualAll)
+	proto.RegisterExtension(E_FaceAll)
+	proto.RegisterExtension(E_GostringAll)
+	proto.RegisterExtension(E_PopulateAll)
+	proto.RegisterExtension(E_StringerAll)
+	proto.RegisterExtension(E_OnlyoneAll)
+	proto.RegisterExtension(E_EqualAll)
+	proto.RegisterExtension(E_DescriptionAll)
+	proto.RegisterExtension(E_TestgenAll)
+	proto.RegisterExtension(E_BenchgenAll)
+	proto.RegisterExtension(E_MarshalerAll)
+	proto.RegisterExtension(E_UnmarshalerAll)
+	proto.RegisterExtension(E_StableMarshalerAll)
+	proto.RegisterExtension(E_SizerAll)
+	proto.RegisterExtension(E_GoprotoEnumStringerAll)
+	proto.RegisterExtension(E_EnumStringerAll)
+	proto.RegisterExtension(E_UnsafeMarshalerAll)
+	proto.RegisterExtension(E_UnsafeUnmarshalerAll)
+	proto.RegisterExtension(E_GoprotoExtensionsMapAll)
+	proto.RegisterExtension(E_GoprotoUnrecognizedAll)
+	proto.RegisterExtension(E_GogoprotoImport)
+	proto.RegisterExtension(E_ProtosizerAll)
+	proto.RegisterExtension(E_CompareAll)
+	proto.RegisterExtension(E_GoprotoGetters)
+	proto.RegisterExtension(E_GoprotoStringer)
+	proto.RegisterExtension(E_VerboseEqual)
+	proto.RegisterExtension(E_Face)
+	proto.RegisterExtension(E_Gostring)
+	proto.RegisterExtension(E_Populate)
+	proto.RegisterExtension(E_Stringer)
+	proto.RegisterExtension(E_Onlyone)
+	proto.RegisterExtension(E_Equal)
+	proto.RegisterExtension(E_Description)
+	proto.RegisterExtension(E_Testgen)
+	proto.RegisterExtension(E_Benchgen)
+	proto.RegisterExtension(E_Marshaler)
+	proto.RegisterExtension(E_Unmarshaler)
+	proto.RegisterExtension(E_StableMarshaler)
+	proto.RegisterExtension(E_Sizer)
+	proto.RegisterExtension(E_UnsafeMarshaler)
+	proto.RegisterExtension(E_UnsafeUnmarshaler)
+	proto.RegisterExtension(E_GoprotoExtensionsMap)
+	proto.RegisterExtension(E_GoprotoUnrecognized)
+	proto.RegisterExtension(E_Protosizer)
+	proto.RegisterExtension(E_Compare)
+	proto.RegisterExtension(E_Nullable)
+	proto.RegisterExtension(E_Embed)
+	proto.RegisterExtension(E_Customtype)
+	proto.RegisterExtension(E_Customname)
+	proto.RegisterExtension(E_Jsontag)
+	proto.RegisterExtension(E_Moretags)
+	proto.RegisterExtension(E_Casttype)
+	proto.RegisterExtension(E_Castkey)
+	proto.RegisterExtension(E_Castvalue)
+}
+
+var fileDescriptorGogo = []byte{
+	// 1096 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x97, 0xcb, 0x6f, 0xdc, 0x54,
+	0x14, 0x87, 0x85, 0x48, 0x95, 0x99, 0x93, 0x17, 0x99, 0x84, 0x50, 0x2a, 0x10, 0xed, 0x8e, 0x55,
+	0xba, 0x42, 0xa8, 0xae, 0x10, 0x6a, 0xab, 0x34, 0x2a, 0x22, 0x10, 0x05, 0x52, 0x40, 0x2c, 0x46,
+	0x9e, 0xc9, 0x8d, 0x3b, 0xe0, 0xf1, 0x35, 0xbe, 0x76, 0xd5, 0xb0, 0x43, 0xe5, 0x21, 0x84, 0x78,
+	0x23, 0x41, 0x4b, 0xcb, 0x63, 0xc1, 0xfb, 0x59, 0x1e, 0x7b, 0x36, 0xc0, 0x9a, 0xff, 0x81, 0x0d,
+	0x10, 0x5e, 0x52, 0x76, 0xd9, 0xf4, 0x1e, 0xfb, 0x1c, 0xcf, 0xb5, 0x67, 0xa4, 0x7b, 0x67, 0xe7,
+	0x64, 0xee, 0xf7, 0xcd, 0xf5, 0x39, 0xbe, 0xe7, 0x37, 0x06, 0x08, 0x64, 0x20, 0x97, 0xe3, 0x44,
+	0xa6, 0xb2, 0xd5, 0xc4, 0xeb, 0xfc, 0xf2, 0xd0, 0xe1, 0x40, 0xca, 0x20, 0x14, 0x47, 0xf3, 0xbf,
+	0x3a, 0xd9, 0xf6, 0xd1, 0x2d, 0xa1, 0xba, 0x49, 0x2f, 0x4e, 0x65, 0x52, 0x2c, 0xf6, 0x1e, 0x80,
+	0x05, 0x5a, 0xdc, 0x16, 0x51, 0xd6, 0x6f, 0xc7, 0x89, 0xd8, 0xee, 0x5d, 0x68, 0xdd, 0xb6, 0x5c,
+	0x90, 0xcb, 0x4c, 0x2e, 0xaf, 0xe8, 0x4f, 0x1f, 0x8c, 0xd3, 0x9e, 0x8c, 0xd4, 0xc1, 0x6b, 0xbf,
+	0xdf, 0x78, 0xf8, 0x86, 0x3b, 0x1b, 0x1b, 0xf3, 0x84, 0xe2, 0x67, 0xeb, 0x39, 0xe8, 0x6d, 0xc0,
+	0xcd, 0x15, 0x9f, 0x4a, 0x93, 0x5e, 0x14, 0x88, 0xc4, 0x62, 0xfc, 0x99, 0x8c, 0x0b, 0x86, 0xf1,
+	0x21, 0x42, 0xbd, 0x53, 0x30, 0x33, 0x8e, 0xeb, 0x17, 0x72, 0x4d, 0x0b, 0x53, 0xb2, 0x0a, 0x73,
+	0xb9, 0xa4, 0x9b, 0xa9, 0x54, 0xf6, 0x23, 0xbf, 0x2f, 0x2c, 0x9a, 0x5f, 0x73, 0x4d, 0x73, 0x63,
+	0x16, 0xb1, 0x53, 0x25, 0xe5, 0x9d, 0x85, 0x45, 0xfc, 0xcf, 0x79, 0x3f, 0xcc, 0x84, 0x69, 0x3b,
+	0x32, 0xd2, 0x76, 0x16, 0x97, 0xb1, 0xf2, 0xb7, 0x8b, 0x13, 0xb9, 0x72, 0xa1, 0x14, 0x18, 0x5e,
+	0xa3, 0x13, 0x81, 0x48, 0x53, 0x91, 0xa8, 0xb6, 0x1f, 0x86, 0x23, 0x36, 0x79, 0xba, 0x17, 0x96,
+	0xc6, 0x4b, 0xbb, 0xd5, 0x4e, 0xac, 0x16, 0xe4, 0x89, 0x30, 0xf4, 0x36, 0xe1, 0x96, 0x11, 0x9d,
+	0x75, 0x70, 0x5e, 0x26, 0xe7, 0xe2, 0x50, 0x77, 0x51, 0xbb, 0x0e, 0xfc, 0xff, 0xb2, 0x1f, 0x0e,
+	0xce, 0x77, 0xc9, 0xd9, 0x22, 0x96, 0xdb, 0x82, 0xc6, 0xfb, 0x60, 0xfe, 0xbc, 0x48, 0x3a, 0x52,
+	0x89, 0xb6, 0x78, 0x2a, 0xf3, 0x43, 0x07, 0xdd, 0x15, 0xd2, 0xcd, 0x11, 0xb8, 0x82, 0x1c, 0xba,
+	0x8e, 0x41, 0x63, 0xdb, 0xef, 0x0a, 0x07, 0xc5, 0x55, 0x52, 0x4c, 0xe2, 0x7a, 0x44, 0x4f, 0xc0,
+	0x74, 0x20, 0x8b, 0x5b, 0x72, 0xc0, 0xdf, 0x23, 0x7c, 0x8a, 0x19, 0x52, 0xc4, 0x32, 0xce, 0x42,
+	0x3f, 0x75, 0xd9, 0xc1, 0xfb, 0xac, 0x60, 0x86, 0x14, 0x63, 0x94, 0xf5, 0x03, 0x56, 0x28, 0xa3,
+	0x9e, 0xf7, 0xc2, 0x94, 0x8c, 0xc2, 0x1d, 0x19, 0xb9, 0x6c, 0xe2, 0x43, 0x32, 0x00, 0x21, 0x28,
+	0x38, 0x0e, 0x4d, 0xd7, 0x46, 0x7c, 0x44, 0x78, 0x43, 0x70, 0x07, 0xf4, 0x39, 0xe3, 0x21, 0xa3,
+	0x57, 0x38, 0x28, 0x3e, 0x26, 0xc5, 0xac, 0x81, 0xd1, 0x6d, 0xa4, 0x42, 0xa5, 0x81, 0x70, 0x91,
+	0x7c, 0xc2, 0xb7, 0x41, 0x08, 0x95, 0xb2, 0x23, 0xa2, 0xee, 0x39, 0x37, 0xc3, 0xa7, 0x5c, 0x4a,
+	0x66, 0x50, 0xa1, 0x27, 0x4f, 0xdf, 0x4f, 0xd4, 0x39, 0x3f, 0x74, 0x6a, 0xc7, 0x67, 0xe4, 0x98,
+	0x2e, 0x21, 0xaa, 0x48, 0x16, 0x8d, 0xa3, 0xf9, 0x9c, 0x2b, 0x62, 0x60, 0x74, 0xf4, 0x54, 0xea,
+	0x77, 0x42, 0xd1, 0x1e, 0xc7, 0xf6, 0x05, 0x1f, 0xbd, 0x82, 0x5d, 0x33, 0x8d, 0xba, 0xd3, 0xaa,
+	0xf7, 0xb4, 0x93, 0xe6, 0x4b, 0xee, 0x74, 0x0e, 0x20, 0xfc, 0x18, 0xdc, 0x3a, 0x72, 0xd4, 0x3b,
+	0xc8, 0xbe, 0x22, 0xd9, 0xd2, 0x88, 0x71, 0x4f, 0x23, 0x61, 0x5c, 0xe5, 0xd7, 0x3c, 0x12, 0x44,
+	0xcd, 0xa5, 0xab, 0x96, 0x45, 0xca, 0xdf, 0x1e, 0xaf, 0x6a, 0xdf, 0x70, 0xd5, 0x0a, 0xb6, 0x52,
+	0xb5, 0x87, 0x61, 0x89, 0x8c, 0xe3, 0xf5, 0xf5, 0x5b, 0x1e, 0xac, 0x05, 0xbd, 0x59, 0xed, 0xee,
+	0xe3, 0x70, 0xa8, 0x2c, 0xe7, 0x85, 0x54, 0x44, 0x0a, 0x19, 0xbd, 0xe7, 0xd8, 0xc1, 0x7c, 0x8d,
+	0xcc, 0x3c, 0xf1, 0x57, 0x4a, 0xc1, 0x9a, 0x1f, 0xa3, 0xfc, 0x51, 0x38, 0xc8, 0xf2, 0x2c, 0x4a,
+	0x44, 0x57, 0x06, 0x91, 0x6e, 0xe3, 0x96, 0x83, 0xfa, 0xbb, 0x5a, 0xab, 0x36, 0x0d, 0x1c, 0xcd,
+	0x67, 0xe0, 0xa6, 0xf2, 0xf7, 0x46, 0xbb, 0xd7, 0x8f, 0x65, 0x92, 0x5a, 0x8c, 0xdf, 0x73, 0xa7,
+	0x4a, 0xee, 0x4c, 0x8e, 0x79, 0x2b, 0x30, 0x9b, 0xff, 0xe9, 0xfa, 0x48, 0xfe, 0x40, 0xa2, 0x99,
+	0x01, 0x45, 0x83, 0xa3, 0x2b, 0xfb, 0xb1, 0x9f, 0xb8, 0xcc, 0xbf, 0x1f, 0x79, 0x70, 0x10, 0x52,
+	0x3c, 0x7d, 0x73, 0xb5, 0x24, 0x6e, 0xdd, 0x31, 0x24, 0x59, 0x13, 0x4a, 0xf9, 0x41, 0xe9, 0x79,
+	0x66, 0x8f, 0xce, 0x6c, 0x35, 0x88, 0xbd, 0xfb, 0xb1, 0x3c, 0xd5, 0xb8, 0xb4, 0xcb, 0x2e, 0xee,
+	0x95, 0x15, 0xaa, 0xa4, 0xa5, 0x77, 0x1a, 0x66, 0x2a, 0x51, 0x69, 0x57, 0x3d, 0x4b, 0xaa, 0x69,
+	0x33, 0x29, 0xbd, 0xbb, 0x60, 0x02, 0x63, 0xcf, 0x8e, 0x3f, 0x47, 0x78, 0xbe, 0xdc, 0xbb, 0x07,
+	0x1a, 0x1c, 0x77, 0x76, 0xf4, 0x79, 0x42, 0x4b, 0x04, 0x71, 0x8e, 0x3a, 0x3b, 0xfe, 0x02, 0xe3,
+	0x8c, 0x20, 0xee, 0x5e, 0xc2, 0x9f, 0x5e, 0x9a, 0xa0, 0x71, 0xc5, 0xb5, 0x3b, 0x0e, 0x93, 0x94,
+	0x71, 0x76, 0xfa, 0x45, 0xfa, 0x72, 0x26, 0xbc, 0xbb, 0xe1, 0x80, 0x63, 0xc1, 0x5f, 0x26, 0xb4,
+	0x58, 0xaf, 0x13, 0x64, 0xca, 0xc8, 0x35, 0x3b, 0xfe, 0x0a, 0xe1, 0x26, 0x85, 0x5b, 0xa7, 0x5c,
+	0xb3, 0x0b, 0x5e, 0xe5, 0xad, 0x13, 0x81, 0x65, 0xe3, 0x48, 0xb3, 0xd3, 0xaf, 0x71, 0xd5, 0x19,
+	0xd1, 0xa7, 0xa9, 0x59, 0x8e, 0x29, 0x3b, 0xff, 0x3a, 0xf1, 0x03, 0x06, 0x2b, 0x60, 0x8c, 0x49,
+	0xbb, 0xe2, 0x0d, 0xae, 0x80, 0x41, 0xe1, 0x31, 0xaa, 0x47, 0x9f, 0xdd, 0xf4, 0x26, 0x1f, 0xa3,
+	0x5a, 0xf2, 0x61, 0x37, 0xf3, 0x69, 0x61, 0x57, 0xbc, 0xc5, 0xdd, 0xcc, 0xd7, 0xe3, 0x36, 0xea,
+	0x59, 0x62, 0x77, 0xbc, 0xcd, 0xdb, 0xa8, 0x45, 0x89, 0x4e, 0xa6, 0xd6, 0x70, 0x8e, 0xd8, 0x7d,
+	0xef, 0x90, 0x6f, 0x7e, 0x28, 0x46, 0xbc, 0x47, 0x60, 0x69, 0x74, 0x86, 0xd8, 0xad, 0x97, 0xf6,
+	0x6a, 0xbf, 0xfa, 0xcd, 0x08, 0xd1, 0x91, 0xb7, 0x38, 0x2a, 0x3f, 0xec, 0xda, 0xcb, 0x7b, 0xd5,
+	0x17, 0x3b, 0x33, 0x3e, 0xf4, 0x2f, 0x34, 0x18, 0x8c, 0x6e, 0xbb, 0xeb, 0x0a, 0xb9, 0x0c, 0x08,
+	0x8f, 0x06, 0x4d, 0x6e, 0x3b, 0x7f, 0x95, 0x8f, 0x06, 0x11, 0x1a, 0x6e, 0x44, 0x59, 0x18, 0xe2,
+	0xc3, 0xd1, 0xba, 0x7d, 0x44, 0x4c, 0x88, 0x70, 0x8b, 0xd9, 0x3f, 0xf6, 0xe9, 0x60, 0x30, 0xa0,
+	0x67, 0xe8, 0x01, 0xd1, 0xef, 0xe8, 0x1a, 0x58, 0xc8, 0x3f, 0xf7, 0x79, 0x20, 0xe0, 0x6a, 0x7d,
+	0x9e, 0xa0, 0x78, 0x69, 0x4c, 0x77, 0x62, 0xeb, 0xb7, 0xfe, 0xb5, 0x5f, 0xbc, 0x83, 0x1a, 0xc8,
+	0x40, 0x90, 0xbf, 0x75, 0x5a, 0x04, 0xbb, 0x55, 0x41, 0xfe, 0xa2, 0x79, 0x0c, 0x26, 0x9f, 0x50,
+	0x32, 0x4a, 0xfd, 0xc0, 0x46, 0xff, 0x4d, 0x34, 0xaf, 0xc7, 0x82, 0xf5, 0x65, 0x22, 0xf4, 0xa5,
+	0xb2, 0xb1, 0xff, 0x10, 0x5b, 0x02, 0x08, 0x77, 0x7d, 0x95, 0xba, 0xdc, 0xf7, 0xbf, 0x0c, 0x33,
+	0x80, 0x9b, 0xc6, 0xeb, 0x27, 0xc5, 0x8e, 0x8d, 0xfd, 0x8f, 0x37, 0x4d, 0xeb, 0xf5, 0x00, 0x6c,
+	0xe2, 0x65, 0xfe, 0xbe, 0x6d, 0x83, 0xff, 0x27, 0x78, 0x40, 0x9c, 0x3c, 0x02, 0x0b, 0xfa, 0x79,
+	0xa9, 0x63, 0x27, 0x61, 0x55, 0xae, 0xca, 0xf5, 0xfc, 0x41, 0xbc, 0x1e, 0x00, 0x00, 0xff, 0xff,
+	0x87, 0x5c, 0xee, 0x2b, 0x7e, 0x11, 0x00, 0x00,
+}

+ 308 - 0
vendor/github.com/gogo/protobuf/gogoproto/helper.go

@@ -0,0 +1,308 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 gogoproto
+
+import google_protobuf "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+import proto "github.com/gogo/protobuf/proto"
+
+func IsEmbed(field *google_protobuf.FieldDescriptorProto) bool {
+	return proto.GetBoolExtension(field.Options, E_Embed, false)
+}
+
+func IsNullable(field *google_protobuf.FieldDescriptorProto) bool {
+	return proto.GetBoolExtension(field.Options, E_Nullable, true)
+}
+
+func NeedsNilCheck(proto3 bool, field *google_protobuf.FieldDescriptorProto) bool {
+	nullable := IsNullable(field)
+	if field.IsMessage() || IsCustomType(field) {
+		return nullable
+	}
+	if proto3 {
+		return false
+	}
+	return nullable || *field.Type == google_protobuf.FieldDescriptorProto_TYPE_BYTES
+}
+
+func IsCustomType(field *google_protobuf.FieldDescriptorProto) bool {
+	typ := GetCustomType(field)
+	if len(typ) > 0 {
+		return true
+	}
+	return false
+}
+
+func IsCastType(field *google_protobuf.FieldDescriptorProto) bool {
+	typ := GetCastType(field)
+	if len(typ) > 0 {
+		return true
+	}
+	return false
+}
+
+func IsCastKey(field *google_protobuf.FieldDescriptorProto) bool {
+	typ := GetCastKey(field)
+	if len(typ) > 0 {
+		return true
+	}
+	return false
+}
+
+func IsCastValue(field *google_protobuf.FieldDescriptorProto) bool {
+	typ := GetCastValue(field)
+	if len(typ) > 0 {
+		return true
+	}
+	return false
+}
+
+func GetCustomType(field *google_protobuf.FieldDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Customtype)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func GetCastType(field *google_protobuf.FieldDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Casttype)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func GetCastKey(field *google_protobuf.FieldDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Castkey)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func GetCastValue(field *google_protobuf.FieldDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Castvalue)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func IsCustomName(field *google_protobuf.FieldDescriptorProto) bool {
+	name := GetCustomName(field)
+	if len(name) > 0 {
+		return true
+	}
+	return false
+}
+
+func IsEnumCustomName(field *google_protobuf.EnumDescriptorProto) bool {
+	name := GetEnumCustomName(field)
+	if len(name) > 0 {
+		return true
+	}
+	return false
+}
+
+func IsEnumValueCustomName(field *google_protobuf.EnumValueDescriptorProto) bool {
+	name := GetEnumValueCustomName(field)
+	if len(name) > 0 {
+		return true
+	}
+	return false
+}
+
+func GetCustomName(field *google_protobuf.FieldDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Customname)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func GetEnumCustomName(field *google_protobuf.EnumDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_EnumCustomname)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func GetEnumValueCustomName(field *google_protobuf.EnumValueDescriptorProto) string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_EnumvalueCustomname)
+		if err == nil && v.(*string) != nil {
+			return *(v.(*string))
+		}
+	}
+	return ""
+}
+
+func GetJsonTag(field *google_protobuf.FieldDescriptorProto) *string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Jsontag)
+		if err == nil && v.(*string) != nil {
+			return (v.(*string))
+		}
+	}
+	return nil
+}
+
+func GetMoreTags(field *google_protobuf.FieldDescriptorProto) *string {
+	if field.Options != nil {
+		v, err := proto.GetExtension(field.Options, E_Moretags)
+		if err == nil && v.(*string) != nil {
+			return (v.(*string))
+		}
+	}
+	return nil
+}
+
+type EnableFunc func(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool
+
+func EnabledGoEnumPrefix(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool {
+	return proto.GetBoolExtension(enum.Options, E_GoprotoEnumPrefix, proto.GetBoolExtension(file.Options, E_GoprotoEnumPrefixAll, true))
+}
+
+func EnabledGoStringer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_GoprotoStringer, proto.GetBoolExtension(file.Options, E_GoprotoStringerAll, true))
+}
+
+func HasGoGetters(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_GoprotoGetters, proto.GetBoolExtension(file.Options, E_GoprotoGettersAll, true))
+}
+
+func IsUnion(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Onlyone, proto.GetBoolExtension(file.Options, E_OnlyoneAll, false))
+}
+
+func HasGoString(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Gostring, proto.GetBoolExtension(file.Options, E_GostringAll, false))
+}
+
+func HasEqual(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Equal, proto.GetBoolExtension(file.Options, E_EqualAll, false))
+}
+
+func HasVerboseEqual(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_VerboseEqual, proto.GetBoolExtension(file.Options, E_VerboseEqualAll, false))
+}
+
+func IsStringer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Stringer, proto.GetBoolExtension(file.Options, E_StringerAll, false))
+}
+
+func IsFace(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Face, proto.GetBoolExtension(file.Options, E_FaceAll, false))
+}
+
+func HasDescription(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Description, proto.GetBoolExtension(file.Options, E_DescriptionAll, false))
+}
+
+func HasPopulate(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Populate, proto.GetBoolExtension(file.Options, E_PopulateAll, false))
+}
+
+func HasTestGen(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Testgen, proto.GetBoolExtension(file.Options, E_TestgenAll, false))
+}
+
+func HasBenchGen(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Benchgen, proto.GetBoolExtension(file.Options, E_BenchgenAll, false))
+}
+
+func IsMarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Marshaler, proto.GetBoolExtension(file.Options, E_MarshalerAll, false))
+}
+
+func IsUnmarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Unmarshaler, proto.GetBoolExtension(file.Options, E_UnmarshalerAll, false))
+}
+
+func IsStableMarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_StableMarshaler, proto.GetBoolExtension(file.Options, E_StableMarshalerAll, false))
+}
+
+func IsSizer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Sizer, proto.GetBoolExtension(file.Options, E_SizerAll, false))
+}
+
+func IsProtoSizer(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Protosizer, proto.GetBoolExtension(file.Options, E_ProtosizerAll, false))
+}
+
+func IsGoEnumStringer(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool {
+	return proto.GetBoolExtension(enum.Options, E_GoprotoEnumStringer, proto.GetBoolExtension(file.Options, E_GoprotoEnumStringerAll, true))
+}
+
+func IsEnumStringer(file *google_protobuf.FileDescriptorProto, enum *google_protobuf.EnumDescriptorProto) bool {
+	return proto.GetBoolExtension(enum.Options, E_EnumStringer, proto.GetBoolExtension(file.Options, E_EnumStringerAll, false))
+}
+
+func IsUnsafeMarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_UnsafeMarshaler, proto.GetBoolExtension(file.Options, E_UnsafeMarshalerAll, false))
+}
+
+func IsUnsafeUnmarshaler(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_UnsafeUnmarshaler, proto.GetBoolExtension(file.Options, E_UnsafeUnmarshalerAll, false))
+}
+
+func HasExtensionsMap(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_GoprotoExtensionsMap, proto.GetBoolExtension(file.Options, E_GoprotoExtensionsMapAll, true))
+}
+
+func HasUnrecognized(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	if IsProto3(file) {
+		return false
+	}
+	return proto.GetBoolExtension(message.Options, E_GoprotoUnrecognized, proto.GetBoolExtension(file.Options, E_GoprotoUnrecognizedAll, true))
+}
+
+func IsProto3(file *google_protobuf.FileDescriptorProto) bool {
+	return file.GetSyntax() == "proto3"
+}
+
+func ImportsGoGoProto(file *google_protobuf.FileDescriptorProto) bool {
+	return proto.GetBoolExtension(file.Options, E_GogoprotoImport, true)
+}
+
+func HasCompare(file *google_protobuf.FileDescriptorProto, message *google_protobuf.DescriptorProto) bool {
+	return proto.GetBoolExtension(message.Options, E_Compare, proto.GetBoolExtension(file.Options, E_CompareAll, false))
+}

+ 102 - 0
vendor/github.com/gogo/protobuf/io/full.go

@@ -0,0 +1,102 @@
+// 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
+//
+// 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 io
+
+import (
+	"github.com/gogo/protobuf/proto"
+	"io"
+)
+
+func NewFullWriter(w io.Writer) WriteCloser {
+	return &fullWriter{w, nil}
+}
+
+type fullWriter struct {
+	w      io.Writer
+	buffer []byte
+}
+
+func (this *fullWriter) WriteMsg(msg proto.Message) (err error) {
+	var data []byte
+	if m, ok := msg.(marshaler); ok {
+		n, ok := getSize(m)
+		if !ok {
+			data, err = proto.Marshal(msg)
+			if err != nil {
+				return err
+			}
+		}
+		if n >= len(this.buffer) {
+			this.buffer = make([]byte, n)
+		}
+		_, err = m.MarshalTo(this.buffer)
+		if err != nil {
+			return err
+		}
+		data = this.buffer[:n]
+	} else {
+		data, err = proto.Marshal(msg)
+		if err != nil {
+			return err
+		}
+	}
+	_, err = this.w.Write(data)
+	return err
+}
+
+func (this *fullWriter) Close() error {
+	if closer, ok := this.w.(io.Closer); ok {
+		return closer.Close()
+	}
+	return nil
+}
+
+type fullReader struct {
+	r   io.Reader
+	buf []byte
+}
+
+func NewFullReader(r io.Reader, maxSize int) ReadCloser {
+	return &fullReader{r, make([]byte, maxSize)}
+}
+
+func (this *fullReader) ReadMsg(msg proto.Message) error {
+	length, err := this.r.Read(this.buf)
+	if err != nil {
+		return err
+	}
+	return proto.Unmarshal(this.buf[:length], msg)
+}
+
+func (this *fullReader) Close() error {
+	if closer, ok := this.r.(io.Closer); ok {
+		return closer.Close()
+	}
+	return nil
+}

+ 70 - 0
vendor/github.com/gogo/protobuf/io/io.go

@@ -0,0 +1,70 @@
+// 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
+//
+// 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 io
+
+import (
+	"github.com/gogo/protobuf/proto"
+	"io"
+)
+
+type Writer interface {
+	WriteMsg(proto.Message) error
+}
+
+type WriteCloser interface {
+	Writer
+	io.Closer
+}
+
+type Reader interface {
+	ReadMsg(msg proto.Message) error
+}
+
+type ReadCloser interface {
+	Reader
+	io.Closer
+}
+
+type marshaler interface {
+	MarshalTo(data []byte) (n int, err error)
+}
+
+func getSize(v interface{}) (int, bool) {
+	if sz, ok := v.(interface {
+		Size() (n int)
+	}); ok {
+		return sz.Size(), true
+	} else if sz, ok := v.(interface {
+		ProtoSize() (n int)
+	}); ok {
+		return sz.ProtoSize(), true
+	} else {
+		return 0, false
+	}
+}

+ 126 - 0
vendor/github.com/gogo/protobuf/io/uint32.go

@@ -0,0 +1,126 @@
+// 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
+//
+// 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 io
+
+import (
+	"encoding/binary"
+	"github.com/gogo/protobuf/proto"
+	"io"
+)
+
+func NewUint32DelimitedWriter(w io.Writer, byteOrder binary.ByteOrder) WriteCloser {
+	return &uint32Writer{w, byteOrder, nil}
+}
+
+func NewSizeUint32DelimitedWriter(w io.Writer, byteOrder binary.ByteOrder, size int) WriteCloser {
+	return &uint32Writer{w, byteOrder, make([]byte, size)}
+}
+
+type uint32Writer struct {
+	w         io.Writer
+	byteOrder binary.ByteOrder
+	buffer    []byte
+}
+
+func (this *uint32Writer) WriteMsg(msg proto.Message) (err error) {
+	var data []byte
+	if m, ok := msg.(marshaler); ok {
+		n, ok := getSize(m)
+		if !ok {
+			data, err = proto.Marshal(msg)
+			if err != nil {
+				return err
+			}
+		}
+		if n >= len(this.buffer) {
+			this.buffer = make([]byte, n)
+		}
+		_, err = m.MarshalTo(this.buffer)
+		if err != nil {
+			return err
+		}
+		data = this.buffer[:n]
+	} else {
+		data, err = proto.Marshal(msg)
+		if err != nil {
+			return err
+		}
+	}
+	length := uint32(len(data))
+	if err = binary.Write(this.w, this.byteOrder, &length); err != nil {
+		return err
+	}
+	_, err = this.w.Write(data)
+	return err
+}
+
+func (this *uint32Writer) Close() error {
+	if closer, ok := this.w.(io.Closer); ok {
+		return closer.Close()
+	}
+	return nil
+}
+
+type uint32Reader struct {
+	r         io.Reader
+	byteOrder binary.ByteOrder
+	lenBuf    []byte
+	buf       []byte
+	maxSize   int
+}
+
+func NewUint32DelimitedReader(r io.Reader, byteOrder binary.ByteOrder, maxSize int) ReadCloser {
+	return &uint32Reader{r, byteOrder, make([]byte, 4), nil, maxSize}
+}
+
+func (this *uint32Reader) ReadMsg(msg proto.Message) error {
+	if _, err := io.ReadFull(this.r, this.lenBuf); err != nil {
+		return err
+	}
+	length32 := this.byteOrder.Uint32(this.lenBuf)
+	length := int(length32)
+	if length < 0 || length > this.maxSize {
+		return io.ErrShortBuffer
+	}
+	if length >= len(this.buf) {
+		this.buf = make([]byte, length)
+	}
+	_, err := io.ReadFull(this.r, this.buf[:length])
+	if err != nil {
+		return err
+	}
+	return proto.Unmarshal(this.buf[:length], msg)
+}
+
+func (this *uint32Reader) Close() error {
+	if closer, ok := this.r.(io.Closer); ok {
+		return closer.Close()
+	}
+	return nil
+}

+ 134 - 0
vendor/github.com/gogo/protobuf/io/varint.go

@@ -0,0 +1,134 @@
+// 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
+//
+// 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 io
+
+import (
+	"bufio"
+	"encoding/binary"
+	"errors"
+	"github.com/gogo/protobuf/proto"
+	"io"
+)
+
+var (
+	errSmallBuffer = errors.New("Buffer Too Small")
+	errLargeValue  = errors.New("Value is Larger than 64 bits")
+)
+
+func NewDelimitedWriter(w io.Writer) WriteCloser {
+	return &varintWriter{w, make([]byte, 10), nil}
+}
+
+type varintWriter struct {
+	w      io.Writer
+	lenBuf []byte
+	buffer []byte
+}
+
+func (this *varintWriter) WriteMsg(msg proto.Message) (err error) {
+	var data []byte
+	if m, ok := msg.(marshaler); ok {
+		n, ok := getSize(m)
+		if !ok {
+			data, err = proto.Marshal(msg)
+			if err != nil {
+				return err
+			}
+		}
+		if n >= len(this.buffer) {
+			this.buffer = make([]byte, n)
+		}
+		_, err = m.MarshalTo(this.buffer)
+		if err != nil {
+			return err
+		}
+		data = this.buffer[:n]
+	} else {
+		data, err = proto.Marshal(msg)
+		if err != nil {
+			return err
+		}
+	}
+	length := uint64(len(data))
+	n := binary.PutUvarint(this.lenBuf, length)
+	_, err = this.w.Write(this.lenBuf[:n])
+	if err != nil {
+		return err
+	}
+	_, err = this.w.Write(data)
+	return err
+}
+
+func (this *varintWriter) Close() error {
+	if closer, ok := this.w.(io.Closer); ok {
+		return closer.Close()
+	}
+	return nil
+}
+
+func NewDelimitedReader(r io.Reader, maxSize int) ReadCloser {
+	var closer io.Closer
+	if c, ok := r.(io.Closer); ok {
+		closer = c
+	}
+	return &varintReader{bufio.NewReader(r), nil, maxSize, closer}
+}
+
+type varintReader struct {
+	r       *bufio.Reader
+	buf     []byte
+	maxSize int
+	closer  io.Closer
+}
+
+func (this *varintReader) ReadMsg(msg proto.Message) error {
+	length64, err := binary.ReadUvarint(this.r)
+	if err != nil {
+		return err
+	}
+	length := int(length64)
+	if length < 0 || length > this.maxSize {
+		return io.ErrShortBuffer
+	}
+	if len(this.buf) < length {
+		this.buf = make([]byte, length)
+	}
+	buf := this.buf[:length]
+	if _, err := io.ReadFull(this.r, buf); err != nil {
+		return err
+	}
+	return proto.Unmarshal(buf, msg)
+}
+
+func (this *varintReader) Close() error {
+	if this.closer != nil {
+		return this.closer.Close()
+	}
+	return nil
+}

+ 608 - 0
vendor/github.com/gogo/protobuf/jsonpb/jsonpb.go

@@ -0,0 +1,608 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2015 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 jsonpb provides marshaling and unmarshaling between protocol buffers and JSON.
+It follows the specification at https://developers.google.com/protocol-buffers/docs/proto3#json.
+
+This package produces a different output than the standard "encoding/json" package,
+which does not operate correctly on protocol buffers.
+*/
+package jsonpb
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"reflect"
+	"sort"
+	"strconv"
+	"strings"
+
+	"github.com/gogo/protobuf/proto"
+)
+
+var (
+	byteArrayType = reflect.TypeOf([]byte{})
+)
+
+// Marshaler is a configurable object for converting between
+// protocol buffer objects and a JSON representation for them.
+type Marshaler struct {
+	// Whether to render enum values as integers, as opposed to string values.
+	EnumsAsInts bool
+
+	// Whether to render fields with zero values.
+	EmitDefaults bool
+
+	// A string to indent each level by. The presence of this field will
+	// also cause a space to appear between the field separator and
+	// value, and for newlines to be appear between fields and array
+	// elements.
+	Indent string
+}
+
+// Marshal marshals a protocol buffer into JSON.
+func (m *Marshaler) Marshal(out io.Writer, pb proto.Message) error {
+	writer := &errWriter{writer: out}
+	return m.marshalObject(writer, pb, "")
+}
+
+// MarshalToString converts a protocol buffer object to JSON string.
+func (m *Marshaler) MarshalToString(pb proto.Message) (string, error) {
+	var buf bytes.Buffer
+	if err := m.Marshal(&buf, pb); err != nil {
+		return "", err
+	}
+	return buf.String(), nil
+}
+
+type int32Slice []int32
+
+// For sorting extensions ids to ensure stable output.
+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] }
+
+// marshalObject writes a struct to the Writer.
+func (m *Marshaler) marshalObject(out *errWriter, v proto.Message, indent string) error {
+	out.write("{")
+	if m.Indent != "" {
+		out.write("\n")
+	}
+
+	s := reflect.ValueOf(v).Elem()
+	firstField := true
+	for i := 0; i < s.NumField(); i++ {
+		value := s.Field(i)
+		valueField := s.Type().Field(i)
+		if strings.HasPrefix(valueField.Name, "XXX_") {
+			continue
+		}
+
+		// IsNil will panic on most value kinds.
+		switch value.Kind() {
+		case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+			if value.IsNil() {
+				continue
+			}
+		}
+
+		if !m.EmitDefaults {
+			switch value.Kind() {
+			case reflect.Bool:
+				if !value.Bool() {
+					continue
+				}
+			case reflect.Int32, reflect.Int64:
+				if value.Int() == 0 {
+					continue
+				}
+			case reflect.Uint32, reflect.Uint64:
+				if value.Uint() == 0 {
+					continue
+				}
+			case reflect.Float32, reflect.Float64:
+				if value.Float() == 0 {
+					continue
+				}
+			case reflect.String:
+				if value.Len() == 0 {
+					continue
+				}
+			}
+		}
+
+		// Oneof fields need special handling.
+		if valueField.Tag.Get("protobuf_oneof") != "" {
+			// value is an interface containing &T{real_value}.
+			sv := value.Elem().Elem() // interface -> *T -> T
+			value = sv.Field(0)
+			valueField = sv.Type().Field(0)
+		}
+		prop := jsonProperties(valueField)
+		if !firstField {
+			m.writeSep(out)
+		}
+		// If the map value is a cast type, it may not implement proto.Message, therefore
+		// allow the struct tag to declare the underlying message type. Instead of changing
+		// the signatures of the child types (and because prop.mvalue is not public), use
+		// CustomType as a passer.
+		if value.Kind() == reflect.Map {
+			if tag := valueField.Tag.Get("protobuf"); tag != "" {
+				for _, v := range strings.Split(tag, ",") {
+					if !strings.HasPrefix(v, "castvaluetype=") {
+						continue
+					}
+					v = strings.TrimPrefix(v, "castvaluetype=")
+					prop.CustomType = v
+					break
+				}
+			}
+		}
+		if err := m.marshalField(out, prop, value, indent); err != nil {
+			return err
+		}
+		firstField = false
+	}
+
+	// Handle proto2 extensions.
+	if ep, ok := v.(extendableProto); ok {
+		extensions := proto.RegisteredExtensions(v)
+		extensionMap := ep.ExtensionMap()
+		// Sort extensions for stable output.
+		ids := make([]int32, 0, len(extensionMap))
+		for id := range extensionMap {
+			ids = append(ids, id)
+		}
+		sort.Sort(int32Slice(ids))
+		for _, id := range ids {
+			desc := extensions[id]
+			if desc == nil {
+				// unknown extension
+				continue
+			}
+			ext, extErr := proto.GetExtension(ep, desc)
+			if extErr != nil {
+				return extErr
+			}
+			value := reflect.ValueOf(ext)
+			var prop proto.Properties
+			prop.Parse(desc.Tag)
+			prop.OrigName = fmt.Sprintf("[%s]", desc.Name)
+			if !firstField {
+				m.writeSep(out)
+			}
+			if err := m.marshalField(out, &prop, value, indent); err != nil {
+				return err
+			}
+			firstField = false
+		}
+
+	}
+
+	if m.Indent != "" {
+		out.write("\n")
+		out.write(indent)
+	}
+	out.write("}")
+	return out.err
+}
+
+func (m *Marshaler) writeSep(out *errWriter) {
+	if m.Indent != "" {
+		out.write(",\n")
+	} else {
+		out.write(",")
+	}
+}
+
+// marshalField writes field description and value to the Writer.
+func (m *Marshaler) marshalField(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error {
+	if m.Indent != "" {
+		out.write(indent)
+		out.write(m.Indent)
+	}
+	out.write(`"`)
+	out.write(prop.OrigName)
+	out.write(`":`)
+	if m.Indent != "" {
+		out.write(" ")
+	}
+	if err := m.marshalValue(out, prop, v, indent); err != nil {
+		return err
+	}
+	return nil
+}
+
+// marshalValue writes the value to the Writer.
+func (m *Marshaler) marshalValue(out *errWriter, prop *proto.Properties, v reflect.Value, indent string) error {
+
+	v = reflect.Indirect(v)
+
+	// Handle repeated elements.
+	if v.Type() != byteArrayType && v.Kind() == reflect.Slice {
+		out.write("[")
+		comma := ""
+		for i := 0; i < v.Len(); i++ {
+			sliceVal := v.Index(i)
+			out.write(comma)
+			if m.Indent != "" {
+				out.write("\n")
+				out.write(indent)
+				out.write(m.Indent)
+				out.write(m.Indent)
+			}
+			m.marshalValue(out, prop, sliceVal, indent+m.Indent)
+			comma = ","
+		}
+		if m.Indent != "" {
+			out.write("\n")
+			out.write(indent)
+			out.write(m.Indent)
+		}
+		out.write("]")
+		return out.err
+	}
+
+	// Handle enumerations.
+	if !m.EnumsAsInts && prop.Enum != "" {
+		// Unknown enum values will are stringified by the proto library as their
+		// value. Such values should _not_ be quoted or they will be interpreted
+		// as an enum string instead of their value.
+		enumStr := v.Interface().(fmt.Stringer).String()
+		var valStr string
+		if v.Kind() == reflect.Ptr {
+			valStr = strconv.Itoa(int(v.Elem().Int()))
+		} else {
+			valStr = strconv.Itoa(int(v.Int()))
+		}
+
+		if m, ok := v.Interface().(interface {
+			MarshalJSON() ([]byte, error)
+		}); ok {
+			data, err := m.MarshalJSON()
+			if err != nil {
+				return err
+			}
+			enumStr = string(data)
+			enumStr, err = strconv.Unquote(enumStr)
+			if err != nil {
+				return err
+			}
+		}
+
+		isKnownEnum := enumStr != valStr
+
+		if isKnownEnum {
+			out.write(`"`)
+		}
+		out.write(enumStr)
+		if isKnownEnum {
+			out.write(`"`)
+		}
+		return out.err
+	}
+
+	// Handle nested messages.
+	if v.Kind() == reflect.Struct {
+		i := v
+		if v.CanAddr() {
+			i = v.Addr()
+		} else {
+			i = reflect.New(v.Type())
+			i.Elem().Set(v)
+		}
+		iface := i.Interface()
+		if iface == nil {
+			out.write(`null`)
+			return out.err
+		}
+		pm, ok := iface.(proto.Message)
+		if !ok {
+			if prop.CustomType == "" {
+				return fmt.Errorf("%v does not implement proto.Message", v.Type())
+			}
+			t := proto.MessageType(prop.CustomType)
+			if t == nil || !i.Type().ConvertibleTo(t) {
+				return fmt.Errorf("%v declared custom type %s but it is not convertible to %v", v.Type(), prop.CustomType, t)
+			}
+			pm = i.Convert(t).Interface().(proto.Message)
+		}
+		return m.marshalObject(out, pm, indent+m.Indent)
+	}
+
+	// Handle maps.
+	// Since Go randomizes map iteration, we sort keys for stable output.
+	if v.Kind() == reflect.Map {
+		out.write(`{`)
+		keys := v.MapKeys()
+		sort.Sort(mapKeys(keys))
+		for i, k := range keys {
+			if i > 0 {
+				out.write(`,`)
+			}
+			if m.Indent != "" {
+				out.write("\n")
+				out.write(indent)
+				out.write(m.Indent)
+				out.write(m.Indent)
+			}
+
+			b, err := json.Marshal(k.Interface())
+			if err != nil {
+				return err
+			}
+			s := string(b)
+
+			// If the JSON is not a string value, encode it again to make it one.
+			if !strings.HasPrefix(s, `"`) {
+				b, err := json.Marshal(s)
+				if err != nil {
+					return err
+				}
+				s = string(b)
+			}
+
+			out.write(s)
+			out.write(`:`)
+			if m.Indent != "" {
+				out.write(` `)
+			}
+
+			if err := m.marshalValue(out, prop, v.MapIndex(k), indent+m.Indent); err != nil {
+				return err
+			}
+		}
+		if m.Indent != "" {
+			out.write("\n")
+			out.write(indent)
+			out.write(m.Indent)
+		}
+		out.write(`}`)
+		return out.err
+	}
+
+	// Default handling defers to the encoding/json library.
+	b, err := json.Marshal(v.Interface())
+	if err != nil {
+		return err
+	}
+	needToQuote := string(b[0]) != `"` && (v.Kind() == reflect.Int64 || v.Kind() == reflect.Uint64)
+	if needToQuote {
+		out.write(`"`)
+	}
+	out.write(string(b))
+	if needToQuote {
+		out.write(`"`)
+	}
+	return out.err
+}
+
+// Unmarshal unmarshals a JSON object stream into a protocol
+// buffer. This function is lenient and will decode any options
+// permutations of the related Marshaler.
+func Unmarshal(r io.Reader, pb proto.Message) error {
+	inputValue := json.RawMessage{}
+	if err := json.NewDecoder(r).Decode(&inputValue); err != nil {
+		return err
+	}
+	return unmarshalValue(reflect.ValueOf(pb).Elem(), inputValue)
+}
+
+// UnmarshalString will populate the fields of a protocol buffer based
+// on a JSON string. This function is lenient and will decode any options
+// permutations of the related Marshaler.
+func UnmarshalString(str string, pb proto.Message) error {
+	return Unmarshal(strings.NewReader(str), pb)
+}
+
+// unmarshalValue converts/copies a value into the target.
+func unmarshalValue(target reflect.Value, inputValue json.RawMessage) error {
+	targetType := target.Type()
+
+	// Allocate memory for pointer fields.
+	if targetType.Kind() == reflect.Ptr {
+		target.Set(reflect.New(targetType.Elem()))
+		return unmarshalValue(target.Elem(), inputValue)
+	}
+
+	// Handle nested messages.
+	if targetType.Kind() == reflect.Struct {
+		var jsonFields map[string]json.RawMessage
+		if err := json.Unmarshal(inputValue, &jsonFields); err != nil {
+			return err
+		}
+
+		sprops := proto.GetProperties(targetType)
+		for i := 0; i < target.NumField(); i++ {
+			ft := target.Type().Field(i)
+			if strings.HasPrefix(ft.Name, "XXX_") {
+				continue
+			}
+			fieldName := jsonProperties(ft).OrigName
+
+			valueForField, ok := jsonFields[fieldName]
+			if !ok {
+				continue
+			}
+			delete(jsonFields, fieldName)
+
+			// Handle enums, which have an underlying type of int32,
+			// and may appear as strings. We do this while handling
+			// the struct so we have access to the enum info.
+			// The case of an enum appearing as a number is handled
+			// by the recursive call to unmarshalValue.
+			if enum := sprops.Prop[i].Enum; valueForField[0] == '"' && enum != "" {
+				vmap := proto.EnumValueMap(enum)
+				// Don't need to do unquoting; valid enum names
+				// are from a limited character set.
+				s := valueForField[1 : len(valueForField)-1]
+				n, ok := vmap[string(s)]
+				if !ok {
+					return fmt.Errorf("unknown value %q for enum %s", s, enum)
+				}
+				f := target.Field(i)
+				if f.Kind() == reflect.Ptr { // proto2
+					f.Set(reflect.New(f.Type().Elem()))
+					f = f.Elem()
+				}
+				f.SetInt(int64(n))
+				continue
+			}
+
+			if err := unmarshalValue(target.Field(i), valueForField); err != nil {
+				return err
+			}
+		}
+		// Check for any oneof fields.
+		for fname, raw := range jsonFields {
+			if oop, ok := sprops.OneofTypes[fname]; ok {
+				nv := reflect.New(oop.Type.Elem())
+				target.Field(oop.Field).Set(nv)
+				if err := unmarshalValue(nv.Elem().Field(0), raw); err != nil {
+					return err
+				}
+				delete(jsonFields, fname)
+			}
+		}
+		if len(jsonFields) > 0 {
+			// Pick any field to be the scapegoat.
+			var f string
+			for fname := range jsonFields {
+				f = fname
+				break
+			}
+			return fmt.Errorf("unknown field %q in %v", f, targetType)
+		}
+		return nil
+	}
+
+	// Handle arrays (which aren't encoded bytes)
+	if targetType != byteArrayType && targetType.Kind() == reflect.Slice {
+		var slc []json.RawMessage
+		if err := json.Unmarshal(inputValue, &slc); err != nil {
+			return err
+		}
+		len := len(slc)
+		target.Set(reflect.MakeSlice(targetType, len, len))
+		for i := 0; i < len; i++ {
+			if err := unmarshalValue(target.Index(i), slc[i]); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+
+	// Handle maps (whose keys are always strings)
+	if targetType.Kind() == reflect.Map {
+		var mp map[string]json.RawMessage
+		if err := json.Unmarshal(inputValue, &mp); err != nil {
+			return err
+		}
+		target.Set(reflect.MakeMap(targetType))
+		for ks, raw := range mp {
+			// Unmarshal map key. The core json library already decoded the key into a
+			// string, so we handle that specially. Other types were quoted post-serialization.
+			var k reflect.Value
+			if targetType.Key().Kind() == reflect.String {
+				k = reflect.ValueOf(ks)
+			} else {
+				k = reflect.New(targetType.Key()).Elem()
+				if err := unmarshalValue(k, json.RawMessage(ks)); err != nil {
+					return err
+				}
+			}
+
+			if !k.Type().AssignableTo(targetType.Key()) {
+				k = k.Convert(targetType.Key())
+			}
+
+			// Unmarshal map value.
+			v := reflect.New(targetType.Elem()).Elem()
+			if err := unmarshalValue(v, raw); err != nil {
+				return err
+			}
+			target.SetMapIndex(k, v)
+		}
+		return nil
+	}
+
+	// 64-bit integers can be encoded as strings. In this case we drop
+	// the quotes and proceed as normal.
+	isNum := targetType.Kind() == reflect.Int64 || targetType.Kind() == reflect.Uint64
+	if isNum && strings.HasPrefix(string(inputValue), `"`) {
+		inputValue = inputValue[1 : len(inputValue)-1]
+	}
+
+	// Use the encoding/json for parsing other value types.
+	return json.Unmarshal(inputValue, target.Addr().Interface())
+}
+
+// jsonProperties returns parsed proto.Properties for the field.
+func jsonProperties(f reflect.StructField) *proto.Properties {
+	var prop proto.Properties
+	prop.Init(f.Type, f.Name, f.Tag.Get("protobuf"), &f)
+	return &prop
+}
+
+// extendableProto is an interface implemented by any protocol buffer that may be extended.
+type extendableProto interface {
+	proto.Message
+	ExtensionRangeArray() []proto.ExtensionRange
+	ExtensionMap() map[int32]proto.Extension
+}
+
+// Writer wrapper inspired by https://blog.golang.org/errors-are-values
+type errWriter struct {
+	writer io.Writer
+	err    error
+}
+
+func (w *errWriter) write(str string) {
+	if w.err != nil {
+		return
+	}
+	_, w.err = w.writer.Write([]byte(str))
+}
+
+// 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())
+}

+ 120 - 0
vendor/github.com/gogo/protobuf/jsonpb/jsonpb_test_proto/more_test_objects.pb.go

@@ -0,0 +1,120 @@
+// Code generated by protoc-gen-gogo.
+// source: more_test_objects.proto
+// DO NOT EDIT!
+
+/*
+Package jsonpb is a generated protocol buffer package.
+
+It is generated from these files:
+	more_test_objects.proto
+	test_objects.proto
+
+It has these top-level messages:
+	Simple3
+	Mappy
+	Simple
+	Repeats
+	Widget
+	Maps
+	MsgWithOneof
+	Real
+	Complex
+*/
+package jsonpb
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+const _ = proto.GoGoProtoPackageIsVersion1
+
+type Simple3 struct {
+	Dub float64 `protobuf:"fixed64,1,opt,name=dub,proto3" json:"dub,omitempty"`
+}
+
+func (m *Simple3) Reset()                    { *m = Simple3{} }
+func (m *Simple3) String() string            { return proto.CompactTextString(m) }
+func (*Simple3) ProtoMessage()               {}
+func (*Simple3) Descriptor() ([]byte, []int) { return fileDescriptorMoreTestObjects, []int{0} }
+
+type Mappy struct {
+	Nummy map[int64]int32    `protobuf:"bytes,1,rep,name=nummy" json:"nummy,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+	Strry map[string]string  `protobuf:"bytes,2,rep,name=strry" json:"strry,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	Objjy map[int32]*Simple3 `protobuf:"bytes,3,rep,name=objjy" json:"objjy,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value"`
+	Buggy map[int64]string   `protobuf:"bytes,4,rep,name=buggy" json:"buggy,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	Booly map[bool]bool      `protobuf:"bytes,5,rep,name=booly" json:"booly,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
+}
+
+func (m *Mappy) Reset()                    { *m = Mappy{} }
+func (m *Mappy) String() string            { return proto.CompactTextString(m) }
+func (*Mappy) ProtoMessage()               {}
+func (*Mappy) Descriptor() ([]byte, []int) { return fileDescriptorMoreTestObjects, []int{1} }
+
+func (m *Mappy) GetNummy() map[int64]int32 {
+	if m != nil {
+		return m.Nummy
+	}
+	return nil
+}
+
+func (m *Mappy) GetStrry() map[string]string {
+	if m != nil {
+		return m.Strry
+	}
+	return nil
+}
+
+func (m *Mappy) GetObjjy() map[int32]*Simple3 {
+	if m != nil {
+		return m.Objjy
+	}
+	return nil
+}
+
+func (m *Mappy) GetBuggy() map[int64]string {
+	if m != nil {
+		return m.Buggy
+	}
+	return nil
+}
+
+func (m *Mappy) GetBooly() map[bool]bool {
+	if m != nil {
+		return m.Booly
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Simple3)(nil), "jsonpb.Simple3")
+	proto.RegisterType((*Mappy)(nil), "jsonpb.Mappy")
+}
+
+var fileDescriptorMoreTestObjects = []byte{
+	// 288 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x92, 0x41, 0x4b, 0xc3, 0x30,
+	0x14, 0xc7, 0xe9, 0xba, 0xcc, 0xf5, 0xed, 0xa0, 0x14, 0xc1, 0xa0, 0x17, 0x19, 0x08, 0x3d, 0xe5,
+	0xb0, 0x5d, 0x86, 0x47, 0xc1, 0x83, 0x07, 0x15, 0xba, 0x0f, 0x30, 0x16, 0x0d, 0xc3, 0xda, 0x36,
+	0x21, 0x4d, 0x85, 0x7c, 0x25, 0x3f, 0xa5, 0x7d, 0x69, 0x67, 0xc3, 0x08, 0xec, 0xf6, 0xca, 0xff,
+	0xf7, 0x83, 0xf7, 0x7f, 0x0d, 0xdc, 0x54, 0x52, 0x8b, 0x9d, 0x11, 0x8d, 0xd9, 0x49, 0x5e, 0x88,
+	0x0f, 0xd3, 0x30, 0xa5, 0xa5, 0x91, 0xe9, 0xac, 0x68, 0x64, 0xad, 0xf8, 0xf2, 0x0e, 0x2e, 0xb6,
+	0x5f, 0x95, 0x2a, 0xc5, 0x3a, 0xbd, 0x82, 0xf8, 0xb3, 0xe5, 0x34, 0xba, 0x8f, 0xb2, 0x28, 0xc7,
+	0x71, 0xf9, 0x3b, 0x05, 0xf2, 0xba, 0x57, 0xca, 0xa6, 0x0c, 0x48, 0xdd, 0x56, 0x95, 0xed, 0xd2,
+	0x38, 0x5b, 0xac, 0x28, 0xeb, 0x75, 0xe6, 0x52, 0xf6, 0x86, 0xd1, 0x73, 0x6d, 0xb4, 0xcd, 0x7b,
+	0x0c, 0xf9, 0xc6, 0x68, 0x6d, 0xe9, 0x24, 0xc4, 0x6f, 0x31, 0x1a, 0x78, 0x87, 0x21, 0xdf, 0xed,
+	0x57, 0x58, 0x1a, 0x87, 0xf8, 0x77, 0x8c, 0x06, 0xde, 0x61, 0xc8, 0xf3, 0xf6, 0x70, 0xb0, 0x74,
+	0x1a, 0xe2, 0x9f, 0x30, 0x1a, 0x78, 0x87, 0x39, 0x5e, 0xca, 0xd2, 0x52, 0x12, 0xe4, 0x31, 0x3a,
+	0xf2, 0x38, 0xdf, 0x6e, 0x00, 0xc6, 0x52, 0x78, 0x99, 0x6f, 0x61, 0xdd, 0x65, 0xe2, 0x1c, 0xc7,
+	0xf4, 0x1a, 0xc8, 0xcf, 0xbe, 0x6c, 0x45, 0xd7, 0x2f, 0xca, 0x48, 0xde, 0x7f, 0x3c, 0x4e, 0x36,
+	0x11, 0x9a, 0x63, 0x3d, 0xdf, 0x4c, 0x02, 0x66, 0xe2, 0x9b, 0x2f, 0x00, 0x63, 0x51, 0xdf, 0x24,
+	0xbd, 0xf9, 0xe0, 0x9b, 0x8b, 0xd5, 0xe5, 0xb1, 0xc3, 0xf0, 0xff, 0x4e, 0x96, 0x18, 0x6f, 0x70,
+	0x6e, 0xfd, 0xe4, 0xd4, 0xfc, 0xbf, 0x86, 0x6f, 0xce, 0x03, 0xe6, 0xdc, 0x33, 0xf9, 0xcc, 0x3d,
+	0xac, 0xf5, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x39, 0x61, 0xe7, 0x9b, 0x73, 0x02, 0x00, 0x00,
+}

+ 586 - 0
vendor/github.com/gogo/protobuf/jsonpb/jsonpb_test_proto/test_objects.pb.go

@@ -0,0 +1,586 @@
+// Code generated by protoc-gen-gogo.
+// source: test_objects.proto
+// DO NOT EDIT!
+
+package jsonpb
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+type Widget_Color int32
+
+const (
+	Widget_RED   Widget_Color = 0
+	Widget_GREEN Widget_Color = 1
+	Widget_BLUE  Widget_Color = 2
+)
+
+var Widget_Color_name = map[int32]string{
+	0: "RED",
+	1: "GREEN",
+	2: "BLUE",
+}
+var Widget_Color_value = map[string]int32{
+	"RED":   0,
+	"GREEN": 1,
+	"BLUE":  2,
+}
+
+func (x Widget_Color) Enum() *Widget_Color {
+	p := new(Widget_Color)
+	*p = x
+	return p
+}
+func (x Widget_Color) String() string {
+	return proto.EnumName(Widget_Color_name, int32(x))
+}
+func (x *Widget_Color) UnmarshalJSON(data []byte) error {
+	value, err := proto.UnmarshalJSONEnum(Widget_Color_value, data, "Widget_Color")
+	if err != nil {
+		return err
+	}
+	*x = Widget_Color(value)
+	return nil
+}
+func (Widget_Color) EnumDescriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{2, 0} }
+
+// Test message for holding primitive types.
+type Simple struct {
+	OBool            *bool    `protobuf:"varint,1,opt,name=o_bool,json=oBool" json:"o_bool,omitempty"`
+	OInt32           *int32   `protobuf:"varint,2,opt,name=o_int32,json=oInt32" json:"o_int32,omitempty"`
+	OInt64           *int64   `protobuf:"varint,3,opt,name=o_int64,json=oInt64" json:"o_int64,omitempty"`
+	OUint32          *uint32  `protobuf:"varint,4,opt,name=o_uint32,json=oUint32" json:"o_uint32,omitempty"`
+	OUint64          *uint64  `protobuf:"varint,5,opt,name=o_uint64,json=oUint64" json:"o_uint64,omitempty"`
+	OSint32          *int32   `protobuf:"zigzag32,6,opt,name=o_sint32,json=oSint32" json:"o_sint32,omitempty"`
+	OSint64          *int64   `protobuf:"zigzag64,7,opt,name=o_sint64,json=oSint64" json:"o_sint64,omitempty"`
+	OFloat           *float32 `protobuf:"fixed32,8,opt,name=o_float,json=oFloat" json:"o_float,omitempty"`
+	ODouble          *float64 `protobuf:"fixed64,9,opt,name=o_double,json=oDouble" json:"o_double,omitempty"`
+	OString          *string  `protobuf:"bytes,10,opt,name=o_string,json=oString" json:"o_string,omitempty"`
+	OBytes           []byte   `protobuf:"bytes,11,opt,name=o_bytes,json=oBytes" json:"o_bytes,omitempty"`
+	XXX_unrecognized []byte   `json:"-"`
+}
+
+func (m *Simple) Reset()                    { *m = Simple{} }
+func (m *Simple) String() string            { return proto.CompactTextString(m) }
+func (*Simple) ProtoMessage()               {}
+func (*Simple) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{0} }
+
+func (m *Simple) GetOBool() bool {
+	if m != nil && m.OBool != nil {
+		return *m.OBool
+	}
+	return false
+}
+
+func (m *Simple) GetOInt32() int32 {
+	if m != nil && m.OInt32 != nil {
+		return *m.OInt32
+	}
+	return 0
+}
+
+func (m *Simple) GetOInt64() int64 {
+	if m != nil && m.OInt64 != nil {
+		return *m.OInt64
+	}
+	return 0
+}
+
+func (m *Simple) GetOUint32() uint32 {
+	if m != nil && m.OUint32 != nil {
+		return *m.OUint32
+	}
+	return 0
+}
+
+func (m *Simple) GetOUint64() uint64 {
+	if m != nil && m.OUint64 != nil {
+		return *m.OUint64
+	}
+	return 0
+}
+
+func (m *Simple) GetOSint32() int32 {
+	if m != nil && m.OSint32 != nil {
+		return *m.OSint32
+	}
+	return 0
+}
+
+func (m *Simple) GetOSint64() int64 {
+	if m != nil && m.OSint64 != nil {
+		return *m.OSint64
+	}
+	return 0
+}
+
+func (m *Simple) GetOFloat() float32 {
+	if m != nil && m.OFloat != nil {
+		return *m.OFloat
+	}
+	return 0
+}
+
+func (m *Simple) GetODouble() float64 {
+	if m != nil && m.ODouble != nil {
+		return *m.ODouble
+	}
+	return 0
+}
+
+func (m *Simple) GetOString() string {
+	if m != nil && m.OString != nil {
+		return *m.OString
+	}
+	return ""
+}
+
+func (m *Simple) GetOBytes() []byte {
+	if m != nil {
+		return m.OBytes
+	}
+	return nil
+}
+
+// Test message for holding repeated primitives.
+type Repeats struct {
+	RBool            []bool    `protobuf:"varint,1,rep,name=r_bool,json=rBool" json:"r_bool,omitempty"`
+	RInt32           []int32   `protobuf:"varint,2,rep,name=r_int32,json=rInt32" json:"r_int32,omitempty"`
+	RInt64           []int64   `protobuf:"varint,3,rep,name=r_int64,json=rInt64" json:"r_int64,omitempty"`
+	RUint32          []uint32  `protobuf:"varint,4,rep,name=r_uint32,json=rUint32" json:"r_uint32,omitempty"`
+	RUint64          []uint64  `protobuf:"varint,5,rep,name=r_uint64,json=rUint64" json:"r_uint64,omitempty"`
+	RSint32          []int32   `protobuf:"zigzag32,6,rep,name=r_sint32,json=rSint32" json:"r_sint32,omitempty"`
+	RSint64          []int64   `protobuf:"zigzag64,7,rep,name=r_sint64,json=rSint64" json:"r_sint64,omitempty"`
+	RFloat           []float32 `protobuf:"fixed32,8,rep,name=r_float,json=rFloat" json:"r_float,omitempty"`
+	RDouble          []float64 `protobuf:"fixed64,9,rep,name=r_double,json=rDouble" json:"r_double,omitempty"`
+	RString          []string  `protobuf:"bytes,10,rep,name=r_string,json=rString" json:"r_string,omitempty"`
+	RBytes           [][]byte  `protobuf:"bytes,11,rep,name=r_bytes,json=rBytes" json:"r_bytes,omitempty"`
+	XXX_unrecognized []byte    `json:"-"`
+}
+
+func (m *Repeats) Reset()                    { *m = Repeats{} }
+func (m *Repeats) String() string            { return proto.CompactTextString(m) }
+func (*Repeats) ProtoMessage()               {}
+func (*Repeats) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{1} }
+
+func (m *Repeats) GetRBool() []bool {
+	if m != nil {
+		return m.RBool
+	}
+	return nil
+}
+
+func (m *Repeats) GetRInt32() []int32 {
+	if m != nil {
+		return m.RInt32
+	}
+	return nil
+}
+
+func (m *Repeats) GetRInt64() []int64 {
+	if m != nil {
+		return m.RInt64
+	}
+	return nil
+}
+
+func (m *Repeats) GetRUint32() []uint32 {
+	if m != nil {
+		return m.RUint32
+	}
+	return nil
+}
+
+func (m *Repeats) GetRUint64() []uint64 {
+	if m != nil {
+		return m.RUint64
+	}
+	return nil
+}
+
+func (m *Repeats) GetRSint32() []int32 {
+	if m != nil {
+		return m.RSint32
+	}
+	return nil
+}
+
+func (m *Repeats) GetRSint64() []int64 {
+	if m != nil {
+		return m.RSint64
+	}
+	return nil
+}
+
+func (m *Repeats) GetRFloat() []float32 {
+	if m != nil {
+		return m.RFloat
+	}
+	return nil
+}
+
+func (m *Repeats) GetRDouble() []float64 {
+	if m != nil {
+		return m.RDouble
+	}
+	return nil
+}
+
+func (m *Repeats) GetRString() []string {
+	if m != nil {
+		return m.RString
+	}
+	return nil
+}
+
+func (m *Repeats) GetRBytes() [][]byte {
+	if m != nil {
+		return m.RBytes
+	}
+	return nil
+}
+
+// Test message for holding enums and nested messages.
+type Widget struct {
+	Color            *Widget_Color  `protobuf:"varint,1,opt,name=color,enum=jsonpb.Widget_Color" json:"color,omitempty"`
+	RColor           []Widget_Color `protobuf:"varint,2,rep,name=r_color,json=rColor,enum=jsonpb.Widget_Color" json:"r_color,omitempty"`
+	Simple           *Simple        `protobuf:"bytes,10,opt,name=simple" json:"simple,omitempty"`
+	RSimple          []*Simple      `protobuf:"bytes,11,rep,name=r_simple,json=rSimple" json:"r_simple,omitempty"`
+	Repeats          *Repeats       `protobuf:"bytes,20,opt,name=repeats" json:"repeats,omitempty"`
+	RRepeats         []*Repeats     `protobuf:"bytes,21,rep,name=r_repeats,json=rRepeats" json:"r_repeats,omitempty"`
+	XXX_unrecognized []byte         `json:"-"`
+}
+
+func (m *Widget) Reset()                    { *m = Widget{} }
+func (m *Widget) String() string            { return proto.CompactTextString(m) }
+func (*Widget) ProtoMessage()               {}
+func (*Widget) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{2} }
+
+func (m *Widget) GetColor() Widget_Color {
+	if m != nil && m.Color != nil {
+		return *m.Color
+	}
+	return Widget_RED
+}
+
+func (m *Widget) GetRColor() []Widget_Color {
+	if m != nil {
+		return m.RColor
+	}
+	return nil
+}
+
+func (m *Widget) GetSimple() *Simple {
+	if m != nil {
+		return m.Simple
+	}
+	return nil
+}
+
+func (m *Widget) GetRSimple() []*Simple {
+	if m != nil {
+		return m.RSimple
+	}
+	return nil
+}
+
+func (m *Widget) GetRepeats() *Repeats {
+	if m != nil {
+		return m.Repeats
+	}
+	return nil
+}
+
+func (m *Widget) GetRRepeats() []*Repeats {
+	if m != nil {
+		return m.RRepeats
+	}
+	return nil
+}
+
+type Maps struct {
+	MInt64Str        map[int64]string `protobuf:"bytes,1,rep,name=m_int64_str,json=mInt64Str" json:"m_int64_str,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	MBoolSimple      map[bool]*Simple `protobuf:"bytes,2,rep,name=m_bool_simple,json=mBoolSimple" json:"m_bool_simple,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	XXX_unrecognized []byte           `json:"-"`
+}
+
+func (m *Maps) Reset()                    { *m = Maps{} }
+func (m *Maps) String() string            { return proto.CompactTextString(m) }
+func (*Maps) ProtoMessage()               {}
+func (*Maps) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{3} }
+
+func (m *Maps) GetMInt64Str() map[int64]string {
+	if m != nil {
+		return m.MInt64Str
+	}
+	return nil
+}
+
+func (m *Maps) GetMBoolSimple() map[bool]*Simple {
+	if m != nil {
+		return m.MBoolSimple
+	}
+	return nil
+}
+
+type MsgWithOneof struct {
+	// Types that are valid to be assigned to Union:
+	//	*MsgWithOneof_Title
+	//	*MsgWithOneof_Salary
+	Union            isMsgWithOneof_Union `protobuf_oneof:"union"`
+	XXX_unrecognized []byte               `json:"-"`
+}
+
+func (m *MsgWithOneof) Reset()                    { *m = MsgWithOneof{} }
+func (m *MsgWithOneof) String() string            { return proto.CompactTextString(m) }
+func (*MsgWithOneof) ProtoMessage()               {}
+func (*MsgWithOneof) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{4} }
+
+type isMsgWithOneof_Union interface {
+	isMsgWithOneof_Union()
+}
+
+type MsgWithOneof_Title struct {
+	Title string `protobuf:"bytes,1,opt,name=title,oneof"`
+}
+type MsgWithOneof_Salary struct {
+	Salary int64 `protobuf:"varint,2,opt,name=salary,oneof"`
+}
+
+func (*MsgWithOneof_Title) isMsgWithOneof_Union()  {}
+func (*MsgWithOneof_Salary) isMsgWithOneof_Union() {}
+
+func (m *MsgWithOneof) GetUnion() isMsgWithOneof_Union {
+	if m != nil {
+		return m.Union
+	}
+	return nil
+}
+
+func (m *MsgWithOneof) GetTitle() string {
+	if x, ok := m.GetUnion().(*MsgWithOneof_Title); ok {
+		return x.Title
+	}
+	return ""
+}
+
+func (m *MsgWithOneof) GetSalary() int64 {
+	if x, ok := m.GetUnion().(*MsgWithOneof_Salary); ok {
+		return x.Salary
+	}
+	return 0
+}
+
+// XXX_OneofFuncs is for the internal use of the proto package.
+func (*MsgWithOneof) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
+	return _MsgWithOneof_OneofMarshaler, _MsgWithOneof_OneofUnmarshaler, _MsgWithOneof_OneofSizer, []interface{}{
+		(*MsgWithOneof_Title)(nil),
+		(*MsgWithOneof_Salary)(nil),
+	}
+}
+
+func _MsgWithOneof_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
+	m := msg.(*MsgWithOneof)
+	// union
+	switch x := m.Union.(type) {
+	case *MsgWithOneof_Title:
+		_ = b.EncodeVarint(1<<3 | proto.WireBytes)
+		_ = b.EncodeStringBytes(x.Title)
+	case *MsgWithOneof_Salary:
+		_ = b.EncodeVarint(2<<3 | proto.WireVarint)
+		_ = b.EncodeVarint(uint64(x.Salary))
+	case nil:
+	default:
+		return fmt.Errorf("MsgWithOneof.Union has unexpected type %T", x)
+	}
+	return nil
+}
+
+func _MsgWithOneof_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
+	m := msg.(*MsgWithOneof)
+	switch tag {
+	case 1: // union.title
+		if wire != proto.WireBytes {
+			return true, proto.ErrInternalBadWireType
+		}
+		x, err := b.DecodeStringBytes()
+		m.Union = &MsgWithOneof_Title{x}
+		return true, err
+	case 2: // union.salary
+		if wire != proto.WireVarint {
+			return true, proto.ErrInternalBadWireType
+		}
+		x, err := b.DecodeVarint()
+		m.Union = &MsgWithOneof_Salary{int64(x)}
+		return true, err
+	default:
+		return false, nil
+	}
+}
+
+func _MsgWithOneof_OneofSizer(msg proto.Message) (n int) {
+	m := msg.(*MsgWithOneof)
+	// union
+	switch x := m.Union.(type) {
+	case *MsgWithOneof_Title:
+		n += proto.SizeVarint(1<<3 | proto.WireBytes)
+		n += proto.SizeVarint(uint64(len(x.Title)))
+		n += len(x.Title)
+	case *MsgWithOneof_Salary:
+		n += proto.SizeVarint(2<<3 | proto.WireVarint)
+		n += proto.SizeVarint(uint64(x.Salary))
+	case nil:
+	default:
+		panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
+	}
+	return n
+}
+
+type Real struct {
+	Value            *float64                  `protobuf:"fixed64,1,opt,name=value" json:"value,omitempty"`
+	XXX_extensions   map[int32]proto.Extension `json:"-"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *Real) Reset()                    { *m = Real{} }
+func (m *Real) String() string            { return proto.CompactTextString(m) }
+func (*Real) ProtoMessage()               {}
+func (*Real) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{5} }
+
+var extRange_Real = []proto.ExtensionRange{
+	{100, 536870911},
+}
+
+func (*Real) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_Real
+}
+func (m *Real) ExtensionMap() map[int32]proto.Extension {
+	if m.XXX_extensions == nil {
+		m.XXX_extensions = make(map[int32]proto.Extension)
+	}
+	return m.XXX_extensions
+}
+
+func (m *Real) GetValue() float64 {
+	if m != nil && m.Value != nil {
+		return *m.Value
+	}
+	return 0
+}
+
+type Complex struct {
+	Imaginary        *float64                  `protobuf:"fixed64,1,opt,name=imaginary" json:"imaginary,omitempty"`
+	XXX_extensions   map[int32]proto.Extension `json:"-"`
+	XXX_unrecognized []byte                    `json:"-"`
+}
+
+func (m *Complex) Reset()                    { *m = Complex{} }
+func (m *Complex) String() string            { return proto.CompactTextString(m) }
+func (*Complex) ProtoMessage()               {}
+func (*Complex) Descriptor() ([]byte, []int) { return fileDescriptorTestObjects, []int{6} }
+
+var extRange_Complex = []proto.ExtensionRange{
+	{100, 536870911},
+}
+
+func (*Complex) ExtensionRangeArray() []proto.ExtensionRange {
+	return extRange_Complex
+}
+func (m *Complex) ExtensionMap() map[int32]proto.Extension {
+	if m.XXX_extensions == nil {
+		m.XXX_extensions = make(map[int32]proto.Extension)
+	}
+	return m.XXX_extensions
+}
+
+func (m *Complex) GetImaginary() float64 {
+	if m != nil && m.Imaginary != nil {
+		return *m.Imaginary
+	}
+	return 0
+}
+
+var E_Complex_RealExtension = &proto.ExtensionDesc{
+	ExtendedType:  (*Real)(nil),
+	ExtensionType: (*Complex)(nil),
+	Field:         123,
+	Name:          "jsonpb.Complex.real_extension",
+	Tag:           "bytes,123,opt,name=real_extension,json=realExtension",
+}
+
+var E_Name = &proto.ExtensionDesc{
+	ExtendedType:  (*Real)(nil),
+	ExtensionType: (*string)(nil),
+	Field:         124,
+	Name:          "jsonpb.name",
+	Tag:           "bytes,124,opt,name=name",
+}
+
+func init() {
+	proto.RegisterType((*Simple)(nil), "jsonpb.Simple")
+	proto.RegisterType((*Repeats)(nil), "jsonpb.Repeats")
+	proto.RegisterType((*Widget)(nil), "jsonpb.Widget")
+	proto.RegisterType((*Maps)(nil), "jsonpb.Maps")
+	proto.RegisterType((*MsgWithOneof)(nil), "jsonpb.MsgWithOneof")
+	proto.RegisterType((*Real)(nil), "jsonpb.Real")
+	proto.RegisterType((*Complex)(nil), "jsonpb.Complex")
+	proto.RegisterEnum("jsonpb.Widget_Color", Widget_Color_name, Widget_Color_value)
+	proto.RegisterExtension(E_Complex_RealExtension)
+	proto.RegisterExtension(E_Name)
+}
+
+var fileDescriptorTestObjects = []byte{
+	// 724 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x94, 0x51, 0x4f, 0xd3, 0x5e,
+	0x18, 0xc6, 0x59, 0xcf, 0xba, 0xb6, 0xef, 0x60, 0xff, 0xfd, 0x4f, 0x40, 0x2b, 0x6a, 0x42, 0x16,
+	0x25, 0x4a, 0x74, 0x17, 0x93, 0x10, 0x83, 0xde, 0x38, 0x98, 0x4a, 0x22, 0x98, 0x94, 0x10, 0x2e,
+	0x97, 0x0e, 0xca, 0x2c, 0x76, 0x3d, 0xcb, 0xdb, 0x4e, 0x59, 0xf4, 0xc2, 0x0f, 0xe1, 0x57, 0xd0,
+	0x8f, 0xe0, 0xe7, 0xf3, 0xbc, 0xe7, 0xb4, 0x3d, 0x03, 0x22, 0x37, 0xec, 0xed, 0xf3, 0x3e, 0xcf,
+	0x4e, 0x7f, 0xe7, 0xc9, 0x80, 0xe7, 0x51, 0x96, 0x0f, 0xc5, 0xe8, 0x32, 0x3a, 0xcb, 0xb3, 0xee,
+	0x14, 0x45, 0x2e, 0x78, 0xe3, 0x32, 0x13, 0xe9, 0x74, 0xd4, 0xf9, 0x65, 0x41, 0xe3, 0x38, 0x9e,
+	0x4c, 0x93, 0x88, 0xaf, 0x41, 0x43, 0x0c, 0x47, 0x42, 0x24, 0x7e, 0x6d, 0xa3, 0xf6, 0xc4, 0x0d,
+	0x6c, 0xd1, 0x97, 0x03, 0xbf, 0x0b, 0x8e, 0x18, 0xc6, 0x69, 0xfe, 0xa2, 0xe7, 0x5b, 0xf2, 0xb9,
+	0x1d, 0x34, 0xc4, 0x01, 0x4d, 0x95, 0xb0, 0xb3, 0xed, 0x33, 0x29, 0x30, 0x2d, 0xec, 0x6c, 0xf3,
+	0x7b, 0xe0, 0x8a, 0xe1, 0x4c, 0x5b, 0xea, 0x52, 0x59, 0x09, 0x1c, 0x71, 0xa2, 0x46, 0x23, 0x49,
+	0x93, 0x2d, 0xa5, 0x7a, 0x21, 0x95, 0xae, 0x4c, 0xbb, 0x1a, 0x52, 0xfa, 0x5f, 0x4a, 0xc7, 0x0b,
+	0xae, 0x4c, 0xbb, 0x1c, 0x29, 0xf1, 0x42, 0x92, 0x2e, 0x75, 0x88, 0x8b, 0x44, 0x84, 0xb9, 0xef,
+	0x4a, 0xc5, 0x92, 0x87, 0x78, 0x4b, 0x93, 0xf6, 0x9c, 0x8b, 0xd9, 0x28, 0x89, 0x7c, 0x4f, 0x2a,
+	0x35, 0xe9, 0xd9, 0x57, 0x63, 0x11, 0x97, 0x63, 0x9c, 0x8e, 0x7d, 0x90, 0x92, 0x47, 0x71, 0x6a,
+	0xd4, 0x71, 0xa3, 0xb9, 0x04, 0xe6, 0x37, 0xa5, 0xb2, 0x2c, 0xe3, 0xfa, 0x34, 0x75, 0x7e, 0x5b,
+	0xe0, 0x04, 0xd1, 0x34, 0x0a, 0xf3, 0x8c, 0x40, 0x61, 0x09, 0x8a, 0x11, 0x28, 0x2c, 0x41, 0x61,
+	0x05, 0x8a, 0x11, 0x28, 0xac, 0x40, 0x61, 0x05, 0x8a, 0x11, 0x28, 0xac, 0x40, 0xa1, 0x01, 0xc5,
+	0x08, 0x14, 0x1a, 0x50, 0x68, 0x40, 0x31, 0x02, 0x85, 0x06, 0x14, 0x1a, 0x50, 0x8c, 0x40, 0xe1,
+	0xf1, 0x82, 0xab, 0x02, 0xc5, 0x08, 0x14, 0x1a, 0x50, 0x58, 0x81, 0x62, 0x04, 0x0a, 0x2b, 0x50,
+	0x68, 0x40, 0x31, 0x02, 0x85, 0x06, 0x14, 0x1a, 0x50, 0x8c, 0x40, 0xa1, 0x01, 0x85, 0x15, 0x28,
+	0x46, 0xa0, 0x50, 0x83, 0xfa, 0x23, 0x0b, 0x75, 0x1a, 0x9f, 0x8f, 0xa3, 0x9c, 0x6f, 0x81, 0x7d,
+	0x26, 0x12, 0x81, 0xaa, 0x4f, 0xad, 0xde, 0x6a, 0x57, 0x77, 0xae, 0xab, 0xe5, 0xee, 0x1e, 0x69,
+	0x81, 0x5e, 0xe1, 0xcf, 0x29, 0x4f, 0x6f, 0x13, 0xbc, 0x7f, 0x6d, 0x37, 0x50, 0xfd, 0xe7, 0x9b,
+	0xd0, 0xc8, 0x54, 0x6b, 0xd5, 0x05, 0x36, 0x7b, 0xad, 0x72, 0x5b, 0x77, 0x39, 0x28, 0x54, 0xfe,
+	0x54, 0x03, 0x51, 0x9b, 0x74, 0xce, 0xdb, 0x9b, 0x04, 0xa8, 0x58, 0x75, 0x50, 0x5f, 0xb0, 0xbf,
+	0xaa, 0x32, 0xff, 0x2b, 0x37, 0x8b, 0x7b, 0x0f, 0x4a, 0x9d, 0x3f, 0x03, 0x0f, 0x87, 0xe5, 0xf2,
+	0x9a, 0x8a, 0xbd, 0xb5, 0xec, 0x62, 0xf1, 0xa9, 0xf3, 0x18, 0x6c, 0x7d, 0x68, 0x07, 0x58, 0x30,
+	0xd8, 0x6f, 0x2f, 0x71, 0x0f, 0xec, 0x77, 0xc1, 0x60, 0x70, 0xd4, 0xae, 0x71, 0x17, 0xea, 0xfd,
+	0x0f, 0x27, 0x83, 0xb6, 0xd5, 0xf9, 0x69, 0x41, 0xfd, 0x30, 0x9c, 0x66, 0xfc, 0x15, 0x34, 0x27,
+	0xba, 0x2e, 0xc4, 0x5e, 0x75, 0xac, 0xd9, 0xbb, 0x5f, 0xe6, 0xd3, 0x4a, 0xf7, 0x50, 0xf5, 0x47,
+	0x5e, 0xc5, 0x20, 0xcd, 0x71, 0x1e, 0x78, 0x93, 0x72, 0xe6, 0x6f, 0x60, 0x65, 0xa2, 0xba, 0x59,
+	0xbe, 0xb5, 0xa5, 0xec, 0x0f, 0xaf, 0xdb, 0xa9, 0xaf, 0xfa, 0xb5, 0x75, 0x40, 0x73, 0x62, 0x9e,
+	0xac, 0xbf, 0x86, 0xd6, 0xf5, 0x7c, 0xde, 0x06, 0xf6, 0x39, 0x9a, 0xab, 0x6b, 0x64, 0x01, 0x7d,
+	0xe4, 0xab, 0x60, 0x7f, 0x09, 0x93, 0x59, 0xa4, 0x7e, 0x12, 0xbc, 0x40, 0x0f, 0xbb, 0xd6, 0xcb,
+	0xda, 0xfa, 0x11, 0xb4, 0x6f, 0xc6, 0x2f, 0xfa, 0x5d, 0xed, 0x7f, 0xb4, 0xe8, 0xbf, 0x7d, 0x29,
+	0x26, 0xaf, 0x73, 0x00, 0xcb, 0x87, 0xd9, 0xf8, 0x34, 0xce, 0x3f, 0x7d, 0x4c, 0x23, 0x71, 0xc1,
+	0xef, 0x80, 0x9d, 0xc7, 0xb9, 0x7c, 0x31, 0x4a, 0xf3, 0xde, 0x2f, 0x05, 0x7a, 0xe4, 0xbe, 0x6c,
+	0x44, 0x98, 0x84, 0x38, 0x57, 0x91, 0x4c, 0x0a, 0xc5, 0xdc, 0x77, 0xc0, 0x9e, 0xa5, 0xb1, 0x48,
+	0x3b, 0x9b, 0x50, 0x0f, 0xa2, 0x30, 0x31, 0x87, 0xaf, 0xa9, 0xdf, 0x05, 0x3d, 0x6c, 0xb9, 0xee,
+	0x79, 0xfb, 0x87, 0xfc, 0xb3, 0x3a, 0x5f, 0xc1, 0xd9, 0x13, 0x74, 0x8e, 0x2b, 0xfe, 0x00, 0xbc,
+	0x78, 0x12, 0x8e, 0xe3, 0x94, 0x82, 0xf5, 0xba, 0x79, 0x60, 0x2c, 0xbd, 0x7d, 0x68, 0xa1, 0x8c,
+	0x1e, 0x46, 0x57, 0x79, 0x94, 0x66, 0xf2, 0xcb, 0xf8, 0xb2, 0x29, 0x44, 0x98, 0xf8, 0xdf, 0xae,
+	0x37, 0xaa, 0x88, 0x0f, 0x56, 0xc8, 0x34, 0x28, 0x3d, 0xbb, 0x1b, 0x50, 0x4f, 0xc3, 0x49, 0x74,
+	0xc3, 0xfb, 0x5d, 0x21, 0x56, 0xca, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0e, 0xd7, 0x26, 0x3c,
+	0xcb, 0x05, 0x00, 0x00,
+}

+ 520 - 0
vendor/github.com/gogo/protobuf/plugin/compare/compare.go

@@ -0,0 +1,520 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 compare
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/proto"
+	descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"github.com/gogo/protobuf/vanity"
+)
+
+type plugin struct {
+	*generator.Generator
+	generator.PluginImports
+	fmtPkg      generator.Single
+	bytesPkg    generator.Single
+	sortkeysPkg generator.Single
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "compare"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.fmtPkg = p.NewImport("fmt")
+	p.bytesPkg = p.NewImport("bytes")
+	p.sortkeysPkg = p.NewImport("github.com/gogo/protobuf/sortkeys")
+
+	for _, msg := range file.Messages() {
+		if msg.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		if gogoproto.HasCompare(file.FileDescriptorProto, msg.DescriptorProto) {
+			p.generateMessage(file, msg)
+		}
+	}
+}
+
+func (p *plugin) generateNullableField(fieldname string) {
+	p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
+	p.In()
+	p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
+	p.In()
+	p.P(`if *this.`, fieldname, ` < *that1.`, fieldname, `{`)
+	p.In()
+	p.P(`return -1`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return 1`)
+	p.Out()
+	p.P(`}`)
+	p.Out()
+	p.P(`} else if this.`, fieldname, ` != nil {`)
+	p.In()
+	p.P(`return 1`)
+	p.Out()
+	p.P(`} else if that1.`, fieldname, ` != nil {`)
+	p.In()
+	p.P(`return -1`)
+	p.Out()
+	p.P(`}`)
+}
+
+func (p *plugin) generateMsgNullAndTypeCheck(ccTypeName string) {
+	p.P(`if that == nil {`)
+	p.In()
+	p.P(`if this == nil {`)
+	p.In()
+	p.P(`return 0`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return 1`)
+	p.Out()
+	p.P(`}`)
+	p.P(``)
+	p.P(`that1, ok := that.(*`, ccTypeName, `)`)
+	p.P(`if !ok {`)
+	p.In()
+	p.P(`that2, ok := that.(`, ccTypeName, `)`)
+	p.P(`if ok {`)
+	p.In()
+	p.P(`that1 = &that2`)
+	p.Out()
+	p.P(`} else {`)
+	p.In()
+	p.P(`return 1`)
+	p.Out()
+	p.P(`}`)
+	p.Out()
+	p.P(`}`)
+	p.P(`if that1 == nil {`)
+	p.In()
+	p.P(`if this == nil {`)
+	p.In()
+	p.P(`return 0`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return 1`)
+	p.Out()
+	p.P(`} else if this == nil {`)
+	p.In()
+	p.P(`return -1`)
+	p.Out()
+	p.P(`}`)
+}
+
+func (p *plugin) generateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto) {
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+	fieldname := p.GetOneOfFieldName(message, field)
+	repeated := field.IsRepeated()
+	ctype := gogoproto.IsCustomType(field)
+	nullable := gogoproto.IsNullable(field)
+	// oneof := field.OneofIndex != nil
+	if !repeated {
+		if ctype {
+			if nullable {
+				p.P(`if that1.`, fieldname, ` == nil {`)
+				p.In()
+				p.P(`if this.`, fieldname, ` != nil {`)
+				p.In()
+				p.P(`return 1`)
+				p.Out()
+				p.P(`}`)
+				p.Out()
+				p.P(`} else if this.`, fieldname, ` == nil {`)
+				p.In()
+				p.P(`return -1`)
+				p.Out()
+				p.P(`} else if c := this.`, fieldname, `.Compare(*that1.`, fieldname, `); c != 0 {`)
+			} else {
+				p.P(`if c := this.`, fieldname, `.Compare(that1.`, fieldname, `); c != 0 {`)
+			}
+			p.In()
+			p.P(`return c`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			if field.IsMessage() || p.IsGroup(field) {
+				if nullable {
+					p.P(`if c := this.`, fieldname, `.Compare(that1.`, fieldname, `); c != 0 {`)
+				} else {
+					p.P(`if c := this.`, fieldname, `.Compare(&that1.`, fieldname, `); c != 0 {`)
+				}
+				p.In()
+				p.P(`return c`)
+				p.Out()
+				p.P(`}`)
+			} else if field.IsBytes() {
+				p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `, that1.`, fieldname, `); c != 0 {`)
+				p.In()
+				p.P(`return c`)
+				p.Out()
+				p.P(`}`)
+			} else if field.IsString() {
+				if nullable && !proto3 {
+					p.generateNullableField(fieldname)
+				} else {
+					p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
+					p.In()
+					p.P(`if this.`, fieldname, ` < that1.`, fieldname, `{`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+					p.P(`return 1`)
+					p.Out()
+					p.P(`}`)
+				}
+			} else if field.IsBool() {
+				if nullable && !proto3 {
+					p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
+					p.In()
+					p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
+					p.In()
+					p.P(`if !*this.`, fieldname, ` {`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+					p.P(`return 1`)
+					p.Out()
+					p.P(`}`)
+					p.Out()
+					p.P(`} else if this.`, fieldname, ` != nil {`)
+					p.In()
+					p.P(`return 1`)
+					p.Out()
+					p.P(`} else if that1.`, fieldname, ` != nil {`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+				} else {
+					p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
+					p.In()
+					p.P(`if !this.`, fieldname, ` {`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+					p.P(`return 1`)
+					p.Out()
+					p.P(`}`)
+				}
+			} else {
+				if nullable && !proto3 {
+					p.generateNullableField(fieldname)
+				} else {
+					p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
+					p.In()
+					p.P(`if this.`, fieldname, ` < that1.`, fieldname, `{`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+					p.P(`return 1`)
+					p.Out()
+					p.P(`}`)
+				}
+			}
+		}
+	} else {
+		p.P(`if len(this.`, fieldname, `) != len(that1.`, fieldname, `) {`)
+		p.In()
+		p.P(`if len(this.`, fieldname, `) < len(that1.`, fieldname, `) {`)
+		p.In()
+		p.P(`return -1`)
+		p.Out()
+		p.P(`}`)
+		p.P(`return 1`)
+		p.Out()
+		p.P(`}`)
+		p.P(`for i := range this.`, fieldname, ` {`)
+		p.In()
+		if ctype {
+			p.P(`if c := this.`, fieldname, `[i].Compare(that1.`, fieldname, `[i]); c != 0 {`)
+			p.In()
+			p.P(`return c`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			if p.IsMap(field) {
+				m := p.GoMapType(nil, field)
+				valuegoTyp, _ := p.GoType(nil, m.ValueField)
+				valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
+				nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
+
+				mapValue := m.ValueAliasField
+				if mapValue.IsMessage() || p.IsGroup(mapValue) {
+					if nullable && valuegoTyp == valuegoAliasTyp {
+						p.P(`if c := this.`, fieldname, `[i].Compare(that1.`, fieldname, `[i]); c != 0 {`)
+					} else {
+						// Compare() has a pointer receiver, but map value is a value type
+						a := `this.` + fieldname + `[i]`
+						b := `that1.` + fieldname + `[i]`
+						if valuegoTyp != valuegoAliasTyp {
+							// cast back to the type that has the generated methods on it
+							a = `(` + valuegoTyp + `)(` + a + `)`
+							b = `(` + valuegoTyp + `)(` + b + `)`
+						}
+						p.P(`a := `, a)
+						p.P(`b := `, b)
+						if nullable {
+							p.P(`if c := a.Compare(b); c != 0 {`)
+						} else {
+							p.P(`if c := (&a).Compare(&b); c != 0 {`)
+						}
+					}
+					p.In()
+					p.P(`return c`)
+					p.Out()
+					p.P(`}`)
+				} else if mapValue.IsBytes() {
+					p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `[i], that1.`, fieldname, `[i]); c != 0 {`)
+					p.In()
+					p.P(`return c`)
+					p.Out()
+					p.P(`}`)
+				} else if mapValue.IsString() {
+					p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+					p.In()
+					p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+					p.P(`return 1`)
+					p.Out()
+					p.P(`}`)
+				} else {
+					p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+					p.In()
+					p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
+					p.In()
+					p.P(`return -1`)
+					p.Out()
+					p.P(`}`)
+					p.P(`return 1`)
+					p.Out()
+					p.P(`}`)
+				}
+			} else if field.IsMessage() || p.IsGroup(field) {
+				if nullable {
+					p.P(`if c := this.`, fieldname, `[i].Compare(that1.`, fieldname, `[i]); c != 0 {`)
+					p.In()
+					p.P(`return c`)
+					p.Out()
+					p.P(`}`)
+				} else {
+					p.P(`if c := this.`, fieldname, `[i].Compare(&that1.`, fieldname, `[i]); c != 0 {`)
+					p.In()
+					p.P(`return c`)
+					p.Out()
+					p.P(`}`)
+				}
+			} else if field.IsBytes() {
+				p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `[i], that1.`, fieldname, `[i]); c != 0 {`)
+				p.In()
+				p.P(`return c`)
+				p.Out()
+				p.P(`}`)
+			} else if field.IsString() {
+				p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+				p.In()
+				p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
+				p.In()
+				p.P(`return -1`)
+				p.Out()
+				p.P(`}`)
+				p.P(`return 1`)
+				p.Out()
+				p.P(`}`)
+			} else if field.IsBool() {
+				p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+				p.In()
+				p.P(`if !this.`, fieldname, `[i] {`)
+				p.In()
+				p.P(`return -1`)
+				p.Out()
+				p.P(`}`)
+				p.P(`return 1`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+				p.In()
+				p.P(`if this.`, fieldname, `[i] < that1.`, fieldname, `[i] {`)
+				p.In()
+				p.P(`return -1`)
+				p.Out()
+				p.P(`}`)
+				p.P(`return 1`)
+				p.Out()
+				p.P(`}`)
+			}
+		}
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func (p *plugin) generateMessage(file *generator.FileDescriptor, message *generator.Descriptor) {
+	ccTypeName := generator.CamelCaseSlice(message.TypeName())
+	p.P(`func (this *`, ccTypeName, `) Compare(that interface{}) int {`)
+	p.In()
+	p.generateMsgNullAndTypeCheck(ccTypeName)
+	oneofs := make(map[string]struct{})
+
+	for _, field := range message.Field {
+		oneof := field.OneofIndex != nil
+		if oneof {
+			fieldname := p.GetFieldName(message, field)
+			if _, ok := oneofs[fieldname]; ok {
+				continue
+			} else {
+				oneofs[fieldname] = struct{}{}
+			}
+			p.P(`if that1.`, fieldname, ` == nil {`)
+			p.In()
+			p.P(`if this.`, fieldname, ` != nil {`)
+			p.In()
+			p.P(`return 1`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`} else if this.`, fieldname, ` == nil {`)
+			p.In()
+			p.P(`return -1`)
+			p.Out()
+			p.P(`} else if c := this.`, fieldname, `.Compare(that1.`, fieldname, `); c != 0 {`)
+			p.In()
+			p.P(`return c`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			p.generateField(file, message, field)
+		}
+	}
+	if message.DescriptorProto.HasExtension() {
+		fieldname := "XXX_extensions"
+		if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P(`extkeys := make([]int32, 0, len(this.`, fieldname, `)+len(that1.`, fieldname, `))`)
+			p.P(`for k, _ := range this.`, fieldname, ` {`)
+			p.In()
+			p.P(`extkeys = append(extkeys, k)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`for k, _ := range that1.`, fieldname, ` {`)
+			p.In()
+			p.P(`if _, ok := this.`, fieldname, `[k]; !ok {`)
+			p.In()
+			p.P(`extkeys = append(extkeys, k)`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+			p.P(p.sortkeysPkg.Use(), `.Int32s(extkeys)`)
+			p.P(`for _, k := range extkeys {`)
+			p.In()
+			p.P(`if v, ok := this.`, fieldname, `[k]; ok {`)
+			p.In()
+			p.P(`if v2, ok := that1.`, fieldname, `[k]; ok {`)
+			p.In()
+			p.P(`if c := v.Compare(&v2); c != 0 {`)
+			p.In()
+			p.P(`return c`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`} else  {`)
+			p.In()
+			p.P(`return 1`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`} else {`)
+			p.In()
+			p.P(`return -1`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `, that1.`, fieldname, `); c != 0 {`)
+			p.In()
+			p.P(`return c`)
+			p.Out()
+			p.P(`}`)
+		}
+	}
+	if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+		fieldname := "XXX_unrecognized"
+		p.P(`if c := `, p.bytesPkg.Use(), `.Compare(this.`, fieldname, `, that1.`, fieldname, `); c != 0 {`)
+		p.In()
+		p.P(`return c`)
+		p.Out()
+		p.P(`}`)
+	}
+	p.P(`return 0`)
+	p.Out()
+	p.P(`}`)
+
+	//Generate Compare methods for oneof fields
+	m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
+	for _, field := range m.Field {
+		oneof := field.OneofIndex != nil
+		if !oneof {
+			continue
+		}
+		ccTypeName := p.OneOfTypeName(message, field)
+		p.P(`func (this *`, ccTypeName, `) Compare(that interface{}) int {`)
+		p.In()
+
+		p.generateMsgNullAndTypeCheck(ccTypeName)
+		vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly(field)
+		p.generateField(file, message, field)
+
+		p.P(`return 0`)
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 105 - 0
vendor/github.com/gogo/protobuf/plugin/compare/comparetest.go

@@ -0,0 +1,105 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 compare
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/plugin/testgen"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type test struct {
+	*generator.Generator
+}
+
+func NewTest(g *generator.Generator) testgen.TestPlugin {
+	return &test{g}
+}
+
+func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
+	used := false
+	randPkg := imports.NewImport("math/rand")
+	timePkg := imports.NewImport("time")
+	testingPkg := imports.NewImport("testing")
+	protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
+	}
+	for _, message := range file.Messages() {
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		if !gogoproto.HasCompare(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+
+		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
+			used = true
+			p.P(`func Test`, ccTypeName, `Compare(t *`, testingPkg.Use(), `.T) {`)
+			p.In()
+			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
+			p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
+			p.P(`data, err := `, protoPkg.Use(), `.Marshal(p)`)
+			p.P(`if err != nil {`)
+			p.In()
+			p.P(`panic(err)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`msg := &`, ccTypeName, `{}`)
+			p.P(`if err := `, protoPkg.Use(), `.Unmarshal(data, msg); err != nil {`)
+			p.In()
+			p.P(`panic(err)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`if c := p.Compare(msg); c != 0 {`)
+			p.In()
+			p.P(`t.Fatalf("%#v !Compare %#v, since %d", msg, p, c)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`p2 := NewPopulated`, ccTypeName, `(popr, false)`)
+			p.P(`c := p.Compare(p2)`)
+			p.P(`c2 := p2.Compare(p)`)
+			p.P(`if c != (-1 * c2) {`)
+			p.In()
+			p.P(`t.Errorf("p.Compare(p2) = %d", c)`)
+			p.P(`t.Errorf("p2.Compare(p) = %d", c2)`)
+			p.P(`t.Errorf("p = %#v", p)`)
+			p.P(`t.Errorf("p2 = %#v", p2)`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+		}
+
+	}
+	return used
+}
+
+func init() {
+	testgen.RegisterTestPlugin(NewTest)
+}

+ 131 - 0
vendor/github.com/gogo/protobuf/plugin/defaultcheck/defaultcheck.go

@@ -0,0 +1,131 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The defaultcheck plugin is used to check whether nullable is not used incorrectly.
+For instance:
+An error is caused if a nullable field:
+  - has a default value,
+  - is an enum which does not start at zero,
+  - is used for an extension,
+  - is used for a native proto3 type,
+  - is used for a repeated native type.
+
+An error is also caused if a field with a default value is used in a message:
+  - which is a face.
+  - without getters.
+
+It is enabled by the following extensions:
+
+  - nullable
+
+For incorrect usage of nullable with tests see:
+
+  github.com/gogo/protobuf/test/nullableconflict
+
+*/
+package defaultcheck
+
+import (
+	"fmt"
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"os"
+)
+
+type plugin struct {
+	*generator.Generator
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "defaultcheck"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+	for _, msg := range file.Messages() {
+		getters := gogoproto.HasGoGetters(file.FileDescriptorProto, msg.DescriptorProto)
+		face := gogoproto.IsFace(file.FileDescriptorProto, msg.DescriptorProto)
+		for _, field := range msg.GetField() {
+			if len(field.GetDefaultValue()) > 0 {
+				if !getters {
+					fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot have a default value and not have a getter method", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+					os.Exit(1)
+				}
+				if face {
+					fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot have a default value be in a face", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+					os.Exit(1)
+				}
+			}
+			if gogoproto.IsNullable(field) {
+				continue
+			}
+			if len(field.GetDefaultValue()) > 0 {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be non-nullable and have a default value", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				os.Exit(1)
+			}
+			if !field.IsMessage() && !gogoproto.IsCustomType(field) {
+				if field.IsRepeated() {
+					fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a repeated non-nullable native type, nullable=false has no effect\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				} else if proto3 {
+					fmt.Fprintf(os.Stderr, "ERROR: field %v.%v is a native type and in proto3 syntax with nullable=false there exists conflicting implementations when encoding zero values", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+					os.Exit(1)
+				}
+				if field.IsBytes() {
+					fmt.Fprintf(os.Stderr, "WARNING: field %v.%v is a non-nullable bytes type, nullable=false has no effect\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				}
+			}
+			if !field.IsEnum() {
+				continue
+			}
+			enum := p.ObjectNamed(field.GetTypeName()).(*generator.EnumDescriptor)
+			if len(enum.Value) == 0 || enum.Value[0].GetNumber() != 0 {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be non-nullable and be an enum type %v which does not start with zero", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name), enum.GetName())
+				os.Exit(1)
+			}
+		}
+	}
+	for _, e := range file.GetExtension() {
+		if !gogoproto.IsNullable(e) {
+			fmt.Fprintf(os.Stderr, "ERROR: extended field %v cannot be nullable %v", generator.CamelCase(e.GetName()), generator.CamelCase(*e.Name))
+			os.Exit(1)
+		}
+	}
+}
+
+func (p *plugin) GenerateImports(*generator.FileDescriptor) {}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 199 - 0
vendor/github.com/gogo/protobuf/plugin/description/description.go

@@ -0,0 +1,199 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The description (experimental) plugin generates a Description method for each message.
+The Description method returns a populated google_protobuf.FileDescriptorSet struct.
+This contains the description of the files used to generate this message.
+
+It is enabled by the following extensions:
+
+  - description
+  - description_all
+
+The description plugin also generates a test given it is enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  message B {
+	option (gogoproto.description) = true;
+	optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
+	repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
+  }
+
+given to the description plugin, will generate the following code:
+
+  func (this *B) Description() (desc *google_protobuf.FileDescriptorSet) {
+	return ExampleDescription()
+  }
+
+and the following test code:
+
+  func TestDescription(t *testing9.T) {
+	ExampleDescription()
+  }
+
+The hope is to use this struct in some way instead of reflect.
+This package is subject to change, since a use has not been figured out yet.
+
+*/
+package description
+
+import (
+	"bytes"
+	"compress/gzip"
+	"fmt"
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/proto"
+	descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type plugin struct {
+	*generator.Generator
+	generator.PluginImports
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "description"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	used := false
+	localName := generator.FileName(file)
+
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	descriptorPkg := p.NewImport("github.com/gogo/protobuf/protoc-gen-gogo/descriptor")
+	protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
+	gzipPkg := p.NewImport("compress/gzip")
+	bytesPkg := p.NewImport("bytes")
+	ioutilPkg := p.NewImport("io/ioutil")
+
+	for _, message := range file.Messages() {
+		if !gogoproto.HasDescription(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		used = true
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		p.P(`func (this *`, ccTypeName, `) Description() (desc *`, descriptorPkg.Use(), `.FileDescriptorSet) {`)
+		p.In()
+		p.P(`return `, localName, `Description()`)
+		p.Out()
+		p.P(`}`)
+	}
+
+	if used {
+
+		p.P(`func `, localName, `Description() (desc *`, descriptorPkg.Use(), `.FileDescriptorSet) {`)
+		p.In()
+		//Don't generate SourceCodeInfo, since it will create too much code.
+
+		ss := make([]*descriptor.SourceCodeInfo, 0)
+		for _, f := range p.Generator.AllFiles().GetFile() {
+			ss = append(ss, f.SourceCodeInfo)
+			f.SourceCodeInfo = nil
+		}
+		b, err := proto.Marshal(p.Generator.AllFiles())
+		if err != nil {
+			panic(err)
+		}
+		for i, f := range p.Generator.AllFiles().GetFile() {
+			f.SourceCodeInfo = ss[i]
+		}
+		p.P(`d := &`, descriptorPkg.Use(), `.FileDescriptorSet{}`)
+		var buf bytes.Buffer
+		w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
+		w.Write(b)
+		w.Close()
+		b = buf.Bytes()
+		p.P("var gzipped = []byte{")
+		p.In()
+		p.P("// ", len(b), " bytes of a gzipped FileDescriptorSet")
+		for len(b) > 0 {
+			n := 16
+			if n > len(b) {
+				n = len(b)
+			}
+
+			s := ""
+			for _, c := range b[:n] {
+				s += fmt.Sprintf("0x%02x,", c)
+			}
+			p.P(s)
+
+			b = b[n:]
+		}
+		p.Out()
+		p.P("}")
+		p.P(`r := `, bytesPkg.Use(), `.NewReader(gzipped)`)
+		p.P(`gzipr, err := `, gzipPkg.Use(), `.NewReader(r)`)
+		p.P(`if err != nil {`)
+		p.In()
+		p.P(`panic(err)`)
+		p.Out()
+		p.P(`}`)
+		p.P(`ungzipped, err := `, ioutilPkg.Use(), `.ReadAll(gzipr)`)
+		p.P(`if err != nil {`)
+		p.In()
+		p.P(`panic(err)`)
+		p.Out()
+		p.P(`}`)
+		p.P(`if err := `, protoPkg.Use(), `.Unmarshal(ungzipped, d); err != nil {`)
+		p.In()
+		p.P(`panic(err)`)
+		p.Out()
+		p.P(`}`)
+		p.P(`return d`)
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 71 - 0
vendor/github.com/gogo/protobuf/plugin/description/descriptiontest.go

@@ -0,0 +1,71 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 description
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/plugin/testgen"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type test struct {
+	*generator.Generator
+}
+
+func NewTest(g *generator.Generator) testgen.TestPlugin {
+	return &test{g}
+}
+
+func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
+	used := false
+	testingPkg := imports.NewImport("testing")
+	for _, message := range file.Messages() {
+		if !gogoproto.HasDescription(file.FileDescriptorProto, message.DescriptorProto) ||
+			!gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		used = true
+	}
+
+	if used {
+		localName := generator.FileName(file)
+		p.P(`func Test`, localName, `Description(t *`, testingPkg.Use(), `.T) {`)
+		p.In()
+		p.P(localName, `Description()`)
+		p.Out()
+		p.P(`}`)
+
+	}
+	return used
+}
+
+func init() {
+	testgen.RegisterTestPlugin(NewTest)
+}

+ 197 - 0
vendor/github.com/gogo/protobuf/plugin/embedcheck/embedcheck.go

@@ -0,0 +1,197 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The embedcheck plugin is used to check whether embed is not used incorrectly.
+For instance:
+An embedded message has a generated string method, but the is a member of a message which does not.
+This causes a warning.
+An error is caused by a namespace conflict.
+
+It is enabled by the following extensions:
+
+  - embed
+  - embed_all
+
+For incorrect usage of embed with tests see:
+
+  github.com/gogo/protobuf/test/embedconflict
+
+*/
+package embedcheck
+
+import (
+	"fmt"
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"os"
+)
+
+type plugin struct {
+	*generator.Generator
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "embedcheck"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+var overwriters []map[string]gogoproto.EnableFunc = []map[string]gogoproto.EnableFunc{
+	{
+		"stringer": gogoproto.IsStringer,
+	},
+	{
+		"gostring": gogoproto.HasGoString,
+	},
+	{
+		"equal": gogoproto.HasEqual,
+	},
+	{
+		"verboseequal": gogoproto.HasVerboseEqual,
+	},
+	{
+		"size":       gogoproto.IsSizer,
+		"protosizer": gogoproto.IsProtoSizer,
+	},
+	{
+		"unmarshaler":        gogoproto.IsUnmarshaler,
+		"unsafe_unmarshaler": gogoproto.IsUnsafeUnmarshaler,
+	},
+	{
+		"marshaler":        gogoproto.IsMarshaler,
+		"unsafe_marshaler": gogoproto.IsUnsafeMarshaler,
+	},
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	for _, msg := range file.Messages() {
+		for _, os := range overwriters {
+			possible := true
+			for _, overwriter := range os {
+				if overwriter(file.FileDescriptorProto, msg.DescriptorProto) {
+					possible = false
+				}
+			}
+			if possible {
+				p.checkOverwrite(msg, os)
+			}
+		}
+		p.checkNameSpace(msg)
+		for _, field := range msg.GetField() {
+			if gogoproto.IsEmbed(field) && gogoproto.IsCustomName(field) {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v with custom name %v cannot be embedded", *field.Name, gogoproto.GetCustomName(field))
+				os.Exit(1)
+			}
+		}
+		p.checkRepeated(msg)
+	}
+	for _, e := range file.GetExtension() {
+		if gogoproto.IsEmbed(e) {
+			fmt.Fprintf(os.Stderr, "ERROR: extended field %v cannot be embedded", generator.CamelCase(*e.Name))
+			os.Exit(1)
+		}
+	}
+}
+
+func (p *plugin) checkNameSpace(message *generator.Descriptor) map[string]bool {
+	ccTypeName := generator.CamelCaseSlice(message.TypeName())
+	names := make(map[string]bool)
+	for _, field := range message.Field {
+		fieldname := generator.CamelCase(*field.Name)
+		if field.IsMessage() && gogoproto.IsEmbed(field) {
+			desc := p.ObjectNamed(field.GetTypeName())
+			moreNames := p.checkNameSpace(desc.(*generator.Descriptor))
+			for another := range moreNames {
+				if names[another] {
+					fmt.Fprintf(os.Stderr, "ERROR: duplicate embedded fieldname %v in type %v\n", fieldname, ccTypeName)
+					os.Exit(1)
+				}
+				names[another] = true
+			}
+		} else {
+			if names[fieldname] {
+				fmt.Fprintf(os.Stderr, "ERROR: duplicate embedded fieldname %v in type %v\n", fieldname, ccTypeName)
+				os.Exit(1)
+			}
+			names[fieldname] = true
+		}
+	}
+	return names
+}
+
+func (p *plugin) checkOverwrite(message *generator.Descriptor, enablers map[string]gogoproto.EnableFunc) {
+	ccTypeName := generator.CamelCaseSlice(message.TypeName())
+	names := []string{}
+	for name := range enablers {
+		names = append(names, name)
+	}
+	for _, field := range message.Field {
+		if field.IsMessage() && gogoproto.IsEmbed(field) {
+			fieldname := generator.CamelCase(*field.Name)
+			desc := p.ObjectNamed(field.GetTypeName())
+			msg := desc.(*generator.Descriptor)
+			for errStr, enabled := range enablers {
+				if enabled(msg.File(), msg.DescriptorProto) {
+					fmt.Fprintf(os.Stderr, "WARNING: found non-%v %v with embedded %v %v\n", names, ccTypeName, errStr, fieldname)
+				}
+			}
+			p.checkOverwrite(msg, enablers)
+		}
+	}
+}
+
+func (p *plugin) checkRepeated(message *generator.Descriptor) {
+	ccTypeName := generator.CamelCaseSlice(message.TypeName())
+	for _, field := range message.Field {
+		if !gogoproto.IsEmbed(field) {
+			continue
+		}
+		if field.IsBytes() {
+			fieldname := generator.CamelCase(*field.Name)
+			fmt.Fprintf(os.Stderr, "ERROR: found embedded bytes field %s in message %s\n", fieldname, ccTypeName)
+			os.Exit(1)
+		}
+		if !field.IsRepeated() {
+			continue
+		}
+		fieldname := generator.CamelCase(*field.Name)
+		fmt.Fprintf(os.Stderr, "ERROR: found repeated embedded field %s in message %s\n", fieldname, ccTypeName)
+		os.Exit(1)
+	}
+}
+
+func (p *plugin) GenerateImports(*generator.FileDescriptor) {}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 102 - 0
vendor/github.com/gogo/protobuf/plugin/enumstringer/enumstringer.go

@@ -0,0 +1,102 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The enumstringer (experimental) plugin generates a String method for each enum.
+
+It is enabled by the following extensions:
+
+  - enum_stringer
+  - enum_stringer_all
+
+This package is subject to change.
+
+*/
+package enumstringer
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type enumstringer struct {
+	*generator.Generator
+	generator.PluginImports
+	atleastOne bool
+	localName  string
+}
+
+func NewEnumStringer() *enumstringer {
+	return &enumstringer{}
+}
+
+func (p *enumstringer) Name() string {
+	return "enumstringer"
+}
+
+func (p *enumstringer) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *enumstringer) Generate(file *generator.FileDescriptor) {
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.atleastOne = false
+
+	p.localName = generator.FileName(file)
+
+	strconvPkg := p.NewImport("strconv")
+
+	for _, enum := range file.Enums() {
+		if !gogoproto.IsEnumStringer(file.FileDescriptorProto, enum.EnumDescriptorProto) {
+			continue
+		}
+		if gogoproto.IsGoEnumStringer(file.FileDescriptorProto, enum.EnumDescriptorProto) {
+			panic("old enum string method needs to be disabled, please use gogoproto.old_enum_stringer or gogoproto.old_enum_string_all and set it to false")
+		}
+		p.atleastOne = true
+		ccTypeName := generator.CamelCaseSlice(enum.TypeName())
+		p.P("func (x ", ccTypeName, ") String() string {")
+		p.In()
+		p.P(`s, ok := `, ccTypeName, `_name[int32(x)]`)
+		p.P(`if ok {`)
+		p.In()
+		p.P(`return s`)
+		p.Out()
+		p.P(`}`)
+		p.P(`return `, strconvPkg.Use(), `.Itoa(int(x))`)
+		p.Out()
+		p.P(`}`)
+	}
+
+	if !p.atleastOne {
+		return
+	}
+
+}
+
+func init() {
+	generator.RegisterPlugin(NewEnumStringer())
+}

+ 602 - 0
vendor/github.com/gogo/protobuf/plugin/equal/equal.go

@@ -0,0 +1,602 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The equal plugin generates an Equal and a VerboseEqual method for each message.
+These equal methods are quite obvious.
+The only difference is that VerboseEqual returns a non nil error if it is not equal.
+This error contains more detail on exactly which part of the message was not equal to the other message.
+The idea is that this is useful for debugging.
+
+Equal is enabled using the following extensions:
+
+  - equal
+  - equal_all
+
+While VerboseEqual is enable dusing the following extensions:
+
+  - verbose_equal
+  - verbose_equal_all
+
+The equal plugin also generates a test given it is enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  option (gogoproto.equal_all) = true;
+  option (gogoproto.verbose_equal_all) = true;
+
+  message B {
+	optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
+	repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
+  }
+
+given to the equal plugin, will generate the following code:
+
+	func (this *B) VerboseEqual(that interface{}) error {
+		if that == nil {
+			if this == nil {
+				return nil
+			}
+			return fmt2.Errorf("that == nil && this != nil")
+		}
+
+		that1, ok := that.(*B)
+		if !ok {
+			return fmt2.Errorf("that is not of type *B")
+		}
+		if that1 == nil {
+			if this == nil {
+				return nil
+			}
+			return fmt2.Errorf("that is type *B but is nil && this != nil")
+		} else if this == nil {
+			return fmt2.Errorf("that is type *B but is not nil && this == nil")
+		}
+		if !this.A.Equal(&that1.A) {
+			return fmt2.Errorf("A this(%v) Not Equal that(%v)", this.A, that1.A)
+		}
+		if len(this.G) != len(that1.G) {
+			return fmt2.Errorf("G this(%v) Not Equal that(%v)", len(this.G), len(that1.G))
+		}
+		for i := range this.G {
+			if !this.G[i].Equal(that1.G[i]) {
+				return fmt2.Errorf("G this[%v](%v) Not Equal that[%v](%v)", i, this.G[i], i, that1.G[i])
+			}
+		}
+		if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
+			return fmt2.Errorf("XXX_unrecognized this(%v) Not Equal that(%v)", this.XXX_unrecognized, that1.XXX_unrecognized)
+		}
+		return nil
+	}
+
+	func (this *B) Equal(that interface{}) bool {
+		if that == nil {
+			if this == nil {
+				return true
+			}
+			return false
+		}
+
+		that1, ok := that.(*B)
+		if !ok {
+			return false
+		}
+		if that1 == nil {
+			if this == nil {
+				return true
+			}
+			return false
+		} else if this == nil {
+			return false
+		}
+		if !this.A.Equal(&that1.A) {
+			return false
+		}
+		if len(this.G) != len(that1.G) {
+			return false
+		}
+		for i := range this.G {
+			if !this.G[i].Equal(that1.G[i]) {
+				return false
+			}
+		}
+		if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
+			return false
+		}
+		return true
+	}
+
+and the following test code:
+
+	func TestBVerboseEqual(t *testing8.T) {
+		popr := math_rand8.New(math_rand8.NewSource(time8.Now().UnixNano()))
+		p := NewPopulatedB(popr, false)
+		data, err := github_com_gogo_protobuf_proto2.Marshal(p)
+		if err != nil {
+			panic(err)
+		}
+		msg := &B{}
+		if err := github_com_gogo_protobuf_proto2.Unmarshal(data, msg); err != nil {
+			panic(err)
+		}
+		if err := p.VerboseEqual(msg); err != nil {
+			t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err)
+	}
+
+*/
+package equal
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/proto"
+	descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"github.com/gogo/protobuf/vanity"
+)
+
+type plugin struct {
+	*generator.Generator
+	generator.PluginImports
+	fmtPkg   generator.Single
+	bytesPkg generator.Single
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "equal"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.fmtPkg = p.NewImport("fmt")
+	p.bytesPkg = p.NewImport("bytes")
+
+	for _, msg := range file.Messages() {
+		if msg.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		if gogoproto.HasVerboseEqual(file.FileDescriptorProto, msg.DescriptorProto) {
+			p.generateMessage(file, msg, true)
+		}
+		if gogoproto.HasEqual(file.FileDescriptorProto, msg.DescriptorProto) {
+			p.generateMessage(file, msg, false)
+		}
+	}
+}
+
+func (p *plugin) generateNullableField(fieldname string, verbose bool) {
+	p.P(`if this.`, fieldname, ` != nil && that1.`, fieldname, ` != nil {`)
+	p.In()
+	p.P(`if *this.`, fieldname, ` != *that1.`, fieldname, `{`)
+	p.In()
+	if verbose {
+		p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", *this.`, fieldname, `, *that1.`, fieldname, `)`)
+	} else {
+		p.P(`return false`)
+	}
+	p.Out()
+	p.P(`}`)
+	p.Out()
+	p.P(`} else if this.`, fieldname, ` != nil {`)
+	p.In()
+	if verbose {
+		p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` == nil && that.`, fieldname, ` != nil")`)
+	} else {
+		p.P(`return false`)
+	}
+	p.Out()
+	p.P(`} else if that1.`, fieldname, ` != nil {`)
+}
+
+func (p *plugin) generateMsgNullAndTypeCheck(ccTypeName string, verbose bool) {
+	p.P(`if that == nil {`)
+	p.In()
+	p.P(`if this == nil {`)
+	p.In()
+	if verbose {
+		p.P(`return nil`)
+	} else {
+		p.P(`return true`)
+	}
+	p.Out()
+	p.P(`}`)
+	if verbose {
+		p.P(`return `, p.fmtPkg.Use(), `.Errorf("that == nil && this != nil")`)
+	} else {
+		p.P(`return false`)
+	}
+	p.Out()
+	p.P(`}`)
+	p.P(``)
+	p.P(`that1, ok := that.(*`, ccTypeName, `)`)
+	p.P(`if !ok {`)
+	p.In()
+	p.P(`that2, ok := that.(`, ccTypeName, `)`)
+	p.P(`if ok {`)
+	p.In()
+	p.P(`that1 = &that2`)
+	p.Out()
+	p.P(`} else {`)
+	p.In()
+	if verbose {
+		p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is not of type *`, ccTypeName, `")`)
+	} else {
+		p.P(`return false`)
+	}
+	p.Out()
+	p.P(`}`)
+	p.Out()
+	p.P(`}`)
+	p.P(`if that1 == nil {`)
+	p.In()
+	p.P(`if this == nil {`)
+	p.In()
+	if verbose {
+		p.P(`return nil`)
+	} else {
+		p.P(`return true`)
+	}
+	p.Out()
+	p.P(`}`)
+	if verbose {
+		p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is type *`, ccTypeName, ` but is nil && this != nil")`)
+	} else {
+		p.P(`return false`)
+	}
+	p.Out()
+	p.P(`} else if this == nil {`)
+	p.In()
+	if verbose {
+		p.P(`return `, p.fmtPkg.Use(), `.Errorf("that is type *`, ccTypeName, ` but is not nil && this == nil")`)
+	} else {
+		p.P(`return false`)
+	}
+	p.Out()
+	p.P(`}`)
+}
+
+func (p *plugin) generateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto, verbose bool) {
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+	fieldname := p.GetOneOfFieldName(message, field)
+	repeated := field.IsRepeated()
+	ctype := gogoproto.IsCustomType(field)
+	nullable := gogoproto.IsNullable(field)
+	// oneof := field.OneofIndex != nil
+	if !repeated {
+		if ctype {
+			if nullable {
+				p.P(`if that1.`, fieldname, ` == nil {`)
+				p.In()
+				p.P(`if this.`, fieldname, ` != nil {`)
+				p.In()
+				if verbose {
+					p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
+				} else {
+					p.P(`return false`)
+				}
+				p.Out()
+				p.P(`}`)
+				p.Out()
+				p.P(`} else if !this.`, fieldname, `.Equal(*that1.`, fieldname, `) {`)
+			} else {
+				p.P(`if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
+			}
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+		} else {
+			if field.IsMessage() || p.IsGroup(field) {
+				if nullable {
+					p.P(`if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
+				} else {
+					p.P(`if !this.`, fieldname, `.Equal(&that1.`, fieldname, `) {`)
+				}
+			} else if field.IsBytes() {
+				p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
+			} else if field.IsString() {
+				if nullable && !proto3 {
+					p.generateNullableField(fieldname, verbose)
+				} else {
+					p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
+				}
+			} else {
+				if nullable && !proto3 {
+					p.generateNullableField(fieldname, verbose)
+				} else {
+					p.P(`if this.`, fieldname, ` != that1.`, fieldname, `{`)
+				}
+			}
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+		}
+	} else {
+		p.P(`if len(this.`, fieldname, `) != len(that1.`, fieldname, `) {`)
+		p.In()
+		if verbose {
+			p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", len(this.`, fieldname, `), len(that1.`, fieldname, `))`)
+		} else {
+			p.P(`return false`)
+		}
+		p.Out()
+		p.P(`}`)
+		p.P(`for i := range this.`, fieldname, ` {`)
+		p.In()
+		if ctype {
+			p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
+		} else {
+			if p.IsMap(field) {
+				m := p.GoMapType(nil, field)
+				valuegoTyp, _ := p.GoType(nil, m.ValueField)
+				valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
+				nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
+
+				mapValue := m.ValueAliasField
+				if mapValue.IsMessage() || p.IsGroup(mapValue) {
+					if nullable && valuegoTyp == valuegoAliasTyp {
+						p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
+					} else {
+						// Equal() has a pointer receiver, but map value is a value type
+						a := `this.` + fieldname + `[i]`
+						b := `that1.` + fieldname + `[i]`
+						if valuegoTyp != valuegoAliasTyp {
+							// cast back to the type that has the generated methods on it
+							a = `(` + valuegoTyp + `)(` + a + `)`
+							b = `(` + valuegoTyp + `)(` + b + `)`
+						}
+						p.P(`a := `, a)
+						p.P(`b := `, b)
+						if nullable {
+							p.P(`if !a.Equal(b) {`)
+						} else {
+							p.P(`if !(&a).Equal(&b) {`)
+						}
+					}
+				} else if mapValue.IsBytes() {
+					p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
+				} else if mapValue.IsString() {
+					p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+				} else {
+					p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+				}
+			} else if field.IsMessage() || p.IsGroup(field) {
+				if nullable {
+					p.P(`if !this.`, fieldname, `[i].Equal(that1.`, fieldname, `[i]) {`)
+				} else {
+					p.P(`if !this.`, fieldname, `[i].Equal(&that1.`, fieldname, `[i]) {`)
+				}
+			} else if field.IsBytes() {
+				p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `[i], that1.`, fieldname, `[i]) {`)
+			} else if field.IsString() {
+				p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+			} else {
+				p.P(`if this.`, fieldname, `[i] != that1.`, fieldname, `[i] {`)
+			}
+		}
+		p.In()
+		if verbose {
+			p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this[%v](%v) Not Equal that[%v](%v)", i, this.`, fieldname, `[i], i, that1.`, fieldname, `[i])`)
+		} else {
+			p.P(`return false`)
+		}
+		p.Out()
+		p.P(`}`)
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func (p *plugin) generateMessage(file *generator.FileDescriptor, message *generator.Descriptor, verbose bool) {
+	ccTypeName := generator.CamelCaseSlice(message.TypeName())
+	if verbose {
+		p.P(`func (this *`, ccTypeName, `) VerboseEqual(that interface{}) error {`)
+	} else {
+		p.P(`func (this *`, ccTypeName, `) Equal(that interface{}) bool {`)
+	}
+	p.In()
+	p.generateMsgNullAndTypeCheck(ccTypeName, verbose)
+	oneofs := make(map[string]struct{})
+
+	for _, field := range message.Field {
+		oneof := field.OneofIndex != nil
+		if oneof {
+			fieldname := p.GetFieldName(message, field)
+			if _, ok := oneofs[fieldname]; ok {
+				continue
+			} else {
+				oneofs[fieldname] = struct{}{}
+			}
+			p.P(`if that1.`, fieldname, ` == nil {`)
+			p.In()
+			p.P(`if this.`, fieldname, ` != nil {`)
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` != nil && that1.`, fieldname, ` == nil")`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`} else if this.`, fieldname, ` == nil {`)
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("this.`, fieldname, ` == nil && that1.`, fieldname, ` != nil")`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			if verbose {
+				p.P(`} else if err := this.`, fieldname, `.VerboseEqual(that1.`, fieldname, `); err != nil {`)
+			} else {
+				p.P(`} else if !this.`, fieldname, `.Equal(that1.`, fieldname, `) {`)
+			}
+			p.In()
+			if verbose {
+				p.P(`return err`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+		} else {
+			p.generateField(file, message, field, verbose)
+		}
+	}
+	if message.DescriptorProto.HasExtension() {
+		fieldname := "XXX_extensions"
+		if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P(`for k, v := range this.`, fieldname, ` {`)
+			p.In()
+			p.P(`if v2, ok := that1.`, fieldname, `[k]; ok {`)
+			p.In()
+			p.P(`if !v.Equal(&v2) {`)
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this[%v](%v) Not Equal that[%v](%v)", k, this.`, fieldname, `[k], k, that1.`, fieldname, `[k])`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`} else  {`)
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, `[%v] Not In that", k)`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+
+			p.P(`for k, _ := range that1.`, fieldname, ` {`)
+			p.In()
+			p.P(`if _, ok := this.`, fieldname, `[k]; !ok {`)
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, `[%v] Not In this", k)`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
+			p.In()
+			if verbose {
+				p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
+			} else {
+				p.P(`return false`)
+			}
+			p.Out()
+			p.P(`}`)
+		}
+	}
+	if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+		fieldname := "XXX_unrecognized"
+		p.P(`if !`, p.bytesPkg.Use(), `.Equal(this.`, fieldname, `, that1.`, fieldname, `) {`)
+		p.In()
+		if verbose {
+			p.P(`return `, p.fmtPkg.Use(), `.Errorf("`, fieldname, ` this(%v) Not Equal that(%v)", this.`, fieldname, `, that1.`, fieldname, `)`)
+		} else {
+			p.P(`return false`)
+		}
+		p.Out()
+		p.P(`}`)
+	}
+	if verbose {
+		p.P(`return nil`)
+	} else {
+		p.P(`return true`)
+	}
+	p.Out()
+	p.P(`}`)
+
+	//Generate Equal methods for oneof fields
+	m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
+	for _, field := range m.Field {
+		oneof := field.OneofIndex != nil
+		if !oneof {
+			continue
+		}
+		ccTypeName := p.OneOfTypeName(message, field)
+		if verbose {
+			p.P(`func (this *`, ccTypeName, `) VerboseEqual(that interface{}) error {`)
+		} else {
+			p.P(`func (this *`, ccTypeName, `) Equal(that interface{}) bool {`)
+		}
+		p.In()
+
+		p.generateMsgNullAndTypeCheck(ccTypeName, verbose)
+		vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly(field)
+		p.generateField(file, message, field, verbose)
+
+		if verbose {
+			p.P(`return nil`)
+		} else {
+			p.P(`return true`)
+		}
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 94 - 0
vendor/github.com/gogo/protobuf/plugin/equal/equaltest.go

@@ -0,0 +1,94 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 equal
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/plugin/testgen"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type test struct {
+	*generator.Generator
+}
+
+func NewTest(g *generator.Generator) testgen.TestPlugin {
+	return &test{g}
+}
+
+func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
+	used := false
+	randPkg := imports.NewImport("math/rand")
+	timePkg := imports.NewImport("time")
+	testingPkg := imports.NewImport("testing")
+	protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
+	}
+	for _, message := range file.Messages() {
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		if !gogoproto.HasVerboseEqual(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+
+		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
+			used = true
+			p.P(`func Test`, ccTypeName, `VerboseEqual(t *`, testingPkg.Use(), `.T) {`)
+			p.In()
+			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
+			p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
+			p.P(`data, err := `, protoPkg.Use(), `.Marshal(p)`)
+			p.P(`if err != nil {`)
+			p.In()
+			p.P(`panic(err)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`msg := &`, ccTypeName, `{}`)
+			p.P(`if err := `, protoPkg.Use(), `.Unmarshal(data, msg); err != nil {`)
+			p.In()
+			p.P(`panic(err)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`if err := p.VerboseEqual(msg); err != nil {`)
+			p.In()
+			p.P(`t.Fatalf("%#v !VerboseEqual %#v, since %v", msg, p, err)`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+		}
+
+	}
+	return used
+}
+
+func init() {
+	testgen.RegisterTestPlugin(NewTest)
+}

+ 231 - 0
vendor/github.com/gogo/protobuf/plugin/face/face.go

@@ -0,0 +1,231 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The face plugin generates a function will be generated which can convert a structure which satisfies an interface (face) to the specified structure.
+This interface contains getters for each of the fields in the struct.
+The specified struct is also generated with the getters.
+This means that getters should be turned off so as not to conflict with face getters.
+This allows it to satisfy its own face.
+
+It is enabled by the following extensions:
+
+  - face
+  - face_all
+
+Turn off getters by using the following extensions:
+
+  - getters
+  - getters_all
+
+The face plugin also generates a test given it is enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  message A {
+	option (gogoproto.face) = true;
+	option (gogoproto.goproto_getters) = false;
+	optional string Description = 1 [(gogoproto.nullable) = false];
+	optional int64 Number = 2 [(gogoproto.nullable) = false];
+	optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
+  }
+
+given to the face plugin, will generate the following code:
+
+	type AFace interface {
+		Proto() github_com_gogo_protobuf_proto.Message
+		GetDescription() string
+		GetNumber() int64
+		GetId() github_com_gogo_protobuf_test_custom.Uuid
+	}
+
+	func (this *A) Proto() github_com_gogo_protobuf_proto.Message {
+		return this
+	}
+
+	func (this *A) TestProto() github_com_gogo_protobuf_proto.Message {
+		return NewAFromFace(this)
+	}
+
+	func (this *A) GetDescription() string {
+		return this.Description
+	}
+
+	func (this *A) GetNumber() int64 {
+		return this.Number
+	}
+
+	func (this *A) GetId() github_com_gogo_protobuf_test_custom.Uuid {
+		return this.Id
+	}
+
+	func NewAFromFace(that AFace) *A {
+		this := &A{}
+		this.Description = that.GetDescription()
+		this.Number = that.GetNumber()
+		this.Id = that.GetId()
+		return this
+	}
+
+and the following test code:
+
+	func TestAFace(t *testing7.T) {
+		popr := math_rand7.New(math_rand7.NewSource(time7.Now().UnixNano()))
+		p := NewPopulatedA(popr, true)
+		msg := p.TestProto()
+		if !p.Equal(msg) {
+			t.Fatalf("%#v !Face Equal %#v", msg, p)
+		}
+	}
+
+The struct A, representing the message, will also be generated just like always.
+As you can see A satisfies its own Face, AFace.
+
+Creating another struct which satisfies AFace is very easy.
+Simply create all these methods specified in AFace.
+Implementing The Proto method is done with the helper function NewAFromFace:
+
+	func (this *MyStruct) Proto() proto.Message {
+	  return NewAFromFace(this)
+	}
+
+just the like TestProto method which is used to test the NewAFromFace function.
+
+*/
+package face
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type plugin struct {
+	*generator.Generator
+	generator.PluginImports
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "face"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = p.NewImport("github.com/golang/protobuf/proto")
+	}
+	for _, message := range file.Messages() {
+		if !gogoproto.IsFace(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		if message.DescriptorProto.HasExtension() {
+			panic("face does not support message with extensions")
+		}
+		if gogoproto.HasGoGetters(file.FileDescriptorProto, message.DescriptorProto) {
+			panic("face requires getters to be disabled please use gogoproto.getters or gogoproto.getters_all and set it to false")
+		}
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		p.P(`type `, ccTypeName, `Face interface{`)
+		p.In()
+		p.P(`Proto() `, protoPkg.Use(), `.Message`)
+		for _, field := range message.Field {
+			fieldname := p.GetFieldName(message, field)
+			goTyp, _ := p.GoType(message, field)
+			if p.IsMap(field) {
+				m := p.GoMapType(nil, field)
+				goTyp = m.GoType
+			}
+			p.P(`Get`, fieldname, `() `, goTyp)
+		}
+		p.Out()
+		p.P(`}`)
+		p.P(``)
+		p.P(`func (this *`, ccTypeName, `) Proto() `, protoPkg.Use(), `.Message {`)
+		p.In()
+		p.P(`return this`)
+		p.Out()
+		p.P(`}`)
+		p.P(``)
+		p.P(`func (this *`, ccTypeName, `) TestProto() `, protoPkg.Use(), `.Message {`)
+		p.In()
+		p.P(`return New`, ccTypeName, `FromFace(this)`)
+		p.Out()
+		p.P(`}`)
+		p.P(``)
+		for _, field := range message.Field {
+			fieldname := p.GetFieldName(message, field)
+			goTyp, _ := p.GoType(message, field)
+			if p.IsMap(field) {
+				m := p.GoMapType(nil, field)
+				goTyp = m.GoType
+			}
+			p.P(`func (this *`, ccTypeName, `) Get`, fieldname, `() `, goTyp, `{`)
+			p.In()
+			p.P(` return this.`, fieldname)
+			p.Out()
+			p.P(`}`)
+			p.P(``)
+		}
+		p.P(``)
+		p.P(`func New`, ccTypeName, `FromFace(that `, ccTypeName, `Face) *`, ccTypeName, ` {`)
+		p.In()
+		p.P(`this := &`, ccTypeName, `{}`)
+		for _, field := range message.Field {
+			fieldname := p.GetFieldName(message, field)
+			p.P(`this.`, fieldname, ` = that.Get`, fieldname, `()`)
+		}
+		p.P(`return this`)
+		p.Out()
+		p.P(`}`)
+		p.P(``)
+	}
+}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 80 - 0
vendor/github.com/gogo/protobuf/plugin/face/facetest.go

@@ -0,0 +1,80 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 face
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/plugin/testgen"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type test struct {
+	*generator.Generator
+}
+
+func NewTest(g *generator.Generator) testgen.TestPlugin {
+	return &test{g}
+}
+
+func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
+	used := false
+	randPkg := imports.NewImport("math/rand")
+	timePkg := imports.NewImport("time")
+	testingPkg := imports.NewImport("testing")
+	for _, message := range file.Messages() {
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		if !gogoproto.IsFace(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+
+		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
+			used = true
+
+			p.P(`func Test`, ccTypeName, `Face(t *`, testingPkg.Use(), `.T) {`)
+			p.In()
+			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
+			p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
+			p.P(`msg := p.TestProto()`)
+			p.P(`if !p.Equal(msg) {`)
+			p.In()
+			p.P(`t.Fatalf("%#v !Face Equal %#v", msg, p)`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+		}
+
+	}
+	return used
+}
+
+func init() {
+	testgen.RegisterTestPlugin(NewTest)
+}

+ 360 - 0
vendor/github.com/gogo/protobuf/plugin/gostring/gostring.go

@@ -0,0 +1,360 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The gostring plugin generates a GoString method for each message.
+The GoString method is called whenever you use a fmt.Printf as such:
+
+  fmt.Printf("%#v", mymessage)
+
+or whenever you actually call GoString()
+The output produced by the GoString method can be copied from the output into code and used to set a variable.
+It is totally valid Go Code and is populated exactly as the struct that was printed out.
+
+It is enabled by the following extensions:
+
+  - gostring
+  - gostring_all
+
+The gostring plugin also generates a test given it is enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  option (gogoproto.gostring_all) = true;
+
+  message A {
+	optional string Description = 1 [(gogoproto.nullable) = false];
+	optional int64 Number = 2 [(gogoproto.nullable) = false];
+	optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
+  }
+
+given to the gostring plugin, will generate the following code:
+
+  func (this *A) GoString() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings1.Join([]string{`&test.A{` + `Description:` + fmt1.Sprintf("%#v", this.Description), `Number:` + fmt1.Sprintf("%#v", this.Number), `Id:` + fmt1.Sprintf("%#v", this.Id), `XXX_unrecognized:` + fmt1.Sprintf("%#v", this.XXX_unrecognized) + `}`}, ", ")
+	return s
+  }
+
+and the following test code:
+
+	func TestAGoString(t *testing6.T) {
+		popr := math_rand6.New(math_rand6.NewSource(time6.Now().UnixNano()))
+		p := NewPopulatedA(popr, false)
+		s1 := p.GoString()
+		s2 := fmt2.Sprintf("%#v", p)
+		if s1 != s2 {
+			t.Fatalf("GoString want %v got %v", s1, s2)
+		}
+		_, err := go_parser.ParseExpr(s1)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+Typically fmt.Printf("%#v") will stop to print when it reaches a pointer and
+not print their values, while the generated GoString method will always print all values, recursively.
+
+*/
+package gostring
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"strconv"
+	"strings"
+)
+
+type gostring struct {
+	*generator.Generator
+	generator.PluginImports
+	atleastOne bool
+	localName  string
+}
+
+func NewGoString() *gostring {
+	return &gostring{}
+}
+
+func (p *gostring) Name() string {
+	return "gostring"
+}
+
+func (p *gostring) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *gostring) Generate(file *generator.FileDescriptor) {
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.atleastOne = false
+
+	p.localName = generator.FileName(file)
+
+	fmtPkg := p.NewImport("fmt")
+	stringsPkg := p.NewImport("strings")
+	protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = p.NewImport("github.com/golang/protobuf/proto")
+	}
+	sortPkg := p.NewImport("sort")
+	strconvPkg := p.NewImport("strconv")
+	reflectPkg := p.NewImport("reflect")
+	sortKeysPkg := p.NewImport("github.com/gogo/protobuf/sortkeys")
+
+	for _, message := range file.Messages() {
+		if !gogoproto.HasGoString(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		p.atleastOne = true
+		packageName := file.PackageName()
+
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		p.P(`func (this *`, ccTypeName, `) GoString() string {`)
+		p.In()
+		p.P(`if this == nil {`)
+		p.In()
+		p.P(`return "nil"`)
+		p.Out()
+		p.P(`}`)
+
+		p.P(`s := make([]string, 0, `, strconv.Itoa(len(message.Field)+4), `)`)
+		p.P(`s = append(s, "&`, packageName, ".", ccTypeName, `{")`)
+
+		oneofs := make(map[string]struct{})
+		for _, field := range message.Field {
+			nullable := gogoproto.IsNullable(field)
+			repeated := field.IsRepeated()
+			fieldname := p.GetFieldName(message, field)
+			oneof := field.OneofIndex != nil
+			if oneof {
+				if _, ok := oneofs[fieldname]; ok {
+					continue
+				} else {
+					oneofs[fieldname] = struct{}{}
+				}
+				p.P(`if this.`, fieldname, ` != nil {`)
+				p.In()
+				p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
+				p.Out()
+				p.P(`}`)
+			} else if p.IsMap(field) {
+				m := p.GoMapType(nil, field)
+				mapgoTyp, keyField, keyAliasField := m.GoType, m.KeyField, m.KeyAliasField
+				keysName := `keysFor` + fieldname
+				keygoTyp, _ := p.GoType(nil, keyField)
+				keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
+				keygoAliasTyp, _ := p.GoType(nil, keyAliasField)
+				keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
+				keyCapTyp := generator.CamelCase(keygoTyp)
+				p.P(keysName, ` := make([]`, keygoTyp, `, 0, len(this.`, fieldname, `))`)
+				p.P(`for k, _ := range this.`, fieldname, ` {`)
+				p.In()
+				if keygoAliasTyp == keygoTyp {
+					p.P(keysName, ` = append(`, keysName, `, k)`)
+				} else {
+					p.P(keysName, ` = append(`, keysName, `, `, keygoTyp, `(k))`)
+				}
+				p.Out()
+				p.P(`}`)
+				p.P(sortKeysPkg.Use(), `.`, keyCapTyp, `s(`, keysName, `)`)
+				mapName := `mapStringFor` + fieldname
+				p.P(mapName, ` := "`, mapgoTyp, `{"`)
+				p.P(`for _, k := range `, keysName, ` {`)
+				p.In()
+				if keygoAliasTyp == keygoTyp {
+					p.P(mapName, ` += fmt.Sprintf("%#v: %#v,", k, this.`, fieldname, `[k])`)
+				} else {
+					p.P(mapName, ` += fmt.Sprintf("%#v: %#v,", k, this.`, fieldname, `[`, keygoAliasTyp, `(k)])`)
+				}
+				p.Out()
+				p.P(`}`)
+				p.P(mapName, ` += "}"`)
+				p.P(`if this.`, fieldname, ` != nil {`)
+				p.In()
+				p.P(`s = append(s, "`, fieldname, `: " + `, mapName, `+ ",\n")`)
+				p.Out()
+				p.P(`}`)
+			} else if field.IsMessage() || p.IsGroup(field) {
+				if nullable || repeated {
+					p.P(`if this.`, fieldname, ` != nil {`)
+					p.In()
+				}
+				if nullable || repeated {
+					p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
+				} else {
+					p.P(`s = append(s, "`, fieldname, `: " + `, stringsPkg.Use(), `.Replace(this.`, fieldname, `.GoString()`, ",`&`,``,1)", ` + ",\n")`)
+				}
+				if nullable || repeated {
+					p.Out()
+					p.P(`}`)
+				}
+			} else {
+				if !proto3 && (nullable || repeated) {
+					p.P(`if this.`, fieldname, ` != nil {`)
+					p.In()
+				}
+				if field.IsEnum() {
+					if nullable && !repeated && !proto3 {
+						goTyp, _ := p.GoType(message, field)
+						p.P(`s = append(s, "`, fieldname, `: " + valueToGoString`, p.localName, `(this.`, fieldname, `,"`, packageName, ".", generator.GoTypeToName(goTyp), `"`, `) + ",\n")`)
+					} else {
+						p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
+					}
+				} else {
+					if nullable && !repeated && !proto3 {
+						goTyp, _ := p.GoType(message, field)
+						p.P(`s = append(s, "`, fieldname, `: " + valueToGoString`, p.localName, `(this.`, fieldname, `,"`, generator.GoTypeToName(goTyp), `"`, `) + ",\n")`)
+					} else {
+						p.P(`s = append(s, "`, fieldname, `: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `) + ",\n")`)
+					}
+				}
+				if !proto3 && (nullable || repeated) {
+					p.Out()
+					p.P(`}`)
+				}
+			}
+		}
+		if message.DescriptorProto.HasExtension() {
+			p.P(`if this.XXX_extensions != nil {`)
+			p.In()
+			if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
+				p.P(`s = append(s, "XXX_extensions: " + extensionToGoString`, p.localName, `(this.XXX_extensions) + ",\n")`)
+			} else {
+				p.P(`s = append(s, "XXX_extensions: " + `, fmtPkg.Use(), `.Sprintf("%#v", this.XXX_extensions) + ",\n")`)
+			}
+			p.Out()
+			p.P(`}`)
+		}
+		if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P(`if this.XXX_unrecognized != nil {`)
+			p.In()
+			p.P(`s = append(s, "XXX_unrecognized:" + `, fmtPkg.Use(), `.Sprintf("%#v", this.XXX_unrecognized) + ",\n")`)
+			p.Out()
+			p.P(`}`)
+		}
+
+		p.P(`s = append(s, "}")`)
+		//outStr += strings.Join([]string{" + `}`", `}`, `,", "`, ")"}, "")
+		p.P(`return `, stringsPkg.Use(), `.Join(s, "")`)
+		p.Out()
+		p.P(`}`)
+
+		//Generate GoString methods for oneof fields
+		for _, field := range message.Field {
+			oneof := field.OneofIndex != nil
+			if !oneof {
+				continue
+			}
+			ccTypeName := p.OneOfTypeName(message, field)
+			p.P(`func (this *`, ccTypeName, `) GoString() string {`)
+			p.In()
+			p.P(`if this == nil {`)
+			p.In()
+			p.P(`return "nil"`)
+			p.Out()
+			p.P(`}`)
+			outFlds := []string{}
+			fieldname := p.GetOneOfFieldName(message, field)
+			if field.IsMessage() || p.IsGroup(field) {
+				tmp := strings.Join([]string{"`", fieldname, ":` + "}, "")
+				tmp += strings.Join([]string{fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, `)`}, "")
+				outFlds = append(outFlds, tmp)
+			} else {
+				tmp := strings.Join([]string{"`", fieldname, ":` + "}, "")
+				tmp += strings.Join([]string{fmtPkg.Use(), `.Sprintf("%#v", this.`, fieldname, ")"}, "")
+				outFlds = append(outFlds, tmp)
+			}
+			outStr := strings.Join([]string{"s := ", stringsPkg.Use(), ".Join([]string{`&", packageName, ".", ccTypeName, "{` + \n"}, "")
+			outStr += strings.Join(outFlds, ",\n")
+			outStr += strings.Join([]string{" + `}`", `}`, `,", "`, ")"}, "")
+			p.P(outStr)
+			p.P(`return s`)
+			p.Out()
+			p.P(`}`)
+		}
+	}
+
+	if !p.atleastOne {
+		return
+	}
+
+	p.P(`func valueToGoString`, p.localName, `(v interface{}, typ string) string {`)
+	p.In()
+	p.P(`rv := `, reflectPkg.Use(), `.ValueOf(v)`)
+	p.P(`if rv.IsNil() {`)
+	p.In()
+	p.P(`return "nil"`)
+	p.Out()
+	p.P(`}`)
+	p.P(`pv := `, reflectPkg.Use(), `.Indirect(rv).Interface()`)
+	p.P(`return `, fmtPkg.Use(), `.Sprintf("func(v %v) *%v { return &v } ( %#v )", typ, typ, pv)`)
+	p.Out()
+	p.P(`}`)
+
+	p.P(`func extensionToGoString`, p.localName, `(e map[int32]`, protoPkg.Use(), `.Extension) string {`)
+	p.In()
+	p.P(`if e == nil { return "nil" }`)
+	p.P(`s := "map[int32]proto.Extension{"`)
+	p.P(`keys := make([]int, 0, len(e))`)
+	p.P(`for k := range e {`)
+	p.In()
+	p.P(`keys = append(keys, int(k))`)
+	p.Out()
+	p.P(`}`)
+	p.P(sortPkg.Use(), `.Ints(keys)`)
+	p.P(`ss := []string{}`)
+	p.P(`for _, k := range keys {`)
+	p.In()
+	p.P(`ss = append(ss, `, strconvPkg.Use(), `.Itoa(k) + ": " + e[int32(k)].GoString())`)
+	p.Out()
+	p.P(`}`)
+	p.P(`s+=`, stringsPkg.Use(), `.Join(ss, ",") + "}"`)
+	p.P(`return s`)
+	p.Out()
+	p.P(`}`)
+
+}
+
+func init() {
+	generator.RegisterPlugin(NewGoString())
+}

+ 88 - 0
vendor/github.com/gogo/protobuf/plugin/gostring/gostringtest.go

@@ -0,0 +1,88 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 gostring
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/plugin/testgen"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type test struct {
+	*generator.Generator
+}
+
+func NewTest(g *generator.Generator) testgen.TestPlugin {
+	return &test{g}
+}
+
+func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
+	used := false
+	randPkg := imports.NewImport("math/rand")
+	timePkg := imports.NewImport("time")
+	testingPkg := imports.NewImport("testing")
+	fmtPkg := imports.NewImport("fmt")
+	parserPkg := imports.NewImport("go/parser")
+	for _, message := range file.Messages() {
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		if !gogoproto.HasGoString(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+
+		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
+			used = true
+			p.P(`func Test`, ccTypeName, `GoString(t *`, testingPkg.Use(), `.T) {`)
+			p.In()
+			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`)
+			p.P(`p := NewPopulated`, ccTypeName, `(popr, false)`)
+			p.P(`s1 := p.GoString()`)
+			p.P(`s2 := `, fmtPkg.Use(), `.Sprintf("%#v", p)`)
+			p.P(`if s1 != s2 {`)
+			p.In()
+			p.P(`t.Fatalf("GoString want %v got %v", s1, s2)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`_, err := `, parserPkg.Use(), `.ParseExpr(s1)`)
+			p.P(`if err != nil {`)
+			p.In()
+			p.P(`panic(err)`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+		}
+
+	}
+	return used
+}
+
+func init() {
+	testgen.RegisterTestPlugin(NewTest)
+}

+ 462 - 0
vendor/github.com/gogo/protobuf/plugin/grpc/grpc.go

@@ -0,0 +1,462 @@
+// Go support for Protocol Buffers - Google's data interchange format
+//
+// Copyright 2015 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 grpc outputs gRPC service descriptions in Go code.
+// It runs as a plugin for the Go protocol buffer compiler plugin.
+// It is linked in to protoc-gen-go.
+package grpc
+
+import (
+	"fmt"
+	"path"
+	"strconv"
+	"strings"
+
+	pb "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+// generatedCodeVersion indicates a version of the generated code.
+// It is incremented whenever an incompatibility between the generated code and
+// the grpc package is introduced; the generated code references
+// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion).
+const generatedCodeVersion = 2
+
+// Paths for packages used by code generated in this file,
+// relative to the import_prefix of the generator.Generator.
+const (
+	contextPkgPath = "golang.org/x/net/context"
+	grpcPkgPath    = "google.golang.org/grpc"
+)
+
+func init() {
+	generator.RegisterPlugin(new(grpc))
+}
+
+// grpc is an implementation of the Go protocol buffer compiler's
+// plugin architecture.  It generates bindings for gRPC support.
+type grpc struct {
+	gen *generator.Generator
+}
+
+// Name returns the name of this plugin, "grpc".
+func (g *grpc) Name() string {
+	return "grpc"
+}
+
+// The names for packages imported in the generated code.
+// They may vary from the final path component of the import path
+// if the name is used by other packages.
+var (
+	contextPkg string
+	grpcPkg    string
+)
+
+// Init initializes the plugin.
+func (g *grpc) Init(gen *generator.Generator) {
+	g.gen = gen
+	contextPkg = generator.RegisterUniquePackageName("context", nil)
+	grpcPkg = generator.RegisterUniquePackageName("grpc", nil)
+}
+
+// Given a type name defined in a .proto, return its object.
+// Also record that we're using it, to guarantee the associated import.
+func (g *grpc) objectNamed(name string) generator.Object {
+	g.gen.RecordTypeUse(name)
+	return g.gen.ObjectNamed(name)
+}
+
+// Given a type name defined in a .proto, return its name as we will print it.
+func (g *grpc) typeName(str string) string {
+	return g.gen.TypeName(g.objectNamed(str))
+}
+
+// P forwards to g.gen.P.
+func (g *grpc) P(args ...interface{}) { g.gen.P(args...) }
+
+// Generate generates code for the services in the given file.
+func (g *grpc) Generate(file *generator.FileDescriptor) {
+	if len(file.FileDescriptorProto.Service) == 0 {
+		return
+	}
+
+	g.P("// Reference imports to suppress errors if they are not otherwise used.")
+	g.P("var _ ", contextPkg, ".Context")
+	g.P("var _ ", grpcPkg, ".ClientConn")
+	g.P()
+
+	// Assert version compatibility.
+	g.P("// This is a compile-time assertion to ensure that this generated file")
+	g.P("// is compatible with the grpc package it is being compiled against.")
+	g.P("const _ = ", grpcPkg, ".SupportPackageIsVersion", generatedCodeVersion)
+	g.P()
+
+	for i, service := range file.FileDescriptorProto.Service {
+		g.generateService(file, service, i)
+	}
+}
+
+// GenerateImports generates the import declaration for this file.
+func (g *grpc) GenerateImports(file *generator.FileDescriptor) {
+	if len(file.FileDescriptorProto.Service) == 0 {
+		return
+	}
+	g.P("import (")
+	g.P(contextPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, contextPkgPath)))
+	g.P(grpcPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, grpcPkgPath)))
+	g.P(")")
+	g.P()
+}
+
+// reservedClientName records whether a client name is reserved on the client side.
+var reservedClientName = map[string]bool{
+// TODO: do we need any in gRPC?
+}
+
+func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }
+
+// generateService generates all the code for the named service.
+func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
+	path := fmt.Sprintf("6,%d", index) // 6 means service.
+
+	origServName := service.GetName()
+	fullServName := origServName
+	if pkg := file.GetPackage(); pkg != "" {
+		fullServName = pkg + "." + fullServName
+	}
+	servName := generator.CamelCase(origServName)
+
+	g.P()
+	g.P("// Client API for ", servName, " service")
+	g.P()
+
+	// Client interface.
+	g.P("type ", servName, "Client interface {")
+	for i, method := range service.Method {
+		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
+		g.P(g.generateClientSignature(servName, method))
+	}
+	g.P("}")
+	g.P()
+
+	// Client structure.
+	g.P("type ", unexport(servName), "Client struct {")
+	g.P("cc *", grpcPkg, ".ClientConn")
+	g.P("}")
+	g.P()
+
+	// NewClient factory.
+	g.P("func New", servName, "Client (cc *", grpcPkg, ".ClientConn) ", servName, "Client {")
+	g.P("return &", unexport(servName), "Client{cc}")
+	g.P("}")
+	g.P()
+
+	var methodIndex, streamIndex int
+	serviceDescVar := "_" + servName + "_serviceDesc"
+	// Client method implementations.
+	for _, method := range service.Method {
+		var descExpr string
+		if !method.GetServerStreaming() && !method.GetClientStreaming() {
+			// Unary RPC method
+			descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex)
+			methodIndex++
+		} else {
+			// Streaming RPC method
+			descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex)
+			streamIndex++
+		}
+		g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr)
+	}
+
+	g.P("// Server API for ", servName, " service")
+	g.P()
+
+	// Server interface.
+	serverType := servName + "Server"
+	g.P("type ", serverType, " interface {")
+	for i, method := range service.Method {
+		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
+		g.P(g.generateServerSignature(servName, method))
+	}
+	g.P("}")
+	g.P()
+
+	// Server registration.
+	g.P("func Register", servName, "Server(s *", grpcPkg, ".Server, srv ", serverType, ") {")
+	g.P("s.RegisterService(&", serviceDescVar, `, srv)`)
+	g.P("}")
+	g.P()
+
+	// Server handler implementations.
+	var handlerNames []string
+	for _, method := range service.Method {
+		hname := g.generateServerMethod(servName, fullServName, method)
+		handlerNames = append(handlerNames, hname)
+	}
+
+	// Service descriptor.
+	g.P("var ", serviceDescVar, " = ", grpcPkg, ".ServiceDesc {")
+	g.P("ServiceName: ", strconv.Quote(fullServName), ",")
+	g.P("HandlerType: (*", serverType, ")(nil),")
+	g.P("Methods: []", grpcPkg, ".MethodDesc{")
+	for i, method := range service.Method {
+		if method.GetServerStreaming() || method.GetClientStreaming() {
+			continue
+		}
+		g.P("{")
+		g.P("MethodName: ", strconv.Quote(method.GetName()), ",")
+		g.P("Handler: ", handlerNames[i], ",")
+		g.P("},")
+	}
+	g.P("},")
+	g.P("Streams: []", grpcPkg, ".StreamDesc{")
+	for i, method := range service.Method {
+		if !method.GetServerStreaming() && !method.GetClientStreaming() {
+			continue
+		}
+		g.P("{")
+		g.P("StreamName: ", strconv.Quote(method.GetName()), ",")
+		g.P("Handler: ", handlerNames[i], ",")
+		if method.GetServerStreaming() {
+			g.P("ServerStreams: true,")
+		}
+		if method.GetClientStreaming() {
+			g.P("ClientStreams: true,")
+		}
+		g.P("},")
+	}
+	g.P("},")
+	g.P("}")
+	g.P()
+}
+
+// generateClientSignature returns the client-side signature for a method.
+func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
+	origMethName := method.GetName()
+	methName := generator.CamelCase(origMethName)
+	if reservedClientName[methName] {
+		methName += "_"
+	}
+	reqArg := ", in *" + g.typeName(method.GetInputType())
+	if method.GetClientStreaming() {
+		reqArg = ""
+	}
+	respName := "*" + g.typeName(method.GetOutputType())
+	if method.GetServerStreaming() || method.GetClientStreaming() {
+		respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
+	}
+	return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName)
+}
+
+func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
+	sname := fmt.Sprintf("/%s/%s", fullServName, method.GetName())
+	methName := generator.CamelCase(method.GetName())
+	inType := g.typeName(method.GetInputType())
+	outType := g.typeName(method.GetOutputType())
+
+	g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{")
+	if !method.GetServerStreaming() && !method.GetClientStreaming() {
+		g.P("out := new(", outType, ")")
+		// TODO: Pass descExpr to Invoke.
+		g.P("err := ", grpcPkg, `.Invoke(ctx, "`, sname, `", in, out, c.cc, opts...)`)
+		g.P("if err != nil { return nil, err }")
+		g.P("return out, nil")
+		g.P("}")
+		g.P()
+		return
+	}
+	streamType := unexport(servName) + methName + "Client"
+	g.P("stream, err := ", grpcPkg, ".NewClientStream(ctx, ", descExpr, `, c.cc, "`, sname, `", opts...)`)
+	g.P("if err != nil { return nil, err }")
+	g.P("x := &", streamType, "{stream}")
+	if !method.GetClientStreaming() {
+		g.P("if err := x.ClientStream.SendMsg(in); err != nil { return nil, err }")
+		g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
+	}
+	g.P("return x, nil")
+	g.P("}")
+	g.P()
+
+	genSend := method.GetClientStreaming()
+	genRecv := method.GetServerStreaming()
+	genCloseAndRecv := !method.GetServerStreaming()
+
+	// Stream auxiliary types and methods.
+	g.P("type ", servName, "_", methName, "Client interface {")
+	if genSend {
+		g.P("Send(*", inType, ") error")
+	}
+	if genRecv {
+		g.P("Recv() (*", outType, ", error)")
+	}
+	if genCloseAndRecv {
+		g.P("CloseAndRecv() (*", outType, ", error)")
+	}
+	g.P(grpcPkg, ".ClientStream")
+	g.P("}")
+	g.P()
+
+	g.P("type ", streamType, " struct {")
+	g.P(grpcPkg, ".ClientStream")
+	g.P("}")
+	g.P()
+
+	if genSend {
+		g.P("func (x *", streamType, ") Send(m *", inType, ") error {")
+		g.P("return x.ClientStream.SendMsg(m)")
+		g.P("}")
+		g.P()
+	}
+	if genRecv {
+		g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {")
+		g.P("m := new(", outType, ")")
+		g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
+		g.P("return m, nil")
+		g.P("}")
+		g.P()
+	}
+	if genCloseAndRecv {
+		g.P("func (x *", streamType, ") CloseAndRecv() (*", outType, ", error) {")
+		g.P("if err := x.ClientStream.CloseSend(); err != nil { return nil, err }")
+		g.P("m := new(", outType, ")")
+		g.P("if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err }")
+		g.P("return m, nil")
+		g.P("}")
+		g.P()
+	}
+}
+
+// generateServerSignature returns the server-side signature for a method.
+func (g *grpc) generateServerSignature(servName string, method *pb.MethodDescriptorProto) string {
+	origMethName := method.GetName()
+	methName := generator.CamelCase(origMethName)
+	if reservedClientName[methName] {
+		methName += "_"
+	}
+
+	var reqArgs []string
+	ret := "error"
+	if !method.GetServerStreaming() && !method.GetClientStreaming() {
+		reqArgs = append(reqArgs, contextPkg+".Context")
+		ret = "(*" + g.typeName(method.GetOutputType()) + ", error)"
+	}
+	if !method.GetClientStreaming() {
+		reqArgs = append(reqArgs, "*"+g.typeName(method.GetInputType()))
+	}
+	if method.GetServerStreaming() || method.GetClientStreaming() {
+		reqArgs = append(reqArgs, servName+"_"+generator.CamelCase(origMethName)+"Server")
+	}
+
+	return methName + "(" + strings.Join(reqArgs, ", ") + ") " + ret
+}
+
+func (g *grpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string {
+	methName := generator.CamelCase(method.GetName())
+	hname := fmt.Sprintf("_%s_%s_Handler", servName, methName)
+	inType := g.typeName(method.GetInputType())
+	outType := g.typeName(method.GetOutputType())
+
+	if !method.GetServerStreaming() && !method.GetClientStreaming() {
+		g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {")
+		g.P("in := new(", inType, ")")
+		g.P("if err := dec(in); err != nil { return nil, err }")
+		g.P("if interceptor == nil { return srv.(", servName, "Server).", methName, "(ctx, in) }")
+		g.P("info := &", grpcPkg, ".UnaryServerInfo{")
+		g.P("Server: srv,")
+		g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",")
+		g.P("}")
+		g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {")
+		g.P("return srv.(", servName, "Server).", methName, "(ctx, req.(*", inType, "))")
+		g.P("}")
+		g.P("return interceptor(ctx, in, info, handler)")
+		g.P("}")
+		g.P()
+		return hname
+	}
+	streamType := unexport(servName) + methName + "Server"
+	g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {")
+	if !method.GetClientStreaming() {
+		g.P("m := new(", inType, ")")
+		g.P("if err := stream.RecvMsg(m); err != nil { return err }")
+		g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})")
+	} else {
+		g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})")
+	}
+	g.P("}")
+	g.P()
+
+	genSend := method.GetServerStreaming()
+	genSendAndClose := !method.GetServerStreaming()
+	genRecv := method.GetClientStreaming()
+
+	// Stream auxiliary types and methods.
+	g.P("type ", servName, "_", methName, "Server interface {")
+	if genSend {
+		g.P("Send(*", outType, ") error")
+	}
+	if genSendAndClose {
+		g.P("SendAndClose(*", outType, ") error")
+	}
+	if genRecv {
+		g.P("Recv() (*", inType, ", error)")
+	}
+	g.P(grpcPkg, ".ServerStream")
+	g.P("}")
+	g.P()
+
+	g.P("type ", streamType, " struct {")
+	g.P(grpcPkg, ".ServerStream")
+	g.P("}")
+	g.P()
+
+	if genSend {
+		g.P("func (x *", streamType, ") Send(m *", outType, ") error {")
+		g.P("return x.ServerStream.SendMsg(m)")
+		g.P("}")
+		g.P()
+	}
+	if genSendAndClose {
+		g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {")
+		g.P("return x.ServerStream.SendMsg(m)")
+		g.P("}")
+		g.P()
+	}
+	if genRecv {
+		g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {")
+		g.P("m := new(", inType, ")")
+		g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }")
+		g.P("return m, nil")
+		g.P("}")
+		g.P()
+	}
+
+	return hname
+}

+ 1323 - 0
vendor/github.com/gogo/protobuf/plugin/marshalto/marshalto.go

@@ -0,0 +1,1323 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The marshalto plugin generates a Marshal and MarshalTo method for each message.
+The `Marshal() ([]byte, error)` method results in the fact that the message
+implements the Marshaler interface.
+This allows proto.Marshal to be faster by calling the generated Marshal method rather than using reflect to Marshal the struct.
+
+If is enabled by the following extensions:
+
+  - marshaler
+  - marshaler_all
+
+Or the following extensions:
+
+  - unsafe_marshaler
+  - unsafe_marshaler_all
+
+That is if you want to use the unsafe package in your generated code.
+The speed up using the unsafe package is not very significant.
+
+The generation of marshalling tests are enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+And benchmarks given it is enabled using one of the following extensions:
+
+  - benchgen
+  - benchgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+option (gogoproto.marshaler_all) = true;
+
+message B {
+	option (gogoproto.description) = true;
+	optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
+	repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
+}
+
+given to the marshalto plugin, will generate the following code:
+
+  func (m *B) 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 *B) MarshalTo(data []byte) (int, error) {
+	var i int
+	_ = i
+	var l int
+	_ = l
+	data[i] = 0xa
+	i++
+	i = encodeVarintExample(data, i, uint64(m.A.Size()))
+	n2, err := m.A.MarshalTo(data[i:])
+	if err != nil {
+		return 0, err
+	}
+	i += n2
+	if len(m.G) > 0 {
+		for _, msg := range m.G {
+			data[i] = 0x12
+			i++
+			i = encodeVarintExample(data, i, uint64(msg.Size()))
+			n, err := msg.MarshalTo(data[i:])
+			if err != nil {
+				return 0, err
+			}
+			i += n
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		i += copy(data[i:], m.XXX_unrecognized)
+	}
+	return i, nil
+  }
+
+As shown above Marshal calculates the size of the not yet marshalled message
+and allocates the appropriate buffer.
+This is followed by calling the MarshalTo method which requires a preallocated buffer.
+The MarshalTo method allows a user to rather preallocated a reusable buffer.
+
+The Size method is generated using the size plugin and the gogoproto.sizer, gogoproto.sizer_all extensions.
+The user can also using the generated Size method to check that his reusable buffer is still big enough.
+
+The generated tests and benchmarks will keep you safe and show that this is really a significant speed improvement.
+
+An additional message-level option `stable_marshaler` (and the file-level
+option `stable_marshaler_all`) exists which causes the generated marshalling
+code to behave deterministically. Today, this only changes the serialization of
+maps; they are serialized in sort order.
+*/
+package marshalto
+
+import (
+	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/proto"
+	descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"github.com/gogo/protobuf/vanity"
+)
+
+type NumGen interface {
+	Next() string
+	Current() string
+}
+
+type numGen struct {
+	index int
+}
+
+func NewNumGen() NumGen {
+	return &numGen{0}
+}
+
+func (this *numGen) Next() string {
+	this.index++
+	return this.Current()
+}
+
+func (this *numGen) Current() string {
+	return strconv.Itoa(this.index)
+}
+
+type marshalto struct {
+	*generator.Generator
+	generator.PluginImports
+	atleastOne  bool
+	unsafePkg   generator.Single
+	errorsPkg   generator.Single
+	protoPkg    generator.Single
+	sortKeysPkg generator.Single
+	mathPkg     generator.Single
+	localName   string
+	unsafe      bool
+}
+
+func NewMarshal() *marshalto {
+	return &marshalto{}
+}
+
+func NewUnsafeMarshal() *marshalto {
+	return &marshalto{unsafe: true}
+}
+
+func (p *marshalto) Name() string {
+	if p.unsafe {
+		return "unsafemarshaler"
+	}
+	return "marshalto"
+}
+
+func (p *marshalto) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *marshalto) callFixed64(varName ...string) {
+	p.P(`i = encodeFixed64`, p.localName, `(data, i, uint64(`, strings.Join(varName, ""), `))`)
+}
+
+func (p *marshalto) callFixed32(varName ...string) {
+	p.P(`i = encodeFixed32`, p.localName, `(data, i, uint32(`, strings.Join(varName, ""), `))`)
+}
+
+func (p *marshalto) callVarint(varName ...string) {
+	p.P(`i = encodeVarint`, p.localName, `(data, i, uint64(`, strings.Join(varName, ""), `))`)
+}
+
+func (p *marshalto) encodeVarint(varName string) {
+	p.P(`for `, varName, ` >= 1<<7 {`)
+	p.In()
+	p.P(`data[i] = uint8(uint64(`, varName, `)&0x7f|0x80)`)
+	p.P(varName, ` >>= 7`)
+	p.P(`i++`)
+	p.Out()
+	p.P(`}`)
+	p.P(`data[i] = uint8(`, varName, `)`)
+	p.P(`i++`)
+}
+
+func (p *marshalto) encodeFixed64(varName string) {
+	p.P(`data[i] = uint8(`, varName, `)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 8)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 16)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 24)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 32)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 40)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 48)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 56)`)
+	p.P(`i++`)
+}
+
+func (p *marshalto) unsafeFixed64(varName string, someType string) {
+	p.P(`*(*`, someType, `)(`, p.unsafePkg.Use(), `.Pointer(&data[i])) = `, varName)
+	p.P(`i+=8`)
+}
+
+func (p *marshalto) encodeFixed32(varName string) {
+	p.P(`data[i] = uint8(`, varName, `)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 8)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 16)`)
+	p.P(`i++`)
+	p.P(`data[i] = uint8(`, varName, ` >> 24)`)
+	p.P(`i++`)
+}
+
+func (p *marshalto) unsafeFixed32(varName string, someType string) {
+	p.P(`*(*`, someType, `)(`, p.unsafePkg.Use(), `.Pointer(&data[i])) = `, varName)
+	p.P(`i+=4`)
+}
+
+func (p *marshalto) encodeKey(fieldNumber int32, wireType int) {
+	x := uint32(fieldNumber)<<3 | uint32(wireType)
+	i := 0
+	keybuf := make([]byte, 0)
+	for i = 0; x > 127; i++ {
+		keybuf = append(keybuf, 0x80|uint8(x&0x7F))
+		x >>= 7
+	}
+	keybuf = append(keybuf, uint8(x))
+	for _, b := range keybuf {
+		p.P(`data[i] = `, fmt.Sprintf("%#v", b))
+		p.P(`i++`)
+	}
+}
+
+func keySize(fieldNumber int32, wireType int) int {
+	x := uint32(fieldNumber)<<3 | uint32(wireType)
+	size := 0
+	for size = 0; x > 127; size++ {
+		x >>= 7
+	}
+	size++
+	return size
+}
+
+func wireToType(wire string) int {
+	switch wire {
+	case "fixed64":
+		return proto.WireFixed64
+	case "fixed32":
+		return proto.WireFixed32
+	case "varint":
+		return proto.WireVarint
+	case "bytes":
+		return proto.WireBytes
+	case "group":
+		return proto.WireBytes
+	case "zigzag32":
+		return proto.WireVarint
+	case "zigzag64":
+		return proto.WireVarint
+	}
+	panic("unreachable")
+}
+
+func (p *marshalto) mapField(numGen NumGen, fieldTyp descriptor.FieldDescriptorProto_Type, varName string, protoSizer bool) {
+	switch fieldTyp {
+	case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
+		p.callFixed64(p.mathPkg.Use(), `.Float64bits(float64(`, varName, `))`)
+	case descriptor.FieldDescriptorProto_TYPE_FLOAT:
+		p.callFixed32(p.mathPkg.Use(), `.Float32bits(float32(`, varName, `))`)
+	case descriptor.FieldDescriptorProto_TYPE_INT64,
+		descriptor.FieldDescriptorProto_TYPE_UINT64,
+		descriptor.FieldDescriptorProto_TYPE_INT32,
+		descriptor.FieldDescriptorProto_TYPE_UINT32,
+		descriptor.FieldDescriptorProto_TYPE_ENUM:
+		p.callVarint(varName)
+	case descriptor.FieldDescriptorProto_TYPE_FIXED64,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+		p.callFixed64(varName)
+	case descriptor.FieldDescriptorProto_TYPE_FIXED32,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+		p.callFixed32(varName)
+	case descriptor.FieldDescriptorProto_TYPE_BOOL:
+		p.P(`if `, varName, ` {`)
+		p.In()
+		p.P(`data[i] = 1`)
+		p.Out()
+		p.P(`} else {`)
+		p.In()
+		p.P(`data[i] = 0`)
+		p.Out()
+		p.P(`}`)
+		p.P(`i++`)
+	case descriptor.FieldDescriptorProto_TYPE_STRING,
+		descriptor.FieldDescriptorProto_TYPE_BYTES:
+		p.callVarint(`len(`, varName, `)`)
+		p.P(`i+=copy(data[i:], `, varName, `)`)
+	case descriptor.FieldDescriptorProto_TYPE_SINT32:
+		p.callVarint(`(uint32(`, varName, `) << 1) ^ uint32((`, varName, ` >> 31))`)
+	case descriptor.FieldDescriptorProto_TYPE_SINT64:
+		p.callVarint(`(uint64(`, varName, `) << 1) ^ uint64((`, varName, ` >> 63))`)
+	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+		if protoSizer {
+			p.callVarint(varName, `.ProtoSize()`)
+		} else {
+			p.callVarint(varName, `.Size()`)
+		}
+		p.P(`n`, numGen.Next(), `, err := `, varName, `.MarshalTo(data[i:])`)
+		p.P(`if err != nil {`)
+		p.In()
+		p.P(`return 0, err`)
+		p.Out()
+		p.P(`}`)
+		p.P(`i+=n`, numGen.Current())
+	}
+}
+
+type orderFields []*descriptor.FieldDescriptorProto
+
+func (this orderFields) Len() int {
+	return len(this)
+}
+
+func (this orderFields) Less(i, j int) bool {
+	return this[i].GetNumber() < this[j].GetNumber()
+}
+
+func (this orderFields) Swap(i, j int) {
+	this[i], this[j] = this[j], this[i]
+}
+
+func (p *marshalto) generateField(proto3 bool, numGen NumGen, file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto) {
+	fieldname := p.GetOneOfFieldName(message, field)
+	nullable := gogoproto.IsNullable(field)
+	repeated := field.IsRepeated()
+	required := field.IsRequired()
+
+	protoSizer := gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto)
+	doNilCheck := gogoproto.NeedsNilCheck(proto3, field)
+	if required && nullable {
+		p.P(`if m.`, fieldname, `== nil {`)
+		p.In()
+		if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+			p.P(`return 0, new(`, p.protoPkg.Use(), `.RequiredNotSetError)`)
+		} else {
+			p.P(`return 0, `, p.protoPkg.Use(), `.NewRequiredNotSetError("`, field.GetName(), `")`)
+		}
+		p.Out()
+		p.P(`} else {`)
+	} else if repeated {
+		p.P(`if len(m.`, fieldname, `) > 0 {`)
+		p.In()
+	} else if doNilCheck {
+		p.P(`if m.`, fieldname, ` != nil {`)
+		p.In()
+	}
+	packed := field.IsPacked()
+	wireType := field.WireType()
+	fieldNumber := field.GetNumber()
+	if packed {
+		wireType = proto.WireBytes
+	}
+	switch *field.Type {
+	case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
+		if !p.unsafe || gogoproto.IsCastType(field) {
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 8`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.P(`f`, numGen.Next(), ` := `, p.mathPkg.Use(), `.Float64bits(float64(num))`)
+				p.encodeFixed64("f" + numGen.Current())
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.P(`f`, numGen.Next(), ` := `, p.mathPkg.Use(), `.Float64bits(float64(num))`)
+				p.encodeFixed64("f" + numGen.Current())
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed64(p.mathPkg.Use(), `.Float64bits(float64(m.`+fieldname, `))`)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed64(p.mathPkg.Use(), `.Float64bits(float64(m.`+fieldname, `))`)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed64(p.mathPkg.Use(), `.Float64bits(float64(*m.`+fieldname, `))`)
+			}
+		} else {
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 8`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.unsafeFixed64("num", "float64")
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64("num", "float64")
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64(`m.`+fieldname, "float64")
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64(`m.`+fieldname, "float64")
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64(`*m.`+fieldname, `float64`)
+			}
+		}
+	case descriptor.FieldDescriptorProto_TYPE_FLOAT:
+		if !p.unsafe || gogoproto.IsCastType(field) {
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 4`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.P(`f`, numGen.Next(), ` := `, p.mathPkg.Use(), `.Float32bits(float32(num))`)
+				p.encodeFixed32("f" + numGen.Current())
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.P(`f`, numGen.Next(), ` := `, p.mathPkg.Use(), `.Float32bits(float32(num))`)
+				p.encodeFixed32("f" + numGen.Current())
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed32(p.mathPkg.Use(), `.Float32bits(float32(m.`+fieldname, `))`)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed32(p.mathPkg.Use(), `.Float32bits(float32(m.`+fieldname, `))`)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed32(p.mathPkg.Use(), `.Float32bits(float32(*m.`+fieldname, `))`)
+			}
+		} else {
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 4`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.unsafeFixed32("num", "float32")
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32("num", "float32")
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32(`m.`+fieldname, `float32`)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32(`m.`+fieldname, `float32`)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32(`*m.`+fieldname, "float32")
+			}
+		}
+	case descriptor.FieldDescriptorProto_TYPE_INT64,
+		descriptor.FieldDescriptorProto_TYPE_UINT64,
+		descriptor.FieldDescriptorProto_TYPE_INT32,
+		descriptor.FieldDescriptorProto_TYPE_UINT32,
+		descriptor.FieldDescriptorProto_TYPE_ENUM:
+		if packed {
+			jvar := "j" + numGen.Next()
+			p.P(`data`, numGen.Next(), ` := make([]byte, len(m.`, fieldname, `)*10)`)
+			p.P(`var `, jvar, ` int`)
+			if *field.Type == descriptor.FieldDescriptorProto_TYPE_INT64 ||
+				*field.Type == descriptor.FieldDescriptorProto_TYPE_INT32 {
+				p.P(`for _, num1 := range m.`, fieldname, ` {`)
+				p.In()
+				p.P(`num := uint64(num1)`)
+			} else {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+			}
+			p.P(`for num >= 1<<7 {`)
+			p.In()
+			p.P(`data`, numGen.Current(), `[`, jvar, `] = uint8(uint64(num)&0x7f|0x80)`)
+			p.P(`num >>= 7`)
+			p.P(jvar, `++`)
+			p.Out()
+			p.P(`}`)
+			p.P(`data`, numGen.Current(), `[`, jvar, `] = uint8(num)`)
+			p.P(jvar, `++`)
+			p.Out()
+			p.P(`}`)
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(jvar)
+			p.P(`i += copy(data[i:], data`, numGen.Current(), `[:`, jvar, `])`)
+		} else if repeated {
+			p.P(`for _, num := range m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint("num")
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`m.`, fieldname)
+			p.Out()
+			p.P(`}`)
+		} else if !nullable {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`m.`, fieldname)
+		} else {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`*m.`, fieldname)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_FIXED64,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+		if !p.unsafe {
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 8`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeFixed64("num")
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.encodeFixed64("num")
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed64("m." + fieldname)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed64("m." + fieldname)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed64("*m." + fieldname)
+			}
+		} else {
+			typeName := "int64"
+			if *field.Type == descriptor.FieldDescriptorProto_TYPE_FIXED64 {
+				typeName = "uint64"
+			}
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 8`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.unsafeFixed64("num", typeName)
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64("num", typeName)
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64("m."+fieldname, typeName)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64("m."+fieldname, typeName)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed64("*m."+fieldname, typeName)
+			}
+		}
+	case descriptor.FieldDescriptorProto_TYPE_FIXED32,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+		if !p.unsafe {
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 4`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeFixed32("num")
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.encodeFixed32("num")
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed32("m." + fieldname)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed32("m." + fieldname)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.callFixed32("*m." + fieldname)
+			}
+		} else {
+			typeName := "int32"
+			if *field.Type == descriptor.FieldDescriptorProto_TYPE_FIXED32 {
+				typeName = "uint32"
+			}
+			if packed {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `) * 4`)
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.unsafeFixed32("num", typeName)
+				p.Out()
+				p.P(`}`)
+			} else if repeated {
+				p.P(`for _, num := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32("num", typeName)
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if m.`, fieldname, ` != 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32("m."+fieldname, typeName)
+				p.Out()
+				p.P(`}`)
+			} else if !nullable {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32("m."+fieldname, typeName)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.unsafeFixed32("*m."+fieldname, typeName)
+			}
+		}
+	case descriptor.FieldDescriptorProto_TYPE_BOOL:
+		if packed {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`len(m.`, fieldname, `)`)
+			p.P(`for _, b := range m.`, fieldname, ` {`)
+			p.In()
+			p.P(`if b {`)
+			p.In()
+			p.P(`data[i] = 1`)
+			p.Out()
+			p.P(`} else {`)
+			p.In()
+			p.P(`data[i] = 0`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i++`)
+			p.Out()
+			p.P(`}`)
+		} else if repeated {
+			p.P(`for _, b := range m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`if b {`)
+			p.In()
+			p.P(`data[i] = 1`)
+			p.Out()
+			p.P(`} else {`)
+			p.In()
+			p.P(`data[i] = 0`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i++`)
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`if m.`, fieldname, ` {`)
+			p.In()
+			p.P(`data[i] = 1`)
+			p.Out()
+			p.P(`} else {`)
+			p.In()
+			p.P(`data[i] = 0`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i++`)
+			p.Out()
+			p.P(`}`)
+		} else if !nullable {
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`if m.`, fieldname, ` {`)
+			p.In()
+			p.P(`data[i] = 1`)
+			p.Out()
+			p.P(`} else {`)
+			p.In()
+			p.P(`data[i] = 0`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i++`)
+		} else {
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`if *m.`, fieldname, ` {`)
+			p.In()
+			p.P(`data[i] = 1`)
+			p.Out()
+			p.P(`} else {`)
+			p.In()
+			p.P(`data[i] = 0`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i++`)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_STRING:
+		if repeated {
+			p.P(`for _, s := range m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`l = len(s)`)
+			p.encodeVarint("l")
+			p.P(`i+=copy(data[i:], s)`)
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if len(m.`, fieldname, `) > 0 {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`len(m.`, fieldname, `)`)
+			p.P(`i+=copy(data[i:], m.`, fieldname, `)`)
+			p.Out()
+			p.P(`}`)
+		} else if !nullable {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`len(m.`, fieldname, `)`)
+			p.P(`i+=copy(data[i:], m.`, fieldname, `)`)
+		} else {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`len(*m.`, fieldname, `)`)
+			p.P(`i+=copy(data[i:], *m.`, fieldname, `)`)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_GROUP:
+		panic(fmt.Errorf("marshaler does not support group %v", fieldname))
+	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+		if p.IsMap(field) {
+			m := p.GoMapType(nil, field)
+			keygoTyp, keywire := p.GoType(nil, m.KeyField)
+			keygoAliasTyp, _ := p.GoType(nil, m.KeyAliasField)
+			// keys may not be pointers
+			keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
+			keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
+			keyCapTyp := generator.CamelCase(keygoTyp)
+			valuegoTyp, valuewire := p.GoType(nil, m.ValueField)
+			valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
+			nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
+			keyKeySize := keySize(1, wireToType(keywire))
+			valueKeySize := keySize(2, wireToType(valuewire))
+			if gogoproto.IsStableMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
+				keysName := `keysFor` + fieldname
+				p.P(keysName, ` := make([]`, keygoTyp, `, 0, len(m.`, fieldname, `))`)
+				p.P(`for k, _ := range m.`, fieldname, ` {`)
+				p.In()
+				p.P(keysName, ` = append(`, keysName, `, `, keygoTyp, `(k))`)
+				p.Out()
+				p.P(`}`)
+				p.P(p.sortKeysPkg.Use(), `.`, keyCapTyp, `s(`, keysName, `)`)
+				p.P(`for _, k := range `, keysName, ` {`)
+			} else {
+				p.P(`for k, _ := range m.`, fieldname, ` {`)
+			}
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			sum := []string{strconv.Itoa(keyKeySize)}
+			switch m.KeyField.GetType() {
+			case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
+				descriptor.FieldDescriptorProto_TYPE_FIXED64,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+				sum = append(sum, `8`)
+			case descriptor.FieldDescriptorProto_TYPE_FLOAT,
+				descriptor.FieldDescriptorProto_TYPE_FIXED32,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+				sum = append(sum, `4`)
+			case descriptor.FieldDescriptorProto_TYPE_INT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT32,
+				descriptor.FieldDescriptorProto_TYPE_ENUM,
+				descriptor.FieldDescriptorProto_TYPE_INT32:
+				sum = append(sum, `sov`+p.localName+`(uint64(k))`)
+			case descriptor.FieldDescriptorProto_TYPE_BOOL:
+				sum = append(sum, `1`)
+			case descriptor.FieldDescriptorProto_TYPE_STRING,
+				descriptor.FieldDescriptorProto_TYPE_BYTES:
+				sum = append(sum, `len(k)+sov`+p.localName+`(uint64(len(k)))`)
+			case descriptor.FieldDescriptorProto_TYPE_SINT32,
+				descriptor.FieldDescriptorProto_TYPE_SINT64:
+				sum = append(sum, `soz`+p.localName+`(uint64(k))`)
+			}
+			if gogoproto.IsStableMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
+				p.P(`v := m.`, fieldname, `[`, keygoAliasTyp, `(k)]`)
+			} else {
+				p.P(`v := m.`, fieldname, `[k]`)
+			}
+			accessor := `v`
+			sum = append(sum, strconv.Itoa(valueKeySize))
+			switch m.ValueField.GetType() {
+			case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
+				descriptor.FieldDescriptorProto_TYPE_FIXED64,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+				sum = append(sum, strconv.Itoa(8))
+			case descriptor.FieldDescriptorProto_TYPE_FLOAT,
+				descriptor.FieldDescriptorProto_TYPE_FIXED32,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+				sum = append(sum, strconv.Itoa(4))
+			case descriptor.FieldDescriptorProto_TYPE_INT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT32,
+				descriptor.FieldDescriptorProto_TYPE_ENUM,
+				descriptor.FieldDescriptorProto_TYPE_INT32:
+				sum = append(sum, `sov`+p.localName+`(uint64(v))`)
+			case descriptor.FieldDescriptorProto_TYPE_BOOL:
+				sum = append(sum, `1`)
+			case descriptor.FieldDescriptorProto_TYPE_STRING,
+				descriptor.FieldDescriptorProto_TYPE_BYTES:
+				sum = append(sum, `len(v)+sov`+p.localName+`(uint64(len(v)))`)
+			case descriptor.FieldDescriptorProto_TYPE_SINT32,
+				descriptor.FieldDescriptorProto_TYPE_SINT64:
+				sum = append(sum, `soz`+p.localName+`(uint64(v))`)
+			case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+				if nullable {
+					p.P(`if v == nil {`)
+					p.In()
+					p.P(`return 0, `, p.errorsPkg.Use(), `.New("proto: map has nil element")`)
+					p.Out()
+					p.P(`}`)
+				}
+				if valuegoTyp != valuegoAliasTyp {
+					if nullable {
+						// cast back to the type that has the generated methods on it
+						accessor = `((` + valuegoTyp + `)(` + accessor + `))`
+					} else {
+						accessor = `((*` + valuegoTyp + `)(&` + accessor + `))`
+					}
+				} else if !nullable {
+					accessor = `(&v)`
+				}
+				if protoSizer {
+					p.P(`msgSize := `, accessor, `.ProtoSize()`)
+				} else {
+					p.P(`msgSize := `, accessor, `.Size()`)
+				}
+				sum = append(sum, `msgSize + sov`+p.localName+`(uint64(msgSize))`)
+			}
+			p.P(`mapSize := `, strings.Join(sum, " + "))
+			p.callVarint("mapSize")
+			p.encodeKey(1, wireToType(keywire))
+			p.mapField(numGen, m.KeyField.GetType(), "k", protoSizer)
+			p.encodeKey(2, wireToType(valuewire))
+			p.mapField(numGen, m.ValueField.GetType(), accessor, protoSizer)
+			p.Out()
+			p.P(`}`)
+		} else if repeated {
+			p.P(`for _, msg := range m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			if protoSizer {
+				p.callVarint("msg.ProtoSize()")
+			} else {
+				p.callVarint("msg.Size()")
+			}
+			p.P(`n, err := msg.MarshalTo(data[i:])`)
+			p.P(`if err != nil {`)
+			p.In()
+			p.P(`return 0, err`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i+=n`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			p.encodeKey(fieldNumber, wireType)
+			if protoSizer {
+				p.callVarint(`m.`, fieldname, `.ProtoSize()`)
+			} else {
+				p.callVarint(`m.`, fieldname, `.Size()`)
+			}
+			p.P(`n`, numGen.Next(), `, err := m.`, fieldname, `.MarshalTo(data[i:])`)
+			p.P(`if err != nil {`)
+			p.In()
+			p.P(`return 0, err`)
+			p.Out()
+			p.P(`}`)
+			p.P(`i+=n`, numGen.Current())
+		}
+	case descriptor.FieldDescriptorProto_TYPE_BYTES:
+		if !gogoproto.IsCustomType(field) {
+			if repeated {
+				p.P(`for _, b := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint("len(b)")
+				p.P(`i+=copy(data[i:], b)`)
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`if len(m.`, fieldname, `) > 0 {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `)`)
+				p.P(`i+=copy(data[i:], m.`, fieldname, `)`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				p.callVarint(`len(m.`, fieldname, `)`)
+				p.P(`i+=copy(data[i:], m.`, fieldname, `)`)
+			}
+		} else {
+			if repeated {
+				p.P(`for _, msg := range m.`, fieldname, ` {`)
+				p.In()
+				p.encodeKey(fieldNumber, wireType)
+				if protoSizer {
+					p.callVarint(`msg.ProtoSize()`)
+				} else {
+					p.callVarint(`msg.Size()`)
+				}
+				p.P(`n, err := msg.MarshalTo(data[i:])`)
+				p.P(`if err != nil {`)
+				p.In()
+				p.P(`return 0, err`)
+				p.Out()
+				p.P(`}`)
+				p.P(`i+=n`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.encodeKey(fieldNumber, wireType)
+				if protoSizer {
+					p.callVarint(`m.`, fieldname, `.ProtoSize()`)
+				} else {
+					p.callVarint(`m.`, fieldname, `.Size()`)
+				}
+				p.P(`n`, numGen.Next(), `, err := m.`, fieldname, `.MarshalTo(data[i:])`)
+				p.P(`if err != nil {`)
+				p.In()
+				p.P(`return 0, err`)
+				p.Out()
+				p.P(`}`)
+				p.P(`i+=n`, numGen.Current())
+			}
+		}
+	case descriptor.FieldDescriptorProto_TYPE_SINT32:
+		if packed {
+			datavar := "data" + numGen.Next()
+			jvar := "j" + numGen.Next()
+			p.P(datavar, ` := make([]byte, len(m.`, fieldname, ")*5)")
+			p.P(`var `, jvar, ` int`)
+			p.P(`for _, num := range m.`, fieldname, ` {`)
+			p.In()
+			xvar := "x" + numGen.Next()
+			p.P(xvar, ` := (uint32(num) << 1) ^ uint32((num >> 31))`)
+			p.P(`for `, xvar, ` >= 1<<7 {`)
+			p.In()
+			p.P(datavar, `[`, jvar, `] = uint8(uint64(`, xvar, `)&0x7f|0x80)`)
+			p.P(jvar, `++`)
+			p.P(xvar, ` >>= 7`)
+			p.Out()
+			p.P(`}`)
+			p.P(datavar, `[`, jvar, `] = uint8(`, xvar, `)`)
+			p.P(jvar, `++`)
+			p.Out()
+			p.P(`}`)
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(jvar)
+			p.P(`i+=copy(data[i:], `, datavar, `[:`, jvar, `])`)
+		} else if repeated {
+			p.P(`for _, num := range m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`x`, numGen.Next(), ` := (uint32(num) << 1) ^ uint32((num >> 31))`)
+			p.encodeVarint("x" + numGen.Current())
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`(uint32(m.`, fieldname, `) << 1) ^ uint32((m.`, fieldname, ` >> 31))`)
+			p.Out()
+			p.P(`}`)
+		} else if !nullable {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`(uint32(m.`, fieldname, `) << 1) ^ uint32((m.`, fieldname, ` >> 31))`)
+		} else {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`(uint32(*m.`, fieldname, `) << 1) ^ uint32((*m.`, fieldname, ` >> 31))`)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_SINT64:
+		if packed {
+			jvar := "j" + numGen.Next()
+			xvar := "x" + numGen.Next()
+			datavar := "data" + numGen.Next()
+			p.P(`var `, jvar, ` int`)
+			p.P(datavar, ` := make([]byte, len(m.`, fieldname, `)*10)`)
+			p.P(`for _, num := range m.`, fieldname, ` {`)
+			p.In()
+			p.P(xvar, ` := (uint64(num) << 1) ^ uint64((num >> 63))`)
+			p.P(`for `, xvar, ` >= 1<<7 {`)
+			p.In()
+			p.P(datavar, `[`, jvar, `] = uint8(uint64(`, xvar, `)&0x7f|0x80)`)
+			p.P(jvar, `++`)
+			p.P(xvar, ` >>= 7`)
+			p.Out()
+			p.P(`}`)
+			p.P(datavar, `[`, jvar, `] = uint8(`, xvar, `)`)
+			p.P(jvar, `++`)
+			p.Out()
+			p.P(`}`)
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(jvar)
+			p.P(`i+=copy(data[i:], `, datavar, `[:`, jvar, `])`)
+		} else if repeated {
+			p.P(`for _, num := range m.`, fieldname, ` {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.P(`x`, numGen.Next(), ` := (uint64(num) << 1) ^ uint64((num >> 63))`)
+			p.encodeVarint("x" + numGen.Current())
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`(uint64(m.`, fieldname, `) << 1) ^ uint64((m.`, fieldname, ` >> 63))`)
+			p.Out()
+			p.P(`}`)
+		} else if !nullable {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`(uint64(m.`, fieldname, `) << 1) ^ uint64((m.`, fieldname, ` >> 63))`)
+		} else {
+			p.encodeKey(fieldNumber, wireType)
+			p.callVarint(`(uint64(*m.`, fieldname, `) << 1) ^ uint64((*m.`, fieldname, ` >> 63))`)
+		}
+	default:
+		panic("not implemented")
+	}
+	if (required && nullable) || repeated || doNilCheck {
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func (p *marshalto) Generate(file *generator.FileDescriptor) {
+	numGen := NewNumGen()
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.atleastOne = false
+	p.localName = generator.FileName(file)
+
+	p.mathPkg = p.NewImport("math")
+	p.sortKeysPkg = p.NewImport("github.com/gogo/protobuf/sortkeys")
+	p.protoPkg = p.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		p.protoPkg = p.NewImport("github.com/golang/protobuf/proto")
+	}
+	p.unsafePkg = p.NewImport("unsafe")
+	p.errorsPkg = p.NewImport("errors")
+
+	for _, message := range file.Messages() {
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		if p.unsafe {
+			if !gogoproto.IsUnsafeMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
+				continue
+			}
+			if gogoproto.IsMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
+				panic(fmt.Sprintf("unsafe_marshaler and marshalto enabled for %v", ccTypeName))
+			}
+		}
+		if !p.unsafe {
+			if !gogoproto.IsMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
+				continue
+			}
+			if gogoproto.IsUnsafeMarshaler(file.FileDescriptorProto, message.DescriptorProto) {
+				panic(fmt.Sprintf("unsafe_marshaler and marshalto enabled for %v", ccTypeName))
+			}
+		}
+		p.atleastOne = true
+
+		p.P(`func (m *`, ccTypeName, `) Marshal() (data []byte, err error) {`)
+		p.In()
+		if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P(`size := m.ProtoSize()`)
+		} else {
+			p.P(`size := m.Size()`)
+		}
+		p.P(`data = make([]byte, size)`)
+		p.P(`n, err := m.MarshalTo(data)`)
+		p.P(`if err != nil {`)
+		p.In()
+		p.P(`return nil, err`)
+		p.Out()
+		p.P(`}`)
+		p.P(`return data[:n], nil`)
+		p.Out()
+		p.P(`}`)
+		p.P(``)
+		p.P(`func (m *`, ccTypeName, `) MarshalTo(data []byte) (int, error) {`)
+		p.In()
+		p.P(`var i int`)
+		p.P(`_ = i`)
+		p.P(`var l int`)
+		p.P(`_ = l`)
+		fields := orderFields(message.GetField())
+		sort.Sort(fields)
+		oneofs := make(map[string]struct{})
+		for _, field := range message.Field {
+			oneof := field.OneofIndex != nil
+			if !oneof {
+				proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+				p.generateField(proto3, numGen, file, message, field)
+			} else {
+				fieldname := p.GetFieldName(message, field)
+				if _, ok := oneofs[fieldname]; !ok {
+					oneofs[fieldname] = struct{}{}
+					p.P(`if m.`, fieldname, ` != nil {`)
+					p.In()
+					p.P(`nn`, numGen.Next(), `, err := m.`, fieldname, `.MarshalTo(data[i:])`)
+					p.P(`if err != nil {`)
+					p.In()
+					p.P(`return 0, err`)
+					p.Out()
+					p.P(`}`)
+					p.P(`i+=nn`, numGen.Current())
+					p.Out()
+					p.P(`}`)
+				}
+			}
+		}
+		if message.DescriptorProto.HasExtension() {
+			if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
+				p.P(`if len(m.XXX_extensions) > 0 {`)
+				p.In()
+				p.P(`n, err := `, p.protoPkg.Use(), `.EncodeExtensionMap(m.XXX_extensions, data[i:])`)
+				p.P(`if err != nil {`)
+				p.In()
+				p.P(`return 0, err`)
+				p.Out()
+				p.P(`}`)
+				p.P(`i+=n`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.P(`if m.XXX_extensions != nil {`)
+				p.In()
+				p.P(`i+=copy(data[i:], m.XXX_extensions)`)
+				p.Out()
+				p.P(`}`)
+			}
+		}
+		if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P(`if m.XXX_unrecognized != nil {`)
+			p.In()
+			p.P(`i+=copy(data[i:], m.XXX_unrecognized)`)
+			p.Out()
+			p.P(`}`)
+		}
+
+		p.P(`return i, nil`)
+		p.Out()
+		p.P(`}`)
+		p.P()
+
+		//Generate MarshalTo methods for oneof fields
+		m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
+		for _, field := range m.Field {
+			oneof := field.OneofIndex != nil
+			if !oneof {
+				continue
+			}
+			ccTypeName := p.OneOfTypeName(message, field)
+			p.P(`func (m *`, ccTypeName, `) MarshalTo(data []byte) (int, error) {`)
+			p.In()
+			p.P(`i := 0`)
+			vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly(field)
+			p.generateField(false, numGen, file, message, field)
+			p.P(`return i, nil`)
+			p.Out()
+			p.P(`}`)
+		}
+	}
+
+	if p.atleastOne {
+		p.P(`func encodeFixed64`, p.localName, `(data []byte, offset int, v uint64) int {`)
+		p.In()
+		p.P(`data[offset] = uint8(v)`)
+		p.P(`data[offset+1] = uint8(v >> 8)`)
+		p.P(`data[offset+2] = uint8(v >> 16)`)
+		p.P(`data[offset+3] = uint8(v >> 24)`)
+		p.P(`data[offset+4] = uint8(v >> 32)`)
+		p.P(`data[offset+5] = uint8(v >> 40)`)
+		p.P(`data[offset+6] = uint8(v >> 48)`)
+		p.P(`data[offset+7] = uint8(v >> 56)`)
+		p.P(`return offset+8`)
+		p.Out()
+		p.P(`}`)
+
+		p.P(`func encodeFixed32`, p.localName, `(data []byte, offset int, v uint32) int {`)
+		p.In()
+		p.P(`data[offset] = uint8(v)`)
+		p.P(`data[offset+1] = uint8(v >> 8)`)
+		p.P(`data[offset+2] = uint8(v >> 16)`)
+		p.P(`data[offset+3] = uint8(v >> 24)`)
+		p.P(`return offset+4`)
+		p.Out()
+		p.P(`}`)
+
+		p.P(`func encodeVarint`, p.localName, `(data []byte, offset int, v uint64) int {`)
+		p.In()
+		p.P(`for v >= 1<<7 {`)
+		p.In()
+		p.P(`data[offset] = uint8(v&0x7f|0x80)`)
+		p.P(`v >>= 7`)
+		p.P(`offset++`)
+		p.Out()
+		p.P(`}`)
+		p.P(`data[offset] = uint8(v)`)
+		p.P(`return offset+1`)
+		p.Out()
+		p.P(`}`)
+	}
+
+}
+
+func init() {
+	generator.RegisterPlugin(NewMarshal())
+	generator.RegisterPlugin(NewUnsafeMarshal())
+}

+ 91 - 0
vendor/github.com/gogo/protobuf/plugin/oneofcheck/oneofcheck.go

@@ -0,0 +1,91 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The oneofcheck plugin is used to check whether oneof is not used incorrectly.
+For instance:
+An error is caused if a oneof field:
+  - is used in a face
+  - is an embedded field
+
+*/
+package oneofcheck
+
+import (
+	"fmt"
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"os"
+)
+
+type plugin struct {
+	*generator.Generator
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "oneofcheck"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	for _, msg := range file.Messages() {
+		face := gogoproto.IsFace(file.FileDescriptorProto, msg.DescriptorProto)
+		for _, field := range msg.GetField() {
+			if field.OneofIndex == nil {
+				continue
+			}
+			if face {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in a face and oneof\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				os.Exit(1)
+			}
+			if gogoproto.IsEmbed(field) {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in an oneof and an embedded field\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				os.Exit(1)
+			}
+			if !gogoproto.IsNullable(field) {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in an oneof and a non-nullable field\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				os.Exit(1)
+			}
+			if gogoproto.IsUnion(file.FileDescriptorProto, msg.DescriptorProto) {
+				fmt.Fprintf(os.Stderr, "ERROR: field %v.%v cannot be in an oneof and in an union (deprecated)\n", generator.CamelCase(*msg.Name), generator.CamelCase(*field.Name))
+				os.Exit(1)
+			}
+		}
+	}
+}
+
+func (p *plugin) GenerateImports(*generator.FileDescriptor) {}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 774 - 0
vendor/github.com/gogo/protobuf/plugin/populate/populate.go

@@ -0,0 +1,774 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The populate plugin generates a NewPopulated function.
+This function returns a newly populated structure.
+
+It is enabled by the following extensions:
+
+  - populate
+  - populate_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  option (gogoproto.populate_all) = true;
+
+  message B {
+	optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
+	repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
+  }
+
+given to the populate plugin, will generate code the following code:
+
+  func NewPopulatedB(r randyExample, easy bool) *B {
+	this := &B{}
+	v2 := NewPopulatedA(r, easy)
+	this.A = *v2
+	if r.Intn(10) != 0 {
+		v3 := r.Intn(10)
+		this.G = make([]github_com_gogo_protobuf_test_custom.Uint128, v3)
+		for i := 0; i < v3; i++ {
+			v4 := github_com_gogo_protobuf_test_custom.NewPopulatedUint128(r)
+			this.G[i] = *v4
+		}
+	}
+	if !easy && r.Intn(10) != 0 {
+		this.XXX_unrecognized = randUnrecognizedExample(r, 3)
+	}
+	return this
+  }
+
+The idea that is useful for testing.
+Most of the other plugins' generated test code uses it.
+You will still be able to use the generated test code of other packages
+if you turn off the popluate plugin and write your own custom NewPopulated function.
+
+If the easy flag is not set the XXX_unrecognized and XXX_extensions fields are also populated.
+These have caused problems with JSON marshalling and unmarshalling tests.
+
+*/
+package populate
+
+import (
+	"fmt"
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/proto"
+	descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"github.com/gogo/protobuf/vanity"
+	"math"
+	"strconv"
+	"strings"
+)
+
+type VarGen interface {
+	Next() string
+	Current() string
+}
+
+type varGen struct {
+	index int64
+}
+
+func NewVarGen() VarGen {
+	return &varGen{0}
+}
+
+func (this *varGen) Next() string {
+	this.index++
+	return fmt.Sprintf("v%d", this.index)
+}
+
+func (this *varGen) Current() string {
+	return fmt.Sprintf("v%d", this.index)
+}
+
+type plugin struct {
+	*generator.Generator
+	generator.PluginImports
+	varGen     VarGen
+	atleastOne bool
+	localName  string
+}
+
+func NewPlugin() *plugin {
+	return &plugin{}
+}
+
+func (p *plugin) Name() string {
+	return "populate"
+}
+
+func (p *plugin) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func value(typeName string, fieldType descriptor.FieldDescriptorProto_Type) string {
+	switch fieldType {
+	case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
+		return typeName + "(r.Float64())"
+	case descriptor.FieldDescriptorProto_TYPE_FLOAT:
+		return typeName + "(r.Float32())"
+	case descriptor.FieldDescriptorProto_TYPE_INT64,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED64,
+		descriptor.FieldDescriptorProto_TYPE_SINT64:
+		return typeName + "(r.Int63())"
+	case descriptor.FieldDescriptorProto_TYPE_UINT64,
+		descriptor.FieldDescriptorProto_TYPE_FIXED64:
+		return typeName + "(uint64(r.Uint32()))"
+	case descriptor.FieldDescriptorProto_TYPE_INT32,
+		descriptor.FieldDescriptorProto_TYPE_SINT32,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED32,
+		descriptor.FieldDescriptorProto_TYPE_ENUM:
+		return typeName + "(r.Int31())"
+	case descriptor.FieldDescriptorProto_TYPE_UINT32,
+		descriptor.FieldDescriptorProto_TYPE_FIXED32:
+		return typeName + "(r.Uint32())"
+	case descriptor.FieldDescriptorProto_TYPE_BOOL:
+		return typeName + `(bool(r.Intn(2) == 0))`
+	case descriptor.FieldDescriptorProto_TYPE_STRING,
+		descriptor.FieldDescriptorProto_TYPE_GROUP,
+		descriptor.FieldDescriptorProto_TYPE_MESSAGE,
+		descriptor.FieldDescriptorProto_TYPE_BYTES:
+	}
+	panic(fmt.Errorf("unexpected type %v", typeName))
+}
+
+func negative(fieldType descriptor.FieldDescriptorProto_Type) bool {
+	switch fieldType {
+	case descriptor.FieldDescriptorProto_TYPE_UINT64,
+		descriptor.FieldDescriptorProto_TYPE_FIXED64,
+		descriptor.FieldDescriptorProto_TYPE_UINT32,
+		descriptor.FieldDescriptorProto_TYPE_FIXED32,
+		descriptor.FieldDescriptorProto_TYPE_BOOL:
+		return false
+	}
+	return true
+}
+
+func getFuncName(goTypName string) string {
+	funcName := "NewPopulated" + goTypName
+	goTypNames := strings.Split(goTypName, ".")
+	if len(goTypNames) == 2 {
+		funcName = goTypNames[0] + ".NewPopulated" + goTypNames[1]
+	} else if len(goTypNames) != 1 {
+		panic(fmt.Errorf("unreachable: too many dots in %v", goTypName))
+	}
+	return funcName
+}
+
+func getFuncCall(goTypName string) string {
+	funcName := getFuncName(goTypName)
+	funcCall := funcName + "(r, easy)"
+	return funcCall
+}
+
+func getCustomFuncCall(goTypName string) string {
+	funcName := getFuncName(goTypName)
+	funcCall := funcName + "(r)"
+	return funcCall
+}
+
+func (p *plugin) getEnumVal(field *descriptor.FieldDescriptorProto, goTyp string) string {
+	enum := p.ObjectNamed(field.GetTypeName()).(*generator.EnumDescriptor)
+	l := len(enum.Value)
+	values := make([]string, l)
+	for i := range enum.Value {
+		values[i] = strconv.Itoa(int(*enum.Value[i].Number))
+	}
+	arr := "[]int32{" + strings.Join(values, ",") + "}"
+	val := strings.Join([]string{generator.GoTypeToName(goTyp), `(`, arr, `[r.Intn(`, fmt.Sprintf("%d", l), `)])`}, "")
+	return val
+}
+
+func (p *plugin) GenerateField(file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto) {
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+	goTyp, _ := p.GoType(message, field)
+	fieldname := p.GetOneOfFieldName(message, field)
+	goTypName := generator.GoTypeToName(goTyp)
+	if p.IsMap(field) {
+		m := p.GoMapType(nil, field)
+		keygoTyp, _ := p.GoType(nil, m.KeyField)
+		keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
+		keygoAliasTyp, _ := p.GoType(nil, m.KeyAliasField)
+		keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
+
+		valuegoTyp, _ := p.GoType(nil, m.ValueField)
+		valuegoAliasTyp, _ := p.GoType(nil, m.ValueAliasField)
+		keytypName := generator.GoTypeToName(keygoTyp)
+		keygoAliasTyp = generator.GoTypeToName(keygoAliasTyp)
+		valuetypAliasName := generator.GoTypeToName(valuegoAliasTyp)
+
+		nullable, valuegoTyp, valuegoAliasTyp := generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
+
+		p.P(p.varGen.Next(), ` := r.Intn(10)`)
+		p.P(`this.`, fieldname, ` = make(`, m.GoType, `)`)
+		p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+		p.In()
+		keyval := ""
+		if m.KeyField.IsString() {
+			keyval = fmt.Sprintf("randString%v(r)", p.localName)
+		} else {
+			keyval = value(keytypName, m.KeyField.GetType())
+		}
+		if keygoAliasTyp != keygoTyp {
+			keyval = keygoAliasTyp + `(` + keyval + `)`
+		}
+		if m.ValueField.IsMessage() || p.IsGroup(field) {
+			s := `this.` + fieldname + `[` + keyval + `] = `
+			goTypName = generator.GoTypeToName(valuegoTyp)
+			funcCall := getFuncCall(goTypName)
+			if !nullable {
+				funcCall = `*` + funcCall
+			}
+			if valuegoTyp != valuegoAliasTyp {
+				funcCall = `(` + valuegoAliasTyp + `)(` + funcCall + `)`
+			}
+			s += funcCall
+			p.P(s)
+		} else if m.ValueField.IsEnum() {
+			s := `this.` + fieldname + `[` + keyval + `]` + ` = ` + p.getEnumVal(m.ValueField, valuegoTyp)
+			p.P(s)
+		} else if m.ValueField.IsBytes() {
+			count := p.varGen.Next()
+			p.P(count, ` := r.Intn(100)`)
+			p.P(p.varGen.Next(), ` := `, keyval)
+			p.P(`this.`, fieldname, `[`, p.varGen.Current(), `] = make(`, valuegoTyp, `, `, count, `)`)
+			p.P(`for i := 0; i < `, count, `; i++ {`)
+			p.In()
+			p.P(`this.`, fieldname, `[`, p.varGen.Current(), `][i] = byte(r.Intn(256))`)
+			p.Out()
+			p.P(`}`)
+		} else if m.ValueField.IsString() {
+			s := `this.` + fieldname + `[` + keyval + `]` + ` = ` + fmt.Sprintf("randString%v(r)", p.localName)
+			p.P(s)
+		} else {
+			p.P(p.varGen.Next(), ` := `, keyval)
+			p.P(`this.`, fieldname, `[`, p.varGen.Current(), `] = `, value(valuetypAliasName, m.ValueField.GetType()))
+			if negative(m.ValueField.GetType()) {
+				p.P(`if r.Intn(2) == 0 {`)
+				p.In()
+				p.P(`this.`, fieldname, `[`, p.varGen.Current(), `] *= -1`)
+				p.Out()
+				p.P(`}`)
+			}
+		}
+		p.Out()
+		p.P(`}`)
+	} else if field.IsMessage() || p.IsGroup(field) {
+		funcCall := getFuncCall(goTypName)
+		if field.IsRepeated() {
+			p.P(p.varGen.Next(), ` := r.Intn(5)`)
+			p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+			p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+			p.In()
+			if gogoproto.IsNullable(field) {
+				p.P(`this.`, fieldname, `[i] = `, funcCall)
+			} else {
+				p.P(p.varGen.Next(), `:= `, funcCall)
+				p.P(`this.`, fieldname, `[i] = *`, p.varGen.Current())
+			}
+			p.Out()
+			p.P(`}`)
+		} else {
+			if gogoproto.IsNullable(field) {
+				p.P(`this.`, fieldname, ` = `, funcCall)
+			} else {
+				p.P(p.varGen.Next(), `:= `, funcCall)
+				p.P(`this.`, fieldname, ` = *`, p.varGen.Current())
+			}
+		}
+	} else {
+		if field.IsEnum() {
+			val := p.getEnumVal(field, goTyp)
+			if field.IsRepeated() {
+				p.P(p.varGen.Next(), ` := r.Intn(10)`)
+				p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+				p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+				p.In()
+				p.P(`this.`, fieldname, `[i] = `, val)
+				p.Out()
+				p.P(`}`)
+			} else if !gogoproto.IsNullable(field) || proto3 {
+				p.P(`this.`, fieldname, ` = `, val)
+			} else {
+				p.P(p.varGen.Next(), ` := `, val)
+				p.P(`this.`, fieldname, ` = &`, p.varGen.Current())
+			}
+		} else if gogoproto.IsCustomType(field) {
+			funcCall := getCustomFuncCall(goTypName)
+			if field.IsRepeated() {
+				p.P(p.varGen.Next(), ` := r.Intn(10)`)
+				p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+				p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+				p.In()
+				p.P(p.varGen.Next(), `:= `, funcCall)
+				p.P(`this.`, fieldname, `[i] = *`, p.varGen.Current())
+				p.Out()
+				p.P(`}`)
+			} else if gogoproto.IsNullable(field) {
+				p.P(`this.`, fieldname, ` = `, funcCall)
+			} else {
+				p.P(p.varGen.Next(), `:= `, funcCall)
+				p.P(`this.`, fieldname, ` = *`, p.varGen.Current())
+			}
+		} else if field.IsBytes() {
+			if field.IsRepeated() {
+				p.P(p.varGen.Next(), ` := r.Intn(10)`)
+				p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+				p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+				p.In()
+				p.P(p.varGen.Next(), ` := r.Intn(100)`)
+				p.P(`this.`, fieldname, `[i] = make([]byte,`, p.varGen.Current(), `)`)
+				p.P(`for j := 0; j < `, p.varGen.Current(), `; j++ {`)
+				p.In()
+				p.P(`this.`, fieldname, `[i][j] = byte(r.Intn(256))`)
+				p.Out()
+				p.P(`}`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.P(p.varGen.Next(), ` := r.Intn(100)`)
+				p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+				p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+				p.In()
+				p.P(`this.`, fieldname, `[i] = byte(r.Intn(256))`)
+				p.Out()
+				p.P(`}`)
+			}
+		} else if field.IsString() {
+			val := fmt.Sprintf("randString%v(r)", p.localName)
+			if field.IsRepeated() {
+				p.P(p.varGen.Next(), ` := r.Intn(10)`)
+				p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+				p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+				p.In()
+				p.P(`this.`, fieldname, `[i] = `, val)
+				p.Out()
+				p.P(`}`)
+			} else if !gogoproto.IsNullable(field) || proto3 {
+				p.P(`this.`, fieldname, ` = `, val)
+			} else {
+				p.P(p.varGen.Next(), `:= `, val)
+				p.P(`this.`, fieldname, ` = &`, p.varGen.Current())
+			}
+		} else {
+			typName := generator.GoTypeToName(goTyp)
+			if field.IsRepeated() {
+				p.P(p.varGen.Next(), ` := r.Intn(10)`)
+				p.P(`this.`, fieldname, ` = make(`, goTyp, `, `, p.varGen.Current(), `)`)
+				p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+				p.In()
+				p.P(`this.`, fieldname, `[i] = `, value(typName, field.GetType()))
+				if negative(field.GetType()) {
+					p.P(`if r.Intn(2) == 0 {`)
+					p.In()
+					p.P(`this.`, fieldname, `[i] *= -1`)
+					p.Out()
+					p.P(`}`)
+				}
+				p.Out()
+				p.P(`}`)
+			} else if !gogoproto.IsNullable(field) || proto3 {
+				p.P(`this.`, fieldname, ` = `, value(typName, field.GetType()))
+				if negative(field.GetType()) {
+					p.P(`if r.Intn(2) == 0 {`)
+					p.In()
+					p.P(`this.`, fieldname, ` *= -1`)
+					p.Out()
+					p.P(`}`)
+				}
+			} else {
+				p.P(p.varGen.Next(), ` := `, value(typName, field.GetType()))
+				if negative(field.GetType()) {
+					p.P(`if r.Intn(2) == 0 {`)
+					p.In()
+					p.P(p.varGen.Current(), ` *= -1`)
+					p.Out()
+					p.P(`}`)
+				}
+				p.P(`this.`, fieldname, ` = &`, p.varGen.Current())
+			}
+		}
+	}
+}
+
+func (p *plugin) hasLoop(field *descriptor.FieldDescriptorProto, visited []*generator.Descriptor, excludes []*generator.Descriptor) *generator.Descriptor {
+	if field.IsMessage() || p.IsGroup(field) || p.IsMap(field) {
+		var fieldMessage *generator.Descriptor
+		if p.IsMap(field) {
+			m := p.GoMapType(nil, field)
+			if !m.ValueField.IsMessage() {
+				return nil
+			}
+			fieldMessage = p.ObjectNamed(m.ValueField.GetTypeName()).(*generator.Descriptor)
+		} else {
+			fieldMessage = p.ObjectNamed(field.GetTypeName()).(*generator.Descriptor)
+		}
+		fieldTypeName := generator.CamelCaseSlice(fieldMessage.TypeName())
+		for _, message := range visited {
+			messageTypeName := generator.CamelCaseSlice(message.TypeName())
+			if fieldTypeName == messageTypeName {
+				for _, e := range excludes {
+					if fieldTypeName == generator.CamelCaseSlice(e.TypeName()) {
+						return nil
+					}
+				}
+				return fieldMessage
+			}
+		}
+		for _, f := range fieldMessage.Field {
+			visited = append(visited, fieldMessage)
+			loopTo := p.hasLoop(f, visited, excludes)
+			if loopTo != nil {
+				return loopTo
+			}
+		}
+	}
+	return nil
+}
+
+func (p *plugin) loops(field *descriptor.FieldDescriptorProto, message *generator.Descriptor) int {
+	//fmt.Fprintf(os.Stderr, "loops %v %v\n", field.GetTypeName(), generator.CamelCaseSlice(message.TypeName()))
+	excludes := []*generator.Descriptor{}
+	loops := 0
+	for {
+		visited := []*generator.Descriptor{}
+		loopTo := p.hasLoop(field, visited, excludes)
+		if loopTo == nil {
+			break
+		}
+		//fmt.Fprintf(os.Stderr, "loopTo %v\n", generator.CamelCaseSlice(loopTo.TypeName()))
+		excludes = append(excludes, loopTo)
+		loops++
+	}
+	return loops
+}
+
+func (p *plugin) Generate(file *generator.FileDescriptor) {
+	p.atleastOne = false
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.varGen = NewVarGen()
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+
+	p.localName = generator.FileName(file)
+	protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = p.NewImport("github.com/golang/protobuf/proto")
+	}
+
+	for _, message := range file.Messages() {
+		if !gogoproto.HasPopulate(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		p.atleastOne = true
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		loopLevels := make([]int, len(message.Field))
+		maxLoopLevel := 0
+		for i, field := range message.Field {
+			loopLevels[i] = p.loops(field, message)
+			if loopLevels[i] > maxLoopLevel {
+				maxLoopLevel = loopLevels[i]
+			}
+		}
+		ranTotal := 0
+		for i := range loopLevels {
+			ranTotal += int(math.Pow10(maxLoopLevel - loopLevels[i]))
+		}
+		p.P(`func NewPopulated`, ccTypeName, `(r randy`, p.localName, `, easy bool) *`, ccTypeName, ` {`)
+		p.In()
+		p.P(`this := &`, ccTypeName, `{}`)
+		if gogoproto.IsUnion(message.File(), message.DescriptorProto) && len(message.Field) > 0 {
+			p.P(`fieldNum := r.Intn(`, fmt.Sprintf("%d", ranTotal), `)`)
+			p.P(`switch fieldNum {`)
+			k := 0
+			for i, field := range message.Field {
+				is := []string{}
+				ran := int(math.Pow10(maxLoopLevel - loopLevels[i]))
+				for j := 0; j < ran; j++ {
+					is = append(is, fmt.Sprintf("%d", j+k))
+				}
+				k += ran
+				p.P(`case `, strings.Join(is, ","), `:`)
+				p.In()
+				p.GenerateField(file, message, field)
+				p.Out()
+			}
+			p.P(`}`)
+		} else {
+			var maxFieldNumber int32
+			oneofs := make(map[string]struct{})
+			for fieldIndex, field := range message.Field {
+				if field.GetNumber() > maxFieldNumber {
+					maxFieldNumber = field.GetNumber()
+				}
+				oneof := field.OneofIndex != nil
+				if !oneof {
+					if field.IsRequired() || (!gogoproto.IsNullable(field) && !field.IsRepeated()) || (proto3 && !field.IsMessage()) {
+						p.GenerateField(file, message, field)
+					} else {
+						if loopLevels[fieldIndex] > 0 {
+							p.P(`if r.Intn(10) == 0 {`)
+						} else {
+							p.P(`if r.Intn(10) != 0 {`)
+						}
+						p.In()
+						p.GenerateField(file, message, field)
+						p.Out()
+						p.P(`}`)
+					}
+				} else {
+					fieldname := p.GetFieldName(message, field)
+					if _, ok := oneofs[fieldname]; ok {
+						continue
+					} else {
+						oneofs[fieldname] = struct{}{}
+					}
+					fieldNumbers := []int32{}
+					for _, f := range message.Field {
+						fname := p.GetFieldName(message, f)
+						if fname == fieldname {
+							fieldNumbers = append(fieldNumbers, f.GetNumber())
+						}
+					}
+
+					p.P(`oneofNumber_`, fieldname, ` := `, fmt.Sprintf("%#v", fieldNumbers), `[r.Intn(`, strconv.Itoa(len(fieldNumbers)), `)]`)
+					p.P(`switch oneofNumber_`, fieldname, ` {`)
+					for _, f := range message.Field {
+						fname := p.GetFieldName(message, f)
+						if fname != fieldname {
+							continue
+						}
+						p.P(`case `, strconv.Itoa(int(f.GetNumber())), `:`)
+						p.In()
+						ccTypeName := p.OneOfTypeName(message, f)
+						p.P(`this.`, fname, ` = NewPopulated`, ccTypeName, `(r, easy)`)
+						p.Out()
+					}
+					p.P(`}`)
+				}
+			}
+			if message.DescriptorProto.HasExtension() {
+				p.P(`if !easy && r.Intn(10) != 0 {`)
+				p.In()
+				p.P(`l := r.Intn(5)`)
+				p.P(`for i := 0; i < l; i++ {`)
+				p.In()
+				if len(message.DescriptorProto.GetExtensionRange()) > 1 {
+					p.P(`eIndex := r.Intn(`, strconv.Itoa(len(message.DescriptorProto.GetExtensionRange())), `)`)
+					p.P(`fieldNumber := 0`)
+					p.P(`switch eIndex {`)
+					for i, e := range message.DescriptorProto.GetExtensionRange() {
+						p.P(`case `, strconv.Itoa(i), `:`)
+						p.In()
+						p.P(`fieldNumber = r.Intn(`, strconv.Itoa(int(e.GetEnd()-e.GetStart())), `) + `, strconv.Itoa(int(e.GetStart())))
+						p.Out()
+						if e.GetEnd() > maxFieldNumber {
+							maxFieldNumber = e.GetEnd()
+						}
+					}
+					p.P(`}`)
+				} else {
+					e := message.DescriptorProto.GetExtensionRange()[0]
+					p.P(`fieldNumber := r.Intn(`, strconv.Itoa(int(e.GetEnd()-e.GetStart())), `) + `, strconv.Itoa(int(e.GetStart())))
+					if e.GetEnd() > maxFieldNumber {
+						maxFieldNumber = e.GetEnd()
+					}
+				}
+				p.P(`wire := r.Intn(4)`)
+				p.P(`if wire == 3 { wire = 5 }`)
+				p.P(`data := randField`, p.localName, `(nil, r, fieldNumber, wire)`)
+				p.P(protoPkg.Use(), `.SetRawExtension(this, int32(fieldNumber), data)`)
+				p.Out()
+				p.P(`}`)
+				p.Out()
+				p.P(`}`)
+			}
+
+			if maxFieldNumber < (1 << 10) {
+				p.P(`if !easy && r.Intn(10) != 0 {`)
+				p.In()
+				if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+					p.P(`this.XXX_unrecognized = randUnrecognized`, p.localName, `(r, `, strconv.Itoa(int(maxFieldNumber+1)), `)`)
+				}
+				p.Out()
+				p.P(`}`)
+			}
+		}
+		p.P(`return this`)
+		p.Out()
+		p.P(`}`)
+		p.P(``)
+
+		//Generate NewPopulated functions for oneof fields
+		m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
+		for _, f := range m.Field {
+			oneof := f.OneofIndex != nil
+			if !oneof {
+				continue
+			}
+			ccTypeName := p.OneOfTypeName(message, f)
+			p.P(`func NewPopulated`, ccTypeName, `(r randy`, p.localName, `, easy bool) *`, ccTypeName, ` {`)
+			p.In()
+			p.P(`this := &`, ccTypeName, `{}`)
+			vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly(f)
+			p.GenerateField(file, message, f)
+			p.P(`return this`)
+			p.Out()
+			p.P(`}`)
+		}
+	}
+
+	if !p.atleastOne {
+		return
+	}
+
+	p.P(`type randy`, p.localName, ` interface {`)
+	p.In()
+	p.P(`Float32() float32`)
+	p.P(`Float64() float64`)
+	p.P(`Int63() int64`)
+	p.P(`Int31() int32`)
+	p.P(`Uint32() uint32`)
+	p.P(`Intn(n int) int`)
+	p.Out()
+	p.P(`}`)
+
+	p.P(`func randUTF8Rune`, p.localName, `(r randy`, p.localName, `) rune {`)
+	p.In()
+	p.P(`ru := r.Intn(62)`)
+	p.P(`if ru < 10 {`)
+	p.In()
+	p.P(`return rune(ru+48)`)
+	p.Out()
+	p.P(`} else if ru < 36 {`)
+	p.In()
+	p.P(`return rune(ru+55)`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return rune(ru+61)`)
+	p.Out()
+	p.P(`}`)
+
+	p.P(`func randString`, p.localName, `(r randy`, p.localName, `) string {`)
+	p.In()
+	p.P(p.varGen.Next(), ` := r.Intn(100)`)
+	p.P(`tmps := make([]rune, `, p.varGen.Current(), `)`)
+	p.P(`for i := 0; i < `, p.varGen.Current(), `; i++ {`)
+	p.In()
+	p.P(`tmps[i] = randUTF8Rune`, p.localName, `(r)`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return string(tmps)`)
+	p.Out()
+	p.P(`}`)
+
+	p.P(`func randUnrecognized`, p.localName, `(r randy`, p.localName, `, maxFieldNumber int) (data []byte) {`)
+	p.In()
+	p.P(`l := r.Intn(5)`)
+	p.P(`for i := 0; i < l; i++ {`)
+	p.In()
+	p.P(`wire := r.Intn(4)`)
+	p.P(`if wire == 3 { wire = 5 }`)
+	p.P(`fieldNumber := maxFieldNumber + r.Intn(100)`)
+	p.P(`data = randField`, p.localName, `(data, r, fieldNumber, wire)`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return data`)
+	p.Out()
+	p.P(`}`)
+
+	p.P(`func randField`, p.localName, `(data []byte, r randy`, p.localName, `, fieldNumber int, wire int) []byte {`)
+	p.In()
+	p.P(`key := uint32(fieldNumber)<<3 | uint32(wire)`)
+	p.P(`switch wire {`)
+	p.P(`case 0:`)
+	p.In()
+	p.P(`data = encodeVarintPopulate`, p.localName, `(data, uint64(key))`)
+	p.P(p.varGen.Next(), ` := r.Int63()`)
+	p.P(`if r.Intn(2) == 0 {`)
+	p.In()
+	p.P(p.varGen.Current(), ` *= -1`)
+	p.Out()
+	p.P(`}`)
+	p.P(`data = encodeVarintPopulate`, p.localName, `(data, uint64(`, p.varGen.Current(), `))`)
+	p.Out()
+	p.P(`case 1:`)
+	p.In()
+	p.P(`data = encodeVarintPopulate`, p.localName, `(data, uint64(key))`)
+	p.P(`data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))`)
+	p.Out()
+	p.P(`case 2:`)
+	p.In()
+	p.P(`data = encodeVarintPopulate`, p.localName, `(data, uint64(key))`)
+	p.P(`ll := r.Intn(100)`)
+	p.P(`data = encodeVarintPopulate`, p.localName, `(data, uint64(ll))`)
+	p.P(`for j := 0; j < ll; j++ {`)
+	p.In()
+	p.P(`data = append(data, byte(r.Intn(256)))`)
+	p.Out()
+	p.P(`}`)
+	p.Out()
+	p.P(`default:`)
+	p.In()
+	p.P(`data = encodeVarintPopulate`, p.localName, `(data, uint64(key))`)
+	p.P(`data = append(data, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))`)
+	p.Out()
+	p.P(`}`)
+	p.P(`return data`)
+	p.Out()
+	p.P(`}`)
+
+	p.P(`func encodeVarintPopulate`, p.localName, `(data []byte, v uint64) []byte {`)
+	p.In()
+	p.P(`for v >= 1<<7 {`)
+	p.In()
+	p.P(`data = append(data, uint8(uint64(v)&0x7f|0x80))`)
+	p.P(`v >>= 7`)
+	p.Out()
+	p.P(`}`)
+	p.P(`data = append(data, uint8(v))`)
+	p.P(`return data`)
+	p.Out()
+	p.P(`}`)
+
+}
+
+func init() {
+	generator.RegisterPlugin(NewPlugin())
+}

+ 599 - 0
vendor/github.com/gogo/protobuf/plugin/size/size.go

@@ -0,0 +1,599 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The size plugin generates a Size or ProtoSize method for each message.
+This is useful with the MarshalTo method generated by the marshalto plugin and the
+gogoproto.marshaler and gogoproto.marshaler_all extensions.
+
+It is enabled by the following extensions:
+
+  - sizer
+  - sizer_all
+  - protosizer
+  - protosizer_all
+
+The size plugin also generates a test given it is enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+And a benchmark given it is enabled using one of the following extensions:
+
+  - benchgen
+  - benchgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  option (gogoproto.sizer_all) = true;
+
+  message B {
+	option (gogoproto.description) = true;
+	optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true];
+	repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false];
+  }
+
+given to the size plugin, will generate the following code:
+
+  func (m *B) Size() (n int) {
+	var l int
+	_ = l
+	l = m.A.Size()
+	n += 1 + l + sovExample(uint64(l))
+	if len(m.G) > 0 {
+		for _, e := range m.G {
+			l = e.Size()
+			n += 1 + l + sovExample(uint64(l))
+		}
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+  }
+
+and the following test code:
+
+	func TestBSize(t *testing5.T) {
+		popr := math_rand5.New(math_rand5.NewSource(time5.Now().UnixNano()))
+		p := NewPopulatedB(popr, true)
+		data, err := github_com_gogo_protobuf_proto2.Marshal(p)
+		if err != nil {
+			panic(err)
+		}
+		size := p.Size()
+		if len(data) != size {
+			t.Fatalf("size %v != marshalled size %v", size, len(data))
+		}
+	}
+
+	func BenchmarkBSize(b *testing5.B) {
+		popr := math_rand5.New(math_rand5.NewSource(616))
+		total := 0
+		pops := make([]*B, 1000)
+		for i := 0; i < 1000; i++ {
+			pops[i] = NewPopulatedB(popr, false)
+		}
+		b.ResetTimer()
+		for i := 0; i < b.N; i++ {
+			total += pops[i%1000].Size()
+		}
+		b.SetBytes(int64(total / b.N))
+	}
+
+The sovExample function is a size of varint function for the example.pb.go file.
+
+*/
+package size
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/proto"
+	descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"github.com/gogo/protobuf/vanity"
+)
+
+type size struct {
+	*generator.Generator
+	generator.PluginImports
+	atleastOne bool
+	localName  string
+}
+
+func NewSize() *size {
+	return &size{}
+}
+
+func (p *size) Name() string {
+	return "size"
+}
+
+func (p *size) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func wireToType(wire string) int {
+	switch wire {
+	case "fixed64":
+		return proto.WireFixed64
+	case "fixed32":
+		return proto.WireFixed32
+	case "varint":
+		return proto.WireVarint
+	case "bytes":
+		return proto.WireBytes
+	case "group":
+		return proto.WireBytes
+	case "zigzag32":
+		return proto.WireVarint
+	case "zigzag64":
+		return proto.WireVarint
+	}
+	panic("unreachable")
+}
+
+func keySize(fieldNumber int32, wireType int) int {
+	x := uint32(fieldNumber)<<3 | uint32(wireType)
+	size := 0
+	for size = 0; x > 127; size++ {
+		x >>= 7
+	}
+	size++
+	return size
+}
+
+func (p *size) sizeVarint() {
+	p.P(`
+	func sov`, p.localName, `(x uint64) (n int) {
+		for {
+			n++
+			x >>= 7
+			if x == 0 {
+				break
+			}
+		}
+		return n
+	}`)
+}
+
+func (p *size) sizeZigZag() {
+	p.P(`func soz`, p.localName, `(x uint64) (n int) {
+		return sov`, p.localName, `(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+	}`)
+}
+
+func (p *size) generateField(proto3 bool, file *generator.FileDescriptor, message *generator.Descriptor, field *descriptor.FieldDescriptorProto, sizeName string) {
+	fieldname := p.GetOneOfFieldName(message, field)
+	nullable := gogoproto.IsNullable(field)
+	repeated := field.IsRepeated()
+	doNilCheck := gogoproto.NeedsNilCheck(proto3, field)
+	if repeated {
+		p.P(`if len(m.`, fieldname, `) > 0 {`)
+		p.In()
+	} else if doNilCheck {
+		p.P(`if m.`, fieldname, ` != nil {`)
+		p.In()
+	}
+	packed := field.IsPacked()
+	_, wire := p.GoType(message, field)
+	wireType := wireToType(wire)
+	fieldNumber := field.GetNumber()
+	if packed {
+		wireType = proto.WireBytes
+	}
+	key := keySize(fieldNumber, wireType)
+	switch *field.Type {
+	case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
+		descriptor.FieldDescriptorProto_TYPE_FIXED64,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+		if packed {
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(len(m.`, fieldname, `)*8))`, `+len(m.`, fieldname, `)*8`)
+		} else if repeated {
+			p.P(`n+=`, strconv.Itoa(key+8), `*len(m.`, fieldname, `)`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key+8))
+			p.Out()
+			p.P(`}`)
+		} else if nullable {
+			p.P(`n+=`, strconv.Itoa(key+8))
+		} else {
+			p.P(`n+=`, strconv.Itoa(key+8))
+		}
+	case descriptor.FieldDescriptorProto_TYPE_FLOAT,
+		descriptor.FieldDescriptorProto_TYPE_FIXED32,
+		descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+		if packed {
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(len(m.`, fieldname, `)*4))`, `+len(m.`, fieldname, `)*4`)
+		} else if repeated {
+			p.P(`n+=`, strconv.Itoa(key+4), `*len(m.`, fieldname, `)`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key+4))
+			p.Out()
+			p.P(`}`)
+		} else if nullable {
+			p.P(`n+=`, strconv.Itoa(key+4))
+		} else {
+			p.P(`n+=`, strconv.Itoa(key+4))
+		}
+	case descriptor.FieldDescriptorProto_TYPE_INT64,
+		descriptor.FieldDescriptorProto_TYPE_UINT64,
+		descriptor.FieldDescriptorProto_TYPE_UINT32,
+		descriptor.FieldDescriptorProto_TYPE_ENUM,
+		descriptor.FieldDescriptorProto_TYPE_INT32:
+		if packed {
+			p.P(`l = 0`)
+			p.P(`for _, e := range m.`, fieldname, ` {`)
+			p.In()
+			p.P(`l+=sov`, p.localName, `(uint64(e))`)
+			p.Out()
+			p.P(`}`)
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(l))+l`)
+		} else if repeated {
+			p.P(`for _, e := range m.`, fieldname, ` {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(e))`)
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(m.`, fieldname, `))`)
+			p.Out()
+			p.P(`}`)
+		} else if nullable {
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(*m.`, fieldname, `))`)
+		} else {
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(m.`, fieldname, `))`)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_BOOL:
+		if packed {
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(len(m.`, fieldname, `)))`, `+len(m.`, fieldname, `)*1`)
+		} else if repeated {
+			p.P(`n+=`, strconv.Itoa(key+1), `*len(m.`, fieldname, `)`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key+1))
+			p.Out()
+			p.P(`}`)
+		} else if nullable {
+			p.P(`n+=`, strconv.Itoa(key+1))
+		} else {
+			p.P(`n+=`, strconv.Itoa(key+1))
+		}
+	case descriptor.FieldDescriptorProto_TYPE_STRING:
+		if repeated {
+			p.P(`for _, s := range m.`, fieldname, ` { `)
+			p.In()
+			p.P(`l = len(s)`)
+			p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`l=len(m.`, fieldname, `)`)
+			p.P(`if l > 0 {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+			p.Out()
+			p.P(`}`)
+		} else if nullable {
+			p.P(`l=len(*m.`, fieldname, `)`)
+			p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+		} else {
+			p.P(`l=len(m.`, fieldname, `)`)
+			p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_GROUP:
+		panic(fmt.Errorf("size does not support group %v", fieldname))
+	case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+		if p.IsMap(field) {
+			m := p.GoMapType(nil, field)
+			_, keywire := p.GoType(nil, m.KeyAliasField)
+			valuegoTyp, _ := p.GoType(nil, m.ValueField)
+			valuegoAliasTyp, valuewire := p.GoType(nil, m.ValueAliasField)
+			_, fieldwire := p.GoType(nil, field)
+
+			nullable, valuegoTyp, valuegoAliasTyp = generator.GoMapValueTypes(field, m.ValueField, valuegoTyp, valuegoAliasTyp)
+
+			fieldKeySize := keySize(field.GetNumber(), wireToType(fieldwire))
+			keyKeySize := keySize(1, wireToType(keywire))
+			valueKeySize := keySize(2, wireToType(valuewire))
+			p.P(`for k, v := range m.`, fieldname, ` { `)
+			p.In()
+			p.P(`_ = k`)
+			p.P(`_ = v`)
+			sum := []string{strconv.Itoa(keyKeySize)}
+			switch m.KeyField.GetType() {
+			case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
+				descriptor.FieldDescriptorProto_TYPE_FIXED64,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+				sum = append(sum, `8`)
+			case descriptor.FieldDescriptorProto_TYPE_FLOAT,
+				descriptor.FieldDescriptorProto_TYPE_FIXED32,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+				sum = append(sum, `4`)
+			case descriptor.FieldDescriptorProto_TYPE_INT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT32,
+				descriptor.FieldDescriptorProto_TYPE_ENUM,
+				descriptor.FieldDescriptorProto_TYPE_INT32:
+				sum = append(sum, `sov`+p.localName+`(uint64(k))`)
+			case descriptor.FieldDescriptorProto_TYPE_BOOL:
+				sum = append(sum, `1`)
+			case descriptor.FieldDescriptorProto_TYPE_STRING,
+				descriptor.FieldDescriptorProto_TYPE_BYTES:
+				sum = append(sum, `len(k)+sov`+p.localName+`(uint64(len(k)))`)
+			case descriptor.FieldDescriptorProto_TYPE_SINT32,
+				descriptor.FieldDescriptorProto_TYPE_SINT64:
+				sum = append(sum, `soz`+p.localName+`(uint64(k))`)
+			}
+			sum = append(sum, strconv.Itoa(valueKeySize))
+			switch m.ValueField.GetType() {
+			case descriptor.FieldDescriptorProto_TYPE_DOUBLE,
+				descriptor.FieldDescriptorProto_TYPE_FIXED64,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED64:
+				sum = append(sum, strconv.Itoa(8))
+			case descriptor.FieldDescriptorProto_TYPE_FLOAT,
+				descriptor.FieldDescriptorProto_TYPE_FIXED32,
+				descriptor.FieldDescriptorProto_TYPE_SFIXED32:
+				sum = append(sum, strconv.Itoa(4))
+			case descriptor.FieldDescriptorProto_TYPE_INT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT64,
+				descriptor.FieldDescriptorProto_TYPE_UINT32,
+				descriptor.FieldDescriptorProto_TYPE_ENUM,
+				descriptor.FieldDescriptorProto_TYPE_INT32:
+				sum = append(sum, `sov`+p.localName+`(uint64(v))`)
+			case descriptor.FieldDescriptorProto_TYPE_BOOL:
+				sum = append(sum, `1`)
+			case descriptor.FieldDescriptorProto_TYPE_STRING,
+				descriptor.FieldDescriptorProto_TYPE_BYTES:
+				sum = append(sum, `len(v)+sov`+p.localName+`(uint64(len(v)))`)
+			case descriptor.FieldDescriptorProto_TYPE_SINT32,
+				descriptor.FieldDescriptorProto_TYPE_SINT64:
+				sum = append(sum, `soz`+p.localName+`(uint64(v))`)
+			case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
+				if nullable {
+					p.P(`l = 0`)
+					p.P(`if v != nil {`)
+					p.In()
+					if valuegoTyp != valuegoAliasTyp {
+						p.P(`l = ((`, valuegoTyp, `)(v)).`, sizeName, `()`)
+					} else {
+						p.P(`l = v.`, sizeName, `()`)
+					}
+					p.Out()
+					p.P(`}`)
+				} else {
+					if valuegoTyp != valuegoAliasTyp {
+						p.P(`l = ((*`, valuegoTyp, `)(&v)).`, sizeName, `()`)
+					} else {
+						p.P(`l = v.`, sizeName, `()`)
+					}
+				}
+				sum = append(sum, `l+sov`+p.localName+`(uint64(l))`)
+			}
+			p.P(`mapEntrySize := `, strings.Join(sum, "+"))
+			p.P(`n+=mapEntrySize+`, fieldKeySize, `+sov`, p.localName, `(uint64(mapEntrySize))`)
+			p.Out()
+			p.P(`}`)
+		} else if repeated {
+			p.P(`for _, e := range m.`, fieldname, ` { `)
+			p.In()
+			p.P(`l=e.`, sizeName, `()`)
+			p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+			p.Out()
+			p.P(`}`)
+		} else {
+			p.P(`l=m.`, fieldname, `.`, sizeName, `()`)
+			p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+		}
+	case descriptor.FieldDescriptorProto_TYPE_BYTES:
+		if !gogoproto.IsCustomType(field) {
+			if repeated {
+				p.P(`for _, b := range m.`, fieldname, ` { `)
+				p.In()
+				p.P(`l = len(b)`)
+				p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+				p.Out()
+				p.P(`}`)
+			} else if proto3 {
+				p.P(`l=len(m.`, fieldname, `)`)
+				p.P(`if l > 0 {`)
+				p.In()
+				p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.P(`l=len(m.`, fieldname, `)`)
+				p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+			}
+		} else {
+			if repeated {
+				p.P(`for _, e := range m.`, fieldname, ` { `)
+				p.In()
+				p.P(`l=e.`, sizeName, `()`)
+				p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+				p.Out()
+				p.P(`}`)
+			} else {
+				p.P(`l=m.`, fieldname, `.`, sizeName, `()`)
+				p.P(`n+=`, strconv.Itoa(key), `+l+sov`, p.localName, `(uint64(l))`)
+			}
+		}
+	case descriptor.FieldDescriptorProto_TYPE_SINT32,
+		descriptor.FieldDescriptorProto_TYPE_SINT64:
+		if packed {
+			p.P(`l = 0`)
+			p.P(`for _, e := range m.`, fieldname, ` {`)
+			p.In()
+			p.P(`l+=soz`, p.localName, `(uint64(e))`)
+			p.Out()
+			p.P(`}`)
+			p.P(`n+=`, strconv.Itoa(key), `+sov`, p.localName, `(uint64(l))+l`)
+		} else if repeated {
+			p.P(`for _, e := range m.`, fieldname, ` {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(e))`)
+			p.Out()
+			p.P(`}`)
+		} else if proto3 {
+			p.P(`if m.`, fieldname, ` != 0 {`)
+			p.In()
+			p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(m.`, fieldname, `))`)
+			p.Out()
+			p.P(`}`)
+		} else if nullable {
+			p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(*m.`, fieldname, `))`)
+		} else {
+			p.P(`n+=`, strconv.Itoa(key), `+soz`, p.localName, `(uint64(m.`, fieldname, `))`)
+		}
+	default:
+		panic("not implemented")
+	}
+	if repeated || doNilCheck {
+		p.Out()
+		p.P(`}`)
+	}
+}
+
+func (p *size) Generate(file *generator.FileDescriptor) {
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.atleastOne = false
+	p.localName = generator.FileName(file)
+	protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = p.NewImport("github.com/golang/protobuf/proto")
+	}
+	for _, message := range file.Messages() {
+		sizeName := ""
+		if gogoproto.IsSizer(file.FileDescriptorProto, message.DescriptorProto) {
+			sizeName = "Size"
+		} else if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
+			sizeName = "ProtoSize"
+		} else {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		p.atleastOne = true
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		p.P(`func (m *`, ccTypeName, `) `, sizeName, `() (n int) {`)
+		p.In()
+		p.P(`var l int`)
+		p.P(`_ = l`)
+		oneofs := make(map[string]struct{})
+		for _, field := range message.Field {
+			oneof := field.OneofIndex != nil
+			if !oneof {
+				proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+				p.generateField(proto3, file, message, field, sizeName)
+			} else {
+				fieldname := p.GetFieldName(message, field)
+				if _, ok := oneofs[fieldname]; ok {
+					continue
+				} else {
+					oneofs[fieldname] = struct{}{}
+				}
+				p.P(`if m.`, fieldname, ` != nil {`)
+				p.In()
+				p.P(`n+=m.`, fieldname, `.`, sizeName, `()`)
+				p.Out()
+				p.P(`}`)
+			}
+		}
+		if message.DescriptorProto.HasExtension() {
+			p.P(`if m.XXX_extensions != nil {`)
+			p.In()
+			if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
+				p.P(`n += `, protoPkg.Use(), `.SizeOfExtensionMap(m.XXX_extensions)`)
+			} else {
+				p.P(`n+=len(m.XXX_extensions)`)
+			}
+			p.Out()
+			p.P(`}`)
+		}
+		if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P(`if m.XXX_unrecognized != nil {`)
+			p.In()
+			p.P(`n+=len(m.XXX_unrecognized)`)
+			p.Out()
+			p.P(`}`)
+		}
+		p.P(`return n`)
+		p.Out()
+		p.P(`}`)
+		p.P()
+
+		//Generate Size methods for oneof fields
+		m := proto.Clone(message.DescriptorProto).(*descriptor.DescriptorProto)
+		for _, f := range m.Field {
+			oneof := f.OneofIndex != nil
+			if !oneof {
+				continue
+			}
+			ccTypeName := p.OneOfTypeName(message, f)
+			p.P(`func (m *`, ccTypeName, `) `, sizeName, `() (n int) {`)
+			p.In()
+			p.P(`var l int`)
+			p.P(`_ = l`)
+			vanity.TurnOffNullableForNativeTypesWithoutDefaultsOnly(f)
+			p.generateField(false, file, message, f, sizeName)
+			p.P(`return n`)
+			p.Out()
+			p.P(`}`)
+		}
+	}
+
+	if !p.atleastOne {
+		return
+	}
+
+	p.sizeVarint()
+	p.sizeZigZag()
+
+}
+
+func init() {
+	generator.RegisterPlugin(NewSize())
+}

+ 132 - 0
vendor/github.com/gogo/protobuf/plugin/size/sizetest.go

@@ -0,0 +1,132 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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 size
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/plugin/testgen"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+)
+
+type test struct {
+	*generator.Generator
+}
+
+func NewTest(g *generator.Generator) testgen.TestPlugin {
+	return &test{g}
+}
+
+func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool {
+	used := false
+	randPkg := imports.NewImport("math/rand")
+	timePkg := imports.NewImport("time")
+	testingPkg := imports.NewImport("testing")
+	protoPkg := imports.NewImport("github.com/gogo/protobuf/proto")
+	if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) {
+		protoPkg = imports.NewImport("github.com/golang/protobuf/proto")
+	}
+	for _, message := range file.Messages() {
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		sizeName := ""
+		if gogoproto.IsSizer(file.FileDescriptorProto, message.DescriptorProto) {
+			sizeName = "Size"
+		} else if gogoproto.IsProtoSizer(file.FileDescriptorProto, message.DescriptorProto) {
+			sizeName = "ProtoSize"
+		} else {
+			continue
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+
+		if gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) {
+			used = true
+			p.P(`func Test`, ccTypeName, sizeName, `(t *`, testingPkg.Use(), `.T) {`)
+			p.In()
+			p.P(`seed := `, timePkg.Use(), `.Now().UnixNano()`)
+			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(seed))`)
+			p.P(`p := NewPopulated`, ccTypeName, `(popr, true)`)
+			p.P(`size2 := `, protoPkg.Use(), `.Size(p)`)
+			p.P(`data, err := `, protoPkg.Use(), `.Marshal(p)`)
+			p.P(`if err != nil {`)
+			p.In()
+			p.P(`t.Fatalf("seed = %d, err = %v", seed, err)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`size := p.`, sizeName, `()`)
+			p.P(`if len(data) != size {`)
+			p.In()
+			p.P(`t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(data))`)
+			p.Out()
+			p.P(`}`)
+			p.P(`if size2 != size {`)
+			p.In()
+			p.P(`t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`size3 := `, protoPkg.Use(), `.Size(p)`)
+			p.P(`if size3 != size {`)
+			p.In()
+			p.P(`t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3)`)
+			p.Out()
+			p.P(`}`)
+			p.Out()
+			p.P(`}`)
+			p.P()
+		}
+
+		if gogoproto.HasBenchGen(file.FileDescriptorProto, message.DescriptorProto) {
+			used = true
+			p.P(`func Benchmark`, ccTypeName, sizeName, `(b *`, testingPkg.Use(), `.B) {`)
+			p.In()
+			p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(616))`)
+			p.P(`total := 0`)
+			p.P(`pops := make([]*`, ccTypeName, `, 1000)`)
+			p.P(`for i := 0; i < 1000; i++ {`)
+			p.In()
+			p.P(`pops[i] = NewPopulated`, ccTypeName, `(popr, false)`)
+			p.Out()
+			p.P(`}`)
+			p.P(`b.ResetTimer()`)
+			p.P(`for i := 0; i < b.N; i++ {`)
+			p.In()
+			p.P(`total += pops[i%1000].`, sizeName, `()`)
+			p.Out()
+			p.P(`}`)
+			p.P(`b.SetBytes(int64(total / b.N))`)
+			p.Out()
+			p.P(`}`)
+			p.P()
+		}
+
+	}
+	return used
+}
+
+func init() {
+	testgen.RegisterTestPlugin(NewTest)
+}

+ 293 - 0
vendor/github.com/gogo/protobuf/plugin/stringer/stringer.go

@@ -0,0 +1,293 @@
+// Copyright (c) 2013, Vastech SA (PTY) LTD. All rights reserved.
+// http://github.com/gogo/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.
+//
+// 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.
+
+/*
+The stringer plugin generates a String method for each message.
+
+It is enabled by the following extensions:
+
+  - stringer
+  - stringer_all
+
+The stringer plugin also generates a test given it is enabled using one of the following extensions:
+
+  - testgen
+  - testgen_all
+
+Let us look at:
+
+  github.com/gogo/protobuf/test/example/example.proto
+
+Btw all the output can be seen at:
+
+  github.com/gogo/protobuf/test/example/*
+
+The following message:
+
+  option (gogoproto.goproto_stringer_all) = false;
+  option (gogoproto.stringer_all) =  true;
+
+  message A {
+	optional string Description = 1 [(gogoproto.nullable) = false];
+	optional int64 Number = 2 [(gogoproto.nullable) = false];
+	optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false];
+  }
+
+given to the stringer stringer, will generate the following code:
+
+  func (this *A) String() string {
+	if this == nil {
+		return "nil"
+	}
+	s := strings.Join([]string{`&A{`,
+		`Description:` + fmt.Sprintf("%v", this.Description) + `,`,
+		`Number:` + fmt.Sprintf("%v", this.Number) + `,`,
+		`Id:` + fmt.Sprintf("%v", this.Id) + `,`,
+		`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
+		`}`,
+	}, "")
+	return s
+  }
+
+and the following test code:
+
+	func TestAStringer(t *testing4.T) {
+		popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano()))
+		p := NewPopulatedA(popr, false)
+		s1 := p.String()
+		s2 := fmt1.Sprintf("%v", p)
+		if s1 != s2 {
+			t.Fatalf("String want %v got %v", s1, s2)
+		}
+	}
+
+Typically fmt.Printf("%v") will stop to print when it reaches a pointer and
+not print their values, while the generated String method will always print all values, recursively.
+
+*/
+package stringer
+
+import (
+	"github.com/gogo/protobuf/gogoproto"
+	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
+	"strings"
+)
+
+type stringer struct {
+	*generator.Generator
+	generator.PluginImports
+	atleastOne bool
+	localName  string
+}
+
+func NewStringer() *stringer {
+	return &stringer{}
+}
+
+func (p *stringer) Name() string {
+	return "stringer"
+}
+
+func (p *stringer) Init(g *generator.Generator) {
+	p.Generator = g
+}
+
+func (p *stringer) Generate(file *generator.FileDescriptor) {
+	proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
+	p.PluginImports = generator.NewPluginImports(p.Generator)
+	p.atleastOne = false
+
+	p.localName = generator.FileName(file)
+
+	fmtPkg := p.NewImport("fmt")
+	stringsPkg := p.NewImport("strings")
+	reflectPkg := p.NewImport("reflect")
+	sortKeysPkg := p.NewImport("github.com/gogo/protobuf/sortkeys")
+	for _, message := range file.Messages() {
+		if !gogoproto.IsStringer(file.FileDescriptorProto, message.DescriptorProto) {
+			continue
+		}
+		if gogoproto.EnabledGoStringer(file.FileDescriptorProto, message.DescriptorProto) {
+			panic("old string method needs to be disabled, please use gogoproto.goproto_stringer or gogoproto.goproto_stringer_all and set it to false")
+		}
+		if message.DescriptorProto.GetOptions().GetMapEntry() {
+			continue
+		}
+		p.atleastOne = true
+		ccTypeName := generator.CamelCaseSlice(message.TypeName())
+		p.P(`func (this *`, ccTypeName, `) String() string {`)
+		p.In()
+		p.P(`if this == nil {`)
+		p.In()
+		p.P(`return "nil"`)
+		p.Out()
+		p.P(`}`)
+		for _, field := range message.Field {
+			if !p.IsMap(field) {
+				continue
+			}
+			fieldname := p.GetFieldName(message, field)
+
+			m := p.GoMapType(nil, field)
+			mapgoTyp, keyField, keyAliasField := m.GoType, m.KeyField, m.KeyAliasField
+			keysName := `keysFor` + fieldname
+			keygoTyp, _ := p.GoType(nil, keyField)
+			keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
+			keygoAliasTyp, _ := p.GoType(nil, keyAliasField)
+			keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
+			keyCapTyp := generator.CamelCase(keygoTyp)
+			p.P(keysName, ` := make([]`, keygoTyp, `, 0, len(this.`, fieldname, `))`)
+			p.P(`for k, _ := range this.`, fieldname, ` {`)
+			p.In()
+			if keygoAliasTyp == keygoTyp {
+				p.P(keysName, ` = append(`, keysName, `, k)`)
+			} else {
+				p.P(keysName, ` = append(`, keysName, `, `, keygoTyp, `(k))`)
+			}
+			p.Out()
+			p.P(`}`)
+			p.P(sortKeysPkg.Use(), `.`, keyCapTyp, `s(`, keysName, `)`)
+			mapName := `mapStringFor` + fieldname
+			p.P(mapName, ` := "`, mapgoTyp, `{"`)
+			p.P(`for _, k := range `, keysName, ` {`)
+			p.In()
+			if keygoAliasTyp == keygoTyp {
+				p.P(mapName, ` += fmt.Sprintf("%v: %v,", k, this.`, fieldname, `[k])`)
+			} else {
+				p.P(mapName, ` += fmt.Sprintf("%v: %v,", k, this.`, fieldname, `[`, keygoAliasTyp, `(k)])`)
+			}
+			p.Out()
+			p.P(`}`)
+			p.P(mapName, ` += "}"`)
+		}
+		p.P("s := ", stringsPkg.Use(), ".Join([]string{`&", ccTypeName, "{`,")
+		oneofs := make(map[string]struct{})
+		for _, field := range message.Field {
+			nullable := gogoproto.IsNullable(field)
+			repeated := field.IsRepeated()
+			fieldname := p.GetFieldName(message, field)
+			oneof := field.OneofIndex != nil
+			if oneof {
+				if _, ok := oneofs[fieldname]; ok {
+					continue
+				} else {
+					oneofs[fieldname] = struct{}{}
+				}
+				p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
+			} else if p.IsMap(field) {
+				mapName := `mapStringFor` + fieldname
+				p.P("`", fieldname, ":`", ` + `, mapName, " + `,", "`,")
+			} else if field.IsMessage() || p.IsGroup(field) {
+				desc := p.ObjectNamed(field.GetTypeName())
+				msgname := p.TypeName(desc)
+				msgnames := strings.Split(msgname, ".")
+				typeName := msgnames[len(msgnames)-1]
+				if nullable {
+					p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1) + `,", "`,")
+				} else if repeated {
+					p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1),`&`,``,1) + `,", "`,")
+				} else {
+					p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, stringsPkg.Use(), `.Replace(this.`, fieldname, `.String(), "`, typeName, `","`, msgname, `"`, ", 1),`&`,``,1) + `,", "`,")
+				}
+			} else {
+				if nullable && !repeated && !proto3 {
+					p.P("`", fieldname, ":`", ` + valueToString`, p.localName, `(this.`, fieldname, ") + `,", "`,")
+				} else {
+					p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
+				}
+			}
+		}
+		if message.DescriptorProto.HasExtension() {
+			if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
+				p.P("`XXX_extensions:` + proto.StringFromExtensionsMap(this.XXX_extensions) + `,`,")
+			} else {
+				p.P("`XXX_extensions:` + proto.StringFromExtensionsBytes(this.XXX_extensions) + `,`,")
+			}
+		}
+		if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
+			p.P("`XXX_unrecognized:` + ", fmtPkg.Use(), `.Sprintf("%v", this.XXX_unrecognized) + `, "`,`,")
+		}
+		p.P("`}`,")
+		p.P(`}`, `,""`, ")")
+		p.P(`return s`)
+		p.Out()
+		p.P(`}`)
+
+		//Generate String methods for oneof fields
+		for _, field := range message.Field {
+			oneof := field.OneofIndex != nil
+			if !oneof {
+				continue
+			}
+			ccTypeName := p.OneOfTypeName(message, field)
+			p.P(`func (this *`, ccTypeName, `) String() string {`)
+			p.In()
+			p.P(`if this == nil {`)
+			p.In()
+			p.P(`return "nil"`)
+			p.Out()
+			p.P(`}`)
+			p.P("s := ", stringsPkg.Use(), ".Join([]string{`&", ccTypeName, "{`,")
+			fieldname := p.GetOneOfFieldName(message, field)
+			if field.IsMessage() || p.IsGroup(field) {
+				desc := p.ObjectNamed(field.GetTypeName())
+				msgname := p.TypeName(desc)
+				msgnames := strings.Split(msgname, ".")
+				typeName := msgnames[len(msgnames)-1]
+				p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1) + `,", "`,")
+			} else {
+				p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
+			}
+			p.P("`}`,")
+			p.P(`}`, `,""`, ")")
+			p.P(`return s`)
+			p.Out()
+			p.P(`}`)
+		}
+	}
+
+	if !p.atleastOne {
+		return
+	}
+
+	p.P(`func valueToString`, p.localName, `(v interface{}) string {`)
+	p.In()
+	p.P(`rv := `, reflectPkg.Use(), `.ValueOf(v)`)
+	p.P(`if rv.IsNil() {`)
+	p.In()
+	p.P(`return "nil"`)
+	p.Out()
+	p.P(`}`)
+	p.P(`pv := `, reflectPkg.Use(), `.Indirect(rv).Interface()`)
+	p.P(`return `, fmtPkg.Use(), `.Sprintf("*%v", pv)`)
+	p.Out()
+	p.P(`}`)
+
+}
+
+func init() {
+	generator.RegisterPlugin(NewStringer())
+}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác