Skip to content

Commit 2ba8c56

Browse files
committed
Correctly decode struct values
For message decoding, the signature of the message is now considered to properly decode structs into []interface{}, and Store converts anything containing such values to respective Go structs. This also removes the need for Signature.Values(), which is removed with this commit. Fixes #18.
1 parent e5e31ef commit 2ba8c56

File tree

7 files changed

+187
-88
lines changed

7 files changed

+187
-88
lines changed

dbus.go

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,20 +45,32 @@ func Store(src []interface{}, dest ...interface{}) error {
4545
return errors.New("dbus.Store: length mismatch")
4646
}
4747

48-
for i, v := range src {
49-
if reflect.TypeOf(dest[i]).Elem() == reflect.TypeOf(v) {
50-
reflect.ValueOf(dest[i]).Elem().Set(reflect.ValueOf(v))
51-
} else if vs, ok := v.([]interface{}); ok {
52-
retv := reflect.ValueOf(dest[i]).Elem()
53-
if retv.Kind() != reflect.Struct {
48+
for i := range src {
49+
if err := store(src[i], dest[i]); err != nil {
50+
return err
51+
}
52+
}
53+
return nil
54+
}
55+
56+
func store(src, dest interface{}) error {
57+
if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) {
58+
reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src))
59+
return nil
60+
} else if hasStruct(dest) {
61+
rv := reflect.ValueOf(dest).Elem()
62+
switch rv.Kind() {
63+
case reflect.Struct:
64+
vs, ok := src.([]interface{})
65+
if !ok {
5466
return errors.New("dbus.Store: type mismatch")
5567
}
56-
t := retv.Type()
57-
ndest := make([]interface{}, 0, retv.NumField())
58-
for i := 0; i < retv.NumField(); i++ {
68+
t := rv.Type()
69+
ndest := make([]interface{}, 0, rv.NumField())
70+
for i := 0; i < rv.NumField(); i++ {
5971
field := t.Field(i)
6072
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
61-
ndest = append(ndest, retv.Field(i).Addr().Interface())
73+
ndest = append(ndest, rv.Field(i).Addr().Interface())
6274
}
6375
}
6476
if len(vs) != len(ndest) {
@@ -68,11 +80,52 @@ func Store(src []interface{}, dest ...interface{}) error {
6880
if err != nil {
6981
return errors.New("dbus.Store: type mismatch")
7082
}
71-
} else {
83+
case reflect.Slice:
84+
sv := reflect.ValueOf(src)
85+
if sv.Kind() != reflect.Slice {
86+
return errors.New("dbus.Store: type mismatch")
87+
}
88+
rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len()))
89+
for i := 0; i < sv.Len(); i++ {
90+
if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil {
91+
return err
92+
}
93+
}
94+
case reflect.Map:
95+
sv := reflect.ValueOf(src)
96+
if sv.Kind() != reflect.Map {
97+
return errors.New("dbus.Store: type mismatch")
98+
}
99+
keys := sv.MapKeys()
100+
rv.Set(reflect.MakeMap(sv.Type()))
101+
for _, key := range keys {
102+
v := reflect.New(sv.Type().Elem())
103+
if err := store(v, sv.MapIndex(key).Interface()); err != nil {
104+
return err
105+
}
106+
rv.SetMapIndex(key, v.Elem())
107+
}
108+
default:
72109
return errors.New("dbus.Store: type mismatch")
73110
}
111+
return nil
112+
} else {
113+
return errors.New("dbus.Store: type mismatch")
114+
}
115+
}
116+
117+
func hasStruct(v interface{}) bool {
118+
t := reflect.TypeOf(v)
119+
for {
120+
switch t.Kind() {
121+
case reflect.Struct:
122+
return true
123+
case reflect.Slice, reflect.Ptr, reflect.Map:
124+
t = t.Elem()
125+
default:
126+
return false
127+
}
74128
}
75-
return nil
76129
}
77130

78131
// An ObjectPath is an object path as defined by the D-Bus spec.

decoder.go

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,6 @@ import (
66
"reflect"
77
)
88

9-
// A decoder reads values that are encoded in the D-Bus wire format.
10-
//
11-
// For decoding, the inverse of the encoding that an Encoder applies is used,
12-
// with the following exceptions:
13-
//
14-
// - If a VARIANT contains a STRUCT, its decoded value will be of type
15-
// []interface{} and contain the struct fields in the correct order.
16-
// - When decoding into a pointer, the pointer is followed unless it is nil,
17-
// in which case a new value for it to point to is allocated.
18-
// - When decoding into a slice, the decoded values are appended to it.
19-
// - Arrays cannot be decoded into.
209
type decoder struct {
2110
in io.Reader
2211
order binary.ByteOrder
@@ -71,6 +60,86 @@ func (dec *decoder) Decode(vs ...interface{}) (err error) {
7160
return nil
7261
}
7362

63+
func (dec *decoder) DecodeSig(sig Signature) (vs []interface{}, err error) {
64+
defer func() {
65+
var ok bool
66+
v := recover()
67+
if err, ok = v.(error); ok {
68+
if err == io.EOF || err == io.ErrUnexpectedEOF {
69+
err = FormatError("unexpected EOF")
70+
}
71+
}
72+
}()
73+
vs = make([]interface{}, 0)
74+
s := sig.str
75+
for s != "" {
76+
err, rem := validSingle(s, 0)
77+
if err != nil {
78+
return nil, err
79+
}
80+
v := dec.decodeSig(s[:len(s)-len(rem)], 0)
81+
vs = append(vs, v)
82+
s = rem
83+
}
84+
return vs, nil
85+
}
86+
87+
func (dec *decoder) decodeSig(s string, depth int) interface{} {
88+
if len(s) == 1 {
89+
if t, ok := sigToType[s[0]]; ok {
90+
v := reflect.New(t)
91+
dec.decode(v, depth)
92+
return v.Elem().Interface()
93+
}
94+
}
95+
switch s[0] {
96+
case 'a':
97+
if len(s) > 1 && s[1] == '{' {
98+
ksig := s[2:3]
99+
vsig := s[3 : len(s)-1]
100+
v := reflect.MakeMap(reflect.MapOf(typeFor(ksig), typeFor(vsig)))
101+
if depth >= 63 {
102+
panic(FormatError("input exceeds container depth limit"))
103+
}
104+
var length uint32
105+
dec.decode(reflect.ValueOf(&length), depth)
106+
spos := dec.pos
107+
for dec.pos < spos+int(length) {
108+
dec.align(8)
109+
if !isKeyType(v.Type().Key()) {
110+
panic(InvalidTypeError{v.Type()})
111+
}
112+
kv := dec.decodeSig(ksig, depth+2)
113+
vv := dec.decodeSig(vsig, depth+2)
114+
v.SetMapIndex(reflect.ValueOf(kv), reflect.ValueOf(vv))
115+
}
116+
return v.Interface()
117+
}
118+
var length uint32
119+
if depth >= 64 {
120+
panic(FormatError("input exceeds container depth limit"))
121+
}
122+
dec.decode(reflect.ValueOf(&length), depth)
123+
v := reflect.MakeSlice(reflect.SliceOf(typeFor(s[1:])), 0, int(length))
124+
spos := dec.pos
125+
for dec.pos < spos+int(length) {
126+
ev := dec.decodeSig(s[1:], depth+1)
127+
v = reflect.Append(v, reflect.ValueOf(ev))
128+
}
129+
return v.Interface()
130+
case '(':
131+
dec.align(8)
132+
v := make([]interface{}, 0)
133+
for _, c := range s[1 : len(s)-1] {
134+
ev := dec.decodeSig(string(c), depth+1)
135+
v = append(v, ev)
136+
}
137+
return v
138+
default:
139+
panic(SignatureError{Sig: s})
140+
}
141+
}
142+
74143
// decode decodes a single value and stores it in *v. depth holds the depth of
75144
// the container nesting.
76145
func (dec *decoder) decode(v reflect.Value, depth int) {
@@ -186,7 +255,7 @@ func (dec *decoder) decode(v reflect.Value, depth int) {
186255
if rem != "" {
187256
panic(FormatError("variant signature has multiple types"))
188257
}
189-
t = value(sig.str)
258+
t = typeFor(sig.str)
190259
if t == interfacesType {
191260
dec.align(8)
192261
s := sig.str[1 : len(sig.str)-1]
@@ -196,7 +265,7 @@ func (dec *decoder) decode(v reflect.Value, depth int) {
196265
if err != nil {
197266
panic(err)
198267
}
199-
t = value(s[:len(s)-len(rem)])
268+
t = typeFor(s[:len(s)-len(rem)])
200269
nv := reflect.New(t)
201270
dec.decode(nv, depth+1)
202271
slice = reflect.Append(slice, nv.Elem())

doc.go

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,10 @@ Pointers encode as the value they're pointed to.
4444
Trying to encode any other type or a slice, map or struct containing an
4545
unsupported type will result in an InvalidTypeError.
4646
47-
For incoming messages, the inverse of these rules are used, with the following
48-
exceptions:
49-
50-
- If an incoming STRUCT can't be directly converted to a Go struct (because
51-
it is, for example, wrapped in a VARIANT), it is stored as a []interface{}
52-
containing the struct fields in the correct order. The Store function can
53-
be used to convert such values to the proper structs.
54-
- When decoding into a pointer, the pointer is followed unless it is nil, in
55-
which case a new value for it to point to is allocated.
56-
- When decoding into a slice, the decoded values are appended to it.
57-
- Arrays cannot be decoded into.
47+
For incoming messages, the inverse of these rules are used, with the exception
48+
of STRUCTs. Incoming STRUCTS are represented as a slice of empty interfaces
49+
containing the struct fields in the correct order. The Store function can be
50+
used to convert such values to Go structs.
5851
5952
Unix FD passing
6053

message.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,13 @@ func DecodeMessage(rd io.Reader) (msg *Message, err error) {
167167
}
168168
sig, _ := msg.Headers[FieldSignature].value.(Signature)
169169
if sig.str != "" {
170-
vs := sig.Values()
171170
buf := bytes.NewBuffer(body)
172171
dec = newDecoder(buf, order)
173-
if err = dec.Decode(vs...); err != nil {
172+
vs, err := dec.DecodeSig(sig)
173+
if err != nil {
174174
return nil, err
175175
}
176-
msg.Body = dereferenceAll(vs)
176+
msg.Body = vs
177177
}
178178

179179
return

proto_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,17 @@ func TestMessage(t *testing.T) {
225225
}
226226
}
227227

228+
func TestProtoStructInterfaces(t *testing.T) {
229+
b := []byte{42}
230+
vs, err := newDecoder(bytes.NewReader(b), binary.LittleEndian).DecodeSig(Signature{"(y)"})
231+
if err != nil {
232+
t.Fatal(err)
233+
}
234+
if vs[0].([]interface{})[0].(byte) != 42 {
235+
t.Errorf("wrongs results (got %v)", vs)
236+
}
237+
}
238+
228239
// ordinary org.freedesktop.DBus.Hello call
229240
var smallMessage = &Message{
230241
Type: TypeMethodCall,

sig.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,6 @@ func (s Signature) String() string {
151151
return s.str
152152
}
153153

154-
// Values returns a slice of pointers to values that match the given signature.
155-
func (s Signature) Values() []interface{} {
156-
slice := make([]interface{}, 0)
157-
str := s.str
158-
for str != "" {
159-
slice = append(slice, reflect.New(value(str)).Interface())
160-
_, str = validSingle(str, 0)
161-
}
162-
return slice
163-
}
164-
165154
// A SignatureError indicates that a signature passed to a function or received
166155
// on a connection is not a valid signature.
167156
type SignatureError struct {
@@ -189,10 +178,11 @@ func validSingle(s string, depth int) (err error, rem string) {
189178
return nil, s[1:]
190179
case 'a':
191180
if len(s) > 1 && s[1] == '{' {
192-
i := strings.LastIndex(s, "}")
181+
i := findMatching(s[1:], '{', '}')
193182
if i == -1 {
194183
return SignatureError{Sig: s, Reason: "unmatched '{'"}, ""
195184
}
185+
i++
196186
rem = s[i+1:]
197187
s = s[2:i]
198188
if err, _ = validSingle(s[:1], depth+1); err != nil {
@@ -209,7 +199,7 @@ func validSingle(s string, depth int) (err error, rem string) {
209199
}
210200
return validSingle(s[1:], depth+1)
211201
case '(':
212-
i := strings.LastIndex(s, ")")
202+
i := findMatching(s, '(', ')')
213203
if i == -1 {
214204
return SignatureError{Sig: s, Reason: "unmatched ')'"}, ""
215205
}
@@ -226,9 +216,24 @@ func validSingle(s string, depth int) (err error, rem string) {
226216
return SignatureError{Sig: s, Reason: "invalid type character"}, ""
227217
}
228218

229-
// value returns the type of the given signature. It ignores any left over
219+
func findMatching(s string, left, right rune) int {
220+
n := 0
221+
for i, v := range s {
222+
if v == left {
223+
n++
224+
} else if v == right {
225+
n--
226+
}
227+
if n == 0 {
228+
return i
229+
}
230+
}
231+
return -1
232+
}
233+
234+
// typeFor returns the type of the given signature. It ignores any left over
230235
// characters and panics if s doesn't start with a valid type signature.
231-
func value(s string) (t reflect.Type) {
236+
func typeFor(s string) (t reflect.Type) {
232237
err, _ := validSingle(s, 0)
233238
if err != nil {
234239
panic(err)
@@ -241,9 +246,9 @@ func value(s string) (t reflect.Type) {
241246
case 'a':
242247
if s[1] == '{' {
243248
i := strings.LastIndex(s, "}")
244-
t = reflect.MapOf(sigToType[s[2]], value(s[3:i]))
249+
t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i]))
245250
} else {
246-
t = reflect.SliceOf(value(s[1:]))
251+
t = reflect.SliceOf(typeFor(s[1:]))
247252
}
248253
case '(':
249254
t = interfacesType

0 commit comments

Comments
 (0)