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