nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright © 2008 Ryan Lortie |
||
3 | * Copyright © 2010 Codethink Limited |
||
4 | * |
||
5 | * This library is free software; you can redistribute it and/or |
||
6 | * modify it under the terms of the GNU Lesser General Public |
||
7 | * License as published by the Free Software Foundation; either |
||
8 | * version 2 of the License, or (at your option) any later version. |
||
9 | * |
||
10 | * This library is distributed in the hope that it will be useful, |
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
13 | * Lesser General Public License for more details. |
||
14 | * |
||
15 | * You should have received a copy of the GNU Lesser General Public |
||
16 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
17 | * |
||
18 | * Author: Ryan Lortie <desrt@desrt.ca> |
||
19 | */ |
||
20 | |||
21 | #include "config.h" |
||
22 | |||
23 | #include "gvarianttypeinfo.h" |
||
24 | |||
25 | #include <glib/gtestutils.h> |
||
26 | #include <glib/gthread.h> |
||
27 | #include <glib/gslice.h> |
||
28 | #include <glib/ghash.h> |
||
29 | |||
30 | /* < private > |
||
31 | * GVariantTypeInfo: |
||
32 | * |
||
33 | * This structure contains the necessary information to facilitate the |
||
34 | * serialisation and fast deserialisation of a given type of GVariant |
||
35 | * value. A GVariant instance holds a pointer to one of these |
||
36 | * structures to provide for efficient operation. |
||
37 | * |
||
38 | * The GVariantTypeInfo structures for all of the base types, plus the |
||
39 | * "variant" type are stored in a read-only static array. |
||
40 | * |
||
41 | * For container types, a hash table and reference counting is used to |
||
42 | * ensure that only one of these structures exists for any given type. |
||
43 | * In general, a container GVariantTypeInfo will exist for a given type |
||
44 | * only if one or more GVariant instances of that type exist or if |
||
45 | * another GVariantTypeInfo has that type as a subtype. For example, if |
||
46 | * a process contains a single GVariant instance with type "(asv)", then |
||
47 | * container GVariantTypeInfo structures will exist for "(asv)" and |
||
48 | * for "as" (note that "s" and "v" always exist in the static array). |
||
49 | * |
||
50 | * The trickiest part of GVariantTypeInfo (and in fact, the major reason |
||
51 | * for its existence) is the storage of somewhat magical constants that |
||
52 | * allow for O(1) lookups of items in tuples. This is described below. |
||
53 | * |
||
54 | * 'container_class' is set to 'a' or 'r' if the GVariantTypeInfo is |
||
55 | * contained inside of an ArrayInfo or TupleInfo, respectively. This |
||
56 | * allows the storage of the necessary additional information. |
||
57 | * |
||
58 | * 'fixed_size' is set to the fixed size of the type, if applicable, or |
||
59 | * 0 otherwise (since no type has a fixed size of 0). |
||
60 | * |
||
61 | * 'alignment' is set to one less than the alignment requirement for |
||
62 | * this type. This makes many operations much more convenient. |
||
63 | */ |
||
64 | struct _GVariantTypeInfo |
||
65 | { |
||
66 | gsize fixed_size; |
||
67 | guchar alignment; |
||
68 | guchar container_class; |
||
69 | }; |
||
70 | |||
71 | /* Container types are reference counted. They also need to have their |
||
72 | * type string stored explicitly since it is not merely a single letter. |
||
73 | */ |
||
74 | typedef struct |
||
75 | { |
||
76 | GVariantTypeInfo info; |
||
77 | |||
78 | gchar *type_string; |
||
79 | gint ref_count; |
||
80 | } ContainerInfo; |
||
81 | |||
82 | /* For 'array' and 'maybe' types, we store some extra information on the |
||
83 | * end of the GVariantTypeInfo struct -- the element type (ie: "s" for |
||
84 | * "as"). The container GVariantTypeInfo structure holds a reference to |
||
85 | * the element typeinfo. |
||
86 | */ |
||
87 | typedef struct |
||
88 | { |
||
89 | ContainerInfo container; |
||
90 | |||
91 | GVariantTypeInfo *element; |
||
92 | } ArrayInfo; |
||
93 | |||
94 | /* For 'tuple' and 'dict entry' types, we store extra information for |
||
95 | * each member -- its type and how to find it inside the serialised data |
||
96 | * in O(1) time using 4 variables -- 'i', 'a', 'b', and 'c'. See the |
||
97 | * comment on GVariantMemberInfo in gvarianttypeinfo.h. |
||
98 | */ |
||
99 | typedef struct |
||
100 | { |
||
101 | ContainerInfo container; |
||
102 | |||
103 | GVariantMemberInfo *members; |
||
104 | gsize n_members; |
||
105 | } TupleInfo; |
||
106 | |||
107 | |||
108 | /* Hard-code the base types in a constant array */ |
||
109 | static const GVariantTypeInfo g_variant_type_info_basic_table[24] = { |
||
110 | #define fixed_aligned(x) x, x - 1 |
||
111 | #define not_a_type 0, |
||
112 | #define unaligned 0, 0 |
||
113 | #define aligned(x) 0, x - 1 |
||
114 | /* 'b' */ { fixed_aligned(1) }, /* boolean */ |
||
115 | /* 'c' */ { not_a_type }, |
||
116 | /* 'd' */ { fixed_aligned(8) }, /* double */ |
||
117 | /* 'e' */ { not_a_type }, |
||
118 | /* 'f' */ { not_a_type }, |
||
119 | /* 'g' */ { unaligned }, /* signature string */ |
||
120 | /* 'h' */ { fixed_aligned(4) }, /* file handle (int32) */ |
||
121 | /* 'i' */ { fixed_aligned(4) }, /* int32 */ |
||
122 | /* 'j' */ { not_a_type }, |
||
123 | /* 'k' */ { not_a_type }, |
||
124 | /* 'l' */ { not_a_type }, |
||
125 | /* 'm' */ { not_a_type }, |
||
126 | /* 'n' */ { fixed_aligned(2) }, /* int16 */ |
||
127 | /* 'o' */ { unaligned }, /* object path string */ |
||
128 | /* 'p' */ { not_a_type }, |
||
129 | /* 'q' */ { fixed_aligned(2) }, /* uint16 */ |
||
130 | /* 'r' */ { not_a_type }, |
||
131 | /* 's' */ { unaligned }, /* string */ |
||
132 | /* 't' */ { fixed_aligned(8) }, /* uint64 */ |
||
133 | /* 'u' */ { fixed_aligned(4) }, /* uint32 */ |
||
134 | /* 'v' */ { aligned(8) }, /* variant */ |
||
135 | /* 'w' */ { not_a_type }, |
||
136 | /* 'x' */ { fixed_aligned(8) }, /* int64 */ |
||
137 | /* 'y' */ { fixed_aligned(1) }, /* byte */ |
||
138 | #undef fixed_aligned |
||
139 | #undef not_a_type |
||
140 | #undef unaligned |
||
141 | #undef aligned |
||
142 | }; |
||
143 | |||
144 | /* We need to have type strings to return for the base types. We store |
||
145 | * those in another array. Since all base type strings are single |
||
146 | * characters this is easy. By not storing pointers to strings into the |
||
147 | * GVariantTypeInfo itself, we save a bunch of relocations. |
||
148 | */ |
||
149 | static const char g_variant_type_info_basic_chars[24][2] = { |
||
150 | "b", " ", "d", " ", " ", "g", "h", "i", " ", " ", " ", " ", |
||
151 | "n", "o", " ", "q", " ", "s", "t", "u", "v", " ", "x", "y" |
||
152 | }; |
||
153 | |||
154 | /* sanity checks to make debugging easier */ |
||
155 | static void |
||
156 | g_variant_type_info_check (const GVariantTypeInfo *info, |
||
157 | char container_class) |
||
158 | { |
||
159 | g_assert (!container_class || info->container_class == container_class); |
||
160 | |||
161 | /* alignment can only be one of these */ |
||
162 | g_assert (info->alignment == 0 || info->alignment == 1 || |
||
163 | info->alignment == 3 || info->alignment == 7); |
||
164 | |||
165 | if (info->container_class) |
||
166 | { |
||
167 | ContainerInfo *container = (ContainerInfo *) info; |
||
168 | |||
169 | /* extra checks for containers */ |
||
170 | g_assert_cmpint (container->ref_count, >, 0); |
||
171 | g_assert (container->type_string != NULL); |
||
172 | } |
||
173 | else |
||
174 | { |
||
175 | gint index; |
||
176 | |||
177 | /* if not a container, then ensure that it is a valid member of |
||
178 | * the basic types table |
||
179 | */ |
||
180 | index = info - g_variant_type_info_basic_table; |
||
181 | |||
182 | g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24); |
||
183 | g_assert (G_N_ELEMENTS (g_variant_type_info_basic_chars) == 24); |
||
184 | g_assert (0 <= index && index < 24); |
||
185 | g_assert (g_variant_type_info_basic_chars[index][0] != ' '); |
||
186 | } |
||
187 | } |
||
188 | |||
189 | /* < private > |
||
190 | * g_variant_type_info_get_type_string: |
||
191 | * @info: a #GVariantTypeInfo |
||
192 | * |
||
193 | * Gets the type string for @info. The string is nul-terminated. |
||
194 | */ |
||
195 | const gchar * |
||
196 | g_variant_type_info_get_type_string (GVariantTypeInfo *info) |
||
197 | { |
||
198 | g_variant_type_info_check (info, 0); |
||
199 | |||
200 | if (info->container_class) |
||
201 | { |
||
202 | ContainerInfo *container = (ContainerInfo *) info; |
||
203 | |||
204 | /* containers have their type string stored inside them */ |
||
205 | return container->type_string; |
||
206 | } |
||
207 | else |
||
208 | { |
||
209 | gint index; |
||
210 | |||
211 | /* look up the type string in the base type array. the call to |
||
212 | * g_variant_type_info_check() above already ensured validity. |
||
213 | */ |
||
214 | index = info - g_variant_type_info_basic_table; |
||
215 | |||
216 | return g_variant_type_info_basic_chars[index]; |
||
217 | } |
||
218 | } |
||
219 | |||
220 | /* < private > |
||
221 | * g_variant_type_info_query: |
||
222 | * @info: a #GVariantTypeInfo |
||
223 | * @alignment: (allow-none): the location to store the alignment, or %NULL |
||
224 | * @fixed_size: (allow-none): the location to store the fixed size, or %NULL |
||
225 | * |
||
226 | * Queries @info to determine the alignment requirements and fixed size |
||
227 | * (if any) of the type. |
||
228 | * |
||
229 | * @fixed_size, if non-%NULL is set to the fixed size of the type, or 0 |
||
230 | * to indicate that the type is a variable-sized type. No type has a |
||
231 | * fixed size of 0. |
||
232 | * |
||
233 | * @alignment, if non-%NULL, is set to one less than the required |
||
234 | * alignment of the type. For example, for a 32bit integer, @alignment |
||
235 | * would be set to 3. This allows you to round an integer up to the |
||
236 | * proper alignment by performing the following efficient calculation: |
||
237 | * |
||
238 | * offset += ((-offset) & alignment); |
||
239 | */ |
||
240 | void |
||
241 | g_variant_type_info_query (GVariantTypeInfo *info, |
||
242 | guint *alignment, |
||
243 | gsize *fixed_size) |
||
244 | { |
||
245 | g_variant_type_info_check (info, 0); |
||
246 | |||
247 | if (alignment) |
||
248 | *alignment = info->alignment; |
||
249 | |||
250 | if (fixed_size) |
||
251 | *fixed_size = info->fixed_size; |
||
252 | } |
||
253 | |||
254 | /* == array == */ |
||
255 | #define GV_ARRAY_INFO_CLASS 'a' |
||
256 | static ArrayInfo * |
||
257 | GV_ARRAY_INFO (GVariantTypeInfo *info) |
||
258 | { |
||
259 | g_variant_type_info_check (info, GV_ARRAY_INFO_CLASS); |
||
260 | |||
261 | return (ArrayInfo *) info; |
||
262 | } |
||
263 | |||
264 | static void |
||
265 | array_info_free (GVariantTypeInfo *info) |
||
266 | { |
||
267 | ArrayInfo *array_info; |
||
268 | |||
269 | g_assert (info->container_class == GV_ARRAY_INFO_CLASS); |
||
270 | array_info = (ArrayInfo *) info; |
||
271 | |||
272 | g_variant_type_info_unref (array_info->element); |
||
273 | g_slice_free (ArrayInfo, array_info); |
||
274 | } |
||
275 | |||
276 | static ContainerInfo * |
||
277 | array_info_new (const GVariantType *type) |
||
278 | { |
||
279 | ArrayInfo *info; |
||
280 | |||
281 | info = g_slice_new (ArrayInfo); |
||
282 | info->container.info.container_class = GV_ARRAY_INFO_CLASS; |
||
283 | |||
284 | info->element = g_variant_type_info_get (g_variant_type_element (type)); |
||
285 | info->container.info.alignment = info->element->alignment; |
||
286 | info->container.info.fixed_size = 0; |
||
287 | |||
288 | return (ContainerInfo *) info; |
||
289 | } |
||
290 | |||
291 | /* < private > |
||
292 | * g_variant_type_info_element: |
||
293 | * @info: a #GVariantTypeInfo for an array or maybe type |
||
294 | * |
||
295 | * Returns the element type for the array or maybe type. A reference is |
||
296 | * not added, so the caller must add their own. |
||
297 | */ |
||
298 | GVariantTypeInfo * |
||
299 | g_variant_type_info_element (GVariantTypeInfo *info) |
||
300 | { |
||
301 | return GV_ARRAY_INFO (info)->element; |
||
302 | } |
||
303 | |||
304 | /* < private > |
||
305 | * g_variant_type_query_element: |
||
306 | * @info: a #GVariantTypeInfo for an array or maybe type |
||
307 | * @alignment: (allow-none): the location to store the alignment, or %NULL |
||
308 | * @fixed_size: (allow-none): the location to store the fixed size, or %NULL |
||
309 | * |
||
310 | * Returns the alignment requires and fixed size (if any) for the |
||
311 | * element type of the array. This call is a convenience wrapper around |
||
312 | * g_variant_type_info_element() and g_variant_type_info_query(). |
||
313 | */ |
||
314 | void |
||
315 | g_variant_type_info_query_element (GVariantTypeInfo *info, |
||
316 | guint *alignment, |
||
317 | gsize *fixed_size) |
||
318 | { |
||
319 | g_variant_type_info_query (GV_ARRAY_INFO (info)->element, |
||
320 | alignment, fixed_size); |
||
321 | } |
||
322 | |||
323 | /* == tuple == */ |
||
324 | #define GV_TUPLE_INFO_CLASS 'r' |
||
325 | static TupleInfo * |
||
326 | GV_TUPLE_INFO (GVariantTypeInfo *info) |
||
327 | { |
||
328 | g_variant_type_info_check (info, GV_TUPLE_INFO_CLASS); |
||
329 | |||
330 | return (TupleInfo *) info; |
||
331 | } |
||
332 | |||
333 | static void |
||
334 | tuple_info_free (GVariantTypeInfo *info) |
||
335 | { |
||
336 | TupleInfo *tuple_info; |
||
337 | gint i; |
||
338 | |||
339 | g_assert (info->container_class == GV_TUPLE_INFO_CLASS); |
||
340 | tuple_info = (TupleInfo *) info; |
||
341 | |||
342 | for (i = 0; i < tuple_info->n_members; i++) |
||
343 | g_variant_type_info_unref (tuple_info->members[i].type_info); |
||
344 | |||
345 | g_slice_free1 (sizeof (GVariantMemberInfo) * tuple_info->n_members, |
||
346 | tuple_info->members); |
||
347 | g_slice_free (TupleInfo, tuple_info); |
||
348 | } |
||
349 | |||
350 | static void |
||
351 | tuple_allocate_members (const GVariantType *type, |
||
352 | GVariantMemberInfo **members, |
||
353 | gsize *n_members) |
||
354 | { |
||
355 | const GVariantType *item_type; |
||
356 | gsize i = 0; |
||
357 | |||
358 | *n_members = g_variant_type_n_items (type); |
||
359 | *members = g_slice_alloc (sizeof (GVariantMemberInfo) * *n_members); |
||
360 | |||
361 | item_type = g_variant_type_first (type); |
||
362 | while (item_type) |
||
363 | { |
||
364 | GVariantMemberInfo *member = &(*members)[i++]; |
||
365 | |||
366 | member->type_info = g_variant_type_info_get (item_type); |
||
367 | item_type = g_variant_type_next (item_type); |
||
368 | |||
369 | if (member->type_info->fixed_size) |
||
370 | member->ending_type = G_VARIANT_MEMBER_ENDING_FIXED; |
||
371 | else if (item_type == NULL) |
||
372 | member->ending_type = G_VARIANT_MEMBER_ENDING_LAST; |
||
373 | else |
||
374 | member->ending_type = G_VARIANT_MEMBER_ENDING_OFFSET; |
||
375 | } |
||
376 | |||
377 | g_assert (i == *n_members); |
||
378 | } |
||
379 | |||
380 | /* this is g_variant_type_info_query for a given member of the tuple. |
||
381 | * before the access is done, it is ensured that the item is within |
||
382 | * range and %FALSE is returned if not. |
||
383 | */ |
||
384 | static gboolean |
||
385 | tuple_get_item (TupleInfo *info, |
||
386 | GVariantMemberInfo *item, |
||
387 | gsize *d, |
||
388 | gsize *e) |
||
389 | { |
||
390 | if (&info->members[info->n_members] == item) |
||
391 | return FALSE; |
||
392 | |||
393 | *d = item->type_info->alignment; |
||
394 | *e = item->type_info->fixed_size; |
||
395 | return TRUE; |
||
396 | } |
||
397 | |||
398 | /* Read the documentation for #GVariantMemberInfo in gvarianttype.h |
||
399 | * before attempting to understand this. |
||
400 | * |
||
401 | * This function adds one set of "magic constant" values (for one item |
||
402 | * in the tuple) to the table. |
||
403 | * |
||
404 | * The algorithm in tuple_generate_table() calculates values of 'a', 'b' |
||
405 | * and 'c' for each item, such that the procedure for finding the item |
||
406 | * is to start at the end of the previous variable-sized item, add 'a', |
||
407 | * then round up to the nearest multiple of 'b', then then add 'c'. |
||
408 | * Note that 'b' is stored in the usual "one less than" form. ie: |
||
409 | * |
||
410 | * start = ROUND_UP(prev_end + a, (b + 1)) + c; |
||
411 | * |
||
412 | * We tweak these values a little to allow for a slightly easier |
||
413 | * computation and more compact storage. |
||
414 | */ |
||
415 | static void |
||
416 | tuple_table_append (GVariantMemberInfo **items, |
||
417 | gsize i, |
||
418 | gsize a, |
||
419 | gsize b, |
||
420 | gsize c) |
||
421 | { |
||
422 | GVariantMemberInfo *item = (*items)++; |
||
423 | |||
424 | /* We can shift multiples of the alignment size from 'c' into 'a'. |
||
425 | * As long as we're shifting whole multiples, it won't affect the |
||
426 | * result. This means that we can take the "aligned" portion off of |
||
427 | * 'c' and add it into 'a'. |
||
428 | * |
||
429 | * Imagine (for sake of clarity) that ROUND_10 rounds up to the |
||
430 | * nearest 10. It is clear that: |
||
431 | * |
||
432 | * ROUND_10(a) + c == ROUND_10(a + 10*(c / 10)) + (c % 10) |
||
433 | * |
||
434 | * ie: remove the 10s portion of 'c' and add it onto 'a'. |
||
435 | * |
||
436 | * To put some numbers on it, imagine we start with a = 34 and c = 27: |
||
437 | * |
||
438 | * ROUND_10(34) + 27 = 40 + 27 = 67 |
||
439 | * |
||
440 | * but also, we can split 27 up into 20 and 7 and do this: |
||
441 | * |
||
442 | * ROUND_10(34 + 20) + 7 = ROUND_10(54) + 7 = 60 + 7 = 67 |
||
443 | * ^^ ^ |
||
444 | * without affecting the result. We do that here. |
||
445 | * |
||
446 | * This reduction in the size of 'c' means that we can store it in a |
||
447 | * gchar instead of a gsize. Due to how the structure is packed, this |
||
448 | * ends up saving us 'two pointer sizes' per item in each tuple when |
||
449 | * allocating using GSlice. |
||
450 | */ |
||
451 | a += ~b & c; /* take the "aligned" part of 'c' and add to 'a' */ |
||
452 | c &= b; /* chop 'c' to contain only the unaligned part */ |
||
453 | |||
454 | |||
455 | /* Finally, we made one last adjustment. Recall: |
||
456 | * |
||
457 | * start = ROUND_UP(prev_end + a, (b + 1)) + c; |
||
458 | * |
||
459 | * Forgetting the '+ c' for the moment: |
||
460 | * |
||
461 | * ROUND_UP(prev_end + a, (b + 1)); |
||
462 | * |
||
463 | * we can do a "round up" operation by adding 1 less than the amount |
||
464 | * to round up to, then rounding down. ie: |
||
465 | * |
||
466 | * #define ROUND_UP(x, y) ROUND_DOWN(x + (y-1), y) |
||
467 | * |
||
468 | * Of course, for rounding down to a power of two, we can just mask |
||
469 | * out the appropriate number of low order bits: |
||
470 | * |
||
471 | * #define ROUND_DOWN(x, y) (x & ~(y - 1)) |
||
472 | * |
||
473 | * Which gives us |
||
474 | * |
||
475 | * #define ROUND_UP(x, y) (x + (y - 1) & ~(y - 1)) |
||
476 | * |
||
477 | * but recall that our alignment value 'b' is already "one less". |
||
478 | * This means that to round 'prev_end + a' up to 'b' we can just do: |
||
479 | * |
||
480 | * ((prev_end + a) + b) & ~b |
||
481 | * |
||
482 | * Associativity, and putting the 'c' back on: |
||
483 | * |
||
484 | * (prev_end + (a + b)) & ~b + c |
||
485 | * |
||
486 | * Now, since (a + b) is constant, we can just add 'b' to 'a' now and |
||
487 | * store that as the number to add to prev_end. Then we use ~b as the |
||
488 | * number to take a bitwise 'and' with. Finally, 'c' is added on. |
||
489 | * |
||
490 | * Note, however, that all the low order bits of the 'aligned' value |
||
491 | * are masked out and that all of the high order bits of 'c' have been |
||
492 | * "moved" to 'a' (in the previous step). This means that there are |
||
493 | * no overlapping bits in the addition -- so we can do a bitwise 'or' |
||
494 | * equivalently. |
||
495 | * |
||
496 | * This means that we can now compute the start address of a given |
||
497 | * item in the tuple using the algorithm given in the documentation |
||
498 | * for #GVariantMemberInfo: |
||
499 | * |
||
500 | * item_start = ((prev_end + a) & b) | c; |
||
501 | */ |
||
502 | |||
503 | item->i = i; |
||
504 | item->a = a + b; |
||
505 | item->b = ~b; |
||
506 | item->c = c; |
||
507 | } |
||
508 | |||
509 | static gsize |
||
510 | tuple_align (gsize offset, |
||
511 | guint alignment) |
||
512 | { |
||
513 | return offset + ((-offset) & alignment); |
||
514 | } |
||
515 | |||
516 | /* This function is the heart of the algorithm for calculating 'i', 'a', |
||
517 | * 'b' and 'c' for each item in the tuple. |
||
518 | * |
||
519 | * Imagine we want to find the start of the "i" in the type "(su(qx)ni)". |
||
520 | * That's a string followed by a uint32, then a tuple containing a |
||
521 | * uint16 and a int64, then an int16, then our "i". In order to get to |
||
522 | * our "i" we: |
||
523 | * |
||
524 | * Start at the end of the string, align to 4 (for the uint32), add 4. |
||
525 | * Align to 8, add 16 (for the tuple). Align to 2, add 2 (for the |
||
526 | * int16). Then we're there. It turns out that, given 3 simple rules, |
||
527 | * we can flatten this iteration into one addition, one alignment, then |
||
528 | * one more addition. |
||
529 | * |
||
530 | * The loop below plays through each item in the tuple, querying its |
||
531 | * alignment and fixed_size into 'd' and 'e', respectively. At all |
||
532 | * times the variables 'a', 'b', and 'c' are maintained such that in |
||
533 | * order to get to the current point, you add 'a', align to 'b' then add |
||
534 | * 'c'. 'b' is kept in "one less than" form. For each item, the proper |
||
535 | * alignment is applied to find the values of 'a', 'b' and 'c' to get to |
||
536 | * the start of that item. Those values are recorded into the table. |
||
537 | * The fixed size of the item (if applicable) is then added on. |
||
538 | * |
||
539 | * These 3 rules are how 'a', 'b' and 'c' are modified for alignment and |
||
540 | * addition of fixed size. They have been proven correct but are |
||
541 | * presented here, without proof: |
||
542 | * |
||
543 | * 1) in order to "align to 'd'" where 'd' is less than or equal to the |
||
544 | * largest level of alignment seen so far ('b'), you align 'c' to |
||
545 | * 'd'. |
||
546 | * 2) in order to "align to 'd'" where 'd' is greater than the largest |
||
547 | * level of alignment seen so far, you add 'c' aligned to 'b' to the |
||
548 | * value of 'a', set 'b' to 'd' (ie: increase the 'largest alignment |
||
549 | * seen') and reset 'c' to 0. |
||
550 | * 3) in order to "add 'e'", just add 'e' to 'c'. |
||
551 | */ |
||
552 | static void |
||
553 | tuple_generate_table (TupleInfo *info) |
||
554 | { |
||
555 | GVariantMemberInfo *items = info->members; |
||
556 | gsize i = -1, a = 0, b = 0, c = 0, d, e; |
||
557 | |||
558 | /* iterate over each item in the tuple. |
||
559 | * 'd' will be the alignment of the item (in one-less form) |
||
560 | * 'e' will be the fixed size (or 0 for variable-size items) |
||
561 | */ |
||
562 | while (tuple_get_item (info, items, &d, &e)) |
||
563 | { |
||
564 | /* align to 'd' */ |
||
565 | if (d <= b) |
||
566 | c = tuple_align (c, d); /* rule 1 */ |
||
567 | else |
||
568 | a += tuple_align (c, b), b = d, c = 0; /* rule 2 */ |
||
569 | |||
570 | /* the start of the item is at this point (ie: right after we |
||
571 | * have aligned for it). store this information in the table. |
||
572 | */ |
||
573 | tuple_table_append (&items, i, a, b, c); |
||
574 | |||
575 | /* "move past" the item by adding in its size. */ |
||
576 | if (e == 0) |
||
577 | /* variable size: |
||
578 | * |
||
579 | * we'll have an offset stored to mark the end of this item, so |
||
580 | * just bump the offset index to give us a new starting point |
||
581 | * and reset all the counters. |
||
582 | */ |
||
583 | i++, a = b = c = 0; |
||
584 | else |
||
585 | /* fixed size */ |
||
586 | c += e; /* rule 3 */ |
||
587 | } |
||
588 | } |
||
589 | |||
590 | static void |
||
591 | tuple_set_base_info (TupleInfo *info) |
||
592 | { |
||
593 | GVariantTypeInfo *base = &info->container.info; |
||
594 | |||
595 | if (info->n_members > 0) |
||
596 | { |
||
597 | GVariantMemberInfo *m; |
||
598 | |||
599 | /* the alignment requirement of the tuple is the alignment |
||
600 | * requirement of its largest item. |
||
601 | */ |
||
602 | base->alignment = 0; |
||
603 | for (m = info->members; m < &info->members[info->n_members]; m++) |
||
604 | /* can find the max of a list of "one less than" powers of two |
||
605 | * by 'or'ing them |
||
606 | */ |
||
607 | base->alignment |= m->type_info->alignment; |
||
608 | |||
609 | m--; /* take 'm' back to the last item */ |
||
610 | |||
611 | /* the structure only has a fixed size if no variable-size |
||
612 | * offsets are stored and the last item is fixed-sized too (since |
||
613 | * an offset is never stored for the last item). |
||
614 | */ |
||
615 | if (m->i == -1 && m->type_info->fixed_size) |
||
616 | /* in that case, the fixed size can be found by finding the |
||
617 | * start of the last item (in the usual way) and adding its |
||
618 | * fixed size. |
||
619 | * |
||
620 | * if a tuple has a fixed size then it is always a multiple of |
||
621 | * the alignment requirement (to make packing into arrays |
||
622 | * easier) so we round up to that here. |
||
623 | */ |
||
624 | base->fixed_size = |
||
625 | tuple_align (((m->a & m->b) | m->c) + m->type_info->fixed_size, |
||
626 | base->alignment); |
||
627 | else |
||
628 | /* else, the tuple is not fixed size */ |
||
629 | base->fixed_size = 0; |
||
630 | } |
||
631 | else |
||
632 | { |
||
633 | /* the empty tuple: '()'. |
||
634 | * |
||
635 | * has a size of 1 and an no alignment requirement. |
||
636 | * |
||
637 | * It has a size of 1 (not 0) for two practical reasons: |
||
638 | * |
||
639 | * 1) So we can determine how many of them are in an array |
||
640 | * without dividing by zero or without other tricks. |
||
641 | * |
||
642 | * 2) Even if we had some trick to know the number of items in |
||
643 | * the array (as GVariant did at one time) this would open a |
||
644 | * potential denial of service attack: an attacker could send |
||
645 | * you an extremely small array (in terms of number of bytes) |
||
646 | * containing trillions of zero-sized items. If you iterated |
||
647 | * over this array you would effectively infinite-loop your |
||
648 | * program. By forcing a size of at least one, we bound the |
||
649 | * amount of computation done in response to a message to a |
||
650 | * reasonable function of the size of that message. |
||
651 | */ |
||
652 | base->alignment = 0; |
||
653 | base->fixed_size = 1; |
||
654 | } |
||
655 | } |
||
656 | |||
657 | static ContainerInfo * |
||
658 | tuple_info_new (const GVariantType *type) |
||
659 | { |
||
660 | TupleInfo *info; |
||
661 | |||
662 | info = g_slice_new (TupleInfo); |
||
663 | info->container.info.container_class = GV_TUPLE_INFO_CLASS; |
||
664 | |||
665 | tuple_allocate_members (type, &info->members, &info->n_members); |
||
666 | tuple_generate_table (info); |
||
667 | tuple_set_base_info (info); |
||
668 | |||
669 | return (ContainerInfo *) info; |
||
670 | } |
||
671 | |||
672 | /* < private > |
||
673 | * g_variant_type_info_n_members: |
||
674 | * @info: a #GVariantTypeInfo for a tuple or dictionary entry type |
||
675 | * |
||
676 | * Returns the number of members in a tuple or dictionary entry type. |
||
677 | * For a dictionary entry this will always be 2. |
||
678 | */ |
||
679 | gsize |
||
680 | g_variant_type_info_n_members (GVariantTypeInfo *info) |
||
681 | { |
||
682 | return GV_TUPLE_INFO (info)->n_members; |
||
683 | } |
||
684 | |||
685 | /* < private > |
||
686 | * g_variant_type_info_member_info: |
||
687 | * @info: a #GVariantTypeInfo for a tuple or dictionary entry type |
||
688 | * @index: the member to fetch information for |
||
689 | * |
||
690 | * Returns the #GVariantMemberInfo for a given member. See |
||
691 | * documentation for that structure for why you would want this |
||
692 | * information. |
||
693 | * |
||
694 | * @index must refer to a valid child (ie: strictly less than |
||
695 | * g_variant_type_info_n_members() returns). |
||
696 | */ |
||
697 | const GVariantMemberInfo * |
||
698 | g_variant_type_info_member_info (GVariantTypeInfo *info, |
||
699 | gsize index) |
||
700 | { |
||
701 | TupleInfo *tuple_info = GV_TUPLE_INFO (info); |
||
702 | |||
703 | if (index < tuple_info->n_members) |
||
704 | return &tuple_info->members[index]; |
||
705 | |||
706 | return NULL; |
||
707 | } |
||
708 | |||
709 | /* == new/ref/unref == */ |
||
710 | static GRecMutex g_variant_type_info_lock; |
||
711 | static GHashTable *g_variant_type_info_table; |
||
712 | |||
713 | /* < private > |
||
714 | * g_variant_type_info_get: |
||
715 | * @type: a #GVariantType |
||
716 | * |
||
717 | * Returns a reference to a #GVariantTypeInfo for @type. |
||
718 | * |
||
719 | * If an info structure already exists for this type, a new reference is |
||
720 | * returned. If not, the required calculations are performed and a new |
||
721 | * info structure is returned. |
||
722 | * |
||
723 | * It is appropriate to call g_variant_type_info_unref() on the return |
||
724 | * value. |
||
725 | */ |
||
726 | GVariantTypeInfo * |
||
727 | g_variant_type_info_get (const GVariantType *type) |
||
728 | { |
||
729 | char type_char; |
||
730 | |||
731 | type_char = g_variant_type_peek_string (type)[0]; |
||
732 | |||
733 | if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE || |
||
734 | type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY || |
||
735 | type_char == G_VARIANT_TYPE_INFO_CHAR_TUPLE || |
||
736 | type_char == G_VARIANT_TYPE_INFO_CHAR_DICT_ENTRY) |
||
737 | { |
||
738 | GVariantTypeInfo *info; |
||
739 | gchar *type_string; |
||
740 | |||
741 | type_string = g_variant_type_dup_string (type); |
||
742 | |||
743 | g_rec_mutex_lock (&g_variant_type_info_lock); |
||
744 | |||
745 | if (g_variant_type_info_table == NULL) |
||
746 | g_variant_type_info_table = g_hash_table_new (g_str_hash, |
||
747 | g_str_equal); |
||
748 | info = g_hash_table_lookup (g_variant_type_info_table, type_string); |
||
749 | |||
750 | if (info == NULL) |
||
751 | { |
||
752 | ContainerInfo *container; |
||
753 | |||
754 | if (type_char == G_VARIANT_TYPE_INFO_CHAR_MAYBE || |
||
755 | type_char == G_VARIANT_TYPE_INFO_CHAR_ARRAY) |
||
756 | { |
||
757 | container = array_info_new (type); |
||
758 | } |
||
759 | else /* tuple or dict entry */ |
||
760 | { |
||
761 | container = tuple_info_new (type); |
||
762 | } |
||
763 | |||
764 | info = (GVariantTypeInfo *) container; |
||
765 | container->type_string = type_string; |
||
766 | container->ref_count = 1; |
||
767 | |||
768 | g_hash_table_insert (g_variant_type_info_table, type_string, info); |
||
769 | type_string = NULL; |
||
770 | } |
||
771 | else |
||
772 | g_variant_type_info_ref (info); |
||
773 | |||
774 | g_rec_mutex_unlock (&g_variant_type_info_lock); |
||
775 | g_variant_type_info_check (info, 0); |
||
776 | g_free (type_string); |
||
777 | |||
778 | return info; |
||
779 | } |
||
780 | else |
||
781 | { |
||
782 | const GVariantTypeInfo *info; |
||
783 | int index; |
||
784 | |||
785 | index = type_char - 'b'; |
||
786 | g_assert (G_N_ELEMENTS (g_variant_type_info_basic_table) == 24); |
||
787 | g_assert_cmpint (0, <=, index); |
||
788 | g_assert_cmpint (index, <, 24); |
||
789 | |||
790 | info = g_variant_type_info_basic_table + index; |
||
791 | g_variant_type_info_check (info, 0); |
||
792 | |||
793 | return (GVariantTypeInfo *) info; |
||
794 | } |
||
795 | } |
||
796 | |||
797 | /* < private > |
||
798 | * g_variant_type_info_ref: |
||
799 | * @info: a #GVariantTypeInfo |
||
800 | * |
||
801 | * Adds a reference to @info. |
||
802 | */ |
||
803 | GVariantTypeInfo * |
||
804 | g_variant_type_info_ref (GVariantTypeInfo *info) |
||
805 | { |
||
806 | g_variant_type_info_check (info, 0); |
||
807 | |||
808 | if (info->container_class) |
||
809 | { |
||
810 | ContainerInfo *container = (ContainerInfo *) info; |
||
811 | |||
812 | g_assert_cmpint (container->ref_count, >, 0); |
||
813 | g_atomic_int_inc (&container->ref_count); |
||
814 | } |
||
815 | |||
816 | return info; |
||
817 | } |
||
818 | |||
819 | /* < private > |
||
820 | * g_variant_type_info_unref: |
||
821 | * @info: a #GVariantTypeInfo |
||
822 | * |
||
823 | * Releases a reference held on @info. This may result in @info being |
||
824 | * freed. |
||
825 | */ |
||
826 | void |
||
827 | g_variant_type_info_unref (GVariantTypeInfo *info) |
||
828 | { |
||
829 | g_variant_type_info_check (info, 0); |
||
830 | |||
831 | if (info->container_class) |
||
832 | { |
||
833 | ContainerInfo *container = (ContainerInfo *) info; |
||
834 | |||
835 | g_rec_mutex_lock (&g_variant_type_info_lock); |
||
836 | if (g_atomic_int_dec_and_test (&container->ref_count)) |
||
837 | { |
||
838 | g_hash_table_remove (g_variant_type_info_table, |
||
839 | container->type_string); |
||
840 | if (g_hash_table_size (g_variant_type_info_table) == 0) |
||
841 | { |
||
842 | g_hash_table_unref (g_variant_type_info_table); |
||
843 | g_variant_type_info_table = NULL; |
||
844 | } |
||
845 | g_rec_mutex_unlock (&g_variant_type_info_lock); |
||
846 | |||
847 | g_free (container->type_string); |
||
848 | |||
849 | if (info->container_class == GV_ARRAY_INFO_CLASS) |
||
850 | array_info_free (info); |
||
851 | |||
852 | else if (info->container_class == GV_TUPLE_INFO_CLASS) |
||
853 | tuple_info_free (info); |
||
854 | |||
855 | else |
||
856 | g_assert_not_reached (); |
||
857 | } |
||
858 | else |
||
859 | g_rec_mutex_unlock (&g_variant_type_info_lock); |
||
860 | } |
||
861 | } |
||
862 | |||
863 | void |
||
864 | g_variant_type_info_assert_no_infos (void) |
||
865 | { |
||
866 | g_assert (g_variant_type_info_table == NULL); |
||
867 | } |