lib: rename include dir to babeltrace2
[babeltrace.git] / lib / trace-ir / resolve-field-path.c
CommitLineData
44c440bc
PP
1/*
2 * Copyright 2018 Philippe Proulx <pproulx@efficios.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#define BT_LOG_TAG "RESOLVE-FIELD-PATH"
3fadfbc0 24#include <babeltrace2/lib-logging-internal.h>
44c440bc 25
3fadfbc0
MJ
26#include <babeltrace2/assert-pre-internal.h>
27#include <babeltrace2/assert-internal.h>
28#include <babeltrace2/trace-ir/field-class-internal.h>
29#include <babeltrace2/trace-ir/field-path-internal.h>
30#include <babeltrace2/trace-ir/field-path-const.h>
31#include <babeltrace2/trace-ir/resolve-field-path-internal.h>
44c440bc
PP
32#include <limits.h>
33#include <stdint.h>
34#include <inttypes.h>
35#include <glib.h>
36
37static
5cd6d0e5
PP
38bool find_field_class_recursive(struct bt_field_class *fc,
39 struct bt_field_class *tgt_fc, struct bt_field_path *field_path)
44c440bc
PP
40{
41 bool found = false;
42
5cd6d0e5 43 if (tgt_fc == fc) {
44c440bc
PP
44 found = true;
45 goto end;
46 }
47
864cad70
PP
48 switch (fc->type) {
49 case BT_FIELD_CLASS_TYPE_STRUCTURE:
50 case BT_FIELD_CLASS_TYPE_VARIANT:
44c440bc 51 {
5cd6d0e5
PP
52 struct bt_field_class_named_field_class_container *container_fc =
53 (void *) fc;
44c440bc
PP
54 uint64_t i;
55
5cd6d0e5
PP
56 for (i = 0; i < container_fc->named_fcs->len; i++) {
57 struct bt_named_field_class *named_fc =
58 BT_FIELD_CLASS_NAMED_FC_AT_INDEX(
59 container_fc, i);
66ddcddf
PP
60 struct bt_field_path_item item = {
61 .type = BT_FIELD_PATH_ITEM_TYPE_INDEX,
62 .index = i,
63 };
44c440bc 64
66ddcddf 65 bt_field_path_append_item(field_path, &item);
5cd6d0e5
PP
66 found = find_field_class_recursive(named_fc->fc,
67 tgt_fc, field_path);
44c440bc
PP
68 if (found) {
69 goto end;
70 }
71
66ddcddf 72 bt_field_path_remove_last_item(field_path);
44c440bc
PP
73 }
74
75 break;
76 }
864cad70
PP
77 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
78 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
44c440bc 79 {
5cd6d0e5 80 struct bt_field_class_array *array_fc = (void *) fc;
66ddcddf
PP
81 struct bt_field_path_item item = {
82 .type = BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT,
83 .index = UINT64_C(-1),
84 };
44c440bc 85
66ddcddf 86 bt_field_path_append_item(field_path, &item);
5cd6d0e5
PP
87 found = find_field_class_recursive(array_fc->element_fc,
88 tgt_fc, field_path);
66ddcddf
PP
89 if (found) {
90 goto end;
91 }
92
93 bt_field_path_remove_last_item(field_path);
44c440bc
PP
94 break;
95 }
96 default:
97 break;
98 }
99
100end:
101 return found;
102}
103
104static
5cd6d0e5
PP
105int find_field_class(struct bt_field_class *root_fc,
106 enum bt_scope root_scope, struct bt_field_class *tgt_fc,
44c440bc
PP
107 struct bt_field_path **ret_field_path)
108{
109 int ret = 0;
110 struct bt_field_path *field_path = NULL;
111
5cd6d0e5 112 if (!root_fc) {
44c440bc
PP
113 goto end;
114 }
115
116 field_path = bt_field_path_create();
117 if (!field_path) {
118 ret = -1;
119 goto end;
120 }
121
122 field_path->root = root_scope;
5cd6d0e5 123 if (!find_field_class_recursive(root_fc, tgt_fc, field_path)) {
44c440bc 124 /* Not found here */
65300d60 125 BT_OBJECT_PUT_REF_AND_RESET(field_path);
44c440bc
PP
126 }
127
128end:
129 *ret_field_path = field_path;
130 return ret;
131}
132
133static
5cd6d0e5 134struct bt_field_path *find_field_class_in_ctx(struct bt_field_class *fc,
44c440bc
PP
135 struct bt_resolve_field_path_context *ctx)
136{
137 struct bt_field_path *field_path = NULL;
138 int ret;
139
5cd6d0e5
PP
140 ret = find_field_class(ctx->packet_context, BT_SCOPE_PACKET_CONTEXT,
141 fc, &field_path);
44c440bc
PP
142 if (ret || field_path) {
143 goto end;
144 }
145
5cd6d0e5
PP
146 ret = find_field_class(ctx->event_common_context,
147 BT_SCOPE_EVENT_COMMON_CONTEXT, fc, &field_path);
44c440bc
PP
148 if (ret || field_path) {
149 goto end;
150 }
151
5cd6d0e5
PP
152 ret = find_field_class(ctx->event_specific_context,
153 BT_SCOPE_EVENT_SPECIFIC_CONTEXT, fc, &field_path);
44c440bc
PP
154 if (ret || field_path) {
155 goto end;
156 }
157
5cd6d0e5
PP
158 ret = find_field_class(ctx->event_payload, BT_SCOPE_EVENT_PAYLOAD,
159 fc, &field_path);
44c440bc
PP
160 if (ret || field_path) {
161 goto end;
162 }
163
164end:
165 return field_path;
166}
167
168BT_ASSERT_PRE_FUNC
169static inline
170bool target_is_before_source(struct bt_field_path *src_field_path,
171 struct bt_field_path *tgt_field_path)
172{
173 bool is_valid = true;
174 uint64_t src_i = 0, tgt_i = 0;
175
176 if (tgt_field_path->root < src_field_path->root) {
177 goto end;
178 }
179
180 if (tgt_field_path->root > src_field_path->root) {
181 is_valid = false;
182 goto end;
183 }
184
185 BT_ASSERT(tgt_field_path->root == src_field_path->root);
186
66ddcddf
PP
187 for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len &&
188 tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) {
189 struct bt_field_path_item *src_fp_item =
190 bt_field_path_borrow_item_by_index_inline(
191 src_field_path, src_i);
192 struct bt_field_path_item *tgt_fp_item =
193 bt_field_path_borrow_item_by_index_inline(
194 tgt_field_path, tgt_i);
195
196 if (src_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX &&
197 tgt_fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX) {
198 if (tgt_fp_item->index > src_fp_item->index) {
199 is_valid = false;
200 goto end;
201 }
44c440bc
PP
202 }
203
204 src_i++;
205 tgt_i++;
206 }
207
208end:
209 return is_valid;
210}
211
212BT_ASSERT_PRE_FUNC
213static inline
5cd6d0e5 214struct bt_field_class *borrow_root_field_class(
44c440bc
PP
215 struct bt_resolve_field_path_context *ctx, enum bt_scope scope)
216{
217 switch (scope) {
44c440bc
PP
218 case BT_SCOPE_PACKET_CONTEXT:
219 return ctx->packet_context;
44c440bc
PP
220 case BT_SCOPE_EVENT_COMMON_CONTEXT:
221 return ctx->event_common_context;
222 case BT_SCOPE_EVENT_SPECIFIC_CONTEXT:
223 return ctx->event_specific_context;
224 case BT_SCOPE_EVENT_PAYLOAD:
225 return ctx->event_payload;
226 default:
227 abort();
228 }
229
230 return NULL;
231}
232
233BT_ASSERT_PRE_FUNC
234static inline
66ddcddf
PP
235struct bt_field_class *borrow_child_field_class(
236 struct bt_field_class *parent_fc,
237 struct bt_field_path_item *fp_item)
44c440bc 238{
5cd6d0e5 239 struct bt_field_class *child_fc = NULL;
44c440bc 240
864cad70
PP
241 switch (parent_fc->type) {
242 case BT_FIELD_CLASS_TYPE_STRUCTURE:
243 case BT_FIELD_CLASS_TYPE_VARIANT:
44c440bc 244 {
66ddcddf 245 struct bt_named_field_class *named_fc;
44c440bc 246
66ddcddf
PP
247 BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX);
248 named_fc = BT_FIELD_CLASS_NAMED_FC_AT_INDEX(parent_fc,
249 fp_item->index);
5cd6d0e5 250 child_fc = named_fc->fc;
44c440bc
PP
251 break;
252 }
864cad70
PP
253 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
254 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
44c440bc 255 {
5cd6d0e5 256 struct bt_field_class_array *array_fc = (void *) parent_fc;
44c440bc 257
66ddcddf
PP
258 BT_ASSERT(fp_item->type ==
259 BT_FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT);
5cd6d0e5 260 child_fc = array_fc->element_fc;
44c440bc
PP
261 break;
262 }
263 default:
264 break;
265 }
266
5cd6d0e5 267 return child_fc;
44c440bc
PP
268}
269
270BT_ASSERT_PRE_FUNC
271static inline
5cd6d0e5 272bool target_field_path_in_different_scope_has_struct_fc_only(
44c440bc
PP
273 struct bt_field_path *src_field_path,
274 struct bt_field_path *tgt_field_path,
275 struct bt_resolve_field_path_context *ctx)
276{
277 bool is_valid = true;
278 uint64_t i = 0;
5cd6d0e5 279 struct bt_field_class *fc;
44c440bc
PP
280
281 if (src_field_path->root == tgt_field_path->root) {
282 goto end;
283 }
284
5cd6d0e5 285 fc = borrow_root_field_class(ctx, tgt_field_path->root);
44c440bc 286
66ddcddf
PP
287 for (i = 0; i < tgt_field_path->items->len; i++) {
288 struct bt_field_path_item *fp_item =
289 bt_field_path_borrow_item_by_index_inline(
290 tgt_field_path, i);
44c440bc 291
864cad70
PP
292 if (fc->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY ||
293 fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY ||
294 fc->type == BT_FIELD_CLASS_TYPE_VARIANT) {
44c440bc
PP
295 is_valid = false;
296 goto end;
297 }
298
66ddcddf
PP
299 BT_ASSERT(fp_item->type == BT_FIELD_PATH_ITEM_TYPE_INDEX);
300 fc = borrow_child_field_class(fc, fp_item);
44c440bc
PP
301 }
302
303end:
304 return is_valid;
305}
306
307BT_ASSERT_PRE_FUNC
308static inline
5cd6d0e5 309bool lca_is_structure_field_class(struct bt_field_path *src_field_path,
44c440bc
PP
310 struct bt_field_path *tgt_field_path,
311 struct bt_resolve_field_path_context *ctx)
312{
313 bool is_valid = true;
5cd6d0e5
PP
314 struct bt_field_class *src_fc;
315 struct bt_field_class *tgt_fc;
316 struct bt_field_class *prev_fc = NULL;
44c440bc
PP
317 uint64_t src_i = 0, tgt_i = 0;
318
319 if (src_field_path->root != tgt_field_path->root) {
320 goto end;
321 }
322
5cd6d0e5
PP
323 src_fc = borrow_root_field_class(ctx, src_field_path->root);
324 tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root);
325 BT_ASSERT(src_fc);
326 BT_ASSERT(tgt_fc);
44c440bc 327
66ddcddf
PP
328 for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len &&
329 tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) {
330 struct bt_field_path_item *src_fp_item =
331 bt_field_path_borrow_item_by_index_inline(
332 src_field_path, src_i);
333 struct bt_field_path_item *tgt_fp_item =
334 bt_field_path_borrow_item_by_index_inline(
335 tgt_field_path, tgt_i);
44c440bc 336
5cd6d0e5
PP
337 if (src_fc != tgt_fc) {
338 if (!prev_fc) {
44c440bc
PP
339 /*
340 * This is correct: the LCA is the root
e6276565
PP
341 * scope field class, which must be a
342 * structure field class.
44c440bc
PP
343 */
344 break;
345 }
346
864cad70 347 if (prev_fc->type != BT_FIELD_CLASS_TYPE_STRUCTURE) {
44c440bc
PP
348 is_valid = false;
349 }
350
351 break;
352 }
353
5cd6d0e5 354 prev_fc = src_fc;
66ddcddf
PP
355 src_fc = borrow_child_field_class(src_fc, src_fp_item);
356 tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item);
44c440bc
PP
357 }
358
359end:
360 return is_valid;
361}
362
363BT_ASSERT_PRE_FUNC
364static inline
5cd6d0e5 365bool lca_to_target_has_struct_fc_only(struct bt_field_path *src_field_path,
44c440bc
PP
366 struct bt_field_path *tgt_field_path,
367 struct bt_resolve_field_path_context *ctx)
368{
369 bool is_valid = true;
5cd6d0e5
PP
370 struct bt_field_class *src_fc;
371 struct bt_field_class *tgt_fc;
44c440bc
PP
372 uint64_t src_i = 0, tgt_i = 0;
373
374 if (src_field_path->root != tgt_field_path->root) {
375 goto end;
376 }
377
5cd6d0e5
PP
378 src_fc = borrow_root_field_class(ctx, src_field_path->root);
379 tgt_fc = borrow_root_field_class(ctx, tgt_field_path->root);
380 BT_ASSERT(src_fc);
381 BT_ASSERT(tgt_fc);
382 BT_ASSERT(src_fc == tgt_fc);
44c440bc
PP
383
384 /* Find LCA */
66ddcddf
PP
385 for (src_i = 0, tgt_i = 0; src_i < src_field_path->items->len &&
386 tgt_i < tgt_field_path->items->len; src_i++, tgt_i++) {
387 struct bt_field_path_item *src_fp_item =
388 bt_field_path_borrow_item_by_index_inline(
389 src_field_path, src_i);
390 struct bt_field_path_item *tgt_fp_item =
391 bt_field_path_borrow_item_by_index_inline(
392 tgt_field_path, tgt_i);
44c440bc
PP
393
394 if (src_i != tgt_i) {
5cd6d0e5 395 /* Next field class is different: LCA is `tgt_fc` */
44c440bc
PP
396 break;
397 }
398
66ddcddf
PP
399 src_fc = borrow_child_field_class(src_fc, src_fp_item);
400 tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item);
44c440bc
PP
401 }
402
5cd6d0e5 403 /* Only structure field classes to the target */
66ddcddf
PP
404 for (; tgt_i < tgt_field_path->items->len; tgt_i++) {
405 struct bt_field_path_item *tgt_fp_item =
406 bt_field_path_borrow_item_by_index_inline(
407 tgt_field_path, tgt_i);
44c440bc 408
864cad70
PP
409 if (tgt_fc->type == BT_FIELD_CLASS_TYPE_STATIC_ARRAY ||
410 tgt_fc->type == BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY ||
411 tgt_fc->type == BT_FIELD_CLASS_TYPE_VARIANT) {
44c440bc
PP
412 is_valid = false;
413 goto end;
414 }
415
66ddcddf 416 tgt_fc = borrow_child_field_class(tgt_fc, tgt_fp_item);
44c440bc
PP
417 }
418
419end:
420 return is_valid;
421}
422
423BT_ASSERT_PRE_FUNC
424static inline
5cd6d0e5
PP
425bool field_path_is_valid(struct bt_field_class *src_fc,
426 struct bt_field_class *tgt_fc,
44c440bc
PP
427 struct bt_resolve_field_path_context *ctx)
428{
429 bool is_valid = true;
5cd6d0e5
PP
430 struct bt_field_path *src_field_path = find_field_class_in_ctx(
431 src_fc, ctx);
432 struct bt_field_path *tgt_field_path = find_field_class_in_ctx(
433 tgt_fc, ctx);
44c440bc
PP
434
435 if (!src_field_path) {
e6276565 436 BT_ASSERT_PRE_MSG("Cannot find requesting field class in "
5cd6d0e5 437 "resolving context: %!+F", src_fc);
44c440bc
PP
438 is_valid = false;
439 goto end;
440 }
441
442 if (!tgt_field_path) {
e6276565 443 BT_ASSERT_PRE_MSG("Cannot find target field class in "
5cd6d0e5 444 "resolving context: %!+F", tgt_fc);
44c440bc
PP
445 is_valid = false;
446 goto end;
447 }
448
449 /* Target must be before source */
450 if (!target_is_before_source(src_field_path, tgt_field_path)) {
e6276565
PP
451 BT_ASSERT_PRE_MSG("Target field class is located after "
452 "requesting field class: %![req-fc-]+F, %![tgt-fc-]+F",
5cd6d0e5 453 src_fc, tgt_fc);
44c440bc
PP
454 is_valid = false;
455 goto end;
456 }
457
458 /*
459 * If target is in a different scope than source, there are no
5cd6d0e5 460 * array or variant field classes on the way to the target.
44c440bc 461 */
5cd6d0e5 462 if (!target_field_path_in_different_scope_has_struct_fc_only(
44c440bc 463 src_field_path, tgt_field_path, ctx)) {
e6276565
PP
464 BT_ASSERT_PRE_MSG("Target field class is located in a "
465 "different scope than requesting field class, "
466 "but within an array or a variant field class: "
5cd6d0e5
PP
467 "%![req-fc-]+F, %![tgt-fc-]+F",
468 src_fc, tgt_fc);
44c440bc
PP
469 is_valid = false;
470 goto end;
471 }
472
e6276565 473 /* Same scope: LCA must be a structure field class */
5cd6d0e5 474 if (!lca_is_structure_field_class(src_field_path, tgt_field_path, ctx)) {
44c440bc 475 BT_ASSERT_PRE_MSG("Lowest common ancestor of target and "
e6276565 476 "requesting field classes is not a structure field class: "
5cd6d0e5
PP
477 "%![req-fc-]+F, %![tgt-fc-]+F",
478 src_fc, tgt_fc);
44c440bc
PP
479 is_valid = false;
480 goto end;
481 }
482
483 /* Same scope: path from LCA to target has no array/variant FTs */
5cd6d0e5 484 if (!lca_to_target_has_struct_fc_only(src_field_path, tgt_field_path,
44c440bc
PP
485 ctx)) {
486 BT_ASSERT_PRE_MSG("Path from lowest common ancestor of target "
e6276565
PP
487 "and requesting field classes to target field class "
488 "contains an array or a variant field class: "
5cd6d0e5 489 "%![req-fc-]+F, %![tgt-fc-]+F", src_fc, tgt_fc);
44c440bc
PP
490 is_valid = false;
491 goto end;
492 }
493
494end:
65300d60
PP
495 bt_object_put_ref(src_field_path);
496 bt_object_put_ref(tgt_field_path);
44c440bc
PP
497 return is_valid;
498}
499
500static
5cd6d0e5
PP
501struct bt_field_path *resolve_field_path(struct bt_field_class *src_fc,
502 struct bt_field_class *tgt_fc,
44c440bc
PP
503 struct bt_resolve_field_path_context *ctx)
504{
5cd6d0e5 505 BT_ASSERT_PRE(field_path_is_valid(src_fc, tgt_fc, ctx),
e6276565 506 "Invalid target field class: %![req-fc-]+F, %![tgt-fc-]+F",
5cd6d0e5
PP
507 src_fc, tgt_fc);
508 return find_field_class_in_ctx(tgt_fc, ctx);
44c440bc
PP
509}
510
511BT_HIDDEN
5cd6d0e5 512int bt_resolve_field_paths(struct bt_field_class *fc,
44c440bc
PP
513 struct bt_resolve_field_path_context *ctx)
514{
515 int ret = 0;
516
5cd6d0e5 517 BT_ASSERT(fc);
44c440bc 518
5cd6d0e5 519 /* Resolving part for dynamic array and variant field classes */
864cad70
PP
520 switch (fc->type) {
521 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
44c440bc 522 {
5cd6d0e5 523 struct bt_field_class_dynamic_array *dyn_array_fc = (void *) fc;
44c440bc 524
5cd6d0e5
PP
525 if (dyn_array_fc->length_fc) {
526 BT_ASSERT(!dyn_array_fc->length_field_path);
527 dyn_array_fc->length_field_path = resolve_field_path(
528 fc, dyn_array_fc->length_fc, ctx);
529 if (!dyn_array_fc->length_field_path) {
44c440bc
PP
530 ret = -1;
531 goto end;
532 }
533 }
534
535 break;
536 }
864cad70 537 case BT_FIELD_CLASS_TYPE_VARIANT:
44c440bc 538 {
5cd6d0e5
PP
539 struct bt_field_class_variant *var_fc = (void *) fc;
540
541 if (var_fc->selector_fc) {
542 BT_ASSERT(!var_fc->selector_field_path);
543 var_fc->selector_field_path =
544 resolve_field_path(fc,
545 var_fc->selector_fc, ctx);
546 if (!var_fc->selector_field_path) {
44c440bc
PP
547 ret = -1;
548 goto end;
549 }
550 }
551 }
552 default:
553 break;
554 }
555
556 /* Recursive part */
864cad70
PP
557 switch (fc->type) {
558 case BT_FIELD_CLASS_TYPE_STRUCTURE:
559 case BT_FIELD_CLASS_TYPE_VARIANT:
44c440bc 560 {
5cd6d0e5
PP
561 struct bt_field_class_named_field_class_container *container_fc =
562 (void *) fc;
44c440bc
PP
563 uint64_t i;
564
5cd6d0e5
PP
565 for (i = 0; i < container_fc->named_fcs->len; i++) {
566 struct bt_named_field_class *named_fc =
567 BT_FIELD_CLASS_NAMED_FC_AT_INDEX(
568 container_fc, i);
44c440bc 569
5cd6d0e5 570 ret = bt_resolve_field_paths(named_fc->fc, ctx);
44c440bc
PP
571 if (ret) {
572 goto end;
573 }
574 }
575
576 break;
577 }
864cad70
PP
578 case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
579 case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY:
44c440bc 580 {
5cd6d0e5 581 struct bt_field_class_array *array_fc = (void *) fc;
44c440bc 582
5cd6d0e5 583 ret = bt_resolve_field_paths(array_fc->element_fc, ctx);
44c440bc
PP
584 break;
585 }
586 default:
587 break;
588 }
589
590end:
591 return ret;
592}
This page took 0.054835 seconds and 4 git commands to generate.