From 7d766bf7c70720d6032746b42a990dd08b77bce2 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Wed, 25 Mar 2015 22:35:05 +0100 Subject: [PATCH] Update protocol --- Godeps/Godeps.json | 4 +- .../github.com/calmh/xdr/cmd/genxdr/main.go | 26 +- .../github.com/syncthing/protocol/deviceid.go | 6 + .../github.com/syncthing/protocol/message.go | 4 +- .../syncthing/protocol/message_xdr.go | 183 ++++++------- .../github.com/syncthing/protocol/vector.go | 105 ++++++++ .../syncthing/protocol/vector_compare.go | 89 +++++++ .../syncthing/protocol/vector_compare_test.go | 249 ++++++++++++++++++ .../syncthing/protocol/vector_test.go | 122 +++++++++ .../syncthing/protocol/vector_xdr.go | 38 +++ 10 files changed, 718 insertions(+), 108 deletions(-) create mode 100644 Godeps/_workspace/src/github.com/syncthing/protocol/vector.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare_test.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/protocol/vector_test.go create mode 100644 Godeps/_workspace/src/github.com/syncthing/protocol/vector_xdr.go diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index b8ea93524..238a0231a 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -19,7 +19,7 @@ }, { "ImportPath": "github.com/calmh/xdr", - "Rev": "03e63d0b968219dd006b17c337f8a6581332f1ab" + "Rev": "bccf335c34c01760bdc89f98c952fcda696e27d2" }, { "ImportPath": "github.com/juju/ratelimit", @@ -31,7 +31,7 @@ }, { "ImportPath": "github.com/syncthing/protocol", - "Rev": "d2ec40bb67846f34d3c1e59714351127a2e869e9" + "Rev": "f9132cae85dcda1caba2f4ba78996d348b00ac6c" }, { "ImportPath": "github.com/syndtr/goleveldb/leveldb", diff --git a/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go b/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go index 7464227b5..267994098 100644 --- a/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go +++ b/Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go @@ -52,7 +52,7 @@ import ( var encodeTpl = template.Must(template.New("encoder").Parse(` func (o {{.TypeName}}) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) }//+n func (o {{.TypeName}}) MarshalXDR() ([]byte, error) { @@ -70,11 +70,11 @@ func (o {{.TypeName}}) MustMarshalXDR() []byte { func (o {{.TypeName}}) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err }//+n -func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) { +func (o {{.TypeName}}) EncodeXDRInto(xw *xdr.Writer) (int, error) { {{range $fieldInfo := .Fields}} {{if not $fieldInfo.IsSlice}} {{if ne $fieldInfo.Convert ""}} @@ -87,7 +87,7 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) { {{end}} xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}) {{else}} - _, err := o.{{$fieldInfo.Name}}.encodeXDR(xw) + _, err := o.{{$fieldInfo.Name}}.EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -105,7 +105,7 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) { {{else if $fieldInfo.IsBasic}} xw.Write{{$fieldInfo.Encoder}}(o.{{$fieldInfo.Name}}[i]) {{else}} - _, err := o.{{$fieldInfo.Name}}[i].encodeXDR(xw) + _, err := o.{{$fieldInfo.Name}}[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -118,16 +118,16 @@ func (o {{.TypeName}}) encodeXDR(xw *xdr.Writer) (int, error) { func (o *{{.TypeName}}) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) }//+n func (o *{{.TypeName}}) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) }//+n -func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error { +func (o *{{.TypeName}}) DecodeXDRFrom(xr *xdr.Reader) error { {{range $fieldInfo := .Fields}} {{if not $fieldInfo.IsSlice}} {{if ne $fieldInfo.Convert ""}} @@ -139,7 +139,7 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error { o.{{$fieldInfo.Name}} = xr.Read{{$fieldInfo.Encoder}}() {{end}} {{else}} - (&o.{{$fieldInfo.Name}}).decodeXDR(xr) + (&o.{{$fieldInfo.Name}}).DecodeXDRFrom(xr) {{end}} {{else}} _{{$fieldInfo.Name}}Size := int(xr.ReadUint32()) @@ -155,7 +155,7 @@ func (o *{{.TypeName}}) decodeXDR(xr *xdr.Reader) error { {{else if $fieldInfo.IsBasic}} o.{{$fieldInfo.Name}}[i] = xr.Read{{$fieldInfo.Encoder}}() {{else}} - (&o.{{$fieldInfo.Name}}[i]).decodeXDR(xr) + (&o.{{$fieldInfo.Name}}[i]).DecodeXDRFrom(xr) {{end}} } {{end}} @@ -257,7 +257,6 @@ func handleStruct(t *ast.StructType) []fieldInfo { } else { f = fieldInfo{ Name: fn, - IsBasic: false, IsSlice: true, FieldType: tn, Max: max, @@ -317,10 +316,9 @@ func generateDiagram(output io.Writer, s structInfo) { for _, f := range fs { tn := f.FieldType - sl := f.IsSlice name := uncamelize(f.Name) - if sl { + if f.IsSlice { fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61)) fmt.Fprintln(output, line) } @@ -347,7 +345,7 @@ func generateDiagram(output io.Writer, s structInfo) { fmt.Fprintf(output, "/ %61s /\n", "") fmt.Fprintln(output, line) default: - if sl { + if f.IsSlice { tn = "Zero or more " + tn + " Structures" fmt.Fprintf(output, "/ %s /\n", center("", 61)) fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61)) diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/deviceid.go b/Godeps/_workspace/src/github.com/syncthing/protocol/deviceid.go index f3b3c5a31..2e0334a6a 100644 --- a/Godeps/_workspace/src/github.com/syncthing/protocol/deviceid.go +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/deviceid.go @@ -6,6 +6,7 @@ import ( "bytes" "crypto/sha256" "encoding/base32" + "encoding/binary" "errors" "fmt" "regexp" @@ -67,6 +68,11 @@ func (n DeviceID) Equals(other DeviceID) bool { return bytes.Compare(n[:], other[:]) == 0 } +// Short returns an integer representing bits 0-63 of the device ID. +func (n DeviceID) Short() uint64 { + return binary.BigEndian.Uint64(n[:]) +} + func (n *DeviceID) MarshalText() ([]byte, error) { return []byte(n.String()), nil } diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/message.go b/Godeps/_workspace/src/github.com/syncthing/protocol/message.go index f6ed9e1a8..91c331903 100644 --- a/Godeps/_workspace/src/github.com/syncthing/protocol/message.go +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/message.go @@ -17,13 +17,13 @@ type FileInfo struct { Name string // max:8192 Flags uint32 Modified int64 - Version int64 + Version Vector LocalVersion int64 Blocks []BlockInfo } func (f FileInfo) String() string { - return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%d, Size:%d, Blocks:%v}", + 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) } diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/message_xdr.go b/Godeps/_workspace/src/github.com/syncthing/protocol/message_xdr.go index 243aea879..95d72eb1a 100644 --- a/Godeps/_workspace/src/github.com/syncthing/protocol/message_xdr.go +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/message_xdr.go @@ -51,7 +51,7 @@ struct IndexMessage { func (o IndexMessage) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o IndexMessage) MarshalXDR() ([]byte, error) { @@ -69,15 +69,15 @@ func (o IndexMessage) MustMarshalXDR() []byte { func (o IndexMessage) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o IndexMessage) encodeXDR(xw *xdr.Writer) (int, error) { +func (o IndexMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { xw.WriteString(o.Folder) xw.WriteUint32(uint32(len(o.Files))) for i := range o.Files { - _, err := o.Files[i].encodeXDR(xw) + _, err := o.Files[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -88,7 +88,7 @@ func (o IndexMessage) encodeXDR(xw *xdr.Writer) (int, error) { } xw.WriteUint32(uint32(len(o.Options))) for i := range o.Options { - _, err := o.Options[i].encodeXDR(xw) + _, err := o.Options[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -98,21 +98,21 @@ func (o IndexMessage) encodeXDR(xw *xdr.Writer) (int, error) { func (o *IndexMessage) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *IndexMessage) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *IndexMessage) decodeXDR(xr *xdr.Reader) error { +func (o *IndexMessage) DecodeXDRFrom(xr *xdr.Reader) error { o.Folder = xr.ReadString() _FilesSize := int(xr.ReadUint32()) o.Files = make([]FileInfo, _FilesSize) for i := range o.Files { - (&o.Files[i]).decodeXDR(xr) + (&o.Files[i]).DecodeXDRFrom(xr) } o.Flags = xr.ReadUint32() _OptionsSize := int(xr.ReadUint32()) @@ -121,7 +121,7 @@ func (o *IndexMessage) decodeXDR(xr *xdr.Reader) error { } o.Options = make([]Option, _OptionsSize) for i := range o.Options { - (&o.Options[i]).decodeXDR(xr) + (&o.Options[i]).DecodeXDRFrom(xr) } return xr.Error() } @@ -145,9 +145,9 @@ FileInfo Structure: + Modified (64 bits) + | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -| | -+ Version (64 bits) + -| | +/ / +\ Vector Structure \ +/ / +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | + Local Version (64 bits) + @@ -165,7 +165,7 @@ struct FileInfo { string Name<8192>; unsigned int Flags; hyper Modified; - hyper Version; + Vector Version; hyper LocalVersion; BlockInfo Blocks<>; } @@ -174,7 +174,7 @@ struct FileInfo { func (o FileInfo) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o FileInfo) MarshalXDR() ([]byte, error) { @@ -192,22 +192,25 @@ func (o FileInfo) MustMarshalXDR() []byte { func (o FileInfo) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o FileInfo) encodeXDR(xw *xdr.Writer) (int, error) { +func (o FileInfo) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.Name); l > 8192 { return xw.Tot(), xdr.ElementSizeExceeded("Name", l, 8192) } xw.WriteString(o.Name) xw.WriteUint32(o.Flags) xw.WriteUint64(uint64(o.Modified)) - xw.WriteUint64(uint64(o.Version)) + _, err := o.Version.EncodeXDRInto(xw) + if err != nil { + return xw.Tot(), err + } xw.WriteUint64(uint64(o.LocalVersion)) xw.WriteUint32(uint32(len(o.Blocks))) for i := range o.Blocks { - _, err := o.Blocks[i].encodeXDR(xw) + _, err := o.Blocks[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -217,25 +220,25 @@ func (o FileInfo) encodeXDR(xw *xdr.Writer) (int, error) { func (o *FileInfo) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *FileInfo) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *FileInfo) decodeXDR(xr *xdr.Reader) error { +func (o *FileInfo) DecodeXDRFrom(xr *xdr.Reader) error { o.Name = xr.ReadStringMax(8192) o.Flags = xr.ReadUint32() o.Modified = int64(xr.ReadUint64()) - o.Version = int64(xr.ReadUint64()) + (&o.Version).DecodeXDRFrom(xr) o.LocalVersion = int64(xr.ReadUint64()) _BlocksSize := int(xr.ReadUint32()) o.Blocks = make([]BlockInfo, _BlocksSize) for i := range o.Blocks { - (&o.Blocks[i]).decodeXDR(xr) + (&o.Blocks[i]).DecodeXDRFrom(xr) } return xr.Error() } @@ -266,7 +269,7 @@ struct BlockInfo { func (o BlockInfo) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o BlockInfo) MarshalXDR() ([]byte, error) { @@ -284,11 +287,11 @@ func (o BlockInfo) MustMarshalXDR() []byte { func (o BlockInfo) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o BlockInfo) encodeXDR(xw *xdr.Writer) (int, error) { +func (o BlockInfo) EncodeXDRInto(xw *xdr.Writer) (int, error) { xw.WriteUint32(uint32(o.Size)) if l := len(o.Hash); l > 64 { return xw.Tot(), xdr.ElementSizeExceeded("Hash", l, 64) @@ -299,16 +302,16 @@ func (o BlockInfo) encodeXDR(xw *xdr.Writer) (int, error) { func (o *BlockInfo) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *BlockInfo) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *BlockInfo) decodeXDR(xr *xdr.Reader) error { +func (o *BlockInfo) DecodeXDRFrom(xr *xdr.Reader) error { o.Size = int32(xr.ReadUint32()) o.Hash = xr.ReadBytesMax(64) return xr.Error() @@ -369,7 +372,7 @@ struct RequestMessage { func (o RequestMessage) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o RequestMessage) MarshalXDR() ([]byte, error) { @@ -387,11 +390,11 @@ func (o RequestMessage) MustMarshalXDR() []byte { func (o RequestMessage) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) { +func (o RequestMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.Folder); l > 64 { return xw.Tot(), xdr.ElementSizeExceeded("Folder", l, 64) } @@ -412,7 +415,7 @@ func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) { } xw.WriteUint32(uint32(len(o.Options))) for i := range o.Options { - _, err := o.Options[i].encodeXDR(xw) + _, err := o.Options[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -422,16 +425,16 @@ func (o RequestMessage) encodeXDR(xw *xdr.Writer) (int, error) { func (o *RequestMessage) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *RequestMessage) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *RequestMessage) decodeXDR(xr *xdr.Reader) error { +func (o *RequestMessage) DecodeXDRFrom(xr *xdr.Reader) error { o.Folder = xr.ReadStringMax(64) o.Name = xr.ReadStringMax(8192) o.Offset = int64(xr.ReadUint64()) @@ -444,7 +447,7 @@ func (o *RequestMessage) decodeXDR(xr *xdr.Reader) error { } o.Options = make([]Option, _OptionsSize) for i := range o.Options { - (&o.Options[i]).decodeXDR(xr) + (&o.Options[i]).DecodeXDRFrom(xr) } return xr.Error() } @@ -475,7 +478,7 @@ struct ResponseMessage { func (o ResponseMessage) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o ResponseMessage) MarshalXDR() ([]byte, error) { @@ -493,11 +496,11 @@ func (o ResponseMessage) MustMarshalXDR() []byte { func (o ResponseMessage) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o ResponseMessage) encodeXDR(xw *xdr.Writer) (int, error) { +func (o ResponseMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { xw.WriteBytes(o.Data) xw.WriteUint32(uint32(o.Error)) return xw.Tot(), xw.Error() @@ -505,16 +508,16 @@ func (o ResponseMessage) encodeXDR(xw *xdr.Writer) (int, error) { func (o *ResponseMessage) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *ResponseMessage) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *ResponseMessage) decodeXDR(xr *xdr.Reader) error { +func (o *ResponseMessage) DecodeXDRFrom(xr *xdr.Reader) error { o.Data = xr.ReadBytes() o.Error = int32(xr.ReadUint32()) return xr.Error() @@ -564,7 +567,7 @@ struct ClusterConfigMessage { func (o ClusterConfigMessage) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o ClusterConfigMessage) MarshalXDR() ([]byte, error) { @@ -582,11 +585,11 @@ func (o ClusterConfigMessage) MustMarshalXDR() []byte { func (o ClusterConfigMessage) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) { +func (o ClusterConfigMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.ClientName); l > 64 { return xw.Tot(), xdr.ElementSizeExceeded("ClientName", l, 64) } @@ -597,7 +600,7 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) { xw.WriteString(o.ClientVersion) xw.WriteUint32(uint32(len(o.Folders))) for i := range o.Folders { - _, err := o.Folders[i].encodeXDR(xw) + _, err := o.Folders[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -607,7 +610,7 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) { } xw.WriteUint32(uint32(len(o.Options))) for i := range o.Options { - _, err := o.Options[i].encodeXDR(xw) + _, err := o.Options[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -617,22 +620,22 @@ func (o ClusterConfigMessage) encodeXDR(xw *xdr.Writer) (int, error) { func (o *ClusterConfigMessage) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *ClusterConfigMessage) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *ClusterConfigMessage) decodeXDR(xr *xdr.Reader) error { +func (o *ClusterConfigMessage) DecodeXDRFrom(xr *xdr.Reader) error { o.ClientName = xr.ReadStringMax(64) o.ClientVersion = xr.ReadStringMax(64) _FoldersSize := int(xr.ReadUint32()) o.Folders = make([]Folder, _FoldersSize) for i := range o.Folders { - (&o.Folders[i]).decodeXDR(xr) + (&o.Folders[i]).DecodeXDRFrom(xr) } _OptionsSize := int(xr.ReadUint32()) if _OptionsSize > 64 { @@ -640,7 +643,7 @@ func (o *ClusterConfigMessage) decodeXDR(xr *xdr.Reader) error { } o.Options = make([]Option, _OptionsSize) for i := range o.Options { - (&o.Options[i]).decodeXDR(xr) + (&o.Options[i]).DecodeXDRFrom(xr) } return xr.Error() } @@ -685,7 +688,7 @@ struct Folder { func (o Folder) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o Folder) MarshalXDR() ([]byte, error) { @@ -703,18 +706,18 @@ func (o Folder) MustMarshalXDR() []byte { func (o Folder) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o Folder) encodeXDR(xw *xdr.Writer) (int, error) { +func (o Folder) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.ID); l > 64 { return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 64) } xw.WriteString(o.ID) xw.WriteUint32(uint32(len(o.Devices))) for i := range o.Devices { - _, err := o.Devices[i].encodeXDR(xw) + _, err := o.Devices[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -725,7 +728,7 @@ func (o Folder) encodeXDR(xw *xdr.Writer) (int, error) { } xw.WriteUint32(uint32(len(o.Options))) for i := range o.Options { - _, err := o.Options[i].encodeXDR(xw) + _, err := o.Options[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -735,21 +738,21 @@ func (o Folder) encodeXDR(xw *xdr.Writer) (int, error) { func (o *Folder) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *Folder) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *Folder) decodeXDR(xr *xdr.Reader) error { +func (o *Folder) DecodeXDRFrom(xr *xdr.Reader) error { o.ID = xr.ReadStringMax(64) _DevicesSize := int(xr.ReadUint32()) o.Devices = make([]Device, _DevicesSize) for i := range o.Devices { - (&o.Devices[i]).decodeXDR(xr) + (&o.Devices[i]).DecodeXDRFrom(xr) } o.Flags = xr.ReadUint32() _OptionsSize := int(xr.ReadUint32()) @@ -758,7 +761,7 @@ func (o *Folder) decodeXDR(xr *xdr.Reader) error { } o.Options = make([]Option, _OptionsSize) for i := range o.Options { - (&o.Options[i]).decodeXDR(xr) + (&o.Options[i]).DecodeXDRFrom(xr) } return xr.Error() } @@ -801,7 +804,7 @@ struct Device { func (o Device) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o Device) MarshalXDR() ([]byte, error) { @@ -819,11 +822,11 @@ func (o Device) MustMarshalXDR() []byte { func (o Device) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o Device) encodeXDR(xw *xdr.Writer) (int, error) { +func (o Device) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.ID); l > 32 { return xw.Tot(), xdr.ElementSizeExceeded("ID", l, 32) } @@ -835,7 +838,7 @@ func (o Device) encodeXDR(xw *xdr.Writer) (int, error) { } xw.WriteUint32(uint32(len(o.Options))) for i := range o.Options { - _, err := o.Options[i].encodeXDR(xw) + _, err := o.Options[i].EncodeXDRInto(xw) if err != nil { return xw.Tot(), err } @@ -845,16 +848,16 @@ func (o Device) encodeXDR(xw *xdr.Writer) (int, error) { func (o *Device) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *Device) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *Device) decodeXDR(xr *xdr.Reader) error { +func (o *Device) DecodeXDRFrom(xr *xdr.Reader) error { o.ID = xr.ReadBytesMax(32) o.MaxLocalVersion = int64(xr.ReadUint64()) o.Flags = xr.ReadUint32() @@ -864,7 +867,7 @@ func (o *Device) decodeXDR(xr *xdr.Reader) error { } o.Options = make([]Option, _OptionsSize) for i := range o.Options { - (&o.Options[i]).decodeXDR(xr) + (&o.Options[i]).DecodeXDRFrom(xr) } return xr.Error() } @@ -899,7 +902,7 @@ struct Option { func (o Option) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o Option) MarshalXDR() ([]byte, error) { @@ -917,11 +920,11 @@ func (o Option) MustMarshalXDR() []byte { func (o Option) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o Option) encodeXDR(xw *xdr.Writer) (int, error) { +func (o Option) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.Key); l > 64 { return xw.Tot(), xdr.ElementSizeExceeded("Key", l, 64) } @@ -935,16 +938,16 @@ func (o Option) encodeXDR(xw *xdr.Writer) (int, error) { func (o *Option) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *Option) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *Option) decodeXDR(xr *xdr.Reader) error { +func (o *Option) DecodeXDRFrom(xr *xdr.Reader) error { o.Key = xr.ReadStringMax(64) o.Value = xr.ReadStringMax(1024) return xr.Error() @@ -976,7 +979,7 @@ struct CloseMessage { func (o CloseMessage) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o CloseMessage) MarshalXDR() ([]byte, error) { @@ -994,11 +997,11 @@ func (o CloseMessage) MustMarshalXDR() []byte { func (o CloseMessage) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o CloseMessage) encodeXDR(xw *xdr.Writer) (int, error) { +func (o CloseMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { if l := len(o.Reason); l > 1024 { return xw.Tot(), xdr.ElementSizeExceeded("Reason", l, 1024) } @@ -1009,16 +1012,16 @@ func (o CloseMessage) encodeXDR(xw *xdr.Writer) (int, error) { func (o *CloseMessage) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *CloseMessage) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *CloseMessage) decodeXDR(xr *xdr.Reader) error { +func (o *CloseMessage) DecodeXDRFrom(xr *xdr.Reader) error { o.Reason = xr.ReadStringMax(1024) o.Code = int32(xr.ReadUint32()) return xr.Error() @@ -1040,7 +1043,7 @@ struct EmptyMessage { func (o EmptyMessage) EncodeXDR(w io.Writer) (int, error) { var xw = xdr.NewWriter(w) - return o.encodeXDR(xw) + return o.EncodeXDRInto(xw) } func (o EmptyMessage) MarshalXDR() ([]byte, error) { @@ -1058,25 +1061,25 @@ func (o EmptyMessage) MustMarshalXDR() []byte { func (o EmptyMessage) AppendXDR(bs []byte) ([]byte, error) { var aw = xdr.AppendWriter(bs) var xw = xdr.NewWriter(&aw) - _, err := o.encodeXDR(xw) + _, err := o.EncodeXDRInto(xw) return []byte(aw), err } -func (o EmptyMessage) encodeXDR(xw *xdr.Writer) (int, error) { +func (o EmptyMessage) EncodeXDRInto(xw *xdr.Writer) (int, error) { return xw.Tot(), xw.Error() } func (o *EmptyMessage) DecodeXDR(r io.Reader) error { xr := xdr.NewReader(r) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } func (o *EmptyMessage) UnmarshalXDR(bs []byte) error { var br = bytes.NewReader(bs) var xr = xdr.NewReader(br) - return o.decodeXDR(xr) + return o.DecodeXDRFrom(xr) } -func (o *EmptyMessage) decodeXDR(xr *xdr.Reader) error { +func (o *EmptyMessage) DecodeXDRFrom(xr *xdr.Reader) error { return xr.Error() } diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/vector.go b/Godeps/_workspace/src/github.com/syncthing/protocol/vector.go new file mode 100644 index 000000000..048594522 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/vector.go @@ -0,0 +1,105 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +// The Vector type represents a version vector. The zero value is a usable +// 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 a +// new allocated Vector with the modified contents. +type Vector []Counter + +// Counter represents a single counter in the version vector. +type Counter struct { + ID uint64 + 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 uint64) Vector { + for i := range v { + if v[i].ID == ID { + // Update an existing index + v[i].Value++ + return v + } else if v[i].ID > ID { + // Insert a new index + nv := make(Vector, len(v)+1) + copy(nv, v[:i]) + nv[i].ID = ID + nv[i].Value = 1 + copy(nv[i+1:], v[i:]) + return nv + } + } + // Append a new new index + return append(v, Counter{ID, 1}) +} + +// Merge returns the vector containing the maximum indexes from a and b. If it +// is possible, the vector a is updated and returned. If it is not, a copy +// will be created, updated and returned. +func (a Vector) Merge(b Vector) Vector { + var ai, bi int + for bi < len(b) { + if ai == len(a) { + // We've reach the end of a, all that remains are appends + return append(a, b[bi:]...) + } + + if a[ai].ID > b[bi].ID { + // The index from b should be inserted here + n := make(Vector, len(a)+1) + copy(n, a[:ai]) + n[ai] = b[bi] + copy(n[ai+1:], a[ai:]) + a = n + } + + if a[ai].ID == b[bi].ID { + if v := b[bi].Value; v > a[ai].Value { + a[ai].Value = v + } + } + + if bi < len(b) && a[ai].ID == b[bi].ID { + bi++ + } + ai++ + } + + return a +} + +// 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 +} + +// Equal returns true when the two vectors are equivalent. +func (a Vector) Equal(b Vector) bool { + return a.Compare(b) == Equal +} + +// LesserEqual returns true when the two vectors are equivalent or a is Lesser +// than b. +func (a Vector) LesserEqual(b Vector) bool { + comp := a.Compare(b) + return comp == Lesser || comp == Equal +} + +// LesserEqual returns true when the two vectors are equivalent or a is Greater +// than b. +func (a Vector) GreaterEqual(b Vector) bool { + comp := a.Compare(b) + return comp == Greater || comp == Equal +} + +// Concurrent returns true when the two vectors are concrurrent. +func (a Vector) Concurrent(b Vector) bool { + comp := a.Compare(b) + return comp == ConcurrentGreater || comp == ConcurrentLesser +} diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare.go b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare.go new file mode 100644 index 000000000..9735ec9d1 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare.go @@ -0,0 +1,89 @@ +// 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 +} diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare_test.go b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare_test.go new file mode 100644 index 000000000..78b6abe43 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_compare_test.go @@ -0,0 +1,249 @@ +// 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) + } + } + } +} diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/vector_test.go b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_test.go new file mode 100644 index 000000000..7815412c2 --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_test.go @@ -0,0 +1,122 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +import "testing" + +func TestUpdate(t *testing.T) { + var v Vector + + // Append + + v = v.Update(42) + expected := Vector{Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } + + // Insert at front + + v = v.Update(36) + expected = Vector{Counter{36, 1}, Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } + + // Insert in moddle + + v = v.Update(37) + expected = Vector{Counter{36, 1}, Counter{37, 1}, Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } + + // Update existing + + v = v.Update(37) + expected = Vector{Counter{36, 1}, Counter{37, 2}, Counter{42, 1}} + + if v.Compare(expected) != Equal { + t.Errorf("Update error, %+v != %+v", v, expected) + } +} + +func TestCopy(t *testing.T) { + v0 := Vector{Counter{42, 1}} + v1 := v0.Copy() + v1.Update(42) + if v0.Compare(v1) != Lesser { + t.Errorf("Copy error, %+v should be ancestor of %+v", v0, v1) + } +} + +func TestMerge(t *testing.T) { + testcases := []struct { + a, b, m Vector + }{ + // No-ops + { + Vector{}, + Vector{}, + Vector{}, + }, + { + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + + // Appends + { + Vector{}, + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + { + Vector{Counter{22, 1}}, + Vector{Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + { + Vector{Counter{22, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + Vector{Counter{22, 1}, Counter{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{42, 1}}, + Vector{Counter{22, 1}}, + Vector{Counter{22, 1}, Counter{42, 1}}, + }, + + // Update + { + Vector{Counter{22, 1}, Counter{42, 2}}, + Vector{Counter{22, 2}, Counter{42, 1}}, + Vector{Counter{22, 2}, Counter{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}}, + }, + } + + for i, tc := range testcases { + if m := tc.a.Merge(tc.b); m.Compare(tc.m) != Equal { + t.Errorf("%d: %+v.Merge(%+v) == %+v (expected %+v)", i, tc.a, tc.b, m, tc.m) + } + } + +} diff --git a/Godeps/_workspace/src/github.com/syncthing/protocol/vector_xdr.go b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_xdr.go new file mode 100644 index 000000000..a4b6b132b --- /dev/null +++ b/Godeps/_workspace/src/github.com/syncthing/protocol/vector_xdr.go @@ -0,0 +1,38 @@ +// Copyright (C) 2015 The Protocol Authors. + +package protocol + +// 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... + +type xdrWriter interface { + WriteUint32(uint32) (int, error) + WriteUint64(uint64) (int, error) +} +type xdrReader interface { + ReadUint32() uint32 + ReadUint64() uint64 +} + +// EncodeXDRInto encodes the vector as an XDR object into the given XDR +// encoder. +func (v Vector) EncodeXDRInto(w xdrWriter) (int, error) { + w.WriteUint32(uint32(len(v))) + for i := range v { + w.WriteUint64(v[i].ID) + w.WriteUint64(v[i].Value) + } + return 4 + 16*len(v), nil +} + +// DecodeXDRFrom decodes the XDR objects from the given reader into itself. +func (v *Vector) DecodeXDRFrom(r xdrReader) error { + l := int(r.ReadUint32()) + n := make(Vector, l) + for i := range n { + n[i].ID = r.ReadUint64() + n[i].Value = r.ReadUint64() + } + *v = n + return nil +}