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