summaryrefslogtreecommitdiff
path: root/src/peers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/peers.c')
-rw-r--r--src/peers.c53
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);
+ }
+}