chiark / gitweb /
Prep v238: Uncomment now needed headers and unmask now needed functions in src/libelo...
[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   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23
24 #include "sd-bus.h"
25
26 #include "bus-gvariant.h"
27 #include "bus-signature.h"
28 #include "bus-type.h"
29
30 int bus_gvariant_get_size(const char *signature) {
31         const char *p;
32         int sum = 0, r;
33
34         /* For fixed size structs. Fails for variable size structs. */
35
36         p = signature;
37         while (*p != 0) {
38                 size_t n;
39
40                 r = signature_element_length(p, &n);
41                 if (r < 0)
42                         return r;
43                 else {
44                         char t[n+1];
45
46                         memcpy(t, p, n);
47                         t[n] = 0;
48
49                         r = bus_gvariant_get_alignment(t);
50                         if (r < 0)
51                                 return r;
52
53                         sum = ALIGN_TO(sum, r);
54                 }
55
56                 switch (*p) {
57
58                 case SD_BUS_TYPE_BOOLEAN:
59                 case SD_BUS_TYPE_BYTE:
60                         sum += 1;
61                         break;
62
63                 case SD_BUS_TYPE_INT16:
64                 case SD_BUS_TYPE_UINT16:
65                         sum += 2;
66                         break;
67
68                 case SD_BUS_TYPE_INT32:
69                 case SD_BUS_TYPE_UINT32:
70                 case SD_BUS_TYPE_UNIX_FD:
71                         sum += 4;
72                         break;
73
74                 case SD_BUS_TYPE_INT64:
75                 case SD_BUS_TYPE_UINT64:
76                 case SD_BUS_TYPE_DOUBLE:
77                         sum += 8;
78                         break;
79
80                 case SD_BUS_TYPE_STRUCT_BEGIN:
81                 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
82                         if (n == 2) {
83                                 /* unary type () has fixed size of 1 */
84                                 r = 1;
85                         } else {
86                                 char t[n-1];
87
88                                 memcpy(t, p + 1, n - 2);
89                                 t[n - 2] = 0;
90
91                                 r = bus_gvariant_get_size(t);
92                                 if (r < 0)
93                                         return r;
94                         }
95
96                         sum += r;
97                         break;
98                 }
99
100                 case SD_BUS_TYPE_STRING:
101                 case SD_BUS_TYPE_OBJECT_PATH:
102                 case SD_BUS_TYPE_SIGNATURE:
103                 case SD_BUS_TYPE_ARRAY:
104                 case SD_BUS_TYPE_VARIANT:
105                         return -EINVAL;
106
107                 default:
108                         assert_not_reached("Unknown signature type");
109                 }
110
111                 p += n;
112         }
113
114         r = bus_gvariant_get_alignment(signature);
115         if (r < 0)
116                 return r;
117
118         return ALIGN_TO(sum, r);
119 }
120
121 int bus_gvariant_get_alignment(const char *signature) {
122         size_t alignment = 1;
123         const char *p;
124         int r;
125
126         p = signature;
127         while (*p != 0 && alignment < 8) {
128                 size_t n;
129                 int a;
130
131                 r = signature_element_length(p, &n);
132                 if (r < 0)
133                         return r;
134
135                 switch (*p) {
136
137                 case SD_BUS_TYPE_BYTE:
138                 case SD_BUS_TYPE_BOOLEAN:
139                 case SD_BUS_TYPE_STRING:
140                 case SD_BUS_TYPE_OBJECT_PATH:
141                 case SD_BUS_TYPE_SIGNATURE:
142                         a = 1;
143                         break;
144
145                 case SD_BUS_TYPE_INT16:
146                 case SD_BUS_TYPE_UINT16:
147                         a = 2;
148                         break;
149
150                 case SD_BUS_TYPE_INT32:
151                 case SD_BUS_TYPE_UINT32:
152                 case SD_BUS_TYPE_UNIX_FD:
153                         a = 4;
154                         break;
155
156                 case SD_BUS_TYPE_INT64:
157                 case SD_BUS_TYPE_UINT64:
158                 case SD_BUS_TYPE_DOUBLE:
159                 case SD_BUS_TYPE_VARIANT:
160                         a = 8;
161                         break;
162
163                 case SD_BUS_TYPE_ARRAY: {
164                         char t[n];
165
166                         memcpy(t, p + 1, n - 1);
167                         t[n - 1] = 0;
168
169                         a = bus_gvariant_get_alignment(t);
170                         break;
171                 }
172
173                 case SD_BUS_TYPE_STRUCT_BEGIN:
174                 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
175                         char t[n-1];
176
177                         memcpy(t, p + 1, n - 2);
178                         t[n - 2] = 0;
179
180                         a = bus_gvariant_get_alignment(t);
181                         break;
182                 }
183
184                 default:
185                         assert_not_reached("Unknown signature type");
186                 }
187
188                 if (a < 0)
189                         return a;
190
191                 assert(a > 0 && a <= 8);
192                 if ((size_t) a > alignment)
193                         alignment = (size_t) a;
194
195                 p += n;
196         }
197
198         return alignment;
199 }
200
201 int bus_gvariant_is_fixed_size(const char *signature) {
202         const char *p;
203         int r;
204
205         assert(signature);
206
207         p = signature;
208         while (*p != 0) {
209                 size_t n;
210
211                 r = signature_element_length(p, &n);
212                 if (r < 0)
213                         return r;
214
215                 switch (*p) {
216
217                 case SD_BUS_TYPE_STRING:
218                 case SD_BUS_TYPE_OBJECT_PATH:
219                 case SD_BUS_TYPE_SIGNATURE:
220                 case SD_BUS_TYPE_ARRAY:
221                 case SD_BUS_TYPE_VARIANT:
222                         return 0;
223
224                 case SD_BUS_TYPE_BYTE:
225                 case SD_BUS_TYPE_BOOLEAN:
226                 case SD_BUS_TYPE_INT16:
227                 case SD_BUS_TYPE_UINT16:
228                 case SD_BUS_TYPE_INT32:
229                 case SD_BUS_TYPE_UINT32:
230                 case SD_BUS_TYPE_UNIX_FD:
231                 case SD_BUS_TYPE_INT64:
232                 case SD_BUS_TYPE_UINT64:
233                 case SD_BUS_TYPE_DOUBLE:
234                         break;
235
236                 case SD_BUS_TYPE_STRUCT_BEGIN:
237                 case SD_BUS_TYPE_DICT_ENTRY_BEGIN: {
238                         char t[n-1];
239
240                         memcpy(t, p + 1, n - 2);
241                         t[n - 2] = 0;
242
243                         r = bus_gvariant_is_fixed_size(t);
244                         if (r <= 0)
245                                 return r;
246                         break;
247                 }
248
249                 default:
250                         assert_not_reached("Unknown signature type");
251                 }
252
253                 p += n;
254         }
255
256         return true;
257 }
258
259 size_t bus_gvariant_determine_word_size(size_t sz, size_t extra) {
260         if (sz + extra <= 0xFF)
261                 return 1;
262         else if (sz + extra*2 <= 0xFFFF)
263                 return 2;
264         else if (sz + extra*4 <= 0xFFFFFFFF)
265                 return 4;
266         else
267                 return 8;
268 }
269
270 size_t bus_gvariant_read_word_le(void *p, size_t sz) {
271         union {
272                 uint16_t u16;
273                 uint32_t u32;
274                 uint64_t u64;
275         } x;
276
277         assert(p);
278
279         if (sz == 1)
280                 return *(uint8_t*) p;
281
282         memcpy(&x, p, sz);
283
284         if (sz == 2)
285                 return le16toh(x.u16);
286         else if (sz == 4)
287                 return le32toh(x.u32);
288         else if (sz == 8)
289                 return le64toh(x.u64);
290
291         assert_not_reached("unknown word width");
292 }
293
294 void bus_gvariant_write_word_le(void *p, size_t sz, size_t value) {
295         union {
296                 uint16_t u16;
297                 uint32_t u32;
298                 uint64_t u64;
299         } x;
300
301         assert(p);
302         assert(sz == 8 || (value < (1ULL << (sz*8))));
303
304         if (sz == 1) {
305                 *(uint8_t*) p = value;
306                 return;
307         } else if (sz == 2)
308                 x.u16 = htole16((uint16_t) value);
309         else if (sz == 4)
310                 x.u32 = htole32((uint32_t) value);
311         else if (sz == 8)
312                 x.u64 = htole64((uint64_t) value);
313         else
314                 assert_not_reached("unknown word width");
315
316         memcpy(p, &x, sz);
317 }