1package dbus 2 3import ( 4 "fmt" 5 "reflect" 6 "strings" 7) 8 9var sigToType = map[byte]reflect.Type{ 10 'y': byteType, 11 'b': boolType, 12 'n': int16Type, 13 'q': uint16Type, 14 'i': int32Type, 15 'u': uint32Type, 16 'x': int64Type, 17 't': uint64Type, 18 'd': float64Type, 19 's': stringType, 20 'g': signatureType, 21 'o': objectPathType, 22 'v': variantType, 23 'h': unixFDIndexType, 24} 25 26// Signature represents a correct type signature as specified by the D-Bus 27// specification. The zero value represents the empty signature, "". 28type Signature struct { 29 str string 30} 31 32// SignatureOf returns the concatenation of all the signatures of the given 33// values. It panics if one of them is not representable in D-Bus. 34func SignatureOf(vs ...interface{}) Signature { 35 var s string 36 for _, v := range vs { 37 s += getSignature(reflect.TypeOf(v)) 38 } 39 return Signature{s} 40} 41 42// SignatureOfType returns the signature of the given type. It panics if the 43// type is not representable in D-Bus. 44func SignatureOfType(t reflect.Type) Signature { 45 return Signature{getSignature(t)} 46} 47 48// getSignature returns the signature of the given type and panics on unknown types. 49func getSignature(t reflect.Type) string { 50 // handle simple types first 51 switch t.Kind() { 52 case reflect.Uint8: 53 return "y" 54 case reflect.Bool: 55 return "b" 56 case reflect.Int16: 57 return "n" 58 case reflect.Uint16: 59 return "q" 60 case reflect.Int, reflect.Int32: 61 if t == unixFDType { 62 return "h" 63 } 64 return "i" 65 case reflect.Uint, reflect.Uint32: 66 if t == unixFDIndexType { 67 return "h" 68 } 69 return "u" 70 case reflect.Int64: 71 return "x" 72 case reflect.Uint64: 73 return "t" 74 case reflect.Float64: 75 return "d" 76 case reflect.Ptr: 77 return getSignature(t.Elem()) 78 case reflect.String: 79 if t == objectPathType { 80 return "o" 81 } 82 return "s" 83 case reflect.Struct: 84 if t == variantType { 85 return "v" 86 } else if t == signatureType { 87 return "g" 88 } 89 var s string 90 for i := 0; i < t.NumField(); i++ { 91 field := t.Field(i) 92 if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { 93 s += getSignature(t.Field(i).Type) 94 } 95 } 96 return "(" + s + ")" 97 case reflect.Array, reflect.Slice: 98 return "a" + getSignature(t.Elem()) 99 case reflect.Map: 100 if !isKeyType(t.Key()) { 101 panic(InvalidTypeError{t}) 102 } 103 return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" 104 case reflect.Interface: 105 return "v" 106 } 107 panic(InvalidTypeError{t}) 108} 109 110// ParseSignature returns the signature represented by this string, or a 111// SignatureError if the string is not a valid signature. 112func ParseSignature(s string) (sig Signature, err error) { 113 if len(s) == 0 { 114 return 115 } 116 if len(s) > 255 { 117 return Signature{""}, SignatureError{s, "too long"} 118 } 119 sig.str = s 120 for err == nil && len(s) != 0 { 121 err, s = validSingle(s, 0) 122 } 123 if err != nil { 124 sig = Signature{""} 125 } 126 127 return 128} 129 130// ParseSignatureMust behaves like ParseSignature, except that it panics if s 131// is not valid. 132func ParseSignatureMust(s string) Signature { 133 sig, err := ParseSignature(s) 134 if err != nil { 135 panic(err) 136 } 137 return sig 138} 139 140// Empty retruns whether the signature is the empty signature. 141func (s Signature) Empty() bool { 142 return s.str == "" 143} 144 145// Single returns whether the signature represents a single, complete type. 146func (s Signature) Single() bool { 147 err, r := validSingle(s.str, 0) 148 return err != nil && r == "" 149} 150 151// String returns the signature's string representation. 152func (s Signature) String() string { 153 return s.str 154} 155 156// A SignatureError indicates that a signature passed to a function or received 157// on a connection is not a valid signature. 158type SignatureError struct { 159 Sig string 160 Reason string 161} 162 163func (e SignatureError) Error() string { 164 return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) 165} 166 167// Try to read a single type from this string. If it was successful, err is nil 168// and rem is the remaining unparsed part. Otherwise, err is a non-nil 169// SignatureError and rem is "". depth is the current recursion depth which may 170// not be greater than 64 and should be given as 0 on the first call. 171func validSingle(s string, depth int) (err error, rem string) { 172 if s == "" { 173 return SignatureError{Sig: s, Reason: "empty signature"}, "" 174 } 175 if depth > 64 { 176 return SignatureError{Sig: s, Reason: "container nesting too deep"}, "" 177 } 178 switch s[0] { 179 case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h': 180 return nil, s[1:] 181 case 'a': 182 if len(s) > 1 && s[1] == '{' { 183 i := findMatching(s[1:], '{', '}') 184 if i == -1 { 185 return SignatureError{Sig: s, Reason: "unmatched '{'"}, "" 186 } 187 i++ 188 rem = s[i+1:] 189 s = s[2:i] 190 if err, _ = validSingle(s[:1], depth+1); err != nil { 191 return err, "" 192 } 193 err, nr := validSingle(s[1:], depth+1) 194 if err != nil { 195 return err, "" 196 } 197 if nr != "" { 198 return SignatureError{Sig: s, Reason: "too many types in dict"}, "" 199 } 200 return nil, rem 201 } 202 return validSingle(s[1:], depth+1) 203 case '(': 204 i := findMatching(s, '(', ')') 205 if i == -1 { 206 return SignatureError{Sig: s, Reason: "unmatched ')'"}, "" 207 } 208 rem = s[i+1:] 209 s = s[1:i] 210 for err == nil && s != "" { 211 err, s = validSingle(s, depth+1) 212 } 213 if err != nil { 214 rem = "" 215 } 216 return 217 } 218 return SignatureError{Sig: s, Reason: "invalid type character"}, "" 219} 220 221func findMatching(s string, left, right rune) int { 222 n := 0 223 for i, v := range s { 224 if v == left { 225 n++ 226 } else if v == right { 227 n-- 228 } 229 if n == 0 { 230 return i 231 } 232 } 233 return -1 234} 235 236// typeFor returns the type of the given signature. It ignores any left over 237// characters and panics if s doesn't start with a valid type signature. 238func typeFor(s string) (t reflect.Type) { 239 err, _ := validSingle(s, 0) 240 if err != nil { 241 panic(err) 242 } 243 244 if t, ok := sigToType[s[0]]; ok { 245 return t 246 } 247 switch s[0] { 248 case 'a': 249 if s[1] == '{' { 250 i := strings.LastIndex(s, "}") 251 t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i])) 252 } else { 253 t = reflect.SliceOf(typeFor(s[1:])) 254 } 255 case '(': 256 t = interfacesType 257 } 258 return 259} 260