From: Steven McDonald Date: Sun, 2 Oct 2011 13:33:36 +0000 (+1100) Subject: initialise X-Git-Url: http://git.steven-mcdonald.id.au/?a=commitdiff_plain;h=c3766ed0969f7780785c14d49b077f98ff812f01;p=debian%2Fgoprotobuf.git initialise --- c3766ed0969f7780785c14d49b077f98ff812f01 diff --git a/.hg_archival.txt b/.hg_archival.txt new file mode 100644 index 0000000..6bcceed --- /dev/null +++ b/.hg_archival.txt @@ -0,0 +1,5 @@ +repo: ecf73c3ec4286914856b51b4e9cb5c320f528952 +node: bc76a153ffde5de8535b7c2791102ddd0b93f187 +branch: default +tag: go.r60 +tag: go.weekly.2011-09-21 diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..7f7b763 --- /dev/null +++ b/.hgignore @@ -0,0 +1,20 @@ +syntax:glob +.DS_Store +.git +.gitignore +*.[568ao] +*.pb.go +*.ao +*.so +*.pyc +._* +.nfs.* +[568a].out +*~ +*.orig +core +_obj +_test +_testmain.go +compiler/protoc-gen-go +compiler/testdata/extension_test diff --git a/.hgtags b/.hgtags new file mode 100644 index 0000000..0de67a6 --- /dev/null +++ b/.hgtags @@ -0,0 +1,16 @@ +96dad172ec5b1aa6946f13f9cbf9cfe0fec40f05 weekly.2011-04-04 +37b09d1346b72c73a76672aa9a830bff9e6be014 weekly.2011-04-13 +44f4e3da3f952834d65c7a5fcc1f1a2cc90e1542 weekly.2011-04-27 +097a1c90eb4bf73ecacbcef71c09298f4a596dcd weekly.2011-06-16 +83a0b16ac3234242663b12be19adc030c1cd666a weekly.2011-06-23 +89e4e9b619197f71e60c3ed309474cfd94475061 weekly.2011-07-07 +32017900448961add50b154f63d1f80907285984 weekly.2011-08-10 +c9278633d1eb1c80bc5946eb6724e2d22cd28265 weekly.2011-08-17 +f3ef978cda6fabc10f6039aeed960340547c6d5a release.r56 +fa1080cfc0a4d0fa24844bc50124aed892365059 release.r57.1 +fa1080cfc0a4d0fa24844bc50124aed892365059 release.r57.2 +cfe37a63cede6cd072434965e6ea426b90bcb223 release.r58 +cfe37a63cede6cd072434965e6ea426b90bcb223 release.r58.1 +da17c98c73d8801baa1d2e493c9ac0844470ef0a release.r59 +5a487ccac6f7fd6cba5deee5d5d0908c7f1d6a53 go.r60 +5a487ccac6f7fd6cba5deee5d5d0908c7f1d6a53 go.weekly.2011-09-07 diff --git a/CONTRIBUTORS b/CONTRIBUTORS new file mode 100644 index 0000000..f926e4e --- /dev/null +++ b/CONTRIBUTORS @@ -0,0 +1,14 @@ +# This is a list of contributors to the Go protobuf repository. + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + + +Dave Cheney +David Symonds +Ken Thompson +Mikkel Krautz +Nigel Tao +Rob Pike diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ae3664f --- /dev/null +++ b/LICENSE @@ -0,0 +1,31 @@ +Go support for Protocol Buffers - Google's data interchange format + +Copyright 2010 Google Inc. All rights reserved. +http://code.google.com/p/goprotobuf/ + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/Make.protobuf b/Make.protobuf new file mode 100644 index 0000000..06d37b5 --- /dev/null +++ b/Make.protobuf @@ -0,0 +1,46 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# 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. + +# Includable Makefile to add a rule for generating .pb.go files from .proto files +# (Google protocol buffer descriptions). +# Typical use if myproto.proto is a file in package mypackage in this directory: +# +# include $(GOROOT)/src/Make.inc +# TARG=mypackage +# GOFILES=\ +# myproto.pb.go +# +# include $(GOROOT)/src/Make.pkg +# include $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/Make.protobuf + +%.pb.go: %.proto + protoc --go_out=. $< + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f1c42f5 --- /dev/null +++ b/Makefile @@ -0,0 +1,65 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +include $(GOROOT)/src/Make.inc + +all: install + +install: + cd proto && gomake install + cd compiler/descriptor && gomake install + cd compiler/plugin && gomake install + cd compiler && gomake install + +test: + cd proto && gomake test + cd compiler && gomake test + +clean: + cd proto && gomake clean + cd proto/testdata && gomake clean + cd compiler && gomake clean + cd compiler/descriptor && gomake clean + cd compiler/plugin && gomake clean + cd compiler/testdata && gomake clean + cd compiler/descriptor && gomake clean + cd compiler/plugin && gomake clean + +nuke: + cd proto && gomake nuke + cd proto/testdata && gomake nuke + cd compiler/descriptor && gomake nuke + cd compiler/plugin && gomake nuke + cd compiler && gomake nuke + cd compiler/testdata && gomake nuke + cd compiler/descriptor && gomake nuke + cd compiler/plugin && gomake nuke diff --git a/README b/README new file mode 100644 index 0000000..487c7f7 --- /dev/null +++ b/README @@ -0,0 +1,162 @@ +Go support for Protocol Buffers - Google's data interchange format +Copyright 2010 Google Inc. +http://code.google.com/p/goprotobuf/ + +This software implements Go bindings for protocol buffers. For +information about protocol buffers themselves, see + http://code.google.com/apis/protocolbuffers/ +To use this software, you must first install the standard C++ +implementation of protocol buffers from + http://code.google.com/p/protobuf/ +And of course you must also install the Go compiler and tools from + http://code.google.com/p/go/ +See + http://golang.org/doc/install.html +for details or, if you are using gccgo, follow the instructions at + http://golang.org/doc/gccgo_install.html + +This software has two parts: a 'protocol compiler plugin' that +generates Go source files that, once compiled, can access and manage +protocol buffers; and a library that implements run-time support for +encoding (marshaling), decoding (unmarshaling), and accessing protocol +buffers. + +There is no support for RPC in Go using protocol buffers. It may come +once a standard RPC protocol develops for protobufs. + +There are no insertion points in the plugin. + +To install this code: + +The simplest way is to run goinstall. + + # Grab the code from the repository and install the proto package. + goinstall goprotobuf.googlecode.com/hg/proto + + # Compile and install the compiler plugin + cd $GOROOT/src/pkg/goprotobuf.googlecode.com/hg/compiler + make install + +The compiler plugin, protoc-gen-go, will be installed in $GOBIN, +defaulting to $HOME/bin. It must be in your $PATH for the protocol +compiler, protoc, to find it. + +Once the software is installed, there are two steps to using it. +First you must compile the protocol buffer definitions and then import +them, with the support library, into your program. + +To compile the protocol buffer definition, write a Makefile in the +style shown in the comment in the file Make.protobuf. If your Makefile +includes Make.protobuf, the rest should follow automatically. The +generated code can be compiled separately or as part of a normal Go +package. + +The generated files will be suffixed .pb.go. See the Test code below +for an example using such a file. + +This repository uses the same code review mechanism as Go, so +if you wish to submit changes add the equivalent of these two lines +to $GOROOT/src/pkg/goprotobuf.googlecode.com/hg/.hg/hgrc + + [extensions] + codereview = $GOROOT/lib/codereview/codereview.py + +*where $GOROOT is the expanded text, such as /usr/foo/go*. + +The package comment for the proto library contains text describing +the interface provided in Go for protocol buffers. Here is an edited +version. + +========== + +The proto package converts data structures to and from the +wire format of protocol buffers. It works in concert with the +Go source code generated for .proto files by the protocol compiler. + +A summary of the properties of the protocol buffer interface +for a protocol buffer variable v: + + - Names are turned from camel_case to CamelCase for export. + - There are no methods on v to set and get fields; just treat + them as structure fields. + - The zero value for a struct is its correct initialization state. + All desired fields must be set before marshaling. + - A Reset() method will restore a protobuf struct to its zero state. + - Non-repeated fields are pointers to the values; nil means unset. + That is, optional or required field int32 f becomes F *int32. + - Repeated fields are slices. + - Helper functions are available to simplify the getting and setting of fields: + foo.String = proto.String("hello") // set field + s := proto.GetString(foo.String) // get field + - Constants are defined to hold the default values of all fields that + have them. They have the form Default_StructName_FieldName. + - Enums are given type names and maps between names to values, + plus a helper function to create values. Enum values are prefixed + with the enum's type name. + - Nested groups and enums have type names prefixed with the name of + the surrounding message type. + - Marshal and Unmarshal are functions to encode and decode the wire format. + +Consider file test.proto, containing + + package example; + + enum FOO { X = 17; }; + + message Test { + required string label = 1; + optional int32 type = 2 [default=77]; + repeated int64 reps = 3; + optional group OptionalGroup = 4 { + required string RequiredField = 5; + }; + } + +To build a package from test.proto and some other Go files, write a +Makefile like this: + + include $(GOROOT)/src/Make.$(GOARCH) + + TARG=path/to/example + GOFILES=\ + test.pb.go\ + other.go + + include $(GOROOT)/src/Make.pkg + include $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/Make.protobuf + + +To create and play with a Test object from the example package, + + package main + + import ( + "log" + + "goprotobuf.googlecode.com/hg/proto" + "path/to/example" + ) + + func main() { + test := &example.Test { + Label: proto.String("hello"), + Type: proto.Int32(17), + Optionalgroup: &example.Test_OptionalGroup { + RequiredField: proto.String("good bye"), + }, + } + data, err := proto.Marshal(test) + if err != nil { + log.Fatal("marshaling error: ", err) + } + newTest := &example.Test{} + err = proto.Unmarshal(data, newTest) + if err != nil { + log.Fatal("unmarshaling error: ", err) + } + // Now test and newTest contain the same data. + if proto.GetString(test.Label) != proto.GetString(newTest.Label) { + log.Fatalf("data mismatch %q != %q", proto.GetString(test.Label), proto.GetString(newTest.Label)) + } + // etc. + } diff --git a/compiler/Makefile b/compiler/Makefile new file mode 100644 index 0000000..75bbd0b --- /dev/null +++ b/compiler/Makefile @@ -0,0 +1,44 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +include $(GOROOT)/src/Make.inc + +TARG=protoc-gen-go +GOFILES=\ + main.go\ + +DEPS=generator descriptor plugin + +include $(GOROOT)/src/Make.cmd + +test: + cd testdata && make test diff --git a/compiler/descriptor/Makefile b/compiler/descriptor/Makefile new file mode 100644 index 0000000..ae052d6 --- /dev/null +++ b/compiler/descriptor/Makefile @@ -0,0 +1,54 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(GOROOT)/src/Make.inc + +TARG=goprotobuf.googlecode.com/hg/compiler/descriptor +GOFILES=\ + descriptor.pb.go\ + +DEPS=../../proto + +include $(GOROOT)/src/Make.pkg + +# Not stored here, but descriptor.proto is in http://code.google.com/p/protobuf +# at protobuf-2.3.0/src/google/protobuf/descriptor.proto +regenerate: + echo WARNING! THIS RULE IS PROBABLY NOT RIGHT FOR YOUR INSTALLATION + cd $(HOME)/protobuf-2.3.0/src && \ + protoc --go_out=. ./google/protobuf/descriptor.proto && \ + cp ./google/protobuf/descriptor.pb.go $(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/compiler/descriptor/descriptor.pb.go + +restore: + cp descriptor.pb.golden descriptor.pb.go + +preserve: + cp descriptor.pb.go descriptor.pb.golden diff --git a/compiler/descriptor/descriptor.pb.go b/compiler/descriptor/descriptor.pb.go new file mode 100644 index 0000000..507126e --- /dev/null +++ b/compiler/descriptor/descriptor.pb.go @@ -0,0 +1,485 @@ +// Code generated by protoc-gen-go from "google/protobuf/descriptor.proto" +// DO NOT EDIT! + +package google_protobuf + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + + +type FieldDescriptorProto_Type int32 + +const ( + FieldDescriptorProto_TYPE_DOUBLE = 1 + FieldDescriptorProto_TYPE_FLOAT = 2 + FieldDescriptorProto_TYPE_INT64 = 3 + FieldDescriptorProto_TYPE_UINT64 = 4 + FieldDescriptorProto_TYPE_INT32 = 5 + FieldDescriptorProto_TYPE_FIXED64 = 6 + FieldDescriptorProto_TYPE_FIXED32 = 7 + FieldDescriptorProto_TYPE_BOOL = 8 + FieldDescriptorProto_TYPE_STRING = 9 + FieldDescriptorProto_TYPE_GROUP = 10 + FieldDescriptorProto_TYPE_MESSAGE = 11 + FieldDescriptorProto_TYPE_BYTES = 12 + FieldDescriptorProto_TYPE_UINT32 = 13 + FieldDescriptorProto_TYPE_ENUM = 14 + FieldDescriptorProto_TYPE_SFIXED32 = 15 + FieldDescriptorProto_TYPE_SFIXED64 = 16 + FieldDescriptorProto_TYPE_SINT32 = 17 + FieldDescriptorProto_TYPE_SINT64 = 18 +) + +var FieldDescriptorProto_Type_name = map[int32]string{ + 1: "TYPE_DOUBLE", + 2: "TYPE_FLOAT", + 3: "TYPE_INT64", + 4: "TYPE_UINT64", + 5: "TYPE_INT32", + 6: "TYPE_FIXED64", + 7: "TYPE_FIXED32", + 8: "TYPE_BOOL", + 9: "TYPE_STRING", + 10: "TYPE_GROUP", + 11: "TYPE_MESSAGE", + 12: "TYPE_BYTES", + 13: "TYPE_UINT32", + 14: "TYPE_ENUM", + 15: "TYPE_SFIXED32", + 16: "TYPE_SFIXED64", + 17: "TYPE_SINT32", + 18: "TYPE_SINT64", +} +var FieldDescriptorProto_Type_value = map[string]int32{ + "TYPE_DOUBLE": 1, + "TYPE_FLOAT": 2, + "TYPE_INT64": 3, + "TYPE_UINT64": 4, + "TYPE_INT32": 5, + "TYPE_FIXED64": 6, + "TYPE_FIXED32": 7, + "TYPE_BOOL": 8, + "TYPE_STRING": 9, + "TYPE_GROUP": 10, + "TYPE_MESSAGE": 11, + "TYPE_BYTES": 12, + "TYPE_UINT32": 13, + "TYPE_ENUM": 14, + "TYPE_SFIXED32": 15, + "TYPE_SFIXED64": 16, + "TYPE_SINT32": 17, + "TYPE_SINT64": 18, +} + +func NewFieldDescriptorProto_Type(x int32) *FieldDescriptorProto_Type { + e := FieldDescriptorProto_Type(x) + return &e +} +func (x FieldDescriptorProto_Type) String() string { + return proto.EnumName(FieldDescriptorProto_Type_name, int32(x)) +} + +type FieldDescriptorProto_Label int32 + +const ( + FieldDescriptorProto_LABEL_OPTIONAL = 1 + FieldDescriptorProto_LABEL_REQUIRED = 2 + FieldDescriptorProto_LABEL_REPEATED = 3 +) + +var FieldDescriptorProto_Label_name = map[int32]string{ + 1: "LABEL_OPTIONAL", + 2: "LABEL_REQUIRED", + 3: "LABEL_REPEATED", +} +var FieldDescriptorProto_Label_value = map[string]int32{ + "LABEL_OPTIONAL": 1, + "LABEL_REQUIRED": 2, + "LABEL_REPEATED": 3, +} + +func NewFieldDescriptorProto_Label(x int32) *FieldDescriptorProto_Label { + e := FieldDescriptorProto_Label(x) + return &e +} +func (x FieldDescriptorProto_Label) String() string { + return proto.EnumName(FieldDescriptorProto_Label_name, int32(x)) +} + +type FileOptions_OptimizeMode int32 + +const ( + FileOptions_SPEED = 1 + FileOptions_CODE_SIZE = 2 + FileOptions_LITE_RUNTIME = 3 +) + +var FileOptions_OptimizeMode_name = map[int32]string{ + 1: "SPEED", + 2: "CODE_SIZE", + 3: "LITE_RUNTIME", +} +var FileOptions_OptimizeMode_value = map[string]int32{ + "SPEED": 1, + "CODE_SIZE": 2, + "LITE_RUNTIME": 3, +} + +func NewFileOptions_OptimizeMode(x int32) *FileOptions_OptimizeMode { + e := FileOptions_OptimizeMode(x) + return &e +} +func (x FileOptions_OptimizeMode) String() string { + return proto.EnumName(FileOptions_OptimizeMode_name, int32(x)) +} + +type FieldOptions_CType int32 + +const ( + FieldOptions_STRING = 0 + FieldOptions_CORD = 1 + FieldOptions_STRING_PIECE = 2 +) + +var FieldOptions_CType_name = map[int32]string{ + 0: "STRING", + 1: "CORD", + 2: "STRING_PIECE", +} +var FieldOptions_CType_value = map[string]int32{ + "STRING": 0, + "CORD": 1, + "STRING_PIECE": 2, +} + +func NewFieldOptions_CType(x int32) *FieldOptions_CType { + e := FieldOptions_CType(x) + return &e +} +func (x FieldOptions_CType) String() string { + return proto.EnumName(FieldOptions_CType_name, int32(x)) +} + +type FileDescriptorSet struct { + File []*FileDescriptorProto `protobuf:"bytes,1,rep,name=file"` + XXX_unrecognized []byte +} + +func (this *FileDescriptorSet) Reset() { *this = FileDescriptorSet{} } +func (this *FileDescriptorSet) String() string { return proto.CompactTextString(this) } + +type FileDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Package *string `protobuf:"bytes,2,opt,name=package"` + Dependency []string `protobuf:"bytes,3,rep,name=dependency"` + PublicDependency []int32 `protobuf:"varint,10,rep,name=public_dependency"` + MessageType []*DescriptorProto `protobuf:"bytes,4,rep,name=message_type"` + EnumType []*EnumDescriptorProto `protobuf:"bytes,5,rep,name=enum_type"` + Service []*ServiceDescriptorProto `protobuf:"bytes,6,rep,name=service"` + Extension []*FieldDescriptorProto `protobuf:"bytes,7,rep,name=extension"` + Options *FileOptions `protobuf:"bytes,8,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *FileDescriptorProto) Reset() { *this = FileDescriptorProto{} } +func (this *FileDescriptorProto) String() string { return proto.CompactTextString(this) } + +type DescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Field []*FieldDescriptorProto `protobuf:"bytes,2,rep,name=field"` + Extension []*FieldDescriptorProto `protobuf:"bytes,6,rep,name=extension"` + NestedType []*DescriptorProto `protobuf:"bytes,3,rep,name=nested_type"` + EnumType []*EnumDescriptorProto `protobuf:"bytes,4,rep,name=enum_type"` + ExtensionRange []*DescriptorProto_ExtensionRange `protobuf:"bytes,5,rep,name=extension_range"` + Options *MessageOptions `protobuf:"bytes,7,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *DescriptorProto) Reset() { *this = DescriptorProto{} } +func (this *DescriptorProto) String() string { return proto.CompactTextString(this) } + +type DescriptorProto_ExtensionRange struct { + Start *int32 `protobuf:"varint,1,opt,name=start"` + End *int32 `protobuf:"varint,2,opt,name=end"` + XXX_unrecognized []byte +} + +func (this *DescriptorProto_ExtensionRange) Reset() { *this = DescriptorProto_ExtensionRange{} } +func (this *DescriptorProto_ExtensionRange) String() string { return proto.CompactTextString(this) } + +type FieldDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Number *int32 `protobuf:"varint,3,opt,name=number"` + Label *FieldDescriptorProto_Label `protobuf:"varint,4,opt,name=label,enum=google_protobuf.FieldDescriptorProto_Label"` + Type *FieldDescriptorProto_Type `protobuf:"varint,5,opt,name=type,enum=google_protobuf.FieldDescriptorProto_Type"` + TypeName *string `protobuf:"bytes,6,opt,name=type_name"` + Extendee *string `protobuf:"bytes,2,opt,name=extendee"` + DefaultValue *string `protobuf:"bytes,7,opt,name=default_value"` + Options *FieldOptions `protobuf:"bytes,8,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *FieldDescriptorProto) Reset() { *this = FieldDescriptorProto{} } +func (this *FieldDescriptorProto) String() string { return proto.CompactTextString(this) } + +type EnumDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Value []*EnumValueDescriptorProto `protobuf:"bytes,2,rep,name=value"` + Options *EnumOptions `protobuf:"bytes,3,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *EnumDescriptorProto) Reset() { *this = EnumDescriptorProto{} } +func (this *EnumDescriptorProto) String() string { return proto.CompactTextString(this) } + +type EnumValueDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Number *int32 `protobuf:"varint,2,opt,name=number"` + Options *EnumValueOptions `protobuf:"bytes,3,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *EnumValueDescriptorProto) Reset() { *this = EnumValueDescriptorProto{} } +func (this *EnumValueDescriptorProto) String() string { return proto.CompactTextString(this) } + +type ServiceDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Method []*MethodDescriptorProto `protobuf:"bytes,2,rep,name=method"` + Options *ServiceOptions `protobuf:"bytes,3,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *ServiceDescriptorProto) Reset() { *this = ServiceDescriptorProto{} } +func (this *ServiceDescriptorProto) String() string { return proto.CompactTextString(this) } + +type MethodDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + InputType *string `protobuf:"bytes,2,opt,name=input_type"` + OutputType *string `protobuf:"bytes,3,opt,name=output_type"` + Options *MethodOptions `protobuf:"bytes,4,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *MethodDescriptorProto) Reset() { *this = MethodDescriptorProto{} } +func (this *MethodDescriptorProto) String() string { return proto.CompactTextString(this) } + +type FileOptions struct { + JavaPackage *string `protobuf:"bytes,1,opt,name=java_package"` + JavaOuterClassname *string `protobuf:"bytes,8,opt,name=java_outer_classname"` + JavaMultipleFiles *bool `protobuf:"varint,10,opt,name=java_multiple_files,def=0"` + OptimizeFor *FileOptions_OptimizeMode `protobuf:"varint,9,opt,name=optimize_for,enum=google_protobuf.FileOptions_OptimizeMode,def=1"` + CcGenericServices *bool `protobuf:"varint,16,opt,name=cc_generic_services,def=1"` + JavaGenericServices *bool `protobuf:"varint,17,opt,name=java_generic_services,def=1"` + PyGenericServices *bool `protobuf:"varint,18,opt,name=py_generic_services,def=1"` + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *FileOptions) Reset() { *this = FileOptions{} } +func (this *FileOptions) String() string { return proto.CompactTextString(this) } + +var extRange_FileOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FileOptions +} +func (this *FileOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +const Default_FileOptions_JavaMultipleFiles bool = false +const Default_FileOptions_OptimizeFor FileOptions_OptimizeMode = FileOptions_SPEED +const Default_FileOptions_CcGenericServices bool = true +const Default_FileOptions_JavaGenericServices bool = true +const Default_FileOptions_PyGenericServices bool = true + +type MessageOptions struct { + MessageSetWireFormat *bool `protobuf:"varint,1,opt,name=message_set_wire_format,def=0"` + NoStandardDescriptorAccessor *bool `protobuf:"varint,2,opt,name=no_standard_descriptor_accessor,def=0"` + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *MessageOptions) Reset() { *this = MessageOptions{} } +func (this *MessageOptions) String() string { return proto.CompactTextString(this) } + +var extRange_MessageOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MessageOptions +} +func (this *MessageOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +const Default_MessageOptions_MessageSetWireFormat bool = false +const Default_MessageOptions_NoStandardDescriptorAccessor bool = false + +type FieldOptions struct { + Ctype *FieldOptions_CType `protobuf:"varint,1,opt,name=ctype,enum=google_protobuf.FieldOptions_CType,def=0"` + Packed *bool `protobuf:"varint,2,opt,name=packed"` + Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0"` + ExperimentalMapKey *string `protobuf:"bytes,9,opt,name=experimental_map_key"` + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *FieldOptions) Reset() { *this = FieldOptions{} } +func (this *FieldOptions) String() string { return proto.CompactTextString(this) } + +var extRange_FieldOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FieldOptions +} +func (this *FieldOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING +const Default_FieldOptions_Deprecated bool = false + +type EnumOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *EnumOptions) Reset() { *this = EnumOptions{} } +func (this *EnumOptions) String() string { return proto.CompactTextString(this) } + +var extRange_EnumOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnumOptions +} +func (this *EnumOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type EnumValueOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *EnumValueOptions) Reset() { *this = EnumValueOptions{} } +func (this *EnumValueOptions) String() string { return proto.CompactTextString(this) } + +var extRange_EnumValueOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnumValueOptions +} +func (this *EnumValueOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type ServiceOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *ServiceOptions) Reset() { *this = ServiceOptions{} } +func (this *ServiceOptions) String() string { return proto.CompactTextString(this) } + +var extRange_ServiceOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ServiceOptions +} +func (this *ServiceOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type MethodOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *MethodOptions) Reset() { *this = MethodOptions{} } +func (this *MethodOptions) String() string { return proto.CompactTextString(this) } + +var extRange_MethodOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MethodOptions +} +func (this *MethodOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type UninterpretedOption struct { + Name []*UninterpretedOption_NamePart `protobuf:"bytes,2,rep,name=name"` + IdentifierValue *string `protobuf:"bytes,3,opt,name=identifier_value"` + PositiveIntValue *uint64 `protobuf:"varint,4,opt,name=positive_int_value"` + NegativeIntValue *int64 `protobuf:"varint,5,opt,name=negative_int_value"` + DoubleValue *float64 `protobuf:"fixed64,6,opt,name=double_value"` + StringValue []byte `protobuf:"bytes,7,opt,name=string_value"` + XXX_unrecognized []byte +} + +func (this *UninterpretedOption) Reset() { *this = UninterpretedOption{} } +func (this *UninterpretedOption) String() string { return proto.CompactTextString(this) } + +type UninterpretedOption_NamePart struct { + NamePart *string `protobuf:"bytes,1,req,name=name_part"` + IsExtension *bool `protobuf:"varint,2,req,name=is_extension"` + XXX_unrecognized []byte +} + +func (this *UninterpretedOption_NamePart) Reset() { *this = UninterpretedOption_NamePart{} } +func (this *UninterpretedOption_NamePart) String() string { return proto.CompactTextString(this) } + +func init() { + proto.RegisterEnum("google_protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) + proto.RegisterEnum("google_protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) + proto.RegisterEnum("google_protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) + proto.RegisterEnum("google_protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) +} diff --git a/compiler/descriptor/descriptor.pb.golden b/compiler/descriptor/descriptor.pb.golden new file mode 100644 index 0000000..507126e --- /dev/null +++ b/compiler/descriptor/descriptor.pb.golden @@ -0,0 +1,485 @@ +// Code generated by protoc-gen-go from "google/protobuf/descriptor.proto" +// DO NOT EDIT! + +package google_protobuf + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + + +type FieldDescriptorProto_Type int32 + +const ( + FieldDescriptorProto_TYPE_DOUBLE = 1 + FieldDescriptorProto_TYPE_FLOAT = 2 + FieldDescriptorProto_TYPE_INT64 = 3 + FieldDescriptorProto_TYPE_UINT64 = 4 + FieldDescriptorProto_TYPE_INT32 = 5 + FieldDescriptorProto_TYPE_FIXED64 = 6 + FieldDescriptorProto_TYPE_FIXED32 = 7 + FieldDescriptorProto_TYPE_BOOL = 8 + FieldDescriptorProto_TYPE_STRING = 9 + FieldDescriptorProto_TYPE_GROUP = 10 + FieldDescriptorProto_TYPE_MESSAGE = 11 + FieldDescriptorProto_TYPE_BYTES = 12 + FieldDescriptorProto_TYPE_UINT32 = 13 + FieldDescriptorProto_TYPE_ENUM = 14 + FieldDescriptorProto_TYPE_SFIXED32 = 15 + FieldDescriptorProto_TYPE_SFIXED64 = 16 + FieldDescriptorProto_TYPE_SINT32 = 17 + FieldDescriptorProto_TYPE_SINT64 = 18 +) + +var FieldDescriptorProto_Type_name = map[int32]string{ + 1: "TYPE_DOUBLE", + 2: "TYPE_FLOAT", + 3: "TYPE_INT64", + 4: "TYPE_UINT64", + 5: "TYPE_INT32", + 6: "TYPE_FIXED64", + 7: "TYPE_FIXED32", + 8: "TYPE_BOOL", + 9: "TYPE_STRING", + 10: "TYPE_GROUP", + 11: "TYPE_MESSAGE", + 12: "TYPE_BYTES", + 13: "TYPE_UINT32", + 14: "TYPE_ENUM", + 15: "TYPE_SFIXED32", + 16: "TYPE_SFIXED64", + 17: "TYPE_SINT32", + 18: "TYPE_SINT64", +} +var FieldDescriptorProto_Type_value = map[string]int32{ + "TYPE_DOUBLE": 1, + "TYPE_FLOAT": 2, + "TYPE_INT64": 3, + "TYPE_UINT64": 4, + "TYPE_INT32": 5, + "TYPE_FIXED64": 6, + "TYPE_FIXED32": 7, + "TYPE_BOOL": 8, + "TYPE_STRING": 9, + "TYPE_GROUP": 10, + "TYPE_MESSAGE": 11, + "TYPE_BYTES": 12, + "TYPE_UINT32": 13, + "TYPE_ENUM": 14, + "TYPE_SFIXED32": 15, + "TYPE_SFIXED64": 16, + "TYPE_SINT32": 17, + "TYPE_SINT64": 18, +} + +func NewFieldDescriptorProto_Type(x int32) *FieldDescriptorProto_Type { + e := FieldDescriptorProto_Type(x) + return &e +} +func (x FieldDescriptorProto_Type) String() string { + return proto.EnumName(FieldDescriptorProto_Type_name, int32(x)) +} + +type FieldDescriptorProto_Label int32 + +const ( + FieldDescriptorProto_LABEL_OPTIONAL = 1 + FieldDescriptorProto_LABEL_REQUIRED = 2 + FieldDescriptorProto_LABEL_REPEATED = 3 +) + +var FieldDescriptorProto_Label_name = map[int32]string{ + 1: "LABEL_OPTIONAL", + 2: "LABEL_REQUIRED", + 3: "LABEL_REPEATED", +} +var FieldDescriptorProto_Label_value = map[string]int32{ + "LABEL_OPTIONAL": 1, + "LABEL_REQUIRED": 2, + "LABEL_REPEATED": 3, +} + +func NewFieldDescriptorProto_Label(x int32) *FieldDescriptorProto_Label { + e := FieldDescriptorProto_Label(x) + return &e +} +func (x FieldDescriptorProto_Label) String() string { + return proto.EnumName(FieldDescriptorProto_Label_name, int32(x)) +} + +type FileOptions_OptimizeMode int32 + +const ( + FileOptions_SPEED = 1 + FileOptions_CODE_SIZE = 2 + FileOptions_LITE_RUNTIME = 3 +) + +var FileOptions_OptimizeMode_name = map[int32]string{ + 1: "SPEED", + 2: "CODE_SIZE", + 3: "LITE_RUNTIME", +} +var FileOptions_OptimizeMode_value = map[string]int32{ + "SPEED": 1, + "CODE_SIZE": 2, + "LITE_RUNTIME": 3, +} + +func NewFileOptions_OptimizeMode(x int32) *FileOptions_OptimizeMode { + e := FileOptions_OptimizeMode(x) + return &e +} +func (x FileOptions_OptimizeMode) String() string { + return proto.EnumName(FileOptions_OptimizeMode_name, int32(x)) +} + +type FieldOptions_CType int32 + +const ( + FieldOptions_STRING = 0 + FieldOptions_CORD = 1 + FieldOptions_STRING_PIECE = 2 +) + +var FieldOptions_CType_name = map[int32]string{ + 0: "STRING", + 1: "CORD", + 2: "STRING_PIECE", +} +var FieldOptions_CType_value = map[string]int32{ + "STRING": 0, + "CORD": 1, + "STRING_PIECE": 2, +} + +func NewFieldOptions_CType(x int32) *FieldOptions_CType { + e := FieldOptions_CType(x) + return &e +} +func (x FieldOptions_CType) String() string { + return proto.EnumName(FieldOptions_CType_name, int32(x)) +} + +type FileDescriptorSet struct { + File []*FileDescriptorProto `protobuf:"bytes,1,rep,name=file"` + XXX_unrecognized []byte +} + +func (this *FileDescriptorSet) Reset() { *this = FileDescriptorSet{} } +func (this *FileDescriptorSet) String() string { return proto.CompactTextString(this) } + +type FileDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Package *string `protobuf:"bytes,2,opt,name=package"` + Dependency []string `protobuf:"bytes,3,rep,name=dependency"` + PublicDependency []int32 `protobuf:"varint,10,rep,name=public_dependency"` + MessageType []*DescriptorProto `protobuf:"bytes,4,rep,name=message_type"` + EnumType []*EnumDescriptorProto `protobuf:"bytes,5,rep,name=enum_type"` + Service []*ServiceDescriptorProto `protobuf:"bytes,6,rep,name=service"` + Extension []*FieldDescriptorProto `protobuf:"bytes,7,rep,name=extension"` + Options *FileOptions `protobuf:"bytes,8,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *FileDescriptorProto) Reset() { *this = FileDescriptorProto{} } +func (this *FileDescriptorProto) String() string { return proto.CompactTextString(this) } + +type DescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Field []*FieldDescriptorProto `protobuf:"bytes,2,rep,name=field"` + Extension []*FieldDescriptorProto `protobuf:"bytes,6,rep,name=extension"` + NestedType []*DescriptorProto `protobuf:"bytes,3,rep,name=nested_type"` + EnumType []*EnumDescriptorProto `protobuf:"bytes,4,rep,name=enum_type"` + ExtensionRange []*DescriptorProto_ExtensionRange `protobuf:"bytes,5,rep,name=extension_range"` + Options *MessageOptions `protobuf:"bytes,7,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *DescriptorProto) Reset() { *this = DescriptorProto{} } +func (this *DescriptorProto) String() string { return proto.CompactTextString(this) } + +type DescriptorProto_ExtensionRange struct { + Start *int32 `protobuf:"varint,1,opt,name=start"` + End *int32 `protobuf:"varint,2,opt,name=end"` + XXX_unrecognized []byte +} + +func (this *DescriptorProto_ExtensionRange) Reset() { *this = DescriptorProto_ExtensionRange{} } +func (this *DescriptorProto_ExtensionRange) String() string { return proto.CompactTextString(this) } + +type FieldDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Number *int32 `protobuf:"varint,3,opt,name=number"` + Label *FieldDescriptorProto_Label `protobuf:"varint,4,opt,name=label,enum=google_protobuf.FieldDescriptorProto_Label"` + Type *FieldDescriptorProto_Type `protobuf:"varint,5,opt,name=type,enum=google_protobuf.FieldDescriptorProto_Type"` + TypeName *string `protobuf:"bytes,6,opt,name=type_name"` + Extendee *string `protobuf:"bytes,2,opt,name=extendee"` + DefaultValue *string `protobuf:"bytes,7,opt,name=default_value"` + Options *FieldOptions `protobuf:"bytes,8,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *FieldDescriptorProto) Reset() { *this = FieldDescriptorProto{} } +func (this *FieldDescriptorProto) String() string { return proto.CompactTextString(this) } + +type EnumDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Value []*EnumValueDescriptorProto `protobuf:"bytes,2,rep,name=value"` + Options *EnumOptions `protobuf:"bytes,3,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *EnumDescriptorProto) Reset() { *this = EnumDescriptorProto{} } +func (this *EnumDescriptorProto) String() string { return proto.CompactTextString(this) } + +type EnumValueDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Number *int32 `protobuf:"varint,2,opt,name=number"` + Options *EnumValueOptions `protobuf:"bytes,3,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *EnumValueDescriptorProto) Reset() { *this = EnumValueDescriptorProto{} } +func (this *EnumValueDescriptorProto) String() string { return proto.CompactTextString(this) } + +type ServiceDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + Method []*MethodDescriptorProto `protobuf:"bytes,2,rep,name=method"` + Options *ServiceOptions `protobuf:"bytes,3,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *ServiceDescriptorProto) Reset() { *this = ServiceDescriptorProto{} } +func (this *ServiceDescriptorProto) String() string { return proto.CompactTextString(this) } + +type MethodDescriptorProto struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + InputType *string `protobuf:"bytes,2,opt,name=input_type"` + OutputType *string `protobuf:"bytes,3,opt,name=output_type"` + Options *MethodOptions `protobuf:"bytes,4,opt,name=options"` + XXX_unrecognized []byte +} + +func (this *MethodDescriptorProto) Reset() { *this = MethodDescriptorProto{} } +func (this *MethodDescriptorProto) String() string { return proto.CompactTextString(this) } + +type FileOptions struct { + JavaPackage *string `protobuf:"bytes,1,opt,name=java_package"` + JavaOuterClassname *string `protobuf:"bytes,8,opt,name=java_outer_classname"` + JavaMultipleFiles *bool `protobuf:"varint,10,opt,name=java_multiple_files,def=0"` + OptimizeFor *FileOptions_OptimizeMode `protobuf:"varint,9,opt,name=optimize_for,enum=google_protobuf.FileOptions_OptimizeMode,def=1"` + CcGenericServices *bool `protobuf:"varint,16,opt,name=cc_generic_services,def=1"` + JavaGenericServices *bool `protobuf:"varint,17,opt,name=java_generic_services,def=1"` + PyGenericServices *bool `protobuf:"varint,18,opt,name=py_generic_services,def=1"` + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *FileOptions) Reset() { *this = FileOptions{} } +func (this *FileOptions) String() string { return proto.CompactTextString(this) } + +var extRange_FileOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FileOptions +} +func (this *FileOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +const Default_FileOptions_JavaMultipleFiles bool = false +const Default_FileOptions_OptimizeFor FileOptions_OptimizeMode = FileOptions_SPEED +const Default_FileOptions_CcGenericServices bool = true +const Default_FileOptions_JavaGenericServices bool = true +const Default_FileOptions_PyGenericServices bool = true + +type MessageOptions struct { + MessageSetWireFormat *bool `protobuf:"varint,1,opt,name=message_set_wire_format,def=0"` + NoStandardDescriptorAccessor *bool `protobuf:"varint,2,opt,name=no_standard_descriptor_accessor,def=0"` + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *MessageOptions) Reset() { *this = MessageOptions{} } +func (this *MessageOptions) String() string { return proto.CompactTextString(this) } + +var extRange_MessageOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MessageOptions +} +func (this *MessageOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +const Default_MessageOptions_MessageSetWireFormat bool = false +const Default_MessageOptions_NoStandardDescriptorAccessor bool = false + +type FieldOptions struct { + Ctype *FieldOptions_CType `protobuf:"varint,1,opt,name=ctype,enum=google_protobuf.FieldOptions_CType,def=0"` + Packed *bool `protobuf:"varint,2,opt,name=packed"` + Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0"` + ExperimentalMapKey *string `protobuf:"bytes,9,opt,name=experimental_map_key"` + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *FieldOptions) Reset() { *this = FieldOptions{} } +func (this *FieldOptions) String() string { return proto.CompactTextString(this) } + +var extRange_FieldOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_FieldOptions +} +func (this *FieldOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING +const Default_FieldOptions_Deprecated bool = false + +type EnumOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *EnumOptions) Reset() { *this = EnumOptions{} } +func (this *EnumOptions) String() string { return proto.CompactTextString(this) } + +var extRange_EnumOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnumOptions +} +func (this *EnumOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type EnumValueOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *EnumValueOptions) Reset() { *this = EnumValueOptions{} } +func (this *EnumValueOptions) String() string { return proto.CompactTextString(this) } + +var extRange_EnumValueOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_EnumValueOptions +} +func (this *EnumValueOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type ServiceOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *ServiceOptions) Reset() { *this = ServiceOptions{} } +func (this *ServiceOptions) String() string { return proto.CompactTextString(this) } + +var extRange_ServiceOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ServiceOptions +} +func (this *ServiceOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type MethodOptions struct { + UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *MethodOptions) Reset() { *this = MethodOptions{} } +func (this *MethodOptions) String() string { return proto.CompactTextString(this) } + +var extRange_MethodOptions = []proto.ExtensionRange{ + proto.ExtensionRange{1000, 536870911}, +} + +func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MethodOptions +} +func (this *MethodOptions) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type UninterpretedOption struct { + Name []*UninterpretedOption_NamePart `protobuf:"bytes,2,rep,name=name"` + IdentifierValue *string `protobuf:"bytes,3,opt,name=identifier_value"` + PositiveIntValue *uint64 `protobuf:"varint,4,opt,name=positive_int_value"` + NegativeIntValue *int64 `protobuf:"varint,5,opt,name=negative_int_value"` + DoubleValue *float64 `protobuf:"fixed64,6,opt,name=double_value"` + StringValue []byte `protobuf:"bytes,7,opt,name=string_value"` + XXX_unrecognized []byte +} + +func (this *UninterpretedOption) Reset() { *this = UninterpretedOption{} } +func (this *UninterpretedOption) String() string { return proto.CompactTextString(this) } + +type UninterpretedOption_NamePart struct { + NamePart *string `protobuf:"bytes,1,req,name=name_part"` + IsExtension *bool `protobuf:"varint,2,req,name=is_extension"` + XXX_unrecognized []byte +} + +func (this *UninterpretedOption_NamePart) Reset() { *this = UninterpretedOption_NamePart{} } +func (this *UninterpretedOption_NamePart) String() string { return proto.CompactTextString(this) } + +func init() { + proto.RegisterEnum("google_protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) + proto.RegisterEnum("google_protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) + proto.RegisterEnum("google_protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) + proto.RegisterEnum("google_protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) +} diff --git a/compiler/doc.go b/compiler/doc.go new file mode 100644 index 0000000..f84ff8e --- /dev/null +++ b/compiler/doc.go @@ -0,0 +1,51 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + A plugin for the Google protocol buffer compiler to generate Go code. + Run it by building this program and putting it in your path with the name + protoc-gen-go + That word 'go' at the end becomes part of the option string set for the + protocol compiler, so once the protocol compiler (protoc) is installed + you can run + protoc --go_out=output_directory input_directory/file.proto + to generate Go bindings for the protocol defined by file.proto. + With that input, the output will be written to + output_directory/file.pb.go + + The generated code is documented in the package comment for + the library. + + See the README and documentation for protocol buffers to learn more: + http://code.google.com/p/protobuf/ + +*/ +package documentation diff --git a/compiler/generator/Makefile b/compiler/generator/Makefile new file mode 100644 index 0000000..c19eeb0 --- /dev/null +++ b/compiler/generator/Makefile @@ -0,0 +1,40 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(GOROOT)/src/Make.inc + +TARG=goprotobuf.googlecode.com/hg/compiler/generator +GOFILES=\ + generator.go\ + +DEPS=../descriptor ../plugin ../../proto + +include $(GOROOT)/src/Make.pkg diff --git a/compiler/generator/generator.go b/compiler/generator/generator.go new file mode 100644 index 0000000..ca1e576 --- /dev/null +++ b/compiler/generator/generator.go @@ -0,0 +1,1425 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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. + +/* + The code generator for the plugin for the Google protocol buffer compiler. + It generates Go code from the protocol buffer description files read by the + main routine. +*/ +package generator + +import ( + "bytes" + "fmt" + "go/parser" + "go/printer" + "go/token" + "log" + "os" + "path" + "strconv" + "strings" + + "goprotobuf.googlecode.com/hg/proto" + plugin "goprotobuf.googlecode.com/hg/compiler/plugin" + descriptor "goprotobuf.googlecode.com/hg/compiler/descriptor" +) + +// A Plugin provides functionality to add to the output during Go code generation, +// such as to produce RPC stubs. +type Plugin interface { + // Name identifies the plugin. + Name() string + // Init is called once after data structures are built but before + // code generation begins. + Init(g *Generator) + // Generate produces the code generated by the plugin for this file, + // except for the imports, by calling the generator's methods P, In, and Out. + Generate(file *FileDescriptor) + // GenerateImports produces the import declarations for this file. + // It is called after Generate. + GenerateImports(file *FileDescriptor) +} + +var plugins []Plugin + +// RegisterPlugin installs a (second-order) plugin to be run when the Go output is generated. +// It is typically called during initialization. +func RegisterPlugin(p Plugin) { + plugins = append(plugins, p) +} + +// Each type we import as a protocol buffer (other than FileDescriptorProto) needs +// a pointer to the FileDescriptorProto that represents it. These types achieve that +// wrapping by placing each Proto inside a struct with the pointer to its File. The +// structs have the same names as their contents, with "Proto" removed. +// FileDescriptor is used to store the things that it points to. + +// The file and package name method are common to messages and enums. +type common struct { + file *descriptor.FileDescriptorProto // File this object comes from. +} + +// PackageName is name in the package clause in the generated file. +func (c *common) PackageName() string { return uniquePackageOf(c.file) } + +func (c *common) File() *descriptor.FileDescriptorProto { return c.file } + +// Descriptor represents a protocol buffer message. +type Descriptor struct { + common + *descriptor.DescriptorProto + parent *Descriptor // The containing message, if any. + nested []*Descriptor // Inner messages, if any. + ext []*ExtensionDescriptor // Extensions, if any. + typename []string // Cached typename vector. +} + +// TypeName returns the elements of the dotted type name. +// The package name is not part of this name. +func (d *Descriptor) TypeName() []string { + if d.typename != nil { + return d.typename + } + n := 0 + for parent := d; parent != nil; parent = parent.parent { + n++ + } + s := make([]string, n, n) + for parent := d; parent != nil; parent = parent.parent { + n-- + s[n] = proto.GetString(parent.Name) + } + d.typename = s + return s +} + +// EnumDescriptor describes an enum. If it's at top level, its parent will be nil. +// Otherwise it will be the descriptor of the message in which it is defined. +type EnumDescriptor struct { + common + *descriptor.EnumDescriptorProto + parent *Descriptor // The containing message, if any. + typename []string // Cached typename vector. +} + +// TypeName returns the elements of the dotted type name. +// The package name is not part of this name. +func (e *EnumDescriptor) TypeName() (s []string) { + if e.typename != nil { + return e.typename + } + name := proto.GetString(e.Name) + if e.parent == nil { + s = make([]string, 1) + } else { + pname := e.parent.TypeName() + s = make([]string, len(pname)+1) + copy(s, pname) + } + s[len(s)-1] = name + e.typename = s + return s +} + +// Everything but the last element of the full type name, CamelCased. +// The values of type Foo.Bar are call Foo_value1... not Foo_Bar_value1... . +func (e *EnumDescriptor) prefix() string { + typeName := e.TypeName() + ccPrefix := CamelCaseSlice(typeName[0:len(typeName)-1]) + "_" + if e.parent == nil { + // If the enum is not part of a message, the prefix is just the type name. + ccPrefix = CamelCase(*e.Name) + "_" + } + return ccPrefix +} + +// The integer value of the named constant in this enumerated type. +func (e *EnumDescriptor) integerValueAsString(name string) string { + for _, c := range e.Value { + if proto.GetString(c.Name) == name { + return fmt.Sprint(proto.GetInt32(c.Number)) + } + } + log.Fatal("cannot find value for enum constant") + return "" +} + +// ExtensionDescriptor describes an extension. If it's at top level, its parent will be nil. +// Otherwise it will be the descriptor of the message in which it is defined. +type ExtensionDescriptor struct { + common + *descriptor.FieldDescriptorProto + parent *Descriptor // The containing message, if any. +} + +// TypeName returns the elements of the dotted type name. +// The package name is not part of this name. +func (e *ExtensionDescriptor) TypeName() (s []string) { + name := proto.GetString(e.Name) + if e.parent == nil { + // top-level extension + s = make([]string, 1) + } else { + pname := e.parent.TypeName() + s = make([]string, len(pname)+1) + copy(s, pname) + } + s[len(s)-1] = name + return s +} + +// DescName returns the variable name used for the generated descriptor. +func (e *ExtensionDescriptor) DescName() string { + // The full type name. + typeName := e.TypeName() + // Each scope of the extension is individually CamelCased, and all are joined with "_" with an "E_" prefix. + for i, s := range typeName { + typeName[i] = CamelCase(s) + } + return "E_" + strings.Join(typeName, "_") +} + +// ImportedDescriptor describes a type that has been publicly imported from another file. +type ImportedDescriptor struct { + common + o Object +} + +func (id *ImportedDescriptor) TypeName() []string { return id.o.TypeName() } + +// FileDescriptor describes an protocol buffer descriptor file (.proto). +// It includes slices of all the messages and enums defined within it. +// Those slices are constructed by WrapTypes. +type FileDescriptor struct { + *descriptor.FileDescriptorProto + desc []*Descriptor // All the messages defined in this file. + enum []*EnumDescriptor // All the enums defined in this file. + ext []*ExtensionDescriptor // All the top-level extensions defined in this file. + imp []*ImportedDescriptor // All types defined in files publicly imported by this file. + + // The full list of symbols that are exported, + // as a map from the exported object to its symbols. + // This is used for supporting public imports. + exported map[Object][]Symbol +} + +// PackageName is the package name we'll use in the generated code to refer to this file. +func (d *FileDescriptor) PackageName() string { return uniquePackageOf(d.FileDescriptorProto) } + +// The package named defined in the input for this file, possibly dotted. +// If the file does not define a package, use the base of the file name. +func (d *FileDescriptor) originalPackageName() string { + // Does the file have a package clause? + if pkg := proto.GetString(d.Package); pkg != "" { + return pkg + } + // Use the file base name. + return BaseName(proto.GetString(d.Name)) +} + +func (d *FileDescriptor) addExport(obj Object, symbol Symbol) { + d.exported[obj] = append(d.exported[obj], symbol) +} + +// Symbol is an interface representing an exported Go symbol. +type Symbol interface { + // GenerateAlias should generate an appropriate alias + // for the symbol from the named package. + GenerateAlias(g *Generator, pkg string) +} + +type messageSymbol struct { + sym string + hasExtensions, isMessageSet bool +} + +func (ms messageSymbol) GenerateAlias(g *Generator, pkg string) { + remoteSym := pkg + "." + ms.sym + + g.P("type ", ms.sym, " ", remoteSym) + g.P("func (this *", ms.sym, ") Reset() { (*", remoteSym, ")(this).Reset() }") + g.P("func (this *", ms.sym, ") String() string { return (*", remoteSym, ")(this).String() }") + if ms.hasExtensions { + g.P("func (*", ms.sym, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange ", + "{ return (*", remoteSym, ")(nil).ExtensionRangeArray() }") + g.P("func (this *", ms.sym, ") ExtensionMap() map[int32]", g.ProtoPkg, ".Extension ", + "{ return (*", remoteSym, ")(this).ExtensionMap() }") + if ms.isMessageSet { + g.P("func (this *", ms.sym, ") Marshal() ([]byte, os.Error) ", + "{ return (*", remoteSym, ")(this).Marshal() }") + g.P("func (this *", ms.sym, ") Unmarshal(buf []byte) os.Error ", + "{ return (*", remoteSym, ")(this).Unmarshal(buf) }") + } + } +} + +type enumSymbol string + +func (es enumSymbol) GenerateAlias(g *Generator, pkg string) { + s := string(es) + g.P("type ", s, " ", pkg, ".", s) + g.P("var ", s, "_name = ", pkg, ".", s, "_name") + g.P("var ", s, "_value = ", pkg, ".", s, "_value") + g.P("func New", s, "(x ", s, ") *", s, " { e := ", s, "(x); return &e }") +} + +type constOrVarSymbol struct { + sym string + typ string // either "const" or "var" +} + +func (cs constOrVarSymbol) GenerateAlias(g *Generator, pkg string) { + g.P(cs.typ, " ", cs.sym, " = ", pkg, ".", cs.sym) +} + +// Object is an interface abstracting the abilities shared by enums, messages, extensions and imported objects. +type Object interface { + PackageName() string // The name we use in our output (a_b_c), possibly renamed for uniqueness. + TypeName() []string + File() *descriptor.FileDescriptorProto +} + +// Each package name we generate must be unique. The package we're generating +// gets its own name but every other package must have a unique name that does +// not conflict in the code we generate. These names are chosen globally (although +// they don't have to be, it simplifies things to do them globally). +func uniquePackageOf(fd *descriptor.FileDescriptorProto) string { + s, ok := uniquePackageName[fd] + if !ok { + log.Fatal("internal error: no package name defined for", proto.GetString(fd.Name)) + } + return s +} + +// Generator is the type whose methods generate the output, stored in the associated response structure. +type Generator struct { + *bytes.Buffer + + Request *plugin.CodeGeneratorRequest // The input. + Response *plugin.CodeGeneratorResponse // The output. + + Param map[string]string // Command-line parameters. + ImportPrefix string // String to prefix to imported package file names. + ImportMap map[string]string // Mapping from import name to generated name + + ProtoPkg string // The name under which we import the library's package proto. + + packageName string // What we're calling ourselves. + allFiles []*FileDescriptor // All files in the tree + genFiles []*FileDescriptor // Those files we will generate output for. + file *FileDescriptor // The file we are compiling now. + usedPackages map[string]bool // Names of packages used in current file. + typeNameToObject map[string]Object // Key is a fully-qualified name in input syntax. + indent string +} + +// New creates a new generator and allocates the request and response protobufs. +func New() *Generator { + g := new(Generator) + g.Buffer = new(bytes.Buffer) + g.Request = new(plugin.CodeGeneratorRequest) + g.Response = new(plugin.CodeGeneratorResponse) + return g +} + +// Error reports a problem, including an os.Error, and exits the program. +func (g *Generator) Error(err os.Error, msgs ...string) { + s := strings.Join(msgs, " ") + ":" + err.String() + log.Println("protoc-gen-go: error:", s) + g.Response.Error = proto.String(s) + os.Exit(1) +} + +// Fail reports a problem and exits the program. +func (g *Generator) Fail(msgs ...string) { + s := strings.Join(msgs, " ") + log.Println("protoc-gen-go: error:", s) + g.Response.Error = proto.String(s) + os.Exit(1) +} + +// CommandLineParameters breaks the comma-separated list of key=value pairs +// in the parameter (a member of the request protobuf) into a key/value map. +// It then sets file name mappings defined by those entries. +func (g *Generator) CommandLineParameters(parameter string) { + g.Param = make(map[string]string) + for _, p := range strings.Split(parameter, ",") { + if i := strings.Index(p, "="); i < 0 { + g.Param[p] = "" + } else { + g.Param[p[0:i]] = p[i+1:] + } + } + + g.ImportMap = make(map[string]string) + for k, v := range g.Param { + if k == "import_prefix" { + g.ImportPrefix = v + } else if len(k) > 0 && k[0] == 'M' { + g.ImportMap[k[1:]] = v + } + } +} + +// DefaultPackageName returns the package name printed for the object. +// If its file is in a different package, it returns the package name we're using for this file, plus ".". +// Otherwise it returns the empty string. +func (g *Generator) DefaultPackageName(obj Object) string { + pkg := obj.PackageName() + if pkg == g.packageName { + return "" + } + return pkg + "." +} + +// For each input file, the unique package name to use, underscored. +var uniquePackageName = make(map[*descriptor.FileDescriptorProto]string) +// Package names already registered. Key is the name from the .proto file; +// value is the name that appears in the generated code. +var pkgNamesInUse = make(map[string]bool) + +// Create and remember a guaranteed unique package name for this file descriptor. +// Pkg is the candidate name. If f is nil, it's a builtin package like "proto" and +// has no file descriptor. +func RegisterUniquePackageName(pkg string, f *FileDescriptor) string { + // Convert dots to underscores before finding a unique alias. + pkg = strings.Map(DotToUnderscore, pkg) + + for i, orig := 1, pkg; pkgNamesInUse[pkg]; i++ { + // It's a duplicate; must rename. + pkg = orig + strconv.Itoa(i) + } + // Install it. + pkgNamesInUse[pkg] = true + if f != nil { + uniquePackageName[f.FileDescriptorProto] = pkg + } + return pkg +} + +// SetPackageNames sets the package name for this run. +// The package name must agree across all files being generated. +// It also defines unique package names for all imported files. +func (g *Generator) SetPackageNames() { + // Register the name for this package. It will be the first name + // registered so is guaranteed to be unmodified. + pkg := g.genFiles[0].originalPackageName() + g.packageName = RegisterUniquePackageName(pkg, g.genFiles[0]) + // Register the proto package name. It might collide with the + // name of a package we import. + g.ProtoPkg = RegisterUniquePackageName("proto", nil) + // Verify that we are generating output for a single package. + for _, f := range g.genFiles { + thisPkg := f.originalPackageName() + if thisPkg != pkg { + g.Fail("inconsistent package names:", thisPkg, pkg) + } + } +AllFiles: + for _, f := range g.allFiles { + for _, genf := range g.genFiles { + if f == genf { + // In this package already. + uniquePackageName[f.FileDescriptorProto] = g.packageName + continue AllFiles + } + } + // The file is a dependency, so we want to ignore its go_package option + // because that is only relevant for its specific generated output. + pkg := proto.GetString(f.Package) + if pkg == "" { + pkg = BaseName(*f.Name) + } + RegisterUniquePackageName(pkg, f) + } +} + +// WrapTypes walks the incoming data, wrapping DescriptorProtos, EnumDescriptorProtos +// and FileDescriptorProtos into file-referenced objects within the Generator. +// It also creates the list of files to generate and so should be called before GenerateAllFiles. +func (g *Generator) WrapTypes() { + g.allFiles = make([]*FileDescriptor, len(g.Request.ProtoFile)) + for i, f := range g.Request.ProtoFile { + // We must wrap the descriptors before we wrap the enums + descs := wrapDescriptors(f) + g.buildNestedDescriptors(descs) + enums := wrapEnumDescriptors(f, descs) + exts := wrapExtensions(f) + imps := wrapImported(f, g) + g.allFiles[i] = &FileDescriptor{ + FileDescriptorProto: f, + desc: descs, + enum: enums, + ext: exts, + imp: imps, + exported: make(map[Object][]Symbol), + } + } + + g.genFiles = make([]*FileDescriptor, len(g.Request.FileToGenerate)) +FindFiles: + for i, fileName := range g.Request.FileToGenerate { + // Search the list. This algorithm is n^2 but n is tiny. + for _, file := range g.allFiles { + if fileName == proto.GetString(file.Name) { + g.genFiles[i] = file + continue FindFiles + } + } + g.Fail("could not find file named", fileName) + } + g.Response.File = make([]*plugin.CodeGeneratorResponse_File, len(g.genFiles)) +} + +// Scan the descriptors in this file. For each one, build the slice of nested descriptors +func (g *Generator) buildNestedDescriptors(descs []*Descriptor) { + for _, desc := range descs { + if len(desc.NestedType) != 0 { + desc.nested = make([]*Descriptor, len(desc.NestedType)) + n := 0 + for _, nest := range descs { + if nest.parent == desc { + desc.nested[n] = nest + n++ + } + } + if n != len(desc.NestedType) { + g.Fail("internal error: nesting failure for", proto.GetString(desc.Name)) + } + } + } +} + +// Construct the Descriptor and add it to the slice +func addDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor { + d := &Descriptor{common{file}, desc, parent, nil, nil, nil} + + d.ext = make([]*ExtensionDescriptor, len(desc.Extension)) + for i, field := range desc.Extension { + d.ext[i] = &ExtensionDescriptor{common{file}, field, d} + } + + return append(sl, d) +} + +// Return a slice of all the Descriptors defined within this file +func wrapDescriptors(file *descriptor.FileDescriptorProto) []*Descriptor { + sl := make([]*Descriptor, 0, len(file.MessageType)+10) + for _, desc := range file.MessageType { + sl = wrapThisDescriptor(sl, desc, nil, file) + } + return sl +} + +// Wrap this Descriptor, recursively +func wrapThisDescriptor(sl []*Descriptor, desc *descriptor.DescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*Descriptor { + sl = addDescriptor(sl, desc, parent, file) + me := sl[len(sl)-1] + for _, nested := range desc.NestedType { + sl = wrapThisDescriptor(sl, nested, me, file) + } + return sl +} + +// Construct the EnumDescriptor and add it to the slice +func addEnumDescriptor(sl []*EnumDescriptor, desc *descriptor.EnumDescriptorProto, parent *Descriptor, file *descriptor.FileDescriptorProto) []*EnumDescriptor { + return append(sl, &EnumDescriptor{common{file}, desc, parent, nil}) +} + +// Return a slice of all the EnumDescriptors defined within this file +func wrapEnumDescriptors(file *descriptor.FileDescriptorProto, descs []*Descriptor) []*EnumDescriptor { + sl := make([]*EnumDescriptor, 0, len(file.EnumType)+10) + // Top-level enums. + for _, enum := range file.EnumType { + sl = addEnumDescriptor(sl, enum, nil, file) + } + // Enums within messages. Enums within embedded messages appear in the outer-most message. + for _, nested := range descs { + for _, enum := range nested.EnumType { + sl = addEnumDescriptor(sl, enum, nested, file) + } + } + return sl +} + +// Return a slice of all the top-level ExtensionDescriptors defined within this file. +func wrapExtensions(file *descriptor.FileDescriptorProto) []*ExtensionDescriptor { + sl := make([]*ExtensionDescriptor, len(file.Extension)) + for i, field := range file.Extension { + sl[i] = &ExtensionDescriptor{common{file}, field, nil} + } + return sl +} + +// Return a slice of all the types that are publicly imported into this file. +func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*ImportedDescriptor) { + for _, index := range file.PublicDependency { + df := g.fileByName(file.Dependency[index]) + for _, d := range df.desc { + sl = append(sl, &ImportedDescriptor{common{file}, d}) + } + for _, e := range df.enum { + sl = append(sl, &ImportedDescriptor{common{file}, e}) + } + for _, ext := range df.ext { + sl = append(sl, &ImportedDescriptor{common{file}, ext}) + } + } + return +} + +// BuildTypeNameMap builds the map from fully qualified type names to objects. +// The key names for the map come from the input data, which puts a period at the beginning. +// It should be called after SetPackageNames and before GenerateAllFiles. +func (g *Generator) BuildTypeNameMap() { + g.typeNameToObject = make(map[string]Object) + for _, f := range g.allFiles { + // The names in this loop are defined by the proto world, not us, so the + // package name may be empty. If so, the dotted package name of X will + // be ".X"; otherwise it will be ".pkg.X". + dottedPkg := "." + proto.GetString(f.Package) + if dottedPkg != "." { + dottedPkg += "." + } + for _, enum := range f.enum { + name := dottedPkg + dottedSlice(enum.TypeName()) + g.typeNameToObject[name] = enum + } + for _, desc := range f.desc { + name := dottedPkg + dottedSlice(desc.TypeName()) + g.typeNameToObject[name] = desc + } + } +} + +// ObjectNamed, given a fully-qualified input type name as it appears in the input data, +// returns the descriptor for the message or enum with that name. +func (g *Generator) ObjectNamed(typeName string) Object { + o, ok := g.typeNameToObject[typeName] + if !ok { + g.Fail("can't find object with type", typeName) + } + + // If the file of this object isn't a direct dependency of the current file, + // or in the current file, then this object has been publicly imported into + // a dependency of the current file. + // We should return the ImportedDescriptor object for it instead. + direct := *o.File().Name == *g.file.Name + if !direct { + for _, dep := range g.file.Dependency { + if *g.fileByName(dep).Name == *o.File().Name { + direct = true + break + } + } + } + if !direct { + found := false + Loop: + for _, dep := range g.file.Dependency { + df := g.fileByName(*g.fileByName(dep).Name) + for _, td := range df.imp { + if td.o == o { + // Found it! + o = td + found = true + break Loop + } + } + } + if !found { + log.Printf("protoc-gen-go: WARNING: failed finding publicly imported dependency for %v, used in %v", typeName, *g.file.Name) + } + } + + return o +} + +// P prints the arguments to the generated output. It handles strings and int32s, plus +// handling indirections because they may be *string, etc. +func (g *Generator) P(str ...interface{}) { + g.WriteString(g.indent) + for _, v := range str { + switch s := v.(type) { + case string: + g.WriteString(s) + case *string: + g.WriteString(*s) + case bool: + g.WriteString(fmt.Sprintf("%t", s)) + case *bool: + g.WriteString(fmt.Sprintf("%t", *s)) + case *int32: + g.WriteString(fmt.Sprintf("%d", *s)) + case float64: + g.WriteString(fmt.Sprintf("%g", s)) + case *float64: + g.WriteString(fmt.Sprintf("%g", *s)) + default: + g.Fail(fmt.Sprintf("unknown type in printer: %T", v)) + } + } + g.WriteByte('\n') +} + +// In Indents the output one tab stop. +func (g *Generator) In() { g.indent += "\t" } + +// Out unindents the output one tab stop. +func (g *Generator) Out() { + if len(g.indent) > 0 { + g.indent = g.indent[1:] + } +} + +// GenerateAllFiles generates the output for all the files we're outputting. +func (g *Generator) GenerateAllFiles() { + // Initialize the plugins + for _, p := range plugins { + p.Init(g) + } + // Generate the output. The generator runs for every file, even the files + // that we don't generate output for, so that we can collate the full list + // of exported symbols to support public imports. + genFileMap := make(map[*FileDescriptor]bool, len(g.genFiles)) + for _, file := range g.genFiles { + genFileMap[file] = true + } + i := 0 + for _, file := range g.allFiles { + g.Reset() + g.generate(file) + if _, ok := genFileMap[file]; !ok { + continue + } + g.Response.File[i] = new(plugin.CodeGeneratorResponse_File) + g.Response.File[i].Name = proto.String(goFileName(*file.Name)) + g.Response.File[i].Content = proto.String(g.String()) + i++ + } +} + +// Run all the plugins associated with the file. +func (g *Generator) runPlugins(file *FileDescriptor) { + for _, p := range plugins { + p.Generate(file) + } +} + +// FileOf return the FileDescriptor for this FileDescriptorProto. +func (g *Generator) FileOf(fd *descriptor.FileDescriptorProto) *FileDescriptor { + for _, file := range g.allFiles { + if file.FileDescriptorProto == fd { + return file + } + } + g.Fail("could not find file in table:", proto.GetString(fd.Name)) + return nil +} + +// Fill the response protocol buffer with the generated output for all the files we're +// supposed to generate. +func (g *Generator) generate(file *FileDescriptor) { + g.file = g.FileOf(file.FileDescriptorProto) + g.usedPackages = make(map[string]bool) + + for _, td := range g.file.imp { + g.generateImported(td) + } + for _, enum := range g.file.enum { + g.generateEnum(enum) + } + for _, desc := range g.file.desc { + g.generateMessage(desc) + } + for _, ext := range g.file.ext { + g.generateExtension(ext) + } + g.generateInitFunction() + + // Run the plugins before the imports so we know which imports are necessary. + g.runPlugins(file) + + // Generate header and imports last, though they appear first in the output. + rem := g.Buffer + g.Buffer = new(bytes.Buffer) + g.generateHeader() + g.generateImports() + g.Write(rem.Bytes()) + + // Reformat generated code. + fset := token.NewFileSet() + ast, err := parser.ParseFile(fset, "", g, parser.ParseComments) + if err != nil { + g.Fail("bad Go source code was generated:", err.String()) + return + } + g.Reset() + _, err = (&printer.Config{printer.TabIndent | printer.UseSpaces, 8}).Fprint(g, fset, ast) + if err != nil { + g.Fail("generated Go source code could not be reformatted:", err.String()) + } +} + +// Generate the header, including package definition and imports +func (g *Generator) generateHeader() { + g.P("// Code generated by protoc-gen-go from ", Quote(*g.file.Name)) + g.P("// DO NOT EDIT!") + g.P() + g.P("package ", g.file.PackageName()) + g.P() +} + +func (g *Generator) fileByName(filename string) *FileDescriptor { + for _, fd := range g.allFiles { + if proto.GetString(fd.Name) == filename { + return fd + } + } + return nil +} + +// Generate the header, including package definition and imports +func (g *Generator) generateImports() { + // We almost always need a proto import. Rather than computing when we + // do, which is tricky when there's a plugin, just import it and + // reference it later. The same argument applies to the os package. + g.P("import " + g.ProtoPkg + " " + Quote(g.ImportPrefix+"goprotobuf.googlecode.com/hg/proto")) + g.P(`import "math"`) + g.P(`import "os"`) + for _, s := range g.file.Dependency { + fd := g.fileByName(s) + // Do not import our own package. + if fd.PackageName() == g.packageName { + continue + } + filename := goFileName(s) + if substitution, ok := g.ImportMap[s]; ok { + filename = substitution + } + filename = g.ImportPrefix + filename + if strings.HasSuffix(filename, ".go") { + filename = filename[0 : len(filename)-3] + } + if _, ok := g.usedPackages[fd.PackageName()]; ok { + g.P("import ", fd.PackageName(), " ", Quote(filename)) + } else { + // TODO: Re-enable this when we are more feature-complete. + // For instance, some protos use foreign field extensions, which we don't support. + // Until then, this is just annoying spam. + //log.Printf("protoc-gen-go: discarding unused import from %v: %v", *g.file.Name, s) + g.P("// discarding unused import ", fd.PackageName(), " ", Quote(filename)) + } + } + g.P() + // TODO: may need to worry about uniqueness across plugins + for _, p := range plugins { + p.GenerateImports(g.file) + g.P() + } + g.P("// Reference proto, math & os imports to suppress error if they are not otherwise used.") + g.P("var _ = ", g.ProtoPkg, ".GetString") + g.P("var _ = math.Inf") + g.P("var _ os.Error") + g.P() +} + +func (g *Generator) generateImported(id *ImportedDescriptor) { + // Don't generate public import symbols for files that we are generating + // code for, since those symbols will already be in this package. + // We can't simply avoid creating the ImportedDescriptor objects, + // because g.genFiles isn't populated at that stage. + tn := id.TypeName() + sn := tn[len(tn)-1] + df := g.FileOf(id.o.File()) + filename := *df.Name + for _, fd := range g.genFiles { + if *fd.Name == filename { + g.P("// Ignoring public import of ", sn, " from ", filename) + g.P() + return + } + } + g.P("// ", sn, " from public import ", filename) + g.usedPackages[df.PackageName()] = true + + for _, sym := range df.exported[id.o] { + sym.GenerateAlias(g, df.PackageName()) + } + + g.P() +} + +// Generate the enum definitions for this EnumDescriptor. +func (g *Generator) generateEnum(enum *EnumDescriptor) { + // The full type name + typeName := enum.TypeName() + // The full type name, CamelCased. + ccTypeName := CamelCaseSlice(typeName) + ccPrefix := enum.prefix() + g.P("type ", ccTypeName, " int32") + g.file.addExport(enum, enumSymbol(ccTypeName)) + g.P("const (") + g.In() + for _, e := range enum.Value { + name := ccPrefix + *e.Name + g.P(name, " ", ccTypeName, " = ", e.Number) + g.file.addExport(enum, constOrVarSymbol{name, "const"}) + } + g.Out() + g.P(")") + g.P("var ", ccTypeName, "_name = map[int32]string{") + g.In() + generated := make(map[int32]bool) // avoid duplicate values + for _, e := range enum.Value { + duplicate := "" + if _, present := generated[*e.Number]; present { + duplicate = "// Duplicate value: " + } + g.P(duplicate, e.Number, ": ", Quote(*e.Name), ",") + generated[*e.Number] = true + } + g.Out() + g.P("}") + g.P("var ", ccTypeName, "_value = map[string]int32{") + g.In() + for _, e := range enum.Value { + g.P(Quote(*e.Name), ": ", e.Number, ",") + } + g.Out() + g.P("}") + + g.P("func New", ccTypeName, "(x ", ccTypeName, ") *", ccTypeName, " {") + g.In() + g.P("e := ", ccTypeName, "(x)") + g.P("return &e") + g.Out() + g.P("}") + + g.P("func (x ", ccTypeName, ") String() string {") + g.In() + g.P("return ", g.ProtoPkg, ".EnumName(", ccTypeName, "_name, int32(x))") + g.Out() + g.P("}") + + g.P() +} + +// The tag is a string like "varint,2,opt,name=fieldname,def=7" that +// identifies details of the field for the protocol buffer marshaling and unmarshaling +// code. The fields are: +// wire encoding +// protocol tag number +// opt,req,rep for optional, required, or repeated +// packed whether the encoding is "packed" (optional; repeated primitives only) +// name= the original declared name +// enum= the name of the enum type if it is an enum-typed field. +// def= string representation of the default value, if any. +// The default value must be in a representation that can be used at run-time +// to generate the default value. Thus bools become 0 and 1, for instance. +func (g *Generator) goTag(field *descriptor.FieldDescriptorProto, wiretype string) string { + optrepreq := "" + switch { + case isOptional(field): + optrepreq = "opt" + case isRequired(field): + optrepreq = "req" + case isRepeated(field): + optrepreq = "rep" + } + defaultValue := proto.GetString(field.DefaultValue) + if defaultValue != "" { + switch *field.Type { + case descriptor.FieldDescriptorProto_TYPE_BOOL: + if defaultValue == "true" { + defaultValue = "1" + } else { + defaultValue = "0" + } + case descriptor.FieldDescriptorProto_TYPE_STRING, + descriptor.FieldDescriptorProto_TYPE_BYTES: + // Nothing to do. Quoting is done for the whole tag. + case descriptor.FieldDescriptorProto_TYPE_ENUM: + // For enums we need to provide the integer constant. + obj := g.ObjectNamed(proto.GetString(field.TypeName)) + enum, ok := obj.(*EnumDescriptor) + if !ok { + g.Fail("enum type inconsistent for", CamelCaseSlice(obj.TypeName())) + } + defaultValue = enum.integerValueAsString(defaultValue) + } + defaultValue = ",def=" + defaultValue + } + enum := "" + if *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM { + // We avoid using obj.PackageName(), because we want to use the + // original (proto-world) package name. + obj := g.ObjectNamed(proto.GetString(field.TypeName)) + enum = ",enum=" + if pkg := proto.GetString(obj.File().Package); pkg != "" { + enum += pkg + "." + } + enum += CamelCaseSlice(obj.TypeName()) + } + packed := "" + if field.Options != nil && proto.GetBool(field.Options.Packed) { + packed = ",packed" + } + fieldName := proto.GetString(field.Name) + name := fieldName + if *field.Type == descriptor.FieldDescriptorProto_TYPE_GROUP { + // We must use the type name for groups instead of + // the field name to preserve capitalization. + // type_name in FieldDescriptorProto is fully-qualified, + // but we only want the local part. + name = *field.TypeName + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[i+1:] + } + } + if name == CamelCase(fieldName) { + name = "" + } else { + name = ",name=" + name + } + return Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s", + wiretype, + proto.GetInt32(field.Number), + optrepreq, + packed, + name, + enum, + defaultValue)) +} + +func needsStar(typ descriptor.FieldDescriptorProto_Type) bool { + switch typ { + case descriptor.FieldDescriptorProto_TYPE_GROUP: + return false + case descriptor.FieldDescriptorProto_TYPE_MESSAGE: + return false + case descriptor.FieldDescriptorProto_TYPE_BYTES: + return false + } + return true +} + +// TypeName is the printed name appropriate for an item. If the object is in the current file, +// TypeName drops the package name and underscores the rest. +// Otherwise the object is from another package; and the result is the underscored +// package name followed by the item name. +// The result always has an initial capital. +func (g *Generator) TypeName(obj Object) string { + return g.DefaultPackageName(obj) + CamelCaseSlice(obj.TypeName()) +} + +// TypeNameWithPackage is like TypeName, but always includes the package +// name even if the object is in our own package. +func (g *Generator) TypeNameWithPackage(obj Object) string { + return obj.PackageName() + CamelCaseSlice(obj.TypeName()) +} + +// GoType returns a string representing the type name, and the wire type +func (g *Generator) GoType(message *Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string) { + // TODO: Options. + switch *field.Type { + case descriptor.FieldDescriptorProto_TYPE_DOUBLE: + typ, wire = "float64", "fixed64" + case descriptor.FieldDescriptorProto_TYPE_FLOAT: + typ, wire = "float32", "fixed32" + case descriptor.FieldDescriptorProto_TYPE_INT64: + typ, wire = "int64", "varint" + case descriptor.FieldDescriptorProto_TYPE_UINT64: + typ, wire = "uint64", "varint" + case descriptor.FieldDescriptorProto_TYPE_INT32: + typ, wire = "int32", "varint" + case descriptor.FieldDescriptorProto_TYPE_UINT32: + typ, wire = "uint32", "varint" + case descriptor.FieldDescriptorProto_TYPE_FIXED64: + typ, wire = "uint64", "fixed64" + case descriptor.FieldDescriptorProto_TYPE_FIXED32: + typ, wire = "uint32", "fixed32" + case descriptor.FieldDescriptorProto_TYPE_BOOL: + typ, wire = "bool", "varint" + case descriptor.FieldDescriptorProto_TYPE_STRING: + typ, wire = "string", "bytes" + case descriptor.FieldDescriptorProto_TYPE_GROUP: + desc := g.ObjectNamed(proto.GetString(field.TypeName)) + typ, wire = "*"+g.TypeName(desc), "group" + case descriptor.FieldDescriptorProto_TYPE_MESSAGE: + desc := g.ObjectNamed(proto.GetString(field.TypeName)) + typ, wire = "*"+g.TypeName(desc), "bytes" + case descriptor.FieldDescriptorProto_TYPE_BYTES: + typ, wire = "[]byte", "bytes" + case descriptor.FieldDescriptorProto_TYPE_ENUM: + desc := g.ObjectNamed(proto.GetString(field.TypeName)) + typ, wire = g.TypeName(desc), "varint" + case descriptor.FieldDescriptorProto_TYPE_SFIXED32: + typ, wire = "int32", "fixed32" + case descriptor.FieldDescriptorProto_TYPE_SFIXED64: + typ, wire = "int64", "fixed64" + case descriptor.FieldDescriptorProto_TYPE_SINT32: + typ, wire = "int32", "zigzag32" + case descriptor.FieldDescriptorProto_TYPE_SINT64: + typ, wire = "int64", "zigzag64" + default: + g.Fail("unknown type for", proto.GetString(field.Name)) + } + if isRepeated(field) { + typ = "[]" + typ + } else if needsStar(*field.Type) { + typ = "*" + typ + } + return +} + +func (g *Generator) RecordTypeUse(t string) { + if obj, ok := g.typeNameToObject[t]; ok { + // Call ObjectNamed to get the true object to record the use. + obj = g.ObjectNamed(t) + g.usedPackages[obj.PackageName()] = true + } +} + +// Generate the type and default constant definitions for this Descriptor. +func (g *Generator) generateMessage(message *Descriptor) { + // The full type name + typeName := message.TypeName() + // The full type name, CamelCased. + ccTypeName := CamelCaseSlice(typeName) + + g.P("type ", ccTypeName, " struct {") + g.In() + for _, field := range message.Field { + fieldname := CamelCase(*field.Name) + typename, wiretype := g.GoType(message, field) + jsonName := *field.Name + tag := fmt.Sprintf("`protobuf:%s json:%q`", g.goTag(field, wiretype), jsonName+",omitempty") + g.P(fieldname, "\t", typename, "\t", tag) + g.RecordTypeUse(proto.GetString(field.TypeName)) + } + // TODO: Use `json:"-"` for these XXX_ fields when that makes it into a Go release. + if len(message.ExtensionRange) > 0 { + g.P("XXX_extensions\t\tmap[int32]", g.ProtoPkg, ".Extension `json:\",omitempty\"`") + } + g.P("XXX_unrecognized\t[]byte `json:\",omitempty\"`") + g.Out() + g.P("}") + + // Reset and String functions + g.P("func (this *", ccTypeName, ") Reset() { *this = ", ccTypeName, "{} }") + g.P("func (this *", ccTypeName, ") String() string { return ", g.ProtoPkg, ".CompactTextString(this) }") + + // Extension support methods + var hasExtensions, isMessageSet bool + if len(message.ExtensionRange) > 0 { + hasExtensions = true + // message_set_wire_format only makes sense when extensions are defined. + if opts := message.Options; opts != nil && proto.GetBool(opts.MessageSetWireFormat) { + isMessageSet = true + g.P() + g.P("func (this *", ccTypeName, ") Marshal() ([]byte, os.Error) {") + g.In() + g.P("return ", g.ProtoPkg, ".MarshalMessageSet(this.ExtensionMap())") + g.Out() + g.P("}") + g.P("func (this *", ccTypeName, ") Unmarshal(buf []byte) os.Error {") + g.In() + g.P("return ", g.ProtoPkg, ".UnmarshalMessageSet(buf, this.ExtensionMap())") + g.Out() + g.P("}") + g.P("// ensure ", ccTypeName, " satisfies proto.Marshaler and proto.Unmarshaler") + g.P("var _ ", g.ProtoPkg, ".Marshaler = (*", ccTypeName, ")(nil)") + g.P("var _ ", g.ProtoPkg, ".Unmarshaler = (*", ccTypeName, ")(nil)") + } + + g.P() + g.P("var extRange_", ccTypeName, " = []", g.ProtoPkg, ".ExtensionRange{") + g.In() + for _, r := range message.ExtensionRange { + end := fmt.Sprint(*r.End - 1) // make range inclusive on both ends + g.P("{", r.Start, ", ", end, "},") + } + g.Out() + g.P("}") + g.P("func (*", ccTypeName, ") ExtensionRangeArray() []", g.ProtoPkg, ".ExtensionRange {") + g.In() + g.P("return extRange_", ccTypeName) + g.Out() + g.P("}") + g.P("func (this *", ccTypeName, ") ExtensionMap() map[int32]", g.ProtoPkg, ".Extension {") + g.In() + g.P("if this.XXX_extensions == nil {") + g.In() + g.P("this.XXX_extensions = make(map[int32]", g.ProtoPkg, ".Extension)") + g.Out() + g.P("}") + g.P("return this.XXX_extensions") + g.Out() + g.P("}") + } + + g.file.addExport(message, messageSymbol{ccTypeName, hasExtensions, isMessageSet}) + + // Default constants + for _, field := range message.Field { + def := proto.GetString(field.DefaultValue) + if def == "" { + continue + } + fieldname := "Default_" + ccTypeName + "_" + CamelCase(*field.Name) + typename, _ := g.GoType(message, field) + if typename[0] == '*' { + typename = typename[1:] + } + kind := "const " + switch { + case typename == "bool": + case typename == "string": + def = Quote(def) + case typename == "[]byte": + def = "[]byte(" + Quote(def) + ")" + kind = "var " + case def == "inf", def == "-inf", def == "nan": + // These names are known to, and defined by, the protocol language. + switch def { + case "inf": + def = "math.Inf(1)" + case "-inf": + def = "math.Inf(-1)" + case "nan": + def = "math.NaN()" + } + if *field.Type == descriptor.FieldDescriptorProto_TYPE_FLOAT { + def = "float32(" + def + ")" + } + kind = "var " + case *field.Type == descriptor.FieldDescriptorProto_TYPE_ENUM: + // Must be an enum. Need to construct the prefixed name. + obj := g.ObjectNamed(proto.GetString(field.TypeName)) + enum, ok := obj.(*EnumDescriptor) + if !ok { + log.Println("don't know how to generate constant for", fieldname) + continue + } + def = g.DefaultPackageName(enum) + enum.prefix() + def + } + g.P(kind, fieldname, " ", typename, " = ", def) + g.file.addExport(message, constOrVarSymbol{fieldname, kind}) + } + g.P() + + for _, ext := range message.ext { + g.generateExtension(ext) + } +} + +func (g *Generator) generateExtension(ext *ExtensionDescriptor) { + ccTypeName := ext.DescName() + + extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee)) + field := ext.FieldDescriptorProto + fieldType, wireType := g.GoType(ext.parent, field) + tag := g.goTag(field, wireType) + g.RecordTypeUse(*ext.Extendee) + if n := ext.FieldDescriptorProto.TypeName; n != nil { + // foreign extension type + g.RecordTypeUse(*n) + } + + g.P("var ", ccTypeName, " = &", g.ProtoPkg, ".ExtensionDesc{") + g.In() + g.P("ExtendedType: (", extendedType, ")(nil),") + g.P("ExtensionType: (", fieldType, ")(nil),") + g.P("Field: ", field.Number, ",") + g.P(`Name: "`, g.packageName, ".", strings.Join(ext.TypeName(), "."), `",`) + g.P("Tag: ", tag, ",") + + g.Out() + g.P("}") + g.P() + + g.file.addExport(ext, constOrVarSymbol{ccTypeName, "var"}) +} + +func (g *Generator) generateInitFunction() { + g.P("func init() {") + g.In() + for _, enum := range g.file.enum { + g.generateEnumRegistration(enum) + } + for _, d := range g.file.desc { + for _, ext := range d.ext { + g.generateExtensionRegistration(ext) + } + } + for _, ext := range g.file.ext { + g.generateExtensionRegistration(ext) + } + g.Out() + g.P("}") +} + +func (g *Generator) generateEnumRegistration(enum *EnumDescriptor) { + // // We always print the full (proto-world) package name here. + pkg := proto.GetString(enum.File().Package) + if pkg != "" { + pkg += "." + } + // The full type name + typeName := enum.TypeName() + // The full type name, CamelCased. + ccTypeName := CamelCaseSlice(typeName) + g.P(g.ProtoPkg+".RegisterEnum(", Quote(pkg+ccTypeName), ", ", ccTypeName+"_name, ", ccTypeName+"_value)") +} + +func (g *Generator) generateExtensionRegistration(ext *ExtensionDescriptor) { + g.P(g.ProtoPkg+".RegisterExtension(", ext.DescName(), ")") +} + +// And now lots of helper functions. + +// Is c an ASCII lower-case letter? +func isASCIILower(c byte) bool { + return 'a' <= c && c <= 'z' +} + +// Is c an ASCII digit? +func isASCIIDigit(c byte) bool { + return '0' <= c && c <= '9' +} + +// CamelCase returns the CamelCased name. +// If there is an interior underscore followed by a lower case letter, +// drop the underscore and convert the letter to upper case. +// There is a remote possibility of this rewrite causing a name collision, +// but it's so remote we're prepared to pretend it's nonexistent - since the +// C++ generator lowercases names, it's extremely unlikely to have two fields +// with different capitalizations. +// In short, _my_field_name_2 becomes XMyFieldName2. +func CamelCase(s string) string { + if s == "" { + return "" + } + t := make([]byte, 0, 32) + i := 0 + if s[0] == '_' { + // Need a capital letter; drop the '_'. + t = append(t, 'X') + i++ + } + // Invariant: if the next letter is lower case, it must be converted + // to upper case. + // That is, we process a word at a time, where words are marked by _ or + // upper case letter. Digits are treated as words. + for ; i < len(s); i++ { + c := s[i] + if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) { + continue // Skip the underscore in s. + } + if isASCIIDigit(c) { + t = append(t, c) + continue + } + // Assume we have a letter now - if not, it's a bogus identifier. + // The next word is a sequence of characters that must start upper case. + if isASCIILower(c) { + c ^= ' ' // Make it a capital letter. + } + t = append(t, c) // Guaranteed not lower case. + // Accept lower case sequence that follows. + for i+1 < len(s) && isASCIILower(s[i+1]) { + i++ + t = append(t, s[i]) + } + } + return string(t) +} + +// CamelCaseSlice is like CamelCase, but the argument is a slice of strings to +// be joined with "_". +func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) } + +// dottedSlice turns a sliced name into a dotted name. +func dottedSlice(elem []string) string { return strings.Join(elem, ".") } + +// Quote returns a Go-source quoted string representation of s. +func Quote(s string) string { return fmt.Sprintf("%q", s) } + +// Given a .proto file name, return the output name for the generated Go program. +func goFileName(name string) string { + ext := path.Ext(name) + if ext == ".proto" || ext == ".protodevel" { + name = name[0 : len(name)-len(ext)] + } + return name + ".pb.go" +} + +// Is this field optional? +func isOptional(field *descriptor.FieldDescriptorProto) bool { + return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_OPTIONAL +} + +// Is this field required? +func isRequired(field *descriptor.FieldDescriptorProto) bool { + return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REQUIRED +} + +// Is this field repeated? +func isRepeated(field *descriptor.FieldDescriptorProto) bool { + return field.Label != nil && *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED +} + +// DotToUnderscore is the mapping function used to generate Go names from package names, +// which can be dotted in the input .proto file. It maps dots to underscores. +// Because we also get here from package names generated from file names, it also maps +// minus signs to underscores. +func DotToUnderscore(rune int) int { + switch rune { + case '.', '-': + return '_' + } + return rune +} + +// BaseName returns the last path element of the name, with the last dotted suffix removed. +func BaseName(name string) string { + // First, find the last element + if i := strings.LastIndex(name, "/"); i >= 0 { + name = name[i+1:] + } + // Now drop the suffix + if i := strings.LastIndex(name, "."); i >= 0 { + name = name[0:i] + } + return name +} diff --git a/compiler/main.go b/compiler/main.go new file mode 100644 index 0000000..9fd6070 --- /dev/null +++ b/compiler/main.go @@ -0,0 +1,89 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +/* + A plugin for the Google protocol buffer compiler to generate Go code. + + This plugin takes no options and the protocol buffer file syntax does + not yet define any options for Go, so program does no option evaluation. + That may change. +*/ + +package main + +import ( + "io/ioutil" + "os" + + "goprotobuf.googlecode.com/hg/proto" + "goprotobuf.googlecode.com/hg/compiler/generator" +) + +func main() { + // Begin by allocating a generator. The request and response structures are stored there + // so we can do error handling easily - the response structure contains the field to + // report failure. + g := generator.New() + + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + g.Error(err, "reading input") + } + + if err := proto.Unmarshal(data, g.Request); err != nil { + g.Error(err, "parsing input proto") + } + + if len(g.Request.FileToGenerate) == 0 { + g.Fail("no files to generate") + } + + g.CommandLineParameters(proto.GetString(g.Request.Parameter)) + + // Create a wrapped version of the Descriptors and EnumDescriptors that + // point to the file that defines them. + g.WrapTypes() + + g.SetPackageNames() + g.BuildTypeNameMap() + + g.GenerateAllFiles() + + // Send back the results. + data, err = proto.Marshal(g.Response) + if err != nil { + g.Error(err, "failed to marshal output proto") + } + _, err = os.Stdout.Write(data) + if err != nil { + g.Error(err, "failed to write output proto") + } +} diff --git a/compiler/plugin/Makefile b/compiler/plugin/Makefile new file mode 100644 index 0000000..e20482d --- /dev/null +++ b/compiler/plugin/Makefile @@ -0,0 +1,56 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(GOROOT)/src/Make.inc + +TARG=goprotobuf.googlecode.com/hg/compiler/plugin +GOFILES=\ + plugin.pb.go\ + +DEPS=../../proto ../descriptor + +include $(GOROOT)/src/Make.pkg + +# Not stored here, but plugin.proto is in http://code.google.com/p/protobuf +# at protobuf-2.3.0/src/google/protobuf/compiler/plugin.proto +# Also we need to fix an import. +regenerate: + echo WARNING! THIS RULE IS PROBABLY NOT RIGHT FOR YOUR INSTALLATION + cd $(HOME)/protobuf-2.3.0/src && \ + protoc --go_out=. ./google/protobuf/compiler/plugin.proto && \ + cat ./google/protobuf/compiler/plugin.pb.go | \ + sed '/^import/s;google/protobuf/descriptor.pb;goprotobuf.googlecode.com/hg/compiler/descriptor;' >$(GOROOT)/src/pkg/goprotobuf.googlecode.com/hg/compiler/plugin/plugin.pb.go + +restore: + cp plugin.pb.golden plugin.pb.go + +preserve: + cp plugin.pb.go plugin.pb.golden diff --git a/compiler/plugin/plugin.pb.go b/compiler/plugin/plugin.pb.go new file mode 100644 index 0000000..ac80dc4 --- /dev/null +++ b/compiler/plugin/plugin.pb.go @@ -0,0 +1,47 @@ +// Code generated by protoc-gen-go from "google/protobuf/compiler/plugin.proto" +// DO NOT EDIT! + +package google_protobuf_compiler + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" +import google_protobuf "goprotobuf.googlecode.com/hg/compiler/descriptor" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + + +type CodeGeneratorRequest struct { + FileToGenerate []string `protobuf:"bytes,1,rep,name=file_to_generate"` + Parameter *string `protobuf:"bytes,2,opt,name=parameter"` + ProtoFile []*google_protobuf.FileDescriptorProto `protobuf:"bytes,15,rep,name=proto_file"` + XXX_unrecognized []byte +} + +func (this *CodeGeneratorRequest) Reset() { *this = CodeGeneratorRequest{} } +func (this *CodeGeneratorRequest) String() string { return proto.CompactTextString(this) } + +type CodeGeneratorResponse struct { + Error *string `protobuf:"bytes,1,opt,name=error"` + File []*CodeGeneratorResponse_File `protobuf:"bytes,15,rep,name=file"` + XXX_unrecognized []byte +} + +func (this *CodeGeneratorResponse) Reset() { *this = CodeGeneratorResponse{} } +func (this *CodeGeneratorResponse) String() string { return proto.CompactTextString(this) } + +type CodeGeneratorResponse_File struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + InsertionPoint *string `protobuf:"bytes,2,opt,name=insertion_point"` + Content *string `protobuf:"bytes,15,opt,name=content"` + XXX_unrecognized []byte +} + +func (this *CodeGeneratorResponse_File) Reset() { *this = CodeGeneratorResponse_File{} } +func (this *CodeGeneratorResponse_File) String() string { return proto.CompactTextString(this) } + +func init() { +} diff --git a/compiler/plugin/plugin.pb.golden b/compiler/plugin/plugin.pb.golden new file mode 100644 index 0000000..ac80dc4 --- /dev/null +++ b/compiler/plugin/plugin.pb.golden @@ -0,0 +1,47 @@ +// Code generated by protoc-gen-go from "google/protobuf/compiler/plugin.proto" +// DO NOT EDIT! + +package google_protobuf_compiler + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" +import google_protobuf "goprotobuf.googlecode.com/hg/compiler/descriptor" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + + +type CodeGeneratorRequest struct { + FileToGenerate []string `protobuf:"bytes,1,rep,name=file_to_generate"` + Parameter *string `protobuf:"bytes,2,opt,name=parameter"` + ProtoFile []*google_protobuf.FileDescriptorProto `protobuf:"bytes,15,rep,name=proto_file"` + XXX_unrecognized []byte +} + +func (this *CodeGeneratorRequest) Reset() { *this = CodeGeneratorRequest{} } +func (this *CodeGeneratorRequest) String() string { return proto.CompactTextString(this) } + +type CodeGeneratorResponse struct { + Error *string `protobuf:"bytes,1,opt,name=error"` + File []*CodeGeneratorResponse_File `protobuf:"bytes,15,rep,name=file"` + XXX_unrecognized []byte +} + +func (this *CodeGeneratorResponse) Reset() { *this = CodeGeneratorResponse{} } +func (this *CodeGeneratorResponse) String() string { return proto.CompactTextString(this) } + +type CodeGeneratorResponse_File struct { + Name *string `protobuf:"bytes,1,opt,name=name"` + InsertionPoint *string `protobuf:"bytes,2,opt,name=insertion_point"` + Content *string `protobuf:"bytes,15,opt,name=content"` + XXX_unrecognized []byte +} + +func (this *CodeGeneratorResponse_File) Reset() { *this = CodeGeneratorResponse_File{} } +func (this *CodeGeneratorResponse_File) String() string { return proto.CompactTextString(this) } + +func init() { +} diff --git a/compiler/testdata/Makefile b/compiler/testdata/Makefile new file mode 100644 index 0000000..f1dcd50 --- /dev/null +++ b/compiler/testdata/Makefile @@ -0,0 +1,68 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include $(GOROOT)/src/Make.inc + +all: + @echo run make test + +include $(GOROOT)/src/Make.common +include ../../Make.protobuf + +CLEANFILES+=*.pb.go extension_test + +test: golden testbuild extension_test + ./extension_test + @echo PASS + +golden: + $(QUOTED_GOBIN)/gomake -B test.pb.go + diff -w test.pb.go test.pb.go.golden + +nuke: clean + +testbuild: main.$O + $(LD) -L. main.$O + +extension_test: extension_test.$O + $(LD) -L. -o $@ $< + +multi.a: multi3.pb.$O multi2.pb.$O multi1.pb.$O + rm -f multi.a + $(QUOTED_GOBIN)/gopack grc $@ $< + +%.$O: %.go + $(QUOTED_GOBIN)/$(GC) -I . -o $@ $< + +test.pb.go: imp.pb.go +multi1.pb.go: multi2.pb.go multi3.pb.go +main.$O: imp.pb.$O test.pb.$O multi.a +extension_test.$O: extension_base.pb.$O extension_extra.pb.$O extension_user.pb.$O diff --git a/compiler/testdata/extension_base.proto b/compiler/testdata/extension_base.proto new file mode 100644 index 0000000..bb35b74 --- /dev/null +++ b/compiler/testdata/extension_base.proto @@ -0,0 +1,44 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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 extension_base; + +message BaseMessage { + optional int32 height = 1; + extensions 4 to 9; + extensions 16 to max; +} + +// Another message that may be extended, using message_set_wire_format. +message OldStyleMessage { + option message_set_wire_format = true; + extensions 100 to max; +} diff --git a/compiler/testdata/extension_extra.proto b/compiler/testdata/extension_extra.proto new file mode 100644 index 0000000..08b949b --- /dev/null +++ b/compiler/testdata/extension_extra.proto @@ -0,0 +1,38 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; + +package extension_extra; + +message ExtraMessage { + optional int32 width = 1; +} diff --git a/compiler/testdata/extension_test.go b/compiler/testdata/extension_test.go new file mode 100644 index 0000000..ad79052 --- /dev/null +++ b/compiler/testdata/extension_test.go @@ -0,0 +1,205 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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. + +// Test that we can use protocol buffers that use extensions. + +package main + +import ( + "bytes" + "regexp" + "testing" + + "goprotobuf.googlecode.com/hg/proto" + base "extension_base.pb" + user "extension_user.pb" +) + +func TestSingleFieldExtension(t *testing.T) { + bm := &base.BaseMessage{ + Height: proto.Int32(178), + } + + // Use extension within scope of another type. + vol := proto.Uint32(11) + err := proto.SetExtension(bm, user.E_LoudMessage_Volume, vol) + if err != nil { + t.Fatal("Failed setting extension:", err) + } + buf, err := proto.Marshal(bm) + if err != nil { + t.Fatal("Failed encoding message with extension:", err) + } + bm_new := new(base.BaseMessage) + if err := proto.Unmarshal(buf, bm_new); err != nil { + t.Fatal("Failed decoding message with extension:", err) + } + if !proto.HasExtension(bm_new, user.E_LoudMessage_Volume) { + t.Fatal("Decoded message didn't contain extension.") + } + vol_out, err := proto.GetExtension(bm_new, user.E_LoudMessage_Volume) + if err != nil { + t.Fatal("Failed getting extension:", err) + } + if v := vol_out.(*uint32); *v != *vol { + t.Errorf("vol_out = %v, expected %v", *v, *vol) + } + proto.ClearExtension(bm_new, user.E_LoudMessage_Volume) + if proto.HasExtension(bm_new, user.E_LoudMessage_Volume) { + t.Fatal("Failed clearing extension.") + } +} + +func TestMessageExtension(t *testing.T) { + bm := &base.BaseMessage{ + Height: proto.Int32(179), + } + + // Use extension that is itself a message. + um := &user.UserMessage{ + Name: proto.String("Dave"), + Rank: proto.String("Major"), + } + err := proto.SetExtension(bm, user.E_LoginMessage_UserMessage, um) + if err != nil { + t.Fatal("Failed setting extension:", err) + } + buf, err := proto.Marshal(bm) + if err != nil { + t.Fatal("Failed encoding message with extension:", err) + } + bm_new := new(base.BaseMessage) + if err := proto.Unmarshal(buf, bm_new); err != nil { + t.Fatal("Failed decoding message with extension:", err) + } + if !proto.HasExtension(bm_new, user.E_LoginMessage_UserMessage) { + t.Fatal("Decoded message didn't contain extension.") + } + um_out, err := proto.GetExtension(bm_new, user.E_LoginMessage_UserMessage) + if err != nil { + t.Fatal("Failed getting extension:", err) + } + if n := um_out.(*user.UserMessage).Name; *n != *um.Name { + t.Errorf("um_out.Name = %q, expected %q", *n, *um.Name) + } + if r := um_out.(*user.UserMessage).Rank; *r != *um.Rank { + t.Errorf("um_out.Rank = %q, expected %q", *r, *um.Rank) + } + proto.ClearExtension(bm_new, user.E_LoginMessage_UserMessage) + if proto.HasExtension(bm_new, user.E_LoginMessage_UserMessage) { + t.Fatal("Failed clearing extension.") + } +} + +func TestTopLevelExtension(t *testing.T) { + bm := &base.BaseMessage{ + Height: proto.Int32(179), + } + + width := proto.Int32(17) + err := proto.SetExtension(bm, user.E_Width, width) + if err != nil { + t.Fatal("Failed setting extension:", err) + } + buf, err := proto.Marshal(bm) + if err != nil { + t.Fatal("Failed encoding message with extension:", err) + } + bm_new := new(base.BaseMessage) + if err := proto.Unmarshal(buf, bm_new); err != nil { + t.Fatal("Failed decoding message with extension:", err) + } + if !proto.HasExtension(bm_new, user.E_Width) { + t.Fatal("Decoded message didn't contain extension.") + } + width_out, err := proto.GetExtension(bm_new, user.E_Width) + if err != nil { + t.Fatal("Failed getting extension:", err) + } + if w := width_out.(*int32); *w != *width { + t.Errorf("width_out = %v, expected %v", *w, *width) + } + proto.ClearExtension(bm_new, user.E_Width) + if proto.HasExtension(bm_new, user.E_Width) { + t.Fatal("Failed clearing extension.") + } +} + +func TestMessageSetWireFormat(t *testing.T) { + osm := new(base.OldStyleMessage) + osp := &user.OldStyleParcel{ + Name: proto.String("Dave"), + Height: proto.Int32(178), + } + + err := proto.SetExtension(osm, user.E_OldStyleParcel_MessageSetExtension, osp) + if err != nil { + t.Fatal("Failed setting extension:", err) + } + + buf, err := proto.Marshal(osm) + if err != nil { + t.Fatal("Failed encoding message:", err) + } + + // Data generated from Python implementation. + expected := []byte{ + 11, 16, 209, 15, 26, 9, 10, 4, 68, 97, 118, 101, 16, 178, 1, 12, + } + + if !bytes.Equal(expected, buf) { + t.Errorf("Encoding mismatch.\nwant %+v\n got %+v", expected, buf) + } + + // Check that it is restored correctly. + osm = new(base.OldStyleMessage) + if err := proto.Unmarshal(buf, osm); err != nil { + t.Fatal("Failed decoding message:", err) + } + osp_out, err := proto.GetExtension(osm, user.E_OldStyleParcel_MessageSetExtension) + if err != nil { + t.Fatal("Failed getting extension:", err) + } + osp = osp_out.(*user.OldStyleParcel) + if *osp.Name != "Dave" || *osp.Height != 178 { + t.Errorf("Retrieved extension from decoded message is not correct: %+v", osp) + } +} + +func main() { + // simpler than rigging up gotest + testing.Main(regexp.MatchString, []testing.InternalTest{ + {"TestSingleFieldExtension", TestSingleFieldExtension}, + {"TestMessageExtension", TestMessageExtension}, + {"TestTopLevelExtension", TestTopLevelExtension}, + }, + []testing.InternalBenchmark{}) +} diff --git a/compiler/testdata/extension_user.proto b/compiler/testdata/extension_user.proto new file mode 100644 index 0000000..b5a731a --- /dev/null +++ b/compiler/testdata/extension_user.proto @@ -0,0 +1,89 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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. + +import "extension_base.proto"; +import "extension_extra.proto"; + +package extension_user; + +message UserMessage { + optional string name = 1; + optional string rank = 2; +} + +// Extend with a message +extend extension_base.BaseMessage { + optional UserMessage user_message = 5; +} + +// Extend with a foreign message +extend extension_base.BaseMessage { + optional extension_extra.ExtraMessage extra_message = 9; +} + +// Extend with some primitive types +extend extension_base.BaseMessage { + optional int32 width = 6; + optional int64 area = 7; +} + +// Extend inside the scope of another type +message LoudMessage { + extend extension_base.BaseMessage { + optional uint32 volume = 8; + } + extensions 100 to max; +} + +// Extend inside the scope of another type, using a message. +message LoginMessage { + extend extension_base.BaseMessage { + required UserMessage user_message = 16; + } +} + +// An extension of an extension +message Announcement { + optional string words = 1; + extend LoudMessage { + optional Announcement loud_ext = 100; + } +} + +// Something that can be put in a message set. +message OldStyleParcel { + extend extension_base.OldStyleMessage { + optional OldStyleParcel message_set_extension = 2001; + } + + required string name = 1; + optional int32 height = 2; +} diff --git a/compiler/testdata/imp.pb.go.golden b/compiler/testdata/imp.pb.go.golden new file mode 100644 index 0000000..c038c74 --- /dev/null +++ b/compiler/testdata/imp.pb.go.golden @@ -0,0 +1,107 @@ +// Code generated by protoc-gen-go from "imp.proto" +// DO NOT EDIT! + +package imp + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" +import imp1 "imp2.pb" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + +// Types from public import imp2.proto +type PubliclyImportedMessage imp1.PubliclyImportedMessage + +func (this *PubliclyImportedMessage) Reset() { (*imp1.PubliclyImportedMessage)(this).Reset() } +func (this *PubliclyImportedMessage) String() string { + return (*imp1.PubliclyImportedMessage)(this).String() +} + +// PubliclyImportedMessage from public import imp.proto + +type ImportedMessage_Owner int32 + +const ( + ImportedMessage_DAVE ImportedMessage_Owner = 1 + ImportedMessage_MIKE ImportedMessage_Owner = 2 +) + +var ImportedMessage_Owner_name = map[int32]string{ + 1: "DAVE", + 2: "MIKE", +} +var ImportedMessage_Owner_value = map[string]int32{ + "DAVE": 1, + "MIKE": 2, +} + +func NewImportedMessage_Owner(x ImportedMessage_Owner) *ImportedMessage_Owner { + e := ImportedMessage_Owner(x) + return &e +} +func (x ImportedMessage_Owner) String() string { + return proto.EnumName(ImportedMessage_Owner_name, int32(x)) +} + +type ImportedMessage struct { + Field *int64 `protobuf:"varint,1,req,name=field" json:"field,omitempty"` + XXX_extensions map[int32][]byte `json:",omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *ImportedMessage) Reset() { *this = ImportedMessage{} } +func (this *ImportedMessage) String() string { return proto.CompactTextString(this) } + +var extRange_ImportedMessage = []proto.ExtensionRange{ + proto.ExtensionRange{90, 100}, +} + +func (*ImportedMessage) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ImportedMessage +} +func (this *ImportedMessage) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type ImportedExtendable struct { + XXX_extensions map[int32][]byte `json:",omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *ImportedExtendable) Reset() { *this = ImportedExtendable{} } +func (this *ImportedExtendable) String() string { return proto.CompactTextString(this) } + +func (this *ImportedExtendable) Marshal() ([]byte, os.Error) { + return proto.MarshalMessageSet(this.ExtensionMap()) +} +func (this *ImportedExtendable) Unmarshal(buf []byte) os.Error { + return proto.UnmarshalMessageSet(buf, this.ExtensionMap()) +} +// ensure ImportedExtendable satisfies proto.Marshaler and proto.Unmarshaler +var _ proto.Marshaler = (*ImportedExtendable)(nil) +var _ proto.Unmarshaler = (*ImportedExtendable)(nil) + +var extRange_ImportedExtendable = []proto.ExtensionRange{ + proto.ExtensionRange{100, 536870911}, +} + +func (*ImportedExtendable) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_ImportedExtendable +} +func (this *ImportedExtendable) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +func init() { + proto.RegisterEnum("imp.ImportedMessage_Owner", ImportedMessage_Owner_name, ImportedMessage_Owner_value) +} diff --git a/compiler/testdata/imp.proto b/compiler/testdata/imp.proto new file mode 100644 index 0000000..b059db2 --- /dev/null +++ b/compiler/testdata/imp.proto @@ -0,0 +1,50 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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 imp; + +import "imp2.proto"; + +message ImportedMessage { + required int64 field = 1; + + enum Owner { + DAVE = 1; + MIKE = 2; + } + + extensions 90 to 100; +} + +message ImportedExtendable { + option message_set_wire_format = true; + extensions 100 to max; +} diff --git a/compiler/testdata/imp2.proto b/compiler/testdata/imp2.proto new file mode 100644 index 0000000..e69870a --- /dev/null +++ b/compiler/testdata/imp2.proto @@ -0,0 +1,35 @@ +// +// Copyright 2011 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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 imp; + +message PubliclyImportedMessage { + optional int64 field = 1; +} diff --git a/compiler/testdata/main.go b/compiler/testdata/main.go new file mode 100644 index 0000000..ed81320 --- /dev/null +++ b/compiler/testdata/main.go @@ -0,0 +1,44 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A simple binary to link together the protocol buffers in this test. + +package main + +import ( + "./test.pb" + "./multi1.pb" +) + +func main() { + _ = &my_test.Request{} + _ = &multitest.Multi1{} +} diff --git a/compiler/testdata/multi1.proto b/compiler/testdata/multi1.proto new file mode 100644 index 0000000..47a4d2f --- /dev/null +++ b/compiler/testdata/multi1.proto @@ -0,0 +1,42 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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. + +import "multi2.proto"; +import "multi3.proto"; + +package multitest; + +message Multi1 { + required Multi2 multi2 = 1; + optional Multi2.Color color = 2; + optional Multi3.HatType hat_type = 3; +} + diff --git a/compiler/testdata/multi2.proto b/compiler/testdata/multi2.proto new file mode 100644 index 0000000..6bb76f9 --- /dev/null +++ b/compiler/testdata/multi2.proto @@ -0,0 +1,44 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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 multitest; + +message Multi2 { + required int32 required_value = 1; + + enum Color { + BLUE = 1; + GREEN = 2; + RED = 3; + }; + optional Color color = 2; +} + diff --git a/compiler/testdata/multi3.proto b/compiler/testdata/multi3.proto new file mode 100644 index 0000000..191fefd --- /dev/null +++ b/compiler/testdata/multi3.proto @@ -0,0 +1,41 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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 multitest; + +message Multi3 { + enum HatType { + FEDORA = 1; + FEZ = 2; + }; + optional HatType hat_type = 1; +} + diff --git a/compiler/testdata/test.pb.go.golden b/compiler/testdata/test.pb.go.golden new file mode 100644 index 0000000..233d1f9 --- /dev/null +++ b/compiler/testdata/test.pb.go.golden @@ -0,0 +1,243 @@ +// Code generated by protoc-gen-go from "test.proto" +// DO NOT EDIT! + +package my_test + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" +import imp1 "imp.pb" +// discarding unused import multitest2 "multi1.pb" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + +type HatType int32 + +const ( + HatType_FEDORA HatType = 1 + HatType_FEZ HatType = 2 +) + +var HatType_name = map[int32]string{ + 1: "FEDORA", + 2: "FEZ", +} +var HatType_value = map[string]int32{ + "FEDORA": 1, + "FEZ": 2, +} + +func NewHatType(x HatType) *HatType { + e := HatType(x) + return &e +} +func (x HatType) String() string { + return proto.EnumName(HatType_name, int32(x)) +} + +type Days int32 + +const ( + Days_MONDAY Days = 1 + Days_TUESDAY Days = 2 + Days_LUNDI Days = 1 +) + +var Days_name = map[int32]string{ + 1: "MONDAY", + 2: "TUESDAY", + // Duplicate value: 1: "LUNDI", +} +var Days_value = map[string]int32{ + "MONDAY": 1, + "TUESDAY": 2, + "LUNDI": 1, +} + +func NewDays(x Days) *Days { + e := Days(x) + return &e +} +func (x Days) String() string { + return proto.EnumName(Days_name, int32(x)) +} + +type Request_Color int32 + +const ( + Request_RED Request_Color = 0 + Request_GREEN Request_Color = 1 + Request_BLUE Request_Color = 2 +) + +var Request_Color_name = map[int32]string{ + 0: "RED", + 1: "GREEN", + 2: "BLUE", +} +var Request_Color_value = map[string]int32{ + "RED": 0, + "GREEN": 1, + "BLUE": 2, +} + +func NewRequest_Color(x Request_Color) *Request_Color { + e := Request_Color(x) + return &e +} +func (x Request_Color) String() string { + return proto.EnumName(Request_Color_name, int32(x)) +} + +type Reply_Entry_Game int32 + +const ( + Reply_Entry_FOOTBALL Reply_Entry_Game = 1 + Reply_Entry_TENNIS Reply_Entry_Game = 2 +) + +var Reply_Entry_Game_name = map[int32]string{ + 1: "FOOTBALL", + 2: "TENNIS", +} +var Reply_Entry_Game_value = map[string]int32{ + "FOOTBALL": 1, + "TENNIS": 2, +} + +func NewReply_Entry_Game(x Reply_Entry_Game) *Reply_Entry_Game { + e := Reply_Entry_Game(x) + return &e +} +func (x Reply_Entry_Game) String() string { + return proto.EnumName(Reply_Entry_Game_name, int32(x)) +} + +type Request struct { + Key []int64 `protobuf:"varint,1,rep,name=key" json:"key,omitempty"` + ImportedMessage *imp1.ImportedMessage `protobuf:"bytes,2,opt,name=imported_message" json:"imported_message,omitempty"` + Hue *Request_Color `protobuf:"varint,3,opt,name=hue,enum=my.test.Request_Color" json:"hue,omitempty"` + Hat *HatType `protobuf:"varint,4,opt,name=hat,enum=my.test.HatType,def=1" json:"hat,omitempty"` + Owner *imp1.ImportedMessage_Owner `protobuf:"varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner" json:"owner,omitempty"` + Deadline *float32 `protobuf:"fixed32,7,opt,name=deadline,def=inf" json:"deadline,omitempty"` + Somegroup *Request_SomeGroup `protobuf:"group,8,opt,name=SomeGroup" json:"somegroup,omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *Request) Reset() { *this = Request{} } +func (this *Request) String() string { return proto.CompactTextString(this) } + +const Default_Request_Hat HatType = HatType_FEDORA + +var Default_Request_Deadline float32 = float32(math.Inf(1)) + +type Request_SomeGroup struct { + GroupField *int32 `protobuf:"varint,9,opt,name=group_field" json:"group_field,omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *Request_SomeGroup) Reset() { *this = Request_SomeGroup{} } +func (this *Request_SomeGroup) String() string { return proto.CompactTextString(this) } + +type Reply struct { + Found []*Reply_Entry `protobuf:"bytes,1,rep,name=found" json:"found,omitempty"` + CompactKeys []int32 `protobuf:"varint,2,rep,packed,name=compact_keys" json:"compact_keys,omitempty"` + XXX_extensions map[int32]proto.Extension `json:",omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *Reply) Reset() { *this = Reply{} } +func (this *Reply) String() string { return proto.CompactTextString(this) } + +var extRange_Reply = []proto.ExtensionRange{ + {100, 536870911}, +} + +func (*Reply) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_Reply +} +func (this *Reply) ExtensionMap() map[int32]proto.Extension { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32]proto.Extension) + } + return this.XXX_extensions +} + +type Reply_Entry struct { + KeyThatNeeds_1234Camel_CasIng *int64 `protobuf:"varint,1,req,name=key_that_needs_1234camel_CasIng" json:"key_that_needs_1234camel_CasIng,omitempty"` + Value *int64 `protobuf:"varint,2,opt,name=value,def=7" json:"value,omitempty"` + XMyFieldName_2 *int64 `protobuf:"varint,3,opt,name=_my_field_name_2" json:"_my_field_name_2,omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *Reply_Entry) Reset() { *this = Reply_Entry{} } +func (this *Reply_Entry) String() string { return proto.CompactTextString(this) } + +const Default_Reply_Entry_Value int64 = 7 + +type ReplyExtensions struct { + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *ReplyExtensions) Reset() { *this = ReplyExtensions{} } +func (this *ReplyExtensions) String() string { return proto.CompactTextString(this) } + +var E_ReplyExtensions_Time = &proto.ExtensionDesc{ + ExtendedType: (*Reply)(nil), + ExtensionType: (*float64)(nil), + Field: 101, + Name: "my_test.ReplyExtensions.time", + Tag: "fixed64,101,opt,name=time", +} + +type OldReply struct { + XXX_extensions map[int32]proto.Extension `json:",omitempty"` + XXX_unrecognized []byte `json:",omitempty"` +} + +func (this *OldReply) Reset() { *this = OldReply{} } +func (this *OldReply) String() string { return proto.CompactTextString(this) } + +func (this *OldReply) Marshal() ([]byte, os.Error) { + return proto.MarshalMessageSet(this.ExtensionMap()) +} +func (this *OldReply) Unmarshal(buf []byte) os.Error { + return proto.UnmarshalMessageSet(buf, this.ExtensionMap()) +} +// ensure OldReply satisfies proto.Marshaler and proto.Unmarshaler +var _ proto.Marshaler = (*OldReply)(nil) +var _ proto.Unmarshaler = (*OldReply)(nil) + +var extRange_OldReply = []proto.ExtensionRange{ + {100, 536870911}, +} + +func (*OldReply) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_OldReply +} +func (this *OldReply) ExtensionMap() map[int32]proto.Extension { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32]proto.Extension) + } + return this.XXX_extensions +} + +var E_Tag = &proto.ExtensionDesc{ + ExtendedType: (*Reply)(nil), + ExtensionType: (*string)(nil), + Field: 103, + Name: "my_test.tag", + Tag: "bytes,103,opt,name=tag", +} + +func init() { + proto.RegisterEnum("my.test.HatType", HatType_name, HatType_value) + proto.RegisterEnum("my.test.Days", Days_name, Days_value) + proto.RegisterEnum("my.test.Request_Color", Request_Color_name, Request_Color_value) + proto.RegisterEnum("my.test.Reply_Entry_Game", Reply_Entry_Game_name, Reply_Entry_Game_value) + proto.RegisterExtension(E_ReplyExtensions_Time) + proto.RegisterExtension(E_Tag) +} diff --git a/compiler/testdata/test.proto b/compiler/testdata/test.proto new file mode 100644 index 0000000..0c197f7 --- /dev/null +++ b/compiler/testdata/test.proto @@ -0,0 +1,101 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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 my.test; // dotted package name + +import "imp.proto"; +import "multi1.proto"; // unused import + +enum HatType { + // deliberately skipping 0 + FEDORA = 1; + FEZ = 2; +} + +enum Days { + MONDAY = 1; + TUESDAY = 2; + LUNDI = 1; // same value as MONDAY +} + +message Request { + enum Color { + RED = 0; + GREEN = 1; + BLUE = 2; + } + repeated int64 key = 1; + optional imp.ImportedMessage imported_message = 2; + optional Color hue = 3; + optional HatType hat = 4 [default=FEDORA]; + optional imp.ImportedMessage.Owner owner = 6; + optional float deadline = 7 [default=inf]; + optional group SomeGroup = 8 { + optional int32 group_field = 9; + } + + // This foreign message type is in imp2.proto, + // which is publicly imported by imp.proto. + // optional imp.PubliclyImportedMessage pub = 10; +} + +message Reply { + message Entry { + required int64 key_that_needs_1234camel_CasIng = 1; + optional int64 value = 2 [default=7]; + optional int64 _my_field_name_2 = 3; + enum Game { + FOOTBALL = 1; + TENNIS = 2; + } + } + repeated Entry found = 1; + repeated int32 compact_keys = 2 [packed=true]; + extensions 100 to max; +} + +message ReplyExtensions { + extend Reply { + optional double time = 101; + } +} + +// top-level extension +extend Reply { + optional string tag = 103; +} + +message OldReply { + // Extensions will be encoded in MessageSet wire format. + option message_set_wire_format = true; + extensions 100 to max; +} + diff --git a/lib/codereview/codereview.cfg b/lib/codereview/codereview.cfg new file mode 100644 index 0000000..93b55c0 --- /dev/null +++ b/lib/codereview/codereview.cfg @@ -0,0 +1 @@ +defaultcc: golang-dev@googlegroups.com diff --git a/proto/Makefile b/proto/Makefile new file mode 100644 index 0000000..8f9380b --- /dev/null +++ b/proto/Makefile @@ -0,0 +1,58 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +include $(GOROOT)/src/Make.inc + +TARG=goprotobuf.googlecode.com/hg/proto +GOFILES=\ + clone.go\ + decode.go\ + encode.go\ + equal.go\ + extensions.go\ + lib.go\ + message_set.go\ + properties.go\ + text.go\ + text_parser.go\ + +include $(GOROOT)/src/Make.pkg + +# We need to compile the .pb.gos, which import this package, so +# to run the test we must make install here and then make in the +# testdata directory. +test: compile-test-pbs + +testshort: compile-test-pbs + +compile-test-pbs: + make install && cd testdata && make nuke && make diff --git a/proto/all_test.go b/proto/all_test.go new file mode 100644 index 0000000..ae419aa --- /dev/null +++ b/proto/all_test.go @@ -0,0 +1,1373 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +package proto_test + +import ( + "bytes" + "fmt" + "json" + "math" + "os" + "reflect" + "strings" + "testing" + + . "goprotobuf.googlecode.com/hg/proto" + . "./testdata/_obj/test_proto" +) + +var globalO *Buffer + +func old() *Buffer { + if globalO == nil { + globalO = NewBuffer(nil) + } + globalO.Reset() + return globalO +} + +func equalbytes(b1, b2 []byte, t *testing.T) { + if len(b1) != len(b2) { + t.Errorf("wrong lengths: 2*%d != %d", len(b1), len(b2)) + return + } + for i := 0; i < len(b1); i++ { + if b1[i] != b2[i] { + t.Errorf("bad byte[%d]:%x %x: %s %s", i, b1[i], b2[i], b1, b2) + } + } +} + +func initGoTestField() *GoTestField { + f := new(GoTestField) + f.Label = String("label") + f.Type = String("type") + return f +} + +// These are all structurally equivalent but the tag numbers differ. +// (It's remarkable that required, optional, and repeated all have +// 8 letters.) +func initGoTest_RequiredGroup() *GoTest_RequiredGroup { + return &GoTest_RequiredGroup{ + RequiredField: String("required"), + } +} + +func initGoTest_OptionalGroup() *GoTest_OptionalGroup { + return &GoTest_OptionalGroup{ + RequiredField: String("optional"), + } +} + +func initGoTest_RepeatedGroup() *GoTest_RepeatedGroup { + return &GoTest_RepeatedGroup{ + RequiredField: String("repeated"), + } +} + +func initGoTest(setdefaults bool) *GoTest { + pb := new(GoTest) + if setdefaults { + pb.F_BoolDefaulted = Bool(Default_GoTest_F_BoolDefaulted) + pb.F_Int32Defaulted = Int32(Default_GoTest_F_Int32Defaulted) + pb.F_Int64Defaulted = Int64(Default_GoTest_F_Int64Defaulted) + pb.F_Fixed32Defaulted = Uint32(Default_GoTest_F_Fixed32Defaulted) + pb.F_Fixed64Defaulted = Uint64(Default_GoTest_F_Fixed64Defaulted) + pb.F_Uint32Defaulted = Uint32(Default_GoTest_F_Uint32Defaulted) + pb.F_Uint64Defaulted = Uint64(Default_GoTest_F_Uint64Defaulted) + pb.F_FloatDefaulted = Float32(Default_GoTest_F_FloatDefaulted) + pb.F_DoubleDefaulted = Float64(Default_GoTest_F_DoubleDefaulted) + pb.F_StringDefaulted = String(Default_GoTest_F_StringDefaulted) + pb.F_BytesDefaulted = Default_GoTest_F_BytesDefaulted + pb.F_Sint32Defaulted = Int32(Default_GoTest_F_Sint32Defaulted) + pb.F_Sint64Defaulted = Int64(Default_GoTest_F_Sint64Defaulted) + } + + pb.Kind = NewGoTest_KIND(GoTest_TIME) + pb.RequiredField = initGoTestField() + pb.F_BoolRequired = Bool(true) + pb.F_Int32Required = Int32(3) + pb.F_Int64Required = Int64(6) + pb.F_Fixed32Required = Uint32(32) + pb.F_Fixed64Required = Uint64(64) + pb.F_Uint32Required = Uint32(3232) + pb.F_Uint64Required = Uint64(6464) + pb.F_FloatRequired = Float32(3232) + pb.F_DoubleRequired = Float64(6464) + pb.F_StringRequired = String("string") + pb.F_BytesRequired = []byte("bytes") + pb.F_Sint32Required = Int32(-32) + pb.F_Sint64Required = Int64(-64) + pb.Requiredgroup = initGoTest_RequiredGroup() + + return pb +} + +func fail(msg string, b *bytes.Buffer, s string, t *testing.T) { + data := b.Bytes() + ld := len(data) + ls := len(s) / 2 + + fmt.Printf("fail %s ld=%d ls=%d\n", msg, ld, ls) + + // find the interesting spot - n + n := ls + if ld < ls { + n = ld + } + j := 0 + for i := 0; i < n; i++ { + bs := hex(s[j])*16 + hex(s[j+1]) + j += 2 + if data[i] == bs { + continue + } + n = i + break + } + l := n - 10 + if l < 0 { + l = 0 + } + h := n + 10 + + // find the interesting spot - n + fmt.Printf("is[%d]:", l) + for i := l; i < h; i++ { + if i >= ld { + fmt.Printf(" --") + continue + } + fmt.Printf(" %.2x", data[i]) + } + fmt.Printf("\n") + + fmt.Printf("sb[%d]:", l) + for i := l; i < h; i++ { + if i >= ls { + fmt.Printf(" --") + continue + } + bs := hex(s[j])*16 + hex(s[j+1]) + j += 2 + fmt.Printf(" %.2x", bs) + } + fmt.Printf("\n") + + t.Fail() + + // t.Errorf("%s: \ngood: %s\nbad: %x", msg, s, b.Bytes()) + // Print the output in a partially-decoded format; can + // be helpful when updating the test. It produces the output + // that is pasted, with minor edits, into the argument to verify(). + // data := b.Bytes() + // nesting := 0 + // for b.Len() > 0 { + // start := len(data) - b.Len() + // var u uint64 + // u, err := DecodeVarint(b) + // if err != nil { + // fmt.Printf("decode error on varint:", err) + // return + // } + // wire := u & 0x7 + // tag := u >> 3 + // switch wire { + // case WireVarint: + // v, err := DecodeVarint(b) + // if err != nil { + // fmt.Printf("decode error on varint:", err) + // return + // } + // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n", + // data[start:len(data)-b.Len()], tag, wire, v) + // case WireFixed32: + // v, err := DecodeFixed32(b) + // if err != nil { + // fmt.Printf("decode error on fixed32:", err) + // return + // } + // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n", + // data[start:len(data)-b.Len()], tag, wire, v) + // case WireFixed64: + // v, err := DecodeFixed64(b) + // if err != nil { + // fmt.Printf("decode error on fixed64:", err) + // return + // } + // fmt.Printf("\t\t\"%x\" // field %d, encoding %d, value %d\n", + // data[start:len(data)-b.Len()], tag, wire, v) + // case WireBytes: + // nb, err := DecodeVarint(b) + // if err != nil { + // fmt.Printf("decode error on bytes:", err) + // return + // } + // after_tag := len(data) - b.Len() + // str := make([]byte, nb) + // _, err = b.Read(str) + // if err != nil { + // fmt.Printf("decode error on bytes:", err) + // return + // } + // fmt.Printf("\t\t\"%x\" \"%x\" // field %d, encoding %d (FIELD)\n", + // data[start:after_tag], str, tag, wire) + // case WireStartGroup: + // nesting++ + // fmt.Printf("\t\t\"%x\"\t\t// start group field %d level %d\n", + // data[start:len(data)-b.Len()], tag, nesting) + // case WireEndGroup: + // fmt.Printf("\t\t\"%x\"\t\t// end group field %d level %d\n", + // data[start:len(data)-b.Len()], tag, nesting) + // nesting-- + // default: + // fmt.Printf("unrecognized wire type %d\n", wire) + // return + // } + // } +} + +func hex(c uint8) uint8 { + if '0' <= c && c <= '9' { + return c - '0' + } + if 'a' <= c && c <= 'f' { + return 10 + c - 'a' + } + if 'A' <= c && c <= 'F' { + return 10 + c - 'A' + } + return 0 +} + +func equal(b []byte, s string, t *testing.T) bool { + if 2*len(b) != len(s) { + // fail(fmt.Sprintf("wrong lengths: 2*%d != %d", len(b), len(s)), b, s, t) + fmt.Printf("wrong lengths: 2*%d != %d\n", len(b), len(s)) + return false + } + for i, j := 0, 0; i < len(b); i, j = i+1, j+2 { + x := hex(s[j])*16 + hex(s[j+1]) + if b[i] != x { + // fail(fmt.Sprintf("bad byte[%d]:%x %x", i, b[i], x), b, s, t) + fmt.Printf("bad byte[%d]:%x %x", i, b[i], x) + return false + } + } + return true +} + +func overify(t *testing.T, pb *GoTest, expected string) { + o := old() + err := o.Marshal(pb) + if err != nil { + fmt.Printf("overify marshal-1 err = %v", err) + o.DebugPrint("", o.Bytes()) + t.Fatalf("expected = %s", expected) + } + if !equal(o.Bytes(), expected, t) { + o.DebugPrint("overify neq 1", o.Bytes()) + t.Fatalf("expected = %s", expected) + } + + // Now test Unmarshal by recreating the original buffer. + pbd := new(GoTest) + err = o.Unmarshal(pbd) + if err != nil { + t.Fatalf("overify unmarshal err = %v", err) + o.DebugPrint("", o.Bytes()) + t.Fatalf("string = %s", expected) + } + o.Reset() + err = o.Marshal(pbd) + if err != nil { + t.Errorf("overify marshal-2 err = %v", err) + o.DebugPrint("", o.Bytes()) + t.Fatalf("string = %s", expected) + } + if !equal(o.Bytes(), expected, t) { + o.DebugPrint("overify neq 2", o.Bytes()) + t.Fatalf("string = %s", expected) + } +} + +// Simple tests for numeric encode/decode primitives (varint, etc.) +func TestNumericPrimitives(t *testing.T) { + for i := uint64(0); i < 1e6; i += 111 { + o := old() + if o.EncodeVarint(i) != nil { + t.Error("EncodeVarint") + break + } + x, e := o.DecodeVarint() + if e != nil { + t.Fatal("DecodeVarint") + } + if x != i { + t.Fatal("varint decode fail:", i, x) + } + + o = old() + if o.EncodeFixed32(i) != nil { + t.Fatal("encFixed32") + } + x, e = o.DecodeFixed32() + if e != nil { + t.Fatal("decFixed32") + } + if x != i { + t.Fatal("fixed32 decode fail:", i, x) + } + + o = old() + if o.EncodeFixed64(i*1234567) != nil { + t.Error("encFixed64") + break + } + x, e = o.DecodeFixed64() + if e != nil { + t.Error("decFixed64") + break + } + if x != i*1234567 { + t.Error("fixed64 decode fail:", i*1234567, x) + break + } + + o = old() + i32 := int32(i - 12345) + if o.EncodeZigzag32(uint64(i32)) != nil { + t.Fatal("EncodeZigzag32") + } + x, e = o.DecodeZigzag32() + if e != nil { + t.Fatal("DecodeZigzag32") + } + if x != uint64(uint32(i32)) { + t.Fatal("zigzag32 decode fail:", i32, x) + } + + o = old() + i64 := int64(i - 12345) + if o.EncodeZigzag64(uint64(i64)) != nil { + t.Fatal("EncodeZigzag64") + } + x, e = o.DecodeZigzag64() + if e != nil { + t.Fatal("DecodeZigzag64") + } + if x != uint64(i64) { + t.Fatal("zigzag64 decode fail:", i64, x) + } + } +} + +// Simple tests for bytes +func TestBytesPrimitives(t *testing.T) { + o := old() + bytes := []byte{'n', 'o', 'w', ' ', 'i', 's', ' ', 't', 'h', 'e', ' ', 't', 'i', 'm', 'e'} + if o.EncodeRawBytes(bytes) != nil { + t.Error("EncodeRawBytes") + } + decb, e := o.DecodeRawBytes(false) + if e != nil { + t.Error("DecodeRawBytes") + } + equalbytes(bytes, decb, t) +} + +// Simple tests for strings +func TestStringPrimitives(t *testing.T) { + o := old() + s := "now is the time" + if o.EncodeStringBytes(s) != nil { + t.Error("enc_string") + } + decs, e := o.DecodeStringBytes() + if e != nil { + t.Error("dec_string") + } + if s != decs { + t.Error("string encode/decode fail:", s, decs) + } +} + +// Do we catch the "required bit not set" case? +func TestRequiredBit(t *testing.T) { + o := old() + pb := new(GoTest) + err := o.Marshal(pb) + if err == nil { + t.Error("did not catch missing required fields") + } else if strings.Index(err.String(), "GoTest") < 0 { + t.Error("wrong error type:", err) + } +} + +// Check that all fields are nil. +// Clearly silly, and a residue from a more interesting test with an earlier, +// different initialization property, but it once caught a compiler bug so +// it lives. +func checkInitialized(pb *GoTest, t *testing.T) { + if pb.F_BoolDefaulted != nil { + t.Error("New or Reset did not set boolean:", *pb.F_BoolDefaulted) + } + if pb.F_Int32Defaulted != nil { + t.Error("New or Reset did not set int32:", *pb.F_Int32Defaulted) + } + if pb.F_Int64Defaulted != nil { + t.Error("New or Reset did not set int64:", *pb.F_Int64Defaulted) + } + if pb.F_Fixed32Defaulted != nil { + t.Error("New or Reset did not set fixed32:", *pb.F_Fixed32Defaulted) + } + if pb.F_Fixed64Defaulted != nil { + t.Error("New or Reset did not set fixed64:", *pb.F_Fixed64Defaulted) + } + if pb.F_Uint32Defaulted != nil { + t.Error("New or Reset did not set uint32:", *pb.F_Uint32Defaulted) + } + if pb.F_Uint64Defaulted != nil { + t.Error("New or Reset did not set uint64:", *pb.F_Uint64Defaulted) + } + if pb.F_FloatDefaulted != nil { + t.Error("New or Reset did not set float:", *pb.F_FloatDefaulted) + } + if pb.F_DoubleDefaulted != nil { + t.Error("New or Reset did not set double:", *pb.F_DoubleDefaulted) + } + if pb.F_StringDefaulted != nil { + t.Error("New or Reset did not set string:", *pb.F_StringDefaulted) + } + if pb.F_BytesDefaulted != nil { + t.Error("New or Reset did not set bytes:", string(pb.F_BytesDefaulted)) + } + if pb.F_Sint32Defaulted != nil { + t.Error("New or Reset did not set int32:", *pb.F_Sint32Defaulted) + } + if pb.F_Sint64Defaulted != nil { + t.Error("New or Reset did not set int64:", *pb.F_Sint64Defaulted) + } +} + +// Does Reset() reset? +func TestReset(t *testing.T) { + pb := initGoTest(true) + // muck with some values + pb.F_BoolDefaulted = Bool(false) + pb.F_Int32Defaulted = Int32(237) + pb.F_Int64Defaulted = Int64(12346) + pb.F_Fixed32Defaulted = Uint32(32000) + pb.F_Fixed64Defaulted = Uint64(666) + pb.F_Uint32Defaulted = Uint32(323232) + pb.F_Uint64Defaulted = nil + pb.F_FloatDefaulted = nil + pb.F_DoubleDefaulted = Float64(0) + pb.F_StringDefaulted = String("gotcha") + pb.F_BytesDefaulted = []byte("asdfasdf") + pb.F_Sint32Defaulted = Int32(123) + pb.F_Sint64Defaulted = Int64(789) + pb.Reset() + checkInitialized(pb, t) +} + +// All required fields set, no defaults provided. +func TestEncodeDecode1(t *testing.T) { + pb := initGoTest(false) + overify(t, pb, + "0807"+ // field 1, encoding 0, value 7 + "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) + "5001"+ // field 10, encoding 0, value 1 + "5803"+ // field 11, encoding 0, value 3 + "6006"+ // field 12, encoding 0, value 6 + "6d20000000"+ // field 13, encoding 5, value 0x20 + "714000000000000000"+ // field 14, encoding 1, value 0x40 + "78a019"+ // field 15, encoding 0, value 0xca0 = 3232 + "8001c032"+ // field 16, encoding 0, value 0x1940 = 6464 + "8d0100004a45"+ // field 17, encoding 5, value 3232.0 + "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 + "9a0106"+"737472696e67"+ // field 19, encoding 2, string "string" + "aa0605"+"6279746573"+ // field 101, encoding 2, string "bytes" + "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 + "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 + "b304"+ // field 70, encoding 3, start group + "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" + "b404") // field 70, encoding 4, end group +} + +// All required fields set, defaults provided. +func TestEncodeDecode2(t *testing.T) { + pb := initGoTest(true) + overify(t, pb, + "0807"+ // field 1, encoding 0, value 7 + "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) + "5001"+ // field 10, encoding 0, value 1 + "5803"+ // field 11, encoding 0, value 3 + "6006"+ // field 12, encoding 0, value 6 + "6d20000000"+ // field 13, encoding 5, value 32 + "714000000000000000"+ // field 14, encoding 1, value 64 + "78a019"+ // field 15, encoding 0, value 3232 + "8001c032"+ // field 16, encoding 0, value 6464 + "8d0100004a45"+ // field 17, encoding 5, value 3232.0 + "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 + "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" + "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" + "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 + "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 + "c00201"+ // field 40, encoding 0, value 1 + "c80220"+ // field 41, encoding 0, value 32 + "d00240"+ // field 42, encoding 0, value 64 + "dd0240010000"+ // field 43, encoding 5, value 320 + "e1028002000000000000"+ // field 44, encoding 1, value 640 + "e8028019"+ // field 45, encoding 0, value 3200 + "f0028032"+ // field 46, encoding 0, value 6400 + "fd02e0659948"+ // field 47, encoding 5, value 314159.0 + "81030000000050971041"+ // field 48, encoding 1, value 271828.0 + "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" + "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" + "90193f"+ // field 402, encoding 0, value 63 + "98197f"+ // field 403, encoding 0, value 127 + "b304"+ // start group field 70 level 1 + "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" + "b404") // end group field 70 level 1 + +} + +// All default fields set to their default value by hand +func TestEncodeDecode3(t *testing.T) { + pb := initGoTest(false) + pb.F_BoolDefaulted = Bool(true) + pb.F_Int32Defaulted = Int32(32) + pb.F_Int64Defaulted = Int64(64) + pb.F_Fixed32Defaulted = Uint32(320) + pb.F_Fixed64Defaulted = Uint64(640) + pb.F_Uint32Defaulted = Uint32(3200) + pb.F_Uint64Defaulted = Uint64(6400) + pb.F_FloatDefaulted = Float32(314159) + pb.F_DoubleDefaulted = Float64(271828) + pb.F_StringDefaulted = String("hello, \"world!\"\n") + pb.F_BytesDefaulted = []byte("Bignose") + pb.F_Sint32Defaulted = Int32(-32) + pb.F_Sint64Defaulted = Int64(-64) + + overify(t, pb, + "0807"+ // field 1, encoding 0, value 7 + "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) + "5001"+ // field 10, encoding 0, value 1 + "5803"+ // field 11, encoding 0, value 3 + "6006"+ // field 12, encoding 0, value 6 + "6d20000000"+ // field 13, encoding 5, value 32 + "714000000000000000"+ // field 14, encoding 1, value 64 + "78a019"+ // field 15, encoding 0, value 3232 + "8001c032"+ // field 16, encoding 0, value 6464 + "8d0100004a45"+ // field 17, encoding 5, value 3232.0 + "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 + "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" + "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" + "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 + "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 + "c00201"+ // field 40, encoding 0, value 1 + "c80220"+ // field 41, encoding 0, value 32 + "d00240"+ // field 42, encoding 0, value 64 + "dd0240010000"+ // field 43, encoding 5, value 320 + "e1028002000000000000"+ // field 44, encoding 1, value 640 + "e8028019"+ // field 45, encoding 0, value 3200 + "f0028032"+ // field 46, encoding 0, value 6400 + "fd02e0659948"+ // field 47, encoding 5, value 314159.0 + "81030000000050971041"+ // field 48, encoding 1, value 271828.0 + "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" + "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" + "90193f"+ // field 402, encoding 0, value 63 + "98197f"+ // field 403, encoding 0, value 127 + "b304"+ // start group field 70 level 1 + "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" + "b404") // end group field 70 level 1 + +} + +// All required fields set, defaults provided, all non-defaulted optional fields have values. +func TestEncodeDecode4(t *testing.T) { + pb := initGoTest(true) + pb.Table = String("hello") + pb.Param = Int32(7) + pb.OptionalField = initGoTestField() + pb.F_BoolOptional = Bool(true) + pb.F_Int32Optional = Int32(32) + pb.F_Int64Optional = Int64(64) + pb.F_Fixed32Optional = Uint32(3232) + pb.F_Fixed64Optional = Uint64(6464) + pb.F_Uint32Optional = Uint32(323232) + pb.F_Uint64Optional = Uint64(646464) + pb.F_FloatOptional = Float32(32.) + pb.F_DoubleOptional = Float64(64.) + pb.F_StringOptional = String("hello") + pb.F_BytesOptional = []byte("Bignose") + pb.F_Sint32Optional = Int32(-32) + pb.F_Sint64Optional = Int64(-64) + pb.Optionalgroup = initGoTest_OptionalGroup() + + overify(t, pb, + "0807"+ // field 1, encoding 0, value 7 + "1205"+"68656c6c6f"+ // field 2, encoding 2, string "hello" + "1807"+ // field 3, encoding 0, value 7 + "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) + "320d"+"0a056c6162656c120474797065"+ // field 6, encoding 2 (GoTestField) + "5001"+ // field 10, encoding 0, value 1 + "5803"+ // field 11, encoding 0, value 3 + "6006"+ // field 12, encoding 0, value 6 + "6d20000000"+ // field 13, encoding 5, value 32 + "714000000000000000"+ // field 14, encoding 1, value 64 + "78a019"+ // field 15, encoding 0, value 3232 + "8001c032"+ // field 16, encoding 0, value 6464 + "8d0100004a45"+ // field 17, encoding 5, value 3232.0 + "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 + "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" + "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" + "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 + "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 + "f00101"+ // field 30, encoding 0, value 1 + "f80120"+ // field 31, encoding 0, value 32 + "800240"+ // field 32, encoding 0, value 64 + "8d02a00c0000"+ // field 33, encoding 5, value 3232 + "91024019000000000000"+ // field 34, encoding 1, value 6464 + "9802a0dd13"+ // field 35, encoding 0, value 323232 + "a002c0ba27"+ // field 36, encoding 0, value 646464 + "ad0200000042"+ // field 37, encoding 5, value 32.0 + "b1020000000000005040"+ // field 38, encoding 1, value 64.0 + "ba0205"+"68656c6c6f"+ // field 39, encoding 2, string "hello" + "ea1207"+"4269676e6f7365"+ // field 301, encoding 2, string "Bignose" + "f0123f"+ // field 302, encoding 0, value 63 + "f8127f"+ // field 303, encoding 0, value 127 + "c00201"+ // field 40, encoding 0, value 1 + "c80220"+ // field 41, encoding 0, value 32 + "d00240"+ // field 42, encoding 0, value 64 + "dd0240010000"+ // field 43, encoding 5, value 320 + "e1028002000000000000"+ // field 44, encoding 1, value 640 + "e8028019"+ // field 45, encoding 0, value 3200 + "f0028032"+ // field 46, encoding 0, value 6400 + "fd02e0659948"+ // field 47, encoding 5, value 314159.0 + "81030000000050971041"+ // field 48, encoding 1, value 271828.0 + "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" + "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" + "90193f"+ // field 402, encoding 0, value 63 + "98197f"+ // field 403, encoding 0, value 127 + "b304"+ // start group field 70 level 1 + "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" + "b404"+ // end group field 70 level 1 + "d305"+ // start group field 90 level 1 + "da0508"+"6f7074696f6e616c"+ // field 91, encoding 2, string "optional" + "d405") // end group field 90 level 1 + +} + +// All required fields set, defaults provided, all repeated fields given two values. +func TestEncodeDecode5(t *testing.T) { + pb := initGoTest(true) + pb.RepeatedField = []*GoTestField{initGoTestField(), initGoTestField()} + pb.F_BoolRepeated = []bool{false, true} + pb.F_Int32Repeated = []int32{32, 33} + pb.F_Int64Repeated = []int64{64, 65} + pb.F_Fixed32Repeated = []uint32{3232, 3333} + pb.F_Fixed64Repeated = []uint64{6464, 6565} + pb.F_Uint32Repeated = []uint32{323232, 333333} + pb.F_Uint64Repeated = []uint64{646464, 656565} + pb.F_FloatRepeated = []float32{32., 33.} + pb.F_DoubleRepeated = []float64{64., 65.} + pb.F_StringRepeated = []string{"hello", "sailor"} + pb.F_BytesRepeated = [][]byte{[]byte("big"), []byte("nose")} + pb.F_Sint32Repeated = []int32{32, -32} + pb.F_Sint64Repeated = []int64{64, -64} + pb.Repeatedgroup = []*GoTest_RepeatedGroup{initGoTest_RepeatedGroup(), initGoTest_RepeatedGroup()} + + overify(t, pb, + "0807"+ // field 1, encoding 0, value 7 + "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) + "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField) + "2a0d"+"0a056c6162656c120474797065"+ // field 5, encoding 2 (GoTestField) + "5001"+ // field 10, encoding 0, value 1 + "5803"+ // field 11, encoding 0, value 3 + "6006"+ // field 12, encoding 0, value 6 + "6d20000000"+ // field 13, encoding 5, value 32 + "714000000000000000"+ // field 14, encoding 1, value 64 + "78a019"+ // field 15, encoding 0, value 3232 + "8001c032"+ // field 16, encoding 0, value 6464 + "8d0100004a45"+ // field 17, encoding 5, value 3232.0 + "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 + "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" + "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" + "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 + "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 + "a00100"+ // field 20, encoding 0, value 0 + "a00101"+ // field 20, encoding 0, value 1 + "a80120"+ // field 21, encoding 0, value 32 + "a80121"+ // field 21, encoding 0, value 33 + "b00140"+ // field 22, encoding 0, value 64 + "b00141"+ // field 22, encoding 0, value 65 + "bd01a00c0000"+ // field 23, encoding 5, value 3232 + "bd01050d0000"+ // field 23, encoding 5, value 3333 + "c1014019000000000000"+ // field 24, encoding 1, value 6464 + "c101a519000000000000"+ // field 24, encoding 1, value 6565 + "c801a0dd13"+ // field 25, encoding 0, value 323232 + "c80195ac14"+ // field 25, encoding 0, value 333333 + "d001c0ba27"+ // field 26, encoding 0, value 646464 + "d001b58928"+ // field 26, encoding 0, value 656565 + "dd0100000042"+ // field 27, encoding 5, value 32.0 + "dd0100000442"+ // field 27, encoding 5, value 33.0 + "e1010000000000005040"+ // field 28, encoding 1, value 64.0 + "e1010000000000405040"+ // field 28, encoding 1, value 65.0 + "ea0105"+"68656c6c6f"+ // field 29, encoding 2, string "hello" + "ea0106"+"7361696c6f72"+ // field 29, encoding 2, string "sailor" + "ca0c03"+"626967"+ // field 201, encoding 2, string "big" + "ca0c04"+"6e6f7365"+ // field 201, encoding 2, string "nose" + "d00c40"+ // field 202, encoding 0, value 32 + "d00c3f"+ // field 202, encoding 0, value -32 + "d80c8001"+ // field 203, encoding 0, value 64 + "d80c7f"+ // field 203, encoding 0, value -64 + "c00201"+ // field 40, encoding 0, value 1 + "c80220"+ // field 41, encoding 0, value 32 + "d00240"+ // field 42, encoding 0, value 64 + "dd0240010000"+ // field 43, encoding 5, value 320 + "e1028002000000000000"+ // field 44, encoding 1, value 640 + "e8028019"+ // field 45, encoding 0, value 3200 + "f0028032"+ // field 46, encoding 0, value 6400 + "fd02e0659948"+ // field 47, encoding 5, value 314159.0 + "81030000000050971041"+ // field 48, encoding 1, value 271828.0 + "8a0310"+"68656c6c6f2c2022776f726c6421220a"+ // field 49, encoding 2 string "hello, \"world!\"\n" + "8a1907"+"4269676e6f7365"+ // field 401, encoding 2, string "Bignose" + "90193f"+ // field 402, encoding 0, value 63 + "98197f"+ // field 403, encoding 0, value 127 + "b304"+ // start group field 70 level 1 + "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" + "b404"+ // end group field 70 level 1 + "8305"+ // start group field 80 level 1 + "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated" + "8405"+ // end group field 80 level 1 + "8305"+ // start group field 80 level 1 + "8a0508"+"7265706561746564"+ // field 81, encoding 2, string "repeated" + "8405") // end group field 80 level 1 + +} + +// All required fields set, all packed repeated fields given two values. +func TestEncodeDecode6(t *testing.T) { + pb := initGoTest(false) + pb.F_BoolRepeatedPacked = []bool{false, true} + pb.F_Int32RepeatedPacked = []int32{32, 33} + pb.F_Int64RepeatedPacked = []int64{64, 65} + pb.F_Fixed32RepeatedPacked = []uint32{3232, 3333} + pb.F_Fixed64RepeatedPacked = []uint64{6464, 6565} + pb.F_Uint32RepeatedPacked = []uint32{323232, 333333} + pb.F_Uint64RepeatedPacked = []uint64{646464, 656565} + pb.F_FloatRepeatedPacked = []float32{32., 33.} + pb.F_DoubleRepeatedPacked = []float64{64., 65.} + pb.F_Sint32RepeatedPacked = []int32{32, -32} + pb.F_Sint64RepeatedPacked = []int64{64, -64} + + overify(t, pb, + "0807"+ // field 1, encoding 0, value 7 + "220d"+"0a056c6162656c120474797065"+ // field 4, encoding 2 (GoTestField) + "5001"+ // field 10, encoding 0, value 1 + "5803"+ // field 11, encoding 0, value 3 + "6006"+ // field 12, encoding 0, value 6 + "6d20000000"+ // field 13, encoding 5, value 32 + "714000000000000000"+ // field 14, encoding 1, value 64 + "78a019"+ // field 15, encoding 0, value 3232 + "8001c032"+ // field 16, encoding 0, value 6464 + "8d0100004a45"+ // field 17, encoding 5, value 3232.0 + "9101000000000040b940"+ // field 18, encoding 1, value 6464.0 + "9a0106"+"737472696e67"+ // field 19, encoding 2 string "string" + "aa0605"+"6279746573"+ // field 101, encoding 2 string "bytes" + "b0063f"+ // field 102, encoding 0, 0x3f zigzag32 + "b8067f"+ // field 103, encoding 0, 0x7f zigzag64 + "9203020001"+ // field 50, encoding 2, 2 bytes, value 0, value 1 + "9a03022021"+ // field 51, encoding 2, 2 bytes, value 32, value 33 + "a203024041"+ // field 52, encoding 2, 2 bytes, value 64, value 65 + "aa0308"+ // field 53, encoding 2, 8 bytes + "a00c0000050d0000"+ // value 3232, value 3333 + "b20310"+ // field 54, encoding 2, 16 bytes + "4019000000000000a519000000000000"+ // value 6464, value 6565 + "ba0306"+ // field 55, encoding 2, 6 bytes + "a0dd1395ac14"+ // value 323232, value 333333 + "c20306"+ // field 56, encoding 2, 6 bytes + "c0ba27b58928"+ // value 646464, value 656565 + "ca0308"+ // field 57, encoding 2, 8 bytes + "0000004200000442"+ // value 32.0, value 33.0 + "d20310"+ // field 58, encoding 2, 16 bytes + "00000000000050400000000000405040"+ // value 64.0, value 65.0 + "b21f02"+ // field 502, encoding 2, 2 bytes + "403f"+ // value 32, value -32 + "ba1f03"+ // field 503, encoding 2, 3 bytes + "80017f"+ // value 64, value -64 + "b304"+ // start group field 70 level 1 + "ba0408"+"7265717569726564"+ // field 71, encoding 2, string "required" + "b404") // end group field 70 level 1 +} + +// Test that we can encode empty bytes fields. +func TestEncodeDecodeBytes1(t *testing.T) { + pb := initGoTest(false) + + // Create our bytes + pb.F_BytesRequired = []byte{} + pb.F_BytesRepeated = [][]byte{{}} + pb.F_BytesOptional = []byte{} + + d, err := Marshal(pb) + if err != nil { + t.Errorf(err.String()) + } + + pbd := new(GoTest) + if err := Unmarshal(d, pbd); err != nil { + t.Errorf(err.String()) + } + + if pbd.F_BytesRequired == nil || len(pbd.F_BytesRequired) != 0 { + t.Errorf("required empty bytes field is incorrect") + } + if pbd.F_BytesRepeated == nil || len(pbd.F_BytesRepeated) == 1 && pbd.F_BytesRepeated[0] == nil { + t.Errorf("repeated empty bytes field is incorrect") + } + if pbd.F_BytesOptional == nil || len(pbd.F_BytesOptional) != 0 { + t.Errorf("optional empty bytes field is incorrect") + } +} + +// Test that we encode nil-valued fields of a repeated bytes field correctly. +// Since entries in a repeated field cannot be nil, nil must mean empty value. +func TestEncodeDecodeBytes2(t *testing.T) { + pb := initGoTest(false) + + // Create our bytes + pb.F_BytesRepeated = [][]byte{nil} + + d, err := Marshal(pb) + if err != nil { + t.Errorf(err.String()) + } + + pbd := new(GoTest) + if err := Unmarshal(d, pbd); err != nil { + t.Errorf(err.String()) + } + + if len(pbd.F_BytesRepeated) != 1 || pbd.F_BytesRepeated[0] == nil { + t.Errorf("Unexpected value for repeated bytes field") + } +} + +// All required fields set, defaults provided, all repeated fields given two values. +func TestSkippingUnrecognizedFields(t *testing.T) { + o := old() + pb := initGoTestField() + + // Marshal it normally. + o.Marshal(pb) + + // Now new a GoSkipTest record. + skip := &GoSkipTest{ + SkipInt32: Int32(32), + SkipFixed32: Uint32(3232), + SkipFixed64: Uint64(6464), + SkipString: String("skipper"), + Skipgroup: &GoSkipTest_SkipGroup{ + GroupInt32: Int32(75), + GroupString: String("wxyz"), + }, + } + + // Marshal it into same buffer. + o.Marshal(skip) + + pbd := new(GoTestField) + o.Unmarshal(pbd) + + // The __unrecognized field should be a marshaling of GoSkipTest + skipd := new(GoSkipTest) + + o.SetBuf(pbd.XXX_unrecognized) + o.Unmarshal(skipd) + + if *skipd.SkipInt32 != *skip.SkipInt32 { + t.Error("skip int32", skipd.SkipInt32) + } + if *skipd.SkipFixed32 != *skip.SkipFixed32 { + t.Error("skip fixed32", skipd.SkipFixed32) + } + if *skipd.SkipFixed64 != *skip.SkipFixed64 { + t.Error("skip fixed64", skipd.SkipFixed64) + } + if *skipd.SkipString != *skip.SkipString { + t.Error("skip string", *skipd.SkipString) + } + if *skipd.Skipgroup.GroupInt32 != *skip.Skipgroup.GroupInt32 { + t.Error("skip group int32", skipd.Skipgroup.GroupInt32) + } + if *skipd.Skipgroup.GroupString != *skip.Skipgroup.GroupString { + t.Error("skip group string", *skipd.Skipgroup.GroupString) + } +} + +// Check that we can grow an array (repeated field) to have many elements. +// This test doesn't depend only on our encoding; for variety, it makes sure +// we create, encode, and decode the correct contents explicitly. It's therefore +// a bit messier. +// This test also uses (and hence tests) the Marshal/Unmarshal functions +// instead of the methods. +func TestBigRepeated(t *testing.T) { + pb := initGoTest(true) + + // Create the arrays + const N = 50 // Internally the library starts much smaller. + pb.Repeatedgroup = make([]*GoTest_RepeatedGroup, N) + pb.F_Sint64Repeated = make([]int64, N) + pb.F_Sint32Repeated = make([]int32, N) + pb.F_BytesRepeated = make([][]byte, N) + pb.F_StringRepeated = make([]string, N) + pb.F_DoubleRepeated = make([]float64, N) + pb.F_FloatRepeated = make([]float32, N) + pb.F_Uint64Repeated = make([]uint64, N) + pb.F_Uint32Repeated = make([]uint32, N) + pb.F_Fixed64Repeated = make([]uint64, N) + pb.F_Fixed32Repeated = make([]uint32, N) + pb.F_Int64Repeated = make([]int64, N) + pb.F_Int32Repeated = make([]int32, N) + pb.F_BoolRepeated = make([]bool, N) + pb.RepeatedField = make([]*GoTestField, N) + + // Fill in the arrays with checkable values. + igtf := initGoTestField() + igtrg := initGoTest_RepeatedGroup() + for i := 0; i < N; i++ { + pb.Repeatedgroup[i] = igtrg + pb.F_Sint64Repeated[i] = int64(i) + pb.F_Sint32Repeated[i] = int32(i) + s := fmt.Sprint(i) + pb.F_BytesRepeated[i] = []byte(s) + pb.F_StringRepeated[i] = s + pb.F_DoubleRepeated[i] = float64(i) + pb.F_FloatRepeated[i] = float32(i) + pb.F_Uint64Repeated[i] = uint64(i) + pb.F_Uint32Repeated[i] = uint32(i) + pb.F_Fixed64Repeated[i] = uint64(i) + pb.F_Fixed32Repeated[i] = uint32(i) + pb.F_Int64Repeated[i] = int64(i) + pb.F_Int32Repeated[i] = int32(i) + pb.F_BoolRepeated[i] = i%2 == 0 + pb.RepeatedField[i] = igtf + } + + // Marshal. + buf, _ := Marshal(pb) + + // Now test Unmarshal by recreating the original buffer. + pbd := new(GoTest) + Unmarshal(buf, pbd) + + // Check the checkable values + for i := uint64(0); i < N; i++ { + if pbd.Repeatedgroup[i] == nil { // TODO: more checking? + t.Error("pbd.Repeatedgroup bad") + } + var x uint64 + x = uint64(pbd.F_Sint64Repeated[i]) + if x != i { + t.Error("pbd.F_Sint64Repeated bad", x, i) + } + x = uint64(pbd.F_Sint32Repeated[i]) + if x != i { + t.Error("pbd.F_Sint32Repeated bad", x, i) + } + s := fmt.Sprint(i) + equalbytes(pbd.F_BytesRepeated[i], []byte(s), t) + if pbd.F_StringRepeated[i] != s { + t.Error("pbd.F_Sint32Repeated bad", pbd.F_StringRepeated[i], i) + } + x = uint64(pbd.F_DoubleRepeated[i]) + if x != i { + t.Error("pbd.F_DoubleRepeated bad", x, i) + } + x = uint64(pbd.F_FloatRepeated[i]) + if x != i { + t.Error("pbd.F_FloatRepeated bad", x, i) + } + x = pbd.F_Uint64Repeated[i] + if x != i { + t.Error("pbd.F_Uint64Repeated bad", x, i) + } + x = uint64(pbd.F_Uint32Repeated[i]) + if x != i { + t.Error("pbd.F_Uint32Repeated bad", x, i) + } + x = pbd.F_Fixed64Repeated[i] + if x != i { + t.Error("pbd.F_Fixed64Repeated bad", x, i) + } + x = uint64(pbd.F_Fixed32Repeated[i]) + if x != i { + t.Error("pbd.F_Fixed32Repeated bad", x, i) + } + x = uint64(pbd.F_Int64Repeated[i]) + if x != i { + t.Error("pbd.F_Int64Repeated bad", x, i) + } + x = uint64(pbd.F_Int32Repeated[i]) + if x != i { + t.Error("pbd.F_Int32Repeated bad", x, i) + } + if pbd.F_BoolRepeated[i] != (i%2 == 0) { + t.Error("pbd.F_BoolRepeated bad", x, i) + } + if pbd.RepeatedField[i] == nil { // TODO: more checking? + t.Error("pbd.RepeatedField bad") + } + } +} + +// Verify we give a useful message when decoding to the wrong structure type. +func TestTypeMismatch(t *testing.T) { + pb1 := initGoTest(true) + + // Marshal + o := old() + o.Marshal(pb1) + + // Now Unmarshal it to the wrong type. + pb2 := initGoTestField() + err := o.Unmarshal(pb2) + switch err { + case ErrWrongType: + // fine + case nil: + t.Error("expected wrong type error, got no error") + default: + t.Error("expected wrong type error, got", err) + } +} + +func encodeDecode(t *testing.T, in, out interface{}, msg string) { + buf, err := Marshal(in) + if err != nil { + t.Fatalf("failed marshaling %v: %v", msg, err) + } + if err := Unmarshal(buf, out); err != nil { + t.Fatalf("failed unmarshaling %v: %v", msg, err) + } +} + +func TestPackedNonPackedDecoderSwitching(t *testing.T) { + np, p := new(NonPackedTest), new(PackedTest) + + // non-packed -> packed + np.A = []int32{0, 1, 1, 2, 3, 5} + encodeDecode(t, np, p, "non-packed -> packed") + if !reflect.DeepEqual(np.A, p.B) { + t.Errorf("failed non-packed -> packed; np.A=%+v, p.B=%+v", np.A, p.B) + } + + // packed -> non-packed + np.Reset() + p.B = []int32{3, 1, 4, 1, 5, 9} + encodeDecode(t, p, np, "packed -> non-packed") + if !reflect.DeepEqual(p.B, np.A) { + t.Errorf("failed packed -> non-packed; p.B=%+v, np.A=%+v", p.B, np.A) + } +} + +func TestProto1RepeatedGroup(t *testing.T) { + pb := &MessageList{ + Message: []*MessageList_Message{ + &MessageList_Message{ + Name: String("blah"), + Count: Int32(7), + }, + // NOTE: pb.Message[1] is a nil + nil, + }, + } + + o := old() + if err := o.Marshal(pb); err != ErrRepeatedHasNil { + t.Fatalf("unexpected or no error when marshaling: %v", err) + } +} + + +// Test that enums work. Checks for a bug introduced by making enums +// named types instead of int32: newInt32FromUint64 would crash with +// a type mismatch in reflect.PointTo. +func TestEnum(t *testing.T) { + pb := new(GoEnum) + pb.Foo = NewFOO(FOO_FOO1) + o := old() + if err := o.Marshal(pb); err != nil { + t.Fatal("error encoding enum:", err) + } + pb1 := new(GoEnum) + if err := o.Unmarshal(pb1); err != nil { + t.Fatal("error decoding enum:", err) + } + if *pb1.Foo != FOO_FOO1 { + t.Error("expected 7 but got ", *pb1.Foo) + } +} + +// Enum types have String methods. Check that enum fields can be printed. +// We don't care what the value actually is, just as long as it doesn't crash. +func TestPrintingNilEnumFields(t *testing.T) { + pb := new(GoEnum) + fmt.Sprintf("%+v", pb) +} + +// Verify that absent required fields cause Marshal/Unmarshal to return errors. +func TestRequiredFieldEnforcement(t *testing.T) { + pb := new(GoTestField) + _, err := Marshal(pb) + if err == nil { + t.Error("marshal: expected error, got nil") + } else if strings.Index(err.String(), "GoTestField") < 0 { + t.Errorf("marshal: bad error type: %v", err) + } + + // A slightly sneaky, yet valid, proto. It encodes the same required field twice, + // so simply counting the required fields is insufficient. + // field 1, encoding 2, value "hi" + buf := []byte("\x0A\x02hi\x0A\x02hi") + err = Unmarshal(buf, pb) + if err == nil { + t.Error("unmarshal: expected error, got nil") + } else if strings.Index(err.String(), "GoTestField") < 0 { + t.Errorf("unmarshal: bad error type: %v", err) + } +} + +// A type that implements the Marshaler interface, but is not nillable. +type nonNillableInt uint64 + +func (nni nonNillableInt) Marshal() ([]byte, os.Error) { + return EncodeVarint(uint64(nni)), nil +} + +type NNIMessage struct { + nni nonNillableInt +} + +// A type that implements the Marshaler interface and is nillable. +type nillableMessage struct { + x uint64 +} + +func (nm *nillableMessage) Marshal() ([]byte, os.Error) { + return EncodeVarint(nm.x), nil +} + +type NMMessage struct { + nm *nillableMessage +} + +// Verify a type that uses the Marshaler interface, but has a nil pointer. +func TestNilMarshaler(t *testing.T) { + // Try a struct with a Marshaler field that is nil. + // It should be directly marshable. + nmm := new(NMMessage) + if _, err := Marshal(nmm); err != nil { + t.Error("unexpected error marshaling nmm: ", err) + } + + // Try a struct with a Marshaler field that is not nillable. + nnim := new(NNIMessage) + nnim.nni = 7 + var _ Marshaler = nnim.nni // verify it is truly a Marshaler + if _, err := Marshal(nnim); err != nil { + t.Error("unexpected error marshaling nnim: ", err) + } +} + +// Check that passing a struct to Marshal returns a good error, +// rather than panicking. +func TestStructMarshaling(t *testing.T) { + _, err := Marshal(OtherMessage{}) + if err != ErrNotPtr { + t.Errorf("got %v, expected %v", err, ErrNotPtr) + } +} + +func TestAllSetDefaults(t *testing.T) { + // Exercise SetDefaults with all scalar field types. + m := &Defaults{ + // NaN != NaN, so override that here. + F_Nan: Float32(1.7), + } + expected := &Defaults{ + F_Bool: Bool(true), + F_Int32: Int32(32), + F_Int64: Int64(64), + F_Fixed32: Uint32(320), + F_Fixed64: Uint64(640), + F_Uint32: Uint32(3200), + F_Uint64: Uint64(6400), + F_Float: Float32(314159), + F_Double: Float64(271828), + F_String: String(`hello, "world!"` + "\n"), + F_Bytes: []byte("Bignose"), + F_Sint32: Int32(-32), + F_Sint64: Int64(-64), + F_Enum: NewDefaults_Color(Defaults_GREEN), + F_Pinf: Float32(float32(math.Inf(1))), + F_Ninf: Float32(float32(math.Inf(-1))), + F_Nan: Float32(1.7), + } + SetDefaults(m) + if !Equal(m, expected) { + t.Errorf(" got %v\nwant %v", m, expected) + } +} + +func TestSetDefaultsWithSetField(t *testing.T) { + // Check that a set value is not overridden. + m := &Defaults{ + F_Int32: Int32(12), + } + SetDefaults(m) + if v := GetInt32(m.F_Int32); v != 12 { + t.Errorf("m.FInt32 = %v, want 12", v) + } +} + +func TestSetDefaultsWithSubMessage(t *testing.T) { + m := &OtherMessage{ + Key: Int64(123), + Inner: &InnerMessage{ + Host: String("gopher"), + }, + } + expected := &OtherMessage{ + Key: Int64(123), + Inner: &InnerMessage{ + Host: String("gopher"), + Port: Int32(4000), + }, + } + SetDefaults(m) + if !Equal(m, expected) { + t.Errorf(" got %v\nwant %v", m, expected) + } +} + +func TestMaximumTagNumber(t *testing.T) { + m := &MaxTag{ + LastField: String("natural goat essence"), + } + buf, err := Marshal(m) + if err != nil { + t.Fatalf("proto.Marshal failed: %v", err) + } + m2 := new(MaxTag) + if err := Unmarshal(buf, m2); err != nil { + t.Fatalf("proto.Unmarshal failed: %v", err) + } + if got, want := GetString(m2.LastField), *m.LastField; got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestJSON(t *testing.T) { + m := &MyMessage{ + Count: Int32(4), + Pet: []string{"bunny", "kitty"}, + Inner: &InnerMessage{ + Host: String("cauchy"), + }, + } + const expected = `{"count":4,"pet":["bunny","kitty"],"inner":{"host":"cauchy"}}` + + b, err := json.Marshal(m) + if err != nil { + t.Fatalf("json.Marshal failed: %v", err) + } + s := string(b) + if s != expected { + t.Errorf("got %s\nwant %s", s, expected) + } +} + +func BenchmarkMarshal(b *testing.B) { + b.StopTimer() + + pb := initGoTest(true) + + // Create an array + const N = 1000 // Internally the library starts much smaller. + pb.F_Int32Repeated = make([]int32, N) + pb.F_DoubleRepeated = make([]float64, N) + + // Fill in the array with some values. + for i := 0; i < N; i++ { + pb.F_Int32Repeated[i] = int32(i) + pb.F_DoubleRepeated[i] = float64(i) + } + + p := NewBuffer(nil) + + b.StartTimer() + for i := 0; i < b.N; i++ { + p.Reset() + p.Marshal(pb) + } +} + +func BenchmarkUnmarshal(b *testing.B) { + b.StopTimer() + + pb := initGoTest(true) + + // Create an array + const N = 1000 // Internally the library starts much smaller. + pb.F_Int32Repeated = make([]int32, N) + + // Fill in the array with some values. + for i := 0; i < N; i++ { + pb.F_Int32Repeated[i] = int32(i) + } + pbd := new(GoTest) + p := NewBuffer(nil) + p.Marshal(pb) + p2 := NewBuffer(nil) + + b.StartTimer() + for i := 0; i < b.N; i++ { + p2.SetBuf(p.Bytes()) + p2.Unmarshal(pbd) + } +} diff --git a/proto/clone.go b/proto/clone.go new file mode 100644 index 0000000..05fb388 --- /dev/null +++ b/proto/clone.go @@ -0,0 +1,122 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Protocol buffer deep copy. +// TODO: MessageSet and RawMessage. + +package proto + +import ( + "log" + "reflect" + "strings" +) + +// Clone returns a deep copy of a protocol buffer. +// pb must be a pointer to a protocol buffer struct. +func Clone(pb interface{}) interface{} { + in := reflect.ValueOf(pb) + if in.Kind() != reflect.Ptr || in.Elem().Kind() != reflect.Struct { + return nil + } + + out := reflect.New(in.Type().Elem()) + copyStruct(out.Elem(), in.Elem()) + return out.Interface() +} + +func copyStruct(out, in reflect.Value) { + for i := 0; i < in.NumField(); i++ { + f := in.Type().Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + copyAny(out.Field(i), in.Field(i)) + } + + if emIn, ok := in.Addr().Interface().(extendableProto); ok { + emOut := out.Addr().Interface().(extendableProto) + copyExtension(emOut.ExtensionMap(), emIn.ExtensionMap()) + } + + // TODO: Deal with XXX_unrecognized. +} + +func copyAny(out, in reflect.Value) { + switch in.Kind() { + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, + reflect.String, reflect.Uint32, reflect.Uint64: + out.Set(in) + case reflect.Ptr: + if in.IsNil() { + return + } + out.Set(reflect.New(in.Type().Elem())) + copyAny(out.Elem(), in.Elem()) + case reflect.Slice: + if in.IsNil() { + return + } + n := in.Len() + out.Set(reflect.MakeSlice(in.Type(), n, n)) + switch in.Type().Elem().Kind() { + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, + reflect.String, reflect.Uint32, reflect.Uint64, reflect.Uint8: + reflect.Copy(out, in) + default: + for i := 0; i < n; i++ { + copyAny(out.Index(i), in.Index(i)) + } + } + case reflect.Struct: + copyStruct(out, in) + default: + // unknown type, so not a protocol buffer + log.Printf("proto: don't know how to copy %v", in) + } +} + +func copyExtension(out, in map[int32]Extension) { + for extNum, eIn := range in { + eOut := Extension{desc: eIn.desc} + if eIn.value != nil { + v := reflect.New(reflect.TypeOf(eIn.value)).Elem() + copyAny(v, reflect.ValueOf(eIn.value)) + eOut.value = v.Interface() + } + if eIn.enc != nil { + eOut.enc = make([]byte, len(eIn.enc)) + copy(eOut.enc, eIn.enc) + } + + out[extNum] = eOut + } +} diff --git a/proto/clone_test.go b/proto/clone_test.go new file mode 100644 index 0000000..c9c4a2f --- /dev/null +++ b/proto/clone_test.go @@ -0,0 +1,80 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto_test + +import ( + "log" + "testing" + + "goprotobuf.googlecode.com/hg/proto" + + pb "./testdata/_obj/test_proto" +) + +var cloneTestMessage = &pb.MyMessage{ + Count: proto.Int32(42), + Name: proto.String("Dave"), + Pet: []string{"bunny", "kitty", "horsey"}, + Inner: &pb.InnerMessage{ + Host: proto.String("niles"), + Port: proto.Int32(9099), + Connected: proto.Bool(true), + }, + Others: []*pb.OtherMessage{ + &pb.OtherMessage{ + Value: []byte("some bytes"), + }, + }, + RepBytes: [][]byte{[]byte("sham"), []byte("wow")}, +} + +func init() { + ext := &pb.Ext{ + Data: proto.String("extension"), + } + if err := proto.SetExtension(cloneTestMessage, pb.E_Ext_More, ext); err != nil { + log.Fatalf("SetExtension: %v", err) + } +} + +func TestClone(t *testing.T) { + m := proto.Clone(cloneTestMessage).(*pb.MyMessage) + if !proto.Equal(m, cloneTestMessage) { + t.Errorf("Clone(%v) = %v", cloneTestMessage, m) + } + + // Verify it was a deep copy. + *m.Inner.Port++ + if proto.Equal(m, cloneTestMessage) { + t.Error("Mutating clone changed the original") + } +} diff --git a/proto/decode.go b/proto/decode.go new file mode 100644 index 0000000..fe28821 --- /dev/null +++ b/proto/decode.go @@ -0,0 +1,773 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Routines for decoding protocol buffer data to construct in-memory representations. + */ + +import ( + "fmt" + "io" + "os" + "reflect" + "runtime" + "unsafe" +) + +// ErrWrongType occurs when the wire encoding for the field disagrees with +// that specified in the type being decoded. This is usually caused by attempting +// to convert an encoded protocol buffer into a struct of the wrong type. +var ErrWrongType = os.NewError("field/encoding mismatch: wrong type for field") + +// The fundamental decoders that interpret bytes on the wire. +// Those that take integer types all return uint64 and are +// therefore of type valueDecoder. + +// DecodeVarint reads a varint-encoded integer from the slice. +// It returns the integer and the number of bytes consumed, or +// zero if there is not enough. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +func DecodeVarint(buf []byte) (x uint64, n int) { + // x, n already 0 + for shift := uint(0); ; shift += 7 { + if n >= len(buf) { + return 0, 0 + } + b := uint64(buf[n]) + n++ + x |= (b & 0x7F) << shift + if (b & 0x80) == 0 { + break + } + } + return x, n +} + +// DecodeVarint reads a varint-encoded integer from the Buffer. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +func (p *Buffer) DecodeVarint() (x uint64, err os.Error) { + // x, err already 0 + + i := p.index + l := len(p.buf) + + for shift := uint(0); ; shift += 7 { + if i >= l { + err = io.ErrUnexpectedEOF + return + } + b := p.buf[i] + i++ + x |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + p.index = i + return +} + +// DecodeFixed64 reads a 64-bit integer from the Buffer. +// This is the format for the +// fixed64, sfixed64, and double protocol buffer types. +func (p *Buffer) DecodeFixed64() (x uint64, err os.Error) { + // x, err already 0 + i := p.index + 8 + if i > len(p.buf) { + err = io.ErrUnexpectedEOF + return + } + p.index = i + + x = uint64(p.buf[i-8]) + x |= uint64(p.buf[i-7]) << 8 + x |= uint64(p.buf[i-6]) << 16 + x |= uint64(p.buf[i-5]) << 24 + x |= uint64(p.buf[i-4]) << 32 + x |= uint64(p.buf[i-3]) << 40 + x |= uint64(p.buf[i-2]) << 48 + x |= uint64(p.buf[i-1]) << 56 + return +} + +// DecodeFixed32 reads a 32-bit integer from the Buffer. +// This is the format for the +// fixed32, sfixed32, and float protocol buffer types. +func (p *Buffer) DecodeFixed32() (x uint64, err os.Error) { + // x, err already 0 + i := p.index + 4 + if i > len(p.buf) { + err = io.ErrUnexpectedEOF + return + } + p.index = i + + x = uint64(p.buf[i-4]) + x |= uint64(p.buf[i-3]) << 8 + x |= uint64(p.buf[i-2]) << 16 + x |= uint64(p.buf[i-1]) << 24 + return +} + +// DecodeZigzag64 reads a zigzag-encoded 64-bit integer +// from the Buffer. +// This is the format used for the sint64 protocol buffer type. +func (p *Buffer) DecodeZigzag64() (x uint64, err os.Error) { + x, err = p.DecodeVarint() + if err != nil { + return + } + x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63) + return +} + +// DecodeZigzag32 reads a zigzag-encoded 32-bit integer +// from the Buffer. +// This is the format used for the sint32 protocol buffer type. +func (p *Buffer) DecodeZigzag32() (x uint64, err os.Error) { + x, err = p.DecodeVarint() + if err != nil { + return + } + x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31)) + return +} + +// These are not ValueDecoders: they produce an array of bytes or a string. +// bytes, embedded messages + +// DecodeRawBytes reads a count-delimited byte buffer from the Buffer. +// This is the format used for the bytes protocol buffer +// type and for embedded messages. +func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err os.Error) { + n, err := p.DecodeVarint() + if err != nil { + return + } + + nb := int(n) + if p.index+nb > len(p.buf) { + err = io.ErrUnexpectedEOF + return + } + + if !alloc { + // todo: check if can get more uses of alloc=false + buf = p.buf[p.index : p.index+nb] + p.index += nb + return + } + + buf = make([]byte, nb) + copy(buf, p.buf[p.index:]) + p.index += nb + return +} + +// DecodeStringBytes reads an encoded string from the Buffer. +// This is the format used for the proto2 string type. +func (p *Buffer) DecodeStringBytes() (s string, err os.Error) { + buf, err := p.DecodeRawBytes(false) + if err != nil { + return + } + return string(buf), nil +} + +// Skip the next item in the buffer. Its wire type is decoded and presented as an argument. +// If the protocol buffer has extensions, and the field matches, add it as an extension. +// Otherwise, if the XXX_unrecognized field exists, append the skipped data there. +func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base uintptr) os.Error { + + oi := o.index + + err := o.skip(t, tag, wire) + if err != nil { + return err + } + + x := fieldIndex(t, "XXX_unrecognized") + if x == nil { + return nil + } + + p := propByIndex(t, x) + ptr := (*[]byte)(unsafe.Pointer(base + p.offset)) + + if *ptr == nil { + // This is the first skipped element, + // allocate a new buffer. + *ptr = o.bufalloc() + } + + // Add the skipped field to struct field + obuf := o.buf + + o.buf = *ptr + o.EncodeVarint(uint64(tag<<3 | wire)) + *ptr = append(o.buf, obuf[oi:o.index]...) + + o.buf = obuf + + return nil +} + +// Skip the next item in the buffer. Its wire type is decoded and presented as an argument. +func (o *Buffer) skip(t reflect.Type, tag, wire int) os.Error { + + var u uint64 + var err os.Error + + switch wire { + case WireVarint: + _, err = o.DecodeVarint() + case WireFixed64: + _, err = o.DecodeFixed64() + case WireBytes: + _, err = o.DecodeRawBytes(false) + case WireFixed32: + _, err = o.DecodeFixed32() + case WireStartGroup: + for { + u, err = o.DecodeVarint() + if err != nil { + break + } + fwire := int(u & 0x7) + if fwire == WireEndGroup { + break + } + ftag := int(u >> 3) + err = o.skip(t, ftag, fwire) + if err != nil { + break + } + } + default: + fmt.Fprintf(os.Stderr, "proto: can't skip wire type %d for %s\n", wire, t) + } + return err +} + +// Unmarshaler is the interface representing objects that can unmarshal themselves. +type Unmarshaler interface { + Unmarshal([]byte) os.Error +} + +// Unmarshal parses the protocol buffer representation in buf and places the +// decoded result in pb. If the struct underlying pb does not match +// the data in buf, the results can be unpredictable. +func Unmarshal(buf []byte, pb interface{}) os.Error { + // If the object can unmarshal itself, let it. + if u, ok := pb.(Unmarshaler); ok { + return u.Unmarshal(buf) + } + + return NewBuffer(buf).Unmarshal(pb) +} + +// Unmarshal parses the protocol buffer representation in the +// Buffer and places the decoded result in pb. If the struct +// underlying pb does not match the data in the buffer, the results can be +// unpredictable. +func (p *Buffer) Unmarshal(pb interface{}) os.Error { + // If the object can unmarshal itself, let it. + if u, ok := pb.(Unmarshaler); ok { + err := u.Unmarshal(p.buf[p.index:]) + p.index = len(p.buf) + return err + } + + mstat := runtime.MemStats.Mallocs + + typ, base, err := getbase(pb) + if err != nil { + return err + } + + err = p.unmarshalType(typ, false, base) + + mstat = runtime.MemStats.Mallocs - mstat + stats.Dmalloc += mstat + stats.Decode++ + + return err +} + +// unmarshalType does the work of unmarshaling a structure. +func (o *Buffer) unmarshalType(t reflect.Type, is_group bool, base uintptr) os.Error { + st := t.Elem() + prop := GetProperties(st) + required, reqFields := prop.reqCount, uint64(0) + sbase := getsbase(prop) // scratch area for data items + + var err os.Error + for err == nil && o.index < len(o.buf) { + oi := o.index + var u uint64 + u, err = o.DecodeVarint() + if err != nil { + break + } + wire := int(u & 0x7) + if wire == WireEndGroup { + if is_group { + return nil // input is satisfied + } + return ErrWrongType + } + tag := int(u >> 3) + fieldnum, ok := prop.tags[tag] + if !ok { + // Maybe it's an extension? + o.ptr = base + iv := unsafe.Unreflect(t, unsafe.Pointer(&o.ptr)) + if e, ok := iv.(extendableProto); ok && isExtensionField(e, int32(tag)) { + if err = o.skip(st, tag, wire); err == nil { + e.ExtensionMap()[int32(tag)] = Extension{enc: append([]byte(nil), o.buf[oi:o.index]...)} + } + continue + } + err = o.skipAndSave(st, tag, wire, base) + continue + } + p := prop.Prop[fieldnum] + + if p.dec == nil { + fmt.Fprintf(os.Stderr, "no protobuf decoder for %s.%s\n", t, st.Field(fieldnum).Name) + continue + } + dec := p.dec + if wire != WireStartGroup && wire != p.WireType { + if wire == WireBytes && p.packedDec != nil { + // a packable field + dec = p.packedDec + } else { + err = ErrWrongType + continue + } + } + err = dec(o, p, base, sbase) + if err == nil && p.Required { + // Successfully decoded a required field. + if tag <= 64 { + // use bitmap for fields 1-64 to catch field reuse. + var mask uint64 = 1 << uint64(tag-1) + if reqFields&mask == 0 { + // new required field + reqFields |= mask + required-- + } + } else { + // This is imprecise. It can be fooled by a required field + // with a tag > 64 that is encoded twice; that's very rare. + // A fully correct implementation would require allocating + // a data structure, which we would like to avoid. + required-- + } + } + } + if err == nil { + if is_group { + return io.ErrUnexpectedEOF + } + if required > 0 { + return &ErrRequiredNotSet{st} + } + } + return err +} + +// Make *pslice have base address base, length 0, and capacity startSize. +func initSlice(pslice unsafe.Pointer, base uintptr) { + sp := (*reflect.SliceHeader)(pslice) + sp.Data = base + sp.Len = 0 + sp.Cap = startSize +} + +// Individual type decoders +// For each, +// u is the decoded value, +// v is a pointer to the field (pointer) in the struct +// x is a pointer to the preallocated scratch space to hold the decoded value. + +// Decode a bool. +func (o *Buffer) dec_bool(p *Properties, base uintptr, sbase uintptr) os.Error { + u, err := p.valDec(o) + if err != nil { + return err + } + v := (**uint8)(unsafe.Pointer(base + p.offset)) + x := (*uint8)(unsafe.Pointer(sbase + p.scratch)) + *x = uint8(u) + *v = x + return nil +} + +// Decode an int32. +func (o *Buffer) dec_int32(p *Properties, base uintptr, sbase uintptr) os.Error { + u, err := p.valDec(o) + if err != nil { + return err + } + v := (**int32)(unsafe.Pointer(base + p.offset)) + x := (*int32)(unsafe.Pointer(sbase + p.scratch)) + *x = int32(u) + *v = x + return nil +} + +// Decode an int64. +func (o *Buffer) dec_int64(p *Properties, base uintptr, sbase uintptr) os.Error { + u, err := p.valDec(o) + if err != nil { + return err + } + v := (**int64)(unsafe.Pointer(base + p.offset)) + x := (*int64)(unsafe.Pointer(sbase + p.scratch)) + *x = int64(u) + *v = x + return nil +} + +// Decode a string. +func (o *Buffer) dec_string(p *Properties, base uintptr, sbase uintptr) os.Error { + s, err := o.DecodeStringBytes() + if err != nil { + return err + } + v := (**string)(unsafe.Pointer(base + p.offset)) + x := (*string)(unsafe.Pointer(sbase + p.scratch)) + *x = s + *v = x + return nil +} + +// Decode a slice of bytes ([]byte). +func (o *Buffer) dec_slice_byte(p *Properties, base uintptr, sbase uintptr) os.Error { + b, err := o.DecodeRawBytes(false) + if err != nil { + return err + } + + x := (*[]uint8)(unsafe.Pointer(base + p.offset)) + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + *x = append(y, b...) + return nil +} + +// Decode a slice of bools ([]bool). +func (o *Buffer) dec_slice_bool(p *Properties, base uintptr, sbase uintptr) os.Error { + u, err := p.valDec(o) + if err != nil { + return err + } + x := (*[]bool)(unsafe.Pointer(base + p.offset)) + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + *x = append(y, u != 0) + return nil +} + +// Decode a slice of bools ([]bool) in packed format. +func (o *Buffer) dec_slice_packed_bool(p *Properties, base uintptr, sbase uintptr) os.Error { + x := (*[]bool)(unsafe.Pointer(base + p.offset)) + + nn, err := o.DecodeVarint() + if err != nil { + return err + } + nb := int(nn) // number of bytes of encoded bools + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + for i := 0; i < nb; i++ { + u, err := p.valDec(o) + if err != nil { + return err + } + y = append(y, u != 0) + } + + *x = y + return nil +} + +// Decode a slice of int32s ([]int32). +func (o *Buffer) dec_slice_int32(p *Properties, base uintptr, sbase uintptr) os.Error { + u, err := p.valDec(o) + if err != nil { + return err + } + x := (*[]int32)(unsafe.Pointer(base + p.offset)) + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + *x = append(y, int32(u)) + return nil +} + +// Decode a slice of int32s ([]int32) in packed format. +func (o *Buffer) dec_slice_packed_int32(p *Properties, base uintptr, sbase uintptr) os.Error { + x := (*[]int32)(unsafe.Pointer(base + p.offset)) + + nn, err := o.DecodeVarint() + if err != nil { + return err + } + nb := int(nn) // number of bytes of encoded int32s + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + fin := o.index + nb + for o.index < fin { + u, err := p.valDec(o) + if err != nil { + return err + } + y = append(y, int32(u)) + } + + *x = y + return nil +} + +// Decode a slice of int64s ([]int64). +func (o *Buffer) dec_slice_int64(p *Properties, base uintptr, sbase uintptr) os.Error { + u, err := p.valDec(o) + if err != nil { + return err + } + x := (*[]int64)(unsafe.Pointer(base + p.offset)) + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + *x = append(y, int64(u)) + return nil +} + +// Decode a slice of int64s ([]int64) in packed format. +func (o *Buffer) dec_slice_packed_int64(p *Properties, base uintptr, sbase uintptr) os.Error { + x := (*[]int64)(unsafe.Pointer(base + p.offset)) + + nn, err := o.DecodeVarint() + if err != nil { + return err + } + nb := int(nn) // number of bytes of encoded int64s + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + fin := o.index + nb + for o.index < fin { + u, err := p.valDec(o) + if err != nil { + return err + } + y = append(y, int64(u)) + } + + *x = y + return nil +} + +// Decode a slice of strings ([]string). +func (o *Buffer) dec_slice_string(p *Properties, base uintptr, sbase uintptr) os.Error { + s, err := o.DecodeStringBytes() + if err != nil { + return err + } + x := (*[]string)(unsafe.Pointer(base + p.offset)) + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + *x = append(y, s) + return nil +} + +// Decode a slice of slice of bytes ([][]byte). +func (o *Buffer) dec_slice_slice_byte(p *Properties, base uintptr, sbase uintptr) os.Error { + b, err := o.DecodeRawBytes(true) + if err != nil { + return err + } + x := (*[][]byte)(unsafe.Pointer(base + p.offset)) + + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + *x = append(y, b) + return nil +} + +// Decode a group. +func (o *Buffer) dec_struct_group(p *Properties, base uintptr, sbase uintptr) os.Error { + ptr := (**struct{})(unsafe.Pointer(base + p.offset)) + typ := p.stype.Elem() + structv := unsafe.New(typ) + bas := uintptr(structv) + *ptr = (*struct{})(structv) + + err := o.unmarshalType(p.stype, true, bas) + + return err +} + +// Decode an embedded message. +func (o *Buffer) dec_struct_message(p *Properties, base uintptr, sbase uintptr) (err os.Error) { + raw, e := o.DecodeRawBytes(false) + if e != nil { + return e + } + + ptr := (**struct{})(unsafe.Pointer(base + p.offset)) + typ := p.stype.Elem() + structv := unsafe.New(typ) + bas := uintptr(structv) + *ptr = (*struct{})(structv) + + // If the object can unmarshal itself, let it. + iv := unsafe.Unreflect(p.stype, unsafe.Pointer(ptr)) + if u, ok := iv.(Unmarshaler); ok { + return u.Unmarshal(raw) + } + + obuf := o.buf + oi := o.index + o.buf = raw + o.index = 0 + + err = o.unmarshalType(p.stype, false, bas) + o.buf = obuf + o.index = oi + + return err +} + +// Decode a slice of embedded messages. +func (o *Buffer) dec_slice_struct_message(p *Properties, base uintptr, sbase uintptr) os.Error { + return o.dec_slice_struct(p, false, base, sbase) +} + +// Decode a slice of embedded groups. +func (o *Buffer) dec_slice_struct_group(p *Properties, base uintptr, sbase uintptr) os.Error { + return o.dec_slice_struct(p, true, base, sbase) +} + +// Decode a slice of structs ([]*struct). +func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base uintptr, sbase uintptr) os.Error { + + x := (*[]*struct{})(unsafe.Pointer(base + p.offset)) + y := *x + if cap(y) == 0 { + initSlice(unsafe.Pointer(x), sbase+p.scratch) + y = *x + } + + typ := p.stype.Elem() + structv := unsafe.New(typ) + bas := uintptr(structv) + y = append(y, (*struct{})(structv)) + *x = y + + if is_group { + err := o.unmarshalType(p.stype, is_group, bas) + return err + } + + raw, err := o.DecodeRawBytes(true) + if err != nil { + return err + } + + // If the object can unmarshal itself, let it. + iv := unsafe.Unreflect(p.stype, unsafe.Pointer(&y[len(y)-1])) + if u, ok := iv.(Unmarshaler); ok { + return u.Unmarshal(raw) + } + + obuf := o.buf + oi := o.index + o.buf = raw + o.index = 0 + + err = o.unmarshalType(p.stype, is_group, bas) + + o.buf = obuf + o.index = oi + + return err +} diff --git a/proto/encode.go b/proto/encode.go new file mode 100644 index 0000000..7060520 --- /dev/null +++ b/proto/encode.go @@ -0,0 +1,598 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Routines for encoding data into the wire format for protocol buffers. + */ + +import ( + "os" + "reflect" + "runtime" + "unsafe" +) + +// ErrRequiredNotSet is the error returned if Marshal is called with +// a protocol buffer struct whose required fields have not +// all been initialized. It is also the error returned if Unmarshal is +// called with an encoded protocol buffer that does not include all the +// required fields. +type ErrRequiredNotSet struct { + t reflect.Type +} + +func (e *ErrRequiredNotSet) String() string { + return "proto: required fields not set in " + e.t.String() +} + +var ( + // ErrRepeatedHasNil is the error returned if Marshal is called with + // a struct with a repeated field containing a nil element. + ErrRepeatedHasNil = os.NewError("proto: repeated field has nil") + + // ErrNil is the error returned if Marshal is called with nil. + ErrNil = os.NewError("proto: Marshal called with nil") + + // ErrNotPtr is the error returned if Marshal is called with a non-pointer. + ErrNotPtr = os.NewError("proto: Marshal called with a non-pointer") +) + +// The fundamental encoders that put bytes on the wire. +// Those that take integer types all accept uint64 and are +// therefore of type valueEncoder. + +const maxVarintBytes = 10 // maximum length of a varint + +// EncodeVarint returns the varint encoding of x. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +// Not used by the package itself, but helpful to clients +// wishing to use the same encoding. +func EncodeVarint(x uint64) []byte { + var buf [maxVarintBytes]byte + var n int + for n = 0; x > 127; n++ { + buf[n] = 0x80 | uint8(x&0x7F) + x >>= 7 + } + buf[n] = uint8(x) + n++ + return buf[0:n] +} + +// EncodeVarint writes a varint-encoded integer to the Buffer. +// This is the format for the +// int32, int64, uint32, uint64, bool, and enum +// protocol buffer types. +func (p *Buffer) EncodeVarint(x uint64) os.Error { + for x >= 1<<7 { + p.buf = append(p.buf, uint8(x&0x7f|0x80)) + x >>= 7 + } + p.buf = append(p.buf, uint8(x)) + return nil +} + +// EncodeFixed64 writes a 64-bit integer to the Buffer. +// This is the format for the +// fixed64, sfixed64, and double protocol buffer types. +func (p *Buffer) EncodeFixed64(x uint64) os.Error { + p.buf = append(p.buf, + uint8(x), + uint8(x>>8), + uint8(x>>16), + uint8(x>>24), + uint8(x>>32), + uint8(x>>40), + uint8(x>>48), + uint8(x>>56)) + return nil +} + +// EncodeFixed32 writes a 32-bit integer to the Buffer. +// This is the format for the +// fixed32, sfixed32, and float protocol buffer types. +func (p *Buffer) EncodeFixed32(x uint64) os.Error { + p.buf = append(p.buf, + uint8(x), + uint8(x>>8), + uint8(x>>16), + uint8(x>>24)) + return nil +} + +// EncodeZigzag64 writes a zigzag-encoded 64-bit integer +// to the Buffer. +// This is the format used for the sint64 protocol buffer type. +func (p *Buffer) EncodeZigzag64(x uint64) os.Error { + // use signed number to get arithmetic right shift. + return p.EncodeVarint(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} + +// EncodeZigzag32 writes a zigzag-encoded 32-bit integer +// to the Buffer. +// This is the format used for the sint32 protocol buffer type. +func (p *Buffer) EncodeZigzag32(x uint64) os.Error { + // use signed number to get arithmetic right shift. + return p.EncodeVarint(uint64((uint32(x) << 1) ^ uint32((int32(x) >> 31)))) +} + +// EncodeRawBytes writes a count-delimited byte buffer to the Buffer. +// This is the format used for the bytes protocol buffer +// type and for embedded messages. +func (p *Buffer) EncodeRawBytes(b []byte) os.Error { + lb := len(b) + p.EncodeVarint(uint64(lb)) + p.buf = append(p.buf, b...) + return nil +} + +// EncodeStringBytes writes an encoded string to the Buffer. +// This is the format used for the proto2 string type. +func (p *Buffer) EncodeStringBytes(s string) os.Error { + + // this works because strings and slices are the same. + y := *(*[]byte)(unsafe.Pointer(&s)) + p.EncodeRawBytes(y) + return nil +} + +// Marshaler is the interface representing objects that can marshal themselves. +type Marshaler interface { + Marshal() ([]byte, os.Error) +} + +// Marshal takes the protocol buffer struct represented by pb +// and encodes it into the wire format, returning the data. +func Marshal(pb interface{}) ([]byte, os.Error) { + // Can the object marshal itself? + if m, ok := pb.(Marshaler); ok { + return m.Marshal() + } + p := NewBuffer(nil) + err := p.Marshal(pb) + if err != nil { + return nil, err + } + return p.buf, err +} + +// Marshal takes the protocol buffer struct represented by pb +// and encodes it into the wire format, writing the result to the +// Buffer. +func (p *Buffer) Marshal(pb interface{}) os.Error { + // Can the object marshal itself? + if m, ok := pb.(Marshaler); ok { + data, err := m.Marshal() + if err != nil { + return err + } + p.buf = append(p.buf, data...) + return nil + } + + mstat := runtime.MemStats.Mallocs + + t, b, err := getbase(pb) + if t.Kind() != reflect.Ptr { + return ErrNotPtr + } + if err == nil { + err = p.enc_struct(t.Elem(), b) + } + + mstat = runtime.MemStats.Mallocs - mstat + stats.Emalloc += mstat + stats.Encode++ + + return err +} + +// Individual type encoders. + +// Encode a bool. +func (o *Buffer) enc_bool(p *Properties, base uintptr) os.Error { + v := *(**uint8)(unsafe.Pointer(base + p.offset)) + if v == nil { + return ErrNil + } + x := *v + if x != 0 { + x = 1 + } + o.buf = append(o.buf, p.tagcode...) + p.valEnc(o, uint64(x)) + return nil +} + +// Encode an int32. +func (o *Buffer) enc_int32(p *Properties, base uintptr) os.Error { + v := *(**uint32)(unsafe.Pointer(base + p.offset)) + if v == nil { + return ErrNil + } + x := *v + o.buf = append(o.buf, p.tagcode...) + p.valEnc(o, uint64(x)) + return nil +} + +// Encode an int64. +func (o *Buffer) enc_int64(p *Properties, base uintptr) os.Error { + v := *(**uint64)(unsafe.Pointer(base + p.offset)) + if v == nil { + return ErrNil + } + x := *v + o.buf = append(o.buf, p.tagcode...) + p.valEnc(o, uint64(x)) + return nil +} + +// Encode a string. +func (o *Buffer) enc_string(p *Properties, base uintptr) os.Error { + v := *(**string)(unsafe.Pointer(base + p.offset)) + if v == nil { + return ErrNil + } + x := *v + o.buf = append(o.buf, p.tagcode...) + o.EncodeStringBytes(x) + return nil +} + +// All protocol buffer fields are nillable, but be careful. +func isNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Map, reflect.Ptr, reflect.Slice: + return v.IsNil() + } + return false +} + +// Encode a message struct. +func (o *Buffer) enc_struct_message(p *Properties, base uintptr) os.Error { + // Can the object marshal itself? + iv := unsafe.Unreflect(p.stype, unsafe.Pointer(base+p.offset)) + if m, ok := iv.(Marshaler); ok { + if isNil(reflect.ValueOf(iv)) { + return ErrNil + } + data, err := m.Marshal() + if err != nil { + return err + } + o.buf = append(o.buf, p.tagcode...) + o.EncodeRawBytes(data) + return nil + } + v := *(**struct{})(unsafe.Pointer(base + p.offset)) + if v == nil { + return ErrNil + } + + // need the length before we can write out the message itself, + // so marshal into a separate byte buffer first. + obuf := o.buf + o.buf = o.bufalloc() + + b := uintptr(unsafe.Pointer(v)) + typ := p.stype.Elem() + err := o.enc_struct(typ, b) + + nbuf := o.buf + o.buf = obuf + if err != nil { + o.buffree(nbuf) + return err + } + o.buf = append(o.buf, p.tagcode...) + o.EncodeRawBytes(nbuf) + o.buffree(nbuf) + return nil +} + +// Encode a group struct. +func (o *Buffer) enc_struct_group(p *Properties, base uintptr) os.Error { + v := *(**struct{})(unsafe.Pointer(base + p.offset)) + if v == nil { + return ErrNil + } + + o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup)) + b := uintptr(unsafe.Pointer(v)) + typ := p.stype.Elem() + err := o.enc_struct(typ, b) + if err != nil { + return err + } + o.EncodeVarint(uint64((p.Tag << 3) | WireEndGroup)) + return nil +} + +// Encode a slice of bools ([]bool). +func (o *Buffer) enc_slice_bool(p *Properties, base uintptr) os.Error { + s := *(*[]uint8)(unsafe.Pointer(base + p.offset)) + l := len(s) + if l == 0 { + return ErrNil + } + for _, x := range s { + o.buf = append(o.buf, p.tagcode...) + if x != 0 { + x = 1 + } + p.valEnc(o, uint64(x)) + } + return nil +} + +// Encode a slice of bools ([]bool) in packed format. +func (o *Buffer) enc_slice_packed_bool(p *Properties, base uintptr) os.Error { + s := *(*[]uint8)(unsafe.Pointer(base + p.offset)) + l := len(s) + if l == 0 { + return ErrNil + } + o.buf = append(o.buf, p.tagcode...) + o.EncodeVarint(uint64(l)) // each bool takes exactly one byte + for _, x := range s { + if x != 0 { + x = 1 + } + p.valEnc(o, uint64(x)) + } + return nil +} + +// Encode a slice of bytes ([]byte). +func (o *Buffer) enc_slice_byte(p *Properties, base uintptr) os.Error { + s := *(*[]uint8)(unsafe.Pointer(base + p.offset)) + if s == nil { + return ErrNil + } + o.buf = append(o.buf, p.tagcode...) + o.EncodeRawBytes(s) + return nil +} + +// Encode a slice of int32s ([]int32). +func (o *Buffer) enc_slice_int32(p *Properties, base uintptr) os.Error { + s := *(*[]uint32)(unsafe.Pointer(base + p.offset)) + l := len(s) + if l == 0 { + return ErrNil + } + for i := 0; i < l; i++ { + o.buf = append(o.buf, p.tagcode...) + x := s[i] + p.valEnc(o, uint64(x)) + } + return nil +} + +// Encode a slice of int32s ([]int32) in packed format. +func (o *Buffer) enc_slice_packed_int32(p *Properties, base uintptr) os.Error { + s := *(*[]uint32)(unsafe.Pointer(base + p.offset)) + l := len(s) + if l == 0 { + return ErrNil + } + // TODO: Reuse a Buffer. + buf := NewBuffer(nil) + for i := 0; i < l; i++ { + p.valEnc(buf, uint64(s[i])) + } + + o.buf = append(o.buf, p.tagcode...) + o.EncodeVarint(uint64(len(buf.buf))) + o.buf = append(o.buf, buf.buf...) + return nil +} + +// Encode a slice of int64s ([]int64). +func (o *Buffer) enc_slice_int64(p *Properties, base uintptr) os.Error { + s := *(*[]uint64)(unsafe.Pointer(base + p.offset)) + l := len(s) + if l == 0 { + return ErrNil + } + for i := 0; i < l; i++ { + o.buf = append(o.buf, p.tagcode...) + x := s[i] + p.valEnc(o, uint64(x)) + } + return nil +} + +// Encode a slice of int64s ([]int64) in packed format. +func (o *Buffer) enc_slice_packed_int64(p *Properties, base uintptr) os.Error { + s := *(*[]uint64)(unsafe.Pointer(base + p.offset)) + l := len(s) + if l == 0 { + return ErrNil + } + // TODO: Reuse a Buffer. + buf := NewBuffer(nil) + for i := 0; i < l; i++ { + p.valEnc(buf, s[i]) + } + + o.buf = append(o.buf, p.tagcode...) + o.EncodeVarint(uint64(len(buf.buf))) + o.buf = append(o.buf, buf.buf...) + return nil +} + +// Encode a slice of slice of bytes ([][]byte). +func (o *Buffer) enc_slice_slice_byte(p *Properties, base uintptr) os.Error { + ss := *(*[][]uint8)(unsafe.Pointer(base + p.offset)) + l := len(ss) + if l == 0 { + return ErrNil + } + for i := 0; i < l; i++ { + o.buf = append(o.buf, p.tagcode...) + s := ss[i] + o.EncodeRawBytes(s) + } + return nil +} + +// Encode a slice of strings ([]string). +func (o *Buffer) enc_slice_string(p *Properties, base uintptr) os.Error { + ss := *(*[]string)(unsafe.Pointer(base + p.offset)) + l := len(ss) + for i := 0; i < l; i++ { + o.buf = append(o.buf, p.tagcode...) + s := ss[i] + o.EncodeStringBytes(s) + } + return nil +} + +// Encode a slice of message structs ([]*struct). +func (o *Buffer) enc_slice_struct_message(p *Properties, base uintptr) os.Error { + s := *(*[]*struct{})(unsafe.Pointer(base + p.offset)) + l := len(s) + typ := p.stype.Elem() + + for i := 0; i < l; i++ { + v := s[i] + if v == nil { + return ErrRepeatedHasNil + } + + // Can the object marshal itself? + iv := unsafe.Unreflect(p.stype, unsafe.Pointer(&s[i])) + if m, ok := iv.(Marshaler); ok { + if isNil(reflect.ValueOf(iv)) { + return ErrNil + } + data, err := m.Marshal() + if err != nil { + return err + } + o.buf = append(o.buf, p.tagcode...) + o.EncodeRawBytes(data) + continue + } + + obuf := o.buf + o.buf = o.bufalloc() + + b := uintptr(unsafe.Pointer(v)) + err := o.enc_struct(typ, b) + + nbuf := o.buf + o.buf = obuf + if err != nil { + o.buffree(nbuf) + if err == ErrNil { + return ErrRepeatedHasNil + } + return err + } + o.buf = append(o.buf, p.tagcode...) + o.EncodeRawBytes(nbuf) + + o.buffree(nbuf) + } + return nil +} + +// Encode a slice of group structs ([]*struct). +func (o *Buffer) enc_slice_struct_group(p *Properties, base uintptr) os.Error { + s := *(*[]*struct{})(unsafe.Pointer(base + p.offset)) + l := len(s) + typ := p.stype.Elem() + + for i := 0; i < l; i++ { + v := s[i] + if v == nil { + return ErrRepeatedHasNil + } + + o.EncodeVarint(uint64((p.Tag << 3) | WireStartGroup)) + + b := uintptr(unsafe.Pointer(v)) + err := o.enc_struct(typ, b) + + if err != nil { + if err == ErrNil { + return ErrRepeatedHasNil + } + return err + } + + o.EncodeVarint(uint64((p.Tag << 3) | WireEndGroup)) + } + return nil +} + +// Encode an extension map. +func (o *Buffer) enc_map(p *Properties, base uintptr) os.Error { + v := *(*map[int32]Extension)(unsafe.Pointer(base + p.offset)) + if err := encodeExtensionMap(v); err != nil { + return err + } + for _, e := range v { + o.buf = append(o.buf, e.enc...) + } + return nil +} + +// Encode a struct. +func (o *Buffer) enc_struct(t reflect.Type, base uintptr) os.Error { + prop := GetProperties(t) + required := prop.reqCount + for _, p := range prop.Prop { + if p.enc != nil { + err := p.enc(o, p, base) + if err != nil { + if err != ErrNil { + return err + } + } else if p.Required { + required-- + } + } + } + // See if we encoded all required fields. + if required > 0 { + return &ErrRequiredNotSet{t} + } + + return nil +} diff --git a/proto/equal.go b/proto/equal.go new file mode 100644 index 0000000..405d99a --- /dev/null +++ b/proto/equal.go @@ -0,0 +1,211 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Protocol buffer comparison. +// TODO: MessageSet and RawMessage. + +package proto + +import ( + "bytes" + "log" + "os" + "reflect" + "strings" +) + +/* +Equal returns true iff protocol buffers a and b are equal. +The arguments must both be protocol buffer structs, +or both be pointers to protocol buffer structs. + +Equality is defined in this way: + - Two messages are equal iff they are the same type, + corresponding fields are equal, unknown field sets + are equal, and extensions sets are equal. + - Two set scalar fields are equal iff their values are equal. + If the fields are of a floating-point type, remember that + NaN != x for all x, including NaN. + - Two repeated fields are equal iff their lengths are the same, + and their corresponding elements are equal. + - Two unset fields are equal. + - Two unknown field sets are equal if their current + encoded state is equal. (TODO) + - Two extension sets are equal iff they have corresponding + elements that are pairwise equal. + - Every other combination of things are not equal. + +The return value is undefined if a and b are not protocol buffers. +*/ +func Equal(a, b interface{}) bool { + v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b) + if v1.Type() != v2.Type() { + return false + } + if v1.Kind() == reflect.Ptr { + v1, v2 = v1.Elem(), v2.Elem() + } + if v1.Kind() != reflect.Struct { + return false + } + return equalStruct(v1, v2) +} + +// v1 and v2 are known to have the same type. +func equalStruct(v1, v2 reflect.Value) bool { + for i := 0; i < v1.NumField(); i++ { + f := v1.Type().Field(i) + if strings.HasPrefix(f.Name, "XXX_") { + continue + } + f1, f2 := v1.Field(i), v2.Field(i) + if f.Type.Kind() == reflect.Ptr { + if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 { + // both unset + continue + } else if n1 != n2 { + // set/unset mismatch + return false + } + f1, f2 = f1.Elem(), f2.Elem() + } + if !equalAny(f1, f2) { + return false + } + } + + if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() { + em2 := v2.FieldByName("XXX_extensions") + if !equalExtensions(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) { + return false + } + } + + // TODO: Deal with XXX_unrecognized. + + return true +} + +// v1 and v2 are known to have the same type. +func equalAny(v1, v2 reflect.Value) bool { + switch v1.Kind() { + case reflect.Bool: + return v1.Bool() == v2.Bool() + case reflect.Float32, reflect.Float64: + return v1.Float() == v2.Float() + case reflect.Int32, reflect.Int64: + return v1.Int() == v2.Int() + case reflect.Ptr: + return equalAny(v1.Elem(), v2.Elem()) + case reflect.Slice: + if n1, n2 := v1.IsNil(), v2.IsNil(); n1 && n2 { + return true + } else if n1 != n2 { + return false + } + if v1.Len() != v2.Len() { + return false + } + // short circuit: []byte + if v1.Type().Elem().Kind() == reflect.Uint8 { + return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte)) + } + for i := 0; i < v1.Len(); i++ { + if !equalAny(v1.Index(i), v2.Index(i)) { + return false + } + } + return true + case reflect.String: + return v1.Interface().(string) == v2.Interface().(string) + case reflect.Struct: + return equalStruct(v1, v2) + case reflect.Uint32, reflect.Uint64: + return v1.Uint() == v2.Uint() + } + + // unknown type, so not a protocol buffer + log.Printf("proto: don't know how to compare %v", v1) + return false +} + +// base is the struct type that the extensions are based on. +// em1 and em2 are extension maps. +func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool { + if len(em1) != len(em2) { + return false + } + + for extNum, e1 := range em1 { + e2, ok := em2[extNum] + if !ok { + return false + } + + m1, m2 := e1.value, e2.value + + if m1 != nil && m2 != nil { + // Both are unencoded. + if !Equal(m1, m2) { + return false + } + continue + } + + // At least one is encoded. To do a semantically correct comparison + // we need to unmarshal them first. + var desc *ExtensionDesc + if m := extensionMaps[base]; m != nil { + desc = m[extNum] + } + if desc == nil { + log.Printf("proto: don't know how to compare extension %d of %v", extNum, base) + continue + } + var err os.Error + if m1 == nil { + m1, err = decodeExtension(e1.enc, desc) + } + if m2 == nil && err == nil { + m2, err = decodeExtension(e2.enc, desc) + } + if err != nil { + // The encoded form is invalid. + log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err) + return false + } + if !Equal(m1, m2) { + return false + } + } + + return true +} diff --git a/proto/equal_test.go b/proto/equal_test.go new file mode 100644 index 0000000..fd347af --- /dev/null +++ b/proto/equal_test.go @@ -0,0 +1,128 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2011 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto_test + +import ( + "log" + "testing" + + . "goprotobuf.googlecode.com/hg/proto" + pb "./testdata/_obj/test_proto" +) + +// Four identical base messages. +// The init function adds extensions to some of them. +var messageWithoutExtension = &pb.MyMessage{Count: Int32(7)} +var messageWithExtension1a = &pb.MyMessage{Count: Int32(7)} +var messageWithExtension1b = &pb.MyMessage{Count: Int32(7)} +var messageWithExtension2 = &pb.MyMessage{Count: Int32(7)} + +func init() { + ext1 := &pb.Ext{Data: String("Kirk")} + ext2 := &pb.Ext{Data: String("Picard")} + + // messageWithExtension1a has ext1, but never marshals it. + if err := SetExtension(messageWithExtension1a, pb.E_Ext_More, ext1); err != nil { + log.Panicf("SetExtension on 1a failed: %v", err) + } + + // messageWithExtension1b is the unmarshaled form of messageWithExtension1a. + if err := SetExtension(messageWithExtension1b, pb.E_Ext_More, ext1); err != nil { + log.Panicf("SetExtension on 1b failed: %v", err) + } + buf, err := Marshal(messageWithExtension1b) + if err != nil { + log.Panicf("Marshal of 1b failed: %v", err) + } + messageWithExtension1b.Reset() + if err := Unmarshal(buf, messageWithExtension1b); err != nil { + log.Panicf("Unmarshal of 1b failed: %v", err) + } + + // messageWithExtension2 has ext2. + if err := SetExtension(messageWithExtension2, pb.E_Ext_More, ext2); err != nil { + log.Panicf("SetExtension on 2 failed: %v", err) + } +} + +var EqualTests = []struct { + desc string + a, b interface{} + exp bool +}{ + {"different types", &pb.GoEnum{}, &pb.GoTestField{}, false}, + {"one pointer, one value", &pb.GoEnum{}, pb.GoEnum{}, false}, + {"non-protocol buffers", 7, 7, false}, + {"equal empty", &pb.GoEnum{}, &pb.GoEnum{}, true}, + + {"one set field, one unset field", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{}, false}, + {"one set field zero, one unset field", &pb.GoTest{Param: Int32(0)}, &pb.GoTest{}, false}, + {"different set fields", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("bar")}, false}, + {"equal set", &pb.GoTestField{Label: String("foo")}, &pb.GoTestField{Label: String("foo")}, true}, + + {"repeated, one set", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{}, false}, + {"repeated, different length", &pb.GoTest{F_Int32Repeated: []int32{2, 3}}, &pb.GoTest{F_Int32Repeated: []int32{2}}, false}, + {"repeated, different value", &pb.GoTest{F_Int32Repeated: []int32{2}}, &pb.GoTest{F_Int32Repeated: []int32{3}}, false}, + {"repeated, equal", &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, &pb.GoTest{F_Int32Repeated: []int32{2, 4}}, true}, + + { + "nested, different", + &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("foo")}}, + &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("bar")}}, + false, + }, + { + "nested, equal", + &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}}, + &pb.GoTest{RequiredField: &pb.GoTestField{Label: String("wow")}}, + true, + }, + + { + "repeated bytes", + &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}}, + &pb.MyMessage{RepBytes: [][]byte{[]byte("sham"), []byte("wow")}}, + true, + }, + + {"extension vs. no extension", messageWithoutExtension, messageWithExtension1a, false}, + {"extension vs. same extension", messageWithExtension1a, messageWithExtension1b, true}, + {"extension vs. different extension", messageWithExtension1a, messageWithExtension2, false}, +} + +func TestEqual(t *testing.T) { + for _, tc := range EqualTests { + if res := Equal(tc.a, tc.b); res != tc.exp { + t.Errorf("%v: Equal(%v, %v) = %v, want %v", tc.desc, tc.a, tc.b, res, tc.exp) + } + } +} diff --git a/proto/extensions.go b/proto/extensions.go new file mode 100644 index 0000000..d329b90 --- /dev/null +++ b/proto/extensions.go @@ -0,0 +1,276 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + + +/* + * Types and routines for supporting protocol buffer extensions. + */ + +import ( + "os" + "reflect" + "strconv" + "unsafe" +) + +// ExtensionRange represents a range of message extensions for a protocol buffer. +// Used in code generated by the protocol compiler. +type ExtensionRange struct { + Start, End int32 // both inclusive +} + +// extendableProto is an interface implemented by any protocol buffer that may be extended. +type extendableProto interface { + ExtensionRangeArray() []ExtensionRange + ExtensionMap() map[int32]Extension +} + +// ExtensionDesc represents an extension specification. +// Used in generated code from the protocol compiler. +type ExtensionDesc struct { + ExtendedType interface{} // nil pointer to the type that is being extended + ExtensionType interface{} // nil pointer to the extension type + Field int32 // field number + Name string // fully-qualified name of extension + Tag string // protobuf tag style +} + +/* +Extension represents an extension in a message. + +When an extension is stored in a message using SetExtension +only desc and value are set. When the message is marshaled +enc will be set to the encoded form of the message. + +When a message is unmarshaled and contains extensions, each +extension will have only enc set. When such an extension is +accessed using GetExtension (or GetExtensions) desc and value +will be set. +*/ +type Extension struct { + desc *ExtensionDesc + value interface{} + enc []byte +} + +// SetRawExtension is for testing only. +func SetRawExtension(base extendableProto, id int32, b []byte) { + base.ExtensionMap()[id] = Extension{enc: b} +} + +// isExtensionField returns true iff the given field number is in an extension range. +func isExtensionField(pb extendableProto, field int32) bool { + for _, er := range pb.ExtensionRangeArray() { + if er.Start <= field && field <= er.End { + return true + } + } + return false +} + +// checkExtensionTypes checks that the given extension is valid for pb. +func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) os.Error { + // Check the extended type. + if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b { + return os.NewError("bad extended type; " + b.String() + " does not extend " + a.String()) + } + // Check the range. + if !isExtensionField(pb, extension.Field) { + return os.NewError("bad extension number; not in declared ranges") + } + return nil +} + +// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m. +func encodeExtensionMap(m map[int32]Extension) os.Error { + for k, e := range m { + if e.value == nil || e.desc == nil { + // Extension is only in its encoded form. + continue + } + + // We don't skip extensions that have an encoded form set, + // because the extension value may have been mutated after + // the last time this function was called. + + et := reflect.TypeOf(e.desc.ExtensionType) + props := new(Properties) + props.Init(et, "unknown_name", e.desc.Tag, 0) + + p := NewBuffer(nil) + // The encoder must be passed a pointer to e.value. + // Allocate a copy of value so that we can use its address. + x := reflect.New(et) + x.Elem().Set(reflect.ValueOf(e.value)) + if err := props.enc(p, props, x.Pointer()); err != nil { + return err + } + e.enc = p.buf + m[k] = e + } + return nil +} + +// HasExtension returns whether the given extension is present in pb. +func HasExtension(pb extendableProto, extension *ExtensionDesc) bool { + // TODO: Check types, field numbers, etc.? + _, ok := pb.ExtensionMap()[extension.Field] + return ok +} + +// ClearExtension removes the given extension from pb. +func ClearExtension(pb extendableProto, extension *ExtensionDesc) { + // TODO: Check types, field numbers, etc.? + pb.ExtensionMap()[extension.Field] = Extension{}, false +} + +// GetExtension parses and returns the given extension of pb. +// If the extension is not present it returns (nil, nil). +func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, os.Error) { + if err := checkExtensionTypes(pb, extension); err != nil { + return nil, err + } + + e, ok := pb.ExtensionMap()[extension.Field] + if !ok { + return nil, nil // not an error + } + if e.value != nil { + // Already decoded. Check the descriptor, though. + if e.desc != extension { + // This shouldn't happen. If it does, it means that + // GetExtension was called twice with two different + // descriptors with the same field number. + return nil, os.NewError("proto: descriptor conflict") + } + return e.value, nil + } + + v, err := decodeExtension(e.enc, extension) + if err != nil { + return nil, err + } + + // Remember the decoded version and drop the encoded version. + // That way it is safe to mutate what we return. + e.value = v + e.desc = extension + e.enc = nil + return e.value, nil +} + +// decodeExtension decodes an extension encoded in b. +func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, os.Error) { + // Discard wire type and field number varint. It isn't needed. + _, n := DecodeVarint(b) + o := NewBuffer(b[n:]) + + t := reflect.TypeOf(extension.ExtensionType) + props := &Properties{} + props.Init(t, "irrelevant_name", extension.Tag, 0) + + base := unsafe.New(t) + var sbase uintptr + if t.Elem().Kind() == reflect.Struct { + // props.dec will be dec_struct_message, which does not refer to sbase. + *(*unsafe.Pointer)(base) = unsafe.New(t.Elem()) + } else { + sbase = uintptr(unsafe.New(t.Elem())) + } + if err := props.dec(o, props, uintptr(base), sbase); err != nil { + return nil, err + } + return unsafe.Unreflect(t, base), nil +} + +// GetExtensions returns a slice of the extensions present in pb that are also listed in es. +// The returned slice has the same length as es; missing extensions will appear as nil elements. +func GetExtensions(pb interface{}, es []*ExtensionDesc) (extensions []interface{}, err os.Error) { + epb, ok := pb.(extendableProto) + if !ok { + err = os.NewError("not an extendable proto") + return + } + extensions = make([]interface{}, len(es)) + for i, e := range es { + extensions[i], err = GetExtension(epb, e) + if err != nil { + return + } + } + return +} + +// TODO: (needed for repeated extensions) +// - ExtensionSize +// - AddExtension + +// SetExtension sets the specified extension of pb to the specified value. +func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) os.Error { + if err := checkExtensionTypes(pb, extension); err != nil { + return err + } + typ := reflect.TypeOf(extension.ExtensionType) + if typ != reflect.TypeOf(value) { + return os.NewError("bad extension value type") + } + + pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value} + return nil +} + +// A global registry of extensions. +// The generated code will register the generated descriptors by calling RegisterExtension. + +var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc) + +// RegisterExtension is called from the generated code. +func RegisterExtension(desc *ExtensionDesc) { + st := reflect.TypeOf(desc.ExtendedType).Elem() + m := extensionMaps[st] + if m == nil { + m = make(map[int32]*ExtensionDesc) + extensionMaps[st] = m + } + if _, ok := m[desc.Field]; ok { + panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field))) + } + m[desc.Field] = desc +} + +// RegisteredExtensions returns a map of the registered extensions of a +// protocol buffer struct, indexed by the extension number. +// The argument pb should be a nil pointer to the struct type. +func RegisteredExtensions(pb interface{}) map[int32]*ExtensionDesc { + return extensionMaps[reflect.TypeOf(pb).Elem()] +} diff --git a/proto/lib.go b/proto/lib.go new file mode 100644 index 0000000..b816e91 --- /dev/null +++ b/proto/lib.go @@ -0,0 +1,770 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// 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. + +/* + The proto package converts data structures to and from the + wire format of protocol buffers. It works in concert with the + Go source code generated for .proto files by the protocol compiler. + + A summary of the properties of the protocol buffer interface + for a protocol buffer variable v: + + - Names are turned from camel_case to CamelCase for export. + - There are no methods on v to set and get fields; just treat + them as structure fields. + - The zero value for a struct is its correct initialization state. + All desired fields must be set before marshaling. + - A Reset() method will restore a protobuf struct to its zero state. + - Non-repeated fields are pointers to the values; nil means unset. + That is, optional or required field int32 f becomes F *int32. + - Repeated fields are slices. + - Helper functions are available to simplify the getting and setting of fields: + foo.String = proto.String("hello") // set field + s := proto.GetString(foo.String) // get field + - Constants are defined to hold the default values of all fields that + have them. They have the form Default_StructName_FieldName. + - Enums are given type names and maps between names to values, + plus a helper function to create values. Enum values are prefixed + with the enum's type name. Enum types have a String method. + - Nested groups and enums have type names prefixed with the name of + the surrounding message type. + - Extensions are given descriptor names that start with E_, + followed by an underscore-delimited list of the nested messages + that contain it (if any) followed by the CamelCased name of the + extension field itself. HasExtension, ClearExtension, GetExtension + and SetExtension are functions for manipulating extensions. + - Marshal and Unmarshal are functions to encode and decode the wire format. + + The simplest way to describe this is to see an example. + Given file test.proto, containing + + package example; + + enum FOO { X = 17; }; + + message Test { + required string label = 1; + optional int32 type = 2 [default=77]; + repeated int64 reps = 3; + optional group OptionalGroup = 4 { + required string RequiredField = 5; + }; + } + + The resulting file, test.pb.go, is: + + package example + + import "goprotobuf.googlecode.com/hg/proto" + + type FOO int32 + const ( + FOO_X = 17 + ) + var FOO_name = map[int32] string { + 17: "X", + } + var FOO_value = map[string] int32 { + "X": 17, + } + func NewFOO(x int32) *FOO { + e := FOO(x) + return &e + } + func (x FOO) String() string { + return proto.EnumName(FOO_name, int32(x)) + } + + type Test struct { + Label *string `protobuf:"bytes,1,req,name=label"` + Type *int32 `protobuf:"varint,2,opt,name=type,def=77"` + Reps []int64 `protobuf:"varint,3,rep,name=reps"` + Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=optionalgroup"` + XXX_unrecognized []byte + } + func (this *Test) Reset() { + *this = Test{} + } + const Default_Test_Type int32 = 77 + + type Test_OptionalGroup struct { + RequiredField *string `protobuf:"bytes,5,req"` + XXX_unrecognized []byte + } + func (this *Test_OptionalGroup) Reset() { + *this = Test_OptionalGroup{} + } + + func init() { + proto.RegisterEnum("example.FOO", FOO_name, FOO_value) + } + + To create and play with a Test object: + + package main + + import ( + "log" + + "goprotobuf.googlecode.com/hg/proto" + "./example.pb" + ) + + func main() { + test := &example.Test{ + Label: proto.String("hello"), + Type: proto.Int32(17), + Optionalgroup: &example.Test_OptionalGroup{ + RequiredField: proto.String("good bye"), + }, + } + data, err := proto.Marshal(test) + if err != nil { + log.Fatal("marshaling error: ", err) + } + newTest := new(example.Test) + err = proto.Unmarshal(data, newTest) + if err != nil { + log.Fatal("unmarshaling error: ", err) + } + // Now test and newTest contain the same data. + if proto.GetString(test.Label) != proto.GetString(newTest.Label) { + log.Fatalf("data mismatch %q != %q", proto.GetString(test.Label), proto.GetString(newTest.Label)) + } + // etc. + } +*/ +package proto + +import ( + "fmt" + "log" + "reflect" + "strconv" + "sync" +) + +// Stats records allocation details about the protocol buffer encoders +// and decoders. Useful for tuning the library itself. +type Stats struct { + Emalloc uint64 // mallocs in encode + Dmalloc uint64 // mallocs in decode + Encode uint64 // number of encodes + Decode uint64 // number of decodes + Chit uint64 // number of cache hits + Cmiss uint64 // number of cache misses +} + +var stats Stats + +// GetStats returns a copy of the global Stats structure. +func GetStats() Stats { return stats } + +// A Buffer is a buffer manager for marshaling and unmarshaling +// protocol buffers. It may be reused between invocations to +// reduce memory usage. It is not necessary to use a Buffer; +// the global functions Marshal and Unmarshal create a +// temporary Buffer and are fine for most applications. +type Buffer struct { + buf []byte // encode/decode byte stream + index int // write point + freelist [10][]byte // list of available buffers + nfreelist int // number of free buffers + ptr uintptr // scratch area for pointers +} + +// NewBuffer allocates a new Buffer and initializes its internal data to +// the contents of the argument slice. +func NewBuffer(e []byte) *Buffer { + p := new(Buffer) + if e == nil { + e = p.bufalloc() + } + p.buf = e + p.index = 0 + return p +} + +// Reset resets the Buffer, ready for marshaling a new protocol buffer. +func (p *Buffer) Reset() { + if p.buf == nil { + p.buf = p.bufalloc() + } + p.buf = p.buf[0:0] // for reading/writing + p.index = 0 // for reading +} + +// SetBuf replaces the internal buffer with the slice, +// ready for unmarshaling the contents of the slice. +func (p *Buffer) SetBuf(s []byte) { + p.buf = s + p.index = 0 +} + +// Bytes returns the contents of the Buffer. +func (p *Buffer) Bytes() []byte { return p.buf } + +// Allocate a buffer for the Buffer. +func (p *Buffer) bufalloc() []byte { + if p.nfreelist > 0 { + // reuse an old one + p.nfreelist-- + s := p.freelist[p.nfreelist] + return s[0:0] + } + // make a new one + s := make([]byte, 0, 16) + return s +} + +// Free (and remember in freelist) a byte buffer for the Buffer. +func (p *Buffer) buffree(s []byte) { + if p.nfreelist < len(p.freelist) { + // Take next slot. + p.freelist[p.nfreelist] = s + p.nfreelist++ + return + } + + // Find the smallest. + besti := -1 + bestl := len(s) + for i, b := range p.freelist { + if len(b) < bestl { + besti = i + bestl = len(b) + } + } + + // Overwrite the smallest. + if besti >= 0 { + p.freelist[besti] = s + } +} + +/* + * Helper routines for simplifying the creation of optional fields of basic type. + */ + +// Bool is a helper routine that allocates a new bool value +// to store v and returns a pointer to it. +func Bool(v bool) *bool { + p := new(bool) + *p = v + return p +} + +// Int32 is a helper routine that allocates a new int32 value +// to store v and returns a pointer to it. +func Int32(v int32) *int32 { + p := new(int32) + *p = v + return p +} + +// Int is a helper routine that allocates a new int32 value +// to store v and returns a pointer to it, but unlike Int32 +// its argument value is an int. +func Int(v int) *int32 { + p := new(int32) + *p = int32(v) + return p +} + +// Int64 is a helper routine that allocates a new int64 value +// to store v and returns a pointer to it. +func Int64(v int64) *int64 { + p := new(int64) + *p = v + return p +} + +// Float32 is a helper routine that allocates a new float32 value +// to store v and returns a pointer to it. +func Float32(v float32) *float32 { + p := new(float32) + *p = v + return p +} + +// Float64 is a helper routine that allocates a new float64 value +// to store v and returns a pointer to it. +func Float64(v float64) *float64 { + p := new(float64) + *p = v + return p +} + +// Uint32 is a helper routine that allocates a new uint32 value +// to store v and returns a pointer to it. +func Uint32(v uint32) *uint32 { + p := new(uint32) + *p = v + return p +} + +// Uint64 is a helper routine that allocates a new uint64 value +// to store v and returns a pointer to it. +func Uint64(v uint64) *uint64 { + p := new(uint64) + *p = v + return p +} + +// String is a helper routine that allocates a new string value +// to store v and returns a pointer to it. +func String(v string) *string { + p := new(string) + *p = v + return p +} + +/* + * Helper routines for simplifying the fetching of optional fields of basic type. + * If the field is missing, they return the zero for the type. + */ + +// GetBool is a helper routine that returns an optional bool value. +func GetBool(p *bool) bool { + if p == nil { + return false + } + return *p +} + +// GetInt32 is a helper routine that returns an optional int32 value. +func GetInt32(p *int32) int32 { + if p == nil { + return 0 + } + return *p +} + +// GetInt64 is a helper routine that returns an optional int64 value. +func GetInt64(p *int64) int64 { + if p == nil { + return 0 + } + return *p +} + +// GetFloat32 is a helper routine that returns an optional float32 value. +func GetFloat32(p *float32) float32 { + if p == nil { + return 0 + } + return *p +} + +// GetFloat64 is a helper routine that returns an optional float64 value. +func GetFloat64(p *float64) float64 { + if p == nil { + return 0 + } + return *p +} + +// GetUint32 is a helper routine that returns an optional uint32 value. +func GetUint32(p *uint32) uint32 { + if p == nil { + return 0 + } + return *p +} + +// GetUint64 is a helper routine that returns an optional uint64 value. +func GetUint64(p *uint64) uint64 { + if p == nil { + return 0 + } + return *p +} + +// GetString is a helper routine that returns an optional string value. +func GetString(p *string) string { + if p == nil { + return "" + } + return *p +} + +// EnumName is a helper function to simplify printing protocol buffer enums +// by name. Given an enum map and a value, it returns a useful string. +func EnumName(m map[int32]string, v int32) string { + s, ok := m[v] + if ok { + return s + } + return "unknown_enum_" + strconv.Itoa(int(v)) +} + +// DebugPrint dumps the encoded data in b in a debugging format with a header +// including the string s. Used in testing but made available for general debugging. +func (o *Buffer) DebugPrint(s string, b []byte) { + var u uint64 + + obuf := o.buf + index := o.index + o.buf = b + o.index = 0 + depth := 0 + + fmt.Printf("\n--- %s ---\n", s) + +out: + for { + for i := 0; i < depth; i++ { + fmt.Print(" ") + } + + index := o.index + if index == len(o.buf) { + break + } + + op, err := o.DecodeVarint() + if err != nil { + fmt.Printf("%3d: fetching op err %v\n", index, err) + break out + } + tag := op >> 3 + wire := op & 7 + + switch wire { + default: + fmt.Printf("%3d: t=%3d unknown wire=%d\n", + index, tag, wire) + break out + + case WireBytes: + var r []byte + + r, err = o.DecodeRawBytes(false) + if err != nil { + break out + } + fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r)) + if len(r) <= 6 { + for i := 0; i < len(r); i++ { + fmt.Printf(" %.2x", r[i]) + } + } else { + for i := 0; i < 3; i++ { + fmt.Printf(" %.2x", r[i]) + } + fmt.Printf(" ..") + for i := len(r) - 3; i < len(r); i++ { + fmt.Printf(" %.2x", r[i]) + } + } + fmt.Printf("\n") + + case WireFixed32: + u, err = o.DecodeFixed32() + if err != nil { + fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u) + + case WireFixed64: + u, err = o.DecodeFixed64() + if err != nil { + fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u) + break + + case WireVarint: + u, err = o.DecodeVarint() + if err != nil { + fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u) + + case WireStartGroup: + if err != nil { + fmt.Printf("%3d: t=%3d start err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d start\n", index, tag) + depth++ + + case WireEndGroup: + depth-- + if err != nil { + fmt.Printf("%3d: t=%3d end err %v\n", index, tag, err) + break out + } + fmt.Printf("%3d: t=%3d end\n", index, tag) + } + } + + if depth != 0 { + fmt.Printf("%3d: start-end not balanced %d\n", o.index, depth) + } + fmt.Printf("\n") + + o.buf = obuf + o.index = index +} + +// SetDefaults sets unset protocol buffer fields to their default values. +// It only modifies fields that are both unset and have defined defaults. +// It recursively sets default values in any non-nil sub-messages. +func SetDefaults(pb interface{}) { + v := reflect.ValueOf(pb) + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + log.Printf("proto: hit non-pointer-to-struct %v", v) + } + setDefaults(v, true, false) +} + +// v is a pointer to a struct. +func setDefaults(v reflect.Value, recur, zeros bool) { + v = v.Elem() + + defaultMu.Lock() + dm, ok := defaults[v.Type()] + defaultMu.Unlock() + if !ok { + dm = buildDefaultMessage(v.Type()) + defaultMu.Lock() + defaults[v.Type()] = dm + defaultMu.Unlock() + } + + for _, sf := range dm.scalars { + f := v.Field(sf.index) + if !f.IsNil() { + // field already set + continue + } + dv := sf.value + if dv == nil && !zeros { + // no explicit default, and don't want to set zeros + continue + } + fptr := f.Addr().Interface() // **T + // TODO: Consider batching the allocations we do here. + switch sf.kind { + case reflect.Bool: + b := new(bool) + if dv != nil { + *b = dv.(bool) + } + *(fptr.(**bool)) = b + case reflect.Float32: + f := new(float32) + if dv != nil { + *f = dv.(float32) + } + *(fptr.(**float32)) = f + case reflect.Float64: + f := new(float64) + if dv != nil { + *f = dv.(float64) + } + *(fptr.(**float64)) = f + case reflect.Int32: + // might be an enum + if ft := f.Type(); ft != int32PtrType { + // enum + f.Set(reflect.New(ft.Elem())) + if dv != nil { + f.Elem().SetInt(int64(dv.(int32))) + } + } else { + // int32 field + i := new(int32) + if dv != nil { + *i = dv.(int32) + } + *(fptr.(**int32)) = i + } + case reflect.Int64: + i := new(int64) + if dv != nil { + *i = dv.(int64) + } + *(fptr.(**int64)) = i + case reflect.String: + s := new(string) + if dv != nil { + *s = dv.(string) + } + *(fptr.(**string)) = s + case reflect.Uint8: + // exceptional case: []byte + var b []byte + if dv != nil { + db := dv.([]byte) + b = make([]byte, len(db)) + copy(b, db) + } else { + b = []byte{} + } + *(fptr.(*[]byte)) = b + case reflect.Uint32: + u := new(uint32) + if dv != nil { + *u = dv.(uint32) + } + *(fptr.(**uint32)) = u + case reflect.Uint64: + u := new(uint64) + if dv != nil { + *u = dv.(uint64) + } + *(fptr.(**uint64)) = u + default: + log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind) + } + } + + for _, ni := range dm.nested { + setDefaults(v.Field(ni), recur, zeros) + } +} + +var ( + // defaults maps a protocol buffer struct type to a slice of the fields, + // with its scalar fields set to their proto-declared non-zero default values. + defaultMu sync.Mutex + defaults = make(map[reflect.Type]defaultMessage) + + int32PtrType = reflect.TypeOf((*int32)(nil)) +) + +// defaultMessage represents information about the default values of a message. +type defaultMessage struct { + scalars []scalarField + nested []int // struct field index of nested messages +} + +type scalarField struct { + index int // struct field index + kind reflect.Kind // element type (the T in *T or []T) + value interface{} // the proto-declared default value, or nil +} + +// t is a struct type. +func buildDefaultMessage(t reflect.Type) (dm defaultMessage) { + sprop := GetProperties(t) + for _, prop := range sprop.Prop { + fi := sprop.tags[prop.Tag] + ft := t.Field(fi).Type + + // nested messages + if ft.Kind() == reflect.Ptr && ft.Elem().Kind() == reflect.Struct { + dm.nested = append(dm.nested, fi) + continue + } + + sf := scalarField{ + index: fi, + kind: ft.Elem().Kind(), + } + + // scalar fields without defaults + if prop.Default == "" { + dm.scalars = append(dm.scalars, sf) + continue + } + + // a scalar field: either *T or []byte + switch ft.Elem().Kind() { + case reflect.Bool: + x, err := strconv.Atob(prop.Default) + if err != nil { + log.Printf("proto: bad default bool %q: %v", prop.Default, err) + continue + } + sf.value = x + case reflect.Float32: + x, err := strconv.Atof32(prop.Default) + if err != nil { + log.Printf("proto: bad default float32 %q: %v", prop.Default, err) + continue + } + sf.value = x + case reflect.Float64: + x, err := strconv.Atof64(prop.Default) + if err != nil { + log.Printf("proto: bad default float64 %q: %v", prop.Default, err) + continue + } + sf.value = x + case reflect.Int32: + x, err := strconv.Atoi64(prop.Default) + if err != nil { + log.Printf("proto: bad default int32 %q: %v", prop.Default, err) + continue + } + sf.value = int32(x) + case reflect.Int64: + x, err := strconv.Atoi64(prop.Default) + if err != nil { + log.Printf("proto: bad default int64 %q: %v", prop.Default, err) + continue + } + sf.value = x + case reflect.String: + sf.value = prop.Default + case reflect.Uint8: + // []byte (not *uint8) + sf.value = []byte(prop.Default) + case reflect.Uint32: + x, err := strconv.Atoui64(prop.Default) + if err != nil { + log.Printf("proto: bad default uint32 %q: %v", prop.Default, err) + continue + } + sf.value = uint32(x) + case reflect.Uint64: + x, err := strconv.Atoui64(prop.Default) + if err != nil { + log.Printf("proto: bad default uint64 %q: %v", prop.Default, err) + continue + } + sf.value = x + default: + log.Printf("proto: unhandled def kind %v", ft.Elem().Kind()) + continue + } + + dm.scalars = append(dm.scalars, sf) + } + + return dm +} diff --git a/proto/message_set.go b/proto/message_set.go new file mode 100644 index 0000000..05e18a8 --- /dev/null +++ b/proto/message_set.go @@ -0,0 +1,202 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Support for message sets. + */ + +import ( + "bytes" + "os" + "reflect" +) + +// ErrNoMessageTypeId occurs when a protocol buffer does not have a message type ID. +// A message type ID is required for storing a protocol buffer in a message set. +var ErrNoMessageTypeId = os.NewError("proto does not have a message type ID") + +// The first two types (_MessageSet_Item and MessageSet) +// model what the protocol compiler produces for the following protocol message: +// message MessageSet { +// repeated group Item = 1 { +// required int32 type_id = 2; +// required string message = 3; +// }; +// } +// That is the MessageSet wire format. We can't use a proto to generate these +// because that would introduce a circular dependency between it and this package. +// +// When a proto1 proto has a field that looks like: +// optional message info = 3; +// the protocol compiler produces a field in the generated struct that looks like: +// Info *_proto_.MessageSet `protobuf:"bytes,3,opt,name=info"` +// The package is automatically inserted so there is no need for that proto file to +// import this package. + +type _MessageSet_Item struct { + TypeId *int32 `protobuf:"varint,2,req,name=type_id"` + Message []byte `protobuf:"bytes,3,req,name=message"` +} + +type MessageSet struct { + Item []*_MessageSet_Item `protobuf:"group,1,rep"` + XXX_unrecognized *bytes.Buffer + // TODO: caching? +} + +// messageTypeIder is an interface satisfied by a protocol buffer type +// that may be stored in a MessageSet. +type messageTypeIder interface { + MessageTypeId() int32 +} + +func (ms *MessageSet) find(pb interface{}) *_MessageSet_Item { + mti, ok := pb.(messageTypeIder) + if !ok { + return nil + } + id := mti.MessageTypeId() + for _, item := range ms.Item { + if *item.TypeId == id { + return item + } + } + return nil +} + +func (ms *MessageSet) Has(pb interface{}) bool { + if ms.find(pb) != nil { + return true + } + return false +} + +func (ms *MessageSet) Unmarshal(pb interface{}) os.Error { + if item := ms.find(pb); item != nil { + return Unmarshal(item.Message, pb) + } + if _, ok := pb.(messageTypeIder); !ok { + return ErrNoMessageTypeId + } + return nil // TODO: return error instead? +} + +func (ms *MessageSet) Marshal(pb interface{}) os.Error { + msg, err := Marshal(pb) + if err != nil { + return err + } + if item := ms.find(pb); item != nil { + // reuse existing item + item.Message = msg + return nil + } + + mti, ok := pb.(messageTypeIder) + if !ok { + return ErrWrongType // TODO: custom error? + } + + mtid := mti.MessageTypeId() + ms.Item = append(ms.Item, &_MessageSet_Item{ + TypeId: &mtid, + Message: msg, + }) + return nil +} + +// Support for the message_set_wire_format message option. + +func skipVarint(buf []byte) []byte { + i := 0 + for ; buf[i]&0x80 != 0; i++ { + } + return buf[i+1:] +} + +// MarshalMessageSet encodes the extension map represented by m in the message set wire format. +// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option. +func MarshalMessageSet(m map[int32]Extension) ([]byte, os.Error) { + if err := encodeExtensionMap(m); err != nil { + return nil, err + } + + ms := &MessageSet{Item: make([]*_MessageSet_Item, len(m))} + i := 0 + for k, e := range m { + // Remove the wire type and field number varint, as well as the length varint. + msg := skipVarint(skipVarint(e.enc)) + + ms.Item[i] = &_MessageSet_Item{ + TypeId: Int32(k), + Message: msg, + } + i++ + } + return Marshal(ms) +} + +// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format. +// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option. +func UnmarshalMessageSet(buf []byte, m map[int32]Extension) os.Error { + ms := new(MessageSet) + if err := Unmarshal(buf, ms); err != nil { + return err + } + for _, item := range ms.Item { + // restore wire type and field number varint, plus length varint. + b := EncodeVarint(uint64(*item.TypeId)<<3 | WireBytes) + b = append(b, EncodeVarint(uint64(len(item.Message)))...) + b = append(b, item.Message...) + + m[*item.TypeId] = Extension{enc: b} + } + return nil +} + +// A global registry of types that can be used in a MessageSet. + +var messageSetMap = make(map[int32]messageSetDesc) + +type messageSetDesc struct { + t reflect.Type // pointer to struct + name string +} + +// RegisterMessageSetType is called from the generated code. +func RegisterMessageSetType(i messageTypeIder, name string) { + messageSetMap[i.MessageTypeId()] = messageSetDesc{ + t: reflect.TypeOf(i), + name: name, + } +} diff --git a/proto/properties.go b/proto/properties.go new file mode 100644 index 0000000..387a275 --- /dev/null +++ b/proto/properties.go @@ -0,0 +1,573 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +/* + * Routines for encoding data into the wire format for protocol buffers. + */ + +import ( + "fmt" + "os" + "reflect" + "strconv" + "strings" + "sync" + "unsafe" +) + +const debug bool = false + +// Constants that identify the encoding of a value on the wire. +const ( + WireVarint = 0 + WireFixed64 = 1 + WireBytes = 2 + WireStartGroup = 3 + WireEndGroup = 4 + WireFixed32 = 5 +) + +const startSize = 10 // initial slice/string sizes + +// Encoders are defined in encoder.go +// An encoder outputs the full representation of a field, including its +// tag and encoder type. +type encoder func(p *Buffer, prop *Properties, base uintptr) os.Error + +// A valueEncoder encodes a single integer in a particular encoding. +type valueEncoder func(o *Buffer, x uint64) os.Error + +// Decoders are defined in decode.go +// A decoder creates a value from its wire representation. +// Unrecognized subelements are saved in unrec. +type decoder func(p *Buffer, prop *Properties, base uintptr, sbase uintptr) os.Error + +// A valueDecoder decodes a single integer in a particular encoding. +type valueDecoder func(o *Buffer) (x uint64, err os.Error) + +// StructProperties represents properties for all the fields of a struct. +type StructProperties struct { + Prop []*Properties // properties for each field + reqCount int // required count + tags map[int]int // map from proto tag to struct field number + origNames map[string]int // map from original name to struct field number + nscratch uintptr // size of scratch space +} + +// Properties represents the protocol-specific behavior of a single struct field. +type Properties struct { + Name string // name of the field, for error messages + OrigName string // original name before protocol compiler (always set) + Wire string + WireType int + Tag int + Required bool + Optional bool + Repeated bool + Packed bool // relevant for repeated primitives only + Enum string // set for enum types only + Default string // default value + def_uint64 uint64 + + enc encoder + valEnc valueEncoder // set for bool and numeric types only + offset uintptr + tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType) + tagbuf [8]byte + stype reflect.Type + + dec decoder + valDec valueDecoder // set for bool and numeric types only + scratch uintptr + sizeof uintptr // calculations of scratch space + alignof uintptr + + // If this is a packable field, this will be the decoder for the packed version of the field. + packedDec decoder +} + +// String formats the properties in the protobuf struct field tag style. +func (p *Properties) String() string { + s := p.Wire + s = "," + s += strconv.Itoa(p.Tag) + if p.Required { + s += ",req" + } + if p.Optional { + s += ",opt" + } + if p.Repeated { + s += ",rep" + } + if p.Packed { + s += ",packed" + } + if p.OrigName != p.Name { + s += ",name=" + p.OrigName + } + if len(p.Enum) > 0 { + s += ",enum=" + p.Enum + } + if len(p.Default) > 0 { + s += ",def=" + p.Default + } + return s +} + +// Parse populates p by parsing a string in the protobuf struct field tag style. +func (p *Properties) Parse(s string) { + // "bytes,49,opt,def=hello!,name=foo" + fields := strings.Split(s, ",") // breaks def=, but handled below. + if len(fields) < 2 { + fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s) + return + } + + p.Wire = fields[0] + switch p.Wire { + case "varint": + p.WireType = WireVarint + p.valEnc = (*Buffer).EncodeVarint + p.valDec = (*Buffer).DecodeVarint + case "fixed32": + p.WireType = WireFixed32 + p.valEnc = (*Buffer).EncodeFixed32 + p.valDec = (*Buffer).DecodeFixed32 + case "fixed64": + p.WireType = WireFixed64 + p.valEnc = (*Buffer).EncodeFixed64 + p.valDec = (*Buffer).DecodeFixed64 + case "zigzag32": + p.WireType = WireVarint + p.valEnc = (*Buffer).EncodeZigzag32 + p.valDec = (*Buffer).DecodeZigzag32 + case "zigzag64": + p.WireType = WireVarint + p.valEnc = (*Buffer).EncodeZigzag64 + p.valDec = (*Buffer).DecodeZigzag64 + case "bytes", "group": + p.WireType = WireBytes + // no numeric converter for non-numeric types + default: + fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s) + return + } + + var err os.Error + p.Tag, err = strconv.Atoi(fields[1]) + if err != nil { + return + } + + for i := 2; i < len(fields); i++ { + f := fields[i] + switch { + case f == "req": + p.Required = true + case f == "opt": + p.Optional = true + case f == "rep": + p.Repeated = true + case f == "packed": + p.Packed = true + case len(f) >= 5 && f[0:5] == "name=": + p.OrigName = f[5:len(f)] + case len(f) >= 5 && f[0:5] == "enum=": + p.Enum = f[5:len(f)] + case len(f) >= 4 && f[0:4] == "def=": + p.Default = f[4:len(f)] // rest of string + if i+1 < len(fields) { + // Commas aren't escaped, and def is always last. + p.Default += "," + strings.Join(fields[i+1:len(fields)], ",") + break + } + } + } +} + +func logNoSliceEnc(t1, t2 reflect.Type) { + fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2) +} + +// Initialize the fields for encoding and decoding. +func (p *Properties) setEncAndDec(typ reflect.Type) { + var vbool bool + var vbyte byte + var vint32 int32 + var vint64 int64 + var vfloat32 float32 + var vfloat64 float64 + var vstring string + var vslice []byte + + p.enc = nil + p.dec = nil + + switch t1 := typ; t1.Kind() { + default: + fmt.Fprintf(os.Stderr, "proto: no coders for %T\n", t1) + break + + case reflect.Ptr: + switch t2 := t1.Elem(); t2.Kind() { + default: + fmt.Fprintf(os.Stderr, "proto: no encoder function for %T -> %T\n", t1, t2) + break + case reflect.Bool: + p.enc = (*Buffer).enc_bool + p.dec = (*Buffer).dec_bool + p.alignof = unsafe.Alignof(vbool) + p.sizeof = unsafe.Sizeof(vbool) + case reflect.Int32, reflect.Uint32: + p.enc = (*Buffer).enc_int32 + p.dec = (*Buffer).dec_int32 + p.alignof = unsafe.Alignof(vint32) + p.sizeof = unsafe.Sizeof(vint32) + case reflect.Int64, reflect.Uint64: + p.enc = (*Buffer).enc_int64 + p.dec = (*Buffer).dec_int64 + p.alignof = unsafe.Alignof(vint64) + p.sizeof = unsafe.Sizeof(vint64) + case reflect.Float32: + p.enc = (*Buffer).enc_int32 // can just treat them as bits + p.dec = (*Buffer).dec_int32 + p.alignof = unsafe.Alignof(vfloat32) + p.sizeof = unsafe.Sizeof(vfloat32) + case reflect.Float64: + p.enc = (*Buffer).enc_int64 // can just treat them as bits + p.dec = (*Buffer).dec_int64 + p.alignof = unsafe.Alignof(vfloat64) + p.sizeof = unsafe.Sizeof(vfloat64) + case reflect.String: + p.enc = (*Buffer).enc_string + p.dec = (*Buffer).dec_string + p.alignof = unsafe.Alignof(vstring) + p.sizeof = unsafe.Sizeof(vstring) + startSize*unsafe.Sizeof(vbyte) + case reflect.Struct: + p.stype = t1 + if p.Wire == "bytes" { + p.enc = (*Buffer).enc_struct_message + p.dec = (*Buffer).dec_struct_message + } else { + p.enc = (*Buffer).enc_struct_group + p.dec = (*Buffer).dec_struct_group + } + } + + case reflect.Slice: + switch t2 := t1.Elem(); t2.Kind() { + default: + logNoSliceEnc(t1, t2) + break + case reflect.Bool: + if p.Packed { + p.enc = (*Buffer).enc_slice_packed_bool + } else { + p.enc = (*Buffer).enc_slice_bool + } + p.dec = (*Buffer).dec_slice_bool + p.packedDec = (*Buffer).dec_slice_packed_bool + p.alignof = unsafe.Alignof(vbool) + p.sizeof = startSize * unsafe.Sizeof(vbool) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + switch t2.Bits() { + case 32: + if p.Packed { + p.enc = (*Buffer).enc_slice_packed_int32 + } else { + p.enc = (*Buffer).enc_slice_int32 + } + p.dec = (*Buffer).dec_slice_int32 + p.packedDec = (*Buffer).dec_slice_packed_int32 + p.alignof = unsafe.Alignof(vint32) + p.sizeof = startSize * unsafe.Sizeof(vint32) + case 64: + if p.Packed { + p.enc = (*Buffer).enc_slice_packed_int64 + } else { + p.enc = (*Buffer).enc_slice_int64 + } + p.dec = (*Buffer).dec_slice_int64 + p.packedDec = (*Buffer).dec_slice_packed_int64 + p.alignof = unsafe.Alignof(vint64) + p.sizeof = startSize * unsafe.Sizeof(vint64) + case 8: + if t2.Kind() == reflect.Uint8 { + p.enc = (*Buffer).enc_slice_byte + p.dec = (*Buffer).dec_slice_byte + p.alignof = unsafe.Alignof(vbyte) + p.sizeof = startSize * unsafe.Sizeof(vbyte) + } + default: + logNoSliceEnc(t1, t2) + break + } + case reflect.Float32, reflect.Float64: + switch t2.Bits() { + case 32: + // can just treat them as bits + if p.Packed { + p.enc = (*Buffer).enc_slice_packed_int32 + } else { + p.enc = (*Buffer).enc_slice_int32 + } + p.dec = (*Buffer).dec_slice_int32 + p.packedDec = (*Buffer).dec_slice_packed_int32 + p.alignof = unsafe.Alignof(vfloat32) + p.sizeof = startSize * unsafe.Sizeof(vfloat32) + case 64: + // can just treat them as bits + if p.Packed { + p.enc = (*Buffer).enc_slice_packed_int64 + } else { + p.enc = (*Buffer).enc_slice_int64 + } + p.dec = (*Buffer).dec_slice_int64 + p.packedDec = (*Buffer).dec_slice_packed_int64 + p.alignof = unsafe.Alignof(vfloat64) + p.sizeof = startSize * unsafe.Sizeof(vfloat64) + default: + logNoSliceEnc(t1, t2) + break + } + case reflect.String: + p.enc = (*Buffer).enc_slice_string + p.dec = (*Buffer).dec_slice_string + p.alignof = unsafe.Alignof(vstring) + p.sizeof = startSize * unsafe.Sizeof(vstring) + case reflect.Ptr: + switch t3 := t2.Elem(); t3.Kind() { + default: + fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3) + break + case reflect.Struct: + p.stype = t2 + p.enc = (*Buffer).enc_slice_struct_group + p.dec = (*Buffer).dec_slice_struct_group + if p.Wire == "bytes" { + p.enc = (*Buffer).enc_slice_struct_message + p.dec = (*Buffer).dec_slice_struct_message + } + p.alignof = unsafe.Alignof(vslice) + p.sizeof = startSize * unsafe.Sizeof(vslice) + } + case reflect.Slice: + switch t2.Elem().Kind() { + default: + fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem()) + break + case reflect.Uint8: + p.enc = (*Buffer).enc_slice_slice_byte + p.dec = (*Buffer).dec_slice_slice_byte + p.alignof = unsafe.Alignof(vslice) + p.sizeof = startSize * unsafe.Sizeof(vslice) + } + } + } + + // precalculate tag code + wire := p.WireType + if p.Packed { + wire = WireBytes + } + x := uint32(p.Tag)<<3 | uint32(wire) + i := 0 + for i = 0; x > 127; i++ { + p.tagbuf[i] = 0x80 | uint8(x&0x7F) + x >>= 7 + } + p.tagbuf[i] = uint8(x) + p.tagcode = p.tagbuf[0 : i+1] +} + +// Init populates the properties from a protocol buffer struct tag. +func (p *Properties) Init(typ reflect.Type, name, tag string, offset uintptr) { + // "bytes,49,opt,def=hello!" + p.Name = name + p.OrigName = name + p.offset = offset + + if tag == "" { + return + } + p.Parse(tag) + p.setEncAndDec(typ) +} + +var ( + mutex sync.Mutex + propertiesMap = make(map[reflect.Type]*StructProperties) +) + +// GetProperties returns the list of properties for the type represented by t. +func GetProperties(t reflect.Type) *StructProperties { + mutex.Lock() + if prop, ok := propertiesMap[t]; ok { + mutex.Unlock() + stats.Chit++ + return prop + } + stats.Cmiss++ + + prop := new(StructProperties) + + // build properties + prop.Prop = make([]*Properties, t.NumField()) + prop.origNames = make(map[string]int) + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + p := new(Properties) + p.Init(f.Type, f.Name, f.Tag.Get("protobuf"), f.Offset) + if f.Name == "XXX_extensions" { // special case + var vmap map[int32][]byte + p.enc = (*Buffer).enc_map + p.dec = nil // not needed + p.alignof = unsafe.Alignof(vmap) + p.sizeof = unsafe.Sizeof(vmap) + } + prop.Prop[i] = p + prop.origNames[p.OrigName] = i + if debug { + print(i, " ", f.Name, " ", t.String(), " ") + if p.Tag > 0 { + print(p.String()) + } + print("\n") + } + if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") { + fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]") + } + } + + // build required counts + // build scratch offsets + // build tags + reqCount := 0 + scratch := uintptr(0) + prop.tags = make(map[int]int) + for i, p := range prop.Prop { + if p.Required { + reqCount++ + } + scratch = align(scratch, p.alignof) + p.scratch = scratch + scratch += p.sizeof + prop.tags[p.Tag] = i + } + prop.reqCount = reqCount + prop.nscratch = scratch + + propertiesMap[t] = prop + mutex.Unlock() + return prop +} + +// Alignment of the data in the scratch area. It doesn't have to be +// exact, just conservative. Returns the first number >= o that divides s. +func align(o uintptr, s uintptr) uintptr { + if s != 0 { + for o%uintptr(s) != 0 { + o++ + } + } + return o +} + +// Return the field index of the named field. +// Returns nil if there is no such field. +func fieldIndex(t reflect.Type, name string) []int { + if field, ok := t.FieldByName(name); ok { + return field.Index + } + return nil +} + +// Return the Properties object for the x[0]'th field of the structure. +func propByIndex(t reflect.Type, x []int) *Properties { + if len(x) != 1 { + fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t) + return nil + } + prop := GetProperties(t) + return prop.Prop[x[0]] +} + +// Get the address and type of a pointer to a struct from an interface. +// unsafe.Reflect can do this, but does multiple mallocs. +func getbase(pb interface{}) (t reflect.Type, b uintptr, err os.Error) { + // get pointer + x := *(*[2]uintptr)(unsafe.Pointer(&pb)) + b = x[1] + if b == 0 { + err = ErrNil + return + } + + // get the reflect type of the struct. + t = reflect.TypeOf(pb) + return +} + +// Allocate the aux space containing all the decoded data. The structure +// handed into Unmarshal is filled with pointers to this newly allocated +// data. +func getsbase(prop *StructProperties) uintptr { + var vbyteptr *byte + if prop.nscratch == 0 { + return 0 + } + + // allocate the decode space as pointers + // so that the GC will scan it for pointers + n := uintptr(unsafe.Sizeof(vbyteptr)) + b := make([]*byte, (prop.nscratch+n-1)/n) + sbase := uintptr(unsafe.Pointer(&b[0])) + return sbase +} + +// A global registry of enum types. +// The generated code will register the generated maps by calling RegisterEnum. + +var enumNameMaps = make(map[string]map[int32]string) +var enumValueMaps = make(map[string]map[string]int32) + +// RegisterEnum is called from the generated code to install the enum descriptor +// maps into the global table to aid parsing ASCII protocol buffers. +func RegisterEnum(typeName string, nameMap map[int32]string, valueMap map[string]int32) { + if _, ok := enumNameMaps[typeName]; ok { + panic("proto: duplicate enum registered: " + typeName) + } + enumNameMaps[typeName] = nameMap + enumValueMaps[typeName] = valueMap +} diff --git a/proto/testdata/Makefile b/proto/testdata/Makefile new file mode 100644 index 0000000..ef73c10 --- /dev/null +++ b/proto/testdata/Makefile @@ -0,0 +1,53 @@ +# Go support for Protocol Buffers - Google's data interchange format +# +# Copyright 2010 Google Inc. All rights reserved. +# http://code.google.com/p/goprotobuf/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +include $(GOROOT)/src/Make.inc +include ../../Make.protobuf + +TARG=test_proto +GOFILES=\ + test.pb.go\ + +include $(GOROOT)/src/Make.pkg + +CLEANFILES+=test.pb.go + +# These rules are just aids to development. Not needed for testing. +regenerate: + rm test.pb.go + make test.pb.go + +restore: + cp test.pb.go.golden test.pb.go + +preserve: + cp test.pb.go test.pb.go.golden diff --git a/proto/testdata/test.pb.go.golden b/proto/testdata/test.pb.go.golden new file mode 100644 index 0000000..9eb7027 --- /dev/null +++ b/proto/testdata/test.pb.go.golden @@ -0,0 +1,393 @@ +// Code generated by protoc-gen-go from "test.proto" +// DO NOT EDIT! + +package test_proto + +import proto "goprotobuf.googlecode.com/hg/proto" +import "math" +import "os" + +// Reference proto, math & os imports to suppress error if they are not otherwise used. +var _ = proto.GetString +var _ = math.Inf +var _ os.Error + + +type FOO int32 + +const ( + FOO_FOO1 = 1 +) + +var FOO_name = map[int32]string{ + 1: "FOO1", +} +var FOO_value = map[string]int32{ + "FOO1": 1, +} + +func NewFOO(x int32) *FOO { + e := FOO(x) + return &e +} +func (x FOO) String() string { + return proto.EnumName(FOO_name, int32(x)) +} + +type GoTest_KIND int32 + +const ( + GoTest_VOID = 0 + GoTest_BOOL = 1 + GoTest_BYTES = 2 + GoTest_FINGERPRINT = 3 + GoTest_FLOAT = 4 + GoTest_INT = 5 + GoTest_STRING = 6 + GoTest_TIME = 7 + GoTest_TUPLE = 8 + GoTest_ARRAY = 9 + GoTest_MAP = 10 + GoTest_TABLE = 11 + GoTest_FUNCTION = 12 +) + +var GoTest_KIND_name = map[int32]string{ + 0: "VOID", + 1: "BOOL", + 2: "BYTES", + 3: "FINGERPRINT", + 4: "FLOAT", + 5: "INT", + 6: "STRING", + 7: "TIME", + 8: "TUPLE", + 9: "ARRAY", + 10: "MAP", + 11: "TABLE", + 12: "FUNCTION", +} +var GoTest_KIND_value = map[string]int32{ + "VOID": 0, + "BOOL": 1, + "BYTES": 2, + "FINGERPRINT": 3, + "FLOAT": 4, + "INT": 5, + "STRING": 6, + "TIME": 7, + "TUPLE": 8, + "ARRAY": 9, + "MAP": 10, + "TABLE": 11, + "FUNCTION": 12, +} + +func NewGoTest_KIND(x int32) *GoTest_KIND { + e := GoTest_KIND(x) + return &e +} +func (x GoTest_KIND) String() string { + return proto.EnumName(GoTest_KIND_name, int32(x)) +} + +type MyMessage_Color int32 + +const ( + MyMessage_RED = 0 + MyMessage_GREEN = 1 + MyMessage_BLUE = 2 +) + +var MyMessage_Color_name = map[int32]string{ + 0: "RED", + 1: "GREEN", + 2: "BLUE", +} +var MyMessage_Color_value = map[string]int32{ + "RED": 0, + "GREEN": 1, + "BLUE": 2, +} + +func NewMyMessage_Color(x int32) *MyMessage_Color { + e := MyMessage_Color(x) + return &e +} +func (x MyMessage_Color) String() string { + return proto.EnumName(MyMessage_Color_name, int32(x)) +} + +type GoEnum struct { + Foo *FOO `protobuf:"varint,1,req,name=foo,enum=test_proto.FOO"` + XXX_unrecognized []byte +} + +func (this *GoEnum) Reset() { *this = GoEnum{} } +func (this *GoEnum) String() string { return proto.CompactTextString(this) } + +type GoTestField struct { + Label *string `protobuf:"bytes,1,req"` + Type *string `protobuf:"bytes,2,req"` + XXX_unrecognized []byte +} + +func (this *GoTestField) Reset() { *this = GoTestField{} } +func (this *GoTestField) String() string { return proto.CompactTextString(this) } + +type GoTest struct { + Kind *int32 `protobuf:"varint,1,req"` + Table *string `protobuf:"bytes,2,opt"` + Param *int32 `protobuf:"varint,3,opt"` + RequiredField *GoTestField `protobuf:"bytes,4,req"` + RepeatedField []*GoTestField `protobuf:"bytes,5,rep"` + OptionalField *GoTestField `protobuf:"bytes,6,opt"` + F_BoolRequired *bool `protobuf:"varint,10,req,name=F_Bool_required"` + F_Int32Required *int32 `protobuf:"varint,11,req,name=F_Int32_required"` + F_Int64Required *int64 `protobuf:"varint,12,req,name=F_Int64_required"` + F_Fixed32Required *uint32 `protobuf:"fixed32,13,req,name=F_Fixed32_required"` + F_Fixed64Required *uint64 `protobuf:"fixed64,14,req,name=F_Fixed64_required"` + F_Uint32Required *uint32 `protobuf:"varint,15,req,name=F_Uint32_required"` + F_Uint64Required *uint64 `protobuf:"varint,16,req,name=F_Uint64_required"` + F_FloatRequired *float32 `protobuf:"fixed32,17,req,name=F_Float_required"` + F_DoubleRequired *float64 `protobuf:"fixed64,18,req,name=F_Double_required"` + F_StringRequired *string `protobuf:"bytes,19,req,name=F_String_required"` + F_BytesRequired []byte `protobuf:"bytes,101,req,name=F_Bytes_required"` + F_Sint32Required *int32 `protobuf:"zigzag32,102,req,name=F_Sint32_required"` + F_Sint64Required *int64 `protobuf:"zigzag64,103,req,name=F_Sint64_required"` + F_BoolRepeated []bool `protobuf:"varint,20,rep,name=F_Bool_repeated"` + F_Int32Repeated []int32 `protobuf:"varint,21,rep,name=F_Int32_repeated"` + F_Int64Repeated []int64 `protobuf:"varint,22,rep,name=F_Int64_repeated"` + F_Fixed32Repeated []uint32 `protobuf:"fixed32,23,rep,name=F_Fixed32_repeated"` + F_Fixed64Repeated []uint64 `protobuf:"fixed64,24,rep,name=F_Fixed64_repeated"` + F_Uint32Repeated []uint32 `protobuf:"varint,25,rep,name=F_Uint32_repeated"` + F_Uint64Repeated []uint64 `protobuf:"varint,26,rep,name=F_Uint64_repeated"` + F_FloatRepeated []float32 `protobuf:"fixed32,27,rep,name=F_Float_repeated"` + F_DoubleRepeated []float64 `protobuf:"fixed64,28,rep,name=F_Double_repeated"` + F_StringRepeated []string `protobuf:"bytes,29,rep,name=F_String_repeated"` + F_BytesRepeated [][]byte `protobuf:"bytes,201,rep,name=F_Bytes_repeated"` + F_Sint32Repeated []int32 `protobuf:"zigzag32,202,rep,name=F_Sint32_repeated"` + F_Sint64Repeated []int64 `protobuf:"zigzag64,203,rep,name=F_Sint64_repeated"` + F_BoolOptional *bool `protobuf:"varint,30,opt,name=F_Bool_optional"` + F_Int32Optional *int32 `protobuf:"varint,31,opt,name=F_Int32_optional"` + F_Int64Optional *int64 `protobuf:"varint,32,opt,name=F_Int64_optional"` + F_Fixed32Optional *uint32 `protobuf:"fixed32,33,opt,name=F_Fixed32_optional"` + F_Fixed64Optional *uint64 `protobuf:"fixed64,34,opt,name=F_Fixed64_optional"` + F_Uint32Optional *uint32 `protobuf:"varint,35,opt,name=F_Uint32_optional"` + F_Uint64Optional *uint64 `protobuf:"varint,36,opt,name=F_Uint64_optional"` + F_FloatOptional *float32 `protobuf:"fixed32,37,opt,name=F_Float_optional"` + F_DoubleOptional *float64 `protobuf:"fixed64,38,opt,name=F_Double_optional"` + F_StringOptional *string `protobuf:"bytes,39,opt,name=F_String_optional"` + F_BytesOptional []byte `protobuf:"bytes,301,opt,name=F_Bytes_optional"` + F_Sint32Optional *int32 `protobuf:"zigzag32,302,opt,name=F_Sint32_optional"` + F_Sint64Optional *int64 `protobuf:"zigzag64,303,opt,name=F_Sint64_optional"` + F_BoolDefaulted *bool `protobuf:"varint,40,opt,name=F_Bool_defaulted,def=1"` + F_Int32Defaulted *int32 `protobuf:"varint,41,opt,name=F_Int32_defaulted,def=32"` + F_Int64Defaulted *int64 `protobuf:"varint,42,opt,name=F_Int64_defaulted,def=64"` + F_Fixed32Defaulted *uint32 `protobuf:"fixed32,43,opt,name=F_Fixed32_defaulted,def=320"` + F_Fixed64Defaulted *uint64 `protobuf:"fixed64,44,opt,name=F_Fixed64_defaulted,def=640"` + F_Uint32Defaulted *uint32 `protobuf:"varint,45,opt,name=F_Uint32_defaulted,def=3200"` + F_Uint64Defaulted *uint64 `protobuf:"varint,46,opt,name=F_Uint64_defaulted,def=6400"` + F_FloatDefaulted *float32 `protobuf:"fixed32,47,opt,name=F_Float_defaulted,def=314159"` + F_DoubleDefaulted *float64 `protobuf:"fixed64,48,opt,name=F_Double_defaulted,def=271828"` + F_StringDefaulted *string `protobuf:"bytes,49,opt,name=F_String_defaulted,def=hello, \\\"world!\\\"\\n"` + F_BytesDefaulted []byte `protobuf:"bytes,401,opt,name=F_Bytes_defaulted,def=Bignose"` + F_Sint32Defaulted *int32 `protobuf:"zigzag32,402,opt,name=F_Sint32_defaulted,def=-32"` + F_Sint64Defaulted *int64 `protobuf:"zigzag64,403,opt,name=F_Sint64_defaulted,def=-64"` + F_BoolRepeatedPacked []bool `protobuf:"varint,50,rep,packed,name=F_Bool_repeated_packed"` + F_Int32RepeatedPacked []int32 `protobuf:"varint,51,rep,packed,name=F_Int32_repeated_packed"` + F_Int64RepeatedPacked []int64 `protobuf:"varint,52,rep,packed,name=F_Int64_repeated_packed"` + F_Fixed32RepeatedPacked []uint32 `protobuf:"fixed32,53,rep,packed,name=F_Fixed32_repeated_packed"` + F_Fixed64RepeatedPacked []uint64 `protobuf:"fixed64,54,rep,packed,name=F_Fixed64_repeated_packed"` + F_Uint32RepeatedPacked []uint32 `protobuf:"varint,55,rep,packed,name=F_Uint32_repeated_packed"` + F_Uint64RepeatedPacked []uint64 `protobuf:"varint,56,rep,packed,name=F_Uint64_repeated_packed"` + F_FloatRepeatedPacked []float32 `protobuf:"fixed32,57,rep,packed,name=F_Float_repeated_packed"` + F_DoubleRepeatedPacked []float64 `protobuf:"fixed64,58,rep,packed,name=F_Double_repeated_packed"` + F_Sint32RepeatedPacked []int32 `protobuf:"zigzag32,502,rep,packed,name=F_Sint32_repeated_packed"` + F_Sint64RepeatedPacked []int64 `protobuf:"zigzag64,503,rep,packed,name=F_Sint64_repeated_packed"` + Requiredgroup *GoTest_RequiredGroup `protobuf:"group,70,req,name=RequiredGroup"` + Repeatedgroup []*GoTest_RepeatedGroup `protobuf:"group,80,rep,name=RepeatedGroup"` + Optionalgroup *GoTest_OptionalGroup `protobuf:"group,90,opt,name=OptionalGroup"` + XXX_unrecognized []byte +} + +func (this *GoTest) Reset() { *this = GoTest{} } +func (this *GoTest) String() string { return proto.CompactTextString(this) } + +const Default_GoTest_F_BoolDefaulted bool = true +const Default_GoTest_F_Int32Defaulted int32 = 32 +const Default_GoTest_F_Int64Defaulted int64 = 64 +const Default_GoTest_F_Fixed32Defaulted uint32 = 320 +const Default_GoTest_F_Fixed64Defaulted uint64 = 640 +const Default_GoTest_F_Uint32Defaulted uint32 = 3200 +const Default_GoTest_F_Uint64Defaulted uint64 = 6400 +const Default_GoTest_F_FloatDefaulted float32 = 314159 +const Default_GoTest_F_DoubleDefaulted float64 = 271828 +const Default_GoTest_F_StringDefaulted string = "hello, \"world!\"\n" + +var Default_GoTest_F_BytesDefaulted []byte = []byte("Bignose") + +const Default_GoTest_F_Sint32Defaulted int32 = -32 +const Default_GoTest_F_Sint64Defaulted int64 = -64 + +type GoTest_RequiredGroup struct { + RequiredField *string `protobuf:"bytes,71,req"` + XXX_unrecognized []byte +} + +func (this *GoTest_RequiredGroup) Reset() { *this = GoTest_RequiredGroup{} } +func (this *GoTest_RequiredGroup) String() string { return proto.CompactTextString(this) } + +type GoTest_RepeatedGroup struct { + RequiredField *string `protobuf:"bytes,81,req"` + XXX_unrecognized []byte +} + +func (this *GoTest_RepeatedGroup) Reset() { *this = GoTest_RepeatedGroup{} } +func (this *GoTest_RepeatedGroup) String() string { return proto.CompactTextString(this) } + +type GoTest_OptionalGroup struct { + RequiredField *string `protobuf:"bytes,91,req"` + XXX_unrecognized []byte +} + +func (this *GoTest_OptionalGroup) Reset() { *this = GoTest_OptionalGroup{} } +func (this *GoTest_OptionalGroup) String() string { return proto.CompactTextString(this) } + +type GoSkipTest struct { + SkipInt32 *int32 `protobuf:"varint,11,req,name=skip_int32"` + SkipFixed32 *uint32 `protobuf:"fixed32,12,req,name=skip_fixed32"` + SkipFixed64 *uint64 `protobuf:"fixed64,13,req,name=skip_fixed64"` + SkipString *string `protobuf:"bytes,14,req,name=skip_string"` + Skipgroup *GoSkipTest_SkipGroup `protobuf:"group,15,req,name=SkipGroup"` + XXX_unrecognized []byte +} + +func (this *GoSkipTest) Reset() { *this = GoSkipTest{} } +func (this *GoSkipTest) String() string { return proto.CompactTextString(this) } + +type GoSkipTest_SkipGroup struct { + GroupInt32 *int32 `protobuf:"varint,16,req,name=group_int32"` + GroupString *string `protobuf:"bytes,17,req,name=group_string"` + XXX_unrecognized []byte +} + +func (this *GoSkipTest_SkipGroup) Reset() { *this = GoSkipTest_SkipGroup{} } +func (this *GoSkipTest_SkipGroup) String() string { return proto.CompactTextString(this) } + +type NonPackedTest struct { + A []int32 `protobuf:"varint,1,rep,name=a"` + XXX_unrecognized []byte +} + +func (this *NonPackedTest) Reset() { *this = NonPackedTest{} } +func (this *NonPackedTest) String() string { return proto.CompactTextString(this) } + +type PackedTest struct { + B []int32 `protobuf:"varint,1,rep,packed,name=b"` + XXX_unrecognized []byte +} + +func (this *PackedTest) Reset() { *this = PackedTest{} } +func (this *PackedTest) String() string { return proto.CompactTextString(this) } + +type InnerMessage struct { + Host *string `protobuf:"bytes,1,req,name=host"` + Port *int32 `protobuf:"varint,2,opt,name=port,def=4000"` + Connected *bool `protobuf:"varint,3,opt,name=connected"` + XXX_unrecognized []byte +} + +func (this *InnerMessage) Reset() { *this = InnerMessage{} } +func (this *InnerMessage) String() string { return proto.CompactTextString(this) } + +const Default_InnerMessage_Port int32 = 4000 + +type OtherMessage struct { + Key *int64 `protobuf:"varint,1,opt,name=key"` + Value []byte `protobuf:"bytes,2,opt,name=value"` + Weight *float32 `protobuf:"fixed32,3,opt,name=weight"` + Inner *InnerMessage `protobuf:"bytes,4,opt,name=inner"` + XXX_unrecognized []byte +} + +func (this *OtherMessage) Reset() { *this = OtherMessage{} } +func (this *OtherMessage) String() string { return proto.CompactTextString(this) } + +type MyMessage struct { + Count *int32 `protobuf:"varint,1,req,name=count"` + Name *string `protobuf:"bytes,2,opt,name=name"` + Quote *string `protobuf:"bytes,3,opt,name=quote"` + Pet []string `protobuf:"bytes,4,rep,name=pet"` + Inner *InnerMessage `protobuf:"bytes,5,opt,name=inner"` + Others []*OtherMessage `protobuf:"bytes,6,rep,name=others"` + Bikeshed *MyMessage_Color `protobuf:"varint,7,opt,name=bikeshed,enum=test_proto.MyMessage_Color"` + Somegroup *MyMessage_SomeGroup `protobuf:"group,8,opt,name=SomeGroup"` + XXX_extensions map[int32][]byte + XXX_unrecognized []byte +} + +func (this *MyMessage) Reset() { *this = MyMessage{} } +func (this *MyMessage) String() string { return proto.CompactTextString(this) } + +var extRange_MyMessage = []proto.ExtensionRange{ + proto.ExtensionRange{100, 536870911}, +} + +func (*MyMessage) ExtensionRangeArray() []proto.ExtensionRange { + return extRange_MyMessage +} +func (this *MyMessage) ExtensionMap() map[int32][]byte { + if this.XXX_extensions == nil { + this.XXX_extensions = make(map[int32][]byte) + } + return this.XXX_extensions +} + +type MyMessage_SomeGroup struct { + GroupField *int32 `protobuf:"varint,9,opt,name=group_field"` + XXX_unrecognized []byte +} + +func (this *MyMessage_SomeGroup) Reset() { *this = MyMessage_SomeGroup{} } +func (this *MyMessage_SomeGroup) String() string { return proto.CompactTextString(this) } + +type Ext struct { + Data *string `protobuf:"bytes,1,opt,name=data"` + XXX_unrecognized []byte +} + +func (this *Ext) Reset() { *this = Ext{} } +func (this *Ext) String() string { return proto.CompactTextString(this) } + +var E_Ext_More = &proto.ExtensionDesc{ + ExtendedType: (*MyMessage)(nil), + ExtensionType: (*Ext)(nil), + Field: 103, + Name: "test_proto.more", + Tag: "bytes,103,opt,name=more", +} + +type MessageList struct { + Message []*MessageList_Message `protobuf:"group,1,rep"` + XXX_unrecognized []byte +} + +func (this *MessageList) Reset() { *this = MessageList{} } +func (this *MessageList) String() string { return proto.CompactTextString(this) } + +type MessageList_Message struct { + Name *string `protobuf:"bytes,2,req,name=name"` + Count *int32 `protobuf:"varint,3,req,name=count"` + XXX_unrecognized []byte +} + +func (this *MessageList_Message) Reset() { *this = MessageList_Message{} } +func (this *MessageList_Message) String() string { return proto.CompactTextString(this) } + +func init() { + proto.RegisterEnum("test_proto.FOO", FOO_name, FOO_value) + proto.RegisterEnum("test_proto.GoTest_KIND", GoTest_KIND_name, GoTest_KIND_value) + proto.RegisterEnum("test_proto.MyMessage_Color", MyMessage_Color_name, MyMessage_Color_value) + proto.RegisterExtension(E_Ext_More) +} diff --git a/proto/testdata/test.proto b/proto/testdata/test.proto new file mode 100644 index 0000000..cd82721 --- /dev/null +++ b/proto/testdata/test.proto @@ -0,0 +1,289 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A feature-rich test file for the protocol compiler and libraries. + +syntax = "proto2"; + +package test_proto; + +enum FOO { FOO1 = 1; }; + +message GoEnum { + required FOO foo = 1; +} + +message GoTestField { + required string Label = 1; + required string Type = 2; +} + +message GoTest { + // An enum, for completeness. + enum KIND { + VOID = 0; + + // Basic types + BOOL = 1; + BYTES = 2; + FINGERPRINT = 3; + FLOAT = 4; + INT = 5; + STRING = 6; + TIME = 7; + + // Groupings + TUPLE = 8; + ARRAY = 9; + MAP = 10; + + // Table types + TABLE = 11; + + // Functions + FUNCTION = 12; // last tag + }; + + // Some typical parameters + required KIND Kind = 1; + optional string Table = 2; + optional int32 Param = 3; + + // Required, repeated and optional foreign fields. + required GoTestField RequiredField = 4; + repeated GoTestField RepeatedField = 5; + optional GoTestField OptionalField = 6; + + // Required fields of all basic types + required bool F_Bool_required = 10; + required int32 F_Int32_required = 11; + required int64 F_Int64_required = 12; + required fixed32 F_Fixed32_required = 13; + required fixed64 F_Fixed64_required = 14; + required uint32 F_Uint32_required = 15; + required uint64 F_Uint64_required = 16; + required float F_Float_required = 17; + required double F_Double_required = 18; + required string F_String_required = 19; + required bytes F_Bytes_required = 101; + required sint32 F_Sint32_required = 102; + required sint64 F_Sint64_required = 103; + + // Repeated fields of all basic types + repeated bool F_Bool_repeated = 20; + repeated int32 F_Int32_repeated = 21; + repeated int64 F_Int64_repeated = 22; + repeated fixed32 F_Fixed32_repeated = 23; + repeated fixed64 F_Fixed64_repeated = 24; + repeated uint32 F_Uint32_repeated = 25; + repeated uint64 F_Uint64_repeated = 26; + repeated float F_Float_repeated = 27; + repeated double F_Double_repeated = 28; + repeated string F_String_repeated = 29; + repeated bytes F_Bytes_repeated = 201; + repeated sint32 F_Sint32_repeated = 202; + repeated sint64 F_Sint64_repeated = 203; + + // Optional fields of all basic types + optional bool F_Bool_optional = 30; + optional int32 F_Int32_optional = 31; + optional int64 F_Int64_optional = 32; + optional fixed32 F_Fixed32_optional = 33; + optional fixed64 F_Fixed64_optional = 34; + optional uint32 F_Uint32_optional = 35; + optional uint64 F_Uint64_optional = 36; + optional float F_Float_optional = 37; + optional double F_Double_optional = 38; + optional string F_String_optional = 39; + optional bytes F_Bytes_optional = 301; + optional sint32 F_Sint32_optional = 302; + optional sint64 F_Sint64_optional = 303; + + // Default-valued fields of all basic types + optional bool F_Bool_defaulted = 40 [default=true]; + optional int32 F_Int32_defaulted = 41 [default=32]; + optional int64 F_Int64_defaulted = 42 [default=64]; + optional fixed32 F_Fixed32_defaulted = 43 [default=320]; + optional fixed64 F_Fixed64_defaulted = 44 [default=640]; + optional uint32 F_Uint32_defaulted = 45 [default=3200]; + optional uint64 F_Uint64_defaulted = 46 [default=6400]; + optional float F_Float_defaulted = 47 [default=314159.]; + optional double F_Double_defaulted = 48 [default=271828.]; + optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"]; + optional bytes F_Bytes_defaulted = 401 [default="Bignose"]; + optional sint32 F_Sint32_defaulted = 402 [default = -32]; + optional sint64 F_Sint64_defaulted = 403 [default = -64]; + + // Packed repeated fields (no string or bytes). + repeated bool F_Bool_repeated_packed = 50 [packed=true]; + repeated int32 F_Int32_repeated_packed = 51 [packed=true]; + repeated int64 F_Int64_repeated_packed = 52 [packed=true]; + repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true]; + repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true]; + repeated uint32 F_Uint32_repeated_packed = 55 [packed=true]; + repeated uint64 F_Uint64_repeated_packed = 56 [packed=true]; + repeated float F_Float_repeated_packed = 57 [packed=true]; + repeated double F_Double_repeated_packed = 58 [packed=true]; + repeated sint32 F_Sint32_repeated_packed = 502 [packed=true]; + repeated sint64 F_Sint64_repeated_packed = 503 [packed=true]; + + // Required, repeated, and optional groups. + required group RequiredGroup = 70 { + required string RequiredField = 71; + }; + + repeated group RepeatedGroup = 80 { + required string RequiredField = 81; + }; + + optional group OptionalGroup = 90 { + required string RequiredField = 91; + }; +} + +// For testing skipping of unrecognized fields. +// Numbers are all big, larger than tag numbers in GoTestField, +// the message used in the corresponding test. +message GoSkipTest { + required int32 skip_int32 = 11; + required fixed32 skip_fixed32 = 12; + required fixed64 skip_fixed64 = 13; + required string skip_string = 14; + required group SkipGroup = 15 { + required int32 group_int32 = 16; + required string group_string = 17; + } +} + +// For testing packed/non-packed decoder switching. +// A serialized instance of one should be deserializable as the other. +message NonPackedTest { + repeated int32 a = 1; +} + +message PackedTest { + repeated int32 b = 1 [packed=true]; +} + +message MaxTag { + // Maximum possible tag number. + optional string last_field = 536870911; +} + +// Smaller tests for ASCII formatting. + +message InnerMessage { + required string host = 1; + optional int32 port = 2 [default=4000]; + optional bool connected = 3; +} + +message OtherMessage { + optional int64 key = 1; + optional bytes value = 2; + optional float weight = 3; + optional InnerMessage inner = 4; +} + +message MyMessage { + required int32 count = 1; + optional string name = 2; + optional string quote = 3; + repeated string pet = 4; + optional InnerMessage inner = 5; + repeated OtherMessage others = 6; + + enum Color { + RED = 0; + GREEN = 1; + BLUE = 2; + }; + optional Color bikeshed = 7; + + optional group SomeGroup = 8 { + optional int32 group_field = 9; + } + + // This field becomes [][]byte in the generated code. + repeated bytes rep_bytes = 10; + + extensions 100 to max; +} + +message Ext { + extend MyMessage { + optional Ext more = 103; + } + + optional string data = 1; +} + +message MessageList { + repeated group Message = 1 { + required string name = 2; + required int32 count = 3; + } +} + +message Strings { + optional string string_field = 1; + optional bytes bytes_field = 2; +} + +message Defaults { + enum Color { + RED = 0; + GREEN = 1; + BLUE = 2; + } + + // Default-valued fields of all basic types. + // Same as GoTest, but copied here to make testing easier. + optional bool F_Bool = 1 [default=true]; + optional int32 F_Int32 = 2 [default=32]; + optional int64 F_Int64 = 3 [default=64]; + optional fixed32 F_Fixed32 = 4 [default=320]; + optional fixed64 F_Fixed64 = 5 [default=640]; + optional uint32 F_Uint32 = 6 [default=3200]; + optional uint64 F_Uint64 = 7 [default=6400]; + optional float F_Float = 8 [default=314159.]; + optional double F_Double = 9 [default=271828.]; + optional string F_String = 10 [default="hello, \"world!\"\n"]; + optional bytes F_Bytes = 11 [default="Bignose"]; + optional sint32 F_Sint32 = 12 [default=-32]; + optional sint64 F_Sint64 = 13 [default=-64]; + optional Color F_Enum = 14 [default=GREEN]; + + // More fields with crazy defaults. + optional float F_Pinf = 15 [default=inf]; + optional float F_Ninf = 16 [default=-inf]; + optional float F_Nan = 17 [default=nan]; +} diff --git a/proto/text.go b/proto/text.go new file mode 100644 index 0000000..0e2b874 --- /dev/null +++ b/proto/text.go @@ -0,0 +1,435 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +// Functions for writing the text protocol buffer format. + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "reflect" + "sort" + "strings" +) + +// textWriter is an io.Writer that tracks its indentation level. +type textWriter struct { + ind int + complete bool // if the current position is a complete line + compact bool // whether to write out as a one-liner + writer io.Writer + + c [1]byte // scratch +} + +func (w *textWriter) Write(p []byte) (n int, err os.Error) { + n, err = len(p), nil + + frags := strings.Split(string(p), "\n") + if w.compact { + w.writer.Write([]byte(strings.Join(frags, " "))) + return + } + + for i, frag := range frags { + if w.complete { + for j := 0; j < w.ind; j++ { + w.writer.Write([]byte{' ', ' '}) + } + w.complete = false + } + + w.writer.Write([]byte(frag)) + if i+1 < len(frags) { + w.writer.Write([]byte{'\n'}) + } + } + w.complete = len(frags[len(frags)-1]) == 0 + + return +} + +func (w *textWriter) WriteByte(c byte) os.Error { + w.c[0] = c + _, err := w.Write(w.c[:]) + return err +} + +func (w *textWriter) indent() { w.ind++ } + +func (w *textWriter) unindent() { + if w.ind == 0 { + log.Printf("proto: textWriter unindented too far!") + return + } + w.ind-- +} + +func writeName(w *textWriter, props *Properties) { + io.WriteString(w, props.OrigName) + if props.Wire != "group" { + w.WriteByte(':') + } +} + +var ( + messageSetType = reflect.TypeOf((*MessageSet)(nil)).Elem() + extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem() +) + +func writeStruct(w *textWriter, sv reflect.Value) { + if sv.Type() == messageSetType { + writeMessageSet(w, sv.Addr().Interface().(*MessageSet)) + return + } + + st := sv.Type() + sprops := GetProperties(st) + for i := 0; i < sv.NumField(); i++ { + fv := sv.Field(i) + if name := st.Field(i).Name; strings.HasPrefix(name, "XXX_") { + // There's only two XXX_ fields: + // XXX_unrecognized []byte + // XXX_extensions map[int32]proto.Extension + // The first is handled here; + // the second is handled at the bottom of this function. + if name == "XXX_unrecognized" && !fv.IsNil() { + writeUnknownStruct(w, fv.Interface().([]byte)) + } + continue + } + props := sprops.Prop[i] + if fv.Kind() == reflect.Ptr && fv.IsNil() { + // Field not filled in. This could be an optional field or + // a required field that wasn't filled in. Either way, there + // isn't anything we can show for it. + continue + } + if fv.Kind() == reflect.Slice && fv.IsNil() { + // Repeated field that is empty, or a bytes field that is unused. + continue + } + + if props.Repeated && fv.Kind() == reflect.Slice { + // Repeated field. + for j := 0; j < fv.Len(); j++ { + writeName(w, props) + if !w.compact { + w.WriteByte(' ') + } + writeAny(w, fv.Index(j), props) + w.WriteByte('\n') + } + continue + } + + writeName(w, props) + if !w.compact { + w.WriteByte(' ') + } + if props.Enum != "" && tryWriteEnum(w, props.Enum, fv) { + // Enum written. + } else { + writeAny(w, fv, props) + } + w.WriteByte('\n') + } + + // Extensions (the XXX_extensions field). + pv := sv.Addr() + if pv.Type().Implements(extendableProtoType) { + writeExtensions(w, pv) + } +} + +// tryWriteEnum attempts to write an enum value as a symbolic constant. +// If the enum is unregistered, nothing is written and false is returned. +func tryWriteEnum(w *textWriter, enum string, v reflect.Value) bool { + v = reflect.Indirect(v) + if v.Type().Kind() != reflect.Int32 { + return false + } + m, ok := enumNameMaps[enum] + if !ok { + return false + } + str, ok := m[int32(v.Int())] + if !ok { + return false + } + fmt.Fprintf(w, str) + return true +} + +// writeAny writes an arbitrary field. +func writeAny(w *textWriter, v reflect.Value, props *Properties) { + v = reflect.Indirect(v) + + // We don't attempt to serialise every possible value type; only those + // that can occur in protocol buffers, plus a few extra that were easy. + switch v.Kind() { + case reflect.Slice: + // Should only be a []byte; repeated fields are handled in writeStruct. + writeString(w, string(v.Interface().([]byte))) + case reflect.String: + writeString(w, v.String()) + case reflect.Struct: + // Required/optional group/message. + var bra, ket byte = '<', '>' + if props != nil && props.Wire == "group" { + bra, ket = '{', '}' + } + w.WriteByte(bra) + if !w.compact { + w.WriteByte('\n') + } + w.indent() + writeStruct(w, v) + w.unindent() + w.WriteByte(ket) + default: + fmt.Fprint(w, v.Interface()) + } +} + +// equivalent to C's isprint. +func isprint(c byte) bool { + return c >= 0x20 && c < 0x7f +} + +// writeString writes a string in the protocol buffer text format. +// It is similar to strconv.Quote except we don't use Go escape sequences, +// we treat the string as a byte sequence, and we use octal escapes. +// These differences are to maintain interoperability with the other +// languages' implementations of the text format. +func writeString(w *textWriter, s string) { + w.WriteByte('"') + + // Loop over the bytes, not the runes. + for i := 0; i < len(s); i++ { + // Divergence from C++: we don't escape apostrophes. + // There's no need to escape them, and the C++ parser + // copes with a naked apostrophe. + switch c := s[i]; c { + case '\n': + w.Write([]byte{'\\', 'n'}) + case '\r': + w.Write([]byte{'\\', 'r'}) + case '\t': + w.Write([]byte{'\\', 't'}) + case '"': + w.Write([]byte{'\\', '"'}) + case '\\': + w.Write([]byte{'\\', '\\'}) + default: + if isprint(c) { + w.WriteByte(c) + } else { + fmt.Fprintf(w, "\\%03o", c) + } + } + } + + w.WriteByte('"') +} + +func writeMessageSet(w *textWriter, ms *MessageSet) { + for _, item := range ms.Item { + id := *item.TypeId + if msd, ok := messageSetMap[id]; ok { + // Known message set type. + fmt.Fprintf(w, "[%s]: <\n", msd.name) + w.indent() + + pb := reflect.New(msd.t.Elem()) + if err := Unmarshal(item.Message, pb.Interface()); err != nil { + fmt.Fprintf(w, "/* bad message: %v */\n", err) + } else { + writeStruct(w, pb.Elem()) + } + } else { + // Unknown type. + fmt.Fprintf(w, "[%d]: <\n", id) + w.indent() + writeUnknownStruct(w, item.Message) + } + w.unindent() + w.Write([]byte(">\n")) + } +} + +func writeUnknownStruct(w *textWriter, data []byte) { + if !w.compact { + fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)) + } + b := NewBuffer(data) + for b.index < len(b.buf) { + x, err := b.DecodeVarint() + if err != nil { + fmt.Fprintf(w, "/* %v */\n", err) + return + } + wire, tag := x&7, x>>3 + if wire == WireEndGroup { + w.unindent() + w.Write([]byte("}\n")) + continue + } + fmt.Fprintf(w, "tag%d", tag) + if wire != WireStartGroup { + w.WriteByte(':') + } + if !w.compact || wire == WireStartGroup { + w.WriteByte(' ') + } + switch wire { + case WireBytes: + buf, err := b.DecodeRawBytes(false) + if err == nil { + fmt.Fprintf(w, "%q", buf) + } else { + fmt.Fprintf(w, "/* %v */", err) + } + case WireFixed32: + x, err := b.DecodeFixed32() + writeUnknownInt(w, x, err) + case WireFixed64: + x, err := b.DecodeFixed64() + writeUnknownInt(w, x, err) + case WireStartGroup: + fmt.Fprint(w, "{") + w.indent() + case WireVarint: + x, err := b.DecodeVarint() + writeUnknownInt(w, x, err) + default: + fmt.Fprintf(w, "/* unknown wire type %d */", wire) + } + w.WriteByte('\n') + } +} + +func writeUnknownInt(w *textWriter, x uint64, err os.Error) { + if err == nil { + fmt.Fprint(w, x) + } else { + fmt.Fprintf(w, "/* %v */", err) + } +} + +type int32Slice []int32 + +func (s int32Slice) Len() int { return len(s) } +func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] } +func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// writeExtensions writes all the extensions in pv. +// pv is assumed to be a pointer to a protocol message struct that is extendable. +func writeExtensions(w *textWriter, pv reflect.Value) { + emap := extensionMaps[pv.Type().Elem()] + ep := pv.Interface().(extendableProto) + + // Order the extensions by ID. + // This isn't strictly necessary, but it will give us + // canonical output, which will also make testing easier. + m := ep.ExtensionMap() + ids := make([]int32, 0, len(m)) + for id := range m { + ids = append(ids, id) + } + sort.Sort(int32Slice(ids)) + + for _, extNum := range ids { + ext := m[extNum] + var desc *ExtensionDesc + if emap != nil { + desc = emap[extNum] + } + if desc == nil { + // Unknown extension. + writeUnknownStruct(w, ext.enc) + continue + } + + pb, err := GetExtension(ep, desc) + if err != nil { + fmt.Fprintln(os.Stderr, "proto: failed getting extension: ", err) + continue + } + + fmt.Fprintf(w, "[%s]:", desc.Name) + if !w.compact { + w.WriteByte(' ') + } + writeAny(w, reflect.ValueOf(pb), nil) + w.WriteByte('\n') + } +} + +func marshalText(w io.Writer, pb interface{}, compact bool) { + if pb == nil { + w.Write([]byte("")) + return + } + aw := new(textWriter) + aw.writer = w + aw.complete = true + aw.compact = compact + + v := reflect.ValueOf(pb) + // We should normally be passed a struct, or a pointer to a struct, + // and we don't want the outer < and > in that case. + v = reflect.Indirect(v) + if v.Kind() == reflect.Struct { + writeStruct(aw, v) + } else { + writeAny(aw, v, nil) + } +} + +// MarshalText writes a given protocol buffer in text format. +// Values that are not protocol buffers can also be written, but their formatting is not guaranteed. +func MarshalText(w io.Writer, pb interface{}) { marshalText(w, pb, false) } + +// CompactText writes a given protocl buffer in compact text format (one line). +// Values that are not protocol buffers can also be written, but their formatting is not guaranteed. +func CompactText(w io.Writer, pb interface{}) { marshalText(w, pb, true) } + +// CompactTextString is the same as CompactText, but returns the string directly. +func CompactTextString(pb interface{}) string { + buf := new(bytes.Buffer) + marshalText(buf, pb, true) + return buf.String() +} diff --git a/proto/text_parser.go b/proto/text_parser.go new file mode 100644 index 0000000..f78adf5 --- /dev/null +++ b/proto/text_parser.go @@ -0,0 +1,458 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto + +// Functions for parsing the Text protocol buffer format. +// TODO: message sets, extensions. + +import ( + "fmt" + "os" + "reflect" + "strconv" +) + +// ParseError satisfies the os.Error interface. +type ParseError struct { + Message string + Line int // 1-based line number + Offset int // 0-based byte offset from start of input +} + +func (p *ParseError) String() string { + if p.Line == 1 { + // show offset only for first line + return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message) + } + return fmt.Sprintf("line %d: %v", p.Line, p.Message) +} + +type token struct { + value string + err *ParseError + line int // line number + offset int // byte number from start of input, not start of line + unquoted string // the unquoted version of value, if it was a quoted string +} + +func (t *token) String() string { + if t.err == nil { + return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset) + } + return fmt.Sprintf("parse error: %v", t.err) +} + +type textParser struct { + s string // remaining input + done bool // whether the parsing is finished (success or error) + backed bool // whether back() was called + offset, line int + cur token +} + +func newTextParser(s string) *textParser { + p := new(textParser) + p.s = s + p.line = 1 + p.cur.line = 1 + return p +} + +func (p *textParser) errorf(format string, a ...interface{}) *ParseError { + pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset} + p.cur.err = pe + p.done = true + return pe +} + +// Numbers and identifiers are matched by [-+._A-Za-z0-9] +func isIdentOrNumberChar(c byte) bool { + switch { + case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z': + return true + case '0' <= c && c <= '9': + return true + } + switch c { + case '-', '+', '.', '_': + return true + } + return false +} + +func isWhitespace(c byte) bool { + switch c { + case ' ', '\t', '\n', '\r': + return true + } + return false +} + +func (p *textParser) skipWhitespace() { + i := 0 + for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') { + if p.s[i] == '#' { + // comment; skip to end of line or input + for i < len(p.s) && p.s[i] != '\n' { + i++ + } + if i == len(p.s) { + break + } + } + if p.s[i] == '\n' { + p.line++ + } + i++ + } + p.offset += i + p.s = p.s[i:len(p.s)] + if len(p.s) == 0 { + p.done = true + } +} + +func (p *textParser) advance() { + // Skip whitespace + p.skipWhitespace() + if p.done { + return + } + + // Start of non-whitespace + p.cur.err = nil + p.cur.offset, p.cur.line = p.offset, p.line + p.cur.unquoted = "" + switch p.s[0] { + case '<', '>', '{', '}', ':': + // Single symbol + p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] + case '"': + // Quoted string + i := 1 + for i < len(p.s) && p.s[i] != '"' && p.s[i] != '\n' { + if p.s[i] == '\\' && i+1 < len(p.s) { + // skip escaped char + i++ + } + i++ + } + if i >= len(p.s) || p.s[i] != '"' { + p.errorf("unmatched quote") + return + } + // TODO: Should be UnquoteC. + unq, err := strconv.Unquote(p.s[0 : i+1]) + if err != nil { + p.errorf("invalid quoted string %v", p.s[0:i+1]) + return + } + p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)] + p.cur.unquoted = unq + default: + i := 0 + for i < len(p.s) && isIdentOrNumberChar(p.s[i]) { + i++ + } + if i == 0 { + p.errorf("unexpected byte %#x", p.s[0]) + return + } + p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)] + } + p.offset += len(p.cur.value) +} + +// Back off the parser by one token. Can only be done between calls to next(). +// It makes the next advance() a no-op. +func (p *textParser) back() { p.backed = true } + +// Advances the parser and returns the new current token. +func (p *textParser) next() *token { + if p.backed || p.done { + p.backed = false + return &p.cur + } + p.advance() + if p.done { + p.cur.value = "" + } else if len(p.cur.value) > 0 && p.cur.value[0] == '"' { + // Look for multiple quoted strings separated by whitespace, + // and concatenate them. + cat := p.cur + for { + p.skipWhitespace() + if p.done || p.s[0] != '"' { + break + } + p.advance() + if p.cur.err != nil { + return &p.cur + } + cat.value += " " + p.cur.value + cat.unquoted += p.cur.unquoted + } + p.done = false // parser may have seen EOF, but we want to return cat + p.cur = cat + } + return &p.cur +} + +// Return an error indicating which required field was not set. +func (p *textParser) missingRequiredFieldError(sv reflect.Value) *ParseError { + st := sv.Type() + sprops := GetProperties(st) + for i := 0; i < st.NumField(); i++ { + if !isNil(sv.Field(i)) { + continue + } + + props := sprops.Prop[i] + if props.Required { + return p.errorf("message %v missing required field %q", st, props.OrigName) + } + } + return p.errorf("message %v missing required field", st) // should not happen +} + +// Returns the index in the struct for the named field, as well as the parsed tag properties. +func structFieldByName(st reflect.Type, name string) (int, *Properties, bool) { + sprops := GetProperties(st) + i, ok := sprops.origNames[name] + if ok { + return i, sprops.Prop[i], true + } + return -1, nil, false +} + +func (p *textParser) readStruct(sv reflect.Value, terminator string) *ParseError { + st := sv.Type() + reqCount := GetProperties(st).reqCount + // A struct is a sequence of "name: value", terminated by one of + // '>' or '}', or the end of the input. + for { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == terminator { + break + } + + fi, props, ok := structFieldByName(st, tok.value) + if !ok { + return p.errorf("unknown field name %q in %v", tok.value, st) + } + + // Check that it's not already set if it's not a repeated field. + if !props.Repeated && !isNil(sv.Field(fi)) { + return p.errorf("non-repeated field %q was repeated", tok.value) + } + + tok = p.next() + if tok.err != nil { + return tok.err + } + if tok.value != ":" { + // Colon is optional when the field is a group or message. + needColon := true + switch props.Wire { + case "group": + needColon = false + case "bytes": + // A "bytes" field is either a message, a string, or a repeated field; + // those three become *T, *string and []T respectively, so we can check for + // this field being a pointer to a non-string. + typ := st.Field(fi).Type + if typ.Kind() == reflect.Ptr { + // *T or *string + if typ.Elem().Kind() == reflect.String { + break + } + } else if typ.Kind() == reflect.Slice { + // []T or []*T + if typ.Elem().Kind() != reflect.Ptr { + break + } + } + needColon = false + } + if needColon { + return p.errorf("expected ':', found %q", tok.value) + } + p.back() + } + + // Parse into the field. + if err := p.readAny(sv.Field(fi), props); err != nil { + return err + } + + if props.Required { + reqCount-- + } + } + + if reqCount > 0 { + return p.missingRequiredFieldError(sv) + } + return nil +} + +const ( + minInt32 = -1 << 31 + maxInt32 = 1<<31 - 1 + maxUint32 = 1<<32 - 1 +) + +func (p *textParser) readAny(v reflect.Value, props *Properties) *ParseError { + tok := p.next() + if tok.err != nil { + return tok.err + } + if tok.value == "" { + return p.errorf("unexpected EOF") + } + + switch fv := v; fv.Kind() { + case reflect.Slice: + at := v.Type() + if at.Elem().Kind() == reflect.Uint8 { + // Special case for []byte + if tok.value[0] != '"' { + // Deliberately written out here, as the error after + // this switch statement would write "invalid []byte: ...", + // which is not as user-friendly. + return p.errorf("invalid string: %v", tok.value) + } + bytes := []byte(tok.unquoted) + fv.Set(reflect.ValueOf(bytes)) + return nil + } + // Repeated field. May already exist. + flen := fv.Len() + if flen == fv.Cap() { + nav := reflect.MakeSlice(at, flen, 2*flen+1) + reflect.Copy(nav, fv) + fv.Set(nav) + } + fv.SetLen(flen + 1) + + // Read one. + p.back() + return p.readAny(fv.Index(flen), nil) // TODO: pass properties? + case reflect.Bool: + // Either "true", "false", 1 or 0. + switch tok.value { + case "true", "1": + fv.SetBool(true) + return nil + case "false", "0": + fv.SetBool(false) + return nil + } + case reflect.Float32, reflect.Float64: + if f, err := strconv.AtofN(tok.value, fv.Type().Bits()); err == nil { + fv.SetFloat(f) + return nil + } + case reflect.Int32: + if x, err := strconv.Atoi64(tok.value); err == nil && minInt32 <= x && x <= maxInt32 { + fv.SetInt(x) + return nil + } + if len(props.Enum) == 0 { + break + } + m, ok := enumValueMaps[props.Enum] + if !ok { + break + } + x, ok := m[tok.value] + if !ok { + break + } + fv.SetInt(int64(x)) + return nil + case reflect.Int64: + if x, err := strconv.Atoi64(tok.value); err == nil { + fv.SetInt(x) + return nil + } + case reflect.Ptr: + // A basic field (indirected through pointer), or a repeated message/group + p.back() + fv.Set(reflect.New(fv.Type().Elem())) + return p.readAny(fv.Elem(), props) + case reflect.String: + if tok.value[0] == '"' { + fv.SetString(tok.unquoted) + return nil + } + case reflect.Struct: + var terminator string + switch tok.value { + case "{": + terminator = "}" + case "<": + terminator = ">" + default: + return p.errorf("expected '{' or '<', found %q", tok.value) + } + return p.readStruct(fv, terminator) + case reflect.Uint32: + if x, err := strconv.Atoui64(tok.value); err == nil && x <= maxUint32 { + fv.SetUint(uint64(x)) + return nil + } + case reflect.Uint64: + if x, err := strconv.Atoui64(tok.value); err == nil { + fv.SetUint(x) + return nil + } + } + return p.errorf("invalid %v: %v", v.Type(), tok.value) +} + +var notPtrStruct os.Error = &ParseError{"destination is not a pointer to a struct", 0, 0} + +// UnmarshalText reads a protobuffer in Text format. +func UnmarshalText(s string, pb interface{}) os.Error { + v := reflect.ValueOf(pb) + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return notPtrStruct + } + if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil { + return pe + } + return nil +} diff --git a/proto/text_parser_test.go b/proto/text_parser_test.go new file mode 100644 index 0000000..4e3f4e9 --- /dev/null +++ b/proto/text_parser_test.go @@ -0,0 +1,293 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto_test + +import ( + . "goprotobuf.googlecode.com/hg/proto" + . "./testdata/_obj/test_proto" + "reflect" + "testing" +) + +type UnmarshalTextTest struct { + in string + error string // if "", no error expected + out *MyMessage +} + +var unMarshalTextTests = []UnmarshalTextTest{ + // Basic + { + in: " count:42\n name:\"Dave\" ", + out: &MyMessage{ + Count: Int32(42), + Name: String("Dave"), + }, + }, + + // Empty quoted string + { + in: `count:42 name:""`, + out: &MyMessage{ + Count: Int32(42), + Name: String(""), + }, + }, + + // Quoted string concatenation + { + in: `count:42 name: "My name is "` + "\n" + `"elsewhere"`, + out: &MyMessage{ + Count: Int32(42), + Name: String("My name is elsewhere"), + }, + }, + + // Bad quoted string + { + in: `inner: < host: "\0" >` + "\n", + error: `line 1.15: invalid quoted string "\0"`, + }, + + // Number too large for int64 + { + in: "count: 123456789012345678901", + error: "line 1.7: invalid int32: 123456789012345678901", + }, + + // Number too large for int32 + { + in: "count: 1234567890123", + error: "line 1.7: invalid int32: 1234567890123", + }, + + // Number too large for float32 + { + in: "others:< weight: 12345678901234567890123456789012345678901234567890 >", + error: "line 1.17: invalid float32: 12345678901234567890123456789012345678901234567890", + }, + + // Number posing as a quoted string + { + in: `inner: < host: 12 >` + "\n", + error: `line 1.15: invalid string: 12`, + }, + + // Quoted string posing as int32 + { + in: `count: "12"`, + error: `line 1.7: invalid int32: "12"`, + }, + + // Quoted string posing a float32 + { + in: `others:< weight: "17.4" >`, + error: `line 1.17: invalid float32: "17.4"`, + }, + + // Enum + { + in: `count:42 bikeshed: BLUE`, + out: &MyMessage{ + Count: Int32(42), + Bikeshed: NewMyMessage_Color(MyMessage_BLUE), + }, + }, + + // Repeated field + { + in: `count:42 pet: "horsey" pet:"bunny"`, + out: &MyMessage{ + Count: Int32(42), + Pet: []string{"horsey", "bunny"}, + }, + }, + + // Repeated message with/without colon and <>/{} + { + in: `count:42 others:{} others{} others:<> others:{}`, + out: &MyMessage{ + Count: Int32(42), + Others: []*OtherMessage{ + &OtherMessage{}, + &OtherMessage{}, + &OtherMessage{}, + &OtherMessage{}, + }, + }, + }, + + // Missing colon for inner message + { + in: `count:42 inner < host: "cauchy.syd" >`, + out: &MyMessage{ + Count: Int32(42), + Inner: &InnerMessage{ + Host: String("cauchy.syd"), + }, + }, + }, + + // Missing colon for string field + { + in: `name "Dave"`, + error: `line 1.5: expected ':', found "\"Dave\""`, + }, + + // Missing colon for int32 field + { + in: `count 42`, + error: `line 1.6: expected ':', found "42"`, + }, + + // Missing required field + { + in: ``, + error: `line 1.0: message test_proto.MyMessage missing required field "count"`, + }, + + // Repeated non-repeated field + { + in: `name: "Rob" name: "Russ"`, + error: `line 1.12: non-repeated field "name" was repeated`, + }, + + // Group + { + in: `count: 17 SomeGroup { group_field: 12 }`, + out: &MyMessage{ + Count: Int32(17), + Somegroup: &MyMessage_SomeGroup{ + GroupField: Int32(12), + }, + }, + }, + + // Big all-in-one + { + in: "count:42 # Meaning\n" + + `name:"Dave" ` + + `quote:"\"I didn't want to go.\"" ` + + `pet:"bunny" ` + + `pet:"kitty" ` + + `pet:"horsey" ` + + `inner:<` + + ` host:"footrest.syd" ` + + ` port:7001 ` + + ` connected:true ` + + `> ` + + `others:<` + + ` key:3735928559 ` + + ` value:"\x01A\a\f" ` + + `> ` + + `others:<` + + " weight:58.9 # Atomic weight of Co\n" + + ` inner:<` + + ` host:"lesha.mtv" ` + + ` port:8002 ` + + ` >` + + `>`, + out: &MyMessage{ + Count: Int32(42), + Name: String("Dave"), + Quote: String(`"I didn't want to go."`), + Pet: []string{"bunny", "kitty", "horsey"}, + Inner: &InnerMessage{ + Host: String("footrest.syd"), + Port: Int32(7001), + Connected: Bool(true), + }, + Others: []*OtherMessage{ + &OtherMessage{ + Key: Int64(3735928559), + Value: []byte{0x1, 'A', '\a', '\f'}, + }, + &OtherMessage{ + Weight: Float32(58.9), + Inner: &InnerMessage{ + Host: String("lesha.mtv"), + Port: Int32(8002), + }, + }, + }, + }, + }, +} + +func TestUnmarshalText(t *testing.T) { + for i, test := range unMarshalTextTests { + pb := new(MyMessage) + err := UnmarshalText(test.in, pb) + if test.error == "" { + // We don't expect failure. + if err != nil { + t.Errorf("Test %d: Unexpected error: %v", i, err) + } else if !reflect.DeepEqual(pb, test.out) { + t.Errorf("Test %d: Incorrect populated \n"+ + "Have: %v\nWant: %v", + i, CompactTextString(pb), CompactTextString(test.out)) + } + } else { + // We do expect failure. + if err == nil { + t.Errorf("Test %d: Didn't get expected error: %v", i, test.error) + } else if err.String() != test.error { + t.Errorf("Test %d: Incorrect error.\nHave: %v\nWant: %v", + i, err.String(), test.error) + } + } + } +} + +var benchInput string + +func init() { + benchInput = "count: 4\n" + for i := 0; i < 1000; i++ { + benchInput += "pet: \"fido\"\n" + } + + // Check it is valid input. + pb := new(MyMessage) + err := UnmarshalText(benchInput, pb) + if err != nil { + panic("Bad benchmark input: " + err.String()) + } +} + +func BenchmarkUnmarshalText(b *testing.B) { + pb := new(MyMessage) + for i := 0; i < b.N; i++ { + UnmarshalText(benchInput, pb) + } + b.SetBytes(int64(len(benchInput))) +} diff --git a/proto/text_test.go b/proto/text_test.go new file mode 100644 index 0000000..81aaca6 --- /dev/null +++ b/proto/text_test.go @@ -0,0 +1,225 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/goprotobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package proto_test + +import ( + "bytes" + "strings" + "testing" + + "goprotobuf.googlecode.com/hg/proto" + + pb "./testdata/_obj/test_proto" +) + +func newTestMessage() *pb.MyMessage { + msg := &pb.MyMessage{ + Count: proto.Int32(42), + Name: proto.String("Dave"), + Quote: proto.String(`"I didn't want to go."`), + Pet: []string{"bunny", "kitty", "horsey"}, + Inner: &pb.InnerMessage{ + Host: proto.String("footrest.syd"), + Port: proto.Int32(7001), + Connected: proto.Bool(true), + }, + Others: []*pb.OtherMessage{ + &pb.OtherMessage{ + Key: proto.Int64(0xdeadbeef), + Value: []byte{1, 65, 7, 12}, + }, + &pb.OtherMessage{ + Weight: proto.Float32(6.022), + Inner: &pb.InnerMessage{ + Host: proto.String("lesha.mtv"), + Port: proto.Int32(8002), + }, + }, + }, + Bikeshed: pb.NewMyMessage_Color(pb.MyMessage_BLUE), + Somegroup: &pb.MyMessage_SomeGroup{ + GroupField: proto.Int32(8), + }, + // One normally wouldn't do this. + // This is an undeclared tag 13, as a varint (wire type 0) with value 4. + XXX_unrecognized: []byte{13<<3 | 0, 4}, + } + ext := &pb.Ext{ + Data: proto.String("Big gobs for big rats"), + } + if err := proto.SetExtension(msg, pb.E_Ext_More, ext); err != nil { + panic(err) + } + + // Add an unknown extension. We marshal a pb.Ext, and fake the ID. + b, err := proto.Marshal(&pb.Ext{Data: proto.String("3G skiing")}) + if err != nil { + panic(err) + } + b = append(proto.EncodeVarint(104<<3|proto.WireBytes), b...) + proto.SetRawExtension(msg, 104, b) + + // Extensions can be plain fields, too, so let's test that. + b = append(proto.EncodeVarint(105<<3|proto.WireVarint), 19) + proto.SetRawExtension(msg, 105, b) + + return msg +} + +const text = `count: 42 +name: "Dave" +quote: "\"I didn't want to go.\"" +pet: "bunny" +pet: "kitty" +pet: "horsey" +inner: < + host: "footrest.syd" + port: 7001 + connected: true +> +others: < + key: 3735928559 + value: "\001A\007\014" +> +others: < + weight: 6.022 + inner: < + host: "lesha.mtv" + port: 8002 + > +> +bikeshed: BLUE +SomeGroup { + group_field: 8 +} +/* 2 unknown bytes */ +tag13: 4 +[test_proto.Ext.more]: < + data: "Big gobs for big rats" +> +/* 13 unknown bytes */ +tag104: "\t3G skiing" +/* 3 unknown bytes */ +tag105: 19 +` + +func TestMarshalTextFull(t *testing.T) { + buf := new(bytes.Buffer) + proto.MarshalText(buf, newTestMessage()) + s := buf.String() + if s != text { + t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v===\n", s, text) + } +} + +func compact(src string) string { + // s/[ \n]+/ /g; s/ $//; + dst := make([]byte, len(src)) + space, comment := false, false + j := 0 + for i := 0; i < len(src); i++ { + if strings.HasPrefix(src[i:], "/*") { + comment = true + i++ + continue + } + if comment && strings.HasPrefix(src[i:], "*/") { + comment = false + i++ + continue + } + if comment { + continue + } + c := src[i] + if c == ' ' || c == '\n' { + space = true + continue + } + if j > 0 && (dst[j-1] == ':' || dst[j-1] == '<' || dst[j-1] == '{') { + space = false + } + if c == '{' { + space = false + } + if space { + dst[j] = ' ' + j++ + space = false + } + dst[j] = c + j++ + } + if space { + dst[j] = ' ' + j++ + } + return string(dst[0:j]) +} + +var compactText = compact(text) + +func TestCompactText(t *testing.T) { + s := proto.CompactTextString(newTestMessage()) + if s != compactText { + t.Errorf("Got:\n===\n%v===\nExpected:\n===\n%v\n===\n", s, compactText) + } +} + +func TestStringEscaping(t *testing.T) { + testCases := []struct { + in *pb.Strings + out string + }{ + { + // Test data from C++ test (TextFormatTest.StringEscape). + // Single divergence: we don't escape apostrophes. + &pb.Strings{StringField: proto.String("\"A string with ' characters \n and \r newlines and \t tabs and \001 slashes \\ and multiple spaces")}, + "string_field: \"\\\"A string with ' characters \\n and \\r newlines and \\t tabs and \\001 slashes \\\\ and multiple spaces\"\n", + }, + { + // Test data from the same C++ test. + &pb.Strings{StringField: proto.String("\350\260\267\346\255\214")}, + "string_field: \"\\350\\260\\267\\346\\255\\214\"\n", + }, + } + + for i, tc := range testCases { + var buf bytes.Buffer + proto.MarshalText(&buf, tc.in) + if s := buf.String(); s != tc.out { + t.Errorf("#%d: Got:\n%s\nExpected:\n%s\n", i, s, tc.out) + } + } +} + +