summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile20
-rw-r--r--src/routing.c14
-rw-r--r--src/routing.h3
-rw-r--r--test/routing.c91
5 files changed, 113 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore
index f8a1367..bcbc3f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
compile_commands.json
obj/
/dht
+*.gcov
diff --git a/Makefile b/Makefile
index 0c1d5ff..4f25977 100644
--- a/Makefile
+++ b/Makefile
@@ -8,10 +8,9 @@ OBJDIR ?= obj
LIBS = -lm
INCS = -Isrc/ -Igen/ -I.
-CFG = -std=gnu11 -fms-extensions -flto
-
-CFLAGS ?= -O3 -D_FORTIFY_SOURCE=2
-CFLAGS += -Wall
+CFLAGS ?= -O3 -D_FORTIFY_SOURCE=2 -Wall
+# add all the required Cflags
+CFLAGS += -std=gnu11 -fms-extensions -flto
APP_MAIN_SOURCES = src/main.c
APP_SOURCES = $(filter-out $(APP_MAIN_SOURCES),$(shell find $(SRCDIR) -name "*.c"))
@@ -35,18 +34,19 @@ print-% : ; @echo $* = $($*)
# We don't really need to run the tests for bear to record them
compile_commands.json: clean Makefile $(APP_SOURCES) $(TEST_LIB_SOURCES) $(TEST_SOURCES)
+ @rm -f "$@"
bear -- make $(TEST_EXES) dht
dht: $(APP_MAIN_OBJS) $(APP_OBJS)
- $(CC) $(CFG) $(CPPFLAGS) $(LDFLAGS) $(CFLAGS) -o $@ $(APP_MAIN_OBJS) $(APP_OBJS) $(LIBS)
+ $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(APP_MAIN_OBJS) $(APP_OBJS) $(LIBS)
$(OBJDIR)/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFG) $(CPPFLAGS) $(CFLAGS) $(INCS) -MMD -o $@ -c $<
+ $(CC) $(CFLAGS) $(INCS) -MMD -o $@ -c $<
clean:
@rm -rf $(OBJDIR)
- @rm -f dht .compile_commands.json
+ @rm -f dht
.PHONY: version
version:
@@ -60,7 +60,7 @@ test: $(TEST_EXES)
$(foreach test,$(TEST_EXES),./$(test);)
$(OBJDIR)/test/%: $(APP_OBJS) $(TEST_LIB_OBJS) $(OBJDIR)/test/%.o $(OBJDIR)/test/%.runner.o
- $(CC) $(CFG) $(CPPFLAGS) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(LIBS)
+ $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $^ $(LIBS)
$(OBJDIR)/test/%.runner.c: test/%.c
@mkdir -p $(dir $@)
@@ -69,12 +69,12 @@ $(OBJDIR)/test/%.runner.c: test/%.c
# Test code needs test includes
$(OBJDIR)/test/%.o: test/%.c
@mkdir -p $(dir $@)
- $(CC) $(CFG) $(CPPFLAGS) $(CFLAGS) $(TEST_LIB_INCS) $(INCS) -MMD -o $@ -c $<
+ $(CC) $(CFLAGS) $(TEST_LIB_INCS) $(INCS) -MMD -o $@ -c $<
# Generated test sources are located under obj
$(OBJDIR)/test/%.o: $(OBJDIR)/test/%.c
@mkdir -p $(dir $@)
- $(CC) $(CFG) $(CPPFLAGS) $(CFLAGS) $(TEST_LIB_INCS) $(INCS) -MMD -o $@ -c $<
+ $(CC) $(CFLAGS) $(TEST_LIB_INCS) $(INCS) -MMD -o $@ -c $<
.DEFAULT_GOAL := all
all: test dht
diff --git a/src/routing.c b/src/routing.c
index 89fa800..d4eaa3f 100644
--- a/src/routing.c
+++ b/src/routing.c
@@ -19,6 +19,10 @@ void routing_init(struct nodeid* myid) {
myID = *myid;
}
+void routing_flush() {
+ memset(table, 0, sizeof(table));
+}
+
// Calculate the common bit prefix between two node ids.
uint8_t prefix(struct nodeid* a, struct nodeid* b) {
uint8_t c = 0;
@@ -58,7 +62,9 @@ int8_t scan(uint16_t baseIndex, struct nodeid* id) {
bool routing_offer(struct nodeid* id, struct entry **dest) {
uint16_t bucketIndex = prefix(&myID, id);
// The nodeid is the same as our own
- assert(bucketIndex != IDBITS);
+ if(bucketIndex == IDBITS) {
+ return false;
+ }
// If they are sufficiently similar they end up in the final bucket. Clamp the index to ensure.
bucketIndex = bucketIndex > (IDBITS - BUCKETBITS) ? (IDBITS - BUCKETBITS) : bucketIndex;
@@ -69,7 +75,6 @@ bool routing_offer(struct nodeid* id, struct entry **dest) {
if(inBucketIndex == -1) {
// The bucket either already contains the node, or it has no more space
- dbg("discarding node");
return false;
}
@@ -97,7 +102,7 @@ int compareItem(const void* a_v, const void* b_v) {
return memcmp(&a->distance, &b->distance, sizeof(struct nodeid));
}
-size_t rounting_closest(struct nodeid* needle, size_t n, struct entry** res) {
+size_t routing_closest(struct nodeid* needle, size_t n, struct entry** res) {
assert(n <= ROUTINGSIZE);
static struct item items[ROUTINGSIZE] = {0};
for(uint16_t i = 0; i < ROUTINGSIZE; i++) {
@@ -115,6 +120,9 @@ size_t rounting_closest(struct nodeid* needle, size_t n, struct entry** res) {
}
}
+ // @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, ROUTINGSIZE, sizeof(struct item), compareItem);
size_t read;
diff --git a/src/routing.h b/src/routing.h
index dc40aa9..b0e9922 100644
--- a/src/routing.h
+++ b/src/routing.h
@@ -35,5 +35,6 @@ struct entry {
};
void routing_init(struct nodeid* myid);
+void routing_flush();
bool routing_offer(struct nodeid* id, struct entry **dest);
-size_t rounting_closest(struct nodeid* needle, size_t n, struct entry** res);
+size_t routing_closest(struct nodeid* needle, size_t n, struct entry** res);
diff --git a/test/routing.c b/test/routing.c
index adb424d..c4b7730 100644
--- a/test/routing.c
+++ b/test/routing.c
@@ -1,6 +1,93 @@
#include "unity.h"
#include "routing.h"
-void test_rt_add() {
- TEST_PASS();
+#define IP(a, b, c, d) (a << 24 | b << 16 | c << 8 | d)
+
+struct nodeid self;
+
+void setUp() {
+ self = (struct nodeid){{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }};
+ routing_init(&self);
+}
+
+void test_deny_self_add() {
+ routing_flush();
+ struct entry* entry;
+ if(!routing_offer(&self, &entry))
+ TEST_PASS();
+
+ TEST_FAIL();
+}
+
+void test_can_find_added() {
+ routing_flush();
+
+ // The address we are going to store
+ struct addr addr = (struct addr){.ip = IP(128,0,0,1), .port = 0};
+
+ // Make a nodeid that is one bit different
+ struct nodeid new = self;
+
+ new.inner[4] += 1;
+ struct entry* entry;
+ TEST_ASSERT_TRUE_MESSAGE(routing_offer(&new, &entry), "Did not accept new entry");
+ new.inner[4] += 1;
+ TEST_ASSERT_TRUE_MESSAGE(routing_offer(&new, &entry), "Did not accept new entry");
+
+ // Set the entries
+ entry->addr = addr;
+ entry->last = time(NULL);
+
+ // Ask for 3 nodes
+ struct entry *out[3] = {0};
+ size_t n = routing_closest(&new, 3, out);
+ // Since we only put 2 in, we should get 2 out
+ TEST_ASSERT_EQUAL_INT(2, n);
+
+ TEST_ASSERT_EQUAL_MEMORY(&addr, &out[0]->addr, sizeof(struct addr));
+ TEST_ASSERT_EQUAL_MEMORY(&new, &out[0]->id, sizeof(struct nodeid));
+}
+
+void test_discard_offer_when_bucket_full() {
+ routing_flush();
+
+ // The address we are going to store
+ struct addr addr = (struct addr){.ip = IP(128,0,0,1), .port = 0};
+
+ struct nodeid new = self;
+ // Flip the top bit of the id to go into the low resolution bucket
+ new.inner[0] ^= 0x80000000;
+
+ // Fill up the bucket with entries
+ for(uint8_t i = 0; i < 8; i++) {
+ struct entry* entry;
+ TEST_ASSERT_TRUE_MESSAGE(routing_offer(&new, &entry), "Did not accept new entry");
+
+ // Set the entries
+ entry->addr = addr;
+ entry->last = time(NULL);
+
+ new.inner[4] += 1;
+ }
+
+ struct entry* entry;
+ TEST_ASSERT_FALSE_MESSAGE(routing_offer(&new, &entry), "Accepted entry when bucket was full");
+}
+
+void test_discard_offer_when_nodeid_added_twice() {
+ routing_flush();
+
+ // The address we are going to store
+ struct addr addr = (struct addr){.ip = IP(128,0,0,1), .port = 0};
+
+ struct nodeid new = self;
+ new.inner[4] ^= 0x00000001;
+
+ struct entry* entry;
+ TEST_ASSERT_TRUE(routing_offer(&new, &entry));
+ entry->addr = addr;
+ entry->last = time(NULL);
+
+ bool accept = routing_offer(&new, &entry);
+ TEST_ASSERT_FALSE_MESSAGE(accept, "Accepted entry when bucket was full");
}