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