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
This commit is contained in:
Jakob Borg
2016-07-04 10:40:29 +00:00
committed by Audrius Butkevicius
parent 21f5b16e47
commit fa0101bd60
269 changed files with 477296 additions and 4175 deletions

View File

@@ -238,8 +238,8 @@ func runCommand(cmd string, target target) {
case "assets":
rebuildAssets()
case "xdr":
xdr()
case "proto":
proto()
case "translate":
translate()
@@ -271,9 +271,12 @@ func runCommand(cmd string, target target) {
case "metalint":
if isGometalinterInstalled() {
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:
@@ -576,8 +579,8 @@ func shouldRebuildAssets() bool {
return assetsAreNewer
}
func xdr() {
runPrint("go", "generate", "./lib/discover", "./lib/db", "./lib/protocol", "./lib/relay/protocol")
func proto() {
runPrint("go", "generate", "./lib/...")
}
func translate() {
@@ -956,13 +959,17 @@ func lint(pkg string) {
}
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 len(line) > 0 {
log.Printf("%s", line)
if analCommentPolicy.MatchString(line) {
continue
}
if strings.Contains(line, ".pb.go:") {
continue
}
log.Println(line)
}
}
@@ -1003,7 +1010,7 @@ func isGometalinterInstalled() bool {
return true
}
func gometalinter(linter string, dirs []string, excludes ...string) {
func gometalinter(linter string, dirs []string, excludes ...string) bool {
params := []string{"--disable-all"}
params = append(params, fmt.Sprintf("--deadline=%ds", 60))
params = append(params, "--enable="+linter)
@@ -1016,12 +1023,19 @@ func gometalinter(linter string, dirs []string, excludes ...string) {
params = append(params, dir)
}
bs, err := runError("gometalinter", params...)
bs, _ := runError("gometalinter", params...)
if len(bs) > 0 {
log.Printf("%s", bs)
nerr := 0
for _, line := range strings.Split(string(bs), "\n") {
if line == "" {
continue
}
if err != nil {
log.Printf("%v", err)
if strings.Contains(line, ".pb.go:") {
continue
}
log.Println(line)
nerr++
}
return nerr == 0
}

View File

@@ -9,6 +9,7 @@ package main
import (
"bytes"
"crypto/rand"
"encoding/binary"
"flag"
"log"
"strings"
@@ -66,24 +67,25 @@ func recv(bc beacon.Interface) {
seen := make(map[string]bool)
for {
data, src := bc.Recv()
var ann discover.Announce
ann.UnmarshalXDR(data)
if m := binary.BigEndian.Uint32(data); m != discover.Magic {
log.Printf("Incorrect magic %x in announcement from %v", m, src)
continue
}
if bytes.Equal(ann.This.ID, myID) {
var ann discover.Announce
ann.Unmarshal(data[4:])
if bytes.Equal(ann.ID, myID) {
// This is one of our own fake packets, don't print it.
continue
}
// Print announcement details for the first packet from a 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] {
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
}
}
@@ -92,15 +94,10 @@ func recv(bc beacon.Interface) {
// sends fake discovery announcements once every second
func send(bc beacon.Interface) {
ann := discover.Announce{
Magic: discover.AnnouncementMagic,
This: discover.Device{
ID: myID,
Addresses: []discover.Address{
{URL: "tcp://fake.example.com:12345"},
},
},
Addresses: []string{"tcp://fake.example.com:12345"},
}
bs, _ := ann.MarshalXDR()
bs, _ := ann.Marshal()
for {
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
func randomDeviceID() []byte {
var id [32]byte

View File

@@ -13,50 +13,61 @@ import (
"github.com/syncthing/syncthing/lib/db"
"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)
var dev protocol.DeviceID
for it.Next() {
key := it.Key()
switch key[0] {
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
err := f.UnmarshalXDR(it.Value())
err := f.Unmarshal(it.Value())
if err != nil {
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:
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
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:
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:
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:
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:
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:
fmt.Printf("[???]\n %x\n %x\n", it.Key(), it.Value())

View File

@@ -8,11 +8,10 @@ package main
import (
"container/heap"
"encoding/binary"
"fmt"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syndtr/goleveldb/leveldb"
)
type SizedElement struct {
@@ -38,33 +37,31 @@ func (h *ElementHeap) Pop() interface{} {
return x
}
func dumpsize(ldb *leveldb.DB) {
func dumpsize(ldb *db.Instance) {
h := &ElementHeap{}
heap.Init(h)
it := ldb.NewIterator(nil, nil)
var dev protocol.DeviceID
var ele SizedElement
for it.Next() {
key := it.Key()
switch key[0] {
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:
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:
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:
ele.key = fmt.Sprintf("DEVICESTATS:%s", key[1:])
@@ -75,6 +72,14 @@ func dumpsize(ldb *leveldb.DB) {
case db.KeyTypeVirtualMtime:
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:
ele.key = fmt.Sprintf("UNKNOWN:%x", key)
}

View File

@@ -13,8 +13,7 @@ import (
"os"
"path/filepath"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
"github.com/syncthing/syncthing/lib/db"
)
func main() {
@@ -28,16 +27,12 @@ func main() {
path := flag.Arg(0)
if path == "" {
path = filepath.Join(defaultConfigDir(), "index-v0.11.0.db")
path = filepath.Join(defaultConfigDir(), "index-v0.14.0.db")
}
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 {
log.Fatal(err)
}

View File

@@ -1174,8 +1174,12 @@ type jsonFileInfo protocol.FileInfo
func (f jsonFileInfo) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"name": f.Name,
"size": protocol.FileInfo(f).Size(),
"flags": fmt.Sprintf("%#o", f.Flags),
"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),
@@ -1188,19 +1192,22 @@ type jsonDBFileInfo db.FileInfoTruncated
func (f jsonDBFileInfo) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"name": f.Name,
"size": db.FileInfoTruncated(f).Size(),
"flags": fmt.Sprintf("%#o", f.Flags),
"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,
"version": jsonVersionVector(f.Version),
})
}
type jsonVersionVector protocol.Vector
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)
}
return json.Marshal(res)

View File

@@ -48,7 +48,7 @@ var locations = map[locationEnum]string{
locKeyFile: "${config}/key.pem",
locHTTPSCertFile: "${config}/https-cert.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
locCsrfTokens: "${config}/csrftokens.txt",
locPanicLog: "${config}/panic-${timestamp}.log",

View File

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

View File

@@ -162,8 +162,17 @@ next:
if err != nil {
if protocol.IsVersionMismatch(err) {
// 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)
} else {
// It's something else - connection reset or whatever

View File

@@ -70,7 +70,7 @@ type Model interface {
ConnectedTo(remoteID protocol.DeviceID) bool
IsPaused(remoteID protocol.DeviceID) bool
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

View File

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

View File

@@ -4,9 +4,6 @@
// 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 leveldb_xdr.go leveldb.go
package db
import (
@@ -46,25 +43,16 @@ const (
KeyTypeDeviceIdx
)
type fileVersion struct {
version protocol.Vector
device []byte
}
type VersionList struct {
versions []fileVersion
}
func (l VersionList) String() string {
var b bytes.Buffer
var id protocol.DeviceID
b.WriteString("{")
for i, v := range l.versions {
for i, v := range l.Versions {
if i > 0 {
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("}")
return b.String()
@@ -101,7 +89,7 @@ func getFile(db dbReader, key []byte) (protocol.FileInfo, bool) {
}
var f protocol.FileInfo
err = f.UnmarshalXDR(bs)
err = f.Unmarshal(bs)
if err != nil {
panic(err)
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -10,12 +10,10 @@ import (
"bytes"
"encoding/binary"
"os"
"path/filepath"
"sort"
"strings"
"sync/atomic"
"github.com/syncthing/syncthing/lib/osutil"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync"
"github.com/syndtr/goleveldb/leveldb"
@@ -48,16 +46,6 @@ func Open(file string) (*Instance, error) {
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)
if leveldbIsCorrupted(err) {
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:
// 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")
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")
if lv := t.insertFile(folder, device, fs[fsi]); lv > maxLocalVer {
maxLocalVer = lv
@@ -232,13 +220,12 @@ func (db *Instance) updateFiles(folder, device []byte, fs []protocol.FileInfo, l
}
var ef FileInfoTruncated
err = ef.UnmarshalXDR(bs)
err = ef.Unmarshal(bs)
if err != nil {
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 {
localSize.removeFile(ef)
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
// from. dbi.Value() just returns an internal slice that it reuses, so
// we need to copy it.
err := f.UnmarshalXDR(append([]byte{}, dbi.Value()...))
err := f.Unmarshal(append([]byte{}, dbi.Value()...))
if err != nil {
panic(err)
}
@@ -347,16 +334,16 @@ func (db *Instance) getGlobal(folder, file []byte, truncate bool) (FileIntf, boo
}
var vl VersionList
err = vl.UnmarshalXDR(bs)
err = vl.Unmarshal(bs)
if err != nil {
panic(err)
}
if len(vl.versions) == 0 {
if len(vl.Versions) == 0 {
l.Debugln(k)
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)
if err != nil {
panic(err)
@@ -384,11 +371,11 @@ func (db *Instance) withGlobal(folder, prefix []byte, truncate bool, fn Iterator
var fk []byte
for dbi.Next() {
var vl VersionList
err := vl.UnmarshalXDR(dbi.Value())
err := vl.Unmarshal(dbi.Value())
if err != nil {
panic(err)
}
if len(vl.versions) == 0 {
if len(vl.Versions) == 0 {
l.Debugln(dbi.Key())
panic("no versions?")
}
@@ -398,13 +385,13 @@ func (db *Instance) withGlobal(folder, prefix []byte, truncate bool, fn Iterator
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)
if err != nil {
l.Debugf("folder: %q (%x)", folder, folder)
l.Debugf("key: %q (%x)", dbi.Key(), dbi.Key())
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("fk: %q", fk)
l.Debugf("fk: %x %x %x",
@@ -436,17 +423,17 @@ func (db *Instance) availability(folder, file []byte) []protocol.DeviceID {
}
var vl VersionList
err = vl.UnmarshalXDR(bs)
err = vl.Unmarshal(bs)
if err != nil {
panic(err)
}
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
}
n := protocol.DeviceIDFromBytes(v.device)
n := protocol.DeviceIDFromBytes(v.Device)
devices = append(devices, n)
}
@@ -464,11 +451,11 @@ func (db *Instance) withNeed(folder, device []byte, truncate bool, fn Iterator)
nextFile:
for dbi.Next() {
var vl VersionList
err := vl.UnmarshalXDR(dbi.Value())
err := vl.Unmarshal(dbi.Value())
if err != nil {
panic(err)
}
if len(vl.versions) == 0 {
if len(vl.Versions) == 0 {
l.Debugln(dbi.Key())
panic("no versions?")
}
@@ -476,29 +463,29 @@ nextFile:
have := false // If we have the file, any version
need := false // If we have a lower version of the file
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
haveVersion = v.version
haveVersion = v.Version
// XXX: This marks Concurrent (i.e. conflicting) changes as
// needs. Maybe we should do that, but it needs special
// handling in the puller.
need = !v.version.GreaterEqual(vl.versions[0].version)
need = !v.Version.GreaterEqual(vl.Versions[0].Version)
break
}
}
if need || !have {
name := db.globalKeyName(dbi.Key())
needVersion := vl.versions[0].version
needVersion := vl.Versions[0].Version
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.
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)
if err != nil {
var id protocol.DeviceID
@@ -528,7 +515,7 @@ 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 {
return
@@ -601,7 +588,7 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
for dbi.Next() {
gk := dbi.Key()
var vl VersionList
err := vl.UnmarshalXDR(dbi.Value())
err := vl.Unmarshal(dbi.Value())
if err != nil {
panic(err)
}
@@ -613,8 +600,8 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
name := db.globalKeyName(gk)
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)
if err == leveldb.ErrNotFound {
@@ -623,10 +610,10 @@ func (db *Instance) checkGlobals(folder []byte, globalSize *sizeTracker) {
if err != nil {
panic(err)
}
newVL.versions = append(newVL.versions, version)
newVL.Versions = append(newVL.Versions, version)
if i == 0 {
fi, ok := t.getFile(folder, version.device, name)
fi, ok := t.getFile(folder, version.Device, name)
if !ok {
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()
}
}
@@ -715,12 +702,12 @@ func (db *Instance) globalKeyFolder(key []byte) []byte {
func unmarshalTrunc(bs []byte, truncate bool) (FileIntf, error) {
if truncate {
var tf FileInfoTruncated
err := tf.UnmarshalXDR(bs)
err := tf.Unmarshal(bs)
return tf, err
}
var tf protocol.FileInfo
err := tf.UnmarshalXDR(bs)
err := tf.Unmarshal(bs)
return tf, err
}
@@ -740,50 +727,6 @@ func leveldbIsCorrupted(err error) bool {
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
// fast lookups in both directions and persists to the database. Don't use for
// storing more items than fit comfortably in RAM.

View File

@@ -83,7 +83,7 @@ func (t readWriteTransaction) insertFile(folder, device []byte, file protocol.Fi
name := []byte(file.Name)
nk := t.db.deviceKey(folder, device, name)
t.Put(nk, file.MustMarshalXDR())
t.Put(nk, mustMarshal(&file))
return file.LocalVersion
}
@@ -105,14 +105,14 @@ func (t readWriteTransaction) updateGlobal(folder, device []byte, file protocol.
var hasOldFile bool
// Remove the device from the current version list
if len(svl) != 0 {
err = fl.UnmarshalXDR(svl)
err = fl.Unmarshal(svl)
if err != nil {
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
return false
}
@@ -120,29 +120,29 @@ func (t readWriteTransaction) updateGlobal(folder, device []byte, file protocol.
if i == 0 {
// Keep the current newest file around so we can subtract it from
// 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
}
}
}
nv := fileVersion{
device: device,
version: file.Version,
nv := FileVersion{
Device: device,
Version: file.Version,
}
insertedAt := -1
// Find a position in the list to insert this file. The file at the front
// 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:
// The version at this point in the list is equal to or lesser
// ("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
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
// IDs in the version vector, which is not the only thing we use
// 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 {
panic("file referenced in version list does not exist")
}
if file.WinsConflict(of) {
fl.versions = insertVersion(fl.versions, i, nv)
fl.Versions = insertVersion(fl.Versions, i, nv)
insertedAt = i
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.
fl.versions = append(fl.versions, nv)
insertedAt = len(fl.versions) - 1
fl.Versions = append(fl.Versions, nv)
insertedAt = len(fl.Versions) - 1
done:
if insertedAt == 0 {
@@ -178,9 +178,9 @@ done:
if hasOldFile {
// We have the old file that was removed at the head of the list.
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.
oldFile, ok := t.getFile(folder, fl.versions[1].device, name)
oldFile, ok := t.getFile(folder, fl.Versions[1].Device, name)
if !ok {
panic("file referenced in version list does not exist")
}
@@ -190,7 +190,7 @@ done:
}
l.Debugf("new global after update: %v", fl)
t.Put(gk, fl.MustMarshalXDR())
t.Put(gk, mustMarshal(&fl))
return true
}
@@ -210,14 +210,14 @@ func (t readWriteTransaction) removeFromGlobal(folder, device, file []byte, glob
}
var fl VersionList
err = fl.UnmarshalXDR(svl)
err = fl.Unmarshal(svl)
if err != nil {
panic(err)
}
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 {
f, ok := t.getFile(folder, device, file)
if !ok {
@@ -226,18 +226,18 @@ func (t readWriteTransaction) removeFromGlobal(folder, device, file []byte, glob
globalSize.removeFile(f)
removed = true
}
fl.versions = append(fl.versions[:i], fl.versions[i+1:]...)
fl.Versions = append(fl.Versions[:i], fl.Versions[i+1:]...)
break
}
}
if len(fl.versions) == 0 {
if len(fl.Versions) == 0 {
t.Delete(gk)
} else {
l.Debugf("new global after remove: %v", fl)
t.Put(gk, fl.MustMarshalXDR())
t.Put(gk, mustMarshal(&fl))
if removed {
f, ok := t.getFile(folder, fl.versions[0].device, file)
f, ok := t.getFile(folder, fl.Versions[0].Device, file)
if !ok {
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:])
t[i] = v
return t
}
type marshaller interface {
Marshal() ([]byte, error)
}
func mustMarshal(f marshaller) []byte {
bs, err := f.Marshal()
if err != nil {
panic(err)
}
return bs
}

View File

@@ -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
}

View File

@@ -31,9 +31,10 @@ type FileSet struct {
}
// FileIntf is the set of methods implemented by both protocol.FileInfo and
// protocol.FileInfoTruncated.
// FileInfoTruncated.
type FileIntf interface {
Size() int64
FileSize() int64
FileName() string
IsDeleted() bool
IsInvalid() bool
IsDirectory() bool
@@ -42,7 +43,7 @@ type FileIntf interface {
}
// 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.
type Iterator func(f FileIntf) bool
@@ -64,7 +65,7 @@ func (s *sizeTracker) addFile(f FileIntf) {
} else {
s.files++
}
s.bytes += f.Size()
s.bytes += f.FileSize()
s.mut.Unlock()
}
@@ -79,7 +80,7 @@ func (s *sizeTracker) removeFile(f FileIntf) {
} else {
s.files--
}
s.bytes -= f.Size()
s.bytes -= f.FileSize()
if s.deleted < 0 || s.files < 0 {
panic("bug: removed more than added")
}

View File

@@ -88,7 +88,7 @@ func (l fileList) String() string {
var b bytes.Buffer
b.WriteString("[]protocol.FileList{\n")
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("}")
return b.String()
@@ -100,35 +100,35 @@ func TestGlobalSet(t *testing.T) {
m := db.NewFileSet("test", ldb)
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{
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{
local0[0],
local0[1],
local0[2],
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{
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{
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{
remote0[0],
@@ -178,7 +178,7 @@ func TestGlobalSet(t *testing.T) {
} else {
globalFiles++
}
globalBytes += f.Size()
globalBytes += f.FileSize()
}
gsFiles, gsDeleted, gsBytes := m.GlobalSize()
if gsFiles != globalFiles {
@@ -208,7 +208,7 @@ func TestGlobalSet(t *testing.T) {
} else {
haveFiles++
}
haveBytes += f.Size()
haveBytes += f.FileSize()
}
lsFiles, lsDeleted, lsBytes := m.LocalSize()
if lsFiles != haveFiles {
@@ -303,23 +303,23 @@ func TestNeedWithInvalid(t *testing.T) {
s := db.NewFileSet("test", ldb)
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{
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{
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{
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)
@@ -340,10 +340,10 @@ func TestUpdateToInvalid(t *testing.T) {
s := db.NewFileSet("test", ldb)
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)
@@ -355,7 +355,7 @@ func TestUpdateToInvalid(t *testing.T) {
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])
have = fileList(haveList(s, protocol.LocalDeviceID))
@@ -372,16 +372,16 @@ func TestInvalidAvailability(t *testing.T) {
s := db.NewFileSet("test", ldb)
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{
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)
@@ -410,17 +410,17 @@ func TestGlobalReset(t *testing.T) {
m := db.NewFileSet("test", ldb)
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{
{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)
@@ -448,23 +448,23 @@ func TestNeed(t *testing.T) {
m := db.NewFileSet("test", ldb)
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{
{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{
{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)
@@ -486,18 +486,18 @@ func TestLocalVersion(t *testing.T) {
m := db.NewFileSet("test", ldb)
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{
local1[0],
// [1] deleted
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)
@@ -515,17 +515,17 @@ func TestListDropFolder(t *testing.T) {
s0 := db.NewFileSet("test0", ldb)
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)
s1 := db.NewFileSet("test1", ldb)
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)
@@ -566,24 +566,24 @@ func TestGlobalNeedWithInvalid(t *testing.T) {
s := db.NewFileSet("test1", ldb)
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)
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)
total := fileList{
// 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))
@@ -609,7 +609,7 @@ func TestLongPath(t *testing.T) {
name := b.String() // 5000 characters
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)
@@ -633,7 +633,7 @@ func TestCommitted(t *testing.T) {
s := db.NewFileSet("test", ldb)
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
@@ -659,12 +659,12 @@ func TestCommitted(t *testing.T) {
func BenchmarkUpdateOneFile(b *testing.B) {
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
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")

57
lib/db/structs.go Normal file
View File

@@ -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
lib/db/structs.pb.go Normal file
View File

@@ -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
lib/db/structs.proto Normal file
View File

@@ -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;
}

Binary file not shown.

View File

@@ -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
}

View File

@@ -4,10 +4,14 @@
// 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 local.proto
//go:generate protoc --proto_path=../../../../../:../../../../gogo/protobuf/protobuf:. --gogofast_out=. local.proto
package discover
import (
"bytes"
"encoding/binary"
"encoding/hex"
"io"
"net"
@@ -38,6 +42,8 @@ type localClient struct {
const (
BroadcastInterval = 30 * time.Second
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) {
@@ -107,25 +113,19 @@ func (c *localClient) Error() error {
}
func (c *localClient) announcementPkt() Announce {
var addrs []Address
for _, addr := range c.addrList.AllAddresses() {
addrs = append(addrs, Address{
URL: addr,
})
}
return Announce{
Magic: AnnouncementMagic,
This: Device{
ID: c.myID[:],
Addresses: addrs,
},
Addresses: c.addrList.AllAddresses(),
}
}
func (c *localClient) sendLocalAnnouncements() {
msg := make([]byte, 4)
binary.BigEndian.PutUint32(msg, Magic)
var pkt = c.announcementPkt()
msg := pkt.MustMarshalXDR()
bs, _ := pkt.Marshal()
msg = append(msg, bs...)
for {
c.beacon.Send(msg)
@@ -138,26 +138,44 @@ func (c *localClient) sendLocalAnnouncements() {
}
func (c *localClient) recvAnnouncements(b beacon.Interface) {
warnedAbout := make(map[string]bool)
for {
buf, addr := b.Recv()
if len(buf) < 4 {
l.Debugf("discover: short packet from %s")
continue
}
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
}
var pkt Announce
err := pkt.UnmarshalXDR(buf)
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
}
if pkt.Magic != AnnouncementMagic {
l.Debugf("discover: Incorrect magic from %s: %s != %s", addr, pkt.Magic, AnnouncementMagic)
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
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 {
@@ -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
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)
var validAddresses []string
for _, addr := range device.Addresses {
u, err := url.Parse(addr.URL)
u, err := url.Parse(addr)
if err != nil {
continue
}
@@ -219,10 +237,10 @@ func (c *localClient) registerDevice(src net.Addr, device Device) bool {
u.Host = net.JoinHostPort(host, strconv.Itoa(tcpAddr.Port))
l.Debugf("discover: Reconstructed URL is %#v", u)
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 {
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
lib/discover/local.pb.go Normal file
View File

@@ -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
lib/discover/local.proto Normal file
View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -41,8 +41,6 @@ import (
// How many files to send in each Index/IndexUpdate message.
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
)
@@ -95,7 +93,7 @@ type Model struct {
conn map[protocol.DeviceID]connections.Connection
helloMessages map[protocol.DeviceID]protocol.HelloResult
deviceClusterConf map[protocol.DeviceID]protocol.ClusterConfigMessage
deviceClusterConf map[protocol.DeviceID]protocol.ClusterConfig
devicePaused map[protocol.DeviceID]bool
deviceDownloads map[protocol.DeviceID]*deviceDownloadState
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),
conn: make(map[protocol.DeviceID]connections.Connection),
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),
deviceDownloads: make(map[protocol.DeviceID]*deviceDownloadState),
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.
downloaded = int64(counts[ft.Name] * protocol.BlockSize)
fileNeed = ft.Size() - downloaded
fileNeed = ft.Size - downloaded
if fileNeed < 0 {
fileNeed = 0
}
@@ -436,7 +434,7 @@ func sizeOfFile(f db.FileIntf) (files, deleted int, bytes int64) {
} else {
deleted++
}
bytes += f.Size()
bytes += f.FileSize()
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.
// 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))
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.
// 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))
if !m.folderSharedWith(folder, deviceID) {
@@ -651,7 +639,7 @@ func (m *Model) folderSharedWithUnlocked(folder string, deviceID protocol.Device
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
// 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
@@ -659,19 +647,9 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
tempIndexFolders := make([]string, 0, len(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 {
for _, folder := range cm.Folders {
if !m.folderSharedWithUnlocked(folder.ID, deviceID) {
events.Default.Log(events.FolderRejected, map[string]string{
"folder": folder.ID,
"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)
continue
}
if folder.Flags&protocol.FlagFolderDisabledTempIndexes == 0 {
if !folder.DisableTempIndexes {
tempIndexFolders = append(tempIndexFolders, folder.ID)
}
}
m.fmut.Unlock()
// This breaks if we send multiple CM messages during the same connection.
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.
if device.Flags&protocol.FlagIntroducer != 0 {
if device.Introducer {
l.Infof("Device %v is now also an introducer", id)
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.
// 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 {
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)
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 {
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()
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
// the temp indexes.
if flags&protocol.FlagFromTemporary != 0 && !folderCfg.DisableTempIndexes {
if fromTemporary && !folderCfg.DisableTempIndexes {
tempFn := filepath.Join(folderPath, defTempNamer.TempName(name))
if err := readOffsetIntoBuf(tempFn, offset, buf); err == 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.
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,
ClientName: m.clientName,
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()})
}
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) {
return
}
@@ -1238,13 +1211,13 @@ func sendIndexTo(initial bool, minLocalVer int64, conn protocol.Connection, fold
if len(batch) == indexBatchSize || currentBatchSize > indexTargetSize {
if initial {
if err = conn.Index(folder, batch, 0, nil); err != nil {
if err = conn.Index(folder, batch); err != nil {
return false
}
l.Debugf("sendIndexes for %s-%s/%q: %d files (<%d bytes) (initial index)", deviceID, name, folder, len(batch), currentBatchSize)
initial = false
} else {
if err = conn.IndexUpdate(folder, batch, 0, nil); err != nil {
if err = conn.IndexUpdate(folder, batch); err != nil {
return false
}
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)
currentBatchSize += indexPerFileSize + len(f.Blocks)*indexPerBlockSize
currentBatchSize += f.ProtoSize()
return true
})
if initial && err == nil {
err = conn.Index(folder, batch, 0, nil)
err = conn.Index(folder, batch)
if err == nil {
l.Debugf("sendIndexes for %s-%s/%q: %d files (small initial index)", deviceID, name, folder, len(batch))
}
} else if len(batch) > 0 && err == nil {
err = conn.IndexUpdate(folder, batch, 0, nil)
err = conn.IndexUpdate(folder, batch)
if err == nil {
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"
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"
}
@@ -1580,8 +1556,12 @@ func (m *Model) internalScanFolderSubdirs(folder string, subDirs []string) error
l.Debugln("setting invalid bit on ignored", f)
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagInvalid,
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)
@@ -1597,16 +1577,13 @@ func (m *Model) internalScanFolderSubdirs(folder string, subDirs []string) error
nf := protocol.FileInfo{
Name: f.Name,
Flags: f.Flags | protocol.FlagDeleted,
Type: f.Type,
Size: f.Size,
Modified: f.Modified,
Deleted: true,
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)
}
}
@@ -1672,8 +1649,8 @@ func (m *Model) numHashers(folder string) int {
// generateClusterConfig returns a ClusterConfigMessage that is correct for
// 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()
for _, folder := range m.deviceFolders[device] {
@@ -1681,21 +1658,12 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
protocolFolder := protocol.Folder{
ID: folder,
Label: folderCfg.Label,
ReadOnly: folderCfg.Type == config.FolderTypeReadOnly,
IgnorePermissions: folderCfg.IgnorePerms,
IgnoreDelete: folderCfg.IgnoreDelete,
DisableTempIndexes: folderCfg.DisableTempIndexes,
}
var flags uint32
if folderCfg.Type == config.FolderTypeReadOnly {
flags |= protocol.FlagFolderReadOnly
}
if folderCfg.IgnorePerms {
flags |= protocol.FlagFolderIgnorePerms
}
if folderCfg.IgnoreDelete {
flags |= protocol.FlagFolderIgnoreDelete
}
if folderCfg.DisableTempIndexes {
flags |= protocol.FlagFolderDisabledTempIndexes
}
protocolFolder.Flags = flags
for _, device := range m.folderDevices[folder] {
// 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[:]
@@ -1707,14 +1675,11 @@ func (m *Model) generateClusterConfig(device protocol.DeviceID) protocol.Cluster
ID: device[:],
Name: deviceCfg.Name,
Addresses: deviceCfg.Addresses,
Compression: uint32(deviceCfg.Compression),
Compression: deviceCfg.Compression,
CertName: deviceCfg.CertName,
Flags: protocol.FlagShareTrusted,
Introducer: deviceCfg.Introducer,
}
if deviceCfg.Introducer {
protocolDevice.Flags |= protocol.FlagIntroducer
}
protocolFolder.Devices = append(protocolFolder.Devices, protocolDevice)
}
message.Folders = append(message.Folders, protocolFolder)
@@ -1759,7 +1724,7 @@ func (m *Model) Override(folder string) {
have, ok := fs.Get(protocol.LocalDeviceID, need.Name)
if !ok || have.Name != need.Name {
// We are missing the file
need.Flags |= protocol.FlagDeleted
need.Deleted = true
need.Blocks = nil
need.Version = need.Version.Update(m.shortID)
} else {
@@ -1868,7 +1833,7 @@ func (m *Model) GlobalDirectoryTree(folder, prefix string, levels int, dirsonly
if !dirsonly && base != "" {
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 {
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])
fs[i] = fs[len(fs)-1]
fs = fs[:len(fs)-1]

View File

@@ -55,19 +55,19 @@ func init() {
var testDataExpected = map[string]protocol.FileInfo{
"foo": {
Name: "foo",
Flags: 0,
Type: protocol.FileInfoTypeFile,
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}}},
},
"empty": {
Name: "empty",
Flags: 0,
Type: protocol.FileInfoTypeFile,
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}}},
},
"bar": {
Name: "bar",
Flags: 0,
Type: protocol.FileInfoTypeFile,
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}}},
},
@@ -77,8 +77,9 @@ func init() {
// Fix expected test data to match reality
for n, f := range testDataExpected {
fi, _ := os.Stat("testdata/" + n)
f.Flags = uint32(fi.Mode())
f.Permissions = uint32(fi.Mode())
f.Modified = fi.ModTime().Unix()
f.Size = fi.Size()
testDataExpected[n] = f
}
}
@@ -98,7 +99,7 @@ func TestRequest(t *testing.T) {
// Existing, shared file
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 {
t.Error(err)
}
@@ -107,32 +108,32 @@ func TestRequest(t *testing.T) {
}
// 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 {
t.Error("Unexpected nil error on insecure file read")
}
// 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 {
t.Error("Unexpected nil error on insecure file read")
}
// 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 {
t.Error("Unexpected nil error on insecure file read")
}
// 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 {
t.Error("Unexpected nil error on insecure file read")
}
// Larger block than available
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 {
t.Error("Unexpected nil error on insecure file read")
}
@@ -168,11 +169,11 @@ func benchmarkIndex(b *testing.B, nfiles int) {
m.ServeBackground()
files := genFiles(nfiles)
m.Index(device1, "default", files, 0, nil)
m.Index(device1, "default", files)
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.Index(device1, "default", files, 0, nil)
m.Index(device1, "default", files)
}
b.ReportAllocs()
}
@@ -199,11 +200,11 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
files := genFiles(nfiles)
ufiles := genFiles(nufiles)
m.Index(device1, "default", files, 0, nil)
m.Index(device1, "default", files)
b.ResetTimer()
for i := 0; i < b.N; i++ {
m.IndexUpdate(device1, "default", ufiles, 0, nil)
m.IndexUpdate(device1, "default", ufiles)
}
b.ReportAllocs()
}
@@ -211,8 +212,6 @@ func benchmarkIndexUpdate(b *testing.B, nfiles, nufiles int) {
type downloadProgressMessage struct {
folder string
updates []protocol.FileDownloadProgressUpdate
flags uint32
options []protocol.Option
}
type FakeConnection struct {
@@ -240,11 +239,11 @@ func (f FakeConnection) Option(string) string {
return ""
}
func (FakeConnection) Index(string, []protocol.FileInfo, uint32, []protocol.Option) error {
func (FakeConnection) Index(string, []protocol.FileInfo) error {
return nil
}
func (FakeConnection) IndexUpdate(string, []protocol.FileInfo, uint32, []protocol.Option) error {
func (FakeConnection) IndexUpdate(string, []protocol.FileInfo) error {
return nil
}
@@ -252,7 +251,7 @@ func (f FakeConnection) Request(folder, name string, offset int64, size int, has
return f.requestData, nil
}
func (FakeConnection) ClusterConfig(protocol.ClusterConfigMessage) {}
func (FakeConnection) ClusterConfig(protocol.ClusterConfig) {}
func (FakeConnection) Ping() bool {
return true
@@ -266,12 +265,10 @@ func (FakeConnection) Statistics() 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{
folder: folder,
updates: updates,
flags: flags,
options: options,
})
}
@@ -305,7 +302,7 @@ func BenchmarkRequest(b *testing.B) {
},
Connection: fc,
}, protocol.HelloResult{})
m.Index(device1, "default", files, 0, nil)
m.Index(device1, "default", files)
b.ResetTimer()
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[:]) {
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")
}
if id := r.Devices[1].ID; !bytes.Equal(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")
}
@@ -471,13 +468,13 @@ func TestClusterConfig(t *testing.T) {
if id := r.Devices[0].ID; !bytes.Equal(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")
}
if id := r.Devices[1].ID; !bytes.Equal(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")
}
}
@@ -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) {
ldb := db.OpenMemory()
set := db.NewFileSet("default", ldb)
@@ -789,17 +748,18 @@ func TestGlobalDirectoryTree(t *testing.T) {
m.ServeBackground()
b := func(isfile bool, path ...string) protocol.FileInfo {
flags := uint32(protocol.FlagDirectory)
typ := protocol.FileInfoTypeDirectory
blocks := []protocol.BlockInfo{}
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}}}
}
return protocol.FileInfo{
Name: filepath.Join(path...),
Flags: flags,
Type: typ,
Modified: 0x666,
Blocks: blocks,
Size: 0xa,
}
}
@@ -871,7 +831,7 @@ func TestGlobalDirectoryTree(t *testing.T) {
return string(bytes)
}
m.Index(device1, "default", testdata, 0, nil)
m.Index(device1, "default", testdata)
result := m.GlobalDirectoryTree("default", "", -1, false)
@@ -1039,17 +999,18 @@ func TestGlobalDirectorySelfFixing(t *testing.T) {
m.ServeBackground()
b := func(isfile bool, path ...string) protocol.FileInfo {
flags := uint32(protocol.FlagDirectory)
typ := protocol.FileInfoTypeDirectory
blocks := []protocol.BlockInfo{}
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}}}
}
return protocol.FileInfo{
Name: filepath.Join(path...),
Flags: flags,
Type: typ,
Modified: 0x666,
Blocks: blocks,
Size: 0xa,
}
}
@@ -1130,7 +1091,7 @@ func TestGlobalDirectorySelfFixing(t *testing.T) {
return string(bytes)
}
m.Index(device1, "default", testdata, 0, nil)
m.Index(device1, "default", testdata)
result := m.GlobalDirectoryTree("default", "", -1, false)
@@ -1215,7 +1176,7 @@ func benchmarkTree(b *testing.B, n1, n2 int) {
m.ScanFolder("default")
files := genDeepFiles(n1, n2)
m.Index(device1, "default", files, 0, nil)
m.Index(device1, "default", files)
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -1244,12 +1205,12 @@ func TestIgnoreDelete(t *testing.T) {
}
// Mark it for deletion
f.Flags = protocol.FlagDeleted
f.Deleted = true
f.Version = f.Version.Update(142) // arbitrary short remote ID
f.Blocks = nil
// Send the index
m.Index(device1, "default", []protocol.FileInfo{f}, 0, nil)
m.Index(device1, "default", []protocol.FileInfo{f})
// Make sure we ignored it
f, ok = m.CurrentGlobalFile("default", "foo")

View File

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

View File

@@ -100,7 +100,6 @@ func TestProgressEmitter(t *testing.T) {
}
func TestSendDownloadProgressMessages(t *testing.T) {
c := config.Wrap("/tmp/test", config.Configuration{})
c.SetOptions(config.OptionsConfiguration{
ProgressUpdateIntervalS: 0,
@@ -112,7 +111,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
p := NewProgressEmitter(c)
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
for i, msg := range fc.downloadProgressMessages {
if msg.folder == state.folder {
@@ -346,7 +345,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
file: protocol.FileInfo{
Name: "state5",
Version: v1,
Flags: protocol.FlagDirectory,
Type: protocol.FileInfoTypeDirectory,
Blocks: blocks,
},
mut: sync.NewRWMutex(),
@@ -359,7 +358,7 @@ func TestSendDownloadProgressMessages(t *testing.T) {
file: protocol.FileInfo{
Name: "state6",
Version: v1,
Flags: protocol.FlagSymlink,
Type: protocol.FileInfoTypeSymlinkUnknown,
},
mut: sync.NewRWMutex(),
available: []int32{1, 2, 3},

View File

@@ -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
// which is being pulled.
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
@@ -435,7 +435,7 @@ func (f *rwFolder) pullerIteration(ignores *ignore.Matcher) int {
if !handleFile(file) {
// A new or changed file or symlink. This is the only case where we
// 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++
@@ -569,7 +569,7 @@ func (f *rwFolder) handleDir(file protocol.FileInfo) {
}()
realName := filepath.Join(f.dir, file.Name)
mode := os.FileMode(file.Flags & 0777)
mode := os.FileMode(file.Permissions & 0777)
if f.ignorePermissions(file) {
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.
if info, err := osutil.Lstat(realName); err == nil {
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)
// Scan() is synchronous (i.e. blocks until the scan is
// 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 {
// Copy the blocks, as we don't want to shuffle them on the FileInfo
blocks = append(blocks, file.Blocks...)
blocksSize = file.Size()
blocksSize = file.Size
}
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 {
realName := filepath.Join(f.dir, file.Name)
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)
f.newError(file.Name, err)
return err
@@ -1235,7 +1235,7 @@ func (f *rwFolder) pullerRoutine(in <-chan pullBlockState, out chan<- *sharedPul
func (f *rwFolder) performFinish(state *sharedPullerState) error {
// Set the correct permission bits on the new 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
}
}

View File

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

View File

@@ -12,7 +12,6 @@ import (
"path/filepath"
"time"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/sync"
)
@@ -150,7 +149,7 @@ func (s *sharedPullerState) tempFile() (io.WriterAt, error) {
if s.sparse && !s.file.IsSymlink() {
// Truncate sets the size of the file. This creates a sparse file or a
// 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)
return nil, err
}
@@ -293,8 +292,8 @@ func (s *sharedPullerState) Progress() *pullerProgress {
CopiedFromElsewhere: s.copyTotal - s.copyNeeded - s.copyOrigin,
Pulled: s.pullTotal - 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()
return blocks
}
func blocksToSize(num int) int64 {
if num < 2 {
return protocol.BlockSize / 2
}
return int64(num-1)*protocol.BlockSize + protocol.BlockSize/2
}

View File

@@ -63,8 +63,8 @@ func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
c1.Start()
// 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
b.ReportAllocs()
@@ -164,13 +164,13 @@ func negotiateTLS(cert tls.Certificate, conn0, conn1 net.Conn) (net.Conn, net.Co
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
// can verify that it did in fact get some data back over the
// connection.
@@ -178,11 +178,11 @@ func (m *fakeModel) Request(deviceID DeviceID, folder string, name string, offse
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) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate, flags uint32, options []Option) {
func (m *fakeModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) {
}

3999
lib/protocol/bep.pb.go Normal file

File diff suppressed because it is too large Load Diff

189
lib/protocol/bep.proto Normal file
View File

@@ -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;
}

View File

@@ -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[:])
}

View File

@@ -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]
}

View File

@@ -11,8 +11,7 @@ type TestModel struct {
offset int64
size int
hash []byte
flags uint32
options []Option
fromTemporary bool
closedCh chan struct{}
closedErr error
}
@@ -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.name = name
t.offset = offset
t.size = len(buf)
t.hash = hash
t.flags = flags
t.options = options
t.fromTemporary = fromTemporary
copy(buf, t.data)
return nil
}
@@ -46,10 +44,10 @@ func (t *TestModel) Close(deviceID DeviceID, err error) {
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 {

View File

@@ -4,13 +4,7 @@ package protocol
import "fmt"
type Compression int
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
)
@@ -31,14 +25,6 @@ var compressionUnmarshal = map[string]Compression{
"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 {
return fmt.Sprintf("%q", c.String())
}

View File

@@ -8,8 +8,8 @@ func TestWinsConflict(t *testing.T) {
testcases := [][2]FileInfo{
// The first should always win over the second
{{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 {

View File

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

View File

@@ -6,13 +6,6 @@ import (
"errors"
)
const (
ecNoError int32 = iota
ecGeneric
ecNoSuchFile
ecInvalid
)
var (
ErrNoError error
ErrGeneric = errors.New("generic error")
@@ -20,32 +13,32 @@ var (
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 {
return ErrGeneric
}
return err
}
func errorToCode(err error) int32 {
func errorToCode(err error) ErrorCode {
code, ok := lookupCode[err]
if !ok {
return ecGeneric
return ErrorCodeGeneric
}
return code
}

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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,
}
}

View File

@@ -9,10 +9,10 @@ import (
"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
// buffer.
type HelloMessage interface {
type HelloIntf interface {
Magic() uint32
Marshal() ([]byte, error)
}
@@ -29,12 +29,15 @@ var (
// ErrTooOldVersion12 is returned by ExchangeHello when the other side
// 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")
// 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
// speaks something entirely unknown.
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 {
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.
func IsVersionMismatch(err error) bool {
switch err {
case ErrTooOldVersion12, ErrUnknownMagic:
case ErrTooOldVersion12, ErrTooOldVersion13, ErrUnknownMagic:
return true
default:
return false
@@ -59,6 +62,28 @@ func readHello(c io.Reader) (HelloResult, error) {
}
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:
// This is a v0.13 Hello message in XDR format
msgSize := binary.BigEndian.Uint32(header[4:])
@@ -79,7 +104,7 @@ func readHello(c io.Reader) (HelloResult, error) {
ClientName: hello.ClientName,
ClientVersion: hello.ClientVersion,
}
return res, nil
return res, ErrTooOldVersion13
case 0x00010001, 0x00010000:
// 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
}
func writeHello(c io.Writer, h HelloMessage) error {
func writeHello(c io.Writer, h HelloIntf) error {
msg, err := h.Marshal()
if err != nil {
return err

View File

@@ -13,6 +13,53 @@ import (
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) {
// 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)
if err != nil {
t.Fatal(err)
if err != ErrTooOldVersion13 {
t.Errorf("unexpected error %v != ErrTooOldVersion13", err)
}
if res.ClientName != expected.ClientName {
@@ -94,7 +141,7 @@ func TestVersion12Hello(t *testing.T) {
_, err := ExchangeHello(conn, send)
if err != ErrTooOldVersion12 {
t.Errorf("unexpected error %v != ErrTooOld", err)
t.Errorf("unexpected error %v != ErrTooOldVersion12", err)
}
}

View File

@@ -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{}

File diff suppressed because it is too large Load Diff

View File

@@ -12,21 +12,21 @@ type nativeModel struct {
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 {
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 {
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)
return m.Model.Request(deviceID, folder, name, offset, hash, flags, options, buf)
return m.Model.Request(deviceID, folder, name, offset, hash, fromTemporary, buf)
}

View File

@@ -24,19 +24,19 @@ type nativeModel struct {
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)
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)
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)
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) {
@@ -47,7 +47,7 @@ func fixupFiles(folder string, files []FileInfo) {
// can't possibly exist here anyway.
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)
}
files[i].Name = filepath.FromSlash(files[i].Name)

View File

@@ -4,7 +4,6 @@ package protocol
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
@@ -12,26 +11,16 @@ import (
"time"
lz4 "github.com/bkaradzic/go-lz4"
"github.com/calmh/xdr"
)
const (
// BlockSize is the standard ata block size (128 KiB)
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 (
@@ -39,31 +28,11 @@ const (
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
const (
FlagFromTemporary uint32 = 1 << iota
)
// FileDownloadProgressUpdate update types
const (
UpdateTypeAppend uint32 = iota
UpdateTypeForget
)
// ClusterConfigMessage.Folders flags
const (
FlagFolderReadOnly uint32 = 1 << 0
@@ -85,35 +54,33 @@ var (
ErrClosed = errors.New("connection closed")
ErrTimeout = errors.New("read timeout")
ErrSwitchingConnections = errors.New("switching connections")
errUnknownMessage = errors.New("unknown message")
)
// Specific variants of empty messages...
type pingMessage struct{ EmptyMessage }
type Model interface {
// 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
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
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
ClusterConfig(deviceID DeviceID, config ClusterConfigMessage)
ClusterConfig(deviceID DeviceID, config ClusterConfig)
// The peer device closed the connection
Close(deviceID DeviceID, err error)
// 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 {
Start()
ID() DeviceID
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)
ClusterConfig(config ClusterConfigMessage)
DownloadProgress(folder string, updates []FileDownloadProgressUpdate, flags uint32, options []Option)
ClusterConfig(config ClusterConfig)
DownloadProgress(folder string, updates []FileDownloadProgressUpdate)
Statistics() Statistics
Closed() bool
}
@@ -126,19 +93,19 @@ type rawConnection struct {
cr *countingReader
cw *countingWriter
awaiting [4096]chan asyncResult
awaiting map[int32]chan asyncResult
awaitingMut sync.Mutex
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{}
once sync.Once
pool sync.Pool
compression Compression
readerBuf []byte // used & reused by readMessage
}
type asyncResult struct {
@@ -146,19 +113,16 @@ type asyncResult struct {
err error
}
type hdrMsg struct {
hdr header
msg encodable
done chan struct{}
type message interface {
ProtoSize() int
Marshal() ([]byte, error)
MarshalTo([]byte) (int, error)
Unmarshal([]byte) error
}
type encodable interface {
MarshalXDRInto(m *xdr.Marshaller) error
XDRSize() int
}
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 (
@@ -170,6 +134,12 @@ const (
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 {
cr := &countingReader{Reader: reader}
cw := &countingWriter{Writer: writer}
@@ -180,8 +150,8 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiv
receiver: nativeModel{receiver},
cr: cr,
cw: cw,
outbox: make(chan hdrMsg),
nextID: make(chan int),
awaiting: make(map[int32]chan asyncResult),
outbox: make(chan asyncMessage),
closed: make(chan struct{}),
pool: sync.Pool{
New: func() interface{} {
@@ -201,7 +171,6 @@ func (c *rawConnection) Start() {
go c.writerLoop()
go c.pingSender()
go c.pingReceiver()
go c.idGenerator()
}
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
func (c *rawConnection) Index(folder string, idx []FileInfo, flags uint32, options []Option) error {
func (c *rawConnection) Index(folder string, idx []FileInfo) error {
select {
case <-c.closed:
return ErrClosed
default:
}
c.idxMut.Lock()
c.send(-1, messageTypeIndex, IndexMessage{
c.send(&Index{
Folder: folder,
Files: idx,
Flags: flags,
Options: options,
}, nil)
c.idxMut.Unlock()
return nil
}
// 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 {
case <-c.closed:
return ErrClosed
default:
}
c.idxMut.Lock()
c.send(-1, messageTypeIndexUpdate, IndexMessage{
c.send(&IndexUpdate{
Folder: folder,
Files: idx,
Flags: flags,
Options: options,
}, nil)
c.idxMut.Unlock()
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.
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()
if ch := c.awaiting[id]; ch != nil {
if _, ok := c.awaiting[id]; ok {
panic("id taken")
}
rc := make(chan asyncResult, 1)
c.awaiting[id] = rc
c.awaitingMut.Unlock()
ok := c.send(id, messageTypeRequest, RequestMessage{
ok := c.send(&Request{
ID: id,
Folder: folder,
Name: name,
Offset: offset,
Size: int32(size),
Hash: hash,
Flags: flags,
Options: nil,
FromTemporary: fromTemporary,
}, nil)
if !ok {
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
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 {
@@ -306,24 +263,15 @@ func (c *rawConnection) Closed() bool {
}
// 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,
Updates: updates,
Flags: flags,
Options: options,
}, nil)
}
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) {
@@ -339,199 +287,172 @@ func (c *rawConnection) readerLoop() (err error) {
default:
}
hdr, msg, err := c.readMessage()
msg, err := c.readMessage()
if err != nil {
return err
}
switch msg := msg.(type) {
case ClusterConfigMessage:
case *ClusterConfig:
l.Debugln("read ClusterConfig message")
if state != stateInitial {
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
case IndexMessage:
switch hdr.msgType {
case messageTypeIndex:
case *Index:
l.Debugln("read Index message")
if state != stateReady {
return fmt.Errorf("protocol error: index message in state %d", state)
}
c.handleIndex(msg)
c.handleIndex(*msg)
state = stateReady
case messageTypeIndexUpdate:
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)
c.handleIndexUpdate(*msg)
state = stateReady
}
case RequestMessage:
case *Request:
l.Debugln("read Request message")
if state != stateReady {
return fmt.Errorf("protocol error: request message in state %d", state)
}
// 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 {
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 {
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 {
return fmt.Errorf("protocol error: ping message in state %d", state)
}
// Nothing
case CloseMessage:
case *Close:
l.Debugln("read Close message")
return errors.New(msg.Reason)
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 {
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)
}
msgLen := int32(binary.BigEndian.Uint32(buf))
if msgLen < 0 {
return nil, fmt.Errorf("negative message length %d", msgLen)
}
if hdr.version != 0 {
err = fmt.Errorf("unknown protocol version 0x%x", hdr.version)
return
// Then comes the message
buf = buffers.upgrade(buf, int(msgLen))
if _, err := io.ReadFull(c.cr, buf); err != nil {
return nil, fmt.Errorf("reading message: %v", err)
}
// 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.
// ... which might be compressed
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)
}
switch hdr.Compression {
case MessageCompressionNone:
// Nothing
_, err = io.ReadFull(c.cr, msgBuf)
case MessageCompressionLZ4:
decomp, err := c.lz4Decompress(buf)
buffers.put(buf)
if err != nil {
return
return nil, fmt.Errorf("decompressing message: %v", err)
}
l.Debugf("read %d bytes", len(msgBuf))
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
msgBuf, err = lz4.Decode(nil, msgBuf)
if err != nil {
return
}
l.Debugf("decompressed to %d bytes", len(msgBuf))
} else {
c.readerBuf = nil
}
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))
}
}
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
case messageTypeResponse:
var resp ResponseMessage
err = resp.UnmarshalXDR(msgBuf)
msg = resp
case messageTypePing:
msg = pingMessage{}
case messageTypeClusterConfig:
var cc ClusterConfigMessage
err = cc.UnmarshalXDR(msgBuf)
msg = cc
case messageTypeClose:
var cm CloseMessage
err = cm.UnmarshalXDR(msgBuf)
msg = cm
case messageTypeDownloadProgress:
var dp DownloadProgressMessage
err := dp.UnmarshalXDR(msgBuf)
if xdrErr, ok := err.(isEofer); ok && xdrErr.IsEOF() {
err = nil
}
msg = dp
buf = decomp
default:
err = fmt.Errorf("protocol error: %s: unknown message type %#x", c.id, hdr.msgType)
return nil, fmt.Errorf("unknown message compression %d", hdr.Compression)
}
// 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
// ... and is then unmarshalled
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)
return msg, nil
}
func (c *rawConnection) readHeader() (Header, error) {
// First comes a 2 byte header length
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)
}
return
// Then comes the header
buf = buffers.upgrade(buf, int(hdrLen))
if _, err := io.ReadFull(c.cr, buf); err != nil {
return Header{}, fmt.Errorf("reading header: %v", err)
}
var hdr Header
if err := hdr.Unmarshal(buf); err != nil {
return Header{}, fmt.Errorf("unmarshalling header: %v", err)
}
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 {
@@ -560,7 +481,7 @@ func filterIndexMessageFiles(fs []FileInfo) []FileInfo {
return fs
}
func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
func (c *rawConnection) handleRequest(req Request) {
size := int(req.Size)
usePool := size <= BlockSize
@@ -574,14 +495,16 @@ func (c *rawConnection) handleRequest(msgID int, req RequestMessage) {
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 {
c.send(msgID, messageTypeResponse, ResponseMessage{
c.send(&Response{
ID: req.ID,
Data: nil,
Code: errorToCode(err),
}, done)
} else {
c.send(msgID, messageTypeResponse, ResponseMessage{
c.send(&Response{
ID: req.ID,
Data: buf,
Code: errorToCode(err),
}, 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()
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)}
close(rc)
}
c.awaitingMut.Unlock()
}
func (c *rawConnection) send(msgID int, msgType int, msg encodable, done chan struct{}) bool {
if msgID < 0 {
func (c *rawConnection) send(msg message, done chan struct{}) bool {
select {
case id := <-c.nextID:
msgID = id
case <-c.closed:
return false
}
}
hdr := header{
version: 0,
msgID: msgID,
msgType: msgType,
}
select {
case c.outbox <- hdrMsg{hdr, msg, done}:
case c.outbox <- asyncMessage{msg, done}:
return true
case <-c.closed:
return false
@@ -628,94 +536,181 @@ func (c *rawConnection) send(msgID int, msgType int, msg encodable, done chan st
}
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 {
var tempBuf []byte
var err error
select {
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 {
if err := c.writeMessage(hm); 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 {
c.close(err)
return
}
case <-c.closed:
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) {
c.once.Do(func() {
l.Debugln("close due to", err)
@@ -725,7 +720,7 @@ func (c *rawConnection) close(err error) {
for i, ch := range c.awaiting {
if ch != nil {
close(ch)
c.awaiting[i] = nil
delete(c.awaiting, i)
}
}
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
// PingSendInterval. If we already have something sent in the last
// 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(),
}
}
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
}

View File

@@ -3,21 +3,17 @@
package protocol
import (
"encoding/binary"
"encoding/hex"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"reflect"
"strings"
"testing"
"testing/quick"
"time"
"github.com/calmh/xdr"
"github.com/syncthing/syncthing/lib/rand"
)
var (
@@ -26,64 +22,6 @@ var (
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) {
ar, aw := io.Pipe()
br, bw := io.Pipe()
@@ -92,8 +30,8 @@ func TestPing(t *testing.T) {
c0.Start()
c1 := NewConnection(c1ID, br, aw, newTestModel(), "name", CompressAlways).(wireFormatConnection).Connection.(*rawConnection)
c1.Start()
c0.ClusterConfig(ClusterConfigMessage{})
c1.ClusterConfig(ClusterConfigMessage{})
c0.ClusterConfig(ClusterConfig{})
c1.ClusterConfig(ClusterConfig{})
if ok := c0.ping(); !ok {
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) {
m0 := newTestModel()
m1 := newTestModel()
@@ -164,8 +52,8 @@ func TestClose(t *testing.T) {
c0.Start()
c1 := NewConnection(c1ID, br, aw, m1, "name", CompressAlways)
c1.Start()
c0.ClusterConfig(ClusterConfigMessage{})
c1.ClusterConfig(ClusterConfigMessage{})
c0.ClusterConfig(ClusterConfig{})
c1.ClusterConfig(ClusterConfig{})
c0.close(errors.New("manual close"))
@@ -180,8 +68,8 @@ func TestClose(t *testing.T) {
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 {
t.Error("Request should return an error")
@@ -193,15 +81,11 @@ func TestMarshalIndexMessage(t *testing.T) {
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 {
m1.Files = nil
}
for i, f := range m1.Files {
m1.Files[i].CachedSize = 0
if len(f.Blocks) == 0 {
m1.Files[i].Blocks = nil
} 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 {
@@ -227,14 +114,11 @@ func TestMarshalRequestMessage(t *testing.T) {
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 {
m1.Hash = nil
}
return testMarshal(t, "request", &m1, &RequestMessage{})
return testMarshal(t, "request", &m1, &Request{})
}
if err := quick.Check(f, quickCfg); err != nil {
@@ -247,11 +131,11 @@ func TestMarshalResponseMessage(t *testing.T) {
quickCfg.MaxCount = 10
}
f := func(m1 ResponseMessage) bool {
f := func(m1 Response) bool {
if len(m1.Data) == 0 {
m1.Data = nil
}
return testMarshal(t, "response", &m1, &ResponseMessage{})
return testMarshal(t, "response", &m1, &Response{})
}
if err := quick.Check(f, quickCfg); err != nil {
@@ -264,10 +148,7 @@ func TestMarshalClusterConfigMessage(t *testing.T) {
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 {
m1.Folders = nil
}
@@ -275,11 +156,8 @@ func TestMarshalClusterConfigMessage(t *testing.T) {
if len(m1.Folders[i].Devices) == 0 {
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 {
@@ -292,8 +170,8 @@ func TestMarshalCloseMessage(t *testing.T) {
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 {
@@ -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 {
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.Marshal()
if err != nil {
t.Fatal(err)
}
err = m2.Unmarshal(buf)
if err != nil {
t.Fatal(err)
}
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
}
buf, err := m1.MarshalXDR()
if err != nil && strings.Contains(err.Error(), "exceeds size") {
return true
}
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
}
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),
}
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}
}
idx := Index{
Folder: "some folder ID",
Files: []FileInfo{f},
}
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)
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 {
failed(nil)
t.Fatal(err)
}
err = m2.UnmarshalXDR(buf)
comp, err := c.lz4Compress(data)
if err != nil {
failed(buf)
t.Fatal(err)
t.Errorf("compressing %d bytes: %v", dataLen, err)
continue
}
ok := reflect.DeepEqual(m1, m2)
if !ok {
failed(buf)
res, err := c.lz4Decompress(comp)
if err != nil {
t.Errorf("decompressing %d bytes to %d: %v", len(comp), dataLen, err)
continue
}
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):
}
}
func TestFileInfoSize(t *testing.T) {
fi := FileInfo{
Blocks: []BlockInfo{
{Size: 42},
{Offset: 42, Size: 23},
{Offset: 42 + 23, Size: 34},
},
}
size := fi.Size()
want := int64(42 + 23 + 34)
if size != want {
t.Errorf("Incorrect size reported, got %d, want %d", size, want)
}
size = fi.Size() // Cached, this time
if size != want {
t.Errorf("Incorrect cached size reported, got %d, want %d", size, want)
}
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)
}
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)
}
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)
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)
}
}

View File

@@ -6,35 +6,30 @@ package protocol
// 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
// new allocated Vector with the modified contents.
type Vector []Counter
// 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
// one. If it is possible, the vector v is updated and returned. If it is not,
// a copy will be created, updated and returned.
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
v[i].Value++
v.Counters[i].Value++
return v
} else if v[i].ID > id {
} else if v.Counters[i].ID > id {
// 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].Value = 1
copy(nv[i+1:], v[i:])
return nv
copy(nv[i+1:], v.Counters[i:])
return Vector{nv}
}
}
// 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
@@ -42,28 +37,28 @@ func (v Vector) Update(id ShortID) Vector {
// will be created, updated and returned.
func (v Vector) Merge(b Vector) Vector {
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
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
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++
}
vi++
@@ -74,9 +69,9 @@ func (v Vector) Merge(b Vector) Vector {
// Copy returns an identical vector that is not shared with v.
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.
@@ -106,10 +101,96 @@ func (v Vector) Concurrent(b Vector) bool {
// Counter returns the current value of the given counter ID.
func (v Vector) Counter(id ShortID) uint64 {
for _, c := range v {
for _, c := range v.Counters {
if c.ID == id {
return c.Value
}
}
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
}

View File

@@ -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
}

View File

@@ -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)
}
}
}
}

View File

@@ -2,7 +2,10 @@
package protocol
import "testing"
import (
"math"
"testing"
)
func TestUpdate(t *testing.T) {
var v Vector
@@ -10,7 +13,7 @@ func TestUpdate(t *testing.T) {
// Append
v = v.Update(42)
expected := Vector{Counter{42, 1}}
expected := Vector{[]Counter{{42, 1}}}
if v.Compare(expected) != Equal {
t.Errorf("Update error, %+v != %+v", v, expected)
@@ -19,7 +22,7 @@ func TestUpdate(t *testing.T) {
// Insert at front
v = v.Update(36)
expected = Vector{Counter{36, 1}, Counter{42, 1}}
expected = Vector{[]Counter{{36, 1}, {42, 1}}}
if v.Compare(expected) != Equal {
t.Errorf("Update error, %+v != %+v", v, expected)
@@ -28,7 +31,7 @@ func TestUpdate(t *testing.T) {
// Insert in moddle
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 {
t.Errorf("Update error, %+v != %+v", v, expected)
@@ -37,7 +40,7 @@ func TestUpdate(t *testing.T) {
// Update existing
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 {
t.Errorf("Update error, %+v != %+v", v, expected)
@@ -45,7 +48,7 @@ func TestUpdate(t *testing.T) {
}
func TestCopy(t *testing.T) {
v0 := Vector{Counter{42, 1}}
v0 := Vector{[]Counter{{42, 1}}}
v1 := v0.Copy()
v1.Update(42)
if v0.Compare(v1) != Lesser {
@@ -64,52 +67,52 @@ func TestMerge(t *testing.T) {
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
{
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
{
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
{
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
{
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) {
v0 := Vector{Counter{42, 1}, Counter{64, 5}}
v0 := Vector{[]Counter{{42, 1}, {64, 5}}}
if 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)
}
}
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)
}
}
}
}

View File

@@ -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)
}

View File

@@ -12,7 +12,7 @@ type wireFormatConnection struct {
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))
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))
}
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))
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))
}
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) {

View File

@@ -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.")
}
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 {
l.Debugln("hash error:", f.Name, err)
continue

View File

@@ -168,7 +168,7 @@ func (w *walker) walk() (chan protocol.FileInfo, error) {
for file := range toHashChan {
filesToHash = append(filesToHash, file)
total += int64(file.CachedSize)
total += int64(file.Size)
}
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)
// - has the same size as previously
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() &&
!cf.IsSymlink() && !cf.IsInvalid() && cf.Size() == info.Size() {
!cf.IsSymlink() && !cf.IsInvalid() && cf.Size == info.Size() {
return nil
}
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{
Name: relPath,
Type: protocol.FileInfoTypeFile,
Version: cf.Version.Update(w.ShortID),
Flags: flags,
Permissions: curMode & uint32(maskModePerm),
NoPermissions: w.IgnorePerms,
Modified: mtime.Unix(),
CachedSize: info.Size(),
Size: info.Size(),
}
l.Debugln("to hash:", relPath, f)
@@ -349,21 +346,17 @@ 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 invalid (since it looks valid now)
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() {
return nil
}
flags := uint32(protocol.FlagDirectory)
if w.IgnorePerms {
flags |= protocol.FlagNoPermBits | 0777
} else {
flags |= uint32(info.Mode() & maskModePerm)
}
f := protocol.FileInfo{
Name: relPath,
Type: protocol.FileInfoTypeDirectory,
Version: cf.Version.Update(w.ShortID),
Flags: flags,
Permissions: uint32(info.Mode() & maskModePerm),
NoPermissions: w.IgnorePerms,
Modified: mtime.Unix(),
}
l.Debugln("dir:", relPath, f)
@@ -421,9 +414,10 @@ func (w *walker) walkSymlink(absPath, relPath string, dchan chan protocol.FileIn
f := protocol.FileInfo{
Name: relPath,
Type: SymlinkType(targetType),
Version: cf.Version.Update(w.ShortID),
Flags: uint32(protocol.FlagSymlink | protocol.FlagNoPermBits | 0666 | SymlinkFlags(targetType)),
Modified: 0,
NoPermissions: true, // Symlinks don't have permissions of their own
Blocks: blocks,
}
@@ -519,21 +513,21 @@ func SymlinkTypeEqual(disk symlinks.TargetType, f protocol.FileInfo) bool {
case symlinks.TargetUnknown:
return true
case symlinks.TargetDirectory:
return f.IsDirectory() && f.Flags&protocol.FlagSymlinkMissingTarget == 0
return f.Type == protocol.FileInfoTypeSymlinkDirectory
case symlinks.TargetFile:
return !f.IsDirectory() && f.Flags&protocol.FlagSymlinkMissingTarget == 0
return f.Type == protocol.FileInfoTypeSymlinkFile
}
panic("unknown symlink TargetType")
}
func SymlinkFlags(t symlinks.TargetType) uint32 {
func SymlinkType(t symlinks.TargetType) protocol.FileInfoType {
switch t {
case symlinks.TargetFile:
return 0
return protocol.FileInfoTypeSymlinkFile
case symlinks.TargetDirectory:
return protocol.FlagDirectory
return protocol.FileInfoTypeSymlinkDirectory
case symlinks.TargetUnknown:
return protocol.FlagSymlinkMissingTarget
return protocol.FileInfoTypeSymlinkUnknown
}
panic("unknown symlink TargetType")
}

View File

@@ -29,7 +29,7 @@ import (
type testfile struct {
name string
size int
length int64
hash string
}
@@ -322,7 +322,7 @@ func (l fileList) testfiles() testfileList {
if len(f.Blocks) > 1 {
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 {
testfiles[i].hash = fmt.Sprintf("%x", f.Blocks[0].Hash)
}
@@ -334,7 +334,7 @@ func (l testfileList) String() string {
var b bytes.Buffer
b.WriteString("{\n")
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("}")
return b.String()
@@ -343,27 +343,27 @@ func (l testfileList) String() string {
func TestSymlinkTypeEqual(t *testing.T) {
testcases := []struct {
onDiskType symlinks.TargetType
inIndexFlags uint32
fiType protocol.FileInfoType
equal bool
}{
// 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
{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
{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 {
res := SymlinkTypeEqual(tc.onDiskType, protocol.FileInfo{Flags: tc.inIndexFlags})
res := SymlinkTypeEqual(tc.onDiskType, protocol.FileInfo{Type: tc.fiType})
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)
}
}
}

View File

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

80
script/protofmt.go Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/LICENSE generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/codec/codec.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/gogoproto/doc.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/gogoproto/gogo.pb.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/gogoproto/helper.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/io/full.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/io/io.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/io/uint32.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/io/varint.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/jsonpb/jsonpb.go generated vendored Normal file
View File

@@ -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())
}

View File

@@ -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,
}

View File

@@ -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,
}

View File

@@ -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())
}

View File

@@ -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)
}

View File

@@ -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())
}

View File

@@ -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())
}

View File

@@ -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)
}

View File

@@ -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())
}

View File

@@ -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
vendor/github.com/gogo/protobuf/plugin/equal/equal.go generated vendored Normal file
View File

@@ -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())
}

View File

@@ -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
vendor/github.com/gogo/protobuf/plugin/face/face.go generated vendored Normal file
View File

@@ -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())
}

View File

@@ -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)
}

View File

@@ -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())
}

View File

@@ -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
vendor/github.com/gogo/protobuf/plugin/grpc/grpc.go generated vendored Normal file
View File

@@ -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
}

File diff suppressed because it is too large Load Diff

View File

@@ -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())
}

View File

@@ -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
vendor/github.com/gogo/protobuf/plugin/size/size.go generated vendored Normal file
View File

@@ -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
vendor/github.com/gogo/protobuf/plugin/size/sizetest.go generated vendored Normal file
View File

@@ -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)
}

View File

@@ -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())
}

Some files were not shown because too many files have changed in this diff Show More