khenaidoo | 7d3c558 | 2021-08-11 18:09:44 -0400 | [diff] [blame] | 1 | // Copyright 2010 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package proto |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "encoding" |
| 10 | "fmt" |
| 11 | "io" |
| 12 | "math" |
| 13 | "sort" |
| 14 | "strings" |
| 15 | |
| 16 | "google.golang.org/protobuf/encoding/prototext" |
| 17 | "google.golang.org/protobuf/encoding/protowire" |
| 18 | "google.golang.org/protobuf/proto" |
| 19 | "google.golang.org/protobuf/reflect/protoreflect" |
| 20 | "google.golang.org/protobuf/reflect/protoregistry" |
| 21 | ) |
| 22 | |
| 23 | const wrapTextMarshalV2 = false |
| 24 | |
| 25 | // TextMarshaler is a configurable text format marshaler. |
| 26 | type TextMarshaler struct { |
| 27 | Compact bool // use compact text format (one line) |
| 28 | ExpandAny bool // expand google.protobuf.Any messages of known types |
| 29 | } |
| 30 | |
| 31 | // Marshal writes the proto text format of m to w. |
| 32 | func (tm *TextMarshaler) Marshal(w io.Writer, m Message) error { |
| 33 | b, err := tm.marshal(m) |
| 34 | if len(b) > 0 { |
| 35 | if _, err := w.Write(b); err != nil { |
| 36 | return err |
| 37 | } |
| 38 | } |
| 39 | return err |
| 40 | } |
| 41 | |
| 42 | // Text returns a proto text formatted string of m. |
| 43 | func (tm *TextMarshaler) Text(m Message) string { |
| 44 | b, _ := tm.marshal(m) |
| 45 | return string(b) |
| 46 | } |
| 47 | |
| 48 | func (tm *TextMarshaler) marshal(m Message) ([]byte, error) { |
| 49 | mr := MessageReflect(m) |
| 50 | if mr == nil || !mr.IsValid() { |
| 51 | return []byte("<nil>"), nil |
| 52 | } |
| 53 | |
| 54 | if wrapTextMarshalV2 { |
| 55 | if m, ok := m.(encoding.TextMarshaler); ok { |
| 56 | return m.MarshalText() |
| 57 | } |
| 58 | |
| 59 | opts := prototext.MarshalOptions{ |
| 60 | AllowPartial: true, |
| 61 | EmitUnknown: true, |
| 62 | } |
| 63 | if !tm.Compact { |
| 64 | opts.Indent = " " |
| 65 | } |
| 66 | if !tm.ExpandAny { |
| 67 | opts.Resolver = (*protoregistry.Types)(nil) |
| 68 | } |
| 69 | return opts.Marshal(mr.Interface()) |
| 70 | } else { |
| 71 | w := &textWriter{ |
| 72 | compact: tm.Compact, |
| 73 | expandAny: tm.ExpandAny, |
| 74 | complete: true, |
| 75 | } |
| 76 | |
| 77 | if m, ok := m.(encoding.TextMarshaler); ok { |
| 78 | b, err := m.MarshalText() |
| 79 | if err != nil { |
| 80 | return nil, err |
| 81 | } |
| 82 | w.Write(b) |
| 83 | return w.buf, nil |
| 84 | } |
| 85 | |
| 86 | err := w.writeMessage(mr) |
| 87 | return w.buf, err |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | var ( |
| 92 | defaultTextMarshaler = TextMarshaler{} |
| 93 | compactTextMarshaler = TextMarshaler{Compact: true} |
| 94 | ) |
| 95 | |
| 96 | // MarshalText writes the proto text format of m to w. |
| 97 | func MarshalText(w io.Writer, m Message) error { return defaultTextMarshaler.Marshal(w, m) } |
| 98 | |
| 99 | // MarshalTextString returns a proto text formatted string of m. |
| 100 | func MarshalTextString(m Message) string { return defaultTextMarshaler.Text(m) } |
| 101 | |
| 102 | // CompactText writes the compact proto text format of m to w. |
| 103 | func CompactText(w io.Writer, m Message) error { return compactTextMarshaler.Marshal(w, m) } |
| 104 | |
| 105 | // CompactTextString returns a compact proto text formatted string of m. |
| 106 | func CompactTextString(m Message) string { return compactTextMarshaler.Text(m) } |
| 107 | |
| 108 | var ( |
| 109 | newline = []byte("\n") |
| 110 | endBraceNewline = []byte("}\n") |
| 111 | posInf = []byte("inf") |
| 112 | negInf = []byte("-inf") |
| 113 | nan = []byte("nan") |
| 114 | ) |
| 115 | |
| 116 | // textWriter is an io.Writer that tracks its indentation level. |
| 117 | type textWriter struct { |
| 118 | compact bool // same as TextMarshaler.Compact |
| 119 | expandAny bool // same as TextMarshaler.ExpandAny |
| 120 | complete bool // whether the current position is a complete line |
| 121 | indent int // indentation level; never negative |
| 122 | buf []byte |
| 123 | } |
| 124 | |
| 125 | func (w *textWriter) Write(p []byte) (n int, _ error) { |
| 126 | newlines := bytes.Count(p, newline) |
| 127 | if newlines == 0 { |
| 128 | if !w.compact && w.complete { |
| 129 | w.writeIndent() |
| 130 | } |
| 131 | w.buf = append(w.buf, p...) |
| 132 | w.complete = false |
| 133 | return len(p), nil |
| 134 | } |
| 135 | |
| 136 | frags := bytes.SplitN(p, newline, newlines+1) |
| 137 | if w.compact { |
| 138 | for i, frag := range frags { |
| 139 | if i > 0 { |
| 140 | w.buf = append(w.buf, ' ') |
| 141 | n++ |
| 142 | } |
| 143 | w.buf = append(w.buf, frag...) |
| 144 | n += len(frag) |
| 145 | } |
| 146 | return n, nil |
| 147 | } |
| 148 | |
| 149 | for i, frag := range frags { |
| 150 | if w.complete { |
| 151 | w.writeIndent() |
| 152 | } |
| 153 | w.buf = append(w.buf, frag...) |
| 154 | n += len(frag) |
| 155 | if i+1 < len(frags) { |
| 156 | w.buf = append(w.buf, '\n') |
| 157 | n++ |
| 158 | } |
| 159 | } |
| 160 | w.complete = len(frags[len(frags)-1]) == 0 |
| 161 | return n, nil |
| 162 | } |
| 163 | |
| 164 | func (w *textWriter) WriteByte(c byte) error { |
| 165 | if w.compact && c == '\n' { |
| 166 | c = ' ' |
| 167 | } |
| 168 | if !w.compact && w.complete { |
| 169 | w.writeIndent() |
| 170 | } |
| 171 | w.buf = append(w.buf, c) |
| 172 | w.complete = c == '\n' |
| 173 | return nil |
| 174 | } |
| 175 | |
| 176 | func (w *textWriter) writeName(fd protoreflect.FieldDescriptor) { |
| 177 | if !w.compact && w.complete { |
| 178 | w.writeIndent() |
| 179 | } |
| 180 | w.complete = false |
| 181 | |
| 182 | if fd.Kind() != protoreflect.GroupKind { |
| 183 | w.buf = append(w.buf, fd.Name()...) |
| 184 | w.WriteByte(':') |
| 185 | } else { |
| 186 | // Use message type name for group field name. |
| 187 | w.buf = append(w.buf, fd.Message().Name()...) |
| 188 | } |
| 189 | |
| 190 | if !w.compact { |
| 191 | w.WriteByte(' ') |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | func requiresQuotes(u string) bool { |
| 196 | // When type URL contains any characters except [0-9A-Za-z./\-]*, it must be quoted. |
| 197 | for _, ch := range u { |
| 198 | switch { |
| 199 | case ch == '.' || ch == '/' || ch == '_': |
| 200 | continue |
| 201 | case '0' <= ch && ch <= '9': |
| 202 | continue |
| 203 | case 'A' <= ch && ch <= 'Z': |
| 204 | continue |
| 205 | case 'a' <= ch && ch <= 'z': |
| 206 | continue |
| 207 | default: |
| 208 | return true |
| 209 | } |
| 210 | } |
| 211 | return false |
| 212 | } |
| 213 | |
| 214 | // writeProto3Any writes an expanded google.protobuf.Any message. |
| 215 | // |
| 216 | // It returns (false, nil) if sv value can't be unmarshaled (e.g. because |
| 217 | // required messages are not linked in). |
| 218 | // |
| 219 | // It returns (true, error) when sv was written in expanded format or an error |
| 220 | // was encountered. |
| 221 | func (w *textWriter) writeProto3Any(m protoreflect.Message) (bool, error) { |
| 222 | md := m.Descriptor() |
| 223 | fdURL := md.Fields().ByName("type_url") |
| 224 | fdVal := md.Fields().ByName("value") |
| 225 | |
| 226 | url := m.Get(fdURL).String() |
| 227 | mt, err := protoregistry.GlobalTypes.FindMessageByURL(url) |
| 228 | if err != nil { |
| 229 | return false, nil |
| 230 | } |
| 231 | |
| 232 | b := m.Get(fdVal).Bytes() |
| 233 | m2 := mt.New() |
| 234 | if err := proto.Unmarshal(b, m2.Interface()); err != nil { |
| 235 | return false, nil |
| 236 | } |
| 237 | w.Write([]byte("[")) |
| 238 | if requiresQuotes(url) { |
| 239 | w.writeQuotedString(url) |
| 240 | } else { |
| 241 | w.Write([]byte(url)) |
| 242 | } |
| 243 | if w.compact { |
| 244 | w.Write([]byte("]:<")) |
| 245 | } else { |
| 246 | w.Write([]byte("]: <\n")) |
| 247 | w.indent++ |
| 248 | } |
| 249 | if err := w.writeMessage(m2); err != nil { |
| 250 | return true, err |
| 251 | } |
| 252 | if w.compact { |
| 253 | w.Write([]byte("> ")) |
| 254 | } else { |
| 255 | w.indent-- |
| 256 | w.Write([]byte(">\n")) |
| 257 | } |
| 258 | return true, nil |
| 259 | } |
| 260 | |
| 261 | func (w *textWriter) writeMessage(m protoreflect.Message) error { |
| 262 | md := m.Descriptor() |
| 263 | if w.expandAny && md.FullName() == "google.protobuf.Any" { |
| 264 | if canExpand, err := w.writeProto3Any(m); canExpand { |
| 265 | return err |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | fds := md.Fields() |
| 270 | for i := 0; i < fds.Len(); { |
| 271 | fd := fds.Get(i) |
| 272 | if od := fd.ContainingOneof(); od != nil { |
| 273 | fd = m.WhichOneof(od) |
| 274 | i += od.Fields().Len() |
| 275 | } else { |
| 276 | i++ |
| 277 | } |
| 278 | if fd == nil || !m.Has(fd) { |
| 279 | continue |
| 280 | } |
| 281 | |
| 282 | switch { |
| 283 | case fd.IsList(): |
| 284 | lv := m.Get(fd).List() |
| 285 | for j := 0; j < lv.Len(); j++ { |
| 286 | w.writeName(fd) |
| 287 | v := lv.Get(j) |
| 288 | if err := w.writeSingularValue(v, fd); err != nil { |
| 289 | return err |
| 290 | } |
| 291 | w.WriteByte('\n') |
| 292 | } |
| 293 | case fd.IsMap(): |
| 294 | kfd := fd.MapKey() |
| 295 | vfd := fd.MapValue() |
| 296 | mv := m.Get(fd).Map() |
| 297 | |
| 298 | type entry struct{ key, val protoreflect.Value } |
| 299 | var entries []entry |
| 300 | mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool { |
| 301 | entries = append(entries, entry{k.Value(), v}) |
| 302 | return true |
| 303 | }) |
| 304 | sort.Slice(entries, func(i, j int) bool { |
| 305 | switch kfd.Kind() { |
| 306 | case protoreflect.BoolKind: |
| 307 | return !entries[i].key.Bool() && entries[j].key.Bool() |
| 308 | case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind: |
| 309 | return entries[i].key.Int() < entries[j].key.Int() |
| 310 | case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind: |
| 311 | return entries[i].key.Uint() < entries[j].key.Uint() |
| 312 | case protoreflect.StringKind: |
| 313 | return entries[i].key.String() < entries[j].key.String() |
| 314 | default: |
| 315 | panic("invalid kind") |
| 316 | } |
| 317 | }) |
| 318 | for _, entry := range entries { |
| 319 | w.writeName(fd) |
| 320 | w.WriteByte('<') |
| 321 | if !w.compact { |
| 322 | w.WriteByte('\n') |
| 323 | } |
| 324 | w.indent++ |
| 325 | w.writeName(kfd) |
| 326 | if err := w.writeSingularValue(entry.key, kfd); err != nil { |
| 327 | return err |
| 328 | } |
| 329 | w.WriteByte('\n') |
| 330 | w.writeName(vfd) |
| 331 | if err := w.writeSingularValue(entry.val, vfd); err != nil { |
| 332 | return err |
| 333 | } |
| 334 | w.WriteByte('\n') |
| 335 | w.indent-- |
| 336 | w.WriteByte('>') |
| 337 | w.WriteByte('\n') |
| 338 | } |
| 339 | default: |
| 340 | w.writeName(fd) |
| 341 | if err := w.writeSingularValue(m.Get(fd), fd); err != nil { |
| 342 | return err |
| 343 | } |
| 344 | w.WriteByte('\n') |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | if b := m.GetUnknown(); len(b) > 0 { |
| 349 | w.writeUnknownFields(b) |
| 350 | } |
| 351 | return w.writeExtensions(m) |
| 352 | } |
| 353 | |
| 354 | func (w *textWriter) writeSingularValue(v protoreflect.Value, fd protoreflect.FieldDescriptor) error { |
| 355 | switch fd.Kind() { |
| 356 | case protoreflect.FloatKind, protoreflect.DoubleKind: |
| 357 | switch vf := v.Float(); { |
| 358 | case math.IsInf(vf, +1): |
| 359 | w.Write(posInf) |
| 360 | case math.IsInf(vf, -1): |
| 361 | w.Write(negInf) |
| 362 | case math.IsNaN(vf): |
| 363 | w.Write(nan) |
| 364 | default: |
| 365 | fmt.Fprint(w, v.Interface()) |
| 366 | } |
| 367 | case protoreflect.StringKind: |
| 368 | // NOTE: This does not validate UTF-8 for historical reasons. |
| 369 | w.writeQuotedString(string(v.String())) |
| 370 | case protoreflect.BytesKind: |
| 371 | w.writeQuotedString(string(v.Bytes())) |
| 372 | case protoreflect.MessageKind, protoreflect.GroupKind: |
| 373 | var bra, ket byte = '<', '>' |
| 374 | if fd.Kind() == protoreflect.GroupKind { |
| 375 | bra, ket = '{', '}' |
| 376 | } |
| 377 | w.WriteByte(bra) |
| 378 | if !w.compact { |
| 379 | w.WriteByte('\n') |
| 380 | } |
| 381 | w.indent++ |
| 382 | m := v.Message() |
| 383 | if m2, ok := m.Interface().(encoding.TextMarshaler); ok { |
| 384 | b, err := m2.MarshalText() |
| 385 | if err != nil { |
| 386 | return err |
| 387 | } |
| 388 | w.Write(b) |
| 389 | } else { |
| 390 | w.writeMessage(m) |
| 391 | } |
| 392 | w.indent-- |
| 393 | w.WriteByte(ket) |
| 394 | case protoreflect.EnumKind: |
| 395 | if ev := fd.Enum().Values().ByNumber(v.Enum()); ev != nil { |
| 396 | fmt.Fprint(w, ev.Name()) |
| 397 | } else { |
| 398 | fmt.Fprint(w, v.Enum()) |
| 399 | } |
| 400 | default: |
| 401 | fmt.Fprint(w, v.Interface()) |
| 402 | } |
| 403 | return nil |
| 404 | } |
| 405 | |
| 406 | // writeQuotedString writes a quoted string in the protocol buffer text format. |
| 407 | func (w *textWriter) writeQuotedString(s string) { |
| 408 | w.WriteByte('"') |
| 409 | for i := 0; i < len(s); i++ { |
| 410 | switch c := s[i]; c { |
| 411 | case '\n': |
| 412 | w.buf = append(w.buf, `\n`...) |
| 413 | case '\r': |
| 414 | w.buf = append(w.buf, `\r`...) |
| 415 | case '\t': |
| 416 | w.buf = append(w.buf, `\t`...) |
| 417 | case '"': |
| 418 | w.buf = append(w.buf, `\"`...) |
| 419 | case '\\': |
| 420 | w.buf = append(w.buf, `\\`...) |
| 421 | default: |
| 422 | if isPrint := c >= 0x20 && c < 0x7f; isPrint { |
| 423 | w.buf = append(w.buf, c) |
| 424 | } else { |
| 425 | w.buf = append(w.buf, fmt.Sprintf(`\%03o`, c)...) |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | w.WriteByte('"') |
| 430 | } |
| 431 | |
| 432 | func (w *textWriter) writeUnknownFields(b []byte) { |
| 433 | if !w.compact { |
| 434 | fmt.Fprintf(w, "/* %d unknown bytes */\n", len(b)) |
| 435 | } |
| 436 | |
| 437 | for len(b) > 0 { |
| 438 | num, wtyp, n := protowire.ConsumeTag(b) |
| 439 | if n < 0 { |
| 440 | return |
| 441 | } |
| 442 | b = b[n:] |
| 443 | |
| 444 | if wtyp == protowire.EndGroupType { |
| 445 | w.indent-- |
| 446 | w.Write(endBraceNewline) |
| 447 | continue |
| 448 | } |
| 449 | fmt.Fprint(w, num) |
| 450 | if wtyp != protowire.StartGroupType { |
| 451 | w.WriteByte(':') |
| 452 | } |
| 453 | if !w.compact || wtyp == protowire.StartGroupType { |
| 454 | w.WriteByte(' ') |
| 455 | } |
| 456 | switch wtyp { |
| 457 | case protowire.VarintType: |
| 458 | v, n := protowire.ConsumeVarint(b) |
| 459 | if n < 0 { |
| 460 | return |
| 461 | } |
| 462 | b = b[n:] |
| 463 | fmt.Fprint(w, v) |
| 464 | case protowire.Fixed32Type: |
| 465 | v, n := protowire.ConsumeFixed32(b) |
| 466 | if n < 0 { |
| 467 | return |
| 468 | } |
| 469 | b = b[n:] |
| 470 | fmt.Fprint(w, v) |
| 471 | case protowire.Fixed64Type: |
| 472 | v, n := protowire.ConsumeFixed64(b) |
| 473 | if n < 0 { |
| 474 | return |
| 475 | } |
| 476 | b = b[n:] |
| 477 | fmt.Fprint(w, v) |
| 478 | case protowire.BytesType: |
| 479 | v, n := protowire.ConsumeBytes(b) |
| 480 | if n < 0 { |
| 481 | return |
| 482 | } |
| 483 | b = b[n:] |
| 484 | fmt.Fprintf(w, "%q", v) |
| 485 | case protowire.StartGroupType: |
| 486 | w.WriteByte('{') |
| 487 | w.indent++ |
| 488 | default: |
| 489 | fmt.Fprintf(w, "/* unknown wire type %d */", wtyp) |
| 490 | } |
| 491 | w.WriteByte('\n') |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | // writeExtensions writes all the extensions in m. |
| 496 | func (w *textWriter) writeExtensions(m protoreflect.Message) error { |
| 497 | md := m.Descriptor() |
| 498 | if md.ExtensionRanges().Len() == 0 { |
| 499 | return nil |
| 500 | } |
| 501 | |
| 502 | type ext struct { |
| 503 | desc protoreflect.FieldDescriptor |
| 504 | val protoreflect.Value |
| 505 | } |
| 506 | var exts []ext |
| 507 | m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool { |
| 508 | if fd.IsExtension() { |
| 509 | exts = append(exts, ext{fd, v}) |
| 510 | } |
| 511 | return true |
| 512 | }) |
| 513 | sort.Slice(exts, func(i, j int) bool { |
| 514 | return exts[i].desc.Number() < exts[j].desc.Number() |
| 515 | }) |
| 516 | |
| 517 | for _, ext := range exts { |
| 518 | // For message set, use the name of the message as the extension name. |
| 519 | name := string(ext.desc.FullName()) |
| 520 | if isMessageSet(ext.desc.ContainingMessage()) { |
| 521 | name = strings.TrimSuffix(name, ".message_set_extension") |
| 522 | } |
| 523 | |
| 524 | if !ext.desc.IsList() { |
| 525 | if err := w.writeSingularExtension(name, ext.val, ext.desc); err != nil { |
| 526 | return err |
| 527 | } |
| 528 | } else { |
| 529 | lv := ext.val.List() |
| 530 | for i := 0; i < lv.Len(); i++ { |
| 531 | if err := w.writeSingularExtension(name, lv.Get(i), ext.desc); err != nil { |
| 532 | return err |
| 533 | } |
| 534 | } |
| 535 | } |
| 536 | } |
| 537 | return nil |
| 538 | } |
| 539 | |
| 540 | func (w *textWriter) writeSingularExtension(name string, v protoreflect.Value, fd protoreflect.FieldDescriptor) error { |
| 541 | fmt.Fprintf(w, "[%s]:", name) |
| 542 | if !w.compact { |
| 543 | w.WriteByte(' ') |
| 544 | } |
| 545 | if err := w.writeSingularValue(v, fd); err != nil { |
| 546 | return err |
| 547 | } |
| 548 | w.WriteByte('\n') |
| 549 | return nil |
| 550 | } |
| 551 | |
| 552 | func (w *textWriter) writeIndent() { |
| 553 | if !w.complete { |
| 554 | return |
| 555 | } |
| 556 | for i := 0; i < w.indent*2; i++ { |
| 557 | w.buf = append(w.buf, ' ') |
| 558 | } |
| 559 | w.complete = false |
| 560 | } |