diff --git a/utp_api.cpp b/utp_api.cpp index 63aff18..edf277a 100644 --- a/utp_api.cpp +++ b/utp_api.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include #include #include "utp_internal.h" #include "utp_utils.h" @@ -72,7 +73,6 @@ struct_utp_context::struct_utp_context() memset(&context_stats, 0, sizeof(context_stats)); memset(callbacks, 0, sizeof(callbacks)); target_delay = CCONTROL_TARGET; - utp_sockets = new UTPSocketHT; callbacks[UTP_GET_UDP_MTU] = &utp_default_get_udp_mtu; callbacks[UTP_GET_UDP_OVERHEAD] = &utp_default_get_udp_overhead; @@ -92,10 +92,6 @@ struct_utp_context::struct_utp_context() last_check = 0; } -struct_utp_context::~struct_utp_context() { - delete this->utp_sockets; -} - utp_context* utp_init (int version) { assert(version == 2); diff --git a/utp_hash.cpp b/utp_hash.cpp index e5f0b45..895c514 100644 --- a/utp_hash.cpp +++ b/utp_hash.cpp @@ -20,13 +20,9 @@ * THE SOFTWARE. */ -#include - #include "utp_hash.h" #include "utp_types.h" -#define LIBUTP_HASH_UNUSED ((utp_link_t)-1) - #ifdef STRICT_ALIGN inline uint32 Read32(const void *p) { @@ -34,59 +30,10 @@ inline uint32 Read32(const void *p) memcpy(&tmp, p, sizeof tmp); return tmp; } - #else inline uint32 Read32(const void *p) { return *(uint32*)p; } #endif - -// Get the amount of memory required for the hash parameters and the bucket set -// Waste a space for an unused bucket in order to ensure the following managed memory have 32-bit aligned addresses -// TODO: make this 64-bit clean -#define BASE_SIZE(bc) (sizeof(utp_hash_t) + sizeof(utp_link_t) * ((bc) + 1 - 1)) - -// Get a pointer to the base of the structure array managed by the hash table -#define get_bep(h) ((byte*)(h)) + BASE_SIZE((h)->N) - -// Get the address of the information associated with a specific structure in the array, -// given the address of the base of the structure. -// This assumes a utp_link_t link member is at the end of the structure. -// Given compilers filling out the memory to a 32-bit clean value, this may mean that -// the location named in the structure may not be the location actually used by the hash table, -// since the compiler may have padded the end of the structure with 2 bytes after the utp_link_t member. -// TODO: this macro should not require that the variable pointing at the hash table be named 'hash' -#define ptr_to_link(p) (utp_link_t *) (((byte *) (p)) + hash->E - sizeof(utp_link_t)) - -// Calculate how much to allocate for a hash table with bucket count, total size, and structure count -// TODO: make this 64-bit clean -#define ALLOCATION_SIZE(bc, ts, sc) (BASE_SIZE((bc)) + (ts) * (sc)) - -utp_hash_t *utp_hash_create(int N, int key_size, int total_size, int initial, utp_hash_compute_t hashfun, utp_hash_equal_t compfun) -{ - // Must have odd number of hash buckets (prime number is best) - assert(N % 2); - // Ensure structures will be at aligned memory addresses - // TODO: make this 64-bit clean - assert(0 == (total_size % 4)); - - int size = ALLOCATION_SIZE(N, total_size, initial); - utp_hash_t *hash = (utp_hash_t *) malloc( size ); - memset( hash, 0, size ); - - for (int i = 0; i < N + 1; ++i) - hash->inits[i] = LIBUTP_HASH_UNUSED; - hash->N = N; - hash->K = key_size; - hash->E = total_size; - hash->hash_compute = hashfun; - hash->hash_equal = compfun; - hash->allocated = initial; - hash->count = 0; - hash->used = 0; - hash->free = LIBUTP_HASH_UNUSED; - return hash; -} - uint utp_hash_mem(const void *keyp, size_t keysize) { uint hash = 0; @@ -105,144 +52,3 @@ uint utp_hash_mem(const void *keyp, size_t keysize) } return hash; } - -uint utp_hash_mkidx(utp_hash_t *hash, const void *keyp) -{ - // Generate a key from the hash - return hash->hash_compute(keyp, hash->K) % hash->N; -} - -static inline bool compare(byte *a, byte *b,int n) -{ - assert(n >= 4); - if (Read32(a) != Read32(b)) return false; - return memcmp(a+4, b+4, n-4) == 0; -} - -#define COMPARE(h,k1,k2,ks) (((h)->hash_equal) ? (h)->hash_equal((void*)k1,(void*)k2,ks) : compare(k1,k2,ks)) - -// Look-up a key in the hash table. -// Returns NULL if not found -void *utp_hash_lookup(utp_hash_t *hash, const void *key) -{ - utp_link_t idx = utp_hash_mkidx(hash, key); - - // base pointer - byte *bep = get_bep(hash); - - utp_link_t cur = hash->inits[idx]; - while (cur != LIBUTP_HASH_UNUSED) { - byte *key2 = bep + (cur * hash->E); - if (COMPARE(hash, (byte*)key, key2, hash->K)) - return key2; - cur = *ptr_to_link(key2); - } - - return NULL; -} - -// Add a new element to the hash table. -// Returns a pointer to the new element. -// This assumes the element is not already present! -void *utp_hash_add(utp_hash_t **hashp, const void *key) -{ - //Allocate a new entry - byte *elemp; - utp_link_t elem; - utp_hash_t *hash = *hashp; - utp_link_t idx = utp_hash_mkidx(hash, key); - - if ((elem=hash->free) == LIBUTP_HASH_UNUSED) { - utp_link_t all = hash->allocated; - if (hash->used == all) { - utp_hash_t *nhash; - if (all <= (LIBUTP_HASH_UNUSED/2)) { - all *= 2; - } else if (all != LIBUTP_HASH_UNUSED) { - all = LIBUTP_HASH_UNUSED; - } else { - // too many items! can't grow! - assert(0); - return NULL; - } - // otherwise need to allocate. - nhash = (utp_hash_t*)realloc(hash, ALLOCATION_SIZE(hash->N, hash->E, all)); - if (!nhash) { - // out of memory (or too big to allocate) - assert(nhash); - return NULL; - } - hash = *hashp = nhash; - hash->allocated = all; - } - - elem = hash->used++; - elemp = get_bep(hash) + elem * hash->E; - } else { - elemp = get_bep(hash) + elem * hash->E; - hash->free = *ptr_to_link(elemp); - } - - *ptr_to_link(elemp) = hash->inits[idx]; - hash->inits[idx] = elem; - hash->count++; - - // copy key into it - memcpy(elemp, key, hash->K); - return elemp; -} - -// Delete an element from the utp_hash_t -// Returns a pointer to the already deleted element. -void *utp_hash_del(utp_hash_t *hash, const void *key) -{ - utp_link_t idx = utp_hash_mkidx(hash, key); - - // base pointer - byte *bep = get_bep(hash); - - utp_link_t *curp = &hash->inits[idx]; - utp_link_t cur; - while ((cur=*curp) != LIBUTP_HASH_UNUSED) { - byte *key2 = bep + (cur * hash->E); - if (COMPARE(hash,(byte*)key,(byte*)key2, hash->K )) { - // found an item that matched. unlink it - *curp = *ptr_to_link(key2); - // Insert into freelist - *ptr_to_link(key2) = hash->free; - hash->free = cur; - hash->count--; - return key2; - } - curp = ptr_to_link(key2); - } - - return NULL; -} - -void *utp_hash_iterate(utp_hash_t *hash, utp_hash_iterator_t *iter) -{ - utp_link_t elem; - - if ((elem=iter->elem) == LIBUTP_HASH_UNUSED) { - // Find a bucket with an element - utp_link_t buck = iter->bucket + 1; - for(;;) { - if (buck >= hash->N) - return NULL; - if ((elem = hash->inits[buck]) != LIBUTP_HASH_UNUSED) - break; - buck++; - } - iter->bucket = buck; - } - - byte *elemp = get_bep(hash) + (elem * hash->E); - iter->elem = *ptr_to_link(elemp); - return elemp; -} - -void utp_hash_free_mem(utp_hash_t* hash) -{ - free(hash); -} diff --git a/utp_hash.h b/utp_hash.h index 36d6b69..ff87a19 100644 --- a/utp_hash.h +++ b/utp_hash.h @@ -23,124 +23,8 @@ #ifndef __UTP_HASH_H__ #define __UTP_HASH_H__ -#include // memset -#include // malloc - #include "utp_types.h" -#include "utp_templates.h" - -// TODO: make utp_link_t a template parameter to HashTable -typedef uint32 utp_link_t; - -#ifdef _MSC_VER -// Silence the warning about the C99-compliant zero-length array at the end of the structure -#pragma warning (disable: 4200) -#endif - -typedef uint32 (*utp_hash_compute_t)(const void *keyp, size_t keysize); -typedef uint (*utp_hash_equal_t)(const void *key_a, const void *key_b, size_t keysize); - -// In memory the HashTable is laid out as follows: -// ---------------------------- low -// | hash table data members | -// ---------------------------- _ -// | indices | ^ -// | . | | utp_link_t indices into the key-values. -// | . | . -// ---------------------------- - <----- bep -// | keys and values | each key-value pair has size total_size -// | . | -// | . | -// ---------------------------- high -// -// The code depends on the ability of the compiler to pad the length -// of the hash table data members structure to -// a length divisible by 32-bits with no remainder. -// -// Since the number of hash buckets (indices) should be odd, the code -// asserts this and adds one to the hash bucket count to ensure that the -// following key-value pairs array starts on a 32-bit boundary. -// -// The key-value pairs array should start on a 32-bit boundary, otherwise -// processors like the ARM will silently mangle 32-bit data in these structures -// (e.g., turning 0xABCD into 0XCDAB when moving a value from memory to register -// when the memory address is 16 bits offset from a 32-bit boundary), -// also, the value will be stored at an address two bytes lower than the address -// value would ordinarily indicate. -// -// The key-value pair is of type T. The first field in T must -// be the key, i.e., the first K bytes of T contains the key. -// total_size = sizeof(T) and thus sizeof(T) >= sizeof(K) -// -// N is the number of buckets. -// -struct utp_hash_t { - utp_link_t N; - byte K; - byte E; - size_t count; - utp_hash_compute_t hash_compute; - utp_hash_equal_t hash_equal; - utp_link_t allocated; - utp_link_t used; - utp_link_t free; - utp_link_t inits[1]; // dynamic -}; - -#ifdef _MSC_VER -#pragma warning (default: 4200) -#endif - -struct utp_hash_iterator_t { - utp_link_t bucket; - utp_link_t elem; - - utp_hash_iterator_t() : bucket(0xffffffff), elem(0xffffffff) {} -}; uint utp_hash_mem(const void *keyp, size_t keysize); -uint utp_hash_comp(const void *key_a, const void *key_b, size_t keysize); - -utp_hash_t *utp_hash_create(int N, int key_size, int total_size, int initial, utp_hash_compute_t hashfun = utp_hash_mem, utp_hash_equal_t eqfun = NULL); -void *utp_hash_lookup(utp_hash_t *hash, const void *key); -void *utp_hash_add(utp_hash_t **hashp, const void *key); -void *utp_hash_del(utp_hash_t *hash, const void *key); - -void *utp_hash_iterate(utp_hash_t *hash, utp_hash_iterator_t *iter); -void utp_hash_free_mem(utp_hash_t *hash); - -/* - This HashTable requires that T have at least sizeof(K)+sizeof(utp_link_t) bytes. - Usually done like this: - - struct K { - int whatever; - }; - - struct T { - K wtf; - utp_link_t link; // also wtf - }; -*/ - -template class utpHashTable { - utp_hash_t *hash; -public: - static uint compare(const void *k1, const void *k2, size_t /*ks*/) { - return *((K*)k1) == *((K*)k2); - } - static uint32 compute_hash(const void *k, size_t /*ks*/) { - return ((K*)k)->compute_hash(); - } - void Init() { hash = NULL; } - bool Allocated() { return (hash != NULL); } - void Free() { utp_hash_free_mem(hash); hash = NULL; } - void Create(int N, int initial) { hash = utp_hash_create(N, sizeof(K), sizeof(T), initial, &compute_hash, &compare); } - T *Lookup(const K &key) { return (T*)utp_hash_lookup(hash, &key); } - T *Add(const K &key) { return (T*)utp_hash_add(&hash, &key); } - T *Delete(const K &key) { return (T*)utp_hash_del(hash, &key); } - T *Iterate(utp_hash_iterator_t &iterator) { return (T*)utp_hash_iterate(hash, &iterator); } - size_t GetCount() { return hash->count; } -}; #endif //__UTP_HASH_H__ diff --git a/utp_internal.cpp b/utp_internal.cpp index b65b35d..1ed5427 100644 --- a/utp_internal.cpp +++ b/utp_internal.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include // for UINT_MAX @@ -35,7 +34,6 @@ #include "utp_types.h" #include "utp_packedsockaddr.h" #include "utp_internal.h" -#include "utp_hash.h" #define TIMEOUT_CHECK_INTERVAL 500 @@ -670,7 +668,7 @@ struct UTPSocket { static void removeSocketFromAckList(UTPSocket *conn) { - auto const ida = conn->ida; + const auto ida = conn->ida; if (ida < 0) return; @@ -2504,10 +2502,10 @@ UTPSocket::~UTPSocket() ctx->last_utp_socket = NULL; } - // Remove object from the global hash table - UTPSocketKeyData* kd = ctx->utp_sockets->Delete(UTPSocketKey(addr, conn_id_recv)); - (void)kd; - assert(kd); + // The `utp_sockets` container owned this socket. + // This destructor is invoked when the socket is removed from `utp_sockets`, + // so confirm that it's been removed. + assert(!ctx->utp_sockets.contains(UTPSocketKey{ addr, conn_id_recv })); // remove the socket from ack_sockets if it was there also removeSocketFromAckList(this); @@ -2524,12 +2522,8 @@ UTPSocket::~UTPSocket() free(outbuf.elements); } -void UTP_FreeAll(struct UTPSocketHT *utp_sockets) { - utp_hash_iterator_t it; - UTPSocketKeyData* keyData; - for (keyData = utp_sockets->Iterate(it); keyData; keyData = utp_sockets->Iterate(it)) { - delete keyData->socket; - } +void utp_socket_delete(UTPSocket* sock) { + delete sock; } void utp_initialize_socket( utp_socket *conn, @@ -2540,14 +2534,14 @@ void utp_initialize_socket( utp_socket *conn, uint32 conn_id_recv, uint32 conn_id_send) { - PackedSockAddr psaddr = PackedSockAddr((const SOCKADDR_STORAGE*)addr, addrlen); + const auto psaddr = PackedSockAddr((const SOCKADDR_STORAGE*)addr, addrlen); if (need_seed_gen) { do { conn_seed = utp_call_get_random(conn->ctx, conn); // we identify v1 and higher by setting the first two bytes to 0x0001 conn_seed &= 0xffff; - } while (conn->ctx->utp_sockets->Lookup(UTPSocketKey(psaddr, conn_seed))); + } while (conn->ctx->utp_sockets.contains(UTPSocketKey{ psaddr, conn_seed })); conn_id_recv += conn_seed; conn_id_send += conn_seed; @@ -2573,7 +2567,7 @@ void utp_initialize_socket( utp_socket *conn, conn->mtu_reset(); conn->mtu_last = conn->mtu_ceiling; - conn->ctx->utp_sockets->Add(UTPSocketKey(conn->addr, conn->conn_id_recv))->socket = conn; + conn->ctx->utp_sockets.add(UTPSocketKey{ psaddr, conn->conn_id_recv }, conn); // we need to fit one packet in the window when we start the connection conn->max_window = conn->get_packet_size(); @@ -2813,6 +2807,27 @@ int utp_connect(utp_socket *conn, const struct sockaddr *to, socklen_t tolen) return 0; } +// id is either our recv id or our send id +// if it's our send id, and we initiated the connection, our recv id is id + 1 +// if it's our send id, and we did not initiate the connection, our recv id is id - 1 +// we have to check every case +UTPSocket* LookupAdjacent(utp_context *ctx, PackedSockAddr const& addr, uint32 const id) { + auto key = UTPSocketKey{ addr, id }; + if (auto* conn = ctx->utp_sockets.lookup(key); conn != nullptr) + return conn; + + key.recv_id = id + 1; + if (auto* conn = ctx->utp_sockets.lookup(key); conn != nullptr && conn->conn_id_send == id) + return conn; + + key.recv_id = id - 1; + if (auto* conn = ctx->utp_sockets.lookup(key); conn != nullptr && conn->conn_id_send == id) + return conn; + + return nullptr; +} + + // Returns 1 if the UDP payload was recognized as a UTP packet, or 0 if it was not int utp_process_udp(utp_context *ctx, const byte *buffer, size_t len, const struct sockaddr *to, socklen_t tolen) { @@ -2854,27 +2869,7 @@ int utp_process_udp(utp_context *ctx, const byte *buffer, size_t len, const stru const byte flags = pf1->type(); if (flags == ST_RESET) { - // id is either our recv id or our send id - // if it's our send id, and we initiated the connection, our recv id is id + 1 - // if it's our send id, and we did not initiate the connection, our recv id is id - 1 - // we have to check every case - - UTPSocketKeyData* keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id)); - if (!keyData) { - keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id + 1)); - if (keyData && keyData->socket->conn_id_send != id) { - keyData = NULL; - } - } - if (!keyData) { - keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id - 1)); - if (keyData && keyData->socket->conn_id_send != id) { - keyData = NULL; - } - } - if (keyData) { - UTPSocket* conn = keyData->socket; - + if (auto* conn = LookupAdjacent(ctx, addr, id); conn != nullptr) { #if UTP_DEBUG_LOGGING ctx->log(UTP_LOG_DEBUG, NULL, "recv RST for existing connection"); #endif @@ -2900,12 +2895,8 @@ int utp_process_udp(utp_context *ctx, const byte *buffer, size_t len, const stru if (ctx->last_utp_socket && ctx->last_utp_socket->addr == addr && ctx->last_utp_socket->conn_id_recv == id) { conn = ctx->last_utp_socket; - } else { - UTPSocketKeyData* keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id)); - if (keyData) { - conn = keyData->socket; - ctx->last_utp_socket = conn; - } + } else if (auto* lookup = ctx->utp_sockets.lookup(UTPSocketKey{ addr, id }); lookup != nullptr) { + conn = ctx->last_utp_socket = lookup; } if (conn) { @@ -2962,8 +2953,7 @@ int utp_process_udp(utp_context *ctx, const byte *buffer, size_t len, const stru ctx->log(UTP_LOG_DEBUG, NULL, "Incoming connection from %s", addrfmt(addr, addrbuf)); #endif - UTPSocketKeyData* keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id + 1)); - if (keyData) { + if (ctx->utp_sockets.contains(UTPSocketKey{ addr, id + 1 })) { #if UTP_DEBUG_LOGGING ctx->log(UTP_LOG_DEBUG, NULL, "rejected incoming connection, connection already exists"); @@ -2972,7 +2962,7 @@ int utp_process_udp(utp_context *ctx, const byte *buffer, size_t len, const stru return 1; } - if (ctx->utp_sockets->GetCount() > 3000) { + if (ctx->utp_sockets.size() > 3000) { #if UTP_DEBUG_LOGGING ctx->log(UTP_LOG_DEBUG, NULL, "rejected incoming connection, too many uTP sockets %d", ctx->utp_sockets->GetCount()); @@ -3059,22 +3049,8 @@ static UTPSocket* parse_icmp_payload(utp_context *ctx, const byte *buffer, size_ return NULL; } - UTPSocketKeyData* keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id)); - if (!keyData) { - keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id + 1)); - if (keyData && keyData->socket->conn_id_send != id) { - keyData = NULL; - } - } - if (!keyData) { - keyData = ctx->utp_sockets->Lookup(UTPSocketKey(addr, id - 1)); - if (keyData && keyData->socket->conn_id_send != id) { - keyData = NULL; - } - } - if (keyData) { - return keyData->socket; - } + if (auto* const conn = LookupAdjacent(ctx, addr, id); conn != nullptr) + return conn; #if UTP_DEBUG_LOGGING ctx->log(UTP_LOG_DEBUG, NULL, "Ignoring ICMP from %s: No matching connection found for id %u", addrfmt(addr, addrbuf), id); @@ -3306,20 +3282,13 @@ void utp_check_timeouts(utp_context *ctx) } infos.shrink_to_fit(); - utp_hash_iterator_t it; - UTPSocketKeyData* keyData; - for (keyData = ctx->utp_sockets->Iterate(it); keyData; keyData = ctx->utp_sockets->Iterate(it)) { - UTPSocket *conn = keyData->socket; - conn->check_timeouts(); - - // Check if the object was deleted - if (conn->state == CS_DESTROY) { - #if UTP_DEBUG_LOGGING - conn->log(UTP_LOG_DEBUG, "Destroying"); - #endif - delete conn; + ctx->utp_sockets.erase_if( + [](auto& item){ + auto& conn = item->second; + conn->check_timeouts(); + return conn->state == CS_DESTROY; } - } + ); } int utp_getpeername(utp_socket *conn, struct sockaddr *addr, socklen_t *addrlen) diff --git a/utp_internal.h b/utp_internal.h index 0bd2c06..2cc7a9f 100644 --- a/utp_internal.h +++ b/utp_internal.h @@ -25,16 +25,17 @@ #include #include -#include #include +#include +#include +#include +#include #include #include "utp.h" #include "utp_callbacks.h" #include "utp_templates.h" -#include "utp_hash.h" -#include "utp_hash.h" #include "utp_packedsockaddr.h" /* These originally lived in utp_config.h */ @@ -63,7 +64,7 @@ enum bandwidth_type_t { struct PACKED_ATTRIBUTE RST_Info { RST_Info() = default; - RST_Info(PackedSockAddr _addr, uint32 _connid, uint16 _ack_nr, uint64 _timestamp) + RST_Info(PackedSockAddr _addr, uint32 _connid, uint16 _ack_nr, uint64 _timestamp) : addr{ _addr } , connid{ _connid } , ack_nr{ _ack_nr } @@ -77,12 +78,6 @@ struct PACKED_ATTRIBUTE RST_Info { uint64 timestamp; }; -// It's really important that we don't have duplicate keys in the hash table. -// If we do, we'll eventually crash. if we try to remove the second instance -// of the key, we'll accidentally remove the first instead. then later, -// checkTimeouts will try to access the second one's already freed memory. -void UTP_FreeAll(struct UTPSocketHT *utp_sockets); - struct UTPSocketKey { PackedSockAddr addr; uint32 recv_id; // "conn_seed", "conn_id" @@ -101,25 +96,66 @@ struct UTPSocketKey { } }; -struct UTPSocketKeyData { - UTPSocketKey key; - UTPSocket *socket; - utp_link_t link; +template<> +struct std::hash +{ + // It's really important that we don't have duplicate keys in the hash table. + // If we do, we'll eventually crash. if we try to remove the second instance + // of the key, we'll accidentally remove the first instead. then later, + // checkTimeouts will try to access the second one's already freed memory. + std::size_t operator()(const UTPSocketKey& key) const { + return key.compute_hash(); + } }; -#define UTP_SOCKET_BUCKETS 79 -#define UTP_SOCKET_INIT 15 +extern void utp_socket_delete(UTPSocket*); + +// Container that owns a set of UTPSockets. +// The sockets are destroyed when removed from the container. +class UTPSocketHT { + using Key = UTPSocketKey; + using Value = UTPSocket; + + // UTPSocket is an opaque type, so we can't use it for V. + // Instead, use a unique_ptr<> which deletes via `utp_socket_delete()`. + struct SocketDeleter { + void operator()(UTPSocket* conn) const noexcept { + utp_socket_delete(conn); + } + }; + using V = std::unique_ptr; + using Map = std::unordered_map; + +public: + [[nodiscard]] auto contains(Key const& key) const { + return map_.count(key) != 0U; + } -struct UTPSocketHT : utpHashTable { - UTPSocketHT() { - const int buckets = UTP_SOCKET_BUCKETS; - const int initial = UTP_SOCKET_INIT; - this->Create(buckets, initial); + [[nodiscard]] auto size() const { + return map_.size(); } - ~UTPSocketHT() { - UTP_FreeAll(this); - this->Free(); + + [[nodiscard]] Value* lookup(Key const& key) const { + const auto iter = map_.find(key); + return iter != map_.end() ? iter->second.get() : nullptr; } + + void add(Key const& key, Value* value) { + map_[key].reset(value); + } + + void erase_if(std::function pred) { + for (auto it = map_.begin(); it != map_.end(); ) { + if (pred(it)) { + it = map_.erase(it); + } else { + ++it; + } + } + } + +private: + Map map_; }; struct struct_utp_context { @@ -131,14 +167,13 @@ struct struct_utp_context { UTPSocket *last_utp_socket; std::vector ack_sockets; std::vector rst_info; - UTPSocketHT *utp_sockets; + UTPSocketHT utp_sockets; size_t target_delay; size_t opt_sndbuf; size_t opt_rcvbuf; uint64 last_check; struct_utp_context(); - ~struct_utp_context(); void log(int level, utp_socket *socket, char const *fmt, ...); void log_unchecked(utp_socket *socket, char const *fmt, ...);