blob: cde2c20d3e8f3a5978e551cdd6b068048b4d54f5 [file] [log] [blame]
Scott Baker105df152020-04-13 15:55:14 -07001// Copyright 2018 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
5package prototext
6
7import (
8 "fmt"
9 "strings"
10 "unicode/utf8"
11
12 "google.golang.org/protobuf/internal/encoding/messageset"
13 "google.golang.org/protobuf/internal/encoding/text"
14 "google.golang.org/protobuf/internal/errors"
15 "google.golang.org/protobuf/internal/fieldnum"
16 "google.golang.org/protobuf/internal/flags"
17 "google.golang.org/protobuf/internal/pragma"
18 "google.golang.org/protobuf/internal/set"
19 "google.golang.org/protobuf/proto"
20 pref "google.golang.org/protobuf/reflect/protoreflect"
21 "google.golang.org/protobuf/reflect/protoregistry"
22)
23
24// Unmarshal reads the given []byte into the given proto.Message.
25func Unmarshal(b []byte, m proto.Message) error {
26 return UnmarshalOptions{}.Unmarshal(b, m)
27}
28
29// UnmarshalOptions is a configurable textproto format unmarshaler.
30type UnmarshalOptions struct {
31 pragma.NoUnkeyedLiterals
32
33 // AllowPartial accepts input for messages that will result in missing
34 // required fields. If AllowPartial is false (the default), Unmarshal will
35 // return error if there are any missing required fields.
36 AllowPartial bool
37
38 // DiscardUnknown specifies whether to ignore unknown fields when parsing.
39 // An unknown field is any field whose field name or field number does not
40 // resolve to any known or extension field in the message.
41 // By default, unmarshal rejects unknown fields as an error.
42 DiscardUnknown bool
43
44 // Resolver is used for looking up types when unmarshaling
45 // google.protobuf.Any messages or extension fields.
46 // If nil, this defaults to using protoregistry.GlobalTypes.
47 Resolver interface {
48 protoregistry.MessageTypeResolver
49 protoregistry.ExtensionTypeResolver
50 }
51}
52
53// Unmarshal reads the given []byte and populates the given proto.Message using options in
54// UnmarshalOptions object.
55func (o UnmarshalOptions) Unmarshal(b []byte, m proto.Message) error {
56 proto.Reset(m)
57
58 if o.Resolver == nil {
59 o.Resolver = protoregistry.GlobalTypes
60 }
61
62 dec := decoder{text.NewDecoder(b), o}
63 if err := dec.unmarshalMessage(m.ProtoReflect(), false); err != nil {
64 return err
65 }
66 if o.AllowPartial {
67 return nil
68 }
69 return proto.CheckInitialized(m)
70}
71
72type decoder struct {
73 *text.Decoder
74 opts UnmarshalOptions
75}
76
77// newError returns an error object with position info.
78func (d decoder) newError(pos int, f string, x ...interface{}) error {
79 line, column := d.Position(pos)
80 head := fmt.Sprintf("(line %d:%d): ", line, column)
81 return errors.New(head+f, x...)
82}
83
84// unexpectedTokenError returns a syntax error for the given unexpected token.
85func (d decoder) unexpectedTokenError(tok text.Token) error {
86 return d.syntaxError(tok.Pos(), "unexpected token: %s", tok.RawString())
87}
88
89// syntaxError returns a syntax error for given position.
90func (d decoder) syntaxError(pos int, f string, x ...interface{}) error {
91 line, column := d.Position(pos)
92 head := fmt.Sprintf("syntax error (line %d:%d): ", line, column)
93 return errors.New(head+f, x...)
94}
95
96// unmarshalMessage unmarshals into the given protoreflect.Message.
97func (d decoder) unmarshalMessage(m pref.Message, checkDelims bool) error {
98 messageDesc := m.Descriptor()
99 if !flags.ProtoLegacy && messageset.IsMessageSet(messageDesc) {
100 return errors.New("no support for proto1 MessageSets")
101 }
102
103 if messageDesc.FullName() == "google.protobuf.Any" {
104 return d.unmarshalAny(m, checkDelims)
105 }
106
107 if checkDelims {
108 tok, err := d.Read()
109 if err != nil {
110 return err
111 }
112
113 if tok.Kind() != text.MessageOpen {
114 return d.unexpectedTokenError(tok)
115 }
116 }
117
118 var seenNums set.Ints
119 var seenOneofs set.Ints
120 fieldDescs := messageDesc.Fields()
121
122 for {
123 // Read field name.
124 tok, err := d.Read()
125 if err != nil {
126 return err
127 }
128 switch typ := tok.Kind(); typ {
129 case text.Name:
130 // Continue below.
131 case text.EOF:
132 if checkDelims {
133 return text.ErrUnexpectedEOF
134 }
135 return nil
136 default:
137 if checkDelims && typ == text.MessageClose {
138 return nil
139 }
140 return d.unexpectedTokenError(tok)
141 }
142
143 // Resolve the field descriptor.
144 var name pref.Name
145 var fd pref.FieldDescriptor
146 var xt pref.ExtensionType
147 var xtErr error
148 var isFieldNumberName bool
149
150 switch tok.NameKind() {
151 case text.IdentName:
152 name = pref.Name(tok.IdentName())
153 fd = fieldDescs.ByName(name)
154 if fd == nil {
155 // The proto name of a group field is in all lowercase,
156 // while the textproto field name is the group message name.
157 gd := fieldDescs.ByName(pref.Name(strings.ToLower(string(name))))
158 if gd != nil && gd.Kind() == pref.GroupKind && gd.Message().Name() == name {
159 fd = gd
160 }
161 } else if fd.Kind() == pref.GroupKind && fd.Message().Name() != name {
162 fd = nil // reset since field name is actually the message name
163 }
164
165 case text.TypeName:
166 // Handle extensions only. This code path is not for Any.
167 xt, xtErr = d.findExtension(pref.FullName(tok.TypeName()))
168
169 case text.FieldNumber:
170 isFieldNumberName = true
171 num := pref.FieldNumber(tok.FieldNumber())
172 if !num.IsValid() {
173 return d.newError(tok.Pos(), "invalid field number: %d", num)
174 }
175 fd = fieldDescs.ByNumber(num)
176 if fd == nil {
177 xt, xtErr = d.opts.Resolver.FindExtensionByNumber(messageDesc.FullName(), num)
178 }
179 }
180
181 if xt != nil {
182 fd = xt.TypeDescriptor()
183 if !messageDesc.ExtensionRanges().Has(fd.Number()) || fd.ContainingMessage().FullName() != messageDesc.FullName() {
184 return d.newError(tok.Pos(), "message %v cannot be extended by %v", messageDesc.FullName(), fd.FullName())
185 }
186 } else if xtErr != nil && xtErr != protoregistry.NotFound {
187 return d.newError(tok.Pos(), "unable to resolve [%s]: %v", tok.RawString(), xtErr)
188 }
189 if flags.ProtoLegacy {
190 if fd != nil && fd.IsWeak() && fd.Message().IsPlaceholder() {
191 fd = nil // reset since the weak reference is not linked in
192 }
193 }
194
195 // Handle unknown fields.
196 if fd == nil {
197 if d.opts.DiscardUnknown || messageDesc.ReservedNames().Has(name) {
198 d.skipValue()
199 continue
200 }
201 return d.newError(tok.Pos(), "unknown field: %v", tok.RawString())
202 }
203
204 // Handle fields identified by field number.
205 if isFieldNumberName {
206 // TODO: Add an option to permit parsing field numbers.
207 //
208 // This requires careful thought as the MarshalOptions.EmitUnknown
209 // option allows formatting unknown fields as the field number and the
210 // best-effort textual representation of the field value. In that case,
211 // it may not be possible to unmarshal the value from a parser that does
212 // have information about the unknown field.
213 return d.newError(tok.Pos(), "cannot specify field by number: %v", tok.RawString())
214 }
215
216 switch {
217 case fd.IsList():
218 kind := fd.Kind()
219 if kind != pref.MessageKind && kind != pref.GroupKind && !tok.HasSeparator() {
220 return d.syntaxError(tok.Pos(), "missing field separator :")
221 }
222
223 list := m.Mutable(fd).List()
224 if err := d.unmarshalList(fd, list); err != nil {
225 return err
226 }
227
228 case fd.IsMap():
229 mmap := m.Mutable(fd).Map()
230 if err := d.unmarshalMap(fd, mmap); err != nil {
231 return err
232 }
233
234 default:
235 kind := fd.Kind()
236 if kind != pref.MessageKind && kind != pref.GroupKind && !tok.HasSeparator() {
237 return d.syntaxError(tok.Pos(), "missing field separator :")
238 }
239
240 // If field is a oneof, check if it has already been set.
241 if od := fd.ContainingOneof(); od != nil {
242 idx := uint64(od.Index())
243 if seenOneofs.Has(idx) {
244 return d.newError(tok.Pos(), "error parsing %q, oneof %v is already set", tok.RawString(), od.FullName())
245 }
246 seenOneofs.Set(idx)
247 }
248
249 num := uint64(fd.Number())
250 if seenNums.Has(num) {
251 return d.newError(tok.Pos(), "non-repeated field %q is repeated", tok.RawString())
252 }
253
254 if err := d.unmarshalSingular(fd, m); err != nil {
255 return err
256 }
257 seenNums.Set(num)
258 }
259 }
260
261 return nil
262}
263
264// findExtension returns protoreflect.ExtensionType from the Resolver if found.
265func (d decoder) findExtension(xtName pref.FullName) (pref.ExtensionType, error) {
266 xt, err := d.opts.Resolver.FindExtensionByName(xtName)
267 if err == nil {
268 return xt, nil
269 }
270 return messageset.FindMessageSetExtension(d.opts.Resolver, xtName)
271}
272
273// unmarshalSingular unmarshals a non-repeated field value specified by the
274// given FieldDescriptor.
275func (d decoder) unmarshalSingular(fd pref.FieldDescriptor, m pref.Message) error {
276 var val pref.Value
277 var err error
278 switch fd.Kind() {
279 case pref.MessageKind, pref.GroupKind:
280 val = m.NewField(fd)
281 err = d.unmarshalMessage(val.Message(), true)
282 default:
283 val, err = d.unmarshalScalar(fd)
284 }
285 if err == nil {
286 m.Set(fd, val)
287 }
288 return err
289}
290
291// unmarshalScalar unmarshals a scalar/enum protoreflect.Value specified by the
292// given FieldDescriptor.
293func (d decoder) unmarshalScalar(fd pref.FieldDescriptor) (pref.Value, error) {
294 tok, err := d.Read()
295 if err != nil {
296 return pref.Value{}, err
297 }
298
299 if tok.Kind() != text.Scalar {
300 return pref.Value{}, d.unexpectedTokenError(tok)
301 }
302
303 kind := fd.Kind()
304 switch kind {
305 case pref.BoolKind:
306 if b, ok := tok.Bool(); ok {
307 return pref.ValueOfBool(b), nil
308 }
309
310 case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
311 if n, ok := tok.Int32(); ok {
312 return pref.ValueOfInt32(n), nil
313 }
314
315 case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
316 if n, ok := tok.Int64(); ok {
317 return pref.ValueOfInt64(n), nil
318 }
319
320 case pref.Uint32Kind, pref.Fixed32Kind:
321 if n, ok := tok.Uint32(); ok {
322 return pref.ValueOfUint32(n), nil
323 }
324
325 case pref.Uint64Kind, pref.Fixed64Kind:
326 if n, ok := tok.Uint64(); ok {
327 return pref.ValueOfUint64(n), nil
328 }
329
330 case pref.FloatKind:
331 if n, ok := tok.Float32(); ok {
332 return pref.ValueOfFloat32(n), nil
333 }
334
335 case pref.DoubleKind:
336 if n, ok := tok.Float64(); ok {
337 return pref.ValueOfFloat64(n), nil
338 }
339
340 case pref.StringKind:
341 if s, ok := tok.String(); ok {
342 if utf8.ValidString(s) {
343 return pref.ValueOfString(s), nil
344 }
345 return pref.Value{}, d.newError(tok.Pos(), "contains invalid UTF-8")
346 }
347
348 case pref.BytesKind:
349 if b, ok := tok.String(); ok {
350 return pref.ValueOfBytes([]byte(b)), nil
351 }
352
353 case pref.EnumKind:
354 if lit, ok := tok.Enum(); ok {
355 // Lookup EnumNumber based on name.
356 if enumVal := fd.Enum().Values().ByName(pref.Name(lit)); enumVal != nil {
357 return pref.ValueOfEnum(enumVal.Number()), nil
358 }
359 }
360 if num, ok := tok.Int32(); ok {
361 return pref.ValueOfEnum(pref.EnumNumber(num)), nil
362 }
363
364 default:
365 panic(fmt.Sprintf("invalid scalar kind %v", kind))
366 }
367
368 return pref.Value{}, d.newError(tok.Pos(), "invalid value for %v type: %v", kind, tok.RawString())
369}
370
371// unmarshalList unmarshals into given protoreflect.List. A list value can
372// either be in [] syntax or simply just a single scalar/message value.
373func (d decoder) unmarshalList(fd pref.FieldDescriptor, list pref.List) error {
374 tok, err := d.Peek()
375 if err != nil {
376 return err
377 }
378
379 switch fd.Kind() {
380 case pref.MessageKind, pref.GroupKind:
381 switch tok.Kind() {
382 case text.ListOpen:
383 d.Read()
384 for {
385 tok, err := d.Peek()
386 if err != nil {
387 return err
388 }
389
390 switch tok.Kind() {
391 case text.ListClose:
392 d.Read()
393 return nil
394 case text.MessageOpen:
395 pval := list.NewElement()
396 if err := d.unmarshalMessage(pval.Message(), true); err != nil {
397 return err
398 }
399 list.Append(pval)
400 default:
401 return d.unexpectedTokenError(tok)
402 }
403 }
404
405 case text.MessageOpen:
406 pval := list.NewElement()
407 if err := d.unmarshalMessage(pval.Message(), true); err != nil {
408 return err
409 }
410 list.Append(pval)
411 return nil
412 }
413
414 default:
415 switch tok.Kind() {
416 case text.ListOpen:
417 d.Read()
418 for {
419 tok, err := d.Peek()
420 if err != nil {
421 return err
422 }
423
424 switch tok.Kind() {
425 case text.ListClose:
426 d.Read()
427 return nil
428 case text.Scalar:
429 pval, err := d.unmarshalScalar(fd)
430 if err != nil {
431 return err
432 }
433 list.Append(pval)
434 default:
435 return d.unexpectedTokenError(tok)
436 }
437 }
438
439 case text.Scalar:
440 pval, err := d.unmarshalScalar(fd)
441 if err != nil {
442 return err
443 }
444 list.Append(pval)
445 return nil
446 }
447 }
448
449 return d.unexpectedTokenError(tok)
450}
451
452// unmarshalMap unmarshals into given protoreflect.Map. A map value is a
453// textproto message containing {key: <kvalue>, value: <mvalue>}.
454func (d decoder) unmarshalMap(fd pref.FieldDescriptor, mmap pref.Map) error {
455 // Determine ahead whether map entry is a scalar type or a message type in
456 // order to call the appropriate unmarshalMapValue func inside
457 // unmarshalMapEntry.
458 var unmarshalMapValue func() (pref.Value, error)
459 switch fd.MapValue().Kind() {
460 case pref.MessageKind, pref.GroupKind:
461 unmarshalMapValue = func() (pref.Value, error) {
462 pval := mmap.NewValue()
463 if err := d.unmarshalMessage(pval.Message(), true); err != nil {
464 return pref.Value{}, err
465 }
466 return pval, nil
467 }
468 default:
469 unmarshalMapValue = func() (pref.Value, error) {
470 return d.unmarshalScalar(fd.MapValue())
471 }
472 }
473
474 tok, err := d.Read()
475 if err != nil {
476 return err
477 }
478 switch tok.Kind() {
479 case text.MessageOpen:
480 return d.unmarshalMapEntry(fd, mmap, unmarshalMapValue)
481
482 case text.ListOpen:
483 for {
484 tok, err := d.Read()
485 if err != nil {
486 return err
487 }
488 switch tok.Kind() {
489 case text.ListClose:
490 return nil
491 case text.MessageOpen:
492 if err := d.unmarshalMapEntry(fd, mmap, unmarshalMapValue); err != nil {
493 return err
494 }
495 default:
496 return d.unexpectedTokenError(tok)
497 }
498 }
499
500 default:
501 return d.unexpectedTokenError(tok)
502 }
503}
504
505// unmarshalMap unmarshals into given protoreflect.Map. A map value is a
506// textproto message containing {key: <kvalue>, value: <mvalue>}.
507func (d decoder) unmarshalMapEntry(fd pref.FieldDescriptor, mmap pref.Map, unmarshalMapValue func() (pref.Value, error)) error {
508 var key pref.MapKey
509 var pval pref.Value
510Loop:
511 for {
512 // Read field name.
513 tok, err := d.Read()
514 if err != nil {
515 return err
516 }
517 switch tok.Kind() {
518 case text.Name:
519 if tok.NameKind() != text.IdentName {
520 if !d.opts.DiscardUnknown {
521 return d.newError(tok.Pos(), "unknown map entry field %q", tok.RawString())
522 }
523 d.skipValue()
524 continue Loop
525 }
526 // Continue below.
527 case text.MessageClose:
528 break Loop
529 default:
530 return d.unexpectedTokenError(tok)
531 }
532
533 name := tok.IdentName()
534 switch name {
535 case "key":
536 if !tok.HasSeparator() {
537 return d.syntaxError(tok.Pos(), "missing field separator :")
538 }
539 if key.IsValid() {
540 return d.newError(tok.Pos(), `map entry "key" cannot be repeated`)
541 }
542 val, err := d.unmarshalScalar(fd.MapKey())
543 if err != nil {
544 return err
545 }
546 key = val.MapKey()
547
548 case "value":
549 if kind := fd.MapValue().Kind(); (kind != pref.MessageKind) && (kind != pref.GroupKind) {
550 if !tok.HasSeparator() {
551 return d.syntaxError(tok.Pos(), "missing field separator :")
552 }
553 }
554 if pval.IsValid() {
555 return d.newError(tok.Pos(), `map entry "value" cannot be repeated`)
556 }
557 pval, err = unmarshalMapValue()
558 if err != nil {
559 return err
560 }
561
562 default:
563 if !d.opts.DiscardUnknown {
564 return d.newError(tok.Pos(), "unknown map entry field %q", name)
565 }
566 d.skipValue()
567 }
568 }
569
570 if !key.IsValid() {
571 key = fd.MapKey().Default().MapKey()
572 }
573 if !pval.IsValid() {
574 switch fd.MapValue().Kind() {
575 case pref.MessageKind, pref.GroupKind:
576 // If value field is not set for message/group types, construct an
577 // empty one as default.
578 pval = mmap.NewValue()
579 default:
580 pval = fd.MapValue().Default()
581 }
582 }
583 mmap.Set(key, pval)
584 return nil
585}
586
587// unmarshalAny unmarshals an Any textproto. It can either be in expanded form
588// or non-expanded form.
589func (d decoder) unmarshalAny(m pref.Message, checkDelims bool) error {
590 var typeURL string
591 var bValue []byte
592
593 // hasFields tracks which valid fields have been seen in the loop below in
594 // order to flag an error if there are duplicates or conflicts. It may
595 // contain the strings "type_url", "value" and "expanded". The literal
596 // "expanded" is used to indicate that the expanded form has been
597 // encountered already.
598 hasFields := map[string]bool{}
599
600 if checkDelims {
601 tok, err := d.Read()
602 if err != nil {
603 return err
604 }
605
606 if tok.Kind() != text.MessageOpen {
607 return d.unexpectedTokenError(tok)
608 }
609 }
610
611Loop:
612 for {
613 // Read field name. Can only have 3 possible field names, i.e. type_url,
614 // value and type URL name inside [].
615 tok, err := d.Read()
616 if err != nil {
617 return err
618 }
619 if typ := tok.Kind(); typ != text.Name {
620 if checkDelims {
621 if typ == text.MessageClose {
622 break Loop
623 }
624 } else if typ == text.EOF {
625 break Loop
626 }
627 return d.unexpectedTokenError(tok)
628 }
629
630 switch tok.NameKind() {
631 case text.IdentName:
632 // Both type_url and value fields require field separator :.
633 if !tok.HasSeparator() {
634 return d.syntaxError(tok.Pos(), "missing field separator :")
635 }
636
637 switch tok.IdentName() {
638 case "type_url":
639 if hasFields["type_url"] {
640 return d.newError(tok.Pos(), "duplicate Any type_url field")
641 }
642 if hasFields["expanded"] {
643 return d.newError(tok.Pos(), "conflict with [%s] field", typeURL)
644 }
645 tok, err := d.Read()
646 if err != nil {
647 return err
648 }
649 var ok bool
650 typeURL, ok = tok.String()
651 if !ok {
652 return d.newError(tok.Pos(), "invalid Any type_url: %v", tok.RawString())
653 }
654 hasFields["type_url"] = true
655
656 case "value":
657 if hasFields["value"] {
658 return d.newError(tok.Pos(), "duplicate Any value field")
659 }
660 if hasFields["expanded"] {
661 return d.newError(tok.Pos(), "conflict with [%s] field", typeURL)
662 }
663 tok, err := d.Read()
664 if err != nil {
665 return err
666 }
667 s, ok := tok.String()
668 if !ok {
669 return d.newError(tok.Pos(), "invalid Any value: %v", tok.RawString())
670 }
671 bValue = []byte(s)
672 hasFields["value"] = true
673
674 default:
675 if !d.opts.DiscardUnknown {
676 return d.newError(tok.Pos(), "invalid field name %q in google.protobuf.Any message", tok.RawString())
677 }
678 }
679
680 case text.TypeName:
681 if hasFields["expanded"] {
682 return d.newError(tok.Pos(), "cannot have more than one type")
683 }
684 if hasFields["type_url"] {
685 return d.newError(tok.Pos(), "conflict with type_url field")
686 }
687 typeURL = tok.TypeName()
688 var err error
689 bValue, err = d.unmarshalExpandedAny(typeURL, tok.Pos())
690 if err != nil {
691 return err
692 }
693 hasFields["expanded"] = true
694
695 default:
696 if !d.opts.DiscardUnknown {
697 return d.newError(tok.Pos(), "invalid field name %q in google.protobuf.Any message", tok.RawString())
698 }
699 }
700 }
701
702 fds := m.Descriptor().Fields()
703 if len(typeURL) > 0 {
704 m.Set(fds.ByNumber(fieldnum.Any_TypeUrl), pref.ValueOfString(typeURL))
705 }
706 if len(bValue) > 0 {
707 m.Set(fds.ByNumber(fieldnum.Any_Value), pref.ValueOfBytes(bValue))
708 }
709 return nil
710}
711
712func (d decoder) unmarshalExpandedAny(typeURL string, pos int) ([]byte, error) {
713 mt, err := d.opts.Resolver.FindMessageByURL(typeURL)
714 if err != nil {
715 return nil, d.newError(pos, "unable to resolve message [%v]: %v", typeURL, err)
716 }
717 // Create new message for the embedded message type and unmarshal the value
718 // field into it.
719 m := mt.New()
720 if err := d.unmarshalMessage(m, true); err != nil {
721 return nil, err
722 }
723 // Serialize the embedded message and return the resulting bytes.
724 b, err := proto.MarshalOptions{
725 AllowPartial: true, // Never check required fields inside an Any.
726 Deterministic: true,
727 }.Marshal(m.Interface())
728 if err != nil {
729 return nil, d.newError(pos, "error in marshaling message into Any.value: %v", err)
730 }
731 return b, nil
732}
733
734// skipValue makes the decoder parse a field value in order to advance the read
735// to the next field. It relies on Read returning an error if the types are not
736// in valid sequence.
737func (d decoder) skipValue() error {
738 tok, err := d.Read()
739 if err != nil {
740 return err
741 }
742 // Only need to continue reading for messages and lists.
743 switch tok.Kind() {
744 case text.MessageOpen:
745 return d.skipMessageValue()
746
747 case text.ListOpen:
748 for {
749 tok, err := d.Read()
750 if err != nil {
751 return err
752 }
753 switch tok.Kind() {
754 case text.ListClose:
755 return nil
756 case text.MessageOpen:
757 return d.skipMessageValue()
758 default:
759 // Skip items. This will not validate whether skipped values are
760 // of the same type or not, same behavior as C++
761 // TextFormat::Parser::AllowUnknownField(true) version 3.8.0.
762 if err := d.skipValue(); err != nil {
763 return err
764 }
765 }
766 }
767 }
768 return nil
769}
770
771// skipMessageValue makes the decoder parse and skip over all fields in a
772// message. It assumes that the previous read type is MessageOpen.
773func (d decoder) skipMessageValue() error {
774 for {
775 tok, err := d.Read()
776 if err != nil {
777 return err
778 }
779 switch tok.Kind() {
780 case text.MessageClose:
781 return nil
782 case text.Name:
783 if err := d.skipValue(); err != nil {
784 return err
785 }
786 }
787 }
788}