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