From c3766ed0969f7780785c14d49b077f98ff812f01 Mon Sep 17 00:00:00 2001 From: Steven McDonald Date: Mon, 3 Oct 2011 00:33:36 +1100 Subject: [PATCH] initialise --- .hg_archival.txt | 5 + .hgignore | 20 + .hgtags | 16 + CONTRIBUTORS | 14 + LICENSE | 31 + Make.protobuf | 46 + Makefile | 65 + README | 162 +++ compiler/Makefile | 44 + compiler/descriptor/Makefile | 54 + compiler/descriptor/descriptor.pb.go | 485 ++++++++ compiler/descriptor/descriptor.pb.golden | 485 ++++++++ compiler/doc.go | 51 + compiler/generator/Makefile | 40 + compiler/generator/generator.go | 1425 ++++++++++++++++++++++ compiler/main.go | 89 ++ compiler/plugin/Makefile | 56 + compiler/plugin/plugin.pb.go | 47 + compiler/plugin/plugin.pb.golden | 47 + compiler/testdata/Makefile | 68 ++ compiler/testdata/extension_base.proto | 44 + compiler/testdata/extension_extra.proto | 38 + compiler/testdata/extension_test.go | 205 ++++ compiler/testdata/extension_user.proto | 89 ++ compiler/testdata/imp.pb.go.golden | 107 ++ compiler/testdata/imp.proto | 50 + compiler/testdata/imp2.proto | 35 + compiler/testdata/main.go | 44 + compiler/testdata/multi1.proto | 42 + compiler/testdata/multi2.proto | 44 + compiler/testdata/multi3.proto | 41 + compiler/testdata/test.pb.go.golden | 243 ++++ compiler/testdata/test.proto | 101 ++ lib/codereview/codereview.cfg | 1 + proto/Makefile | 58 + proto/all_test.go | 1373 +++++++++++++++++++++ proto/clone.go | 122 ++ proto/clone_test.go | 80 ++ proto/decode.go | 773 ++++++++++++ proto/encode.go | 598 +++++++++ proto/equal.go | 211 ++++ proto/equal_test.go | 128 ++ proto/extensions.go | 276 +++++ proto/lib.go | 770 ++++++++++++ proto/message_set.go | 202 +++ proto/properties.go | 573 +++++++++ proto/testdata/Makefile | 53 + proto/testdata/test.pb.go.golden | 393 ++++++ proto/testdata/test.proto | 289 +++++ proto/text.go | 435 +++++++ proto/text_parser.go | 458 +++++++ proto/text_parser_test.go | 293 +++++ proto/text_test.go | 225 ++++ 53 files changed, 11644 insertions(+) create mode 100644 .hg_archival.txt create mode 100644 .hgignore create mode 100644 .hgtags create mode 100644 CONTRIBUTORS create mode 100644 LICENSE create mode 100644 Make.protobuf create mode 100644 Makefile create mode 100644 README create mode 100644 compiler/Makefile create mode 100644 compiler/descriptor/Makefile create mode 100644 compiler/descriptor/descriptor.pb.go create mode 100644 compiler/descriptor/descriptor.pb.golden create mode 100644 compiler/doc.go create mode 100644 compiler/generator/Makefile create mode 100644 compiler/generator/generator.go create mode 100644 compiler/main.go create mode 100644 compiler/plugin/Makefile create mode 100644 compiler/plugin/plugin.pb.go create mode 100644 compiler/plugin/plugin.pb.golden create mode 100644 compiler/testdata/Makefile create mode 100644 compiler/testdata/extension_base.proto create mode 100644 compiler/testdata/extension_extra.proto create mode 100644 compiler/testdata/extension_test.go create mode 100644 compiler/testdata/extension_user.proto create mode 100644 compiler/testdata/imp.pb.go.golden create mode 100644 compiler/testdata/imp.proto create mode 100644 compiler/testdata/imp2.proto create mode 100644 compiler/testdata/main.go create mode 100644 compiler/testdata/multi1.proto create mode 100644 compiler/testdata/multi2.proto create mode 100644 compiler/testdata/multi3.proto create mode 100644 compiler/testdata/test.pb.go.golden create mode 100644 compiler/testdata/test.proto create mode 100644 lib/codereview/codereview.cfg create mode 100644 proto/Makefile create mode 100644 proto/all_test.go create mode 100644 proto/clone.go create mode 100644 proto/clone_test.go create mode 100644 proto/decode.go create mode 100644 proto/encode.go create mode 100644 proto/equal.go create mode 100644 proto/equal_test.go create mode 100644 proto/extensions.go create mode 100644 proto/lib.go create mode 100644 proto/message_set.go create mode 100644 proto/properties.go create mode 100644 proto/testdata/Makefile create mode 100644 proto/testdata/test.pb.go.golden create mode 100644 proto/testdata/test.proto create mode 100644 proto/text.go create mode 100644 proto/text_parser.go create mode 100644 proto/text_parser_test.go create mode 100644 proto/text_test.go 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) + } + } +} + + -- 2.30.2