1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <string.h>
5 
6 #include "sd-bus.h"
7 
8 #include "bus-gvariant.h"
9 #include "bus-signature.h"
10 #include "bus-type.h"
11 
bus_gvariant_get_size(const char * signature)12 int bus_gvariant_get_size(const char *signature) {
13         const char *p;
14         int sum = 0, r;
15 
16         /* For fixed size structs. Fails for variable size structs. */
17 
18         p = signature;
19         while (*p != 0) {
20                 size_t n;
21 
22                 r = signature_element_length(p, &n);
23                 if (r < 0)
24                         return r;
25                 else {
26                         char t[n+1];
27 
28                         memcpy(t, p, n);
29                         t[n] = 0;
30 
31                         r = bus_gvariant_get_alignment(t);
32                         if (r < 0)
33                                 return r;
34 
35                         sum = ALIGN_TO(sum, r);
36                 }
37 
38                 switch (*p) {
39 
40                 case SD_BUS_TYPE_BOOLEAN:
41                 case SD_BUS_TYPE_BYTE:
42                         sum += 1;
43                         break;
44 
45                 case SD_BUS_TYPE_INT16:
46                 case SD_BUS_TYPE_UINT16:
47                         sum += 2;
48                         break;
49 
50                 case SD_BUS_TYPE_INT32:
51                 case SD_BUS_TYPE_UINT32:
52                 case SD_BUS_TYPE_UNIX_FD:
53                         sum += 4;
54                         break;
55 
56                 case SD_BUS_TYPE_INT64:
57                 case SD_BUS_TYPE_UINT64:
58                 case SD_BUS_TYPE_DOUBLE:
59                         sum += 8;
60                         break;
61 
62                 case SD_BUS_TYPE_STRUCT_BEGIN:
63                 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
64                         if (n == 2) {
65                                 /* unary type () has fixed size of 1 */
66                                 r = 1;
67                         } else {
68                                 char t[n-1];
69 
70                                 memcpy(t, p + 1, n - 2);
71                                 t[n - 2] = 0;
72 
73                                 r = bus_gvariant_get_size(t);
74                                 if (r < 0)
75                                         return r;
76                         }
77 
78                         sum += r;
79                         break;
80                 }
81 
82                 case SD_BUS_TYPE_STRING:
83                 case SD_BUS_TYPE_OBJECT_PATH:
84                 case SD_BUS_TYPE_SIGNATURE:
85                 case SD_BUS_TYPE_ARRAY:
86                 case SD_BUS_TYPE_VARIANT:
87                         return -EINVAL;
88 
89                 default:
90                         assert_not_reached();
91                 }
92 
93                 p += n;
94         }
95 
96         r = bus_gvariant_get_alignment(signature);
97         if (r < 0)
98                 return r;
99 
100         return ALIGN_TO(sum, r);
101 }
102 
bus_gvariant_get_alignment(const char * signature)103 int bus_gvariant_get_alignment(const char *signature) {
104         size_t alignment = 1;
105         const char *p;
106         int r;
107 
108         p = signature;
109         while (*p != 0 && alignment < 8) {
110                 size_t n;
111                 int a;
112 
113                 r = signature_element_length(p, &n);
114                 if (r < 0)
115                         return r;
116 
117                 switch (*p) {
118 
119                 case SD_BUS_TYPE_BYTE:
120                 case SD_BUS_TYPE_BOOLEAN:
121                 case SD_BUS_TYPE_STRING:
122                 case SD_BUS_TYPE_OBJECT_PATH:
123                 case SD_BUS_TYPE_SIGNATURE:
124                         a = 1;
125                         break;
126 
127                 case SD_BUS_TYPE_INT16:
128                 case SD_BUS_TYPE_UINT16:
129                         a = 2;
130                         break;
131 
132                 case SD_BUS_TYPE_INT32:
133                 case SD_BUS_TYPE_UINT32:
134                 case SD_BUS_TYPE_UNIX_FD:
135                         a = 4;
136                         break;
137 
138                 case SD_BUS_TYPE_INT64:
139                 case SD_BUS_TYPE_UINT64:
140                 case SD_BUS_TYPE_DOUBLE:
141                 case SD_BUS_TYPE_VARIANT:
142                         a = 8;
143                         break;
144 
145                 case SD_BUS_TYPE_ARRAY: {
146                         char t[n];
147 
148                         memcpy(t, p + 1, n - 1);
149                         t[n - 1] = 0;
150 
151                         a = bus_gvariant_get_alignment(t);
152                         break;
153                 }
154 
155                 case SD_BUS_TYPE_STRUCT_BEGIN:
156                 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
157                         char t[n-1];
158 
159                         memcpy(t, p + 1, n - 2);
160                         t[n - 2] = 0;
161 
162                         a = bus_gvariant_get_alignment(t);
163                         break;
164                 }
165 
166                 default:
167                         assert_not_reached();
168                 }
169 
170                 if (a < 0)
171                         return a;
172 
173                 assert(a > 0 && a <= 8);
174                 if ((size_t) a > alignment)
175                         alignment = (size_t) a;
176 
177                 p += n;
178         }
179 
180         return alignment;
181 }
182 
bus_gvariant_is_fixed_size(const char * signature)183 int bus_gvariant_is_fixed_size(const char *signature) {
184         const char *p;
185         int r;
186 
187         assert(signature);
188 
189         p = signature;
190         while (*p != 0) {
191                 size_t n;
192 
193                 r = signature_element_length(p, &n);
194                 if (r < 0)
195                         return r;
196 
197                 switch (*p) {
198 
199                 case SD_BUS_TYPE_STRING:
200                 case SD_BUS_TYPE_OBJECT_PATH:
201                 case SD_BUS_TYPE_SIGNATURE:
202                 case SD_BUS_TYPE_ARRAY:
203                 case SD_BUS_TYPE_VARIANT:
204                         return 0;
205 
206                 case SD_BUS_TYPE_BYTE:
207                 case SD_BUS_TYPE_BOOLEAN:
208                 case SD_BUS_TYPE_INT16:
209                 case SD_BUS_TYPE_UINT16:
210                 case SD_BUS_TYPE_INT32:
211                 case SD_BUS_TYPE_UINT32:
212                 case SD_BUS_TYPE_UNIX_FD:
213                 case SD_BUS_TYPE_INT64:
214                 case SD_BUS_TYPE_UINT64:
215                 case SD_BUS_TYPE_DOUBLE:
216                         break;
217 
218                 case SD_BUS_TYPE_STRUCT_BEGIN:
219                 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
220                         char t[n-1];
221 
222                         memcpy(t, p + 1, n - 2);
223                         t[n - 2] = 0;
224 
225                         r = bus_gvariant_is_fixed_size(t);
226                         if (r <= 0)
227                                 return r;
228                         break;
229                 }
230 
231                 default:
232                         assert_not_reached();
233                 }
234 
235                 p += n;
236         }
237 
238         return true;
239 }
240 
bus_gvariant_determine_word_size(size_t sz,size_t extra)241 size_t bus_gvariant_determine_word_size(size_t sz, size_t extra) {
242         if (sz + extra <= 0xFF)
243                 return 1;
244         else if (sz + extra*2 <= 0xFFFF)
245                 return 2;
246         else if (sz + extra*4 <= 0xFFFFFFFF)
247                 return 4;
248         else
249                 return 8;
250 }
251 
bus_gvariant_read_word_le(void * p,size_t sz)252 size_t bus_gvariant_read_word_le(void *p, size_t sz) {
253         union {
254                 uint16_t u16;
255                 uint32_t u32;
256                 uint64_t u64;
257         } x;
258 
259         assert(p);
260 
261         if (sz == 1)
262                 return *(uint8_t*) p;
263 
264         memcpy(&x, p, sz);
265 
266         if (sz == 2)
267                 return le16toh(x.u16);
268         else if (sz == 4)
269                 return le32toh(x.u32);
270         else if (sz == 8)
271                 return le64toh(x.u64);
272 
273         assert_not_reached();
274 }
275 
bus_gvariant_write_word_le(void * p,size_t sz,size_t value)276 void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) {
277         union {
278                 uint16_t u16;
279                 uint32_t u32;
280                 uint64_t u64;
281         } x;
282 
283         assert(p);
284         assert(sz == 8 || (value < (1ULL << (sz*8))));
285 
286         if (sz == 1) {
287                 *(uint8_t*) p = value;
288                 return;
289         } else if (sz == 2)
290                 x.u16 = htole16((uint16_t) value);
291         else if (sz == 4)
292                 x.u32 = htole32((uint32_t) value);
293         else if (sz == 8)
294                 x.u64 = htole64((uint64_t) value);
295         else
296                 assert_not_reached();
297 
298         memcpy(p, &x, sz);
299 }
300