blob: 9f840fa2f9fee048d4cf1a50948d01f502fd9f88 [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
3// Copyright 2010 Google Inc. All rights reserved.
4// http://code.google.com/p/goprotobuf/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32package proto
33
Rob Pikeaaa3a622010-03-20 22:32:34 -070034/*
35 * Types and routines for supporting protocol buffer extensions.
36 */
37
38import (
Rob Pikea17fdd92011-11-02 12:43:05 -070039 "errors"
Rob Pikeaaa3a622010-03-20 22:32:34 -070040 "reflect"
David Symondse37856c2011-06-22 12:52:53 +100041 "strconv"
Rob Pikeaaa3a622010-03-20 22:32:34 -070042)
43
David Symondsa7e9ef92012-01-18 18:27:58 +110044// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
45var ErrMissingExtension = errors.New("proto: missing extension")
46
Rob Pikeaaa3a622010-03-20 22:32:34 -070047// ExtensionRange represents a range of message extensions for a protocol buffer.
48// Used in code generated by the protocol compiler.
49type ExtensionRange struct {
50 Start, End int32 // both inclusive
51}
52
53// extendableProto is an interface implemented by any protocol buffer that may be extended.
54type extendableProto interface {
55 ExtensionRangeArray() []ExtensionRange
David Symonds1d72f7a2011-08-19 18:28:52 +100056 ExtensionMap() map[int32]Extension
Rob Pikeaaa3a622010-03-20 22:32:34 -070057}
58
59// ExtensionDesc represents an extension specification.
60// Used in generated code from the protocol compiler.
61type ExtensionDesc struct {
62 ExtendedType interface{} // nil pointer to the type that is being extended
63 ExtensionType interface{} // nil pointer to the extension type
64 Field int32 // field number
David Symondse37856c2011-06-22 12:52:53 +100065 Name string // fully-qualified name of extension
David Symonds8935abf2011-07-04 15:53:16 +100066 Tag string // protobuf tag style
Rob Pikeaaa3a622010-03-20 22:32:34 -070067}
68
David Symonds1d72f7a2011-08-19 18:28:52 +100069/*
70Extension represents an extension in a message.
71
72When an extension is stored in a message using SetExtension
73only desc and value are set. When the message is marshaled
74enc will be set to the encoded form of the message.
75
76When a message is unmarshaled and contains extensions, each
77extension will have only enc set. When such an extension is
78accessed using GetExtension (or GetExtensions) desc and value
79will be set.
80*/
81type Extension struct {
82 desc *ExtensionDesc
83 value interface{}
84 enc []byte
85}
86
87// SetRawExtension is for testing only.
88func SetRawExtension(base extendableProto, id int32, b []byte) {
89 base.ExtensionMap()[id] = Extension{enc: b}
90}
91
David Symonds940b9612011-04-01 10:45:23 +110092// isExtensionField returns true iff the given field number is in an extension range.
93func isExtensionField(pb extendableProto, field int32) bool {
94 for _, er := range pb.ExtensionRangeArray() {
Rob Pikeaaa3a622010-03-20 22:32:34 -070095 if er.Start <= field && field <= er.End {
96 return true
97 }
98 }
99 return false
100}
101
David Symonds940b9612011-04-01 10:45:23 +1100102// checkExtensionTypes checks that the given extension is valid for pb.
Rob Pikea17fdd92011-11-02 12:43:05 -0700103func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700104 // Check the extended type.
Nigel Tao4ede8452011-04-28 11:27:25 +1000105 if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
Rob Pikea17fdd92011-11-02 12:43:05 -0700106 return errors.New("bad extended type; " + b.String() + " does not extend " + a.String())
Rob Pikeaaa3a622010-03-20 22:32:34 -0700107 }
108 // Check the range.
David Symonds940b9612011-04-01 10:45:23 +1100109 if !isExtensionField(pb, extension.Field) {
Rob Pikea17fdd92011-11-02 12:43:05 -0700110 return errors.New("bad extension number; not in declared ranges")
Rob Pikeaaa3a622010-03-20 22:32:34 -0700111 }
112 return nil
113}
114
David Symonds1d72f7a2011-08-19 18:28:52 +1000115// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
Rob Pikea17fdd92011-11-02 12:43:05 -0700116func encodeExtensionMap(m map[int32]Extension) error {
David Symonds1d72f7a2011-08-19 18:28:52 +1000117 for k, e := range m {
118 if e.value == nil || e.desc == nil {
119 // Extension is only in its encoded form.
120 continue
121 }
122
123 // We don't skip extensions that have an encoded form set,
124 // because the extension value may have been mutated after
125 // the last time this function was called.
126
127 et := reflect.TypeOf(e.desc.ExtensionType)
128 props := new(Properties)
129 props.Init(et, "unknown_name", e.desc.Tag, 0)
130
131 p := NewBuffer(nil)
132 // The encoder must be passed a pointer to e.value.
133 // Allocate a copy of value so that we can use its address.
134 x := reflect.New(et)
135 x.Elem().Set(reflect.ValueOf(e.value))
136 if err := props.enc(p, props, x.Pointer()); err != nil {
137 return err
138 }
139 e.enc = p.buf
140 m[k] = e
141 }
142 return nil
143}
144
David Symonds940b9612011-04-01 10:45:23 +1100145// HasExtension returns whether the given extension is present in pb.
146func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700147 // TODO: Check types, field numbers, etc.?
David Symonds940b9612011-04-01 10:45:23 +1100148 _, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700149 return ok
150}
151
David Symonds940b9612011-04-01 10:45:23 +1100152// ClearExtension removes the given extension from pb.
153func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700154 // TODO: Check types, field numbers, etc.?
Rob Pikef48475f2011-10-28 12:07:40 -0700155 delete(pb.ExtensionMap(), extension.Field)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700156}
157
David Symonds940b9612011-04-01 10:45:23 +1100158// GetExtension parses and returns the given extension of pb.
David Symondsa7e9ef92012-01-18 18:27:58 +1100159// If the extension is not present it returns ErrMissingExtension.
Rob Pikea17fdd92011-11-02 12:43:05 -0700160func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
David Symonds940b9612011-04-01 10:45:23 +1100161 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700162 return nil, err
163 }
164
David Symonds1d72f7a2011-08-19 18:28:52 +1000165 e, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700166 if !ok {
David Symondsa7e9ef92012-01-18 18:27:58 +1100167 return nil, ErrMissingExtension
Rob Pikeaaa3a622010-03-20 22:32:34 -0700168 }
David Symonds1d72f7a2011-08-19 18:28:52 +1000169 if e.value != nil {
170 // Already decoded. Check the descriptor, though.
171 if e.desc != extension {
172 // This shouldn't happen. If it does, it means that
173 // GetExtension was called twice with two different
174 // descriptors with the same field number.
Rob Pikea17fdd92011-11-02 12:43:05 -0700175 return nil, errors.New("proto: descriptor conflict")
David Symonds1d72f7a2011-08-19 18:28:52 +1000176 }
177 return e.value, nil
178 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700179
David Symondsa4b61c02011-09-07 14:06:00 +1000180 v, err := decodeExtension(e.enc, extension)
181 if err != nil {
182 return nil, err
183 }
184
185 // Remember the decoded version and drop the encoded version.
186 // That way it is safe to mutate what we return.
187 e.value = v
188 e.desc = extension
189 e.enc = nil
190 return e.value, nil
191}
192
193// decodeExtension decodes an extension encoded in b.
Rob Pikea17fdd92011-11-02 12:43:05 -0700194func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700195 // Discard wire type and field number varint. It isn't needed.
David Symondsa4b61c02011-09-07 14:06:00 +1000196 _, n := DecodeVarint(b)
197 o := NewBuffer(b[n:])
Rob Pikeaaa3a622010-03-20 22:32:34 -0700198
Nigel Tao4ede8452011-04-28 11:27:25 +1000199 t := reflect.TypeOf(extension.ExtensionType)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700200 props := &Properties{}
201 props.Init(t, "irrelevant_name", extension.Tag, 0)
202
Rob Pikeb0447c02011-10-20 15:20:04 -0700203 // t is a pointer, likely to a struct.
204 // Allocate a "field" to store the pointer itself; the
205 // struct pointer will be stored here. We pass
206 // the address of this field to props.dec.
207 value := reflect.New(t).Elem()
208 if err := props.dec(o, props, value.UnsafeAddr()); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700209 return nil, err
210 }
Rob Pikeb0447c02011-10-20 15:20:04 -0700211 return value.Interface(), nil
Rob Pikeaaa3a622010-03-20 22:32:34 -0700212}
213
David Symonds940b9612011-04-01 10:45:23 +1100214// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
215// The returned slice has the same length as es; missing extensions will appear as nil elements.
Rob Pikea17fdd92011-11-02 12:43:05 -0700216func GetExtensions(pb interface{}, es []*ExtensionDesc) (extensions []interface{}, err error) {
David Symonds940b9612011-04-01 10:45:23 +1100217 epb, ok := pb.(extendableProto)
218 if !ok {
Rob Pikea17fdd92011-11-02 12:43:05 -0700219 err = errors.New("not an extendable proto")
David Symonds940b9612011-04-01 10:45:23 +1100220 return
221 }
222 extensions = make([]interface{}, len(es))
223 for i, e := range es {
224 extensions[i], err = GetExtension(epb, e)
225 if err != nil {
226 return
227 }
228 }
229 return
230}
231
David Symondsc37ad662010-04-07 09:25:13 +1000232// TODO: (needed for repeated extensions)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700233// - ExtensionSize
234// - AddExtension
235
David Symonds940b9612011-04-01 10:45:23 +1100236// SetExtension sets the specified extension of pb to the specified value.
Rob Pikea17fdd92011-11-02 12:43:05 -0700237func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
David Symonds940b9612011-04-01 10:45:23 +1100238 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700239 return err
240 }
Nigel Tao4ede8452011-04-28 11:27:25 +1000241 typ := reflect.TypeOf(extension.ExtensionType)
242 if typ != reflect.TypeOf(value) {
Rob Pikea17fdd92011-11-02 12:43:05 -0700243 return errors.New("bad extension value type")
Rob Pikeaaa3a622010-03-20 22:32:34 -0700244 }
245
David Symonds1d72f7a2011-08-19 18:28:52 +1000246 pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
Rob Pikeaaa3a622010-03-20 22:32:34 -0700247 return nil
248}
David Symondse37856c2011-06-22 12:52:53 +1000249
250// A global registry of extensions.
251// The generated code will register the generated descriptors by calling RegisterExtension.
252
253var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
254
255// RegisterExtension is called from the generated code.
256func RegisterExtension(desc *ExtensionDesc) {
257 st := reflect.TypeOf(desc.ExtendedType).Elem()
258 m := extensionMaps[st]
259 if m == nil {
260 m = make(map[int32]*ExtensionDesc)
261 extensionMaps[st] = m
262 }
263 if _, ok := m[desc.Field]; ok {
264 panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
265 }
266 m[desc.Field] = desc
267}
David Symondsc7cb3fd2011-08-29 16:49:58 +1000268
269// RegisteredExtensions returns a map of the registered extensions of a
270// protocol buffer struct, indexed by the extension number.
271// The argument pb should be a nil pointer to the struct type.
272func RegisteredExtensions(pb interface{}) map[int32]*ExtensionDesc {
273 return extensionMaps[reflect.TypeOf(pb).Elem()]
274}