+/*
+ * This function protects a given variant FC option name (with the `_`
+ * prefix) if required. On success, `name_buf->str` contains the variant
+ * FC option name to use (original option name or protected if
+ * required).
+ *
+ * One of the goals of `sink.ctf.fs` is to write a CTF trace which is as
+ * close as possible to an original CTF trace as decoded by
+ * `src.ctf.fs`.
+ *
+ * This scenario is valid in CTF 1.8:
+ *
+ * enum {
+ * HELLO,
+ * MEOW
+ * } tag;
+ *
+ * variant <tag> {
+ * int HELLO;
+ * string MEOW;
+ * };
+ *
+ * Once in trace IR, the enumeration FC mapping names and variant FC
+ * option names are kept as is. For this reason, we don't want to
+ * protect the variant FC option names here (by prepending `_`): this
+ * would make the variant FC option name and the enumeration FC mapping
+ * name not match.
+ *
+ * This scenario is also valid in CTF 1.8:
+ *
+ * enum {
+ * _HELLO,
+ * MEOW
+ * } tag;
+ *
+ * variant <tag> {
+ * int _HELLO;
+ * string MEOW;
+ * };
+ *
+ * Once in trace IR, the enumeration FC mapping names are kept as is,
+ * but the `_HELLO` variant FC option name becomes `HELLO` (unprotected
+ * for presentation, as recommended by CTF 1.8). When going back to
+ * TSDL, we need to protect `HELLO` so that it becomes `_HELLO` to match
+ * the corresponding enumeration FC mapping name.
+ *
+ * This scenario is also valid in CTF 1.8:
+ *
+ * enum {
+ * __HELLO,
+ * MEOW
+ * } tag;
+ *
+ * variant <tag> {
+ * int __HELLO;
+ * string MEOW;
+ * };
+ *
+ * Once in trace IR, the enumeration FC mapping names are kept as is,
+ * but the `__HELLO` variant FC option name becomes `_HELLO`
+ * (unprotected). When going back to TSDL, we need to protect `_HELLO`
+ * so that it becomes `__HELLO` to match the corresponding enumeration
+ * FC mapping name.
+ *
+ * `src.ctf.fs` always uses the _same_ integer range sets for a selector
+ * FC mapping and a corresponding variant FC option. We can use that
+ * fact to find the original variant FC option names by matching variant
+ * FC options and enumeration FC mappings by range set.
+ */
+static
+int maybe_protect_variant_option_name(const bt_field_class *ir_var_fc,
+ const bt_field_class *ir_tag_fc, uint64_t opt_i,
+ GString *name_buf)
+{
+ int ret = 0;
+ uint64_t i;
+ bt_field_class_type ir_var_fc_type;
+ const void *opt_ranges = NULL;
+ const char *mapping_label = NULL;
+ const char *ir_opt_name;
+ const bt_field_class_variant_option *base_var_opt;
+ bool force_protect = false;
+
+ ir_var_fc_type = bt_field_class_get_type(ir_var_fc);
+ base_var_opt = bt_field_class_variant_borrow_option_by_index_const(
+ ir_var_fc, opt_i);
+ BT_ASSERT(base_var_opt);
+ ir_opt_name = bt_field_class_variant_option_get_name(base_var_opt);
+ BT_ASSERT(ir_opt_name);
+
+ /*
+ * Check if the variant FC option name is required to be
+ * protected (reserved TSDL keyword or starts with `_`). In that
+ * case, the name of the selector FC mapping we find must match
+ * exactly the protected name.
+ */
+ force_protect = must_protect_identifier(ir_opt_name);
+ if (force_protect) {
+ g_string_assign(name_buf, "_");
+ g_string_append(name_buf, ir_opt_name);
+ } else {
+ g_string_assign(name_buf, ir_opt_name);
+ }
+
+ /* Borrow option's ranges */
+ if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR) {
+ /* No ranges: we're done */
+ goto end;
+ } if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR) {
+ const bt_field_class_variant_with_selector_unsigned_option *var_opt =
+ bt_field_class_variant_with_selector_unsigned_borrow_option_by_index_const(
+ ir_var_fc, opt_i);
+ opt_ranges =
+ bt_field_class_variant_with_selector_unsigned_option_borrow_ranges_const(
+ var_opt);
+ } else {
+ const bt_field_class_variant_with_selector_signed_option *var_opt =
+ bt_field_class_variant_with_selector_signed_borrow_option_by_index_const(
+ ir_var_fc, opt_i);
+ opt_ranges =
+ bt_field_class_variant_with_selector_signed_option_borrow_ranges_const(
+ var_opt);
+ }
+
+ /* Find corresponding mapping by range set in selector FC */
+ for (i = 0; i < bt_field_class_enumeration_get_mapping_count(ir_tag_fc);
+ i++) {
+ if (ir_var_fc_type == BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_SELECTOR) {
+ const bt_field_class_enumeration_mapping *mapping_base;
+ const bt_field_class_enumeration_unsigned_mapping *mapping;
+ const bt_integer_range_set_unsigned *mapping_ranges;
+
+ mapping = bt_field_class_enumeration_unsigned_borrow_mapping_by_index_const(
+ ir_tag_fc, i);
+ mapping_ranges = bt_field_class_enumeration_unsigned_mapping_borrow_ranges_const(
+ mapping);
+
+ if (bt_integer_range_set_unsigned_compare(opt_ranges,
+ mapping_ranges)) {
+ /* We have a winner */
+ mapping_base =
+ bt_field_class_enumeration_unsigned_mapping_as_mapping_const(
+ mapping);
+ mapping_label =
+ bt_field_class_enumeration_mapping_get_label(
+ mapping_base);
+ break;
+ }
+ } else {
+ const bt_field_class_enumeration_mapping *mapping_base;
+ const bt_field_class_enumeration_signed_mapping *mapping;
+ const bt_integer_range_set_signed *mapping_ranges;
+
+ mapping = bt_field_class_enumeration_signed_borrow_mapping_by_index_const(
+ ir_tag_fc, i);
+ mapping_ranges = bt_field_class_enumeration_signed_mapping_borrow_ranges_const(
+ mapping);
+
+ if (bt_integer_range_set_signed_compare(opt_ranges,
+ mapping_ranges)) {
+ /* We have a winner */
+ mapping_base =
+ bt_field_class_enumeration_signed_mapping_as_mapping_const(
+ mapping);
+ mapping_label =
+ bt_field_class_enumeration_mapping_get_label(
+ mapping_base);
+ break;
+ }
+ }
+ }
+
+ if (!mapping_label) {
+ /* Range set not found: invalid selector for CTF 1.8 */
+ ret = -1;
+ goto end;
+ }
+
+ /*
+ * If the enumeration FC mapping name is not the same as the
+ * variant FC option name and we didn't protect already, try
+ * protecting the option name and check again.
+ */
+ if (strcmp(mapping_label, name_buf->str) != 0) {
+ if (force_protect) {
+ ret = -1;
+ goto end;
+ }
+
+ if (mapping_label[0] == '\0') {
+ ret = -1;
+ goto end;
+ }
+
+ g_string_assign(name_buf, "_");
+ g_string_append(name_buf, ir_opt_name);
+
+ if (strcmp(mapping_label, name_buf->str) != 0) {
+ ret = -1;
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+static inline
+int translate_option_field_class(struct ctx *ctx)
+{
+ struct fs_sink_ctf_field_class_option *fc =
+ fs_sink_ctf_field_class_option_create_empty(
+ cur_path_stack_top(ctx)->ir_fc,
+ cur_path_stack_top(ctx)->index_in_parent);
+ const bt_field_class *content_ir_fc =
+ bt_field_class_option_borrow_field_class_const(fc->base.ir_fc);
+ int ret;
+
+ BT_ASSERT(fc);
+
+ /*
+ * CTF 1.8 does not support the option field class type. To
+ * write something anyway, this component translates this type
+ * to a variant field class where the options are:
+ *
+ * * An empty structure field class.
+ * * The optional field class itself.
+ *
+ * The "tag" is always generated/before in that case (an 8-bit
+ * unsigned enumeration field class).
+ */
+ append_to_parent_field_class(ctx, (void *) fc);
+ ret = cur_path_stack_push(ctx, UINT64_C(-1), NULL, false, content_ir_fc,
+ (void *) fc);
+ if (ret) {
+ BT_COMP_LOGE_STR("Cannot translate option field class content.");
+ goto end;
+ }
+
+ ret = translate_field_class(ctx);
+ if (ret) {
+ BT_COMP_LOGE_STR("Cannot translate option field class content.");
+ goto end;
+ }
+
+ cur_path_stack_pop(ctx);
+ update_parent_field_class_alignment(ctx, fc->base.alignment);
+
+end:
+ return ret;
+}
+