blob: 4594c8b5c99a5babd171e53abb1998d91ed5debc [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
34
35/*
36 * Types and routines for supporting protocol buffer extensions.
37 */
38
39import (
40 "os"
41 "reflect"
David Symondse37856c2011-06-22 12:52:53 +100042 "strconv"
Rob Pikeaaa3a622010-03-20 22:32:34 -070043 "unsafe"
44)
45
46// ExtensionRange represents a range of message extensions for a protocol buffer.
47// Used in code generated by the protocol compiler.
48type ExtensionRange struct {
49 Start, End int32 // both inclusive
50}
51
52// extendableProto is an interface implemented by any protocol buffer that may be extended.
53type extendableProto interface {
54 ExtensionRangeArray() []ExtensionRange
55 ExtensionMap() map[int32][]byte
56}
57
58// ExtensionDesc represents an extension specification.
59// Used in generated code from the protocol compiler.
60type ExtensionDesc struct {
61 ExtendedType interface{} // nil pointer to the type that is being extended
62 ExtensionType interface{} // nil pointer to the extension type
63 Field int32 // field number
David Symondse37856c2011-06-22 12:52:53 +100064 Name string // fully-qualified name of extension
Rob Pikeaaa3a622010-03-20 22:32:34 -070065 Tag string // PB(...) tag style
66}
67
David Symonds940b9612011-04-01 10:45:23 +110068// isExtensionField returns true iff the given field number is in an extension range.
69func isExtensionField(pb extendableProto, field int32) bool {
70 for _, er := range pb.ExtensionRangeArray() {
Rob Pikeaaa3a622010-03-20 22:32:34 -070071 if er.Start <= field && field <= er.End {
72 return true
73 }
74 }
75 return false
76}
77
David Symonds940b9612011-04-01 10:45:23 +110078// checkExtensionTypes checks that the given extension is valid for pb.
79func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) os.Error {
Rob Pikeaaa3a622010-03-20 22:32:34 -070080 // Check the extended type.
Nigel Tao4ede8452011-04-28 11:27:25 +100081 if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
Rob Pikeaaa3a622010-03-20 22:32:34 -070082 return os.NewError("bad extended type; " + b.String() + " does not extend " + a.String())
83 }
84 // Check the range.
David Symonds940b9612011-04-01 10:45:23 +110085 if !isExtensionField(pb, extension.Field) {
Rob Pikeaaa3a622010-03-20 22:32:34 -070086 return os.NewError("bad extension number; not in declared ranges")
87 }
88 return nil
89}
90
David Symonds940b9612011-04-01 10:45:23 +110091// HasExtension returns whether the given extension is present in pb.
92func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
Rob Pikeaaa3a622010-03-20 22:32:34 -070093 // TODO: Check types, field numbers, etc.?
David Symonds940b9612011-04-01 10:45:23 +110094 _, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -070095 return ok
96}
97
David Symonds940b9612011-04-01 10:45:23 +110098// ClearExtension removes the given extension from pb.
99func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700100 // TODO: Check types, field numbers, etc.?
David Symonds940b9612011-04-01 10:45:23 +1100101 pb.ExtensionMap()[extension.Field] = nil, false
Rob Pikeaaa3a622010-03-20 22:32:34 -0700102}
103
David Symonds940b9612011-04-01 10:45:23 +1100104// GetExtension parses and returns the given extension of pb.
105// If the extension is not present it returns (nil, nil).
106func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, os.Error) {
107 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700108 return nil, err
109 }
110
David Symonds940b9612011-04-01 10:45:23 +1100111 b, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700112 if !ok {
113 return nil, nil // not an error
114 }
115
116 // Discard wire type and field number varint. It isn't needed.
117 _, n := DecodeVarint(b)
118 o := NewBuffer(b[n:])
119
Nigel Tao4ede8452011-04-28 11:27:25 +1000120 t := reflect.TypeOf(extension.ExtensionType)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700121 props := &Properties{}
122 props.Init(t, "irrelevant_name", extension.Tag, 0)
123
124 base := unsafe.New(t)
125 var sbase uintptr
Rob Pike97e934d2011-04-11 12:52:49 -0700126 if t.Elem().Kind() == reflect.Struct {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700127 // props.dec will be dec_struct_message, which does not refer to sbase.
128 *(*unsafe.Pointer)(base) = unsafe.New(t.Elem())
129 } else {
130 sbase = uintptr(unsafe.New(t.Elem()))
131 }
132 if err := props.dec(o, props, uintptr(base), sbase); err != nil {
133 return nil, err
134 }
135 return unsafe.Unreflect(t, base), nil
136}
137
David Symonds940b9612011-04-01 10:45:23 +1100138// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
139// The returned slice has the same length as es; missing extensions will appear as nil elements.
140func GetExtensions(pb interface{}, es []*ExtensionDesc) (extensions []interface{}, err os.Error) {
141 epb, ok := pb.(extendableProto)
142 if !ok {
143 err = os.NewError("not an extendable proto")
144 return
145 }
146 extensions = make([]interface{}, len(es))
147 for i, e := range es {
148 extensions[i], err = GetExtension(epb, e)
149 if err != nil {
150 return
151 }
152 }
153 return
154}
155
David Symondsc37ad662010-04-07 09:25:13 +1000156// TODO: (needed for repeated extensions)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700157// - ExtensionSize
158// - AddExtension
159
David Symonds940b9612011-04-01 10:45:23 +1100160// SetExtension sets the specified extension of pb to the specified value.
161func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) os.Error {
162 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700163 return err
164 }
Nigel Tao4ede8452011-04-28 11:27:25 +1000165 typ := reflect.TypeOf(extension.ExtensionType)
166 if typ != reflect.TypeOf(value) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700167 return os.NewError("bad extension value type")
168 }
169
170 props := new(Properties)
Nigel Tao4ede8452011-04-28 11:27:25 +1000171 props.Init(reflect.TypeOf(extension.ExtensionType), "unknown_name", extension.Tag, 0)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700172
173 p := NewBuffer(nil)
David Symondsd01278e2011-04-21 10:05:33 +1000174 // The encoder must be passed a pointer to value.
175 // Allocate a copy of value so that we can use its address.
176 x := reflect.New(typ)
Nigel Tao4ede8452011-04-28 11:27:25 +1000177 x.Elem().Set(reflect.ValueOf(value))
David Symondsd01278e2011-04-21 10:05:33 +1000178 if err := props.enc(p, props, x.Pointer()); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700179 return err
180 }
David Symonds940b9612011-04-01 10:45:23 +1100181 pb.ExtensionMap()[extension.Field] = p.buf
Rob Pikeaaa3a622010-03-20 22:32:34 -0700182 return nil
183}
David Symondse37856c2011-06-22 12:52:53 +1000184
185// A global registry of extensions.
186// The generated code will register the generated descriptors by calling RegisterExtension.
187
188var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
189
190// RegisterExtension is called from the generated code.
191func RegisterExtension(desc *ExtensionDesc) {
192 st := reflect.TypeOf(desc.ExtendedType).Elem()
193 m := extensionMaps[st]
194 if m == nil {
195 m = make(map[int32]*ExtensionDesc)
196 extensionMaps[st] = m
197 }
198 if _, ok := m[desc.Field]; ok {
199 panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
200 }
201 m[desc.Field] = desc
202}