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