From b5917d0182421fc8e23db3f38a0a5c75f1e6d55b Mon Sep 17 00:00:00 2001 From: John Thacker Date: Sat, 20 Nov 2021 19:51:59 -0500 Subject: [PATCH] wmem: Add a multimap A number of protocols have IDs that can be reused that are used as lookup keys. In most cases the frame number should be used as well to differentiate repeat appearances of an ID. For response/request matching, it is frequently useful to find the most recent frame number (greatest value less than or equal to the current one) that contained an ID. We can achieve that by using a multimap that stores values with a given ID in a tree keyed with the frame number. This works better than using a map or a tree alone: 1) A map isn't ordered, so doesn't allow for less than or equal comparison. 2) Using a tree requires an ordering on all the ID components, and then having to test all the components other than the frame number separately for equality after retrieval. Currently the multimap does not support inserting items without specifying the tree key (and having the multimap generate a key), because the total capacity of trees (including deleted nodes) is not tracked. If other use cases are needed, this could be added later along with more generic multimap support. Use a multimap in ANSI MAP, ANSI TCAP, and GSM SMS, all of which need to match lookup IDs that can be reused. Fix #7653. --- debian/libwsutil0.symbols | 9 + doc/README.wmem | 4 + .../asn1/ansi_map/packet-ansi_map-template.c | 13 +- .../ansi_tcap/packet-ansi_tcap-template.c | 14 +- epan/dissectors/packet-ansi_map.c | 19 +- epan/dissectors/packet-ansi_tcap.c | 20 +- epan/dissectors/packet-gsm_sms.c | 18 +- wsutil/wmem/CMakeLists.txt | 2 + wsutil/wmem/wmem.h | 1 + wsutil/wmem/wmem_multimap.c | 185 +++++++++++++++++ wsutil/wmem/wmem_multimap.h | 193 ++++++++++++++++++ 11 files changed, 429 insertions(+), 49 deletions(-) create mode 100644 wsutil/wmem/wmem_multimap.c create mode 100644 wsutil/wmem/wmem_multimap.h 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: + */