summaryrefslogtreecommitdiff
path: root/src/routing.c.orig
diff options
context:
space:
mode:
authorJesper Jensen <jesper@jnsn.dev>2023-07-23 12:15:34 +0200
committerJesper Jensen <jesper@jnsn.dev>2025-04-12 10:21:20 +0200
commit47d4780eb448f839fc6b0644135395f879080ba4 (patch)
tree2c9a84bf45a4b8af5411331db50173fc5a34f630 /src/routing.c.orig
parent3515701479fe04f73e9194ee3074457bb85030f6 (diff)
I don't remember
Diffstat (limited to 'src/routing.c.orig')
-rw-r--r--src/routing.c.orig267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/routing.c.orig b/src/routing.c.orig
new file mode 100644
index 0000000..596548a
--- /dev/null
+++ b/src/routing.c.orig
@@ -0,0 +1,267 @@
+#include "routing.h"
+
+#include "log.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+// The DHT routing table has a keyspace of 0 -- 2^160 split into buckets of 8.
+// When a bucket becomes full, we split it in half. As we further expand the
+// routing table we only continue to split the buckets on the side we fall on.
+//
+// Initially, this may sound like a binary tree (because we split it in two),
+// but looking at it as a flat array leads to some interesting intuitions.
+// Since we only expand one half of the "tree", the total size is bounded by
+// the depth of the tree log2(2^160) == 160.
+//
+// As a flat array we notice the intrinsic properties of the routing table.
+// With a bucket size of 8, the routing table contains 160 * 8 == 1280 nodes.
+// As the node ids get less similar to our own our grouping of them becomes
+// less detailed. While the bucket we are in contains node very close to us,
+// the nodes furthest away from us are grouped in buckets with nodes they
+// barely resemble.
+//
+// +----------------------------+
+// | n1 | n2 | n3 | ... | n1280 |
+// +----------------------------+
+// More Less
+// <--------Similarity-------->
+// <----------Detail---------->
+//
+
+<<<<<<< HEAD
+struct table {
+ struct nodeid myID;
+ struct entry table[RT_SIZE];
+};
+
+struct table* pTable;
+=======
+#include<arpa/inet.h>
+
+#define IDBITS 160
+#define BUCKETSIZE 8
+// The 3 here is log2(BUCKETSIZE), since the final bucket will contain all those combinations
+#define BUCKETBITS 3
+#define ROUTINGSIZE (IDBITS * BUCKETSIZE)
+
+struct nodeid myID;
+struct entry table[ROUTINGSIZE];
+int table_size = ROUTINGSIZE;
+>>>>>>> 685b13e (I don't remember)
+
+void routing_init(struct nodeid* myid) {
+ pTable = malloc(sizeof(struct table));
+ pTable->myID = *myid;
+ routing_flush();
+}
+
+void routing_flush() {
+ memset(pTable->table, 0, sizeof(pTable->table));
+}
+
+// Calculate the common bit prefix between two node ids.
+static uint8_t prefix(struct nodeid* a, struct nodeid* b) {
+ uint8_t c = 0;
+ for(uint8_t i = 0; i < 5; i++) {
+ // Since the nodeids are stored in host byteorder in the words we have
+ // to make sure they're big endian before doing the prefix match,
+ // otherwise we end up with prefix matching that's different from the
+ // rest of the network
+ uint32_t word = htonl(a->inner[i]) ^ htonl(b->inner[i]);
+
+ // This word is different, find the location of the difference
+ if (word != 0)
+ return c + __builtin_clz(word);
+
+ // This word is completely the same
+ c += sizeof(word) * CHAR_BIT;
+ }
+
+ return c;
+}
+
+static int8_t scan(uint16_t baseIndex, struct nodeid* id) {
+ assert(baseIndex < RT_SIZE - RT_BSIZE);
+ int8_t index = -2;
+
+ for(size_t i = baseIndex; i < baseIndex + RT_BSIZE; i++) {
+ if(!pTable->table[i].set) {
+ index = index == -2 ? i - baseIndex : index;
+ continue;
+ }
+
+ if(memcmp(&pTable->table[i].id, id, sizeof(struct nodeid)) == 0) {
+ return -1;
+ }
+ }
+
+ return index;
+}
+
+static uint16_t base_bucket(struct nodeid* id) {
+ uint16_t bucketIndex = prefix(&pTable->myID, id);
+ assert(bucketIndex != RT_IDBITS);
+
+ // If they are sufficiently similar they end up in the final bucket. Clamp the index to ensure.
+ bucketIndex = bucketIndex > (RT_IDBITS - RT_BBITS) ? (RT_IDBITS - RT_BBITS) : bucketIndex;
+ assert(bucketIndex <= RT_IDBITS - RT_BBITS);
+
+ return bucketIndex * RT_BSIZE;
+}
+
+struct entry* routing_get(struct nodeid* id) {
+ uint16_t baseIndex = base_bucket(id);
+ for(size_t i = baseIndex; i < baseIndex + RT_BSIZE; i++) {
+ if(!pTable->table[i].set) continue;
+
+ if(memcmp(&pTable->table[i].id, id, sizeof(struct nodeid)) == 0) {
+ return &pTable->table[i];
+ }
+ }
+
+ return NULL;
+}
+
+void routing_remove(struct nodeid* id) {
+ struct entry* entry = routing_get(id);
+
+ entry->set = false;
+}
+
+bool routing_interested(struct nodeid* id) {
+ uint16_t bucketIndex = prefix(&pTable->myID, id);
+ // The nodeid is the same as our own
+ if(bucketIndex == RT_IDBITS) {
+ return false;
+ }
+
+ uint16_t baseIndex = base_bucket(id);
+ int8_t inBucketIndex = scan(baseIndex, id);
+
+ if(inBucketIndex < 0) {
+ // The bucket either already contains the node, or it has no more space
+ return false;
+ }
+
+ return true;
+}
+
+// Offer the routing table a new node
+bool routing_offer(struct nodeid* id, struct entry **dest) {
+ uint16_t bucketIndex = prefix(&pTable->myID, id);
+ // The nodeid is the same as our own
+ if(bucketIndex == RT_IDBITS) {
+ return false;
+ }
+
+ uint16_t baseIndex = base_bucket(id);
+ int8_t inBucketIndex = scan(baseIndex, id);
+
+ if(inBucketIndex < 0) {
+ // The bucket either already contains the node, or it has no more space
+ return false;
+ }
+
+ struct entry* entry = &pTable->table[baseIndex + inBucketIndex];
+ entry->set = true;
+ entry->id = *id;
+
+ *dest = entry;
+ return true;
+}
+
+struct item {
+ struct nodeid distance;
+ bool set;
+ uint16_t index;
+};
+int compareItem(const void* a_v, const void* b_v) {
+ struct item* a = (struct item*)a_v;
+ struct item* b = (struct item*)b_v;
+
+ // If either of the two are not set, the one that is set comes before the
+ // one that isn't.
+ if(!a->set || !b->set) return b->set - a->set;
+
+ return memcmp(&a->distance, &b->distance, sizeof(struct nodeid));
+}
+
+size_t routing_closest(struct nodeid* needle, size_t n, struct entry** res) {
+ assert(n <= RT_SIZE);
+ static struct item items[RT_SIZE] = {0};
+ for(uint16_t i = 0; i < RT_SIZE; i++) {
+ items[i].index = i;
+ }
+
+ {
+ struct item* item;
+ struct entry* entry;
+ for(item = &items[0], entry = &pTable->table[0]; item < &items[RT_SIZE] && entry < &pTable->table[RT_SIZE]; item++, entry++){
+ item->set = entry->set;
+ for(uint8_t j = 0; j < 5; j++) {
+ item->distance.inner[j] = entry->id.inner[j] ^ needle->inner[j];
+ }
+ }
+ }
+
+ // @PERFORMANCE: There's an algorithm known as quickselect which can select
+ // the top k elements from a list while only doing a partial sort.
+ // I imagine that would be more efficient than this full sort.
+ qsort(items, RT_SIZE, sizeof(struct item), compareItem);
+
+ size_t read;
+ for(read = 0; read < n; read++) {
+ if(!items[read].set)
+ break;
+ res[read] = &pTable->table[items[read].index];
+ }
+
+ return read;
+}
+
+void routing_oldest(struct entry** dest) {
+ *dest = NULL;
+
+ for(struct entry* entry = pTable->table; entry < pTable->table+RT_SIZE; entry++){
+ if(!entry->set)
+ continue;
+
+ if(entry->expire == 0)
+ continue;
+
+ if(*dest == NULL) {
+ *dest = entry;
+ continue;
+ }
+
+ if(difftime((*dest)->expire, entry->expire) > 0.0) {
+ *dest = entry;
+ }
+ }
+}
+
+void routing_status(int* filled, int* size, double* load_factor, size_t load_factor_len) {
+ *size = RT_SIZE;
+
+ *filled = 0;
+ for(size_t i = 0; i < RT_SIZE; i++) {
+ if(pTable->table[i].set)
+ (*filled)++;
+ }
+
+ int per_bucket = RT_SIZE / load_factor_len;
+ int overflow = RT_SIZE % load_factor_len;
+ struct entry* table_cursor = pTable->table;
+ for(int i = 0; i < load_factor_len; i++) {
+ int is_overflow = i < overflow;
+ for(int j = 0; j < per_bucket + is_overflow; j++) {
+ load_factor[i] += table_cursor->set;
+ table_cursor++;
+ }
+ load_factor[i] /= per_bucket + is_overflow;
+ }
+}