Commit | Line | Data |
---|---|---|
448d3cc7 | 1 | /* |
ccd7e1c8 | 2 | * enum.c |
448d3cc7 | 3 | * |
ccd7e1c8 | 4 | * BabelTrace - Enumeration Type |
448d3cc7 | 5 | * |
64fa3fec MD |
6 | * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation |
7 | * | |
8 | * Author: Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | |
448d3cc7 | 9 | * |
ccd7e1c8 MD |
10 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
11 | * of this software and associated documentation files (the "Software"), to deal | |
12 | * in the Software without restriction, including without limitation the rights | |
13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
14 | * copies of the Software, and to permit persons to whom the Software is | |
15 | * furnished to do so, subject to the following conditions: | |
448d3cc7 | 16 | * |
ccd7e1c8 MD |
17 | * The above copyright notice and this permission notice shall be included in |
18 | * all copies or substantial portions of the Software. | |
c462e188 MD |
19 | * |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
26 | * SOFTWARE. | |
448d3cc7 MD |
27 | */ |
28 | ||
29 | #include <babeltrace/compiler.h> | |
4c8bfb7e | 30 | #include <babeltrace/format.h> |
3122e6f0 | 31 | #include <babeltrace/types.h> |
448d3cc7 MD |
32 | #include <stdint.h> |
33 | #include <glib.h> | |
34 | ||
6148d997 MD |
35 | #if (__LONG_MAX__ == 2147483647L) |
36 | #define WORD_SIZE 32 | |
37 | #elif (__LONG_MAX__ == 9223372036854775807L) | |
38 | #define WORD_SIZE 64 | |
39 | #else | |
40 | #error "Unknown long size." | |
41 | #endif | |
42 | ||
c054553d | 43 | static |
ecc54f11 | 44 | struct bt_definition *_enum_definition_new(struct bt_declaration *declaration, |
05c749e5 | 45 | struct definition_scope *parent_scope, |
98df1c9f MD |
46 | GQuark field_name, int index, |
47 | const char *root_name); | |
c054553d | 48 | static |
0d69b916 | 49 | void _enum_definition_free(struct bt_definition *definition); |
c054553d | 50 | |
d65d8abb MD |
51 | static |
52 | void enum_range_set_free(void *ptr) | |
448d3cc7 | 53 | { |
d65d8abb | 54 | g_array_unref(ptr); |
448d3cc7 MD |
55 | } |
56 | ||
6148d997 | 57 | #if (WORD_SIZE == 32) |
bcdf4cf2 MD |
58 | static inline |
59 | gpointer get_uint_v(uint64_t *v) | |
60 | { | |
61 | return v; | |
62 | } | |
63 | ||
64 | static inline | |
65 | gpointer get_int_v(int64_t *v) | |
66 | { | |
67 | return v; | |
68 | } | |
69 | ||
70 | static | |
71 | guint enum_val_hash(gconstpointer key) | |
72 | { | |
73 | int64_t ukey = *(const int64_t *)key; | |
74 | ||
75 | return (guint)ukey ^ (guint)(ukey >> 32); | |
76 | } | |
77 | ||
78 | static | |
79 | gboolean enum_val_equal(gconstpointer a, gconstpointer b) | |
80 | { | |
81 | int64_t ua = *(const int64_t *)a; | |
82 | int64_t ub = *(const int64_t *)b; | |
83 | ||
84 | return ua == ub; | |
85 | } | |
86 | ||
87 | static | |
88 | void enum_val_free(void *ptr) | |
89 | { | |
90 | g_free(ptr); | |
91 | } | |
6148d997 | 92 | #else /* WORD_SIZE != 32 */ |
bcdf4cf2 MD |
93 | static inline |
94 | gpointer get_uint_v(uint64_t *v) | |
95 | { | |
96 | return (gpointer) *v; | |
97 | } | |
98 | ||
99 | static inline | |
100 | gpointer get_int_v(int64_t *v) | |
101 | { | |
102 | return (gpointer) *v; | |
103 | } | |
104 | ||
105 | static | |
106 | guint enum_val_hash(gconstpointer key) | |
107 | { | |
108 | return g_direct_hash(key); | |
109 | } | |
110 | ||
111 | static | |
112 | gboolean enum_val_equal(gconstpointer a, gconstpointer b) | |
113 | { | |
114 | return g_direct_equal(a, b); | |
115 | } | |
116 | ||
117 | static | |
118 | void enum_val_free(void *ptr) | |
119 | { | |
120 | } | |
6148d997 | 121 | #endif /* WORD_SIZE != 32 */ |
bcdf4cf2 | 122 | |
d65d8abb MD |
123 | /* |
124 | * Returns a GArray or NULL. | |
125 | * Caller must release the GArray with g_array_unref(). | |
126 | */ | |
2399b6f4 | 127 | GArray *bt_enum_uint_to_quark_set(const struct declaration_enum *enum_declaration, |
d65d8abb | 128 | uint64_t v) |
448d3cc7 | 129 | { |
d65d8abb MD |
130 | struct enum_range_to_quark *iter; |
131 | GArray *qs, *ranges = NULL; | |
448d3cc7 | 132 | |
d65d8abb | 133 | /* Single values lookup */ |
bcdf4cf2 MD |
134 | qs = g_hash_table_lookup(enum_declaration->table.value_to_quark_set, |
135 | get_uint_v(&v)); | |
d65d8abb MD |
136 | |
137 | /* Range lookup */ | |
3122e6f0 | 138 | bt_list_for_each_entry(iter, &enum_declaration->table.range_to_quark, node) { |
d65d8abb MD |
139 | if (iter->range.start._unsigned > v || iter->range.end._unsigned < v) |
140 | continue; | |
141 | if (!ranges) { | |
142 | size_t qs_len = 0; | |
143 | ||
144 | if (qs) | |
145 | qs_len = qs->len; | |
146 | ranges = g_array_sized_new(FALSE, TRUE, | |
ccdb988e | 147 | sizeof(GQuark), |
d65d8abb MD |
148 | qs_len + 1); |
149 | g_array_set_size(ranges, qs_len + 1); | |
150 | if (qs) | |
151 | memcpy(ranges->data, qs->data, | |
ccdb988e MD |
152 | sizeof(GQuark) * qs_len); |
153 | g_array_index(ranges, GQuark, qs_len) = iter->quark; | |
d65d8abb | 154 | } else { |
bcdf4cf2 MD |
155 | size_t qs_len = ranges->len; |
156 | ||
157 | g_array_set_size(ranges, qs_len + 1); | |
ccdb988e | 158 | g_array_index(ranges, GQuark, qs_len) = iter->quark; |
d65d8abb MD |
159 | } |
160 | } | |
fdacfb73 | 161 | if (!ranges) { |
bcdf4cf2 MD |
162 | if (!qs) |
163 | return NULL; | |
d65d8abb | 164 | ranges = qs; |
fdacfb73 MD |
165 | g_array_ref(ranges); |
166 | } | |
d65d8abb | 167 | return ranges; |
448d3cc7 MD |
168 | } |
169 | ||
d65d8abb MD |
170 | /* |
171 | * Returns a GArray or NULL. | |
172 | * Caller must release the GArray with g_array_unref(). | |
173 | */ | |
2399b6f4 | 174 | GArray *bt_enum_int_to_quark_set(const struct declaration_enum *enum_declaration, |
bcdf4cf2 | 175 | int64_t v) |
448d3cc7 | 176 | { |
d65d8abb MD |
177 | struct enum_range_to_quark *iter; |
178 | GArray *qs, *ranges = NULL; | |
179 | ||
180 | /* Single values lookup */ | |
bcdf4cf2 MD |
181 | qs = g_hash_table_lookup(enum_declaration->table.value_to_quark_set, |
182 | get_int_v(&v)); | |
d65d8abb MD |
183 | |
184 | /* Range lookup */ | |
3122e6f0 | 185 | bt_list_for_each_entry(iter, &enum_declaration->table.range_to_quark, node) { |
d65d8abb MD |
186 | if (iter->range.start._signed > v || iter->range.end._signed < v) |
187 | continue; | |
188 | if (!ranges) { | |
189 | size_t qs_len = 0; | |
190 | ||
191 | if (qs) | |
192 | qs_len = qs->len; | |
193 | ranges = g_array_sized_new(FALSE, TRUE, | |
ccdb988e | 194 | sizeof(GQuark), |
d65d8abb MD |
195 | qs_len + 1); |
196 | g_array_set_size(ranges, qs_len + 1); | |
197 | if (qs) | |
198 | memcpy(ranges->data, qs->data, | |
ccdb988e MD |
199 | sizeof(GQuark) * qs_len); |
200 | g_array_index(ranges, GQuark, qs_len) = iter->quark; | |
d65d8abb | 201 | } else { |
bcdf4cf2 MD |
202 | size_t qs_len = ranges->len; |
203 | ||
204 | g_array_set_size(ranges, qs_len + 1); | |
ccdb988e | 205 | g_array_index(ranges, GQuark, qs_len) = iter->quark; |
d65d8abb MD |
206 | } |
207 | } | |
fdacfb73 | 208 | if (!ranges) { |
bcdf4cf2 MD |
209 | if (!qs) |
210 | return NULL; | |
d65d8abb | 211 | ranges = qs; |
fdacfb73 MD |
212 | g_array_ref(ranges); |
213 | } | |
d65d8abb | 214 | return ranges; |
448d3cc7 MD |
215 | } |
216 | ||
d65d8abb | 217 | static |
2399b6f4 | 218 | void bt_enum_unsigned_insert_value_to_quark_set(struct declaration_enum *enum_declaration, |
d65d8abb | 219 | uint64_t v, GQuark q) |
448d3cc7 | 220 | { |
d65d8abb MD |
221 | uint64_t *valuep; |
222 | GArray *array; | |
448d3cc7 | 223 | |
bcdf4cf2 MD |
224 | array = g_hash_table_lookup(enum_declaration->table.value_to_quark_set, |
225 | get_uint_v(&v)); | |
d65d8abb MD |
226 | if (!array) { |
227 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
228 | g_array_set_size(array, 1); | |
229 | g_array_index(array, GQuark, array->len - 1) = q; | |
6148d997 | 230 | #if (WORD_SIZE == 32) |
d65d8abb MD |
231 | valuep = g_new(uint64_t, 1); |
232 | *valuep = v; | |
6148d997 | 233 | #else /* WORD_SIZE != 32 */ |
bcdf4cf2 | 234 | valuep = get_uint_v(&v); |
6148d997 | 235 | #endif /* WORD_SIZE != 32 */ |
f6625916 | 236 | g_hash_table_insert(enum_declaration->table.value_to_quark_set, valuep, array); |
d65d8abb MD |
237 | } else { |
238 | g_array_set_size(array, array->len + 1); | |
239 | g_array_index(array, GQuark, array->len - 1) = q; | |
240 | } | |
448d3cc7 | 241 | } |
448d3cc7 | 242 | |
d65d8abb | 243 | static |
2399b6f4 | 244 | void bt_enum_signed_insert_value_to_quark_set(struct declaration_enum *enum_declaration, |
d65d8abb MD |
245 | int64_t v, GQuark q) |
246 | { | |
bcdf4cf2 | 247 | int64_t *valuep; |
d65d8abb MD |
248 | GArray *array; |
249 | ||
f6625916 | 250 | array = g_hash_table_lookup(enum_declaration->table.value_to_quark_set, |
bcdf4cf2 | 251 | get_int_v(&v)); |
d65d8abb MD |
252 | if (!array) { |
253 | array = g_array_sized_new(FALSE, TRUE, sizeof(GQuark), 1); | |
254 | g_array_set_size(array, 1); | |
255 | g_array_index(array, GQuark, array->len - 1) = q; | |
6148d997 | 256 | #if (WORD_SIZE == 32) |
bcdf4cf2 MD |
257 | valuep = g_new(int64_t, 1); |
258 | *valuep = v; | |
6148d997 | 259 | #else /* WORD_SIZE != 32 */ |
bcdf4cf2 | 260 | valuep = get_int_v(&v); |
6148d997 | 261 | #endif /* WORD_SIZE != 32 */ |
bcdf4cf2 | 262 | g_hash_table_insert(enum_declaration->table.value_to_quark_set, valuep, array); |
d65d8abb MD |
263 | } else { |
264 | g_array_set_size(array, array->len + 1); | |
265 | g_array_index(array, GQuark, array->len - 1) = q; | |
266 | } | |
448d3cc7 MD |
267 | } |
268 | ||
2399b6f4 | 269 | GArray *bt_enum_quark_to_range_set(const struct declaration_enum *enum_declaration, |
d65d8abb | 270 | GQuark q) |
448d3cc7 | 271 | { |
f6625916 | 272 | return g_hash_table_lookup(enum_declaration->table.quark_to_range_set, |
47e0f2e2 | 273 | (gconstpointer) (unsigned long) q); |
448d3cc7 MD |
274 | } |
275 | ||
d65d8abb | 276 | static |
2399b6f4 | 277 | void bt_enum_signed_insert_range_to_quark(struct declaration_enum *enum_declaration, |
d65d8abb | 278 | int64_t start, int64_t end, GQuark q) |
448d3cc7 | 279 | { |
d65d8abb MD |
280 | struct enum_range_to_quark *rtoq; |
281 | ||
282 | rtoq = g_new(struct enum_range_to_quark, 1); | |
3122e6f0 | 283 | bt_list_add(&rtoq->node, &enum_declaration->table.range_to_quark); |
d65d8abb MD |
284 | rtoq->range.start._signed = start; |
285 | rtoq->range.end._signed = end; | |
286 | rtoq->quark = q; | |
448d3cc7 MD |
287 | } |
288 | ||
d65d8abb | 289 | static |
2399b6f4 | 290 | void bt_enum_unsigned_insert_range_to_quark(struct declaration_enum *enum_declaration, |
d65d8abb | 291 | uint64_t start, uint64_t end, GQuark q) |
448d3cc7 | 292 | { |
d65d8abb MD |
293 | struct enum_range_to_quark *rtoq; |
294 | ||
295 | rtoq = g_new(struct enum_range_to_quark, 1); | |
3122e6f0 | 296 | bt_list_add(&rtoq->node, &enum_declaration->table.range_to_quark); |
d65d8abb MD |
297 | rtoq->range.start._unsigned = start; |
298 | rtoq->range.end._unsigned = end; | |
299 | rtoq->quark = q; | |
448d3cc7 MD |
300 | } |
301 | ||
2399b6f4 | 302 | void bt_enum_signed_insert(struct declaration_enum *enum_declaration, |
d65d8abb | 303 | int64_t start, int64_t end, GQuark q) |
448d3cc7 | 304 | { |
d65d8abb MD |
305 | GArray *array; |
306 | struct enum_range *range; | |
307 | ||
308 | if (start == end) { | |
2399b6f4 | 309 | bt_enum_signed_insert_value_to_quark_set(enum_declaration, start, q); |
d65d8abb MD |
310 | } else { |
311 | if (start > end) { | |
312 | uint64_t tmp; | |
313 | ||
314 | tmp = start; | |
315 | start = end; | |
316 | end = tmp; | |
317 | } | |
2399b6f4 | 318 | bt_enum_signed_insert_range_to_quark(enum_declaration, start, end, q); |
d65d8abb MD |
319 | } |
320 | ||
f6625916 | 321 | array = g_hash_table_lookup(enum_declaration->table.quark_to_range_set, |
d65d8abb MD |
322 | (gconstpointer) (unsigned long) q); |
323 | if (!array) { | |
324 | array = g_array_sized_new(FALSE, TRUE, | |
325 | sizeof(struct enum_range), 1); | |
f6625916 | 326 | g_hash_table_insert(enum_declaration->table.quark_to_range_set, |
d65d8abb MD |
327 | (gpointer) (unsigned long) q, |
328 | array); | |
329 | } | |
330 | g_array_set_size(array, array->len + 1); | |
331 | range = &g_array_index(array, struct enum_range, array->len - 1); | |
332 | range->start._signed = start; | |
333 | range->end._signed = end; | |
448d3cc7 MD |
334 | } |
335 | ||
2399b6f4 | 336 | void bt_enum_unsigned_insert(struct declaration_enum *enum_declaration, |
d65d8abb | 337 | uint64_t start, uint64_t end, GQuark q) |
448d3cc7 | 338 | { |
d65d8abb MD |
339 | GArray *array; |
340 | struct enum_range *range; | |
341 | ||
342 | ||
343 | if (start == end) { | |
2399b6f4 | 344 | bt_enum_unsigned_insert_value_to_quark_set(enum_declaration, start, q); |
d65d8abb MD |
345 | } else { |
346 | if (start > end) { | |
347 | uint64_t tmp; | |
348 | ||
349 | tmp = start; | |
350 | start = end; | |
351 | end = tmp; | |
352 | } | |
2399b6f4 | 353 | bt_enum_unsigned_insert_range_to_quark(enum_declaration, start, end, q); |
d65d8abb MD |
354 | } |
355 | ||
f6625916 | 356 | array = g_hash_table_lookup(enum_declaration->table.quark_to_range_set, |
d65d8abb MD |
357 | (gconstpointer) (unsigned long) q); |
358 | if (!array) { | |
359 | array = g_array_sized_new(FALSE, TRUE, | |
360 | sizeof(struct enum_range), 1); | |
f6625916 | 361 | g_hash_table_insert(enum_declaration->table.quark_to_range_set, |
d65d8abb MD |
362 | (gpointer) (unsigned long) q, |
363 | array); | |
364 | } | |
365 | g_array_set_size(array, array->len + 1); | |
366 | range = &g_array_index(array, struct enum_range, array->len - 1); | |
367 | range->start._unsigned = start; | |
368 | range->end._unsigned = end; | |
448d3cc7 | 369 | } |
448d3cc7 | 370 | |
2399b6f4 | 371 | size_t bt_enum_get_nr_enumerators(struct declaration_enum *enum_declaration) |
c054553d | 372 | { |
f6625916 | 373 | return g_hash_table_size(enum_declaration->table.quark_to_range_set); |
c054553d MD |
374 | } |
375 | ||
c054553d | 376 | static |
ecc54f11 | 377 | void _enum_declaration_free(struct bt_declaration *declaration) |
90b676d7 | 378 | { |
f6625916 MD |
379 | struct declaration_enum *enum_declaration = |
380 | container_of(declaration, struct declaration_enum, p); | |
d65d8abb MD |
381 | struct enum_range_to_quark *iter, *tmp; |
382 | ||
f6625916 | 383 | g_hash_table_destroy(enum_declaration->table.value_to_quark_set); |
3122e6f0 JD |
384 | bt_list_for_each_entry_safe(iter, tmp, &enum_declaration->table.range_to_quark, node) { |
385 | bt_list_del(&iter->node); | |
d65d8abb MD |
386 | g_free(iter); |
387 | } | |
f6625916 | 388 | g_hash_table_destroy(enum_declaration->table.quark_to_range_set); |
e6b4b4f4 | 389 | bt_declaration_unref(&enum_declaration->integer_declaration->p); |
f6625916 | 390 | g_free(enum_declaration); |
90b676d7 MD |
391 | } |
392 | ||
f6625916 | 393 | struct declaration_enum * |
2399b6f4 | 394 | bt_enum_declaration_new(struct declaration_integer *integer_declaration) |
448d3cc7 | 395 | { |
f6625916 | 396 | struct declaration_enum *enum_declaration; |
448d3cc7 | 397 | |
f6625916 | 398 | enum_declaration = g_new(struct declaration_enum, 1); |
e19c3d69 | 399 | |
f6625916 | 400 | enum_declaration->table.value_to_quark_set = g_hash_table_new_full(enum_val_hash, |
d65d8abb MD |
401 | enum_val_equal, |
402 | enum_val_free, | |
403 | enum_range_set_free); | |
3122e6f0 | 404 | BT_INIT_LIST_HEAD(&enum_declaration->table.range_to_quark); |
068665f5 MD |
405 | enum_declaration->table.quark_to_range_set = g_hash_table_new_full(g_direct_hash, |
406 | g_direct_equal, | |
d65d8abb | 407 | NULL, enum_range_set_free); |
e6b4b4f4 | 408 | bt_declaration_ref(&integer_declaration->p); |
f6625916 MD |
409 | enum_declaration->integer_declaration = integer_declaration; |
410 | enum_declaration->p.id = CTF_TYPE_ENUM; | |
f6625916 | 411 | enum_declaration->p.alignment = 1; |
f6625916 MD |
412 | enum_declaration->p.declaration_free = _enum_declaration_free; |
413 | enum_declaration->p.definition_new = _enum_definition_new; | |
414 | enum_declaration->p.definition_free = _enum_definition_free; | |
415 | enum_declaration->p.ref = 1; | |
416 | return enum_declaration; | |
c054553d MD |
417 | } |
418 | ||
419 | static | |
0d69b916 | 420 | struct bt_definition * |
ecc54f11 | 421 | _enum_definition_new(struct bt_declaration *declaration, |
05c749e5 | 422 | struct definition_scope *parent_scope, |
98df1c9f MD |
423 | GQuark field_name, int index, |
424 | const char *root_name) | |
c054553d | 425 | { |
f6625916 MD |
426 | struct declaration_enum *enum_declaration = |
427 | container_of(declaration, struct declaration_enum, p); | |
e1151715 | 428 | struct definition_enum *_enum; |
0d69b916 | 429 | struct bt_definition *definition_integer_parent; |
98df1c9f | 430 | int ret; |
e19c3d69 | 431 | |
e1151715 | 432 | _enum = g_new(struct definition_enum, 1); |
e6b4b4f4 | 433 | bt_declaration_ref(&enum_declaration->p); |
f6625916 MD |
434 | _enum->p.declaration = declaration; |
435 | _enum->declaration = enum_declaration; | |
c054553d | 436 | _enum->p.ref = 1; |
98df1c9f MD |
437 | /* |
438 | * Use INT_MAX order to ensure that all fields of the parent | |
439 | * scope are seen as being prior to this scope. | |
440 | */ | |
441 | _enum->p.index = root_name ? INT_MAX : index; | |
b1a2f580 | 442 | _enum->p.name = field_name; |
2b77e6a6 JD |
443 | _enum->p.path = bt_new_definition_path(parent_scope, field_name, root_name); |
444 | _enum->p.scope = bt_new_definition_scope(parent_scope, field_name, root_name); | |
c054553d | 445 | _enum->value = NULL; |
2b77e6a6 | 446 | ret = bt_register_field_definition(field_name, &_enum->p, |
98df1c9f MD |
447 | parent_scope); |
448 | assert(!ret); | |
e1151715 | 449 | definition_integer_parent = |
f6625916 | 450 | enum_declaration->integer_declaration->p.definition_new(&enum_declaration->integer_declaration->p, |
a35173fe | 451 | _enum->p.scope, |
98df1c9f | 452 | g_quark_from_static_string("container"), 0, NULL); |
e1151715 MD |
453 | _enum->integer = container_of(definition_integer_parent, |
454 | struct definition_integer, p); | |
c054553d MD |
455 | return &_enum->p; |
456 | } | |
457 | ||
458 | static | |
0d69b916 | 459 | void _enum_definition_free(struct bt_definition *definition) |
c054553d | 460 | { |
e1151715 MD |
461 | struct definition_enum *_enum = |
462 | container_of(definition, struct definition_enum, p); | |
c054553d | 463 | |
13fad8b6 | 464 | bt_definition_unref(&_enum->integer->p); |
2b77e6a6 | 465 | bt_free_definition_scope(_enum->p.scope); |
e6b4b4f4 | 466 | bt_declaration_unref(_enum->p.declaration); |
c054553d | 467 | if (_enum->value) |
6ee5115e | 468 | g_array_unref(_enum->value); |
c054553d | 469 | g_free(_enum); |
448d3cc7 | 470 | } |