Add a simple json encoder and use it to print fio output in json format

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/json.c b/json.c
new file mode 100644
index 0000000..707ea14
--- /dev/null
+++ b/json.c
@@ -0,0 +1,336 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdarg.h>
+#include "json.h"
+#include "log.h"
+
+struct json_object *json_create_object(void)
+{
+	struct json_object *obj = malloc(sizeof(struct json_object));
+	if (obj)
+		memset(obj, 0, sizeof(struct json_object));
+	return obj;
+}
+
+struct json_array *json_create_array(void)
+{
+	struct json_array *array = malloc(sizeof(struct json_array));
+	if (array)
+		memset(array, 0, sizeof(struct json_array));
+	return array;
+}
+
+static struct json_pair *json_create_pair(const char *name, struct json_value *value)
+{
+	struct json_pair *pair = malloc(sizeof(struct json_pair));
+	if (pair) {
+		pair->name = strdup(name);
+		pair->value = value;
+
+		value->parent_type = JSON_PARENT_TYPE_PAIR;
+		value->parent_pair = pair;
+	}
+	return pair;
+}
+
+static struct json_value *json_create_value_int(long number)
+{
+	struct json_value *value = malloc(sizeof(struct json_value));
+
+	if (value) {
+		value->type = JSON_TYPE_INTEGER;
+		value->integer_number = number;
+	}
+	return value;
+}
+
+static struct json_value *json_create_value_float(float number)
+{
+	struct json_value *value = malloc(sizeof(struct json_value));
+
+	if (value) {
+		value->type = JSON_TYPE_FLOAT;
+		value->float_number = number;
+	}
+	return value;
+}
+
+static struct json_value *json_create_value_string(const char *str)
+{
+	struct json_value *value = malloc(sizeof(struct json_value));
+
+	if (value) {
+		value->type = JSON_TYPE_STRING;
+		value->string = strdup(str);
+		if (!value->string) {
+			free(value);
+			value = NULL;
+		}
+	}
+	return value;
+}
+
+static struct json_value *json_create_value_object(struct json_object *obj)
+{
+	struct json_value *value = malloc(sizeof(struct json_value));
+
+	if (value) {
+		value->type = JSON_TYPE_OBJECT;
+		value->object = obj;
+		obj->parent = value;
+	}
+	return value;
+}
+
+static struct json_value *json_create_value_array(struct json_array *array)
+{
+	struct json_value *value = malloc(sizeof(struct json_value));
+
+	if (value) {
+		value->type = JSON_TYPE_ARRAY;
+		value->array = array;
+		array->parent = value;
+	}
+	return value;
+}
+
+static void json_free_array(struct json_array *array);
+static void json_free_pair(struct json_pair *pair);
+static void json_free_value(struct json_value *value);
+void json_free_object(struct json_object *obj)
+{
+	int i;
+
+	for (i = 0; i < obj->pair_cnt; i++)
+		json_free_pair(obj->pairs[i]);
+	free(obj->pairs);
+	free(obj);
+}
+
+static void json_free_array(struct json_array *array)
+{
+	int i;
+
+	for (i = 0; i < array->value_cnt; i++)
+		json_free_value(array->values[i]);
+	free(array->values);
+	free(array);
+}
+
+static void json_free_pair(struct json_pair *pair)
+{
+	json_free_value(pair->value);
+	free(pair->name);
+	free(pair);
+}
+
+static void json_free_value(struct json_value *value)
+{
+	switch (value->type) {
+	case JSON_TYPE_STRING:
+		free(value->string);
+		break;
+	case JSON_TYPE_OBJECT:
+		json_free_object(value->object);
+		break;
+	case JSON_TYPE_ARRAY:
+		json_free_array(value->array);
+		break;
+	}
+	free(value);
+}
+
+static int json_array_add_value(struct json_array *array, struct json_value *value)
+{
+	struct json_value **values = realloc(array->values,
+		sizeof(struct json_value *) * (array->value_cnt + 1));
+
+	if (!values)
+		return ENOMEM;
+	values[array->value_cnt] = value;
+	array->value_cnt++;
+	array->values = values;
+
+	value->parent_type = JSON_PARENT_TYPE_ARRAY;
+	value->parent_array = array;
+	return 0;
+}
+
+static int json_object_add_pair(struct json_object *obj, struct json_pair *pair)
+{
+	struct json_pair **pairs = realloc(obj->pairs,
+		sizeof(struct json_pair *) * (obj->pair_cnt + 1));
+	if (!pairs)
+		return ENOMEM;
+	pairs[obj->pair_cnt] = pair;
+	obj->pair_cnt++;
+	obj->pairs = pairs;
+
+	pair->parent = obj;
+	return 0;
+}
+
+int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...)
+{
+	struct json_value *value;
+	struct json_pair *pair;
+	va_list args;
+	int ret;
+
+	va_start(args, type);
+	if (type == JSON_TYPE_STRING)
+		value = json_create_value_string(va_arg(args, char *));
+	else if (type == JSON_TYPE_INTEGER)
+		value = json_create_value_int(va_arg(args, long));
+	else if (type == JSON_TYPE_FLOAT)
+		value = json_create_value_float(va_arg(args, double));
+	else if (type == JSON_TYPE_OBJECT)
+		value = json_create_value_object(va_arg(args, struct json_object *));
+	else
+		value = json_create_value_array(va_arg(args, struct json_array *));
+	va_end(args);
+
+	if (!value)
+		return ENOMEM;
+
+	pair = json_create_pair(name, value);
+	if (!pair) {
+		json_free_value(value);
+		return ENOMEM;
+	}
+	ret = json_object_add_pair(obj, pair);
+	if (ret) {
+		json_free_pair(pair);
+		return ENOMEM;
+	}
+	return 0;
+}
+
+static void json_print_array(struct json_array *array);
+int json_array_add_value_type(struct json_array *array, int type, ...)
+{
+	struct json_value *value;
+	va_list args;
+	int ret;
+
+	va_start(args, type);
+	if (type == JSON_TYPE_STRING)
+		value = json_create_value_string(va_arg(args, char *));
+	else if (type == JSON_TYPE_INTEGER)
+		value = json_create_value_int(va_arg(args, long));
+	else if (type == JSON_TYPE_FLOAT)
+		value = json_create_value_float(va_arg(args, double));
+	else if (type == JSON_TYPE_OBJECT)
+		value = json_create_value_object(va_arg(args, struct json_object *));
+	else
+		value = json_create_value_array(va_arg(args, struct json_array *));
+	va_end(args);
+
+	if (!value)
+		return ENOMEM;
+
+	ret = json_array_add_value(array, value);
+	if (ret) {
+		json_free_value(value);
+		return ENOMEM;
+	}
+	return 0;
+}
+
+static int json_value_level(struct json_value *value);
+static int json_pair_level(struct json_pair *pair);
+static int json_array_level(struct json_array *array);
+static int json_object_level(struct json_object *object)
+{
+	if (object->parent == NULL)
+		return 0;
+	return json_value_level(object->parent);
+}
+
+static int json_pair_level(struct json_pair *pair)
+{
+	return json_object_level(pair->parent) + 1;
+}
+
+static int json_array_level(struct json_array *array)
+{
+	return json_value_level(array->parent);
+}
+
+static int json_value_level(struct json_value *value)
+{
+	if (value->parent_type == JSON_PARENT_TYPE_PAIR)
+		return json_pair_level(value->parent_pair);
+	else
+		return json_array_level(value->parent_array) + 1;
+}
+
+static void json_print_level(int level)
+{
+	while (level-- > 0)
+		log_info("  ");
+}
+
+static void json_print_pair(struct json_pair *pair);
+static void json_print_array(struct json_array *array);
+static void json_print_value(struct json_value *value);
+void json_print_object(struct json_object *obj)
+{
+	int i;
+
+	log_info("{\n");
+	for (i = 0; i < obj->pair_cnt; i++) {
+		if (i > 0)
+			log_info(",\n");
+		json_print_pair(obj->pairs[i]);
+	}
+	log_info("\n");
+	json_print_level(json_object_level(obj));
+	log_info("}");
+}
+
+static void json_print_pair(struct json_pair *pair)
+{
+	json_print_level(json_pair_level(pair));
+	log_info("\"%s\" : ", pair->name);
+	json_print_value(pair->value);
+}
+
+static void json_print_array(struct json_array *array)
+{
+	int i;
+
+	log_info("[\n");
+	for (i = 0; i < array->value_cnt; i++) {
+		if (i > 0)
+			log_info(",\n");
+		json_print_level(json_value_level(array->values[i]));
+		json_print_value(array->values[i]);
+	}
+	log_info("\n");
+	json_print_level(json_array_level(array));
+	log_info("]");
+}
+
+static void json_print_value(struct json_value *value)
+{
+	switch (value->type) {
+	case JSON_TYPE_STRING:
+		log_info("\"%s\"", value->string);
+		break;
+	case JSON_TYPE_INTEGER:
+		log_info("%ld", value->integer_number);
+		break;
+	case JSON_TYPE_FLOAT:
+		log_info("%.2f", value->float_number);
+		break;
+	case JSON_TYPE_OBJECT:
+		json_print_object(value->object);
+		break;
+	case JSON_TYPE_ARRAY:
+		json_print_array(value->array);
+		break;
+	}
+}