+/*
+ * This function returns whether or not a given field class `tag_fc`
+ * is valid as the tag field class of the variant field class `fc`.
+ *
+ * CTF 1.8 requires that the tag field class be an enumeration field
+ * class and that, for each variant field class option's range set, the
+ * tag field class contains a mapping which has the option's name and an
+ * equal range set.
+ */
+static inline
+bool _is_variant_field_class_tag_valid(
+ struct fs_sink_ctf_field_class_variant *fc,
+ struct fs_sink_ctf_field_class *tag_fc)
+{
+ bool is_valid = true;
+ bt_field_class_type ir_tag_fc_type = bt_field_class_get_type(
+ tag_fc->ir_fc);
+ uint64_t i;
+ GString *escaped_opt_name = g_string_new(NULL);
+
+ BT_ASSERT(escaped_opt_name);
+
+ if (ir_tag_fc_type != BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION &&
+ ir_tag_fc_type != BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION) {
+ is_valid = false;
+ goto end;
+ }
+
+ for (i = 0; i < bt_field_class_variant_get_option_count(
+ fc->base.ir_fc); i++) {
+ const bt_field_class_variant_option *var_opt_base =
+ bt_field_class_variant_borrow_option_by_index_const(
+ fc->base.ir_fc, i);
+ const char *opt_name = bt_field_class_variant_option_get_name(
+ var_opt_base);
+
+ /*
+ * If the option is named `name` in trace IR, then it
+ * was _possibly_ named `_name` originally if it comes
+ * from `src.ctf.fs`. This means the corresponding
+ * enumeration field class mapping was also named
+ * `_name`, but this one didn't change, as enumeration
+ * FC mapping names are not escaped; they are literal
+ * strings.
+ *
+ * The `sink.ctf.fs` component escapes all the variant
+ * FC option names with `_`. Therefore the
+ * _escaped name_ must match the original enumeration
+ * FC mapping name.
+ */
+ g_string_assign(escaped_opt_name, "_");
+ g_string_append(escaped_opt_name, opt_name);
+
+ if (ir_tag_fc_type == BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION) {
+ const bt_field_class_variant_with_unsigned_selector_option *var_opt =
+ bt_field_class_variant_with_unsigned_selector_borrow_option_by_index_const(
+ fc->base.ir_fc, i);
+ const bt_field_class_unsigned_enumeration_mapping *mapping;
+ const bt_integer_range_set_unsigned *opt_ranges;
+ const bt_integer_range_set_unsigned *mapping_ranges;
+
+ mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_label_const(
+ tag_fc->ir_fc, escaped_opt_name->str);
+ if (!mapping) {
+ is_valid = false;
+ goto end;
+ }
+
+ opt_ranges = bt_field_class_variant_with_unsigned_selector_option_borrow_ranges_const(
+ var_opt);
+ mapping_ranges = bt_field_class_unsigned_enumeration_mapping_borrow_ranges_const(
+ mapping);
+ if (!bt_integer_range_set_unsigned_compare(opt_ranges,
+ mapping_ranges)) {
+ is_valid = false;
+ goto end;
+ }
+ } else {
+ const bt_field_class_variant_with_signed_selector_option *var_opt =
+ bt_field_class_variant_with_signed_selector_borrow_option_by_index_const(
+ fc->base.ir_fc, i);
+ const bt_field_class_signed_enumeration_mapping *mapping;
+ const bt_integer_range_set_signed *opt_ranges;
+ const bt_integer_range_set_signed *mapping_ranges;
+
+ mapping = bt_field_class_signed_enumeration_borrow_mapping_by_label_const(
+ tag_fc->ir_fc, escaped_opt_name->str);
+ if (!mapping) {
+ is_valid = false;
+ goto end;
+ }
+
+ opt_ranges = bt_field_class_variant_with_signed_selector_option_borrow_ranges_const(
+ var_opt);
+ mapping_ranges = bt_field_class_signed_enumeration_mapping_borrow_ranges_const(
+ mapping);
+ if (!bt_integer_range_set_signed_compare(opt_ranges,
+ mapping_ranges)) {
+ is_valid = false;
+ goto end;
+ }
+ }
+ }
+
+end:
+ return is_valid;
+}
+
+/*
+ * This function indicates whether or not a given variant FC option name
+ * must be protected (with the `_` prefix).
+ *
+ * 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 must_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, bool *must_protect)
+{
+ 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;
+
+ *must_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) {
+ *must_protect = true;
+ 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_unsigned_selector_option *var_opt =
+ bt_field_class_variant_with_unsigned_selector_borrow_option_by_index_const(
+ ir_var_fc, opt_i);
+ opt_ranges =
+ bt_field_class_variant_with_unsigned_selector_option_borrow_ranges_const(
+ var_opt);
+ } else {
+ const bt_field_class_variant_with_signed_selector_option *var_opt =
+ bt_field_class_variant_with_signed_selector_borrow_option_by_index_const(
+ ir_var_fc, opt_i);
+ opt_ranges =
+ bt_field_class_variant_with_signed_selector_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_unsigned_enumeration_mapping *mapping;
+ const bt_integer_range_set_unsigned *mapping_ranges;
+
+ mapping = bt_field_class_unsigned_enumeration_borrow_mapping_by_index_const(
+ ir_tag_fc, i);
+ mapping_ranges = bt_field_class_unsigned_enumeration_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_unsigned_enumeration_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_signed_enumeration_mapping *mapping;
+ const bt_integer_range_set_signed *mapping_ranges;
+
+ mapping = bt_field_class_signed_enumeration_borrow_mapping_by_index_const(
+ ir_tag_fc, i);
+ mapping_ranges = bt_field_class_signed_enumeration_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_signed_enumeration_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;
+ }
+
+ /*
+ * If this comes from a `src.ctf.fs` source, it looks
+ * like the variant FC option name was initially
+ * protected: protect it again when going back to TSDL.
+ */
+ *must_protect = true;
+ }
+
+end:
+ return ret;
+}
+