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