diff --git a/debian/libwsutil0.symbols b/debian/libwsutil0.symbols index 253bd1ff71..2700b2316f 100644 --- a/debian/libwsutil0.symbols +++ b/debian/libwsutil0.symbols @@ -287,6 +287,15 @@ libwsutil.so.0 libwsutil0 #MINVER# wmem_map_size@Base 3.5.0 wmem_map_steal@Base 3.5.0 wmem_memdup@Base 3.5.0 + wmem_multimap_count@Base 3.7.0 + wmem_multimap_get_keys@Base 3.7.0 + wmem_multimap_insert32@Base 3.7.0 + wmem_multimap_lookup32@Base 3.7.0 + wmem_multimap_lookup32_le@Base 3.7.0 + wmem_multimap_new@Base 3.7.0 + wmem_multimap_new_autoreset@Base 3.7.0 + wmem_multimap_remove32@Base 3.7.0 + wmem_multimap_size@Base 3.7.0 wmem_realloc@Base 3.5.0 wmem_register_callback@Base 3.5.0 wmem_stack_peek@Base 3.5.0 diff --git a/doc/README.wmem b/doc/README.wmem index 3e1e95aaef..40ad021e6b 100644 --- a/doc/README.wmem +++ b/doc/README.wmem @@ -113,6 +113,10 @@ wmem_list.h wmem_map.h - A hash map (AKA hash table) implementation. +wmem_multimap.h + - A hash multimap (map that can store multiple values with the same key) + implementation. + wmem_queue.h - A queue implementation (first-in, first-out). diff --git a/epan/dissectors/asn1/ansi_map/packet-ansi_map-template.c b/epan/dissectors/asn1/ansi_map/packet-ansi_map-template.c index 1e2dbd7e9e..5306cfc022 100644 --- a/epan/dissectors/asn1/ansi_map/packet-ansi_map-template.c +++ b/epan/dissectors/asn1/ansi_map/packet-ansi_map-template.c @@ -377,7 +377,7 @@ static void dissect_ansi_map_win_trigger_list(tvbuff_t *tvb, packet_info *pinfo /* Transaction table */ -static wmem_map_t *TransactionId_table=NULL; +static wmem_multimap_t *TransactionId_table=NULL; /* Store Invoke information needed for the corresponding reply */ static void @@ -408,17 +408,14 @@ update_saved_invokedata(packet_info *pinfo, struct ansi_tcap_private_t *p_privat buf = wmem_strdup_printf(pinfo->pool, "%s%s%s",p_private_tcap->TransactionID_str,src_str,dst_str); break; } - /* If the entry allready exists don't owervrite it */ - ansi_map_saved_invokedata = (struct ansi_map_invokedata_t *)wmem_map_lookup(TransactionId_table,buf); - if(ansi_map_saved_invokedata) - return; ansi_map_saved_invokedata = wmem_new(wmem_file_scope(), struct ansi_map_invokedata_t); ansi_map_saved_invokedata->opcode = p_private_tcap->d.OperationCode_private; ansi_map_saved_invokedata->ServiceIndicator = ServiceIndicator; - wmem_map_insert(TransactionId_table, + wmem_multimap_insert32(TransactionId_table, wmem_strdup(wmem_file_scope(), buf), + pinfo->num, ansi_map_saved_invokedata); /*ws_warning("Invoke Hash string %s pkt: %u",buf,pinfo->num);*/ @@ -4330,7 +4327,7 @@ find_saved_invokedata(asn1_ctx_t *actx, struct ansi_tcap_private_t *p_private_tc } /*ws_warning("Find Hash string %s pkt: %u",buf,actx->pinfo->num);*/ - ansi_map_saved_invokedata = (struct ansi_map_invokedata_t *)wmem_map_lookup(TransactionId_table, buf); + ansi_map_saved_invokedata = (struct ansi_map_invokedata_t *)wmem_multimap_lookup32_le(TransactionId_table, buf, actx->pinfo->num); if(ansi_map_saved_invokedata){ OperationCode = ansi_map_saved_invokedata->opcode & 0xff; ServiceIndicator = ansi_map_saved_invokedata->ServiceIndicator; @@ -5480,7 +5477,7 @@ void proto_register_ansi_map(void) { "Type of matching invoke/response, risk of mismatch if loose matching chosen", &ansi_map_response_matching_type, ansi_map_response_matching_type_values, FALSE); - TransactionId_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); + TransactionId_table = wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); register_stat_tap_table_ui(&stat_table); } diff --git a/epan/dissectors/asn1/ansi_tcap/packet-ansi_tcap-template.c b/epan/dissectors/asn1/ansi_tcap/packet-ansi_tcap-template.c index 8b4e1f2ff9..b9119bc8de 100644 --- a/epan/dissectors/asn1/ansi_tcap/packet-ansi_tcap-template.c +++ b/epan/dissectors/asn1/ansi_tcap/packet-ansi_tcap-template.c @@ -134,7 +134,7 @@ struct ansi_tcap_invokedata_t { gint32 OperationCode_national; }; -static wmem_map_t *TransactionId_table=NULL; +static wmem_multimap_t *TransactionId_table=NULL; /* Store Invoke information needed for the corresponding reply */ static void @@ -163,18 +163,14 @@ save_invoke_data(packet_info *pinfo, proto_tree *tree _U_, tvbuff_t *tvb _U_){ break; } - /* If the entry allready exists don't owervrite it */ - ansi_tcap_saved_invokedata = (struct ansi_tcap_invokedata_t *)wmem_map_lookup(TransactionId_table,buf); - if(ansi_tcap_saved_invokedata) - return; - ansi_tcap_saved_invokedata = wmem_new(wmem_file_scope(), struct ansi_tcap_invokedata_t); ansi_tcap_saved_invokedata->OperationCode = ansi_tcap_private.d.OperationCode; ansi_tcap_saved_invokedata->OperationCode_national = ansi_tcap_private.d.OperationCode_national; ansi_tcap_saved_invokedata->OperationCode_private = ansi_tcap_private.d.OperationCode_private; - wmem_map_insert(TransactionId_table, + wmem_multimap_insert32(TransactionId_table, wmem_strdup(wmem_file_scope(), buf), + pinfo->num, ansi_tcap_saved_invokedata); /* ws_warning("Tcap Invoke Hash string %s",buf); @@ -212,7 +208,7 @@ find_saved_invokedata(packet_info *pinfo, proto_tree *tree _U_, tvbuff_t *tvb _U break; } - ansi_tcap_saved_invokedata = (struct ansi_tcap_invokedata_t *)wmem_map_lookup(TransactionId_table, buf); + ansi_tcap_saved_invokedata = (struct ansi_tcap_invokedata_t *)wmem_multimap_lookup32_le(TransactionId_table, buf, pinfo->num); if(ansi_tcap_saved_invokedata){ ansi_tcap_private.d.OperationCode = ansi_tcap_saved_invokedata->OperationCode; ansi_tcap_private.d.OperationCode_national = ansi_tcap_saved_invokedata->OperationCode_national; @@ -487,5 +483,5 @@ proto_register_ansi_tcap(void) "Type of matching invoke/response, risk of mismatch if loose matching chosen", &ansi_tcap_response_matching_type, ansi_tcap_response_matching_type_values, FALSE); - TransactionId_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); + TransactionId_table = wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); } diff --git a/epan/dissectors/packet-ansi_map.c b/epan/dissectors/packet-ansi_map.c index e4f5570efe..4ed7435370 100644 --- a/epan/dissectors/packet-ansi_map.c +++ b/epan/dissectors/packet-ansi_map.c @@ -1165,7 +1165,7 @@ static void dissect_ansi_map_win_trigger_list(tvbuff_t *tvb, packet_info *pinfo /* Transaction table */ -static wmem_map_t *TransactionId_table=NULL; +static wmem_multimap_t *TransactionId_table=NULL; /* Store Invoke information needed for the corresponding reply */ static void @@ -1196,17 +1196,14 @@ update_saved_invokedata(packet_info *pinfo, struct ansi_tcap_private_t *p_privat buf = wmem_strdup_printf(pinfo->pool, "%s%s%s",p_private_tcap->TransactionID_str,src_str,dst_str); break; } - /* If the entry allready exists don't owervrite it */ - ansi_map_saved_invokedata = (struct ansi_map_invokedata_t *)wmem_map_lookup(TransactionId_table,buf); - if(ansi_map_saved_invokedata) - return; ansi_map_saved_invokedata = wmem_new(wmem_file_scope(), struct ansi_map_invokedata_t); ansi_map_saved_invokedata->opcode = p_private_tcap->d.OperationCode_private; ansi_map_saved_invokedata->ServiceIndicator = ServiceIndicator; - wmem_map_insert(TransactionId_table, + wmem_multimap_insert32(TransactionId_table, wmem_strdup(wmem_file_scope(), buf), + pinfo->num, ansi_map_saved_invokedata); /*ws_warning("Invoke Hash string %s pkt: %u",buf,pinfo->num);*/ @@ -15253,7 +15250,7 @@ dissect_ansi_map_QualificationRequest2Res(gboolean implicit_tag _U_, tvbuff_t *t /*--- End of included file: packet-ansi_map-fn.c ---*/ -#line 3608 "./asn1/ansi_map/packet-ansi_map-template.c" +#line 3605 "./asn1/ansi_map/packet-ansi_map-template.c" /* * 6.5.2.dk N.S0013-0 v 1.0,X.S0004-550-E v1.0 2.301 @@ -15979,7 +15976,7 @@ find_saved_invokedata(asn1_ctx_t *actx, struct ansi_tcap_private_t *p_private_tc } /*ws_warning("Find Hash string %s pkt: %u",buf,actx->pinfo->num);*/ - ansi_map_saved_invokedata = (struct ansi_map_invokedata_t *)wmem_map_lookup(TransactionId_table, buf); + ansi_map_saved_invokedata = (struct ansi_map_invokedata_t *)wmem_multimap_lookup32_le(TransactionId_table, buf, actx->pinfo->num); if(ansi_map_saved_invokedata){ OperationCode = ansi_map_saved_invokedata->opcode & 0xff; ServiceIndicator = ansi_map_saved_invokedata->ServiceIndicator; @@ -19215,7 +19212,7 @@ void proto_register_ansi_map(void) { NULL, HFILL }}, /*--- End of included file: packet-ansi_map-hfarr.c ---*/ -#line 5377 "./asn1/ansi_map/packet-ansi_map-template.c" +#line 5374 "./asn1/ansi_map/packet-ansi_map-template.c" }; /* List of subtrees */ @@ -19476,7 +19473,7 @@ void proto_register_ansi_map(void) { &ett_ansi_map_ReturnData, /*--- End of included file: packet-ansi_map-ettarr.c ---*/ -#line 5410 "./asn1/ansi_map/packet-ansi_map-template.c" +#line 5407 "./asn1/ansi_map/packet-ansi_map-template.c" }; static ei_register_info ei[] = { @@ -19550,7 +19547,7 @@ void proto_register_ansi_map(void) { "Type of matching invoke/response, risk of mismatch if loose matching chosen", &ansi_map_response_matching_type, ansi_map_response_matching_type_values, FALSE); - TransactionId_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); + TransactionId_table = wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); register_stat_tap_table_ui(&stat_table); } diff --git a/epan/dissectors/packet-ansi_tcap.c b/epan/dissectors/packet-ansi_tcap.c index 910db69205..3e108b391a 100644 --- a/epan/dissectors/packet-ansi_tcap.c +++ b/epan/dissectors/packet-ansi_tcap.c @@ -225,7 +225,7 @@ struct ansi_tcap_invokedata_t { gint32 OperationCode_national; }; -static wmem_map_t *TransactionId_table=NULL; +static wmem_multimap_t *TransactionId_table=NULL; /* Store Invoke information needed for the corresponding reply */ static void @@ -254,18 +254,14 @@ save_invoke_data(packet_info *pinfo, proto_tree *tree _U_, tvbuff_t *tvb _U_){ break; } - /* If the entry allready exists don't owervrite it */ - ansi_tcap_saved_invokedata = (struct ansi_tcap_invokedata_t *)wmem_map_lookup(TransactionId_table,buf); - if(ansi_tcap_saved_invokedata) - return; - ansi_tcap_saved_invokedata = wmem_new(wmem_file_scope(), struct ansi_tcap_invokedata_t); ansi_tcap_saved_invokedata->OperationCode = ansi_tcap_private.d.OperationCode; ansi_tcap_saved_invokedata->OperationCode_national = ansi_tcap_private.d.OperationCode_national; ansi_tcap_saved_invokedata->OperationCode_private = ansi_tcap_private.d.OperationCode_private; - wmem_map_insert(TransactionId_table, + wmem_multimap_insert32(TransactionId_table, wmem_strdup(wmem_file_scope(), buf), + pinfo->num, ansi_tcap_saved_invokedata); /* ws_warning("Tcap Invoke Hash string %s",buf); @@ -303,7 +299,7 @@ find_saved_invokedata(packet_info *pinfo, proto_tree *tree _U_, tvbuff_t *tvb _U break; } - ansi_tcap_saved_invokedata = (struct ansi_tcap_invokedata_t *)wmem_map_lookup(TransactionId_table, buf); + ansi_tcap_saved_invokedata = (struct ansi_tcap_invokedata_t *)wmem_multimap_lookup32_le(TransactionId_table, buf, pinfo->num); if(ansi_tcap_saved_invokedata){ ansi_tcap_private.d.OperationCode = ansi_tcap_saved_invokedata->OperationCode; ansi_tcap_private.d.OperationCode_national = ansi_tcap_saved_invokedata->OperationCode_national; @@ -1366,7 +1362,7 @@ dissect_ansi_tcap_PackageType(gboolean implicit_tag _U_, tvbuff_t *tvb _U_, int /*--- End of included file: packet-ansi_tcap-fn.c ---*/ -#line 309 "./asn1/ansi_tcap/packet-ansi_tcap-template.c" +#line 305 "./asn1/ansi_tcap/packet-ansi_tcap-template.c" @@ -1711,7 +1707,7 @@ proto_register_ansi_tcap(void) NULL, HFILL }}, /*--- End of included file: packet-ansi_tcap-hfarr.c ---*/ -#line 445 "./asn1/ansi_tcap/packet-ansi_tcap-template.c" +#line 441 "./asn1/ansi_tcap/packet-ansi_tcap-template.c" }; /* Setup protocol subtree array */ @@ -1749,7 +1745,7 @@ proto_register_ansi_tcap(void) &ett_ansi_tcap_T_paramSet, /*--- End of included file: packet-ansi_tcap-ettarr.c ---*/ -#line 456 "./asn1/ansi_tcap/packet-ansi_tcap-template.c" +#line 452 "./asn1/ansi_tcap/packet-ansi_tcap-template.c" }; static ei_register_info ei[] = { @@ -1784,5 +1780,5 @@ proto_register_ansi_tcap(void) "Type of matching invoke/response, risk of mismatch if loose matching chosen", &ansi_tcap_response_matching_type, ansi_tcap_response_matching_type_values, FALSE); - TransactionId_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); + TransactionId_table = wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(), wmem_str_hash, g_str_equal); } diff --git a/epan/dissectors/packet-gsm_sms.c b/epan/dissectors/packet-gsm_sms.c index e4240c3bfa..d7f387ba8d 100644 --- a/epan/dissectors/packet-gsm_sms.c +++ b/epan/dissectors/packet-gsm_sms.c @@ -287,7 +287,7 @@ static value_string_ext gsm_sms_coding_group_bits_vals_ext = VALUE_STRING_EXT_IN static dissector_table_t gsm_sms_dissector_tbl; /* Short Message reassembly */ static reassembly_table g_sm_reassembly_table; -static wmem_map_t *g_sm_fragment_params_table = NULL; +static wmem_multimap_t *g_sm_fragment_params_table = NULL; static gint ett_gsm_sms_ud_fragment = -1; static gint ett_gsm_sms_ud_fragments = -1; /* @@ -2106,7 +2106,7 @@ dis_field_ud(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset p_frag_params->udl = udl; p_frag_params->fill_bits = fill_bits; p_frag_params->length = length; - wmem_map_insert(g_sm_fragment_params_table, p_frag_params_key, p_frag_params); + wmem_multimap_insert32(g_sm_fragment_params_table, p_frag_params_key, pinfo->num, p_frag_params); } } /* Else: not fragmented */ if (! sm_tvb) /* One single Short Message, or not reassembled */ @@ -2148,8 +2148,8 @@ dis_field_ud(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset copy_address_shallow(&frag_params_key.src, &pinfo->src); copy_address_shallow(&frag_params_key.dst, &pinfo->dst); frag_params_key.id = (udh_fields.sm_id<<16)|i; - p_frag_params = (sm_fragment_params*)wmem_map_lookup(g_sm_fragment_params_table, - &frag_params_key); + p_frag_params = (sm_fragment_params*)wmem_multimap_lookup32_le(g_sm_fragment_params_table, + &frag_params_key, pinfo->num); if (p_frag_params) { proto_tree_add_item(subtree, hf_gsm_sms_text, sm_tvb, total_sms_len, @@ -2181,8 +2181,8 @@ dis_field_ud(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset copy_address_shallow(&frag_params_key.src, &pinfo->src); copy_address_shallow(&frag_params_key.dst, &pinfo->dst); frag_params_key.id = (udh_fields.sm_id<<16)|i; - p_frag_params = (sm_fragment_params*)wmem_map_lookup(g_sm_fragment_params_table, - &frag_params_key); + p_frag_params = (sm_fragment_params*)wmem_multimap_lookup32_le(g_sm_fragment_params_table, + &frag_params_key, pinfo->num); if (p_frag_params) { proto_tree_add_ts_23_038_7bits_packed_item(subtree, hf_gsm_sms_text, sm_tvb, @@ -2235,8 +2235,8 @@ dis_field_ud(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 offset copy_address_shallow(&frag_params_key.src, &pinfo->src); copy_address_shallow(&frag_params_key.dst, &pinfo->dst); frag_params_key.id = (udh_fields.sm_id<<16)|i; - p_frag_params = (sm_fragment_params*)wmem_map_lookup(g_sm_fragment_params_table, - &frag_params_key); + p_frag_params = (sm_fragment_params*)wmem_multimap_lookup32_le(g_sm_fragment_params_table, + &frag_params_key, pinfo->num); if (p_frag_params) { /* Decode as ENC_UTF_16 instead of UCS2 because Android and iOS smartphones @@ -3593,7 +3593,7 @@ proto_register_gsm_sms(void) register_dissector("gsm_sms", dissect_gsm_sms, proto_gsm_sms); - g_sm_fragment_params_table = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), + g_sm_fragment_params_table = wmem_multimap_new_autoreset(wmem_epan_scope(), wmem_file_scope(), sm_fragment_params_hash, sm_fragment_params_equal); reassembly_table_register(&g_sm_reassembly_table, diff --git a/wsutil/wmem/CMakeLists.txt b/wsutil/wmem/CMakeLists.txt index d3d4e88db5..e0407f1274 100644 --- a/wsutil/wmem/CMakeLists.txt +++ b/wsutil/wmem/CMakeLists.txt @@ -14,6 +14,7 @@ set(WMEM_PUBLIC_HEADERS wmem_list.h wmem_map.h wmem_miscutl.h + wmem_multimap.h wmem_queue.h wmem_stack.h wmem_strbuf.h @@ -47,6 +48,7 @@ set(WMEM_FILES wmem_list.c wmem_map.c wmem_miscutl.c + wmem_multimap.c wmem_stack.c wmem_strbuf.c wmem_strutl.c diff --git a/wsutil/wmem/wmem.h b/wsutil/wmem/wmem.h index 65f06fdc04..8b191084d0 100644 --- a/wsutil/wmem/wmem.h +++ b/wsutil/wmem/wmem.h @@ -17,6 +17,7 @@ #include "wmem_list.h" #include "wmem_map.h" #include "wmem_miscutl.h" +#include "wmem_multimap.h" #include "wmem_queue.h" #include "wmem_stack.h" #include "wmem_strbuf.h" diff --git a/wsutil/wmem/wmem_multimap.c b/wsutil/wmem/wmem_multimap.c new file mode 100644 index 0000000000..f97d0c1126 --- /dev/null +++ b/wsutil/wmem/wmem_multimap.c @@ -0,0 +1,185 @@ +/* wmem_multimap.c + * Wireshark Memory Manager Hash Multimap + * Copyright 2021, John Thacker + * Copyright 2014, Evan Huus + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "config.h" + +#include + +#include "wmem_core.h" +#include "wmem_list.h" +#include "wmem_map.h" +#include "wmem_multimap.h" +#include "wmem_tree.h" +#include "wmem_user_cb.h" + +struct _wmem_multimap_t { + + wmem_map_t *map; + + guint metadata_scope_cb_id; + guint data_scope_cb_id; + + wmem_allocator_t *metadata_allocator; + wmem_allocator_t *data_allocator; +}; + +wmem_multimap_t * +wmem_multimap_new(wmem_allocator_t *allocator, + GHashFunc hash_func, GEqualFunc eql_func) +{ + wmem_multimap_t *multimap; + + multimap = wmem_new(allocator, wmem_multimap_t); + + multimap->map = wmem_map_new(allocator, hash_func, eql_func); + multimap->metadata_allocator = allocator; + multimap->data_allocator = allocator; + + return multimap; +} + +static gboolean +wmem_multimap_reset_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event, + void *user_data) +{ + wmem_multimap_t *multimap = (wmem_multimap_t*)user_data; + + if (event == WMEM_CB_DESTROY_EVENT) { + wmem_unregister_callback(multimap->metadata_allocator, multimap->metadata_scope_cb_id); + wmem_free(multimap->metadata_allocator, multimap); + } + + return TRUE; +} + +static gboolean +wmem_multimap_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, + void *user_data) +{ + wmem_multimap_t *multimap = (wmem_multimap_t*)user_data; + + wmem_unregister_callback(multimap->data_allocator, multimap->data_scope_cb_id); + + return FALSE; +} + +wmem_multimap_t * +wmem_multimap_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope, + GHashFunc hash_func, GEqualFunc eql_func) +{ + wmem_multimap_t *multimap; + + multimap = wmem_new(metadata_scope, wmem_multimap_t); + + multimap->map = wmem_map_new_autoreset(metadata_scope, data_scope, hash_func, eql_func); + multimap->metadata_allocator = metadata_scope; + multimap->data_allocator = data_scope; + + multimap->metadata_scope_cb_id = wmem_register_callback(metadata_scope, wmem_multimap_destroy_cb, multimap); + multimap->data_scope_cb_id = wmem_register_callback(data_scope, wmem_multimap_reset_cb, multimap); + + return multimap; +} + +wmem_list_t* +wmem_multimap_get_keys(wmem_allocator_t *list_allocator, wmem_multimap_t *map) +{ + return wmem_map_get_keys(list_allocator, map->map); +} + +static void +count_nodes(gpointer key _U_, gpointer value, gpointer user_data) +{ + guint* count = (guint*)user_data; + (*count) += wmem_tree_count(value); +} + +guint +wmem_multimap_size(wmem_multimap_t *map) +{ + guint count = 0; + + wmem_map_foreach(map->map, count_nodes, &count); + return count; +} + +guint +wmem_multimap_count(wmem_multimap_t *map, const void *key) +{ + wmem_tree_t *tree; + + if ((tree = wmem_map_lookup(map->map, key)) == NULL) { + return 0; + } + return wmem_tree_count(tree); +} + +gboolean +wmem_multimap_insert32(wmem_multimap_t *map, const void *key, guint32 frame_num, void *value) +{ + wmem_tree_t *tree; + gboolean ret = TRUE; + + if ((tree = wmem_map_lookup(map->map, key)) == NULL) { + tree = wmem_tree_new(map->data_allocator); + wmem_map_insert(map->map, key, tree); + ret = FALSE; + } + wmem_tree_insert32(tree, frame_num, value); + + return ret; +} + +void * +wmem_multimap_lookup32(wmem_multimap_t *map, const void *key, guint32 frame_num) +{ + wmem_tree_t *tree; + + if ((tree = wmem_map_lookup(map->map, key)) == NULL) { + return NULL; + } + return wmem_tree_lookup32(tree, frame_num); +} + +void * +wmem_multimap_lookup32_le(wmem_multimap_t *map, const void *key, guint32 frame_num) +{ + wmem_tree_t *tree; + + if ((tree = wmem_map_lookup(map->map, key)) == NULL) { + return NULL; + } + return wmem_tree_lookup32_le(tree, frame_num); +} + +void * +wmem_multimap_remove32(wmem_multimap_t *map, const void *key, const guint32 frame_num) +{ + wmem_tree_t *tree; + + if ((tree = wmem_map_lookup(map->map, key)) == NULL) { + return NULL; + } + return wmem_tree_remove32(tree, frame_num); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/wsutil/wmem/wmem_multimap.h b/wsutil/wmem/wmem_multimap.h new file mode 100644 index 0000000000..b593c95f78 --- /dev/null +++ b/wsutil/wmem/wmem_multimap.h @@ -0,0 +1,193 @@ +/* wmem_map.h + * Definitions for the Wireshark Memory Manager Hash Multimap + * Copyright 2021, John Thacker + * Copyright 2014, Evan Huus + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __WMEM_MULTIMAP_H__ +#define __WMEM_MULTIMAP_H__ + +#include + +#include "wmem_core.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup wmem + * @{ + * @defgroup wmem-multimap Hash Multimap + * + * A hash multimap implementation on top of wmem_map and wmem_tree, storing + * multiple values at each hash key in a tree indexed by a 32 bit integer. + * + * The primary use case is a protocol with an ID used as the hash lookup + * key that can be reused in a capture, and the frame number used as the + * tree key. We often want to find the most recent frame that had a certain + * ID, e.g. for request/response matching, and wmem_multimap_lookup32_le() + * serves that purpose. + * + * Since the tree implementation is a self-balancing red-black tree, lookup + * time is still O(log(n)) even though elements with equivalent hash keys + * are usually added in increasing order of frame number. + * + * NOTE: The multimap does not yet support inserting items without + * specifying the tree key, because the total capacity of individual trees + * (including deleted nodes) is not tracked. + * + * @{ + */ + +typedef struct _wmem_multimap_t wmem_multimap_t; + +/** Creates a multimap with the given allocator scope. When the scope is emptied, + * the map is fully destroyed. Items stored in it will not be freed unless they + * were allocated from the same scope. + * + * @param allocator The allocator scope with which to create the map. + * @param hash_func The hash function used to place inserted keys. + * @param eql_func The equality function used to compare inserted keys. + * @return The newly-allocated map. + */ +WS_DLL_PUBLIC +wmem_multimap_t * +wmem_multimap_new(wmem_allocator_t *allocator, + GHashFunc hash_func, GEqualFunc eql_func) +G_GNUC_MALLOC; + +/** Creates a multimap with two allocator scopes. The base structure lives in the + * metadata scope, and the map data lives in the data scope. Every time free_all + * occurs in the data scope the map is transparently emptied without affecting + * the location of the base / metadata structure. + * + * WARNING: None of the map (even the part in the metadata scope) can be used + * after the data scope has been *destroyed*. + * + * The primary use for this function is to create maps that reset for each new + * capture file that is loaded. This can be done by specifying wmem_epan_scope() + * as the metadata scope and wmem_file_scope() as the data scope. + */ +WS_DLL_PUBLIC +wmem_multimap_t * +wmem_multimap_new_autoreset(wmem_allocator_t *metadata_scope, wmem_allocator_t *data_scope, + GHashFunc hash_func, GEqualFunc eql_func) +G_GNUC_MALLOC; + +/** Retrieves a list of the keys inside the multimap + * + * @param list_allocator The allocator scope for the returned list. + * @param map The multimap to extract keys from + * @return list of keys in the multimap + */ +WS_DLL_PUBLIC +wmem_list_t* +wmem_multimap_get_keys(wmem_allocator_t *list_allocator, wmem_multimap_t *map); + +/** Return the total number of elements in the multimap. + * + * @param map The multimap to use + * @return the number of elements +*/ +WS_DLL_PUBLIC +guint +wmem_multimap_size(wmem_multimap_t *map); + +/** Returns the number of values in the multimap with a certain hash key. + * (Note: This is the number of current elements, so this can only be used to + * safely generate unique tree keys prior to insertion if no values have been + * removed, due to how the tree implementation works.) + * + * @param map The multimap to search in. + * @param key The primary key to lookup in the map. + * @return The number of values in the tree stored at map key, or zero if no + * tree exists at that key. + */ +WS_DLL_PUBLIC +guint +wmem_multimap_count(wmem_multimap_t *map, const void *key); + +/** Insert a value in the multimap. + * + * @param map The multimap to insert into. + * @param key The key to insert by in the map. + * @param frame_num The key to insert by in the tree. + * @param value The value to insert. + * @return TRUE if there was already a tree mapped at key, in which case the + * caller may safely free key. (This is not necessary if key is allocated with + * a wmem pool.) + * + * Note: as with wmem_tree, if there is already a node with the same pair + * of keys, then the existing value will simply be overwritten. This is not + * a problem if the value is wmem allocated, but if it is manually managed, + * then you must ensure that the pair is unique or do a lookup before inserting. + */ +WS_DLL_PUBLIC +gboolean +wmem_multimap_insert32(wmem_multimap_t *map, const void *key, guint32 frame_num, void *value); + +/** Lookup a value in the multimap combination with an exact match. + * + * @param map The multimap to search in. + * @param key The primary key to lookup in the map. + * @param frame_num The secondary key to lookup in the tree. + * @return The value stored at the keys if any, or NULL. + */ +WS_DLL_PUBLIC +void * +wmem_multimap_lookup32(wmem_multimap_t *map, const void *key, const guint32 frame_num); + +/** Lookup a value in the multimap with an exact match for the map key + * and the largest value less than or equal to the tree key. This is + * useful for request/response matching where IDs can be reused. + * + * @param map The multimap to search in. + * @param key The primary key to lookup in the map. + * @param frame_num The secondary key to lookup in the tree. + * @return The value stored at the primary key in the map and with the largest + * key in the tree that is less than or equal to the second key if any, or NULL. + */ +WS_DLL_PUBLIC +void * +wmem_multimap_lookup32_le(wmem_multimap_t *map, const void *key, const guint32 frame_num); + +/** Remove a value from the multimap. If no value is stored at that key pair, + * nothing happens. As with wmem_tree, this is not really a remove, but the + * value is set to NULL so that wmem_multimap_lookup32 not will find it. + * + * @param map The multimap to remove from. + * @param key The map key of the value to remove. + * @param frame_num The tree key of the value to remove. + * @return The (removed) value stored at the key if any, or NULL. + */ +WS_DLL_PUBLIC +void * +wmem_multimap_remove32(wmem_multimap_t *map, const void *key, const guint32 frame_num); + +/** @} + * @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __WMEM_MULTIMAP_H__ */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */