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