blob: a8b6c0da4dc8a90cf1f90d3dbdf96722fea56018 [file] [log] [blame]
Damien Neil302cb322019-06-19 15:22:13 -07001// Copyright 2019 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 messageset encodes and decodes the obsolete MessageSet wire format.
6package messageset
7
8import (
9 "google.golang.org/protobuf/internal/encoding/wire"
10 "google.golang.org/protobuf/internal/errors"
11 pref "google.golang.org/protobuf/reflect/protoreflect"
12)
13
14// The MessageSet wire format is equivalent to a message defiend as follows,
15// where each Item defines an extension field with a field number of 'type_id'
16// and content of 'message'. MessageSet extensions must be non-repeated message
17// fields.
18//
19// message MessageSet {
20// repeated group Item = 1 {
21// required int32 type_id = 2;
22// required string message = 3;
23// }
24// }
25const (
26 FieldItem = wire.Number(1)
27 FieldTypeID = wire.Number(2)
28 FieldMessage = wire.Number(3)
29)
30
31// IsMessageSet returns whether the message uses the MessageSet wire format.
32func IsMessageSet(md pref.MessageDescriptor) bool {
33 xmd, ok := md.(interface{ IsMessageSet() bool })
34 return ok && xmd.IsMessageSet()
35}
36
37// SizeField returns the size of a MessageSet item field containing an extension
38// with the given field number, not counting the contents of the message subfield.
39func SizeField(num wire.Number) int {
40 return 2*wire.SizeTag(FieldItem) + wire.SizeTag(FieldTypeID) + wire.SizeVarint(uint64(num))
41}
42
43// ConsumeField parses a MessageSet item field and returns the contents of the
44// type_id and message subfields and the total item length.
45func ConsumeField(b []byte) (typeid wire.Number, message []byte, n int, err error) {
46 num, wtyp, n := wire.ConsumeTag(b)
47 if n < 0 {
48 return 0, nil, 0, wire.ParseError(n)
49 }
50 if num != FieldItem || wtyp != wire.StartGroupType {
51 return 0, nil, 0, errors.New("invalid MessageSet field number")
52 }
53 typeid, message, fieldLen, err := ConsumeFieldValue(b[n:], false)
54 if err != nil {
55 return 0, nil, 0, err
56 }
57 return typeid, message, n + fieldLen, nil
58}
59
60// ConsumeFieldValue parses b as a MessageSet item field value until and including
61// the trailing end group marker. It assumes the start group tag has already been parsed.
62// It returns the contents of the type_id and message subfields and the total
63// item length.
64//
65// If wantLen is true, the returned message value includes the length prefix.
66// This is ugly, but simplifies the fast-path decoder in internal/impl.
67func ConsumeFieldValue(b []byte, wantLen bool) (typeid wire.Number, message []byte, n int, err error) {
68 ilen := len(b)
69 for {
70 num, wtyp, n := wire.ConsumeTag(b)
71 if n < 0 {
72 return 0, nil, 0, wire.ParseError(n)
73 }
74 b = b[n:]
75 switch {
76 case num == FieldItem && wtyp == wire.EndGroupType:
77 if wantLen && len(message) == 0 {
78 // The message field was missing, which should never happen.
79 // Be prepared for this case anyway.
80 message = wire.AppendVarint(message, 0)
81 }
82 return typeid, message, ilen - len(b), nil
83 case num == FieldTypeID && wtyp == wire.VarintType:
84 v, n := wire.ConsumeVarint(b)
85 if n < 0 {
86 return 0, nil, 0, wire.ParseError(n)
87 }
88 b = b[n:]
89 typeid = wire.Number(v)
90 case num == FieldMessage && wtyp == wire.BytesType:
91 m, n := wire.ConsumeBytes(b)
92 if n < 0 {
93 return 0, nil, 0, wire.ParseError(n)
94 }
95 if message == nil {
96 if wantLen {
97 message = b[:n]
98 } else {
99 message = m
100 }
101 } else {
102 // This case should never happen in practice, but handle it for
103 // correctness: The MessageSet item contains multiple message
104 // fields, which need to be merged.
105 //
106 // In the case where we're returning the length, this becomes
107 // quite inefficient since we need to strip the length off
108 // the existing data and reconstruct it with the combined length.
109 if wantLen {
110 _, nn := wire.ConsumeVarint(message)
111 m0 := message[nn:]
112 message = message[:0]
113 message = wire.AppendVarint(message, uint64(len(m0)+len(m)))
114 message = append(message, m0...)
115 message = append(message, m...)
116 } else {
117 message = append(message, m...)
118 }
119 }
120 b = b[n:]
121 }
122 }
123}
124
125// AppendFieldStart appends the start of a MessageSet item field containing
126// an extension with the given number. The caller must add the message
127// subfield (including the tag).
128func AppendFieldStart(b []byte, num wire.Number) []byte {
129 b = wire.AppendTag(b, FieldItem, wire.StartGroupType)
130 b = wire.AppendTag(b, FieldTypeID, wire.VarintType)
131 b = wire.AppendVarint(b, uint64(num))
132 return b
133}
134
135// AppendFieldEnd appends the trailing end group marker for a MessageSet item field.
136func AppendFieldEnd(b []byte) []byte {
137 return wire.AppendTag(b, FieldItem, wire.EndGroupType)
138}