/*
 * SPDX-License-Identifier: MIT
 *
 * Copyright 2017-2018 Philippe Proulx <pproulx@efficios.com>
 * Copyright 2013, 2014 Jérémie Galarneau <jeremie.galarneau@efficios.com>
 */

#define BT_LOG_TAG "LIB/FIELD"
#include "lib/logging.h"

#include "lib/assert-cond.h"
#include <babeltrace2/trace-ir/field.h>
#include "lib/object.h"
#include "compat/compiler.h"
#include "compat/fcntl.h"
#include "common/assert.h"
#include <inttypes.h>
#include <stdbool.h>

#include "field.h"
#include "field-class.h"
#include "lib/func-status.h"
#include "utils.h"

#define BT_ASSERT_PRE_DEV_FIELD_HOT(_field)				\
	BT_ASSERT_PRE_DEV_HOT("field",					\
		(const struct bt_field *) (_field), "Field", ": %!+f", (_field))

static
void reset_single_field(struct bt_field *field);

static
void reset_array_field(struct bt_field *field);

static
void reset_structure_field(struct bt_field *field);

static
void reset_option_field(struct bt_field *field);

static
void reset_variant_field(struct bt_field *field);

static
void set_single_field_is_frozen(struct bt_field *field, bool is_frozen);

static
void set_array_field_is_frozen(struct bt_field *field, bool is_frozen);

static
void set_structure_field_is_frozen(struct bt_field *field, bool is_frozen);

static
void set_option_field_is_frozen(struct bt_field *field, bool is_frozen);

static
void set_variant_field_is_frozen(struct bt_field *field, bool is_frozen);

static
bool single_field_is_set(const struct bt_field *field);

static
bool array_field_is_set(const struct bt_field *field);

static
bool structure_field_is_set(const struct bt_field *field);

static
bool option_field_is_set(const struct bt_field *field);

static
bool variant_field_is_set(const struct bt_field *field);

static
struct bt_field_methods bool_field_methods = {
	.set_is_frozen = set_single_field_is_frozen,
	.is_set = single_field_is_set,
	.reset = reset_single_field,
};

static
struct bt_field_methods bit_array_field_methods = {
	.set_is_frozen = set_single_field_is_frozen,
	.is_set = single_field_is_set,
	.reset = reset_single_field,
};

static
struct bt_field_methods integer_field_methods = {
	.set_is_frozen = set_single_field_is_frozen,
	.is_set = single_field_is_set,
	.reset = reset_single_field,
};

static
struct bt_field_methods real_field_methods = {
	.set_is_frozen = set_single_field_is_frozen,
	.is_set = single_field_is_set,
	.reset = reset_single_field,
};

static
struct bt_field_methods string_field_methods = {
	.set_is_frozen = set_single_field_is_frozen,
	.is_set = single_field_is_set,
	.reset = reset_single_field,
};

static
struct bt_field_methods structure_field_methods = {
	.set_is_frozen = set_structure_field_is_frozen,
	.is_set = structure_field_is_set,
	.reset = reset_structure_field,
};

static
struct bt_field_methods array_field_methods = {
	.set_is_frozen = set_array_field_is_frozen,
	.is_set = array_field_is_set,
	.reset = reset_array_field,
};

static
struct bt_field_methods option_field_methods = {
	.set_is_frozen = set_option_field_is_frozen,
	.is_set = option_field_is_set,
	.reset = reset_option_field,
};

static
struct bt_field_methods variant_field_methods = {
	.set_is_frozen = set_variant_field_is_frozen,
	.is_set = variant_field_is_set,
	.reset = reset_variant_field,
};

static
struct bt_field_methods blob_field_methods = {
	.set_is_frozen = set_single_field_is_frozen,
	.is_set = single_field_is_set,
	.reset = reset_single_field,
};

static
struct bt_field *create_bool_field(struct bt_field_class *);

static
struct bt_field *create_bit_array_field(struct bt_field_class *);

static
struct bt_field *create_integer_field(struct bt_field_class *);

static
struct bt_field *create_real_field(struct bt_field_class *);

static
struct bt_field *create_string_field(struct bt_field_class *);

static
struct bt_field *create_structure_field(struct bt_field_class *);

static
struct bt_field *create_static_array_field(struct bt_field_class *);

static
struct bt_field *create_dynamic_array_field(struct bt_field_class *);

static
struct bt_field *create_option_field(struct bt_field_class *);

static
struct bt_field *create_variant_field(struct bt_field_class *);

static
struct bt_field *create_blob_field(struct bt_field_class *);

static
void destroy_bool_field(struct bt_field *field);

static
void destroy_bit_array_field(struct bt_field *field);

static
void destroy_integer_field(struct bt_field *field);

static
void destroy_real_field(struct bt_field *field);

static
void destroy_string_field(struct bt_field *field);

static
void destroy_structure_field(struct bt_field *field);

static
void destroy_array_field(struct bt_field *field);

static
void destroy_option_field(struct bt_field *field);

static
void destroy_variant_field(struct bt_field *field);

static
void destroy_blob_field(struct bt_field *field);

BT_EXPORT
struct bt_field_class *bt_field_borrow_class(struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	return field->class;
}

BT_EXPORT
const struct bt_field_class *bt_field_borrow_class_const(
		const struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	return field->class;
}

BT_EXPORT
enum bt_field_class_type bt_field_get_class_type(const struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	return field->class->type;
}

struct bt_field *bt_field_create(struct bt_field_class *fc)
{
	struct bt_field *field = NULL;

	BT_ASSERT(fc);

	switch (fc->type) {
	case BT_FIELD_CLASS_TYPE_BOOL:
		field = create_bool_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_BIT_ARRAY:
		field = create_bit_array_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
	case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
	case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
	case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
		field = create_integer_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL:
	case BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL:
		field = create_real_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_STRING:
		field = create_string_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_STRUCTURE:
		field = create_structure_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
		field = create_static_array_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD:
		field = create_dynamic_array_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_OPTION_WITHOUT_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
		field = create_option_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
		field = create_variant_field(fc);
		break;
	case BT_FIELD_CLASS_TYPE_STATIC_BLOB:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_BLOB_WITHOUT_LENGTH_FIELD:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_BLOB_WITH_LENGTH_FIELD:
		field = create_blob_field(fc);
		break;
	default:
		bt_common_abort();
	}

	if (!field) {
		BT_LIB_LOGE_APPEND_CAUSE("Cannot create field object from field class: "
			"%![fc-]+F", fc);
		goto end;
	}

end:
	return field;
}

static inline
void init_field(struct bt_field *field, struct bt_field_class *fc,
		struct bt_field_methods *methods)
{
	BT_ASSERT(field);
	BT_ASSERT(fc);
	bt_object_init_unique(&field->base);
	field->methods = methods;
	field->class = fc;
	bt_object_get_ref_no_null_check(fc);
}

static
struct bt_field *create_bool_field(struct bt_field_class *fc)
{
	struct bt_field_bool *bool_field;

	BT_LIB_LOGD("Creating boolean field object: %![fc-]+F", fc);
	bool_field = g_new0(struct bt_field_bool, 1);
	if (!bool_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one boolean field.");
		goto end;
	}

	init_field((void *) bool_field, fc, &bool_field_methods);
	BT_LIB_LOGD("Created boolean field object: %!+f", bool_field);

end:
	return (void *) bool_field;
}

static
struct bt_field *create_bit_array_field(struct bt_field_class *fc)
{
	struct bt_field_bit_array *ba_field;

	BT_LIB_LOGD("Creating bit array field object: %![fc-]+F", fc);
	ba_field = g_new0(struct bt_field_bit_array, 1);
	if (!ba_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one bit array field.");
		goto end;
	}

	init_field((void *) ba_field, fc, &bit_array_field_methods);
	BT_LIB_LOGD("Created bit array field object: %!+f", ba_field);

end:
	return (void *) ba_field;
}

static
struct bt_field *create_integer_field(struct bt_field_class *fc)
{
	struct bt_field_integer *int_field;

	BT_LIB_LOGD("Creating integer field object: %![fc-]+F", fc);
	int_field = g_new0(struct bt_field_integer, 1);
	if (!int_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one integer field.");
		goto end;
	}

	init_field((void *) int_field, fc, &integer_field_methods);
	BT_LIB_LOGD("Created integer field object: %!+f", int_field);

end:
	return (void *) int_field;
}

static
struct bt_field *create_real_field(struct bt_field_class *fc)
{
	struct bt_field_real *real_field;

	BT_LIB_LOGD("Creating real field object: %![fc-]+F", fc);
	real_field = g_new0(struct bt_field_real, 1);
	if (!real_field) {
		BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate one real field.");
		goto end;
	}

	init_field((void *) real_field, fc, &real_field_methods);
	BT_LIB_LOGD("Created real field object: %!+f", real_field);

end:
	return (void *) real_field;
}

static
struct bt_field *create_string_field(struct bt_field_class *fc)
{
	struct bt_field_string *string_field;

	BT_LIB_LOGD("Creating string field object: %![fc-]+F", fc);
	string_field = g_new0(struct bt_field_string, 1);
	if (!string_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one string field.");
		goto end;
	}

	init_field((void *) string_field, fc, &string_field_methods);
	string_field->buf = g_array_sized_new(FALSE, FALSE,
		sizeof(char), 1);
	if (!string_field->buf) {
		BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate a GArray.");
		bt_field_destroy((void *) string_field);
		string_field = NULL;
		goto end;
	}

	g_array_set_size(string_field->buf, 1);
	bt_g_array_index(string_field->buf, char, 0) = '\0';
	BT_LIB_LOGD("Created string field object: %!+f", string_field);

end:
	return (void *) string_field;
}

static inline
int create_fields_from_named_field_classes(
		struct bt_field_class_named_field_class_container *fc,
		GPtrArray **fields)
{
	int ret = 0;
	uint64_t i;

	*fields = g_ptr_array_new_with_free_func(
		(GDestroyNotify) bt_field_destroy);
	if (!*fields) {
		BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate a GPtrArray.");
		ret = -1;
		goto end;
	}

	g_ptr_array_set_size(*fields, fc->named_fcs->len);

	for (i = 0; i < fc->named_fcs->len; i++) {
		struct bt_field *field;
		struct bt_named_field_class *named_fc = fc->named_fcs->pdata[i];

		field = bt_field_create(named_fc->fc);
		if (!field) {
			BT_LIB_LOGE_APPEND_CAUSE(
				"Failed to create structure member or variant option field: "
				"name=\"%s\", %![fc-]+F",
				named_fc->name->str, named_fc->fc);
			ret = -1;
			goto end;
		}

		g_ptr_array_index(*fields, i) = field;
	}

end:
	return ret;
}

static
struct bt_field *create_structure_field(struct bt_field_class *fc)
{
	struct bt_field_structure *struct_field;

	BT_LIB_LOGD("Creating structure field object: %![fc-]+F", fc);
	struct_field = g_new0(struct bt_field_structure, 1);
	if (!struct_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one structure field.");
		goto end;
	}

	init_field((void *) struct_field, fc, &structure_field_methods);

	if (create_fields_from_named_field_classes((void *) fc,
			&struct_field->fields)) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Cannot create structure member fields: %![fc-]+F", fc);
		bt_field_destroy((void *) struct_field);
		struct_field = NULL;
		goto end;
	}

	BT_LIB_LOGD("Created structure field object: %!+f", struct_field);

end:
	return (void *) struct_field;
}

static
struct bt_field *create_option_field(struct bt_field_class *fc)
{
	struct bt_field_option *opt_field;
	struct bt_field_class_option *opt_fc = (void *) fc;

	BT_LIB_LOGD("Creating option field object: %![fc-]+F", fc);
	opt_field = g_new0(struct bt_field_option, 1);
	if (!opt_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one option field.");
		goto end;
	}

	init_field((void *) opt_field, fc, &option_field_methods);
	opt_field->content_field = bt_field_create(opt_fc->content_fc);
	if (!opt_field->content_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to create option field's content field: "
			"%![opt-fc-]+F, %![content-fc-]+F",
			opt_fc, opt_fc->content_fc);
		bt_field_destroy((void *) opt_field);
		opt_field = NULL;
		goto end;
	}

	BT_LIB_LOGD("Created option field object: %!+f", opt_field);

end:
	return (void *) opt_field;
}

static
struct bt_field *create_variant_field(struct bt_field_class *fc)
{
	struct bt_field_variant *var_field;

	BT_LIB_LOGD("Creating variant field object: %![fc-]+F", fc);
	var_field = g_new0(struct bt_field_variant, 1);
	if (!var_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one variant field.");
		goto end;
	}

	init_field((void *) var_field, fc, &variant_field_methods);

	if (create_fields_from_named_field_classes((void *) fc,
			&var_field->fields)) {
		BT_LIB_LOGE_APPEND_CAUSE("Cannot create variant member fields: "
			"%![fc-]+F", fc);
		bt_field_destroy((void *) var_field);
		var_field = NULL;
		goto end;
	}

	BT_LIB_LOGD("Created variant field object: %!+f", var_field);

end:
	return (void *) var_field;
}

static
struct bt_field *create_blob_field(struct bt_field_class *fc)
{
	struct bt_field_blob *blob_field;

	BT_LIB_LOGD("Creating BLOB field object: %![fc-]+F", fc);
	blob_field = g_new0(struct bt_field_blob, 1);
	if (!blob_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one BLOB field.");
		goto end;
	}

	init_field((void *) blob_field, fc, &blob_field_methods);

	if (bt_field_class_type_is(fc->type, BT_FIELD_CLASS_TYPE_STATIC_BLOB)) {
		struct bt_field_class_blob_static *blob_static_fc =
			(void *) fc;
		blob_field->length = blob_static_fc->length;
		blob_field->data = g_malloc(blob_field->length);
		if (!blob_field->data) {
			BT_LIB_LOGE_APPEND_CAUSE(
				"Failed to allocate BLOB field data: %![fc-]+F",
				fc);
			goto error;
		}
	}

	goto end;
error:
	bt_field_destroy((void *) blob_field);
	blob_field = NULL;

end:
	return (void *) blob_field;
}

static inline
int init_array_field_fields(struct bt_field_array *array_field)
{
	int ret = 0;
	uint64_t i;
	struct bt_field_class_array *array_fc;

	BT_ASSERT(array_field);
	array_fc = (void *) array_field->common.class;
	array_field->fields = g_ptr_array_sized_new(array_field->length);
	if (!array_field->fields) {
		BT_LIB_LOGE_APPEND_CAUSE("Failed to allocate a GPtrArray.");
		ret = -1;
		goto end;
	}

	g_ptr_array_set_free_func(array_field->fields,
		(GDestroyNotify) bt_field_destroy);
	g_ptr_array_set_size(array_field->fields, array_field->length);

	for (i = 0; i < array_field->length; i++) {
		array_field->fields->pdata[i] = bt_field_create(
			array_fc->element_fc);
		if (!array_field->fields->pdata[i]) {
			BT_LIB_LOGE_APPEND_CAUSE(
				"Cannot create array field's element field: "
				"index=%" PRIu64 ", %![fc-]+F", i, array_fc);
			ret = -1;
			goto end;
		}
	}

end:
	return ret;
}

static
struct bt_field *create_static_array_field(struct bt_field_class *fc)
{
	struct bt_field_class_array_static *array_fc = (void *) fc;
	struct bt_field_array *array_field;

	BT_LIB_LOGD("Creating static array field object: %![fc-]+F", fc);
	array_field = g_new0(struct bt_field_array, 1);
	if (!array_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one static array field.");
		goto end;
	}

	init_field((void *) array_field, fc, &array_field_methods);
	array_field->length = array_fc->length;

	if (init_array_field_fields(array_field)) {
		BT_LIB_LOGE_APPEND_CAUSE("Cannot create static array fields: "
			"%![fc-]+F", fc);
		bt_field_destroy((void *) array_field);
		array_field = NULL;
		goto end;
	}

	BT_LIB_LOGD("Created static array field object: %!+f", array_field);

end:
	return (void *) array_field;
}

static
struct bt_field *create_dynamic_array_field(struct bt_field_class *fc)
{
	struct bt_field_array *array_field;

	BT_LIB_LOGD("Creating dynamic array field object: %![fc-]+F", fc);
	array_field = g_new0(struct bt_field_array, 1);
	if (!array_field) {
		BT_LIB_LOGE_APPEND_CAUSE(
			"Failed to allocate one dynamic array field.");
		goto end;
	}

	init_field((void *) array_field, fc, &array_field_methods);

	if (init_array_field_fields(array_field)) {
		BT_LIB_LOGE_APPEND_CAUSE("Cannot create dynamic array fields: "
			"%![fc-]+F", fc);
		bt_field_destroy((void *) array_field);
		array_field = NULL;
		goto end;
	}

	BT_LIB_LOGD("Created dynamic array field object: %!+f", array_field);

end:
	return (void *) array_field;
}

BT_EXPORT
bt_bool bt_field_bool_get_value(const struct bt_field *field)
{
	const struct bt_field_bool *bool_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"boolean-field", BT_FIELD_CLASS_TYPE_BOOL, "Field");
	return (bt_bool) bool_field->value;
}

BT_EXPORT
void bt_field_bool_set_value(struct bt_field *field, bt_bool value)
{
	struct bt_field_bool *bool_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"boolean-field", BT_FIELD_CLASS_TYPE_BOOL, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	bool_field->value = (bool) value;
	bt_field_set_single(field, true);
}

BT_EXPORT
uint64_t bt_field_bit_array_get_value_as_integer(const struct bt_field *field)
{
	const struct bt_field_bit_array *ba_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"bit-array-field", BT_FIELD_CLASS_TYPE_BIT_ARRAY, "Field");
	return ba_field->value_as_int;
}

BT_EXPORT
void bt_field_bit_array_set_value_as_integer(struct bt_field *field,
		uint64_t value)
{
	struct bt_field_bit_array *ba_field = (void *) field;
	struct bt_field_class_bit_array *ba_fc;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"bit-array-field", BT_FIELD_CLASS_TYPE_BIT_ARRAY, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	ba_fc = (void *) field->class;
	ba_field->value_as_int = value;

	if (ba_fc->length < 64) {
		/* Apply mask */
		ba_field->value_as_int &= ((UINT64_C(1) << ba_fc->length) - 1);
	}

	bt_field_set_single(field, true);
}

BT_EXPORT
bt_field_bit_array_get_active_flag_labels_status
bt_field_bit_array_get_active_flag_labels(const struct bt_field *field,
		bt_field_class_bit_array_flag_label_array *label_array,
		uint64_t *count)
{
	const struct bt_field_bit_array *ba_field = (const void *) field;

	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"bit-array-field", BT_FIELD_CLASS_TYPE_BIT_ARRAY, "Field");
	BT_ASSERT_PRE_DEV_NON_NULL("label-array-output", label_array,
		"Label array (output)");
	BT_ASSERT_PRE_DEV_NON_NULL("count-output", count, "Count (output)");
	return (int)
		bt_field_class_bit_array_get_active_flag_labels_for_value_as_integer(
			field->class, ba_field->value_as_int, label_array, count);
}

BT_EXPORT
int64_t bt_field_integer_signed_get_value(const struct bt_field *field)
{
	const struct bt_field_integer *int_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SIGNED_INT("field", field, "Field");
	return int_field->value.i;
}

BT_EXPORT
void bt_field_integer_signed_set_value(struct bt_field *field, int64_t value)
{
	struct bt_field_integer *int_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SIGNED_INT("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	BT_ASSERT_PRE_DEV("valid-value-for-field-class-field-value-range",
		bt_util_value_is_in_range_signed(
			((struct bt_field_class_integer *) field->class)->range,
			value),
		"Value is out of bounds: value=%" PRId64 ", %![field-]+f, "
		"%![fc-]+F", value, field, field->class);
	int_field->value.i = value;
	bt_field_set_single(field, true);
}

BT_EXPORT
uint64_t bt_field_integer_unsigned_get_value(const struct bt_field *field)
{
	const struct bt_field_integer *int_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_IS_UNSIGNED_INT("field", field, "Field");
	return int_field->value.u;
}

BT_EXPORT
void bt_field_integer_unsigned_set_value(struct bt_field *field, uint64_t value)
{
	struct bt_field_integer *int_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_UNSIGNED_INT("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	BT_ASSERT_PRE_DEV("valid-value-for-field-class-field-value-range",
		bt_util_value_is_in_range_unsigned(
			((struct bt_field_class_integer *) field->class)->range,
			value),
		"Value is out of bounds: value=%" PRIu64 ", %![field-]+f, "
		"%![fc-]+F", value, field, field->class);
	int_field->value.u = value;
	bt_field_set_single(field, true);
}

BT_EXPORT
float bt_field_real_single_precision_get_value(const struct bt_field *field)
{
	const struct bt_field_real *real_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"single-precision-real-field",
		BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL, "Field");
	return (float) real_field->value;
}

BT_EXPORT
double bt_field_real_double_precision_get_value(const struct bt_field *field)
{
	const struct bt_field_real *real_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"double-precision-real-field",
		BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL, "Field");
	return real_field->value;
}

BT_EXPORT
void bt_field_real_single_precision_set_value(struct bt_field *field,
		float value)
{
	struct bt_field_real *real_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"single-precision-real-field",
		BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);

	real_field->value = (double) value;
	bt_field_set_single(field, true);
}

BT_EXPORT
void bt_field_real_double_precision_set_value(struct bt_field *field,
		double value)
{
	struct bt_field_real *real_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"double-precision-real-field",
		BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);

	real_field->value = value;
	bt_field_set_single(field, true);
}

BT_EXPORT
enum bt_field_enumeration_get_mapping_labels_status
bt_field_enumeration_unsigned_get_mapping_labels(
		const struct bt_field *field,
		bt_field_class_enumeration_mapping_label_array *label_array,
		uint64_t *count)
{
	const struct bt_field_integer *int_field = (const void *) field;

	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_NON_NULL("label-array-output", label_array,
		"Label array (output)");
	BT_ASSERT_PRE_DEV_NON_NULL("count-output", count, "Count (output)");
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"unsigned-enumeration-field",
		BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION, "Field");
	return (int)
		bt_field_class_enumeration_unsigned_get_mapping_labels_for_value(
			field->class, int_field->value.u, label_array, count);
}

BT_EXPORT
enum bt_field_enumeration_get_mapping_labels_status
bt_field_enumeration_signed_get_mapping_labels(
		const struct bt_field *field,
		bt_field_class_enumeration_mapping_label_array *label_array,
		uint64_t *count)
{
	const struct bt_field_integer *int_field = (const void *) field;

	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_NON_NULL("label-array-output", label_array,
		"Label array (output)");
	BT_ASSERT_PRE_DEV_NON_NULL("count-output", count, "Count (output)");
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"signed-enumeration-field",
		BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION, "Field");
	return (int)
		bt_field_class_enumeration_signed_get_mapping_labels_for_value(
			field->class, int_field->value.i, label_array, count);
}

BT_EXPORT
const char *bt_field_string_get_value(const struct bt_field *field)
{
	const struct bt_field_string *string_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field, "string-field",
		BT_FIELD_CLASS_TYPE_STRING, "Field");
	return (const char *) string_field->buf->data;
}

BT_EXPORT
uint64_t bt_field_string_get_length(const struct bt_field *field)
{
	const struct bt_field_string *string_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_SET("field", field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field, "string-field",
		BT_FIELD_CLASS_TYPE_STRING, "Field");
	return string_field->length;
}

static inline
void clear_string_field(struct bt_field *field)
{
	struct bt_field_string *string_field = (void *) field;

	BT_ASSERT_DBG(field);
	string_field->length = 0;
	bt_g_array_index(string_field->buf, char, 0) = '\0';
	bt_field_set_single(field, true);
}

BT_EXPORT
enum bt_field_string_set_value_status bt_field_string_set_value(
		struct bt_field *field, const char *value)
{
	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_NON_NULL("value", value, "Value");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field, "string-field",
		BT_FIELD_CLASS_TYPE_STRING, "Field");
	clear_string_field(field);
	return (int) bt_field_string_append_with_length(field, value,
		(uint64_t) strlen(value));
}

#define BT_ASSERT_PRE_DEV_FOR_APPEND_TO_STRING_FIELD_WITH_LENGTH(_field, _value, _length) \
	do {								\
		BT_ASSERT_PRE_DEV_NO_ERROR();				\
		BT_ASSERT_PRE_DEV_FIELD_NON_NULL(_field);		\
		BT_ASSERT_PRE_DEV_NON_NULL("value", (_value), "Value");	\
		BT_ASSERT_PRE_DEV_FIELD_HOT(_field);			\
		BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field",		\
			(_field), "string-field",			\
			BT_FIELD_CLASS_TYPE_STRING, "Field");		\
		BT_ASSERT_PRE_DEV("value-has-no-null-byte",		\
			!memchr((_value), '\0', (_length)),		\
			"String value to append contains a null character: " \
			"partial-value=\"%.32s\", length=%" PRIu64,	\
			(_value), (_length));					\
	} while (0)

static
enum bt_field_string_append_status append_to_string_field_with_length(
		struct bt_field *field, const char *value, uint64_t length)
{
	struct bt_field_string *string_field = (void *) field;
	char *data;
	uint64_t new_length;

	BT_ASSERT_DBG(field);
	BT_ASSERT_DBG(value);
	new_length = length + string_field->length;

	if (G_UNLIKELY(new_length + 1 > string_field->buf->len)) {
		g_array_set_size(string_field->buf, new_length + 1);
	}

	data = string_field->buf->data;
	memcpy(data + string_field->length, value, length);
	((char *) string_field->buf->data)[new_length] = '\0';
	string_field->length = new_length;
	bt_field_set_single(field, true);
	return BT_FUNC_STATUS_OK;
}

BT_EXPORT
enum bt_field_string_append_status bt_field_string_append_with_length(
		struct bt_field *field, const char *value, uint64_t length)
{
	BT_ASSERT_PRE_DEV_FOR_APPEND_TO_STRING_FIELD_WITH_LENGTH(field, value,
		length);
	return append_to_string_field_with_length(field, value, length);
}

BT_EXPORT
enum bt_field_string_append_status bt_field_string_append(
		struct bt_field *field, const char *value)
{
	uint64_t length = (uint64_t) strlen(value);

	BT_ASSERT_PRE_DEV_FOR_APPEND_TO_STRING_FIELD_WITH_LENGTH(field, value,
		length);
	return append_to_string_field_with_length(field, value, length);
}

BT_EXPORT
void bt_field_string_clear(struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field, "string-field",
		BT_FIELD_CLASS_TYPE_STRING, "Field");
	clear_string_field(field);
}

BT_EXPORT
uint64_t bt_field_array_get_length(const struct bt_field *field)
{
	const struct bt_field_array *array_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_ARRAY("field", field, "Field");
	return array_field->length;
}

BT_EXPORT
enum bt_field_array_dynamic_set_length_status bt_field_array_dynamic_set_length(
		struct bt_field *field, uint64_t length)
{
	int ret = BT_FUNC_STATUS_OK;
	struct bt_field_array *array_field = (void *) field;

	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_DYNAMIC_ARRAY("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);

	if (G_UNLIKELY(length > array_field->fields->len)) {
		/* Make more room */
		struct bt_field_class_array *array_fc;
		uint64_t cur_len = array_field->fields->len;
		uint64_t i;

		g_ptr_array_set_size(array_field->fields, length);
		array_fc = (void *) field->class;

		for (i = cur_len; i < array_field->fields->len; i++) {
			struct bt_field *elem_field = bt_field_create(
				array_fc->element_fc);

			if (!elem_field) {
				BT_LIB_LOGE_APPEND_CAUSE(
					"Cannot create element field for "
					"dynamic array field: "
					"index=%" PRIu64 ", "
					"%![array-field-]+f", i, field);
				ret = BT_FUNC_STATUS_MEMORY_ERROR;
				goto end;
			}

			BT_ASSERT_DBG(!array_field->fields->pdata[i]);
			array_field->fields->pdata[i] = elem_field;
		}
	}

	array_field->length = length;

end:
	return ret;
}

static inline
struct bt_field *borrow_array_field_element_field_by_index(
		struct bt_field *field, uint64_t index, const char *api_func)
{
	struct bt_field_array *array_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL_FROM_FUNC(api_func, field);
	BT_ASSERT_PRE_DEV_FIELD_IS_ARRAY_FROM_FUNC(api_func, "field", field,
		"Field");
	BT_ASSERT_PRE_DEV_VALID_INDEX_FROM_FUNC(api_func, index,
		array_field->length);
	return array_field->fields->pdata[index];
}

BT_EXPORT
struct bt_field *bt_field_array_borrow_element_field_by_index(
		struct bt_field *field, uint64_t index)
{
	return borrow_array_field_element_field_by_index(field, index,
		__func__);
}

BT_EXPORT
const struct bt_field *
bt_field_array_borrow_element_field_by_index_const(
		const struct bt_field *field, uint64_t index)
{
	return borrow_array_field_element_field_by_index((void *) field, index,
		__func__);
}

static inline
struct bt_field *borrow_structure_field_member_field_by_index(
		struct bt_field *field, uint64_t index, const char *api_func)
{
	struct bt_field_structure *struct_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL_FROM_FUNC(api_func, field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE_FROM_FUNC(api_func, "field",
		field, "structure-field", BT_FIELD_CLASS_TYPE_STRUCTURE,
		"Field");
	BT_ASSERT_PRE_DEV_VALID_INDEX_FROM_FUNC(api_func, index,
		struct_field->fields->len);
	return struct_field->fields->pdata[index];
}

BT_EXPORT
struct bt_field *bt_field_structure_borrow_member_field_by_index(
		struct bt_field *field, uint64_t index)
{
	return borrow_structure_field_member_field_by_index(field,
		index, __func__);
}

BT_EXPORT
const struct bt_field *
bt_field_structure_borrow_member_field_by_index_const(
		const struct bt_field *field, uint64_t index)
{
	return borrow_structure_field_member_field_by_index(
		(void *) field, index, __func__);
}

static inline
struct bt_field *borrow_structure_field_member_field_by_name(
		struct bt_field *field, const char *name, const char *api_func)
{
	struct bt_field *ret_field = NULL;
	struct bt_field_class_structure *struct_fc;
	struct bt_field_structure *struct_field = (void *) field;
	gpointer orig_key;
	gpointer index;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL_FROM_FUNC(api_func, field);
	BT_ASSERT_PRE_DEV_NON_NULL_FROM_FUNC(api_func, "member-name", name,
		"Member name");
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE_FROM_FUNC(api_func, "field",
		field, "structure-field", BT_FIELD_CLASS_TYPE_STRUCTURE,
		"Field");
	struct_fc = (void *) field->class;

	if (!g_hash_table_lookup_extended(struct_fc->common.name_to_index, name,
			&orig_key, &index)) {
		goto end;
	}

	ret_field = struct_field->fields->pdata[GPOINTER_TO_UINT(index)];
	BT_ASSERT_DBG(ret_field);

end:
	return ret_field;
}

BT_EXPORT
struct bt_field *bt_field_structure_borrow_member_field_by_name(
		struct bt_field *field, const char *name)
{
	return borrow_structure_field_member_field_by_name(field, name,
		__func__);
}

BT_EXPORT
const struct bt_field *bt_field_structure_borrow_member_field_by_name_const(
		const struct bt_field *field, const char *name)
{
	return borrow_structure_field_member_field_by_name(
		(void *) field, name, __func__);
}

BT_EXPORT
void bt_field_option_set_has_field(struct bt_field *field, bt_bool has_field)
{
	struct bt_field_option *opt_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_OPTION("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);

	if (has_field) {
		opt_field->selected_field = opt_field->content_field;
	} else {
		opt_field->selected_field = NULL;
	}
}

BT_EXPORT
struct bt_field *bt_field_option_borrow_field(struct bt_field *field)
{
	struct bt_field_option *opt_field = (void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_OPTION("field", field, "Field");
	return opt_field->selected_field;
}

BT_EXPORT
const struct bt_field *bt_field_option_borrow_field_const(
		const struct bt_field *field)
{
	return (const void *) bt_field_option_borrow_field((void *) field);
}

#define BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_OPT_FIELD(_field)	\
	do {								\
		struct bt_field_variant *_var_field = (void *) field;	\
		BT_ASSERT_PRE_DEV_FIELD_NON_NULL(_field);		\
		BT_ASSERT_PRE_DEV_FIELD_IS_VARIANT("field", (_field),	\
			"Field");					\
		BT_ASSERT_PRE_DEV("has-selected-field",			\
			_var_field->selected_field,			\
			"Variant field has no selected field: %!+f",	\
			field);						\
	} while (0)

static inline
struct bt_field *borrow_variant_field_selected_option_field(
		struct bt_field *field)
{
	struct bt_field_variant *var_field = (void *) field;

	BT_ASSERT_DBG(field);
	return var_field->selected_field;
}

BT_EXPORT
struct bt_field *bt_field_variant_borrow_selected_option_field(
		struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_OPT_FIELD(field);
	return borrow_variant_field_selected_option_field(field);
}

BT_EXPORT
const struct bt_field *bt_field_variant_borrow_selected_option_field_const(
		const struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_OPT_FIELD(field);
	return borrow_variant_field_selected_option_field((void *) field);
}

#define BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_CLASS_OPT(_field)	\
	do {								\
		struct bt_field_variant *_var_field = (void *) field;	\
		BT_ASSERT_PRE_DEV("has-selected-field",			\
			_var_field->selected_field,			\
			"Variant field has no selected field: %!+f",	\
			(_field));					\
	} while (0)

static
const struct bt_field_class_variant_option *
borrow_variant_field_selected_class_option(const struct bt_field *field)
{
	const struct bt_field_class_named_field_class_container *container_fc;
	const struct bt_field_variant *var_field = (const void *) field;

	BT_ASSERT_DBG(field);
	container_fc = (const void *) field->class;
	return container_fc->named_fcs->pdata[var_field->selected_index];
}

BT_EXPORT
const struct bt_field_class_variant_option *
bt_field_variant_borrow_selected_option_class_const(
		const struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_VARIANT("field", field, "Field");
	BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_CLASS_OPT(field);
	return borrow_variant_field_selected_class_option(field);
}

BT_EXPORT
const struct bt_field_class_variant_with_selector_field_integer_unsigned_option *
bt_field_variant_with_selector_field_integer_unsigned_borrow_selected_option_class_const(
		const struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"variant-field-with-unsigned-selector-field",
		BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD,
		"Field");
	BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_CLASS_OPT(field);
	return (const void *) borrow_variant_field_selected_class_option(field);
}

BT_EXPORT
const struct bt_field_class_variant_with_selector_field_integer_signed_option *
bt_field_variant_with_selector_field_integer_signed_borrow_selected_option_class_const(
		const struct bt_field *field)
{
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_HAS_CLASS_TYPE("field", field,
		"variant-field-with-signed-selector-field",
		BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD,
		"Field");
	BT_ASSERT_PRE_DEV_FOR_BORROW_VAR_FIELD_SEL_CLASS_OPT(field);
	return (const void *) borrow_variant_field_selected_class_option(field);
}

BT_EXPORT
enum bt_field_variant_select_option_by_index_status
bt_field_variant_select_option_by_index(
		struct bt_field *field, uint64_t index)
{
	struct bt_field_variant *var_field = (void *) field;

	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_VARIANT("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	BT_ASSERT_PRE_DEV_VALID_INDEX(index, var_field->fields->len);
	var_field->selected_field = var_field->fields->pdata[index];
	var_field->selected_index = index;
	return BT_FUNC_STATUS_OK;
}

BT_EXPORT
uint64_t bt_field_variant_get_selected_option_index(
		const struct bt_field *field)
{
	const struct bt_field_variant *var_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_VARIANT("field", field, "Field");
	BT_ASSERT_PRE_DEV("has-selected-field", var_field->selected_field,
		"Variant field has no selected field: %!+f", field);
	return var_field->selected_index;
}

BT_EXPORT
uint8_t *bt_field_blob_get_data(struct bt_field *field)
{
	const struct bt_field_blob *blob_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_BLOB("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);
	BT_ASSERT_PRE_DEV("blob-field-length-is-set",
		blob_field->length > 0,
		"BLOB field length is not set: %!+f", field);

	/* Assume that the user will fill the bytes. */
	bt_field_set_single(field, true);

	return blob_field->data;
}

BT_EXPORT
const uint8_t *bt_field_blob_get_data_const(const struct bt_field *field)
{
	const struct bt_field_blob *blob_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_BLOB("field", field, "Field");

	return blob_field->data;
}

BT_EXPORT
enum bt_field_blob_dynamic_set_length_status bt_field_blob_dynamic_set_length(
		struct bt_field *field, uint64_t length)
{
	int ret;
	struct bt_field_blob *blob_field = (void *) field;

	BT_ASSERT_PRE_DEV_NO_ERROR();
	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_DYNAMIC_BLOB("field", field, "Field");
	BT_ASSERT_PRE_DEV_FIELD_HOT(field);

	if (G_UNLIKELY(length > blob_field->length)) {
		/* Make more room */
		uint8_t *data = g_realloc(blob_field->data, length);

		if (!data) {
			BT_LIB_LOGE_APPEND_CAUSE(
				"Failed to reallocate BLOB field data: %!+f",
				field);
			ret = BT_FIELD_DYNAMIC_BLOB_SET_LENGTH_STATUS_MEMORY_ERROR;
			goto end;
		}

		blob_field->data = data;
	}

	blob_field->length = length;
	ret = BT_FIELD_DYNAMIC_BLOB_SET_LENGTH_STATUS_OK;

end:
	return ret;
}

BT_EXPORT
uint64_t bt_field_blob_get_length(const struct bt_field *field)
{
	const struct bt_field_blob *blob_field = (const void *) field;

	BT_ASSERT_PRE_DEV_FIELD_NON_NULL(field);
	BT_ASSERT_PRE_DEV_FIELD_IS_BLOB("field", field, "Field");

	return blob_field->length;
}


static inline
void bt_field_finalize(struct bt_field *field)
{
	BT_ASSERT(field);
	BT_LOGD_STR("Putting field's class.");
	BT_OBJECT_PUT_REF_AND_RESET(field->class);
}

static
void destroy_bool_field(struct bt_field *field)
{
	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying boolean field object: %!+f", field);
	bt_field_finalize(field);
	g_free(field);
}

static
void destroy_bit_array_field(struct bt_field *field)
{
	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying bit array field object: %!+f", field);
	bt_field_finalize(field);
	g_free(field);
}

static
void destroy_integer_field(struct bt_field *field)
{
	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying integer field object: %!+f", field);
	bt_field_finalize(field);
	g_free(field);
}

static
void destroy_real_field(struct bt_field *field)
{
	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying real field object: %!+f", field);
	bt_field_finalize(field);
	g_free(field);
}

static
void destroy_structure_field(struct bt_field *field)
{
	struct bt_field_structure *struct_field = (void *) field;

	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying structure field object: %!+f", field);
	bt_field_finalize(field);

	if (struct_field->fields) {
		g_ptr_array_free(struct_field->fields, TRUE);
		struct_field->fields = NULL;
	}

	g_free(field);
}

static
void destroy_option_field(struct bt_field *field)
{
	struct bt_field_option *opt_field = (void *) field;

	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying option field object: %!+f", field);
	bt_field_finalize(field);

	if (opt_field->content_field) {
		bt_field_destroy(opt_field->content_field);
	}

	g_free(field);
}

static
void destroy_variant_field(struct bt_field *field)
{
	struct bt_field_variant *var_field = (void *) field;

	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying variant field object: %!+f", field);
	bt_field_finalize(field);

	if (var_field->fields) {
		g_ptr_array_free(var_field->fields, TRUE);
		var_field->fields = NULL;
	}

	g_free(field);
}

static
void destroy_blob_field(struct bt_field *field)
{
	struct bt_field_blob *blob_field = (void *) field;

	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying BLOB field object: %!+f", field);
	bt_field_finalize(field);

	g_free(blob_field->data);

	g_free(field);
}

static
void destroy_array_field(struct bt_field *field)
{
	struct bt_field_array *array_field = (void *) field;

	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying array field object: %!+f", field);
	bt_field_finalize(field);

	if (array_field->fields) {
		g_ptr_array_free(array_field->fields, TRUE);
		array_field->fields = NULL;
	}

	g_free(field);
}

static
void destroy_string_field(struct bt_field *field)
{
	struct bt_field_string *string_field = (void *) field;

	BT_ASSERT(field);
	BT_LIB_LOGD("Destroying string field object: %!+f", field);
	bt_field_finalize(field);

	if (string_field->buf) {
		g_array_free(string_field->buf, TRUE);
		string_field->buf = NULL;
	}

	g_free(field);
}

void bt_field_destroy(struct bt_field *field)
{
	BT_ASSERT(field);

	switch (field->class->type) {
	case BT_FIELD_CLASS_TYPE_BOOL:
		destroy_bool_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_BIT_ARRAY:
		destroy_bit_array_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER:
	case BT_FIELD_CLASS_TYPE_SIGNED_INTEGER:
	case BT_FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION:
	case BT_FIELD_CLASS_TYPE_SIGNED_ENUMERATION:
		destroy_integer_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL:
	case BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL:
		destroy_real_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_STRING:
		destroy_string_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_STRUCTURE:
		destroy_structure_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_STATIC_ARRAY:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITHOUT_LENGTH_FIELD:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_ARRAY_WITH_LENGTH_FIELD:
		destroy_array_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_OPTION_WITHOUT_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_OPTION_WITH_BOOL_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_OPTION_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_OPTION_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
		destroy_option_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_VARIANT_WITHOUT_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_VARIANT_WITH_UNSIGNED_INTEGER_SELECTOR_FIELD:
	case BT_FIELD_CLASS_TYPE_VARIANT_WITH_SIGNED_INTEGER_SELECTOR_FIELD:
		destroy_variant_field(field);
		break;
	case BT_FIELD_CLASS_TYPE_STATIC_BLOB:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_BLOB_WITHOUT_LENGTH_FIELD:
	case BT_FIELD_CLASS_TYPE_DYNAMIC_BLOB_WITH_LENGTH_FIELD:
		destroy_blob_field(field);
		break;
	default:
		bt_common_abort();
	}
}

static
void reset_single_field(struct bt_field *field)
{
	BT_ASSERT_DBG(field);
	field->is_set = false;
}

static
void reset_structure_field(struct bt_field *field)
{
	uint64_t i;
	struct bt_field_structure *struct_field = (void *) field;

	BT_ASSERT_DBG(field);

	for (i = 0; i < struct_field->fields->len; i++) {
		bt_field_reset(struct_field->fields->pdata[i]);
	}
}

static
void reset_option_field(struct bt_field *field)
{
	struct bt_field_option *opt_field = (void *) field;

	BT_ASSERT_DBG(opt_field);
	bt_field_reset(opt_field->content_field);
	opt_field->selected_field = NULL;
}

static
void reset_variant_field(struct bt_field *field)
{
	uint64_t i;
	struct bt_field_variant *var_field = (void *) field;

	BT_ASSERT_DBG(field);

	for (i = 0; i < var_field->fields->len; i++) {
		bt_field_reset(var_field->fields->pdata[i]);
	}
}

static
void reset_array_field(struct bt_field *field)
{
	uint64_t i;
	struct bt_field_array *array_field = (void *) field;

	BT_ASSERT_DBG(field);

	for (i = 0; i < array_field->fields->len; i++) {
		bt_field_reset(array_field->fields->pdata[i]);
	}
}

static
void set_single_field_is_frozen(struct bt_field *field, bool is_frozen)
{
	field->frozen = is_frozen;
}

static
void set_structure_field_is_frozen(struct bt_field *field, bool is_frozen)
{
	uint64_t i;
	struct bt_field_structure *struct_field = (void *) field;

	BT_LIB_LOGD("Setting structure field's frozen state: "
		"%![field-]+f, is-frozen=%d", field, is_frozen);

	for (i = 0; i < struct_field->fields->len; i++) {
		struct bt_field *member_field = struct_field->fields->pdata[i];

		BT_LIB_LOGD("Setting structure field's member field's "
			"frozen state: %![field-]+f, index=%" PRIu64,
			member_field, i);
		_bt_field_set_is_frozen(member_field, is_frozen);
	}

	set_single_field_is_frozen(field, is_frozen);
}

static
void set_option_field_is_frozen(struct bt_field *field, bool is_frozen)
{
	struct bt_field_option *opt_field = (void *) field;

	BT_LIB_LOGD("Setting option field's frozen state: "
		"%![field-]+f, is-frozen=%d", field, is_frozen);
	_bt_field_set_is_frozen(opt_field->content_field, is_frozen);
	set_single_field_is_frozen(field, is_frozen);
}

static
void set_variant_field_is_frozen(struct bt_field *field, bool is_frozen)
{
	uint64_t i;
	struct bt_field_variant *var_field = (void *) field;

	BT_LIB_LOGD("Setting variant field's frozen state: "
		"%![field-]+f, is-frozen=%d", field, is_frozen);

	for (i = 0; i < var_field->fields->len; i++) {
		struct bt_field *option_field = var_field->fields->pdata[i];

		BT_LIB_LOGD("Setting variant field's option field's "
			"frozen state: %![field-]+f, index=%" PRIu64,
			option_field, i);
		_bt_field_set_is_frozen(option_field, is_frozen);
	}

	set_single_field_is_frozen(field, is_frozen);
}

static
void set_array_field_is_frozen(struct bt_field *field, bool is_frozen)
{
	uint64_t i;
	struct bt_field_array *array_field = (void *) field;

	BT_LIB_LOGD("Setting array field's frozen state: "
		"%![field-]+f, is-frozen=%d", field, is_frozen);

	for (i = 0; i < array_field->fields->len; i++) {
		struct bt_field *elem_field = array_field->fields->pdata[i];

		BT_LIB_LOGD("Setting array field's element field's "
			"frozen state: %![field-]+f, index=%" PRIu64,
			elem_field, i);
		_bt_field_set_is_frozen(elem_field, is_frozen);
	}

	set_single_field_is_frozen(field, is_frozen);
}

void _bt_field_set_is_frozen(const struct bt_field *field,
		bool is_frozen)
{
	BT_ASSERT_DBG(field);
	BT_LIB_LOGD("Setting field object's frozen state: %!+f, is-frozen=%d",
		field, is_frozen);
	BT_ASSERT_DBG(field->methods->set_is_frozen);
	field->methods->set_is_frozen((void *) field, is_frozen);
}

static
bool single_field_is_set(const struct bt_field *field)
{
	BT_ASSERT_DBG(field);
	return field->is_set;
}

static
bool structure_field_is_set(const struct bt_field *field)
{
	bool is_set = true;
	uint64_t i;
	const struct bt_field_structure *struct_field = (const void *) field;

	BT_ASSERT_DBG(field);

	for (i = 0; i < struct_field->fields->len; i++) {
		is_set = bt_field_is_set(struct_field->fields->pdata[i]);
		if (!is_set) {
			goto end;
		}
	}

end:
	return is_set;
}

static
bool option_field_is_set(const struct bt_field *field)
{
	const struct bt_field_option *opt_field = (const void *) field;
	bool is_set = false;

	BT_ASSERT_DBG(field);

	if (opt_field->selected_field) {
		is_set = bt_field_is_set(opt_field->selected_field);
	}

	return is_set;
}

static
bool variant_field_is_set(const struct bt_field *field)
{
	const struct bt_field_variant *var_field = (const void *) field;
	bool is_set = false;

	BT_ASSERT_DBG(field);

	if (var_field->selected_field) {
		is_set = bt_field_is_set(var_field->selected_field);
	}

	return is_set;
}

static
bool array_field_is_set(const struct bt_field *field)
{
	bool is_set = true;
	uint64_t i;
	const struct bt_field_array *array_field = (const void *) field;

	BT_ASSERT_DBG(field);

	for (i = 0; i < array_field->length; i++) {
		is_set = bt_field_is_set(array_field->fields->pdata[i]);
		if (!is_set) {
			goto end;
		}
	}

end:
	return is_set;
}
