diff options
Diffstat (limited to 'src/peers.c')
| -rw-r--r-- | src/peers.c | 53 |
1 files changed, 49 insertions, 4 deletions
diff --git a/src/peers.c b/src/peers.c index 55f1f79..781bc7e 100644 --- a/src/peers.c +++ b/src/peers.c @@ -1,6 +1,7 @@ #include "peers.h" #include "log.h" +#include "metrics.h" #include <stdlib.h> #include <string.h> @@ -24,8 +25,11 @@ int allocate_hashtable() { } static uint64_t hash(struct infohash* key, size_t size) { - uint64_t hash = (key->inner[4] << 4) | key->inner[3]; - return hash % size; + uint64_t x = 5381; + for(uint8_t i = 0; i < sizeof(key->inner_b); i++) { + x = ((x << 5) + x) + key->inner_b[i]; + } + return x % size; } static void find(struct peer_entry* table, size_t size, struct infohash* infohash, struct peer_entry** entry) { @@ -71,7 +75,7 @@ uint64_t next_pow2(uint64_t x) { return 1 << (64 - leading); } -int add_peer(struct infohash* infohash, struct addr* peer) { +int add_peer(struct infohash* infohash, struct addr* peer, time_t now) { assert(peer_table_load < peer_table_size); if(load_factor(peer_table_size, peer_table_load + 1) > 0.75) { @@ -85,10 +89,13 @@ int add_peer(struct infohash* infohash, struct addr* peer) { if(!entry->set) { entry->set = true; entry->key = *infohash; + entry->last_seen = now; peer_table_load++; peer_status.hashes++; + prom_counter_inc(hashes, NULL); } + entry->last_seen = now; size_t peern = entry->value_len; if(peern == PEERS_PER_HASH) return PEER_EFULL; @@ -96,11 +103,12 @@ int add_peer(struct infohash* infohash, struct addr* peer) { entry->value[peern] = *peer; entry->value_len++; peer_status.peers++; + prom_counter_inc(peers, NULL); return 0; } -void get_peers(struct infohash* infohash, struct addr *peers[PEERS_PER_HASH], size_t *peers_len) { +void get_peers(struct infohash* infohash, struct addr **peers, size_t *peers_len) { struct peer_entry* entry; find(peer_table, peer_table_size, infohash, &entry); @@ -113,3 +121,40 @@ void get_peers(struct infohash* infohash, struct addr *peers[PEERS_PER_HASH], si *peers = entry->value; *peers_len = entry->value_len; } + +void expire_hashes(time_t now) { + assert(peer_table_load < peer_table_size); + for(size_t i = 0; i < peer_table_size; i++) { + struct peer_entry *entry = &peer_table[i]; + if(!entry->set) continue; + + if(difftime(now, entry->last_seen + HASH_TIMEOUT) < 0.0) continue; + + // Find the last hash that collides with us + uint64_t entry_hash = hash(&entry->key, peer_table_size); + size_t last_in_slot = i; + while(true) { + size_t next = (last_in_slot + 1) % peer_table_size; + assert(next != i); + + // There can't be holes in the chain + if(!peer_table[next].set) break; + + // If the two hashes are different we've reached the end of the probe chain + uint64_t next_hash = hash(&peer_table[next].key, peer_table_size); + if(next_hash != entry_hash) break; + + last_in_slot = next; + } + + // If some hashes were chained on us, we copy the last one into our slot + if(last_in_slot != i) { + *entry = peer_table[last_in_slot]; + entry = &peer_table[last_in_slot]; + } + + // Remove the slot + entry->set = false; + prom_counter_inc(hash_expired, NULL); + } +} |
