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