Skip to content

Commit 535a104

Browse files
committed
Fix size of oneof fields when they are set to their zero value.
We use the proto3 sizers for oneof fields (because they don't have a pointer in the wrapper struct), but they are always encoded when set, so we should not skip their zero value. Fixes #74.
1 parent 1dceb1a commit 535a104

File tree

10 files changed

+122
-72
lines changed

10 files changed

+122
-72
lines changed

jsonpb/jsonpb_test_proto/more_test_objects.pb.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

jsonpb/jsonpb_test_proto/test_objects.pb.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

proto/encode.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ func size_bool(p *Properties, base structPointer) int {
332332

333333
func size_proto3_bool(p *Properties, base structPointer) int {
334334
v := *structPointer_BoolVal(base, p.field)
335-
if !v {
335+
if !v && !p.oneof {
336336
return 0
337337
}
338338
return len(p.tagcode) + 1 // each bool takes exactly one byte
@@ -375,7 +375,7 @@ func size_int32(p *Properties, base structPointer) (n int) {
375375
func size_proto3_int32(p *Properties, base structPointer) (n int) {
376376
v := structPointer_Word32Val(base, p.field)
377377
x := int32(word32Val_Get(v)) // permit sign extension to use full 64-bit range
378-
if x == 0 {
378+
if x == 0 && !p.oneof {
379379
return 0
380380
}
381381
n += len(p.tagcode)
@@ -421,7 +421,7 @@ func size_uint32(p *Properties, base structPointer) (n int) {
421421
func size_proto3_uint32(p *Properties, base structPointer) (n int) {
422422
v := structPointer_Word32Val(base, p.field)
423423
x := word32Val_Get(v)
424-
if x == 0 {
424+
if x == 0 && !p.oneof {
425425
return 0
426426
}
427427
n += len(p.tagcode)
@@ -466,7 +466,7 @@ func size_int64(p *Properties, base structPointer) (n int) {
466466
func size_proto3_int64(p *Properties, base structPointer) (n int) {
467467
v := structPointer_Word64Val(base, p.field)
468468
x := word64Val_Get(v)
469-
if x == 0 {
469+
if x == 0 && !p.oneof {
470470
return 0
471471
}
472472
n += len(p.tagcode)
@@ -509,7 +509,7 @@ func size_string(p *Properties, base structPointer) (n int) {
509509

510510
func size_proto3_string(p *Properties, base structPointer) (n int) {
511511
v := *structPointer_StringVal(base, p.field)
512-
if v == "" {
512+
if v == "" && !p.oneof {
513513
return 0
514514
}
515515
n += len(p.tagcode)
@@ -681,7 +681,7 @@ func (o *Buffer) enc_proto3_slice_byte(p *Properties, base structPointer) error
681681

682682
func size_slice_byte(p *Properties, base structPointer) (n int) {
683683
s := *structPointer_Bytes(base, p.field)
684-
if s == nil {
684+
if s == nil && !p.oneof {
685685
return 0
686686
}
687687
n += len(p.tagcode)
@@ -691,7 +691,7 @@ func size_slice_byte(p *Properties, base structPointer) (n int) {
691691

692692
func size_proto3_slice_byte(p *Properties, base structPointer) (n int) {
693693
s := *structPointer_Bytes(base, p.field)
694-
if len(s) == 0 {
694+
if len(s) == 0 && !p.oneof {
695695
return 0
696696
}
697697
n += len(p.tagcode)

proto/properties.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ type Properties struct {
177177
Packed bool // relevant for repeated primitives only
178178
Enum string // set for enum types only
179179
proto3 bool // whether this is known to be a proto3 field; set for []byte only
180+
oneof bool // whether this is a oneof field
180181

181182
Default string // default value
182183
HasDefault bool // whether an explicit default was provided
@@ -229,6 +230,9 @@ func (p *Properties) String() string {
229230
if p.proto3 {
230231
s += ",proto3"
231232
}
233+
if p.oneof {
234+
s += ",oneof"
235+
}
232236
if len(p.Enum) > 0 {
233237
s += ",enum=" + p.Enum
234238
}
@@ -305,6 +309,8 @@ func (p *Properties) Parse(s string) {
305309
p.Enum = f[5:]
306310
case f == "proto3":
307311
p.proto3 = true
312+
case f == "oneof":
313+
p.oneof = true
308314
case strings.HasPrefix(f, "def="):
309315
p.HasDefault = true
310316
p.Default = f[4:] // rest of string

proto/size_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ var SizeTests = []struct {
126126
{"map field with big numeric key", &pb.MessageWithMap{NameMapping: map[int32]string{0xf00d: "om nom nom"}}},
127127

128128
{"oneof not set", &pb.Communique{}},
129+
{"oneof zero int32", &pb.Communique{Union: &pb.Communique_Number{0}}},
129130
{"oneof int32", &pb.Communique{Union: &pb.Communique_Number{3}}},
130131
{"oneof string", &pb.Communique{Union: &pb.Communique_Name{"Rhythmic Fman"}}},
131132
}

proto/testdata/test.pb.go

Lines changed: 37 additions & 37 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

protoc-gen-go/generator/generator.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ type getterSymbol struct {
303303
name string
304304
typ string
305305
typeName string // canonical name in proto world; empty for proto.Message and similar
306-
genType bool // whether typ is a generated type (message/group/enum)
306+
genType bool // whether typ contains a generated type (message/group/enum)
307307
}
308308

309309
func (ms *messageSymbol) GenerateAlias(g *Generator, pkg string) {
@@ -333,15 +333,19 @@ func (ms *messageSymbol) GenerateAlias(g *Generator, pkg string) {
333333
typ := get.typ
334334
val := "(*" + remoteSym + ")(m)." + get.name + "()"
335335
if get.genType {
336-
// typ will be "*pkg.T" (message/group) or "pkg.T" (enum).
337-
// Either of those might have a "[]" prefix if it is repeated.
338-
// Drop the package qualifier since we have hoisted the type into this package.
336+
// typ will be "*pkg.T" (message/group) or "pkg.T" (enum)
337+
// or "map[t]*pkg.T" (map to message/enum).
338+
// The first two of those might have a "[]" prefix if it is repeated.
339+
// Drop any package qualifier since we have hoisted the type into this package.
339340
rep := strings.HasPrefix(typ, "[]")
340341
if rep {
341342
typ = typ[2:]
342343
}
344+
isMap := strings.HasPrefix(typ, "map[")
343345
star := typ[0] == '*'
344-
typ = typ[strings.Index(typ, ".")+1:]
346+
if !isMap { // map types handled lower down
347+
typ = typ[strings.Index(typ, ".")+1:]
348+
}
345349
if star {
346350
typ = "*" + typ
347351
}
@@ -376,6 +380,30 @@ func (ms *messageSymbol) GenerateAlias(g *Generator, pkg string) {
376380
g.P("}")
377381
continue
378382
}
383+
if isMap {
384+
// Split map[keyTyp]valTyp.
385+
bra, ket := strings.Index(typ, "["), strings.Index(typ, "]")
386+
keyTyp, valTyp := typ[bra+1:ket], typ[ket+1:]
387+
// Drop any package qualifier.
388+
// Only the value type may be foreign.
389+
star := valTyp[0] == '*'
390+
valTyp = valTyp[strings.Index(valTyp, ".")+1:]
391+
if star {
392+
valTyp = "*" + valTyp
393+
}
394+
395+
typ := "map[" + keyTyp + "]" + valTyp
396+
g.P("func (m *", ms.sym, ") ", get.name, "() ", typ, " {")
397+
g.P("o := ", val)
398+
g.P("if o == nil { return nil }")
399+
g.P("s := make(", typ, ", len(o))")
400+
g.P("for k, v := range o {")
401+
g.P("s[k] = (", valTyp, ")(v)")
402+
g.P("}")
403+
g.P("return s")
404+
g.P("}")
405+
continue
406+
}
379407
// Convert imported type into the forwarding type.
380408
val = "(" + typ + ")(" + val + ")"
381409
}
@@ -863,6 +891,9 @@ func wrapImported(file *descriptor.FileDescriptorProto, g *Generator) (sl []*Imp
863891
for _, index := range file.PublicDependency {
864892
df := g.fileByName(file.Dependency[index])
865893
for _, d := range df.desc {
894+
if d.GetOptions().GetMapEntry() {
895+
continue
896+
}
866897
sl = append(sl, &ImportedDescriptor{common{file}, d})
867898
}
868899
for _, e := range df.enum {
@@ -1433,13 +1464,18 @@ func (g *Generator) goTag(message *Descriptor, field *descriptor.FieldDescriptor
14331464
name += ",proto3"
14341465
}
14351466
}
1436-
return strconv.Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s",
1467+
oneof := ""
1468+
if field.OneofIndex != nil {
1469+
oneof = ",oneof"
1470+
}
1471+
return strconv.Quote(fmt.Sprintf("%s,%d,%s%s%s%s%s%s",
14371472
wiretype,
14381473
field.GetNumber(),
14391474
optrepreq,
14401475
packed,
14411476
name,
14421477
enum,
1478+
oneof,
14431479
defaultValue))
14441480
}
14451481

protoc-gen-go/testdata/imp.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ message ImportedMessage {
4848
repeated Owner boss = 6;
4949
repeated ImportedMessage2 memo = 7;
5050

51+
map<string, ImportedMessage2> msg_map = 8;
52+
5153
enum Owner {
5254
DAVE = 1;
5355
MIKE = 2;

0 commit comments

Comments
 (0)