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.Int32: 61 if t == unixFDType { 62 return "h" 63 } 64 return "i" 65 case 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 } 105 panic(InvalidTypeError{t}) 106} 107 108// ParseSignature returns the signature represented by this string, or a 109// SignatureError if the string is not a valid signature. 110func ParseSignature(s string) (sig Signature, err error) { 111 if len(s) == 0 { 112 return 113 } 114 if len(s) > 255 { 115 return Signature{""}, SignatureError{s, "too long"} 116 } 117 sig.str = s 118 for err == nil && len(s) != 0 { 119 err, s = validSingle(s, 0) 120 } 121 if err != nil { 122 sig = Signature{""} 123 } 124 125 return 126} 127 128// ParseSignatureMust behaves like ParseSignature, except that it panics if s 129// is not valid. 130func ParseSignatureMust(s string) Signature { 131 sig, err := ParseSignature(s) 132 if err != nil { 133 panic(err) 134 } 135 return sig 136} 137 138// Empty retruns whether the signature is the empty signature. 139func (s Signature) Empty() bool { 140 return s.str == "" 141} 142 143// Single returns whether the signature represents a single, complete type. 144func (s Signature) Single() bool { 145 err, r := validSingle(s.str, 0) 146 return err != nil && r == "" 147} 148 149// String returns the signature's string representation. 150func (s Signature) String() string { 151 return s.str 152} 153 154// A SignatureError indicates that a signature passed to a function or received 155// on a connection is not a valid signature. 156type SignatureError struct { 157 Sig string 158 Reason string 159} 160 161func (e SignatureError) Error() string { 162 return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) 163} 164 165// Try to read a single type from this string. If it was successfull, err is nil 166// and rem is the remaining unparsed part. Otherwise, err is a non-nil 167// SignatureError and rem is "". depth is the current recursion depth which may 168// not be greater than 64 and should be given as 0 on the first call. 169func validSingle(s string, depth int) (err error, rem string) { 170 if s == "" { 171 return SignatureError{Sig: s, Reason: "empty signature"}, "" 172 } 173 if depth > 64 { 174 return SignatureError{Sig: s, Reason: "container nesting too deep"}, "" 175 } 176 switch s[0] { 177 case 'y', 'b', 'n', 'q', 'i', 'u', 'x', 't', 'd', 's', 'g', 'o', 'v', 'h': 178 return nil, s[1:] 179 case 'a': 180 if len(s) > 1 && s[1] == '{' { 181 i := findMatching(s[1:], '{', '}') 182 if i == -1 { 183 return SignatureError{Sig: s, Reason: "unmatched '{'"}, "" 184 } 185 i++ 186 rem = s[i+1:] 187 s = s[2:i] 188 if err, _ = validSingle(s[:1], depth+1); err != nil { 189 return err, "" 190 } 191 err, nr := validSingle(s[1:], depth+1) 192 if err != nil { 193 return err, "" 194 } 195 if nr != "" { 196 return SignatureError{Sig: s, Reason: "too many types in dict"}, "" 197 } 198 return nil, rem 199 } 200 return validSingle(s[1:], depth+1) 201 case '(': 202 i := findMatching(s, '(', ')') 203 if i == -1 { 204 return SignatureError{Sig: s, Reason: "unmatched ')'"}, "" 205 } 206 rem = s[i+1:] 207 s = s[1:i] 208 for err == nil && s != "" { 209 err, s = validSingle(s, depth+1) 210 } 211 if err != nil { 212 rem = "" 213 } 214 return 215 } 216 return SignatureError{Sig: s, Reason: "invalid type character"}, "" 217} 218 219func 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 235// characters and panics if s doesn't start with a valid type signature. 236func typeFor(s string) (t reflect.Type) { 237 err, _ := validSingle(s, 0) 238 if err != nil { 239 panic(err) 240 } 241 242 if t, ok := sigToType[s[0]]; ok { 243 return t 244 } 245 switch s[0] { 246 case 'a': 247 if s[1] == '{' { 248 i := strings.LastIndex(s, "}") 249 t = reflect.MapOf(sigToType[s[2]], typeFor(s[3:i])) 250 } else { 251 t = reflect.SliceOf(typeFor(s[1:])) 252 } 253 case '(': 254 t = interfacesType 255 } 256 return 257} 258