goprotobuf: sync from Google internal version.
Interesting changes:
- String method on protocol buffer message types
that renders the message in compact text format.
- Extension text formatting.
R=r
CC=golang-dev
http://codereview.appspot.com/4643050
diff --git a/compiler/generator/generator.go b/compiler/generator/generator.go
index 94d3768..884fef1 100644
--- a/compiler/generator/generator.go
+++ b/compiler/generator/generator.go
@@ -195,6 +195,17 @@
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, "_")
+}
+
// 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.
@@ -244,6 +255,7 @@
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() }")
@@ -892,7 +904,8 @@
if field.Options != nil && proto.GetBool(field.Options.Packed) {
packed = ",packed"
}
- name := proto.GetString(field.Name)
+ 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.
@@ -902,8 +915,8 @@
if i := strings.LastIndex(name, "."); i >= 0 {
name = name[i+1:]
}
- name = ",name=" + name
- } else if name == CamelCase(name) {
+ }
+ if name == CamelCase(fieldName) {
name = ""
} else {
name = ",name=" + name
@@ -1028,12 +1041,9 @@
g.Out()
g.P("}")
- // Reset function
- g.P("func (this *", ccTypeName, ") Reset() {")
- g.In()
- g.P("*this = ", ccTypeName, "{}")
- 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
@@ -1140,13 +1150,7 @@
}
func (g *Generator) generateExtension(ext *ExtensionDescriptor) {
- // The full type name
- typeName := ext.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)
- }
- ccTypeName := "E_" + strings.Join(typeName, "_")
+ ccTypeName := ext.DescName()
extendedType := "*" + g.TypeName(g.ObjectNamed(*ext.Extendee))
field := ext.FieldDescriptorProto
@@ -1163,6 +1167,7 @@
g.P("ExtendedType: (", extendedType, ")(nil),")
g.P("ExtensionType: (", fieldType, ")(nil),")
g.P("Field: ", field.Number, ",")
+ g.P(`Name: "`, g.packageName, ".", *field.Name, `",`)
g.P("Tag: ", tag, ",")
g.Out()
@@ -1178,6 +1183,14 @@
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("}")
}
@@ -1191,6 +1204,10 @@
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?
diff --git a/compiler/testdata/Makefile b/compiler/testdata/Makefile
index 8238870..f1dcd50 100644
--- a/compiler/testdata/Makefile
+++ b/compiler/testdata/Makefile
@@ -45,7 +45,7 @@
golden:
$(QUOTED_GOBIN)/gomake -B test.pb.go
- diff test.pb.go test.pb.go.golden
+ diff -w test.pb.go test.pb.go.golden
nuke: clean
diff --git a/compiler/testdata/test.pb.go.golden b/compiler/testdata/test.pb.go.golden
index ccd0f09..50b2f70 100644
--- a/compiler/testdata/test.pb.go.golden
+++ b/compiler/testdata/test.pb.go.golden
@@ -17,17 +17,17 @@
type HatType int32
const (
- HatType_FEDORA = 1
- HatType_FEZ = 2
+ HatType_FEDORA = 1
+ HatType_FEZ = 2
)
var HatType_name = map[int32]string{
- 1: "FEDORA",
- 2: "FEZ",
+ 1: "FEDORA",
+ 2: "FEZ",
}
var HatType_value = map[string]int32{
- "FEDORA": 1,
- "FEZ": 2,
+ "FEDORA": 1,
+ "FEZ": 2,
}
func NewHatType(x int32) *HatType {
@@ -41,20 +41,20 @@
type Days int32
const (
- Days_MONDAY = 1
- Days_TUESDAY = 2
- Days_LUNDI = 1
+ Days_MONDAY = 1
+ Days_TUESDAY = 2
+ Days_LUNDI = 1
)
var Days_name = map[int32]string{
- 1: "MONDAY",
- 2: "TUESDAY",
+ 1: "MONDAY",
+ 2: "TUESDAY",
// Duplicate value: 1: "LUNDI",
}
var Days_value = map[string]int32{
- "MONDAY": 1,
- "TUESDAY": 2,
- "LUNDI": 1,
+ "MONDAY": 1,
+ "TUESDAY": 2,
+ "LUNDI": 1,
}
func NewDays(x int32) *Days {
@@ -68,20 +68,20 @@
type Request_Color int32
const (
- Request_RED = 0
- Request_GREEN = 1
- Request_BLUE = 2
+ Request_RED = 0
+ Request_GREEN = 1
+ Request_BLUE = 2
)
var Request_Color_name = map[int32]string{
- 0: "RED",
- 1: "GREEN",
- 2: "BLUE",
+ 0: "RED",
+ 1: "GREEN",
+ 2: "BLUE",
}
var Request_Color_value = map[string]int32{
- "RED": 0,
- "GREEN": 1,
- "BLUE": 2,
+ "RED": 0,
+ "GREEN": 1,
+ "BLUE": 2,
}
func NewRequest_Color(x int32) *Request_Color {
@@ -95,17 +95,17 @@
type Reply_Entry_Game int32
const (
- Reply_Entry_FOOTBALL = 1
- Reply_Entry_TENNIS = 2
+ Reply_Entry_FOOTBALL = 1
+ Reply_Entry_TENNIS = 2
)
var Reply_Entry_Game_name = map[int32]string{
- 1: "FOOTBALL",
- 2: "TENNIS",
+ 1: "FOOTBALL",
+ 2: "TENNIS",
}
var Reply_Entry_Game_value = map[string]int32{
- "FOOTBALL": 1,
- "TENNIS": 2,
+ "FOOTBALL": 1,
+ "TENNIS": 2,
}
func NewReply_Entry_Game(x int32) *Reply_Entry_Game {
@@ -117,43 +117,40 @@
}
type Request struct {
- Key []int64 "PB(varint,1,rep,name=key)"
- ImportedMessage *imp.ImportedMessage "PB(bytes,2,opt,name=imported_message)"
- Hue *Request_Color "PB(varint,3,opt,name=hue,enum=my_test.Request_Color)"
- Hat *HatType "PB(varint,4,opt,name=hat,enum=my_test.HatType,def=1)"
- Owner *imp.ImportedMessage_Owner "PB(varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner)"
- Deadline *float32 "PB(fixed32,7,opt,name=deadline,def=inf)"
- Somegroup *Request_SomeGroup "PB(group,8,opt,name=SomeGroup)"
- XXX_unrecognized []byte
+ Key []int64 "PB(varint,1,rep,name=key)"
+ ImportedMessage *imp.ImportedMessage "PB(bytes,2,opt,name=imported_message)"
+ Hue *Request_Color "PB(varint,3,opt,name=hue,enum=my_test.Request_Color)"
+ Hat *HatType "PB(varint,4,opt,name=hat,enum=my_test.HatType,def=1)"
+ Owner *imp.ImportedMessage_Owner "PB(varint,6,opt,name=owner,enum=imp.ImportedMessage_Owner)"
+ Deadline *float32 "PB(fixed32,7,opt,name=deadline,def=inf)"
+ Somegroup *Request_SomeGroup "PB(group,8,opt,name=SomeGroup)"
+ XXX_unrecognized []byte
}
-func (this *Request) Reset() {
- *this = Request{}
-}
+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 "PB(varint,9,opt,name=group_field)"
- XXX_unrecognized []byte
+ GroupField *int32 "PB(varint,9,opt,name=group_field)"
+ XXX_unrecognized []byte
}
-func (this *Request_SomeGroup) Reset() {
- *this = Request_SomeGroup{}
-}
+func (this *Request_SomeGroup) Reset() { *this = Request_SomeGroup{} }
+func (this *Request_SomeGroup) String() string { return proto.CompactTextString(this) }
type Reply struct {
- Found []*Reply_Entry "PB(bytes,1,rep,name=found)"
- CompactKeys []int32 "PB(varint,2,rep,packed,name=compact_keys)"
- XXX_extensions map[int32][]byte
- XXX_unrecognized []byte
+ Found []*Reply_Entry "PB(bytes,1,rep,name=found)"
+ CompactKeys []int32 "PB(varint,2,rep,packed,name=compact_keys)"
+ XXX_extensions map[int32][]byte
+ XXX_unrecognized []byte
}
-func (this *Reply) Reset() {
- *this = Reply{}
-}
+func (this *Reply) Reset() { *this = Reply{} }
+func (this *Reply) String() string { return proto.CompactTextString(this) }
var extRange_Reply = []proto.ExtensionRange{
proto.ExtensionRange{100, 536870911},
@@ -170,15 +167,14 @@
}
type Reply_Entry struct {
- KeyThatNeeds_1234Camel_CasIng *int64 "PB(varint,1,req,name=key_that_needs_1234camel_CasIng)"
- Value *int64 "PB(varint,2,opt,name=value,def=7)"
- XMyFieldName_2 *int64 "PB(varint,3,opt,name=_my_field_name_2)"
- XXX_unrecognized []byte
+ KeyThatNeeds_1234Camel_CasIng *int64 "PB(varint,1,req,name=key_that_needs_1234camel_CasIng)"
+ Value *int64 "PB(varint,2,opt,name=value,def=7)"
+ XMyFieldName_2 *int64 "PB(varint,3,opt,name=_my_field_name_2)"
+ XXX_unrecognized []byte
}
-func (this *Reply_Entry) Reset() {
- *this = Reply_Entry{}
-}
+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
@@ -186,25 +182,24 @@
XXX_unrecognized []byte
}
-func (this *ReplyExtensions) Reset() {
- *this = ReplyExtensions{}
-}
+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,
- Tag: "PB(fixed64,101,opt,name=time)",
+ ExtendedType: (*Reply)(nil),
+ ExtensionType: (*float64)(nil),
+ Field: 101,
+ Name: "my_test.time",
+ Tag: "PB(fixed64,101,opt,name=time)",
}
type OldReply struct {
- XXX_extensions map[int32][]byte
- XXX_unrecognized []byte
+ XXX_extensions map[int32][]byte
+ XXX_unrecognized []byte
}
-func (this *OldReply) Reset() {
- *this = OldReply{}
-}
+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())
@@ -231,10 +226,11 @@
}
var E_Tag = &proto.ExtensionDesc{
- ExtendedType: (*Reply)(nil),
- ExtensionType: (*string)(nil),
- Field: 103,
- Tag: "PB(bytes,103,opt,name=tag)",
+ ExtendedType: (*Reply)(nil),
+ ExtensionType: (*string)(nil),
+ Field: 103,
+ Name: "my_test.tag",
+ Tag: "PB(bytes,103,opt,name=tag)",
}
func init() {
@@ -242,4 +238,6 @@
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/proto/all_test.go b/proto/all_test.go
index 71d7e13..eb32155 100644
--- a/proto/all_test.go
+++ b/proto/all_test.go
@@ -1141,6 +1141,13 @@
}
}
+// 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)
diff --git a/proto/encode.go b/proto/encode.go
index 5eab33b..7f242e1 100644
--- a/proto/encode.go
+++ b/proto/encode.go
@@ -94,11 +94,7 @@
// protocol buffer types.
func (p *Buffer) EncodeVarint(x uint64) os.Error {
l := len(p.buf)
- if l+maxVarintBytes > cap(p.buf) { // not necessary except for performance
- p.buf = append(p.buf, emptyBytes[:]...)
- } else {
- p.buf = p.buf[:l+maxVarintBytes]
- }
+ p.buf = append(p.buf, emptyBytes[:]...)
for x >= 1<<7 {
p.buf[l] = uint8(x&0x7f | 0x80)
@@ -116,11 +112,7 @@
func (p *Buffer) EncodeFixed64(x uint64) os.Error {
const fixed64Bytes = 8
l := len(p.buf)
- if l+fixed64Bytes > cap(p.buf) { // not necessary except for performance
- p.buf = append(p.buf, emptyBytes[:fixed64Bytes]...)
- } else {
- p.buf = p.buf[:l+fixed64Bytes]
- }
+ p.buf = append(p.buf, emptyBytes[:fixed64Bytes]...)
p.buf[l] = uint8(x)
p.buf[l+1] = uint8(x >> 8)
@@ -139,11 +131,7 @@
func (p *Buffer) EncodeFixed32(x uint64) os.Error {
const fixed32Bytes = 4
l := len(p.buf)
- if l+fixed32Bytes > cap(p.buf) { // not necessary except for performance
- p.buf = append(p.buf, emptyBytes[:fixed32Bytes]...)
- } else {
- p.buf = p.buf[:l+fixed32Bytes]
- }
+ p.buf = append(p.buf, emptyBytes[:fixed32Bytes]...)
p.buf[l] = uint8(x)
p.buf[l+1] = uint8(x >> 8)
diff --git a/proto/extensions.go b/proto/extensions.go
index fe71709..4594c8b 100644
--- a/proto/extensions.go
+++ b/proto/extensions.go
@@ -39,6 +39,7 @@
import (
"os"
"reflect"
+ "strconv"
"unsafe"
)
@@ -60,6 +61,7 @@
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 // PB(...) tag style
}
@@ -179,3 +181,22 @@
pb.ExtensionMap()[extension.Field] = p.buf
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
+}
diff --git a/proto/testdata/test.pb.go b/proto/testdata/test.pb.go
index 5e4a8c6..eea8b69 100644
--- a/proto/testdata/test.pb.go
+++ b/proto/testdata/test.pb.go
@@ -123,9 +123,8 @@
XXX_unrecognized []byte
}
-func (this *GoEnum) Reset() {
- *this = GoEnum{}
-}
+func (this *GoEnum) Reset() { *this = GoEnum{} }
+func (this *GoEnum) String() string { return proto.CompactTextString(this) }
type GoTestField struct {
Label *string "PB(bytes,1,req)"
@@ -133,9 +132,8 @@
XXX_unrecognized []byte
}
-func (this *GoTestField) Reset() {
- *this = GoTestField{}
-}
+func (this *GoTestField) Reset() { *this = GoTestField{} }
+func (this *GoTestField) String() string { return proto.CompactTextString(this) }
type GoTest struct {
Kind *int32 "PB(varint,1,req)"
@@ -213,9 +211,8 @@
XXX_unrecognized []byte
}
-func (this *GoTest) Reset() {
- *this = GoTest{}
-}
+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
@@ -238,27 +235,24 @@
XXX_unrecognized []byte
}
-func (this *GoTest_RequiredGroup) Reset() {
- *this = GoTest_RequiredGroup{}
-}
+func (this *GoTest_RequiredGroup) Reset() { *this = GoTest_RequiredGroup{} }
+func (this *GoTest_RequiredGroup) String() string { return proto.CompactTextString(this) }
type GoTest_RepeatedGroup struct {
RequiredField *string "PB(bytes,81,req)"
XXX_unrecognized []byte
}
-func (this *GoTest_RepeatedGroup) Reset() {
- *this = GoTest_RepeatedGroup{}
-}
+func (this *GoTest_RepeatedGroup) Reset() { *this = GoTest_RepeatedGroup{} }
+func (this *GoTest_RepeatedGroup) String() string { return proto.CompactTextString(this) }
type GoTest_OptionalGroup struct {
RequiredField *string "PB(bytes,91,req)"
XXX_unrecognized []byte
}
-func (this *GoTest_OptionalGroup) Reset() {
- *this = GoTest_OptionalGroup{}
-}
+func (this *GoTest_OptionalGroup) Reset() { *this = GoTest_OptionalGroup{} }
+func (this *GoTest_OptionalGroup) String() string { return proto.CompactTextString(this) }
type GoSkipTest struct {
SkipInt32 *int32 "PB(varint,11,req,name=skip_int32)"
@@ -269,9 +263,8 @@
XXX_unrecognized []byte
}
-func (this *GoSkipTest) Reset() {
- *this = GoSkipTest{}
-}
+func (this *GoSkipTest) Reset() { *this = GoSkipTest{} }
+func (this *GoSkipTest) String() string { return proto.CompactTextString(this) }
type GoSkipTest_SkipGroup struct {
GroupInt32 *int32 "PB(varint,16,req,name=group_int32)"
@@ -279,27 +272,24 @@
XXX_unrecognized []byte
}
-func (this *GoSkipTest_SkipGroup) Reset() {
- *this = GoSkipTest_SkipGroup{}
-}
+func (this *GoSkipTest_SkipGroup) Reset() { *this = GoSkipTest_SkipGroup{} }
+func (this *GoSkipTest_SkipGroup) String() string { return proto.CompactTextString(this) }
type NonPackedTest struct {
A []int32 "PB(varint,1,rep,name=a)"
XXX_unrecognized []byte
}
-func (this *NonPackedTest) Reset() {
- *this = NonPackedTest{}
-}
+func (this *NonPackedTest) Reset() { *this = NonPackedTest{} }
+func (this *NonPackedTest) String() string { return proto.CompactTextString(this) }
type PackedTest struct {
B []int32 "PB(varint,1,rep,packed,name=b)"
XXX_unrecognized []byte
}
-func (this *PackedTest) Reset() {
- *this = PackedTest{}
-}
+func (this *PackedTest) Reset() { *this = PackedTest{} }
+func (this *PackedTest) String() string { return proto.CompactTextString(this) }
type InnerMessage struct {
Host *string "PB(bytes,1,req,name=host)"
@@ -308,9 +298,8 @@
XXX_unrecognized []byte
}
-func (this *InnerMessage) Reset() {
- *this = InnerMessage{}
-}
+func (this *InnerMessage) Reset() { *this = InnerMessage{} }
+func (this *InnerMessage) String() string { return proto.CompactTextString(this) }
const Default_InnerMessage_Port int32 = 4000
@@ -322,9 +311,8 @@
XXX_unrecognized []byte
}
-func (this *OtherMessage) Reset() {
- *this = OtherMessage{}
-}
+func (this *OtherMessage) Reset() { *this = OtherMessage{} }
+func (this *OtherMessage) String() string { return proto.CompactTextString(this) }
type MyMessage struct {
Count *int32 "PB(varint,1,req,name=count)"
@@ -335,11 +323,25 @@
Others []*OtherMessage "PB(bytes,6,rep,name=others)"
Bikeshed *MyMessage_Color "PB(varint,7,opt,name=bikeshed,enum=test_proto.MyMessage_Color)"
Somegroup *MyMessage_SomeGroup "PB(group,8,opt,name=SomeGroup)"
+ XXX_extensions map[int32][]byte
XXX_unrecognized []byte
}
-func (this *MyMessage) Reset() {
- *this = MyMessage{}
+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 {
@@ -347,31 +349,45 @@
XXX_unrecognized []byte
}
-func (this *MyMessage_SomeGroup) Reset() {
- *this = MyMessage_SomeGroup{}
-}
+func (this *MyMessage_SomeGroup) Reset() { *this = MyMessage_SomeGroup{} }
+func (this *MyMessage_SomeGroup) String() string { return proto.CompactTextString(this) }
-type MessageList struct {
- Message []*MessageList_Message "PB(group,1,rep,name=Message)"
+type Ext struct {
+ Data *string "PB(bytes,1,opt,name=data)"
XXX_unrecognized []byte
}
-func (this *MessageList) Reset() {
- *this = MessageList{}
+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: "PB(bytes,103,opt,name=more)",
}
+type MessageList struct {
+ Message []*MessageList_Message "PB(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 "PB(bytes,2,req,name=name)"
Count *int32 "PB(varint,3,req,name=count)"
XXX_unrecognized []byte
}
-func (this *MessageList_Message) Reset() {
- *this = MessageList_Message{}
-}
+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
index 5002d33..b3f4bd6 100644
--- a/proto/testdata/test.proto
+++ b/proto/testdata/test.proto
@@ -226,6 +226,16 @@
optional group SomeGroup = 8 {
optional int32 group_field = 9;
}
+
+ extensions 100 to max;
+}
+
+message Ext {
+ extend MyMessage {
+ optional Ext more = 103;
+ }
+
+ optional string data = 1;
}
message MessageList {
diff --git a/proto/text.go b/proto/text.go
index 1ca46e8..bb32983 100644
--- a/proto/text.go
+++ b/proto/text.go
@@ -31,25 +31,26 @@
package proto
-// Functions for writing the Text protocol buffer format.
-// TODO: message sets, extensions.
+// Functions for writing the text protocol buffer format.
+// TODO: message sets.
import (
"bytes"
"fmt"
"io"
+ "log"
"os"
"reflect"
"strconv"
"strings"
)
-// An io.Writer wrapper that tracks its indentation level.
+// textWriter is an io.Writer that tracks its indentation level.
type textWriter struct {
- indent_level int
- complete bool // if the current position is a complete line
- compact bool // whether to write out as a one-liner
- writer io.Writer
+ 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
}
@@ -63,15 +64,15 @@
return
}
- for i := 0; i < len(frags); i++ {
+ for i, frag := range frags {
if w.complete {
- for j := 0; j < w.indent_level; j++ {
+ for j := 0; j < w.ind; j++ {
w.writer.Write([]byte{' ', ' '})
}
w.complete = false
}
- w.writer.Write([]byte(frags[i]))
+ w.writer.Write([]byte(frag))
if i+1 < len(frags) {
w.writer.Write([]byte{'\n'})
}
@@ -87,14 +88,14 @@
return err
}
-func (w *textWriter) indent() { w.indent_level++ }
+func (w *textWriter) indent() { w.ind++ }
func (w *textWriter) unindent() {
- if w.indent_level == 0 {
- fmt.Fprintln(os.Stderr, "proto: textWriter unindented too far!")
- } else {
- w.indent_level--
+ if w.ind == 0 {
+ log.Printf("proto: textWriter unindented too far!")
+ return
}
+ w.ind--
}
func writeName(w *textWriter, props *Properties) {
@@ -104,6 +105,8 @@
}
}
+var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
+
func writeStruct(w *textWriter, sv reflect.Value) {
st := sv.Type()
sprops := GetProperties(st)
@@ -125,15 +128,15 @@
}
if props.Repeated {
- if av := fv; av.Kind() == reflect.Slice {
+ if fv.Kind() == reflect.Slice {
// Repeated field.
- for j := 0; j < av.Len(); j++ {
+ for j := 0; j < fv.Len(); j++ {
writeName(w, props)
if !w.compact {
w.WriteByte(' ')
}
- writeAny(w, av.Index(j), props)
- fmt.Fprint(w, "\n")
+ writeAny(w, fv.Index(j), props)
+ w.WriteByte('\n')
}
continue
}
@@ -143,13 +146,23 @@
if !w.compact {
w.WriteByte(' ')
}
- if len(props.Enum) == 0 || !tryWriteEnum(w, props.Enum, fv) {
+ if props.Enum != "" && tryWriteEnum(w, props.Enum, fv) {
+ // Enum written.
+ } else {
writeAny(w, fv, props)
}
- fmt.Fprint(w, "\n")
+ w.WriteByte('\n')
+ }
+
+ // Extensions.
+ 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 {
@@ -167,24 +180,20 @@
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 val := v; val.Kind() {
+ switch v.Kind() {
case reflect.Slice:
// Should only be a []byte; repeated fields are handled in writeStruct.
- // TODO: Handle other cases more cleanly.
- bytes := make([]byte, val.Len())
- for i := 0; i < val.Len(); i++ {
- bytes[i] = byte(val.Index(i).Uint())
- }
- // TODO: Should be strconv.QuoteC, which doesn't exist yet
- fmt.Fprint(w, strconv.Quote(string(bytes)))
+ // TODO: Should be strconv.QuoteToASCII, which should be released after 2011-06-20.
+ fmt.Fprint(w, strconv.Quote(string(v.Interface().([]byte))))
case reflect.String:
- // TODO: Should be strconv.QuoteC, which doesn't exist yet
- fmt.Fprint(w, strconv.Quote(val.String()))
+ // TODO: Should be strconv.QuoteToASCII, which should be released after 2011-06-20.
+ fmt.Fprint(w, strconv.Quote(v.String()))
case reflect.Struct:
// Required/optional group/message.
var bra, ket byte = '<', '>'
@@ -196,11 +205,42 @@
w.WriteByte('\n')
}
w.indent()
- writeStruct(w, val)
+ writeStruct(w, v)
w.unindent()
w.WriteByte(ket)
default:
- fmt.Fprint(w, val.Interface())
+ fmt.Fprint(w, v.Interface())
+ }
+}
+
+// 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)
+ for extNum := range ep.ExtensionMap() {
+ var desc *ExtensionDesc
+ if emap != nil {
+ desc = emap[extNum]
+ }
+ if desc == nil {
+ // TODO: Handle printing unknown extensions.
+ fmt.Fprintln(os.Stderr, "proto: unknown extension: ", extNum)
+ 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')
}
}
@@ -225,12 +265,12 @@
}
}
-// MarshalText writes a given protobuffer in Text format.
-// Non-protobuffers can also be written, but their formatting is not guaranteed.
+// 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 protobuffer in compact Text format (one line).
-// Non-protobuffers can also be written, but their formatting is not guaranteed.
+// 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.
diff --git a/proto/text_test.go b/proto/text_test.go
index e31e21c..1427740 100644
--- a/proto/text_test.go
+++ b/proto/text_test.go
@@ -39,7 +39,7 @@
)
func newTestMessage() *MyMessage {
- return &MyMessage{
+ msg := &MyMessage{
Count: Int32(42),
Name: String("Dave"),
Quote: String(`"I didn't want to go."`),
@@ -67,6 +67,13 @@
GroupField: Int32(8),
},
}
+ ext := &Ext{
+ Data: String("Big gobs for big rats"),
+ }
+ if err := SetExtension(msg, E_Ext_More, ext); err != nil {
+ panic(err)
+ }
+ return msg
}
const text = `count: 42
@@ -95,6 +102,9 @@
SomeGroup {
group_field: 8
}
+[test_proto.more]: <
+ data: "Big gobs for big rats"
+>
`
func TestMarshalTextFull(t *testing.T) {
@@ -107,7 +117,7 @@
}
func compact(src string) string {
- // ,s/[ \n]+/ /g; s/ $//;
+ // s/[ \n]+/ /g; s/ $//;
dst := make([]byte, len(src))
space := false
j := 0