blob: fe717096193d15da316f382290b879b6cd524186 [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"
42 "unsafe"
43)
44
45// ExtensionRange represents a range of message extensions for a protocol buffer.
46// Used in code generated by the protocol compiler.
47type ExtensionRange struct {
48 Start, End int32 // both inclusive
49}
50
51// extendableProto is an interface implemented by any protocol buffer that may be extended.
52type extendableProto interface {
53 ExtensionRangeArray() []ExtensionRange
54 ExtensionMap() map[int32][]byte
55}
56
57// ExtensionDesc represents an extension specification.
58// Used in generated code from the protocol compiler.
59type ExtensionDesc struct {
60 ExtendedType interface{} // nil pointer to the type that is being extended
61 ExtensionType interface{} // nil pointer to the extension type
62 Field int32 // field number
63 Tag string // PB(...) tag style
64}
65
David Symonds940b9612011-04-01 10:45:23 +110066// isExtensionField returns true iff the given field number is in an extension range.
67func isExtensionField(pb extendableProto, field int32) bool {
68 for _, er := range pb.ExtensionRangeArray() {
Rob Pikeaaa3a622010-03-20 22:32:34 -070069 if er.Start <= field && field <= er.End {
70 return true
71 }
72 }
73 return false
74}
75
David Symonds940b9612011-04-01 10:45:23 +110076// checkExtensionTypes checks that the given extension is valid for pb.
77func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) os.Error {
Rob Pikeaaa3a622010-03-20 22:32:34 -070078 // Check the extended type.
Nigel Tao4ede8452011-04-28 11:27:25 +100079 if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
Rob Pikeaaa3a622010-03-20 22:32:34 -070080 return os.NewError("bad extended type; " + b.String() + " does not extend " + a.String())
81 }
82 // Check the range.
David Symonds940b9612011-04-01 10:45:23 +110083 if !isExtensionField(pb, extension.Field) {
Rob Pikeaaa3a622010-03-20 22:32:34 -070084 return os.NewError("bad extension number; not in declared ranges")
85 }
86 return nil
87}
88
David Symonds940b9612011-04-01 10:45:23 +110089// HasExtension returns whether the given extension is present in pb.
90func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
Rob Pikeaaa3a622010-03-20 22:32:34 -070091 // TODO: Check types, field numbers, etc.?
David Symonds940b9612011-04-01 10:45:23 +110092 _, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -070093 return ok
94}
95
David Symonds940b9612011-04-01 10:45:23 +110096// ClearExtension removes the given extension from pb.
97func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
Rob Pikeaaa3a622010-03-20 22:32:34 -070098 // TODO: Check types, field numbers, etc.?
David Symonds940b9612011-04-01 10:45:23 +110099 pb.ExtensionMap()[extension.Field] = nil, false
Rob Pikeaaa3a622010-03-20 22:32:34 -0700100}
101
David Symonds940b9612011-04-01 10:45:23 +1100102// GetExtension parses and returns the given extension of pb.
103// If the extension is not present it returns (nil, nil).
104func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, os.Error) {
105 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700106 return nil, err
107 }
108
David Symonds940b9612011-04-01 10:45:23 +1100109 b, ok := pb.ExtensionMap()[extension.Field]
Rob Pikeaaa3a622010-03-20 22:32:34 -0700110 if !ok {
111 return nil, nil // not an error
112 }
113
114 // Discard wire type and field number varint. It isn't needed.
115 _, n := DecodeVarint(b)
116 o := NewBuffer(b[n:])
117
Nigel Tao4ede8452011-04-28 11:27:25 +1000118 t := reflect.TypeOf(extension.ExtensionType)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700119 props := &Properties{}
120 props.Init(t, "irrelevant_name", extension.Tag, 0)
121
122 base := unsafe.New(t)
123 var sbase uintptr
Rob Pike97e934d2011-04-11 12:52:49 -0700124 if t.Elem().Kind() == reflect.Struct {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700125 // props.dec will be dec_struct_message, which does not refer to sbase.
126 *(*unsafe.Pointer)(base) = unsafe.New(t.Elem())
127 } else {
128 sbase = uintptr(unsafe.New(t.Elem()))
129 }
130 if err := props.dec(o, props, uintptr(base), sbase); err != nil {
131 return nil, err
132 }
133 return unsafe.Unreflect(t, base), nil
134}
135
David Symonds940b9612011-04-01 10:45:23 +1100136// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
137// The returned slice has the same length as es; missing extensions will appear as nil elements.
138func GetExtensions(pb interface{}, es []*ExtensionDesc) (extensions []interface{}, err os.Error) {
139 epb, ok := pb.(extendableProto)
140 if !ok {
141 err = os.NewError("not an extendable proto")
142 return
143 }
144 extensions = make([]interface{}, len(es))
145 for i, e := range es {
146 extensions[i], err = GetExtension(epb, e)
147 if err != nil {
148 return
149 }
150 }
151 return
152}
153
David Symondsc37ad662010-04-07 09:25:13 +1000154// TODO: (needed for repeated extensions)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700155// - ExtensionSize
156// - AddExtension
157
David Symonds940b9612011-04-01 10:45:23 +1100158// SetExtension sets the specified extension of pb to the specified value.
159func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) os.Error {
160 if err := checkExtensionTypes(pb, extension); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700161 return err
162 }
Nigel Tao4ede8452011-04-28 11:27:25 +1000163 typ := reflect.TypeOf(extension.ExtensionType)
164 if typ != reflect.TypeOf(value) {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700165 return os.NewError("bad extension value type")
166 }
167
168 props := new(Properties)
Nigel Tao4ede8452011-04-28 11:27:25 +1000169 props.Init(reflect.TypeOf(extension.ExtensionType), "unknown_name", extension.Tag, 0)
Rob Pikeaaa3a622010-03-20 22:32:34 -0700170
171 p := NewBuffer(nil)
David Symondsd01278e2011-04-21 10:05:33 +1000172 // The encoder must be passed a pointer to value.
173 // Allocate a copy of value so that we can use its address.
174 x := reflect.New(typ)
Nigel Tao4ede8452011-04-28 11:27:25 +1000175 x.Elem().Set(reflect.ValueOf(value))
David Symondsd01278e2011-04-21 10:05:33 +1000176 if err := props.enc(p, props, x.Pointer()); err != nil {
Rob Pikeaaa3a622010-03-20 22:32:34 -0700177 return err
178 }
David Symonds940b9612011-04-01 10:45:23 +1100179 pb.ExtensionMap()[extension.Field] = p.buf
Rob Pikeaaa3a622010-03-20 22:32:34 -0700180 return nil
181}