This syncs the protobuf version with the one that is used with
containerd.
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
| ... | ... |
@@ -67,7 +67,7 @@ clone git github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c22 |
| 67 | 67 |
clone git github.com/coreos/go-systemd v4 |
| 68 | 68 |
clone git github.com/godbus/dbus v4.0.0 |
| 69 | 69 |
clone git github.com/syndtr/gocapability 2c00daeb6c3b45114c80ac44119e7b8801fdd852 |
| 70 |
-clone git github.com/golang/protobuf 68415e7123da32b07eab49c96d2c4d6158360e9b |
|
| 70 |
+clone git github.com/golang/protobuf 8d92cf5fc15a4382f8964b08e1f42a75c0591aa3 |
|
| 71 | 71 |
|
| 72 | 72 |
# gelf logging driver deps |
| 73 | 73 |
clone git github.com/Graylog2/go-gelf aab2f594e4585d43468ac57287b0dece9d806883 |
| ... | ... |
@@ -39,5 +39,5 @@ test: install generate-test-pbs |
| 39 | 39 |
generate-test-pbs: |
| 40 | 40 |
make install |
| 41 | 41 |
make -C testdata |
| 42 |
- protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto |
|
| 42 |
+ protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any:. proto3_proto/proto3.proto |
|
| 43 | 43 |
make |
| ... | ... |
@@ -768,10 +768,11 @@ func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
|
| 768 | 768 |
} |
| 769 | 769 |
} |
| 770 | 770 |
keyelem, valelem := keyptr.Elem(), valptr.Elem() |
| 771 |
- if !keyelem.IsValid() || !valelem.IsValid() {
|
|
| 772 |
- // We did not decode the key or the value in the map entry. |
|
| 773 |
- // Either way, it's an invalid map entry. |
|
| 774 |
- return fmt.Errorf("proto: bad map data: missing key/val")
|
|
| 771 |
+ if !keyelem.IsValid() {
|
|
| 772 |
+ keyelem = reflect.Zero(p.mtype.Key()) |
|
| 773 |
+ } |
|
| 774 |
+ if !valelem.IsValid() {
|
|
| 775 |
+ valelem = reflect.Zero(p.mtype.Elem()) |
|
| 775 | 776 |
} |
| 776 | 777 |
|
| 777 | 778 |
v.SetMapIndex(keyelem, valelem) |
| ... | ... |
@@ -64,6 +64,10 @@ var ( |
| 64 | 64 |
// a struct with a repeated field containing a nil element. |
| 65 | 65 |
errRepeatedHasNil = errors.New("proto: repeated field has nil element")
|
| 66 | 66 |
|
| 67 |
+ // errOneofHasNil is the error returned if Marshal is called with |
|
| 68 |
+ // a struct with a oneof field containing a nil element. |
|
| 69 |
+ errOneofHasNil = errors.New("proto: oneof field has nil value")
|
|
| 70 |
+ |
|
| 67 | 71 |
// ErrNil is the error returned if Marshal is called with nil. |
| 68 | 72 |
ErrNil = errors.New("proto: Marshal called with nil")
|
| 69 | 73 |
) |
| ... | ... |
@@ -1222,7 +1226,9 @@ func (o *Buffer) enc_struct(prop *StructProperties, base structPointer) error {
|
| 1222 | 1222 |
// Do oneof fields. |
| 1223 | 1223 |
if prop.oneofMarshaler != nil {
|
| 1224 | 1224 |
m := structPointer_Interface(base, prop.stype).(Message) |
| 1225 |
- if err := prop.oneofMarshaler(m, o); err != nil {
|
|
| 1225 |
+ if err := prop.oneofMarshaler(m, o); err == ErrNil {
|
|
| 1226 |
+ return errOneofHasNil |
|
| 1227 |
+ } else if err != nil {
|
|
| 1226 | 1228 |
return err |
| 1227 | 1229 |
} |
| 1228 | 1230 |
} |
| ... | ... |
@@ -50,7 +50,9 @@ Equality is defined in this way: |
| 50 | 50 |
are equal, and extensions sets are equal. |
| 51 | 51 |
- Two set scalar fields are equal iff their values are equal. |
| 52 | 52 |
If the fields are of a floating-point type, remember that |
| 53 |
- NaN != x for all x, including NaN. |
|
| 53 |
+ NaN != x for all x, including NaN. If the message is defined |
|
| 54 |
+ in a proto3 .proto file, fields are not "set"; specifically, |
|
| 55 |
+ zero length proto3 "bytes" fields are equal (nil == {}).
|
|
| 54 | 56 |
- Two repeated fields are equal iff their lengths are the same, |
| 55 | 57 |
and their corresponding elements are equal (a "bytes" field, |
| 56 | 58 |
although represented by []byte, is not a repeated field) |
| ... | ... |
@@ -88,6 +90,7 @@ func Equal(a, b Message) bool {
|
| 88 | 88 |
|
| 89 | 89 |
// v1 and v2 are known to have the same type. |
| 90 | 90 |
func equalStruct(v1, v2 reflect.Value) bool {
|
| 91 |
+ sprop := GetProperties(v1.Type()) |
|
| 91 | 92 |
for i := 0; i < v1.NumField(); i++ {
|
| 92 | 93 |
f := v1.Type().Field(i) |
| 93 | 94 |
if strings.HasPrefix(f.Name, "XXX_") {
|
| ... | ... |
@@ -113,7 +116,7 @@ func equalStruct(v1, v2 reflect.Value) bool {
|
| 113 | 113 |
} |
| 114 | 114 |
f1, f2 = f1.Elem(), f2.Elem() |
| 115 | 115 |
} |
| 116 |
- if !equalAny(f1, f2) {
|
|
| 116 |
+ if !equalAny(f1, f2, sprop.Prop[i]) {
|
|
| 117 | 117 |
return false |
| 118 | 118 |
} |
| 119 | 119 |
} |
| ... | ... |
@@ -140,7 +143,8 @@ func equalStruct(v1, v2 reflect.Value) bool {
|
| 140 | 140 |
} |
| 141 | 141 |
|
| 142 | 142 |
// v1 and v2 are known to have the same type. |
| 143 |
-func equalAny(v1, v2 reflect.Value) bool {
|
|
| 143 |
+// prop may be nil. |
|
| 144 |
+func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
|
|
| 144 | 145 |
if v1.Type() == protoMessageType {
|
| 145 | 146 |
m1, _ := v1.Interface().(Message) |
| 146 | 147 |
m2, _ := v2.Interface().(Message) |
| ... | ... |
@@ -163,7 +167,7 @@ func equalAny(v1, v2 reflect.Value) bool {
|
| 163 | 163 |
if e1.Type() != e2.Type() {
|
| 164 | 164 |
return false |
| 165 | 165 |
} |
| 166 |
- return equalAny(e1, e2) |
|
| 166 |
+ return equalAny(e1, e2, nil) |
|
| 167 | 167 |
case reflect.Map: |
| 168 | 168 |
if v1.Len() != v2.Len() {
|
| 169 | 169 |
return false |
| ... | ... |
@@ -174,16 +178,22 @@ func equalAny(v1, v2 reflect.Value) bool {
|
| 174 | 174 |
// This key was not found in the second map. |
| 175 | 175 |
return false |
| 176 | 176 |
} |
| 177 |
- if !equalAny(v1.MapIndex(key), val2) {
|
|
| 177 |
+ if !equalAny(v1.MapIndex(key), val2, nil) {
|
|
| 178 | 178 |
return false |
| 179 | 179 |
} |
| 180 | 180 |
} |
| 181 | 181 |
return true |
| 182 | 182 |
case reflect.Ptr: |
| 183 |
- return equalAny(v1.Elem(), v2.Elem()) |
|
| 183 |
+ return equalAny(v1.Elem(), v2.Elem(), prop) |
|
| 184 | 184 |
case reflect.Slice: |
| 185 | 185 |
if v1.Type().Elem().Kind() == reflect.Uint8 {
|
| 186 | 186 |
// short circuit: []byte |
| 187 |
+ |
|
| 188 |
+ // Edge case: if this is in a proto3 message, a zero length |
|
| 189 |
+ // bytes field is considered the zero value. |
|
| 190 |
+ if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
|
|
| 191 |
+ return true |
|
| 192 |
+ } |
|
| 187 | 193 |
if v1.IsNil() != v2.IsNil() {
|
| 188 | 194 |
return false |
| 189 | 195 |
} |
| ... | ... |
@@ -194,7 +204,7 @@ func equalAny(v1, v2 reflect.Value) bool {
|
| 194 | 194 |
return false |
| 195 | 195 |
} |
| 196 | 196 |
for i := 0; i < v1.Len(); i++ {
|
| 197 |
- if !equalAny(v1.Index(i), v2.Index(i)) {
|
|
| 197 |
+ if !equalAny(v1.Index(i), v2.Index(i), prop) {
|
|
| 198 | 198 |
return false |
| 199 | 199 |
} |
| 200 | 200 |
} |
| ... | ... |
@@ -229,7 +239,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
| 229 | 229 |
|
| 230 | 230 |
if m1 != nil && m2 != nil {
|
| 231 | 231 |
// Both are unencoded. |
| 232 |
- if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) {
|
|
| 232 |
+ if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
|
| 233 | 233 |
return false |
| 234 | 234 |
} |
| 235 | 235 |
continue |
| ... | ... |
@@ -257,7 +267,7 @@ func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
| 257 | 257 |
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
|
| 258 | 258 |
return false |
| 259 | 259 |
} |
| 260 |
- if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2)) {
|
|
| 260 |
+ if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
|
|
| 261 | 261 |
return false |
| 262 | 262 |
} |
| 263 | 263 |
} |
| ... | ... |
@@ -70,6 +70,12 @@ for a protocol buffer variable v: |
| 70 | 70 |
with distinguished wrapper types for each possible field value. |
| 71 | 71 |
- Marshal and Unmarshal are functions to encode and decode the wire format. |
| 72 | 72 |
|
| 73 |
+When the .proto file specifies `syntax="proto3"`, there are some differences: |
|
| 74 |
+ |
|
| 75 |
+ - Non-repeated fields of non-message type are values instead of pointers. |
|
| 76 |
+ - Getters are only generated for message and oneof fields. |
|
| 77 |
+ - Enum types do not get an Enum method. |
|
| 78 |
+ |
|
| 73 | 79 |
The simplest way to describe this is to see an example. |
| 74 | 80 |
Given file test.proto, containing |
| 75 | 81 |
|
| ... | ... |
@@ -229,6 +235,7 @@ To create and play with a Test object: |
| 229 | 229 |
test := &pb.Test{
|
| 230 | 230 |
Label: proto.String("hello"),
|
| 231 | 231 |
Type: proto.Int32(17), |
| 232 |
+ Reps: []int64{1, 2, 3},
|
|
| 232 | 233 |
Optionalgroup: &pb.Test_OptionalGroup{
|
| 233 | 234 |
RequiredField: proto.String("good bye"),
|
| 234 | 235 |
}, |
| ... | ... |
@@ -881,3 +888,7 @@ func isProto3Zero(v reflect.Value) bool {
|
| 881 | 881 |
} |
| 882 | 882 |
return false |
| 883 | 883 |
} |
| 884 |
+ |
|
| 885 |
+// ProtoPackageIsVersion1 is referenced from generated protocol buffer files |
|
| 886 |
+// to assert that that code is compatible with this version of the proto package. |
|
| 887 |
+const ProtoPackageIsVersion1 = true |
| ... | ... |
@@ -173,6 +173,7 @@ func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order
|
| 173 | 173 |
type Properties struct {
|
| 174 | 174 |
Name string // name of the field, for error messages |
| 175 | 175 |
OrigName string // original name before protocol compiler (always set) |
| 176 |
+ JSONName string // name to use for JSON; determined by protoc |
|
| 176 | 177 |
Wire string |
| 177 | 178 |
WireType int |
| 178 | 179 |
Tag int |
| ... | ... |
@@ -229,8 +230,9 @@ func (p *Properties) String() string {
|
| 229 | 229 |
if p.Packed {
|
| 230 | 230 |
s += ",packed" |
| 231 | 231 |
} |
| 232 |
- if p.OrigName != p.Name {
|
|
| 233 |
- s += ",name=" + p.OrigName |
|
| 232 |
+ s += ",name=" + p.OrigName |
|
| 233 |
+ if p.JSONName != p.OrigName {
|
|
| 234 |
+ s += ",json=" + p.JSONName |
|
| 234 | 235 |
} |
| 235 | 236 |
if p.proto3 {
|
| 236 | 237 |
s += ",proto3" |
| ... | ... |
@@ -310,6 +312,8 @@ func (p *Properties) Parse(s string) {
|
| 310 | 310 |
p.Packed = true |
| 311 | 311 |
case strings.HasPrefix(f, "name="): |
| 312 | 312 |
p.OrigName = f[5:] |
| 313 |
+ case strings.HasPrefix(f, "json="): |
|
| 314 |
+ p.JSONName = f[5:] |
|
| 313 | 315 |
case strings.HasPrefix(f, "enum="): |
| 314 | 316 |
p.Enum = f[5:] |
| 315 | 317 |
case f == "proto3": |
| ... | ... |
@@ -175,7 +175,93 @@ type raw interface {
|
| 175 | 175 |
Bytes() []byte |
| 176 | 176 |
} |
| 177 | 177 |
|
| 178 |
-func writeStruct(w *textWriter, sv reflect.Value) error {
|
|
| 178 |
+func requiresQuotes(u string) bool {
|
|
| 179 |
+ // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. |
|
| 180 |
+ for _, ch := range u {
|
|
| 181 |
+ switch {
|
|
| 182 |
+ case ch == '.' || ch == '/' || ch == '_': |
|
| 183 |
+ continue |
|
| 184 |
+ case '0' <= ch && ch <= '9': |
|
| 185 |
+ continue |
|
| 186 |
+ case 'A' <= ch && ch <= 'Z': |
|
| 187 |
+ continue |
|
| 188 |
+ case 'a' <= ch && ch <= 'z': |
|
| 189 |
+ continue |
|
| 190 |
+ default: |
|
| 191 |
+ return true |
|
| 192 |
+ } |
|
| 193 |
+ } |
|
| 194 |
+ return false |
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+// isAny reports whether sv is a google.protobuf.Any message |
|
| 198 |
+func isAny(sv reflect.Value) bool {
|
|
| 199 |
+ type wkt interface {
|
|
| 200 |
+ XXX_WellKnownType() string |
|
| 201 |
+ } |
|
| 202 |
+ t, ok := sv.Addr().Interface().(wkt) |
|
| 203 |
+ return ok && t.XXX_WellKnownType() == "Any" |
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+// writeProto3Any writes an expanded google.protobuf.Any message. |
|
| 207 |
+// |
|
| 208 |
+// It returns (false, nil) if sv value can't be unmarshaled (e.g. because |
|
| 209 |
+// required messages are not linked in). |
|
| 210 |
+// |
|
| 211 |
+// It returns (true, error) when sv was written in expanded format or an error |
|
| 212 |
+// was encountered. |
|
| 213 |
+func (tm *TextMarshaler) writeProto3Any(w *textWriter, sv reflect.Value) (bool, error) {
|
|
| 214 |
+ turl := sv.FieldByName("TypeUrl")
|
|
| 215 |
+ val := sv.FieldByName("Value")
|
|
| 216 |
+ if !turl.IsValid() || !val.IsValid() {
|
|
| 217 |
+ return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
| 218 |
+ } |
|
| 219 |
+ |
|
| 220 |
+ b, ok := val.Interface().([]byte) |
|
| 221 |
+ if !ok {
|
|
| 222 |
+ return true, errors.New("proto: invalid google.protobuf.Any message")
|
|
| 223 |
+ } |
|
| 224 |
+ |
|
| 225 |
+ parts := strings.Split(turl.String(), "/") |
|
| 226 |
+ mt := MessageType(parts[len(parts)-1]) |
|
| 227 |
+ if mt == nil {
|
|
| 228 |
+ return false, nil |
|
| 229 |
+ } |
|
| 230 |
+ m := reflect.New(mt.Elem()) |
|
| 231 |
+ if err := Unmarshal(b, m.Interface().(Message)); err != nil {
|
|
| 232 |
+ return false, nil |
|
| 233 |
+ } |
|
| 234 |
+ w.Write([]byte("["))
|
|
| 235 |
+ u := turl.String() |
|
| 236 |
+ if requiresQuotes(u) {
|
|
| 237 |
+ writeString(w, u) |
|
| 238 |
+ } else {
|
|
| 239 |
+ w.Write([]byte(u)) |
|
| 240 |
+ } |
|
| 241 |
+ if w.compact {
|
|
| 242 |
+ w.Write([]byte("]:<"))
|
|
| 243 |
+ } else {
|
|
| 244 |
+ w.Write([]byte("]: <\n"))
|
|
| 245 |
+ w.ind++ |
|
| 246 |
+ } |
|
| 247 |
+ if err := tm.writeStruct(w, m.Elem()); err != nil {
|
|
| 248 |
+ return true, err |
|
| 249 |
+ } |
|
| 250 |
+ if w.compact {
|
|
| 251 |
+ w.Write([]byte("> "))
|
|
| 252 |
+ } else {
|
|
| 253 |
+ w.ind-- |
|
| 254 |
+ w.Write([]byte(">\n"))
|
|
| 255 |
+ } |
|
| 256 |
+ return true, nil |
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
|
|
| 260 |
+ if tm.ExpandAny && isAny(sv) {
|
|
| 261 |
+ if canExpand, err := tm.writeProto3Any(w, sv); canExpand {
|
|
| 262 |
+ return err |
|
| 263 |
+ } |
|
| 264 |
+ } |
|
| 179 | 265 |
st := sv.Type() |
| 180 | 266 |
sprops := GetProperties(st) |
| 181 | 267 |
for i := 0; i < sv.NumField(); i++ {
|
| ... | ... |
@@ -227,7 +313,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
|
| 227 | 227 |
} |
| 228 | 228 |
continue |
| 229 | 229 |
} |
| 230 |
- if err := writeAny(w, v, props); err != nil {
|
|
| 230 |
+ if err := tm.writeAny(w, v, props); err != nil {
|
|
| 231 | 231 |
return err |
| 232 | 232 |
} |
| 233 | 233 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -269,7 +355,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
|
| 269 | 269 |
return err |
| 270 | 270 |
} |
| 271 | 271 |
} |
| 272 |
- if err := writeAny(w, key, props.mkeyprop); err != nil {
|
|
| 272 |
+ if err := tm.writeAny(w, key, props.mkeyprop); err != nil {
|
|
| 273 | 273 |
return err |
| 274 | 274 |
} |
| 275 | 275 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -286,7 +372,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
|
| 286 | 286 |
return err |
| 287 | 287 |
} |
| 288 | 288 |
} |
| 289 |
- if err := writeAny(w, val, props.mvalprop); err != nil {
|
|
| 289 |
+ if err := tm.writeAny(w, val, props.mvalprop); err != nil {
|
|
| 290 | 290 |
return err |
| 291 | 291 |
} |
| 292 | 292 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -358,7 +444,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
|
| 358 | 358 |
} |
| 359 | 359 |
|
| 360 | 360 |
// Enums have a String method, so writeAny will work fine. |
| 361 |
- if err := writeAny(w, fv, props); err != nil {
|
|
| 361 |
+ if err := tm.writeAny(w, fv, props); err != nil {
|
|
| 362 | 362 |
return err |
| 363 | 363 |
} |
| 364 | 364 |
|
| ... | ... |
@@ -370,7 +456,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
|
| 370 | 370 |
// Extensions (the XXX_extensions field). |
| 371 | 371 |
pv := sv.Addr() |
| 372 | 372 |
if pv.Type().Implements(extendableProtoType) {
|
| 373 |
- if err := writeExtensions(w, pv); err != nil {
|
|
| 373 |
+ if err := tm.writeExtensions(w, pv); err != nil {
|
|
| 374 | 374 |
return err |
| 375 | 375 |
} |
| 376 | 376 |
} |
| ... | ... |
@@ -400,7 +486,7 @@ func writeRaw(w *textWriter, b []byte) error {
|
| 400 | 400 |
} |
| 401 | 401 |
|
| 402 | 402 |
// writeAny writes an arbitrary field. |
| 403 |
-func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
| 403 |
+func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
|
| 404 | 404 |
v = reflect.Indirect(v) |
| 405 | 405 |
|
| 406 | 406 |
// Floats have special cases. |
| ... | ... |
@@ -449,15 +535,15 @@ func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
|
| 449 | 449 |
} |
| 450 | 450 |
} |
| 451 | 451 |
w.indent() |
| 452 |
- if tm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
| 453 |
- text, err := tm.MarshalText() |
|
| 452 |
+ if etm, ok := v.Interface().(encoding.TextMarshaler); ok {
|
|
| 453 |
+ text, err := etm.MarshalText() |
|
| 454 | 454 |
if err != nil {
|
| 455 | 455 |
return err |
| 456 | 456 |
} |
| 457 | 457 |
if _, err = w.Write(text); err != nil {
|
| 458 | 458 |
return err |
| 459 | 459 |
} |
| 460 |
- } else if err := writeStruct(w, v); err != nil {
|
|
| 460 |
+ } else if err := tm.writeStruct(w, v); err != nil {
|
|
| 461 | 461 |
return err |
| 462 | 462 |
} |
| 463 | 463 |
w.unindent() |
| ... | ... |
@@ -601,7 +687,7 @@ func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
| 601 | 601 |
|
| 602 | 602 |
// writeExtensions writes all the extensions in pv. |
| 603 | 603 |
// pv is assumed to be a pointer to a protocol message struct that is extendable. |
| 604 |
-func writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
| 604 |
+func (tm *TextMarshaler) writeExtensions(w *textWriter, pv reflect.Value) error {
|
|
| 605 | 605 |
emap := extensionMaps[pv.Type().Elem()] |
| 606 | 606 |
ep := pv.Interface().(extendableProto) |
| 607 | 607 |
|
| ... | ... |
@@ -636,13 +722,13 @@ func writeExtensions(w *textWriter, pv reflect.Value) error {
|
| 636 | 636 |
|
| 637 | 637 |
// Repeated extensions will appear as a slice. |
| 638 | 638 |
if !desc.repeated() {
|
| 639 |
- if err := writeExtension(w, desc.Name, pb); err != nil {
|
|
| 639 |
+ if err := tm.writeExtension(w, desc.Name, pb); err != nil {
|
|
| 640 | 640 |
return err |
| 641 | 641 |
} |
| 642 | 642 |
} else {
|
| 643 | 643 |
v := reflect.ValueOf(pb) |
| 644 | 644 |
for i := 0; i < v.Len(); i++ {
|
| 645 |
- if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
| 645 |
+ if err := tm.writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
|
|
| 646 | 646 |
return err |
| 647 | 647 |
} |
| 648 | 648 |
} |
| ... | ... |
@@ -651,7 +737,7 @@ func writeExtensions(w *textWriter, pv reflect.Value) error {
|
| 651 | 651 |
return nil |
| 652 | 652 |
} |
| 653 | 653 |
|
| 654 |
-func writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
| 654 |
+func (tm *TextMarshaler) writeExtension(w *textWriter, name string, pb interface{}) error {
|
|
| 655 | 655 |
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
|
| 656 | 656 |
return err |
| 657 | 657 |
} |
| ... | ... |
@@ -660,7 +746,7 @@ func writeExtension(w *textWriter, name string, pb interface{}) error {
|
| 660 | 660 |
return err |
| 661 | 661 |
} |
| 662 | 662 |
} |
| 663 |
- if err := writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
| 663 |
+ if err := tm.writeAny(w, reflect.ValueOf(pb), nil); err != nil {
|
|
| 664 | 664 |
return err |
| 665 | 665 |
} |
| 666 | 666 |
if err := w.WriteByte('\n'); err != nil {
|
| ... | ... |
@@ -685,7 +771,15 @@ func (w *textWriter) writeIndent() {
|
| 685 | 685 |
w.complete = false |
| 686 | 686 |
} |
| 687 | 687 |
|
| 688 |
-func marshalText(w io.Writer, pb Message, compact bool) error {
|
|
| 688 |
+// TextMarshaler is a configurable text format marshaler. |
|
| 689 |
+type TextMarshaler struct {
|
|
| 690 |
+ Compact bool // use compact text format (one line). |
|
| 691 |
+ ExpandAny bool // expand google.protobuf.Any messages of known types |
|
| 692 |
+} |
|
| 693 |
+ |
|
| 694 |
+// Marshal writes a given protocol buffer in text format. |
|
| 695 |
+// The only errors returned are from w. |
|
| 696 |
+func (tm *TextMarshaler) Marshal(w io.Writer, pb Message) error {
|
|
| 689 | 697 |
val := reflect.ValueOf(pb) |
| 690 | 698 |
if pb == nil || val.IsNil() {
|
| 691 | 699 |
w.Write([]byte("<nil>"))
|
| ... | ... |
@@ -700,11 +794,11 @@ func marshalText(w io.Writer, pb Message, compact bool) error {
|
| 700 | 700 |
aw := &textWriter{
|
| 701 | 701 |
w: ww, |
| 702 | 702 |
complete: true, |
| 703 |
- compact: compact, |
|
| 703 |
+ compact: tm.Compact, |
|
| 704 | 704 |
} |
| 705 | 705 |
|
| 706 |
- if tm, ok := pb.(encoding.TextMarshaler); ok {
|
|
| 707 |
- text, err := tm.MarshalText() |
|
| 706 |
+ if etm, ok := pb.(encoding.TextMarshaler); ok {
|
|
| 707 |
+ text, err := etm.MarshalText() |
|
| 708 | 708 |
if err != nil {
|
| 709 | 709 |
return err |
| 710 | 710 |
} |
| ... | ... |
@@ -718,7 +812,7 @@ func marshalText(w io.Writer, pb Message, compact bool) error {
|
| 718 | 718 |
} |
| 719 | 719 |
// Dereference the received pointer so we don't have outer < and >. |
| 720 | 720 |
v := reflect.Indirect(val) |
| 721 |
- if err := writeStruct(aw, v); err != nil {
|
|
| 721 |
+ if err := tm.writeStruct(aw, v); err != nil {
|
|
| 722 | 722 |
return err |
| 723 | 723 |
} |
| 724 | 724 |
if bw != nil {
|
| ... | ... |
@@ -727,25 +821,29 @@ func marshalText(w io.Writer, pb Message, compact bool) error {
|
| 727 | 727 |
return nil |
| 728 | 728 |
} |
| 729 | 729 |
|
| 730 |
+// Text is the same as Marshal, but returns the string directly. |
|
| 731 |
+func (tm *TextMarshaler) Text(pb Message) string {
|
|
| 732 |
+ var buf bytes.Buffer |
|
| 733 |
+ tm.Marshal(&buf, pb) |
|
| 734 |
+ return buf.String() |
|
| 735 |
+} |
|
| 736 |
+ |
|
| 737 |
+var ( |
|
| 738 |
+ defaultTextMarshaler = TextMarshaler{}
|
|
| 739 |
+ compactTextMarshaler = TextMarshaler{Compact: true}
|
|
| 740 |
+) |
|
| 741 |
+ |
|
| 742 |
+// TODO: consider removing some of the Marshal functions below. |
|
| 743 |
+ |
|
| 730 | 744 |
// MarshalText writes a given protocol buffer in text format. |
| 731 | 745 |
// The only errors returned are from w. |
| 732 |
-func MarshalText(w io.Writer, pb Message) error {
|
|
| 733 |
- return marshalText(w, pb, false) |
|
| 734 |
-} |
|
| 746 |
+func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
|
|
| 735 | 747 |
|
| 736 | 748 |
// MarshalTextString is the same as MarshalText, but returns the string directly. |
| 737 |
-func MarshalTextString(pb Message) string {
|
|
| 738 |
- var buf bytes.Buffer |
|
| 739 |
- marshalText(&buf, pb, false) |
|
| 740 |
- return buf.String() |
|
| 741 |
-} |
|
| 749 |
+func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }
|
|
| 742 | 750 |
|
| 743 | 751 |
// CompactText writes a given protocol buffer in compact text format (one line). |
| 744 |
-func CompactText(w io.Writer, pb Message) error { return marshalText(w, pb, true) }
|
|
| 752 |
+func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
|
|
| 745 | 753 |
|
| 746 | 754 |
// CompactTextString is the same as CompactText, but returns the string directly. |
| 747 |
-func CompactTextString(pb Message) string {
|
|
| 748 |
- var buf bytes.Buffer |
|
| 749 |
- marshalText(&buf, pb, true) |
|
| 750 |
- return buf.String() |
|
| 751 |
-} |
|
| 755 |
+func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }
|
| ... | ... |
@@ -119,6 +119,14 @@ func isWhitespace(c byte) bool {
|
| 119 | 119 |
return false |
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 |
+func isQuote(c byte) bool {
|
|
| 123 |
+ switch c {
|
|
| 124 |
+ case '"', '\'': |
|
| 125 |
+ return true |
|
| 126 |
+ } |
|
| 127 |
+ return false |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 122 | 130 |
func (p *textParser) skipWhitespace() {
|
| 123 | 131 |
i := 0 |
| 124 | 132 |
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
|
| ... | ... |
@@ -155,7 +163,7 @@ func (p *textParser) advance() {
|
| 155 | 155 |
p.cur.offset, p.cur.line = p.offset, p.line |
| 156 | 156 |
p.cur.unquoted = "" |
| 157 | 157 |
switch p.s[0] {
|
| 158 |
- case '<', '>', '{', '}', ':', '[', ']', ';', ',':
|
|
| 158 |
+ case '<', '>', '{', '}', ':', '[', ']', ';', ',', '/':
|
|
| 159 | 159 |
// Single symbol |
| 160 | 160 |
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)] |
| 161 | 161 |
case '"', '\'': |
| ... | ... |
@@ -333,13 +341,13 @@ func (p *textParser) next() *token {
|
| 333 | 333 |
p.advance() |
| 334 | 334 |
if p.done {
|
| 335 | 335 |
p.cur.value = "" |
| 336 |
- } else if len(p.cur.value) > 0 && p.cur.value[0] == '"' {
|
|
| 336 |
+ } else if len(p.cur.value) > 0 && isQuote(p.cur.value[0]) {
|
|
| 337 | 337 |
// Look for multiple quoted strings separated by whitespace, |
| 338 | 338 |
// and concatenate them. |
| 339 | 339 |
cat := p.cur |
| 340 | 340 |
for {
|
| 341 | 341 |
p.skipWhitespace() |
| 342 |
- if p.done || p.s[0] != '"' {
|
|
| 342 |
+ if p.done || !isQuote(p.s[0]) {
|
|
| 343 | 343 |
break |
| 344 | 344 |
} |
| 345 | 345 |
p.advance() |
| ... | ... |
@@ -443,7 +451,10 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
| 443 | 443 |
fieldSet := make(map[string]bool) |
| 444 | 444 |
// A struct is a sequence of "name: value", terminated by one of |
| 445 | 445 |
// '>' or '}', or the end of the input. A name may also be |
| 446 |
- // "[extension]". |
|
| 446 |
+ // "[extension]" or "[type/url]". |
|
| 447 |
+ // |
|
| 448 |
+ // The whole struct can also be an expanded Any message, like: |
|
| 449 |
+ // [type/url] < ... struct contents ... > |
|
| 447 | 450 |
for {
|
| 448 | 451 |
tok := p.next() |
| 449 | 452 |
if tok.err != nil {
|
| ... | ... |
@@ -453,33 +464,66 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
| 453 | 453 |
break |
| 454 | 454 |
} |
| 455 | 455 |
if tok.value == "[" {
|
| 456 |
- // Looks like an extension. |
|
| 456 |
+ // Looks like an extension or an Any. |
|
| 457 | 457 |
// |
| 458 | 458 |
// TODO: Check whether we need to handle |
| 459 | 459 |
// namespace rooted names (e.g. ".something.Foo"). |
| 460 |
- tok = p.next() |
|
| 461 |
- if tok.err != nil {
|
|
| 462 |
- return tok.err |
|
| 460 |
+ extName, err := p.consumeExtName() |
|
| 461 |
+ if err != nil {
|
|
| 462 |
+ return err |
|
| 463 |
+ } |
|
| 464 |
+ |
|
| 465 |
+ if s := strings.LastIndex(extName, "/"); s >= 0 {
|
|
| 466 |
+ // If it contains a slash, it's an Any type URL. |
|
| 467 |
+ messageName := extName[s+1:] |
|
| 468 |
+ mt := MessageType(messageName) |
|
| 469 |
+ if mt == nil {
|
|
| 470 |
+ return p.errorf("unrecognized message %q in google.protobuf.Any", messageName)
|
|
| 471 |
+ } |
|
| 472 |
+ tok = p.next() |
|
| 473 |
+ if tok.err != nil {
|
|
| 474 |
+ return tok.err |
|
| 475 |
+ } |
|
| 476 |
+ // consume an optional colon |
|
| 477 |
+ if tok.value == ":" {
|
|
| 478 |
+ tok = p.next() |
|
| 479 |
+ if tok.err != nil {
|
|
| 480 |
+ return tok.err |
|
| 481 |
+ } |
|
| 482 |
+ } |
|
| 483 |
+ var terminator string |
|
| 484 |
+ switch tok.value {
|
|
| 485 |
+ case "<": |
|
| 486 |
+ terminator = ">" |
|
| 487 |
+ case "{":
|
|
| 488 |
+ terminator = "}" |
|
| 489 |
+ default: |
|
| 490 |
+ return p.errorf("expected '{' or '<', found %q", tok.value)
|
|
| 491 |
+ } |
|
| 492 |
+ v := reflect.New(mt.Elem()) |
|
| 493 |
+ if pe := p.readStruct(v.Elem(), terminator); pe != nil {
|
|
| 494 |
+ return pe |
|
| 495 |
+ } |
|
| 496 |
+ b, err := Marshal(v.Interface().(Message)) |
|
| 497 |
+ if err != nil {
|
|
| 498 |
+ return p.errorf("failed to marshal message of type %q: %v", messageName, err)
|
|
| 499 |
+ } |
|
| 500 |
+ sv.FieldByName("TypeUrl").SetString(extName)
|
|
| 501 |
+ sv.FieldByName("Value").SetBytes(b)
|
|
| 502 |
+ continue |
|
| 463 | 503 |
} |
| 504 |
+ |
|
| 464 | 505 |
var desc *ExtensionDesc |
| 465 | 506 |
// This could be faster, but it's functional. |
| 466 | 507 |
// TODO: Do something smarter than a linear scan. |
| 467 | 508 |
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
|
| 468 |
- if d.Name == tok.value {
|
|
| 509 |
+ if d.Name == extName {
|
|
| 469 | 510 |
desc = d |
| 470 | 511 |
break |
| 471 | 512 |
} |
| 472 | 513 |
} |
| 473 | 514 |
if desc == nil {
|
| 474 |
- return p.errorf("unrecognized extension %q", tok.value)
|
|
| 475 |
- } |
|
| 476 |
- // Check the extension terminator. |
|
| 477 |
- tok = p.next() |
|
| 478 |
- if tok.err != nil {
|
|
| 479 |
- return tok.err |
|
| 480 |
- } |
|
| 481 |
- if tok.value != "]" {
|
|
| 482 |
- return p.errorf("unrecognized extension terminator %q", tok.value)
|
|
| 515 |
+ return p.errorf("unrecognized extension %q", extName)
|
|
| 483 | 516 |
} |
| 484 | 517 |
|
| 485 | 518 |
props := &Properties{}
|
| ... | ... |
@@ -635,6 +679,35 @@ func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
|
| 635 | 635 |
return reqFieldErr |
| 636 | 636 |
} |
| 637 | 637 |
|
| 638 |
+// consumeExtName consumes extension name or expanded Any type URL and the |
|
| 639 |
+// following ']'. It returns the name or URL consumed. |
|
| 640 |
+func (p *textParser) consumeExtName() (string, error) {
|
|
| 641 |
+ tok := p.next() |
|
| 642 |
+ if tok.err != nil {
|
|
| 643 |
+ return "", tok.err |
|
| 644 |
+ } |
|
| 645 |
+ |
|
| 646 |
+ // If extension name or type url is quoted, it's a single token. |
|
| 647 |
+ if len(tok.value) > 2 && isQuote(tok.value[0]) && tok.value[len(tok.value)-1] == tok.value[0] {
|
|
| 648 |
+ name, err := unquoteC(tok.value[1:len(tok.value)-1], rune(tok.value[0])) |
|
| 649 |
+ if err != nil {
|
|
| 650 |
+ return "", err |
|
| 651 |
+ } |
|
| 652 |
+ return name, p.consumeToken("]")
|
|
| 653 |
+ } |
|
| 654 |
+ |
|
| 655 |
+ // Consume everything up to "]" |
|
| 656 |
+ var parts []string |
|
| 657 |
+ for tok.value != "]" {
|
|
| 658 |
+ parts = append(parts, tok.value) |
|
| 659 |
+ tok = p.next() |
|
| 660 |
+ if tok.err != nil {
|
|
| 661 |
+ return "", p.errorf("unrecognized type_url or extension name: %s", tok.err)
|
|
| 662 |
+ } |
|
| 663 |
+ } |
|
| 664 |
+ return strings.Join(parts, ""), nil |
|
| 665 |
+} |
|
| 666 |
+ |
|
| 638 | 667 |
// consumeOptionalSeparator consumes an optional semicolon or comma. |
| 639 | 668 |
// It is used in readStruct to provide backward compatibility. |
| 640 | 669 |
func (p *textParser) consumeOptionalSeparator() error {
|