blob: e730b68ddf2b7d060538158ca4209673fa7bcdcc [file] [log] [blame]
Rob Pikeaaa3a622010-03-20 22:32:34 -07001// Go support for Protocol Buffers - Google's data interchange format
2//
David Symondsee6e9c52012-11-29 08:51:07 +11003// Copyright 2010 The Go Authors. All rights reserved.
Rob Pikeaaa3a622010-03-20 22:32:34 -07004// 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"
David Symonds29d5d392012-12-20 12:14:58 +110042 "sync"
Rob Pikeaaa3a622010-03-20 22:32:34 -070043)
44
David Symondsa7e9ef92012-01-18 18:27:58 +110045// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
46var ErrMissingExtension = errors.New("proto: missing extension")
47
Rob Pikeaaa3a622010-03-20 22:32:34 -070048// ExtensionRange represents a range of message extensions for a protocol buffer.
49// Used in code generated by the protocol compiler.
50type ExtensionRange struct {
51 Start, End int32 // both inclusive
52}
53
54// extendableProto is an interface implemented by any protocol buffer that may be extended.
55type extendableProto interface {
David Symonds9f60f432012-06-14 09:45:25 +100056 Message
Rob Pikeaaa3a622010-03-20 22:32:34 -070057 ExtensionRangeArray() []ExtensionRange
David Symonds1d72f7a2011-08-19 18:28:52 +100058 ExtensionMap() map[int32]Extension
Rob Pikeaaa3a622010-03-20 22:32:34 -070059}
60
Russ Coxd4ce3f12012-09-12 10:36:26 +100061var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
62
Rob Pikeaaa3a622010-03-20 22:32:34 -070063// ExtensionDesc represents an extension specification.
64// Used in generated code from the protocol compiler.
65type ExtensionDesc struct {
David Symonds9f60f432012-06-14 09:45:25 +100066 ExtendedType Message // nil pointer to the type that is being extended
Rob Pikeaaa3a622010-03-20 22:32:34 -070067 ExtensionType interface{} // nil pointer to the extension type
68 Field int32 // field number
David Symondsc057ad52012-02-11 15:43:42 +110069 Name string // fully-qualified name of extension, for text formatting
David Symonds8935abf2011-07-04 15:53:16 +100070 Tag string // protobuf tag style
Rob Pikeaaa3a622010-03-20 22:32:34 -070071}
72
David Symonds61826da2012-05-05 09:31:28 +100073func (ed *ExtensionDesc) repeated() bool {
74 t := reflect.TypeOf(ed.ExtensionType)
75 return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
76}
77
David Symondsf511ee32012-09-06 11:36:28 +100078// Extension represents an extension in a message.
David Symonds1d72f7a2011-08-19 18:28:52 +100079type Extension struct {
David Symondsf511ee32012-09-06 11:36:28 +100080 // When an extension is stored in a message using SetExtension
81 // only desc and value are set. When the message is marshaled
82 // enc will be set to the encoded form of the message.
83 //
84 // When a message is unmarshaled and contains extensions, each
85 // extension will have only enc set. When such an extension is
86 // accessed using GetExtension (or GetExtensions) desc and value
87 // will be set.
David Symonds1d72f7a2011-08-19 18:28:52 +100088 desc *ExtensionDesc
89 value interface{}
90 enc []byte
91}
92
93// SetRawExtension is for testing only.
94func SetRawExtension(base extendableProto, id int32, b []byte) {
95 base.ExtensionMap()[id] = Extension{enc: b}
96}
97
David Symonds940b9612011-04-01 10:45:23 +110098// isExtensionField returns true iff the given field number is in an extension range.
99func isExtensionField(pb extendableProto, field int32) bool {
100 for _, er := range pb.ExtensionRangeArray() {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700101 if er.Start <= field && field <= er.End {
102 return true
103 }
104 }
105 return false
106}
107
David Symonds940b9612011-04-01 10:45:23 +1100108// checkExtensionTypes checks that the given extension is valid for pb.
Rob Pikea17fdd92011-11-02 12:43:05 -0700109func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700110 // Check the extended type.
Nigel Tao4ede8452011-04-28 11:27:25 +1000111 if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
David Symondsa7f3a0f2013-09-09 13:32:33 +1000112 return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
Rob Pikeaaa3a622010-03-20 22:32:34 -0700113 }
114 // Check the range.
David Symonds940b9612011-04-01 10:45:23 +1100115 if !isExtensionField(pb, extension.Field) {
David Symondsa7f3a0f2013-09-09 13:32:33 +1000116 return errors.New("proto: bad extension number; not in declared ranges")
Rob Pikeaaa3a622010-03-20 22:32:34 -0700117 }
118 return nil
119}
120
David Symonds29d5d392012-12-20 12:14:58 +1100121// extPropKey is sufficient to uniquely identify an extension.
122type extPropKey struct {
123 base reflect.Type
124 field int32
125}
126
127var extProp = struct {
128 sync.RWMutex
129 m map[extPropKey]*Properties
130}{
131 m: make(map[extPropKey]*Properties),
132}
133
134func extensionProperties(ed *ExtensionDesc) *Properties {
135 key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
136
137 extProp.RLock()
138 if prop, ok := extProp.m[key]; ok {
139 extProp.RUnlock()
140 return prop
141 }
142 extProp.RUnlock()
143
144 extProp.Lock()
145 defer extProp.Unlock()
146 // Check again.
147 if prop, ok := extProp.m[key]; ok {
148 return prop
149 }
150
151 prop := new(Properties)
152 prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
153 extProp.m[key] = prop
154 return prop
155}
156
David Symonds1d72f7a2011-08-19 18:28:52 +1000157// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
Rob Pikea17fdd92011-11-02 12:43:05 -0700158func encodeExtensionMap(m map[int32]Extension) error {
David Symonds1d72f7a2011-08-19 18:28:52 +1000159 for k, e := range m {
160 if e.value == nil || e.desc == nil {
161 // Extension is only in its encoded form.
162 continue
163 }
164
165 // We don't skip extensions that have an encoded form set,
166 // because the extension value may have been mutated after
167 // the last time this function was called.
168
169 et := reflect.TypeOf(e.desc.ExtensionType)
David Symonds29d5d392012-12-20 12:14:58 +1100170 props := extensionProperties(e.desc)
David Symonds1d72f7a2011-08-19 18:28:52 +1000171
172 p := NewBuffer(nil)
Russ Coxd4ce3f12012-09-12 10:36:26 +1000173 // If e.value has type T, the encoder expects a *struct{ X T }.
174 // Pass a *T with a zero field and hope it all works out.
David Symonds1d72f7a2011-08-19 18:28:52 +1000175 x := reflect.New(et)
176 x.Elem().Set(reflect.ValueOf(e.value))
Russ Coxd4ce3f12012-09-12 10:36:26 +1000177 if err := props.enc(p, props, toStructPointer(x)); err != nil {
David Symonds1d72f7a2011-08-19 18:28:52 +1000178 return err
179 }
180 e.enc = p.buf
181 m[k] = e
182 }
183 return nil
184}
185
David Symonds940b9612011-04-01 10:45:23 +1100186// HasExtension returns whether the given extension is present in pb.
187func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700188 // TODO: Check types, field numbers, etc.?
David Symonds940b9612011-04-01 10:45:23 +1100189 _, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700190 return ok
191}
192
David Symonds940b9612011-04-01 10:45:23 +1100193// ClearExtension removes the given extension from pb.
194func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700195 // TODO: Check types, field numbers, etc.?
Rob Pikef48475f2011-10-28 12:07:40 -0700196 delete(pb.ExtensionMap(), extension.Field)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700197}
198
David Symonds940b9612011-04-01 10:45:23 +1100199// GetExtension parses and returns the given extension of pb.
David Symondsa7e9ef92012-01-18 18:27:58 +1100200// If the extension is not present it returns ErrMissingExtension.
David Symondsf511ee32012-09-06 11:36:28 +1000201// If the returned extension is modified, SetExtension must be called
202// for the modifications to be reflected in pb.
Rob Pikea17fdd92011-11-02 12:43:05 -0700203func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
David Symonds940b9612011-04-01 10:45:23 +1100204 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700205 return nil, err
206 }
207
David Symonds1d72f7a2011-08-19 18:28:52 +1000208 e, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700209 if !ok {
David Symondsa7e9ef92012-01-18 18:27:58 +1100210 return nil, ErrMissingExtension
Rob Pikeaaa3a622010-03-20 22:32:34 -0700211 }
David Symonds1d72f7a2011-08-19 18:28:52 +1000212 if e.value != nil {
213 // Already decoded. Check the descriptor, though.
214 if e.desc != extension {
215 // This shouldn't happen. If it does, it means that
216 // GetExtension was called twice with two different
217 // descriptors with the same field number.
Rob Pikea17fdd92011-11-02 12:43:05 -0700218 return nil, errors.New("proto: descriptor conflict")
David Symonds1d72f7a2011-08-19 18:28:52 +1000219 }
220 return e.value, nil
221 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700222
David Symondsa4b61c02011-09-07 14:06:00 +1000223 v, err := decodeExtension(e.enc, extension)
224 if err != nil {
225 return nil, err
226 }
227
228 // Remember the decoded version and drop the encoded version.
229 // That way it is safe to mutate what we return.
230 e.value = v
231 e.desc = extension
232 e.enc = nil
233 return e.value, nil
234}
235
236// decodeExtension decodes an extension encoded in b.
Rob Pikea17fdd92011-11-02 12:43:05 -0700237func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
David Symonds61826da2012-05-05 09:31:28 +1000238 o := NewBuffer(b)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700239
Nigel Tao4ede8452011-04-28 11:27:25 +1000240 t := reflect.TypeOf(extension.ExtensionType)
David Symonds61826da2012-05-05 09:31:28 +1000241 rep := extension.repeated()
242
David Symonds29d5d392012-12-20 12:14:58 +1100243 props := extensionProperties(extension)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700244
David Symonds61826da2012-05-05 09:31:28 +1000245 // t is a pointer to a struct, pointer to basic type or a slice.
246 // Allocate a "field" to store the pointer/slice itself; the
247 // pointer/slice will be stored here. We pass
Rob Pikeb0447c02011-10-20 15:20:04 -0700248 // the address of this field to props.dec.
Russ Coxd4ce3f12012-09-12 10:36:26 +1000249 // This passes a zero field and a *t and lets props.dec
250 // interpret it as a *struct{ x t }.
Rob Pikeb0447c02011-10-20 15:20:04 -0700251 value := reflect.New(t).Elem()
David Symonds61826da2012-05-05 09:31:28 +1000252
253 for {
254 // Discard wire type and field number varint. It isn't needed.
255 if _, err := o.DecodeVarint(); err != nil {
256 return nil, err
257 }
258
Russ Coxd4ce3f12012-09-12 10:36:26 +1000259 if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
David Symonds61826da2012-05-05 09:31:28 +1000260 return nil, err
261 }
262
263 if !rep || o.index >= len(o.buf) {
264 break
265 }
Rob Pikeaaa3a622010-03-20 22:32:34 -0700266 }
Rob Pikeb0447c02011-10-20 15:20:04 -0700267 return value.Interface(), nil
Rob Pikeaaa3a622010-03-20 22:32:34 -0700268}
269
David Symonds940b9612011-04-01 10:45:23 +1100270// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
271// The returned slice has the same length as es; missing extensions will appear as nil elements.
David Symonds9f60f432012-06-14 09:45:25 +1000272func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
David Symonds940b9612011-04-01 10:45:23 +1100273 epb, ok := pb.(extendableProto)
274 if !ok {
David Symondsa7f3a0f2013-09-09 13:32:33 +1000275 err = errors.New("proto: not an extendable proto")
David Symonds940b9612011-04-01 10:45:23 +1100276 return
277 }
278 extensions = make([]interface{}, len(es))
279 for i, e := range es {
280 extensions[i], err = GetExtension(epb, e)
281 if err != nil {
282 return
283 }
284 }
285 return
286}
287
David Symonds940b9612011-04-01 10:45:23 +1100288// SetExtension sets the specified extension of pb to the specified value.
Rob Pikea17fdd92011-11-02 12:43:05 -0700289func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
David Symonds940b9612011-04-01 10:45:23 +1100290 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700291 return err
292 }
Nigel Tao4ede8452011-04-28 11:27:25 +1000293 typ := reflect.TypeOf(extension.ExtensionType)
294 if typ != reflect.TypeOf(value) {
David Symondsa7f3a0f2013-09-09 13:32:33 +1000295 return errors.New("proto: bad extension value type")
Rob Pikeaaa3a622010-03-20 22:32:34 -0700296 }
297
David Symonds1d72f7a2011-08-19 18:28:52 +1000298 pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
Rob Pikeaaa3a622010-03-20 22:32:34 -0700299 return nil
300}
David Symondse37856c2011-06-22 12:52:53 +1000301
302// A global registry of extensions.
303// The generated code will register the generated descriptors by calling RegisterExtension.
304
305var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
306
307// RegisterExtension is called from the generated code.
308func RegisterExtension(desc *ExtensionDesc) {
309 st := reflect.TypeOf(desc.ExtendedType).Elem()
310 m := extensionMaps[st]
311 if m == nil {
312 m = make(map[int32]*ExtensionDesc)
313 extensionMaps[st] = m
314 }
315 if _, ok := m[desc.Field]; ok {
316 panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
317 }
318 m[desc.Field] = desc
319}
David Symondsc7cb3fd2011-08-29 16:49:58 +1000320
321// RegisteredExtensions returns a map of the registered extensions of a
322// protocol buffer struct, indexed by the extension number.
323// The argument pb should be a nil pointer to the struct type.
David Symonds9f60f432012-06-14 09:45:25 +1000324func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
David Symondsc7cb3fd2011-08-29 16:49:58 +1000325 return extensionMaps[reflect.TypeOf(pb).Elem()]
326}