diff options
Diffstat (limited to 'epan/print.c')
-rw-r--r-- | epan/print.c | 107 |
1 files changed, 67 insertions, 40 deletions
diff --git a/epan/print.c b/epan/print.c index fad8dc5e41..3a66064e88 100644 --- a/epan/print.c +++ b/epan/print.c @@ -72,6 +72,7 @@ typedef struct { pf_flags filter_flags; gboolean print_hex; gboolean print_text; + proto_node_children_grouper_func node_children_grouper; } write_json_data; typedef struct { @@ -122,11 +123,7 @@ static void write_json_proto_node_filtered(proto_node *node, write_json_data *da static void write_json_proto_node_hex_dump(proto_node *node, write_json_data *data); static void write_json_proto_node_children(proto_node *node, write_json_data *data); static void write_json_proto_node_value(proto_node *node, write_json_data *data); - -typedef GSList* (*proto_node_children_grouper_func)(proto_node *node); static void write_json_proto_node_no_value(proto_node *node, write_json_data *data); -static GSList *proto_node_group_children_by_unique(proto_node *node); - static const char *proto_node_to_json_key(proto_node *node); static void print_pdml_geninfo(epan_dissect_t *edt, FILE *fh); @@ -135,10 +132,6 @@ static void proto_tree_get_node_field_values(proto_node *node, gpointer data); static gboolean json_is_first; -// Function used to group a node's children. Children in the same group are represented in the json output by a single -// json key. If multiple nodes are in a group they are wrapped in a json array in the json output. -static proto_node_children_grouper_func json_proto_node_children_grouper = proto_node_group_children_by_unique; - /* Cache the protocols and field handles that the print functionality needs This helps break explicit dependency on the dissectors. */ static int proto_data = -1; @@ -695,6 +688,7 @@ write_json_proto_tree(output_fields_t* fields, print_dissections_e print_dissections, gboolean print_hex, gchar **protocolfilter, pf_flags protocolfilter_flags, epan_dissect_t *edt, + proto_node_children_grouper_func node_children_grouper, FILE *fh) { char ts[30]; @@ -734,16 +728,9 @@ write_json_proto_tree(output_fields_t* fields, if (print_dissections == print_dissections_none) { data.print_text = FALSE; } + data.node_children_grouper = node_children_grouper; - /* - * Group nodes together by the key they will have in the json output. This is necessary to know which json keys - * have multiple values which need to be put in a json array in the output. A map is not required since we can - * easily retrieve the json key from the first value in the linked list. - */ - GSList *same_key_nodes_list = json_proto_node_children_grouper(edt->tree); - write_json_proto_node_list(same_key_nodes_list, &data); - g_slist_free(same_key_nodes_list); - + write_json_proto_node_children(edt->tree, &data); } else { write_specified_fields(FORMAT_JSON, fields, edt, NULL, fh); } @@ -822,10 +809,14 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data) if (has_children) { if (delimiter_needed) fputs(",\n", data->fh); + // If a node has both a value and a set of children we print the value and the children in separate + // key:value pairs. These can't have the same key so whenever a value is already printed with the node + // json key we print the children with the same key with a "_tree" suffix added. + char *suffix = has_value ? "_tree": ""; + if (is_filtered) { - write_json_proto_node(node_values_list, "", write_json_proto_node_filtered, data); + write_json_proto_node(node_values_list, suffix, write_json_proto_node_filtered, data); } else { - // Remove protocol filter for children, if children should be included. This functionality is enabled // with the "-J" command line option. We save the filter so it can be reenabled when we are done with // the current key:value pair. @@ -835,14 +826,7 @@ write_json_proto_node_list(GSList *proto_node_list_head, write_json_data *data) data->filter = NULL; } - // If a node has both a value and a set of children we print the value and the children in separate - // key:value pairs. These can't have the same key so whenever a value is already printed with the node - // json key we print the children with the same key with a "_tree" suffix added. - if (has_value) { - write_json_proto_node(node_values_list, "_tree", write_json_proto_node_children, data); - } else { - write_json_proto_node(node_values_list, "", write_json_proto_node_children, data); - } + write_json_proto_node(node_values_list, suffix, write_json_proto_node_children, data); // Put protocol filter back if ((data->filter_flags&PF_INCLUDE_CHILDREN) == PF_INCLUDE_CHILDREN) { @@ -913,14 +897,9 @@ write_json_proto_node_value_list(GSList *node_values_head, proto_node_value_writ fputs("[\n", data->fh); data->level++; - // Print first value outside the while loop so we write the delimiter at the start of each loop without having - // to check if we are at the last element. - print_indent(data->level, data->fh); - value_writer((proto_node *) current_value->data, data); - current_value = current_value->next; - while (current_value != NULL) { - fputs(",\n", data->fh); + // Do not print delimiter before first value + if (current_value != node_values_head) fputs(",\n", data->fh); print_indent(data->level, data->fh); value_writer((proto_node *) current_value->data, data); @@ -1017,9 +996,9 @@ write_json_proto_node_hex_dump(proto_node *node, write_json_data *data) static void write_json_proto_node_children(proto_node *node, write_json_data *data) { - GSList *same_key_nodes_list = json_proto_node_children_grouper(node); - write_json_proto_node_list(same_key_nodes_list, data); - g_slist_free(same_key_nodes_list); + GSList *grouped_children_list = data->node_children_grouper(node); + write_json_proto_node_list(grouped_children_list, data); + g_slist_free_full(grouped_children_list, (GDestroyNotify) g_slist_free); } /** @@ -1064,10 +1043,10 @@ write_json_proto_node_no_value(proto_node *node, write_json_data *data) } /** - * Groups each node separately as if it had a unique json key even if it doesn't. Using this function leads to duplicate - * keys in the json output. + * Groups each child of the node separately. + * @return Linked list where each element is another linked list containing a single node. */ -static GSList * +GSList * proto_node_group_children_by_unique(proto_node *node) { GSList *unique_nodes_list = NULL; proto_node *current_child = node->first_child; @@ -1082,6 +1061,54 @@ proto_node_group_children_by_unique(proto_node *node) { } /** + * Groups the children of a node by their json key. Children are put in the same group if they have the same json key. + * @return Linked list where each element is another linked list of nodes associated with the same json key. + */ +GSList * +proto_node_group_children_by_json_key(proto_node *node) +{ + /** + * For each different json key we store a linked list of values corresponding to that json key. These lists are kept + * in both a linked list and a hashmap. The hashmap is used to quickly retrieve the values of a json key. The linked + * list is used to preserve the ordering of keys as they are encountered which is not guaranteed when only using a + * hashmap. + */ + GSList *same_key_nodes_list = NULL; + GHashTable *lookup_by_json_key = g_hash_table_new(g_str_hash, g_str_equal); + proto_node *current_child = node->first_child; + + /** + * For each child of the node get the key and get the list of values already associated with that key from the + * hashmap. If no list exist yet for that key create a new one and add it to both the linked list and hashmap. If a + * list already exists add the node to that list. + */ + while (current_child != NULL) { + char *json_key = (char *) proto_node_to_json_key(current_child); + GSList *json_key_nodes = (GSList *) g_hash_table_lookup(lookup_by_json_key, json_key); + + if (json_key_nodes == NULL) { + json_key_nodes = g_slist_append(json_key_nodes, current_child); + // Prepending in single linked list is O(1), appending is O(n). Better to prepend here and reverse at the + // end than potentially looping to the end of the linked list for each child. + same_key_nodes_list = g_slist_prepend(same_key_nodes_list, json_key_nodes); + g_hash_table_insert(lookup_by_json_key, json_key, json_key_nodes); + } else { + // Store and insert value again to circumvent unused_variable warning. + // Append in this case since most value lists will only have a single value. + json_key_nodes = g_slist_append(json_key_nodes, current_child); + g_hash_table_insert(lookup_by_json_key, json_key, json_key_nodes); + } + + current_child = current_child->next; + } + + // Hash table is not needed anymore since the linked list with the correct ordering is returned. + g_hash_table_destroy(lookup_by_json_key); + + return g_slist_reverse(same_key_nodes_list); +} + +/** * Returns the json key of a node. Tries to use the node's abbreviated name. If the abbreviated name is not available * the representation is used instead. */ |