From: kostasch Date: Thu, 24 Sep 2020 21:05:07 +0300 Subject: [PATCH] pk_mi_json convert functions and corresponding tests. 2020-09-24 Kostas Chasialis * poke/pk-mi-json.h (pk_mi_val_to_json): Prototype. (pk_mi_json_to_val): Prototype. * poke/pk-mi-json.c (pk_mi_val_to_json): Define. (pk_mi_json_to_val): Define. (pk_mi_val_to_json): New function. (pk_mi_val_to_json_1) : Likewise. (pk_mi_int_to_json): Likewise. (pk_mi_uint_to_json): Likewise. (pk_mi_string_to_json): Likewise. (pk_mi_offset_to_json): Likewise. (pk_mi_mapping_to_json): Likewise. (pk_mi_sct_to_json): Likewise. (pk_mi_array_to_json): Likewise. (pk_mi_null_to_json): Likewise. (pk_mi_json_to_val): Likewise. (pk_mi_json_to_val_1): Likewise. (pk_mi_json_to_int): Likewise. (pk_mi_json_to_uint): Likewise. (pk_mi_json_to_string): Likewise. (pk_mi_json_to_offset): Likewise. (pk_mi_json_to_mapping): Likewise. (pk_mi_json_to_sct): Likewise. (pk_mi_json_to_array): Likewise. (pk_mi_json_poke_value_type): Likewise. (pk_mi_json_array_element_pair): Likewise. * testsuite/poke.mi-json/mi-json.c (test_json_to_val_to_json): New function. (test_json_file): Likewise. (test_json_to_val): Likewise. (test_val_to_json): Likewise. (test_json_pk_val): Likewise. (test_json_pk_int): Likewise. (test_json_pk_uint): Likewise. (test_json_pk_string): Likewise. (test_json_pk_offset): Likewise. (test_json_pk_null): Likewise. (test_json_pk_sct): Likewise. (test_json_pk_array): Likewise. (compile_initial_poke_code): Likewise. (compile_poke_expression): Likewise. (read_json_object): Likewise. (parse_json_str_object): Likewise. * testsuite/poke.mi-json/pk_int.json: New test. * testsuite/poke.mi-json/pk_uint.json: New test. * testsuite/poke.mi-json/pk_string.json: New test. * testsuite/poke.mi-json/pk_offset.json: New test. * testsuite/poke.mi-json/pk_array.json: New test. * testsuite/poke.mi-json/pk_sct.json: New test. * testsuite/poke.mi-json/Makefile.am : Added TESTDIR. * testsuite/Makefile.am (EXTRA_DIST): Updated with new tests. --- ChangeLog | 52 ++ poke/pk-mi-json.c | 887 +++++++++++++++++++++++++- poke/pk-mi-json.h | 34 +- testsuite/Makefile.am | 8 +- testsuite/poke.mi-json/Makefile.am | 8 +- testsuite/poke.mi-json/mi-json.c | 604 ++++++++++++++++++ testsuite/poke.mi-json/pk_array.json | 109 ++++ testsuite/poke.mi-json/pk_int.json | 10 + testsuite/poke.mi-json/pk_offset.json | 18 + testsuite/poke.mi-json/pk_sct.json | 51 ++ testsuite/poke.mi-json/pk_string.json | 9 + testsuite/poke.mi-json/pk_uint.json | 10 + 12 files changed, 1792 insertions(+), 8 deletions(-) create mode 100644 testsuite/poke.mi-json/pk_array.json create mode 100644 testsuite/poke.mi-json/pk_int.json create mode 100644 testsuite/poke.mi-json/pk_offset.json create mode 100644 testsuite/poke.mi-json/pk_sct.json create mode 100644 testsuite/poke.mi-json/pk_string.json create mode 100644 testsuite/poke.mi-json/pk_uint.json diff --git a/ChangeLog b/ChangeLog index 84790756..6527b1ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,55 @@ +2020-09-24 Kostas Chasialis + + * poke/pk-mi-json.h (pk_mi_val_to_json): Prototype. + (pk_mi_json_to_val): Prototype. + * poke/pk-mi-json.c (pk_mi_val_to_json): Define. + (pk_mi_json_to_val): Define. + (pk_mi_val_to_json): New function. + (pk_mi_val_to_json_1) : Likewise. + (pk_mi_int_to_json): Likewise. + (pk_mi_uint_to_json): Likewise. + (pk_mi_string_to_json): Likewise. + (pk_mi_offset_to_json): Likewise. + (pk_mi_mapping_to_json): Likewise. + (pk_mi_sct_to_json): Likewise. + (pk_mi_array_to_json): Likewise. + (pk_mi_null_to_json): Likewise. + (pk_mi_json_to_val): Likewise. + (pk_mi_json_to_val_1): Likewise. + (pk_mi_json_to_int): Likewise. + (pk_mi_json_to_uint): Likewise. + (pk_mi_json_to_string): Likewise. + (pk_mi_json_to_offset): Likewise. + (pk_mi_json_to_mapping): Likewise. + (pk_mi_json_to_sct): Likewise. + (pk_mi_json_to_array): Likewise. + (pk_mi_json_poke_value_type): Likewise. + (pk_mi_json_array_element_pair): Likewise. + * testsuite/poke.mi-json/mi-json.c (test_json_to_val_to_json): New function. + (test_json_file): Likewise. + (test_json_to_val): Likewise. + (test_val_to_json): Likewise. + (test_json_pk_val): Likewise. + (test_json_pk_int): Likewise. + (test_json_pk_uint): Likewise. + (test_json_pk_string): Likewise. + (test_json_pk_offset): Likewise. + (test_json_pk_null): Likewise. + (test_json_pk_sct): Likewise. + (test_json_pk_array): Likewise. + (compile_initial_poke_code): Likewise. + (compile_poke_expression): Likewise. + (read_json_object): Likewise. + (parse_json_str_object): Likewise. + * testsuite/poke.mi-json/pk_int.json: New test. + * testsuite/poke.mi-json/pk_uint.json: New test. + * testsuite/poke.mi-json/pk_string.json: New test. + * testsuite/poke.mi-json/pk_offset.json: New test. + * testsuite/poke.mi-json/pk_array.json: New test. + * testsuite/poke.mi-json/pk_sct.json: New test. + * testsuite/poke.mi-json/Makefile.am : Added TESTDIR. + * testsuite/Makefile.am (EXTRA_DIST): Updated with new tests. + 2020-09-15 Aurélien Aptel * etc/poke-mode.el: Add SMIE grammar and indent code. diff --git a/poke/pk-mi-json.c b/poke/pk-mi-json.c index 3ec81924..d92444fd 100644 --- a/poke/pk-mi-json.c +++ b/poke/pk-mi-json.c @@ -26,6 +26,20 @@ #include "pk-mi.h" #include "pk-mi-json.h" #include "pk-mi-msg.h" +#include "libpoke.h" + +#define PK_MI_CHECK(errmsg, A, M, ...) \ + do \ + { \ + if (!(A)) \ + { \ + if (errmsg == NULL) \ + goto error; \ + assert (asprintf (errmsg, "[ERROR] " M , ##__VA_ARGS__) != -1); \ + goto error; \ + } \ + } \ + while (0) /* Message:: { @@ -421,7 +435,7 @@ pk_mi_msg pk_mi_json_to_msg (const char *str) { pk_mi_msg msg = NULL; - struct json_tokener *tokener; + json_tokener *tokener; json_object *json; tokener = json_tokener_new (); @@ -432,8 +446,875 @@ pk_mi_json_to_msg (const char *str) json_tokener_free (tokener); if (json) - msg = pk_mi_json_object_to_msg (json); + { + msg = pk_mi_json_object_to_msg (json); + assert (json_object_put (json) == 1); + } - /* XXX: free the json object */ return msg; } + +/* Functions to convert pk_val to JSON Poke Value. */ +static json_object *pk_mi_val_to_json_1 (pk_val val, char **errmsg); + +static json_object * +pk_mi_int_to_json (pk_val pk_int, char **errmsg) +{ + json_object *int_object, *type_object, *value_object, *size_object; + const char *type; + int size; + + assert (pk_type_code (pk_typeof (pk_int)) == PK_INT); + + int_object = json_object_new_object (); + PK_MI_CHECK (errmsg, int_object != NULL, "json_object_new_object () failed"); + + value_object = json_object_new_int64 (pk_int_value (pk_int)); + PK_MI_CHECK (errmsg, value_object != NULL, + "json_object_new_object () failed"); + size = pk_int_size (pk_int); + type = "Integer"; + + size_object = json_object_new_int (size); + type_object = json_object_new_string (type); + PK_MI_CHECK (errmsg, size_object != NULL, "json_object_new_object () failed"); + PK_MI_CHECK (errmsg, type_object != NULL, "json_object_new_object () failed"); + + /* OK, fill the properties of our object. */ + json_object_object_add (int_object, "type", type_object); + json_object_object_add (int_object, "value", value_object); + json_object_object_add (int_object, "size", size_object); + + return int_object; + + error: + return NULL; +} + +static json_object * +pk_mi_uint_to_json (pk_val pk_uint, char **errmsg) +{ + json_object *uint_object, *type_object, *value_object, *size_object; + const char *type; + int size; + + assert (pk_type_code (pk_typeof (pk_uint)) == PK_UINT); + + uint_object = json_object_new_object (); + PK_MI_CHECK (errmsg, uint_object != NULL, "json_object_new_object () failed"); + + /* Older versions of libjson-c (0.3.1 & under) do not support + json_object_new_uint64. + + In order to support previous versions, we store unsigned integers + as signed integers despite them being too large to represent as + signed. (i.e. they are stored as negative). */ + value_object = json_object_new_int64 ((int64_t) pk_uint_value (pk_uint)); + PK_MI_CHECK (errmsg, value_object != NULL, + "json_object_new_object () failed"); + size = pk_uint_size (pk_uint); + type = "UnsignedInteger"; + + size_object = json_object_new_int (size); + type_object = json_object_new_string (type); + PK_MI_CHECK (errmsg, size_object != NULL, "json_object_new_object () failed"); + PK_MI_CHECK (errmsg, type_object != NULL, "json_object_new_object () failed"); + + /* OK, fill the properties of our object. */ + json_object_object_add (uint_object, "type", type_object); + json_object_object_add (uint_object, "value", value_object); + json_object_object_add (uint_object, "size", size_object); + + return uint_object; + + error: + return NULL; +} + +static json_object * +pk_mi_string_to_json (pk_val pk_str, char **errmsg) +{ + json_object *string_object, *string_type_object, *string_value_object; + + assert (pk_type_code (pk_typeof (pk_str)) == PK_STRING); + + string_object = json_object_new_object (); + PK_MI_CHECK (errmsg, string_object != NULL, + "json_object_new_object () failed"); + + string_type_object = json_object_new_string ("String"); + PK_MI_CHECK (errmsg, string_type_object != NULL, + "json_object_new_object () failed"); + + string_value_object = json_object_new_string (pk_string_str (pk_str)); + PK_MI_CHECK (errmsg, string_value_object != NULL, + "json_object_new_object () failed"); + + /* OK, fill the properties of our object. */ + json_object_object_add (string_object, "type", string_type_object); + json_object_object_add (string_object, "value", string_value_object); + + return string_object; + + error: + return NULL; +} + +static json_object * +pk_mi_offset_to_json (pk_val pk_offset, char **errmsg) +{ + json_object *offset_object, *offset_type_object; + json_object *magnitude_object; + json_object *unit_object, *unit_type_object, *unit_size_object; + json_object *unit_value_object; + pk_val off_unit; + + assert (pk_type_code (pk_typeof (pk_offset)) == PK_OFFSET); + + offset_type_object = json_object_new_string ("Offset"); + PK_MI_CHECK (errmsg, offset_type_object != NULL, + "json_object_new_object () failed"); + + magnitude_object = pk_mi_int_to_json (pk_offset_magnitude (pk_offset), errmsg); + + unit_type_object = json_object_new_string ("UnsignedInteger"); + PK_MI_CHECK (errmsg, unit_type_object != NULL, + "json_object_new_object () failed"); + + unit_size_object = json_object_new_int (64); + PK_MI_CHECK (errmsg, unit_size_object != NULL, + "json_object_new_object () failed"); + + off_unit = pk_offset_unit (pk_offset); + unit_value_object = json_object_new_int64 ((int64_t) pk_uint_value (off_unit)); + PK_MI_CHECK (errmsg, unit_value_object != NULL, + "json_object_new_object () failed"); + + unit_object = json_object_new_object (); + PK_MI_CHECK (errmsg, unit_object != NULL, + "json_object_new_object () failed"); + + json_object_object_add (unit_object, "type", unit_type_object); + json_object_object_add (unit_object, "value", unit_value_object); + json_object_object_add (unit_object, "size", unit_size_object); + + offset_object = json_object_new_object (); + PK_MI_CHECK (errmsg, offset_object != NULL, "json_object_new_object () failed"); + + /* Built sub-objects, add them to our offset_object. */ + json_object_object_add (offset_object, "type", offset_type_object); + json_object_object_add (offset_object, "magnitude", magnitude_object); + json_object_object_add (offset_object, "unit", unit_object); + + return offset_object; + + error: + return NULL; +} + +static json_object * +pk_mi_mapping_to_json (pk_val val, char **errmsg) +{ + json_object *mapping_object, *ios_object, *offset_object; + + offset_object = pk_mi_offset_to_json (pk_val_offset (val), errmsg); + + ios_object = json_object_new_int64 (pk_int_value (pk_val_ios (val))); + PK_MI_CHECK (errmsg, ios_object != NULL, "json_object_new_object () failed"); + + mapping_object = json_object_new_object (); + PK_MI_CHECK (errmsg, mapping_object != NULL, "json_object_new_object () failed"); + + json_object_object_add (mapping_object, "IOS", ios_object); + json_object_object_add (mapping_object, "offset", offset_object); + + return mapping_object; + + error: + return NULL; +} + +static json_object * +pk_mi_null_to_json (char **errmsg) +{ + json_object *pk_null_object; + + pk_null_object = json_object_new_object (); + PK_MI_CHECK (errmsg, pk_null_object != NULL, + "json_object_new_object () failed"); + + json_object_object_add (pk_null_object, "type", + json_object_new_string ("Null")); + json_object_object_add (pk_null_object, "value", NULL); + + return pk_null_object; + + error: + return NULL; +} + +static json_object * +pk_mi_sct_to_json (pk_val pk_sct, char **errmsg) +{ + json_object *pk_sct_object, *pk_sct_type_object; + json_object *pk_sct_fields_object, *pk_sct_field_object; + json_object *pk_sct_mapping_object, *pk_sct_name_object; + json_object *pk_sct_field_value_object; + json_object *pk_sct_field_offset_object; + json_object *pk_sct_field_name_object; + pk_val tmp; + int err; + + assert (pk_type_code (pk_typeof (pk_sct)) == PK_STRUCT); + + pk_sct_type_object = json_object_new_string ("Struct"); + PK_MI_CHECK (errmsg, pk_sct_type_object != NULL, + "json_object_new_object () failed"); + + pk_sct_fields_object = json_object_new_array (); + PK_MI_CHECK (errmsg, pk_sct_fields_object != NULL, + "json_object_new_object () failed"); + + /* Get the name of the struct type and convert it to JSON object. */ + tmp = pk_struct_type (pk_sct); + pk_sct_name_object = pk_mi_string_to_json (pk_struct_type_name (tmp), + errmsg); + PK_MI_CHECK (errmsg, pk_sct_name_object != NULL, + "json_object_new_object () failed"); + + /* Fill the array of struct fields. */ + for (ssize_t i = 0 ; i < pk_uint_value (pk_struct_nfields (pk_sct)) ; i++) + { + tmp = pk_struct_field_value (pk_sct, i); + pk_sct_field_value_object = pk_mi_val_to_json_1 (tmp, errmsg); + tmp = pk_struct_field_boffset (pk_sct, i); + pk_sct_field_offset_object = pk_mi_uint_to_json (tmp, errmsg); + tmp = pk_struct_field_name (pk_sct, i); + pk_sct_field_name_object = pk_mi_string_to_json (tmp, errmsg); + + if (pk_sct_field_value_object == NULL + || pk_sct_field_offset_object == NULL + || pk_sct_field_name_object == NULL) + goto error; + + pk_sct_field_object = json_object_new_object (); + + json_object_object_add (pk_sct_field_object, "name", + pk_sct_field_name_object); + + json_object_object_add (pk_sct_field_object, "value", + pk_sct_field_value_object); + + json_object_object_add (pk_sct_field_object, "boffset", + pk_sct_field_offset_object); + + err = json_object_array_add (pk_sct_fields_object, + pk_sct_field_object); + PK_MI_CHECK (errmsg, err != -1, + "failed to add name object to struct field"); + } + + /* Optionally, add a mapping. */ + pk_sct_mapping_object = pk_val_mapped_p (pk_sct) ? + pk_mi_mapping_to_json (pk_sct, errmsg) : + pk_mi_null_to_json (errmsg); + + pk_sct_object = json_object_new_object (); + PK_MI_CHECK (errmsg, pk_sct_object != NULL, + "json_object_new_object () failed"); + + json_object_object_add (pk_sct_object, "type", pk_sct_type_object); + json_object_object_add (pk_sct_object, "name", pk_sct_name_object); + json_object_object_add (pk_sct_object, "fields", pk_sct_fields_object); + json_object_object_add (pk_sct_object, "mapping", pk_sct_mapping_object); + + return pk_sct_object; + + error: + return NULL; +} + +static json_object * +pk_mi_array_to_json (pk_val pk_array, char **errmsg) +{ + json_object *pk_array_object, *pk_array_type_object; + json_object *pk_array_mapping_object, *pk_array_elements_object; + json_object *pk_array_element_object, *pk_array_element_value_object; + json_object *pk_array_element_offset_object; + pk_val tmp; + int err; + + assert (pk_type_code (pk_typeof (pk_array)) == PK_ARRAY); + + pk_array_object = json_object_new_object (); + PK_MI_CHECK (errmsg, pk_array_object != NULL, + "json_object_new_object () failed"); + + const char *type = "Array"; + pk_array_type_object = json_object_new_string (type); + PK_MI_CHECK (errmsg, pk_array_type_object != NULL, + "json_object_new_object () failed"); + + /* Initialize our array. */ + pk_array_elements_object = json_object_new_array (); + PK_MI_CHECK (errmsg, pk_array_elements_object != NULL, + "json_object_new_object () failed"); + + /* Fill elements object. */ + for (size_t i = 0 ; i < pk_uint_value (pk_array_nelem (pk_array)) ; i++) + { + /* For every element on the array, get its value & offset and build + the corresponding JSON objects. */ + tmp = pk_array_elem_val (pk_array, i); + pk_array_element_value_object = pk_mi_val_to_json_1 (tmp, errmsg); + tmp = pk_array_elem_boffset (pk_array, i); + pk_array_element_offset_object = pk_mi_uint_to_json (tmp, errmsg); + + pk_array_element_object = json_object_new_object (); + + json_object_object_add (pk_array_element_object, "value", + pk_array_element_value_object); + + json_object_object_add (pk_array_element_object, "boffset", + pk_array_element_offset_object); + + err = json_object_array_add (pk_array_elements_object, + pk_array_element_object); + PK_MI_CHECK (errmsg, err != -1, "failed to add element to array"); + } + + /* Optionally, add a mapping. */ + pk_array_mapping_object = pk_val_mapped_p (pk_array) ? + pk_mi_mapping_to_json (pk_array, errmsg) : + pk_mi_null_to_json (errmsg); + + if (pk_array_mapping_object == NULL) + goto error; + + /* Everything is built on this point. + Fill the properties of the array object. */ + json_object_object_add (pk_array_object, "type", pk_array_type_object); + json_object_object_add (pk_array_object, "elements", pk_array_elements_object); + json_object_object_add (pk_array_object, "mapping", pk_array_mapping_object); + + return pk_array_object; + + error: + return NULL; +} + +static json_object * +pk_mi_val_to_json_1 (pk_val val, char **errmsg) +{ + json_object *pk_val_object = NULL; + + if (val == PK_NULL) { + pk_val_object = pk_mi_null_to_json (errmsg); + } + else { + switch (pk_type_code (pk_typeof (val))) + { + case PK_INT: + pk_val_object = pk_mi_int_to_json (val, errmsg); + break; + case PK_UINT: + pk_val_object = pk_mi_uint_to_json (val, errmsg); + break; + case PK_STRING: + pk_val_object = pk_mi_string_to_json (val, errmsg); + break; + case PK_OFFSET: + pk_val_object = pk_mi_offset_to_json (val, errmsg); + break; + case PK_STRUCT: + pk_val_object = pk_mi_sct_to_json (val, errmsg); + break; + case PK_ARRAY: + pk_val_object = pk_mi_array_to_json (val, errmsg); + break; + case PK_CLOSURE: + case PK_ANY: + default: + assert (0); + } + if (!pk_val_object) + return NULL; + } + + return pk_val_object; +} + +const char * +pk_mi_val_to_json (pk_val val, char **errmsg) +{ + json_object *pk_val_object, *pk_object; + + pk_val_object = pk_mi_val_to_json_1 (val, errmsg); + + pk_object = json_object_new_object (); + PK_MI_CHECK (errmsg, pk_object != NULL, + "failed to create new json_object"); + + json_object_object_add (pk_object, "PokeValue", pk_val_object); + + return pk_object != NULL ? + json_object_to_json_string_ext (pk_object, JSON_C_TO_STRING_PRETTY) + : NULL; + + error: + return NULL; +} + +/* Functions to convert JSON object to Poke value. */ +static int pk_mi_json_to_val_1 (pk_val *poke_value, json_object *obj, + char **errmsg); + +static const char * +pk_mi_json_poke_value_type (json_object *obj) +{ + json_object *search_object; + + /* Get the Poke value type. */ + if (json_object_object_get_ex (obj, "type", &search_object) == 0) + return NULL; + + return json_object_to_json_string (search_object); +} + +static int +pk_mi_json_to_int (pk_val *pk_int, json_object *int_obj, char **errmsg) +{ + json_object *value_object, *size_object; + int64_t value; + int size, err; + + err = json_object_object_get_ex (int_obj, "value", &value_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"value\"", + pk_mi_json_poke_value_type (int_obj)); + value = json_object_get_int64 (value_object); + + err = json_object_object_get_ex (int_obj, "size", &size_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"size\"", + pk_mi_json_poke_value_type (int_obj)); + size = json_object_get_int (size_object); + + *pk_int = pk_make_int (value, size); + PK_MI_CHECK (errmsg, *pk_int != PK_NULL, "pk_make_int failed"); + + return 0; + + error: + return -1; +} + +static int +pk_mi_json_to_uint (pk_val *pk_uint, json_object *uint_obj, + char **errmsg) +{ + json_object *value_object, *size_object; + uint64_t value; + int size, err; + + err = json_object_object_get_ex (uint_obj, "value", &value_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"value\"", + pk_mi_json_poke_value_type (uint_obj)); + + /* Older versions of libjson-c (0.3.1 & under) do not support + json_object_get_uint64 (only json_object_get_int64). + + In order to support previous versions, we store unsigned integers + as signed integers despite them being too large to represent as + signed. (i.e. they are stored as negative). + + However, users expect to get the unsigned integer they stored. + + Thus, we have to convert it back to uint64_t. */ + value = (uint64_t) json_object_get_int64 (value_object); + + err = json_object_object_get_ex (uint_obj, "size", &size_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"size\"", + pk_mi_json_poke_value_type (uint_obj)); + size = json_object_get_int (size_object); + + *pk_uint = pk_make_uint (value, size); + PK_MI_CHECK (errmsg, *pk_uint != PK_NULL, "pk_make_uint failed"); + + return 0; + + error: + return -1; +} + +static int +pk_mi_json_to_string (pk_val *pk_string, json_object *str_obj, + char **errmsg) +{ + json_object *value_object; + const char *value_str; + int err; + + err = json_object_object_get_ex (str_obj, "value", &value_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"value\"", + pk_mi_json_poke_value_type (str_obj)); + value_str = json_object_get_string (value_object); + + *pk_string = pk_make_string (value_str); + PK_MI_CHECK (errmsg, *pk_string != PK_NULL, "pk_make_string failed"); + + return 0; + + error: + return -1; +} + +static int +pk_mi_json_to_offset (pk_val *pk_offset, json_object *offset_obj, + char **errmsg) +{ + /* To build a pk_offset, we need its magnitude and its unit. */ + pk_val magnitude, unit; + json_object *magnitude_object, *unit_object; + int err; + + err = json_object_object_get_ex (offset_obj, "magnitude", &magnitude_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"magnitude\"", + pk_mi_json_poke_value_type (offset_obj)); + + if (pk_mi_json_to_val_1 (&magnitude, magnitude_object, errmsg) == -1) + goto error; + + err = json_object_object_get_ex (offset_obj, "unit", &unit_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"unit\"", + pk_mi_json_poke_value_type (offset_obj)); + + assert (json_object_get_type (unit_object) == json_type_object); + + err = pk_mi_json_to_uint (&unit, unit_object, errmsg); + PK_MI_CHECK (errmsg, err != -1, + "unable to convert offset unit to pk_uint"); + assert (pk_uint_size (unit) == 64); + + *pk_offset = pk_make_offset (magnitude, unit); + PK_MI_CHECK(errmsg, *pk_offset != PK_NULL, "pk_make_offset failed"); + + return 0; + + error: + return -1; +} + +static int +pk_mi_json_to_mapping (pk_val *poke_value, json_object *obj, + char **errmsg) +{ + /* TODO: write me. */ + return 0; +} + +static int +pk_mi_json_to_sct (pk_val *pk_sct, json_object *sct_obj, + char **errmsg) +{ + /* To build a pk_struct, we need its fields, its name and its mapping. */ + json_object *array_object, *fields_object, *search_object; + pk_val mapping, sct_type, sct_field_name, sct_field_value, sct_field_boffset; + pk_val sct, *fnames, *ftypes, nfields, name; + size_t fields_len; + int err; + + err = json_object_object_get_ex (sct_obj, "fields", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does does not contain key \"fields\"", + pk_mi_json_poke_value_type (sct_obj)); + + fields_object = search_object; + assert (json_object_get_type (fields_object) == json_type_array); + + fields_len = json_object_array_length (fields_object); + + if (fields_len > 0) + { + err = json_object_object_get_ex (sct_obj, "name", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does does not contain key \"name\"", + pk_mi_json_poke_value_type (sct_obj)); + nfields = pk_make_uint (fields_len, 64); + + /* Here, we don't use PK_MI_CHECK because + it would reallocate & refill errmsg. + + TODO (kostas): check if there is a bug anywhere in the code + if asprintf () gets called more than once. */ + if (pk_mi_json_to_val_1 (&name, search_object, errmsg) == -1) + goto error; + + pk_allocate_struct_attrs (nfields, &fnames, &ftypes); + + sct_type = pk_make_struct_type (nfields, name, fnames, ftypes); + sct = pk_make_struct (nfields, sct_type); + for (size_t i = 0 ; i < fields_len ; i++) + { + array_object = json_object_array_get_idx (fields_object, i); + + err = json_object_object_get_ex (array_object, "name", + &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"name\"", + pk_mi_json_poke_value_type (array_object)); + + if (pk_mi_json_to_val_1 (&sct_field_name, search_object, errmsg) == -1) + goto error; + + err = json_object_object_get_ex (array_object, "value", + &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"value\"", + pk_mi_json_poke_value_type (array_object)); + + if (pk_mi_json_to_val_1 (&sct_field_value, search_object, errmsg) == -1) + goto error; + + err = json_object_object_get_ex (array_object, "boffset", + &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"boffset\"", + pk_mi_json_poke_value_type (array_object)); + + if (pk_mi_json_to_val_1 (&sct_field_boffset, search_object, errmsg) == -1) + goto error; + + assert (pk_type_code (pk_typeof (sct_field_name)) == PK_STRING); + assert (pk_type_code (pk_typeof (sct_field_boffset)) == PK_UINT); + + pk_struct_type_set_fname (sct_type, i, sct_field_name); + pk_struct_type_set_ftype (sct_type, i, pk_typeof (sct_field_value)); + pk_struct_set_field_boffset (sct, i, sct_field_boffset); + pk_struct_set_field_name (sct, i, sct_field_name); + pk_struct_set_field_value (sct, i, sct_field_value); + } + + err = json_object_object_get_ex (sct_obj, "mapping", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"mapping\"", + pk_mi_json_poke_value_type (sct_obj)); + PK_MI_CHECK (errmsg, + pk_mi_json_to_mapping (&mapping, search_object, errmsg) != -1, + "failed to create mapping for struct"); + } + else + { + sct = PK_NULL; + } + + *pk_sct = sct; + + return 0; + + error: + return -1; +} + +static pk_val * +pk_mi_json_array_element_pair (json_object *elements_object, size_t idx, + char **errmsg) +{ + json_object *element_object, *search_object; + int err; + + /* value-offset pair. */ + pk_val *pair = (pk_val *) malloc (sizeof (pk_val) * 2); + + element_object = json_object_array_get_idx (elements_object, idx); + + err = json_object_object_get_ex (element_object, "value", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"value\"", + pk_mi_json_poke_value_type (element_object)); + if (pk_mi_json_to_val_1 (&(pair[0]), search_object, errmsg) == -1) + goto error; + + err = json_object_object_get_ex (element_object, "boffset", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"boffset\"", + pk_mi_json_poke_value_type (element_object)); + + if (pk_mi_json_to_val_1 (&(pair[1]), search_object, errmsg) == -1) + goto error; + + return pair; + + error: + return NULL; +} + +static int +pk_mi_json_to_array (pk_val *pk_array, json_object *array_obj, + char **errmsg) +{ + /* To build a pk_array, we need its elements and its mapping. */ + json_object *elements_object, *search_object; + pk_val array_type, mapping, array_etype, array, *element_pair; + size_t elements_len; + int err; + + err = json_object_object_get_ex (array_obj, "elements", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"elements\"", + pk_mi_json_poke_value_type (array_obj)); + + elements_object = search_object; + assert (json_object_get_type (elements_object) == json_type_array); + + elements_len = json_object_array_length (elements_object); + + if (elements_len > 0) + { + /* We need to get the type of array elements first. */ + element_pair = pk_mi_json_array_element_pair (elements_object, 0, errmsg); + if (element_pair == NULL) + goto error; + + array_etype = pk_typeof (element_pair[0]); + array_type = pk_make_array_type (array_etype, PK_NULL); + array = pk_make_array (pk_make_uint (elements_len, 64), array_type); + + assert (pk_type_code (pk_typeof (element_pair[1])) == PK_UINT); + + pk_array_set_elem_val (array, 0, element_pair[0]); + pk_array_set_elem_boffset (array, 0, element_pair[1]); + + free (element_pair); + + for (size_t i = 1 ; i < elements_len ; i++) + { + element_pair = pk_mi_json_array_element_pair (elements_object, i, + errmsg); + if (element_pair == NULL) + goto error; + + assert (pk_type_code (pk_typeof (element_pair[0])) + == pk_type_code (array_etype)); + assert (pk_type_code (pk_typeof (element_pair[1])) == PK_UINT); + + pk_array_set_elem_val (array, i, element_pair[0]); + pk_array_set_elem_boffset (array, i, element_pair[1]); + + free (element_pair); + } + + err = json_object_object_get_ex (array_obj, "mapping", &search_object); + PK_MI_CHECK (errmsg, err != 0, + "json type %s does not contain key \"mapping\"", + pk_mi_json_poke_value_type (array_obj)); + + PK_MI_CHECK(errmsg, + pk_mi_json_to_mapping (&mapping, search_object, errmsg) != -1, + "failed to create mapping for struct"); + } + else + { + array = PK_NULL; + } + + *pk_array = array; + + return 0; + + error: + return -1; +} + +static int +pk_mi_json_to_val_1 (pk_val *poke_value, json_object *obj, + char **errmsg) +{ + const char *poke_object_type; + + poke_object_type = pk_mi_json_poke_value_type (obj); + if (poke_object_type == NULL) + goto error; + + if (!strncmp (poke_object_type, "\"Integer\"", strlen ("\"Integer\""))) + { + if (pk_mi_json_to_int (poke_value, obj, errmsg) == -1) + goto error; + } + else if (!strncmp (poke_object_type, "\"UnsignedInteger\"", + strlen ("\"UnsignedInteger\""))) + { + if (pk_mi_json_to_uint (poke_value, obj, errmsg) == -1) + goto error; + } + else if (!strncmp (poke_object_type, "\"String\"", strlen ("\"String\""))) + { + if (pk_mi_json_to_string (poke_value, obj, errmsg) == -1) + goto error; + } + else if (!strncmp (poke_object_type, "\"Offset\"", strlen ("\"Offset\""))) + { + if (pk_mi_json_to_offset (poke_value, obj, errmsg) == -1) + goto error; + } + else if (!strncmp (poke_object_type, "\"Array\"", strlen ("Array"))) + { + if (pk_mi_json_to_array (poke_value, obj, errmsg) == -1) + goto error; + } + else if (!strncmp (poke_object_type, "\"Struct\"", strlen ("\"Struct\""))) + { + if (pk_mi_json_to_sct (poke_value, obj, errmsg) == -1) + goto error; + } + else if (!strncmp (poke_object_type, "\"Null\"", strlen ("\"Null\""))) + *poke_value = PK_NULL; + else + { + asprintf (errmsg, "An unexpected error happened, this is a bug."); + return -1; + } + + return 0; + + error: + return -1; +} + +int +pk_mi_json_to_val (pk_val *value, const char *json_str, char **errmsg) +{ + json_object *search_object, *poke_object = NULL; + json_tokener *tok = json_tokener_new (); + enum json_tokener_error jerr; + int err; + + /* Parse the current object and get its PK_TYPE. */ + do + { + poke_object = json_tokener_parse_ex (tok, json_str, + strlen (json_str)); + } + while ((jerr = json_tokener_get_error (tok)) == json_tokener_continue); + + PK_MI_CHECK(errmsg, jerr == json_tokener_success, "%s", + json_tokener_error_desc (jerr)); + + err = json_object_object_get_ex (poke_object, "PokeValue", &search_object); + PK_MI_CHECK(errmsg, err != 0, "Not a valid PokeValue object"); + + if (pk_mi_json_to_val_1 (value, search_object, errmsg) == -1) + goto error; + + json_tokener_free (tok); + + return 0; + + error: + return -1; +} diff --git a/poke/pk-mi-json.h b/poke/pk-mi-json.h index 57898c41..74bad1aa 100644 --- a/poke/pk-mi-json.h +++ b/poke/pk-mi-json.h @@ -21,7 +21,10 @@ #include +#include + #include "pk-mi-msg.h" +#include "libpoke.h" /* Given a string containing a JSON message, parse it and return a MI message. @@ -37,7 +40,34 @@ pk_mi_msg pk_mi_json_to_msg (const char *str); const char *pk_mi_msg_to_json (pk_mi_msg msg); -/* XXX services to pk_val <-> json */ -/* XXX services to pk_type <-> json */ +/* Given a json object, create the Poke value associated with it. + + VALUE is the Poke value to be created. + + STR is the string value of a pk_val to be converted. + + ERRMSG is a buffer to be allocated that contains any error messages. + + Its caller's responsibility to deallocate the buffer (ERRMSG). + + If ERRMSG is NULL, nothing happens on the buffer. + + In case of error returns -1 and sets errmsg appropriately. */ + +int pk_mi_json_to_val (pk_val *value, const char *str, char **errmsg); + +/* Given a pk val, return the json object associated with this val + + VAL is the pk_val to be converted. + + ERRMSG is a buffer to be allocated that contains any error messages. + + Its caller's responsibility to deallocate the buffer (ERRMSG). + + If ERRMSG is NULL, nothing happens on the buffer. + + In case of error return NULL. */ + +const char *pk_mi_val_to_json (pk_val val, char **errmsg); #endif /* ! PK_MI_JSON */ diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am index 9213a08c..5ba9900a 100644 --- a/testsuite/Makefile.am +++ b/testsuite/Makefile.am @@ -1588,4 +1588,10 @@ EXTRA_DIST = \ poke.libpoke/pk_nequal_str.test \ poke.libpoke/pk_nequal_off.test \ poke.libpoke/pk_nequal_sct.test \ - poke.libpoke/pk_nequal_arr.test + poke.libpoke/pk_nequal_arr.test \ + poke.mi-json/pk_int.json \ + poke.mi-json/pk_uint.json \ + poke.mi-json/pk_string.json \ + poke.mi-json/pk_offset.json \ + poke.mi-json/pk_array.json \ + poke.mi-json/pk_sct.json diff --git a/testsuite/poke.mi-json/Makefile.am b/testsuite/poke.mi-json/Makefile.am index 86e32d8d..3c70b4cb 100644 --- a/testsuite/poke.mi-json/Makefile.am +++ b/testsuite/poke.mi-json/Makefile.am @@ -27,9 +27,13 @@ mi_json_SOURCES = mi-json.c \ $(top_srcdir)/poke/pk-mi-json.c mi_json_CPPFLAGS = -I$(top_builddir)/gl -I$(top_srcdir)/gl \ - -I$(top_srcdir)/poke -I$(top_builddir)/poke + -I$(top_srcdir)/poke -I$(top_builddir)/poke \ + -DTESTDIR=\"$(abs_srcdir)\" \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -I$(top_srcdir)/libpoke -I$(top_builddir)/libpoke mi_json_CFLAGS = $(JSON_C_CFLAGS) -mi_json_LDADD = $(JSON_C_LIBS) $(top_builddir)/gl/libgnu.la +mi_json_LDADD = $(JSON_C_LIBS) $(top_builddir)/gl/libgnu.la \ + $(top_builddir)/libpoke/libpoke.la endif diff --git a/testsuite/poke.mi-json/mi-json.c b/testsuite/poke.mi-json/mi-json.c index 4b7aea69..8e23aa95 100644 --- a/testsuite/poke.mi-json/mi-json.c +++ b/testsuite/poke.mi-json/mi-json.c @@ -18,11 +18,20 @@ #include +#include #include +#include #include +#include +#include #include "pk-mi-msg.h" #include "pk-mi-json.h" +#include "libpoke.h" +#include "../poke.libpoke/term-if.h" + +#define PASS 1 +#define FAIL 0 void test_json_to_msg () @@ -37,6 +46,600 @@ test_json_to_msg () fail ("json_to_msg_1"); } +int +parse_json_str_object (const char *json_str, json_object **pk_obj) +{ + struct json_tokener *tok = json_tokener_new (); + enum json_tokener_error jerr; + + do + { + *pk_obj = json_tokener_parse_ex (tok, json_str, + strlen (json_str)); + } + while ((jerr = json_tokener_get_error (tok)) == json_tokener_continue); + + json_tokener_free (tok); + + return jerr == json_tokener_success ? PASS : FAIL; +} + +/* The following functions check if a JSON representation and a pk_val match. + + In order to test pk_mi_json_to_val and pk_mi_val_to_json we do the following: + * Read the .json file which contains a pk_val and its JSON representation. + * Call pk_mi_json_to_val to get the pk_val to be tested. + * Call pk_mi_val_to_json to get the JSON representation to be tested. + * Based on the type of the object, we call the following functions twice. + * The first call is used to check if the pk_val returned from + pk_mi_json_to_val matches with the JSON representation of the .json file. + * The second call is used to check if the JSON representation returned from + pk_mi_val_to_json matches with the pk_val of the .json file. */ + + +int test_json_pk_val (json_object *obj, pk_val val); + +int +test_json_pk_int (json_object *pk_int_obj, pk_val pk_int) +{ + json_object *current; + const char *typename; + + /* Poke integers properties are : "type", "value" and "size". */ + if (json_object_object_length (pk_int_obj) != 3) + return FAIL; + + if (!json_object_object_get_ex (pk_int_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "Integer", strlen ("Integer"))) + return FAIL; + + if (!json_object_object_get_ex (pk_int_obj, "value", ¤t)) + return FAIL; + + if (json_object_get_int64 (current) != pk_int_value (pk_int)) + return FAIL; + + if (!json_object_object_get_ex (pk_int_obj, "size", ¤t)) + return FAIL; + + if (json_object_get_int (current) != pk_int_size (pk_int)) + return FAIL; + return PASS; +} + +int +test_json_pk_uint (json_object *pk_uint_obj, pk_val pk_uint) +{ + json_object *current; + const char *typename; + + /* Poke unsigned integers properties are : "type", "value" and "size". */ + if (json_object_object_length (pk_uint_obj) != 3) + return FAIL; + + if (!json_object_object_get_ex (pk_uint_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "UnsignedInteger", + strlen ("UnsignedInteger"))) + return FAIL; + + if (!json_object_object_get_ex (pk_uint_obj, "value", ¤t)) + return FAIL; + + /* NOTE: This version of libjson-c does not support *_get_uint64 so we use + the int64 version and always cast to the unsigned type. */ + if ((uint64_t) json_object_get_int64 (current) != pk_uint_value (pk_uint)) + return FAIL; + + if (!json_object_object_get_ex (pk_uint_obj, "size", ¤t)) + return FAIL; + + if (json_object_get_int (current) != pk_uint_size (pk_uint)) + return FAIL; + + return PASS; +} + +int +test_json_pk_string (json_object *pk_string_obj, pk_val pk_string) +{ + json_object *current; + const char *typename, *json_str_value, *pk_string_value; + + /* Poke string properties are : "type" and "value". */ + if (json_object_object_length (pk_string_obj) != 2) + return FAIL; + + if (!json_object_object_get_ex (pk_string_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "String", strlen ("String"))) + return FAIL; + + if (!json_object_object_get_ex (pk_string_obj, "value", ¤t)) + return FAIL; + + json_str_value = json_object_get_string (current); + pk_string_value = pk_string_str (pk_string); + if (strncmp (json_str_value, pk_string_value, strlen (pk_string_value))) + return FAIL; + + return PASS; +} + +int +test_json_pk_offset (json_object *pk_offset_obj, pk_val pk_offset) +{ + json_object *current; + pk_val pk_magnitude, pk_unit; + const char *typename; + int signed_p; + + /* Poke offset properties are : "type", "magnitude" and "unit". */ + if (json_object_object_length (pk_offset_obj) != 3) + return FAIL; + + if (!json_object_object_get_ex (pk_offset_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "Offset", strlen ("Offset"))) + return FAIL; + + if (!json_object_object_get_ex (pk_offset_obj, "magnitude", ¤t)) + return FAIL; + + /* "magnitude" is either an UnsignedInteger or an Integer. + Check if its UnsignedInteger or Integer. */ + pk_magnitude = pk_offset_magnitude (pk_offset); + signed_p = pk_int_value (pk_integral_type_signed_p (pk_typeof (pk_magnitude))); + if (signed_p && test_json_pk_int (current, pk_magnitude) == FAIL) + return FAIL; + + if (!signed_p && test_json_pk_uint (current, pk_magnitude) == FAIL) + return FAIL; + + /* "unit" is an UnsignedInteger. */ + if (!json_object_object_get_ex (pk_offset_obj, "unit", ¤t)) + return FAIL; + + pk_unit = pk_offset_unit (pk_offset); + if (test_json_pk_uint (current, pk_unit) == FAIL) + return FAIL; + + return PASS; +} + +int +test_json_pk_null (json_object *pk_null_obj, pk_val pk_null) +{ + json_object *current; + const char *typename; + + /* Poke null properties are : "type" and "value". */ + if (json_object_object_length (pk_null_obj) != 2) + return FAIL; + + if (!json_object_object_get_ex (pk_null_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "Null", strlen ("Null"))) + return FAIL; + + if (!json_object_object_get_ex (pk_null_obj, "value", ¤t)) + return FAIL; + + if (current != NULL) + return FAIL; + + return PASS; +} + +int +test_json_pk_sct (json_object *pk_sct_obj, pk_val pk_sct) +{ + json_object *current, *pk_sct_fields_obj, *pk_sct_field_obj; + pk_val pk_sct_name, pk_sct_fname, pk_sct_fboffset, pk_sct_fvalue; + int signed_p; + const char *typename; + + /* Poke struct properties are : "type", "name", "fields" and "mapping". */ + if (json_object_object_length (pk_sct_obj) != 4) + return FAIL; + + if (!json_object_object_get_ex (pk_sct_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "Struct", strlen ("Struct"))) + return FAIL; + + if (!json_object_object_get_ex (pk_sct_obj, "name", ¤t)) + return FAIL; + + pk_sct_name = pk_struct_type_name (pk_struct_type (pk_sct)); + + if (test_json_pk_string (current, pk_sct_name) == FAIL) + return FAIL; + + /* Get the fields of a struct and check them. */ + if (!json_object_object_get_ex (pk_sct_obj, "fields", &pk_sct_fields_obj)) + return FAIL; + + for (size_t i = 0 ; i < pk_uint_value (pk_struct_nfields (pk_sct)) ; i++) + pk_sct_fboffset = pk_struct_field_boffset (pk_sct, i); + + for (size_t i = 0 ; i < pk_uint_value (pk_struct_nfields (pk_sct)) ; i++) + { + pk_sct_fname = pk_struct_field_name (pk_sct, i); + pk_sct_fboffset = pk_struct_field_boffset (pk_sct, i); + pk_sct_fvalue = pk_struct_field_value (pk_sct, i); + + pk_sct_field_obj = json_object_array_get_idx (pk_sct_fields_obj, i); + + if (!json_object_object_get_ex (pk_sct_field_obj, "name", ¤t)) + return FAIL; + + if (test_json_pk_string (current, pk_sct_fname) == FAIL) + return FAIL; + + if (!json_object_object_get_ex (pk_sct_field_obj, "boffset", ¤t)) + return FAIL; + + if (test_json_pk_uint (current, pk_sct_fboffset) == FAIL) + return FAIL; + + if (!json_object_object_get_ex (pk_sct_field_obj, "value", ¤t)) + return FAIL; + + if (test_json_pk_val (current, pk_sct_fvalue) == FAIL) + return FAIL; + } + + /* TODO: add test for mapping when its added on pk-mi-json.c. */ + return PASS; +} + +int +test_json_pk_array (json_object *pk_array_obj, pk_val pk_array) +{ + json_object *current, *pk_array_elems_obj, *pk_array_elem_obj; + pk_val pk_array_elem_value, pk_array_elem_boff; + const char *typename; + + /* Poke array properties are : "type", "elements" and "mapping". */ + if (json_object_object_length (pk_array_obj) != 3) + return FAIL; + + if (!json_object_object_get_ex (pk_array_obj, "type", ¤t)) + return FAIL; + + typename = json_object_get_string (current); + if (strncmp (typename, "Array", strlen ("Array"))) + return FAIL; + + if (!json_object_object_get_ex (pk_array_obj, "elements", &pk_array_elems_obj)) + return FAIL; + + /* Access every element of the array and check it. */ + for (size_t i = 0 ; i < pk_uint_value (pk_array_nelem (pk_array)) ; i++) + { + pk_array_elem_value = pk_array_elem_val (pk_array, i); + pk_array_elem_boff = pk_array_elem_boffset (pk_array, i); + + pk_array_elem_obj = json_object_array_get_idx (pk_array_elems_obj, i); + + if (!json_object_object_get_ex (pk_array_elem_obj, "boffset", ¤t)) + return FAIL; + + if (test_json_pk_uint (current, pk_array_elem_boff) == FAIL) + return FAIL; + + if (!json_object_object_get_ex (pk_array_elem_obj, "value", ¤t)) + return FAIL; + + if (test_json_pk_val (current, pk_array_elem_value) == FAIL) + return FAIL; + } + + /* TODO: add test for mapping when its added on pk-mi-json.c. */ + return PASS; +} + +int +test_json_pk_val (json_object *obj, pk_val val) +{ + if (val == PK_NULL) { + return test_json_pk_null (obj, val); + } + + switch (pk_type_code (pk_typeof (val))) + { + case PK_INT: + return test_json_pk_int (obj, val); + case PK_UINT: + return test_json_pk_uint (obj, val); + case PK_STRING: + return test_json_pk_string (obj, val); + case PK_OFFSET: + return test_json_pk_offset (obj, val); + case PK_STRUCT: + return test_json_pk_sct (obj, val); + case PK_ARRAY: + return test_json_pk_array (obj, val); + case PK_CLOSURE: + case PK_ANY: + default: + return FAIL; + } +} + +int +compile_initial_poke_code (FILE *ifp, pk_compiler pkc) +{ + ssize_t nread, s_nread = 0; + char *line = NULL, *poke_code = NULL; + size_t len = 0; + int error = 1; + + while (1) + { + nread = getline (&line, &len, ifp); + /* That should not happen on a correct file. + We should prolly check ferror (ifp), postpone it for now. */ + if (nread == -1) + return 0; + + /* If we reached the next section of the file, break. */ + if (nread == 3 && line[0] == '#' && line[1] == '#') + break; + + line[nread - 1] = '\0'; + s_nread += nread - 1; + + if (poke_code == NULL) + { + poke_code = (char *) malloc (nread); + memcpy (poke_code, line, nread); + } + else + { + poke_code = (char *) realloc (poke_code, s_nread + 1); + strncat (poke_code, line, nread + 1); + } + } + + if (poke_code) + { + error = pk_compile_buffer (pkc, poke_code, NULL); + free (poke_code); + } + + free (line); + return error; +} + +/* Returns a C array containing the Poke values that were compiled. + Returns NULL on error. */ +int +compile_poke_expression (FILE *ifp, pk_compiler pkc, pk_val *val) +{ + ssize_t nread; + char *line = NULL; + size_t len = 0; + + while (1) + { + nread = getline (&line, &len, ifp); + if (nread == -1) + return 0; + + if (nread == 3 && line[0] == '#' && line[1] == '#') + break; + + line[nread - 1] = '\0'; + + if (pk_compile_expression (pkc, (const char *) line, NULL, val) == 0) + goto error; + } + + free (line); + return 1; + + error: + free (line); + return 0; +} + +const char * +read_json_object (FILE *ifp) +{ + ssize_t nread, s_read = 0; + size_t len = 0; + char *line = NULL, *json_str = NULL; + size_t cap = 1024; + + /* Optimistic allocation, to avoid multiple reallocations. */ + json_str = (char *) malloc (cap); + if (json_str == NULL) + return NULL; + json_str[0] = '\0'; + + while ((nread = getline (&line, &len, ifp)) != -1) + { + s_read += nread; + + if (s_read >= cap) + { + cap *= 2; + json_str = (char *) realloc (json_str, cap); + } + + strncat (json_str, line, nread); + } + + return json_str; +} + +int +test_json_to_val (const char *pk_obj_str, pk_val val) +{ + pk_val pk_test_val; + json_object *pk_obj, *current; + char *errmsg; + + if (pk_mi_json_to_val (&pk_test_val, pk_obj_str, &errmsg) == -1) + return FAIL; + + /* Check if the pk_val returned from pk_mi_json_to_val + is the same as the pk_val that we read from the test file. */ + + if (!pk_val_equal_p (pk_test_val, val)) + return FAIL; + + if (parse_json_str_object (pk_obj_str, &pk_obj) == FAIL) + return FAIL; + + if (json_object_object_get_ex (pk_obj, "PokeValue", ¤t) == 0) + return FAIL; + + if (test_json_pk_val (current, pk_test_val) == FAIL) + return FAIL; + + if (json_object_put (pk_obj) != 1) + return FAIL; + + return PASS; +} + +int +test_val_to_json (const char *pk_obj_str, pk_val val) +{ + const char *pk_test_obj_str; + json_object *pk_test_obj, *current; + + if ((pk_test_obj_str = pk_mi_val_to_json (val, NULL)) == NULL) + return FAIL; + + if (parse_json_str_object (pk_test_obj_str, &pk_test_obj) == FAIL) + return FAIL; + + if (json_object_object_get_ex (pk_test_obj, "PokeValue", ¤t) == 0) + return FAIL; + + if (test_json_pk_val (current, val) == FAIL) + return FAIL; + + if (json_object_put (pk_test_obj) != 1) + return FAIL; + + return PASS; +} + +void +test_json_file (const char *filename, FILE *ifp) +{ + char *poke_datadir, *line = NULL; + const char *json_obj_str; + ssize_t nread; + size_t len = 0; + pk_compiler pkc; + pk_val val; + + poke_datadir = getenv ("POKEDATADIR"); + if (poke_datadir == NULL) + poke_datadir = PKGDATADIR; + + pkc = pk_compiler_new (poke_datadir, &poke_term_if); + + if (pkc == NULL) + goto error; + + if (compile_initial_poke_code (ifp, pkc) == 0) + goto error; + + if (compile_poke_expression (ifp, pkc, &val) == 0) + goto error; + + /* Right now, pk_arrays' boffsets are not correctly calculated unless + the array is mapped, thats why we can omit this test for now. */ + if (pk_type_code (pk_typeof (val)) == PK_ARRAY) + { + xfail (filename); + return; + } + + if ((json_obj_str = read_json_object (ifp)) == NULL) + goto error; + + if (test_json_to_val (json_obj_str, val) == FAIL) + goto error; + + if (test_val_to_json (json_obj_str, val) == FAIL) + goto error; + + pass (filename); + return; + + error: + fail (filename); +} + +void +test_json_to_val_to_json () +{ + FILE *ifp; + DIR *directory; + struct dirent *dir; + const char *extension; + char *testdir, *testfile; + size_t testdir_len; + + testdir_len = strlen (TESTDIR); + testdir = (char *) malloc (testdir_len + 2); + memcpy (testdir, TESTDIR, testdir_len + 1); + strncat (testdir, "/", 1); + testdir_len = strlen (testdir); + + directory = opendir (testdir); + if (directory) + { + while ((dir = readdir (directory)) != NULL) + { + /* If this file a .json file, proccess it. */ + extension = strrchr (dir->d_name, '.'); + if (extension) + { + if (!strncmp (extension + 1, "json", 4)) + { + testfile = (char *) malloc (testdir_len + + strlen (dir->d_name) + 1); + memcpy (testfile, testdir, testdir_len + 1); + strncat (testfile, dir->d_name, strlen (dir->d_name)); + + ifp = fopen (testfile, "r"); + if (ifp) + { + test_json_file (dir->d_name, ifp); + fclose (ifp); + } + free (testfile); + } + } + } + closedir (directory); + } + + free (testdir); +} + void pk_fatal (const char *msg) { @@ -49,6 +652,7 @@ int main (int argc, char *argv[]) { test_json_to_msg (); + test_json_to_val_to_json (); totals (); return 0; } diff --git a/testsuite/poke.mi-json/pk_array.json b/testsuite/poke.mi-json/pk_array.json new file mode 100644 index 00000000..3ed5d833 --- /dev/null +++ b/testsuite/poke.mi-json/pk_array.json @@ -0,0 +1,109 @@ +## +[1L, 2L, 3L, 15L, 25L, -35L, 64L, 9223372036854775807L] as int<64>[] +## +{ + "PokeValue" : { + "type" : "Array", + "elements" : [ + { + "value" : { + "type" : "Integer", + "value" : 1, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 0, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : 2, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 64, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : 3, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 128, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : 15, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 192, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : 25, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 256, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : -35, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 320, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : 64, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 384, + "size" : 64 + } + }, + { + "value" : { + "type" : "Integer", + "value" : 9223372036854775807, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 448, + "size" : 64 + } + } + ], + "mapping" : { + "type" : "Null", "value" : null + } + } +} \ No newline at end of file diff --git a/testsuite/poke.mi-json/pk_int.json b/testsuite/poke.mi-json/pk_int.json new file mode 100644 index 00000000..0b9bff0c --- /dev/null +++ b/testsuite/poke.mi-json/pk_int.json @@ -0,0 +1,10 @@ +## +-3500 as int<32> +## +{ + "PokeValue" : { + "type" : "Integer", + "value" : -3500, + "size" : 32 + } +} \ No newline at end of file diff --git a/testsuite/poke.mi-json/pk_offset.json b/testsuite/poke.mi-json/pk_offset.json new file mode 100644 index 00000000..2c9e3c5a --- /dev/null +++ b/testsuite/poke.mi-json/pk_offset.json @@ -0,0 +1,18 @@ +## +23#B +## +{ + "PokeValue" : { + "type" : "Offset", + "magnitude" : { + "type" : "Integer", + "value" : 23, + "size" : 32 + }, + "unit" : { + "type" : "UnsignedInteger", + "value" : 8, + "size" : 64 + } + } +} \ No newline at end of file diff --git a/testsuite/poke.mi-json/pk_sct.json b/testsuite/poke.mi-json/pk_sct.json new file mode 100644 index 00000000..9a928f52 --- /dev/null +++ b/testsuite/poke.mi-json/pk_sct.json @@ -0,0 +1,51 @@ +deftype Packet = struct { int i; long j; }; +## +Packet { i = 1, j = 2} +## +{ + "PokeValue" : { + "type" : "Struct", + "name" : { + "type" : "String", + "value" : "Packet" + }, + "fields" : [ + { + "name" : { + "type" : "String", + "value" : "i" + }, + "value" : { + "type" : "Integer", + "value" : 1, + "size" : 32 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 0, + "size" : 64 + } + }, + { + "name" : { + "type" : "String", + "value" : "j" + }, + "value" : { + "type" : "Integer", + "value" : 2, + "size" : 64 + }, + "boffset" : { + "type" : "UnsignedInteger", + "value" : 32, + "size" : 64 + } + } + ], + "mapping" : { + "type" : "Null", + "value" : null + } + } +} \ No newline at end of file diff --git a/testsuite/poke.mi-json/pk_string.json b/testsuite/poke.mi-json/pk_string.json new file mode 100644 index 00000000..045eb1b3 --- /dev/null +++ b/testsuite/poke.mi-json/pk_string.json @@ -0,0 +1,9 @@ +## +"foo" +## +{ + "PokeValue" : { + "type" : "String", + "value" : "foo" + } +} \ No newline at end of file diff --git a/testsuite/poke.mi-json/pk_uint.json b/testsuite/poke.mi-json/pk_uint.json new file mode 100644 index 00000000..2cd83dcf --- /dev/null +++ b/testsuite/poke.mi-json/pk_uint.json @@ -0,0 +1,10 @@ +## +3500 as uint<32> +## +{ + "PokeValue" : { + "type" : "UnsignedInteger", + "value" : 3500, + "size" : 32 + } +} \ No newline at end of file -- 2.17.1