summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Van Doorn <vandoorn.nick@gmail.com>2019-02-23 02:19:04 -0800
committerNick Van Doorn <vandoorn.nick@gmail.com>2019-02-23 02:19:04 -0800
commit93ff075b775e77d83e8f0dbcd19e7d2aa81a43f8 (patch)
treefcc785e6b45d153dc786ae4ecd721636748c3ca6
parent8ace2558f882c0298d02a50ffebb3aacf639407b (diff)
Holy fuck my life hurts
-rw-r--r--Makefile8
-rw-r--r--core.c24
-rw-r--r--core.test.c7
-rw-r--r--hash-table.c152
-rw-r--r--hash-table.h29
-rw-r--r--hash-table.test.c182
-rw-r--r--hash-table.test.h6
-rwxr-xr-xnaive-watch.sh7
-rw-r--r--parser.c25
-rw-r--r--parser.h2
-rw-r--r--server.h3
11 files changed, 427 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index 4979003..aed0b8c 100644
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,8 @@
CC=gcc
CFLAGS=-I. -I./mongoose -I./pool -DTHPOOL_DEBUG -pthread
OBJDIR = build
-DEPS= parser.h server.h test-lib.h core.h graph.h graph.test.h pool/thpool.h hash-table.h
-OBJ = parser.o server.o test-lib.o core.o graph.o graph.test.o pool/thpool.o hash-table.o
+DEPS= parser.h server.h test-lib.h core.h graph.h graph.test.h pool/thpool.h hash-table.h hash-table.test.h cjson/cJSON.h cjson/cJSON_Utils.h
+OBJ = parser.o server.o test-lib.o core.o graph.o graph.test.o pool/thpool.o hash-table.o hash-table.test.o cjson/cJSON.o cjson/cJSON_Utils.o
MAIN_OBJ = main.o
TEST_OBJ = core.test.o
@@ -14,12 +14,12 @@ TEST_OBJ = core.test.o
main: $(OBJ) $(MAIN_OBJ)
$(CC) -o $@ $^ $(CFLAGS)
-test-bin: $(OBJ) $(TEST_OBJ)
+cnaked-test-bin: $(OBJ) $(TEST_OBJ)
$(CC) -o $@ $^ $(CFLAGS)
.PHONY: test
test:
- make test-bin && ./test-bin
+ make cnaked-test-bin && ./cnaked-test-bin
.PHONY: clean
clean:
diff --git a/core.c b/core.c
index fec7265..1745235 100644
--- a/core.c
+++ b/core.c
@@ -8,6 +8,10 @@
#define DEFAULT_PORT 5000
+const static char *HEAD_TYPE = "bendr";
+const static char *TAIL_TYPE = "pixel";
+const static char *SNAKE_COLOUR = "#ff00ff";
+
struct StateNode {
struct GameState_t state;
// Track the number of connected
@@ -23,8 +27,7 @@ void parseArgs(struct server_Ctx_t *ctx, int argc, char *argv[]) {
printf("Usage: ./cnaked PORT\n");
exit(1);
}
- int port = atoi(argv[0]);
- ctx->port = port == 0 ? DEFAULT_PORT : port;
+ ctx->port = argv[0];
}
// TODO I have no idea how this function
@@ -44,10 +47,10 @@ int remakeGameState(struct GameState_t *oldState, struct GameState_t *newState,
return -1;
}
-int gameMoveToJson(enum GameMove_t *res, char *buff, int buffSize) {
+int gameMoveToJson(enum GameMove_t *move, char *buff, int buffSize) {
char *moveStr;
// save a byte, pass a pointer
- switch ((*res)) {
+ switch ((*move)) {
case UP:
moveStr = "up";
break;
@@ -65,6 +68,12 @@ int gameMoveToJson(enum GameMove_t *res, char *buff, int buffSize) {
return snprintf(buff, buffSize, "{\"move\":\"%s\"}", moveStr);
}
+int startJson(char *buff, int buffSize) {
+ return snprintf(buff, 1024,
+ "{\"color\":\"%s\",\"headType\":\"%s\",\"tailType\":\"%s\"}",
+ SNAKE_COLOUR, HEAD_TYPE, TAIL_TYPE);
+}
+
void moveHandler(struct server_Req_t *req, struct server_Res_t *res,
void *ctx) {
char buff[300];
@@ -84,7 +93,12 @@ void moveHandler(struct server_Req_t *req, struct server_Res_t *res,
void startHandler(struct server_Req_t *req, struct server_Res_t *res,
void *ctx) {
- res->write("{}", HTTP_OK);
+ char buff[1024];
+ int r = startJson(buff, 1024);
+ if (r)
+ res->write("Ya blew it", HTTP_INTERNAL_SERVER_ERR);
+ else
+ res->write(buff, HTTP_OK);
}
int core_setup(int argc, char *argv[]) {
diff --git a/core.test.c b/core.test.c
index 7be1e20..f51c907 100644
--- a/core.test.c
+++ b/core.test.c
@@ -1,6 +1,7 @@
#include "core.h"
#include "game-state.h"
#include "graph.test.h"
+#include "hash-table.test.h"
#include "test-lib.h"
#include <string.h>
@@ -13,6 +14,12 @@ int testMoveToJson() {
}
int main() {
+ // core tests written here
syncTest("gameMoveToJson", "JSON payload is incorrect", testMoveToJson);
+
+ // Tests included from other lib
+ // graph.test.c
graph_test();
+ // hash-table.test.c
+ hash_test();
}
diff --git a/hash-table.c b/hash-table.c
index 4375d15..6f4f048 100644
--- a/hash-table.c
+++ b/hash-table.c
@@ -1,7 +1,153 @@
#include "hash-table.h"
+#include <pthread.h>
+#include <stdlib.h>
-int hash_insert(struct hash_Table_t *t, int key) { return -1; }
+int computeHashIndex(int key) {
+ int r = key % HASH_TABLE_SIZE;
+ return r;
+}
-int hash_remove(struct hash_Table_t *t, int key) { return -1; }
+struct hash_Node_t *linearListSearch(struct hash_Node_t *node, int key) {
+ while (node->next != NULL) {
+ if (node->key == key)
+ return node;
+ node = (struct hash_Node_t *)node->next;
+ }
+ return NULL;
+}
-int hash_contains(struct hash_Table_t *t, int key) { return -1; }
+int hash_init(struct hash_Table_t *t) {
+ // mark all node "slots" as NULL initially
+ for (int i = 0; i < HASH_TABLE_SIZE; i++) {
+ t->nodes[i] = NULL;
+ }
+ t->nNodes = 0;
+ return pthread_mutex_init(&t->lock, NULL);
+}
+
+// we don't _need_ a malloc because
+// in theory we could predictably insert one stattically
+// and then dynamically link all collisions, assuming
+// the table is large enough
+int hash_insert(struct hash_Table_t *t, int key) {
+ int r, index;
+ struct hash_Node_t *node, *slot;
+ r = pthread_mutex_lock(&t->lock);
+ if (r)
+ return r;
+
+ index = computeHashIndex(key);
+ slot = t->nodes[index];
+
+ if (slot != NULL && slot->key == key) {
+ return DUPLICATE_KEY_ERR;
+ }
+
+ node = malloc(sizeof(struct hash_Node_t));
+ node->key = key;
+ node->next = NULL;
+ node->prev = NULL;
+
+ // easy case
+ if (slot == NULL) {
+ t->nodes[index] = node;
+ } // harder-ish case
+ else {
+ while (slot->next != NULL) {
+ slot = (struct hash_Node_t *)slot->next;
+ }
+ slot->next = node;
+ node->prev = slot;
+ }
+ t->nNodes++;
+ if (t->nNodes < 0) {
+ printf("%d\n", t->nNodes);
+ }
+ r = pthread_mutex_unlock(&t->lock);
+ return r;
+}
+
+// TODO free da node
+int hash_remove(struct hash_Table_t *t, int key) {
+ int r, hashKey;
+ struct hash_Node_t *node;
+ r = pthread_mutex_lock(&t->lock);
+ if (r)
+ return r;
+ hashKey = computeHashIndex(key);
+ node = t->nodes[hashKey];
+ // could not find it case
+ if (node == NULL) {
+ return KEY_DOES_NOT_EXIST;
+ }
+
+ // Note that all error exits should
+ // try and unlock it first
+ // direct hit case
+ else if (node->key == key) {
+ // move up the next node
+ if (node->next != NULL) {
+ t->nodes[hashKey] = node->next;
+ node->prev = NULL;
+ }
+ // empty it out case
+ else {
+ t->nodes[hashKey] = NULL;
+ }
+ }
+ // linear probing case
+ else {
+ node = linearListSearch(node, key);
+ if (node == NULL) {
+ pthread_mutex_unlock(&t->lock);
+ return KEY_DOES_NOT_EXIST;
+ }
+ if (node->next != NULL) {
+ // we assume this node
+ // must have a previous
+ // since we had to search to find it
+ struct hash_Node_t *newNext = node->next;
+ struct hash_Node_t *newPrev = node->prev;
+ newPrev->next = newNext;
+ newNext->prev = newPrev;
+ }
+ }
+ // This should never happen.
+ // If we have nothing to free,
+ // we should have exited from
+ // an error before hitting this
+ if (node == NULL) {
+ pthread_mutex_unlock(&t->lock);
+ return FREE_EMPTY_NODE;
+ }
+ free(node);
+ t->nNodes--;
+ r = pthread_mutex_unlock(&t->lock);
+ return r;
+}
+
+int hash_contains(struct hash_Table_t *t, int key) {
+ int r, hashKey, contains;
+ r = pthread_mutex_lock(&t->lock);
+ if (r)
+ return r;
+
+ hashKey = computeHashIndex(key);
+ if (t->nodes[hashKey] == NULL) {
+ contains = 0;
+ goto done;
+ } else {
+ if (t->nodes[hashKey]->key == key) {
+ contains = 1;
+ goto done;
+ } else {
+ contains = linearListSearch(t->nodes[hashKey], key) != NULL;
+ goto done;
+ }
+ }
+done:
+ r = pthread_mutex_unlock(&t->lock);
+ if (r)
+ return r;
+ return contains;
+}
diff --git a/hash-table.h b/hash-table.h
index 16e4abb..5c081e2 100644
--- a/hash-table.h
+++ b/hash-table.h
@@ -4,19 +4,48 @@
#include <pthread.h>
#define DUPLICATE_KEY_ERR -1
+#define KEY_DOES_NOT_EXIST -2
+#define FREE_EMPTY_NODE -3
+
+#define HASH_TABLE_SIZE 4096 * 1024
+
+struct hash_Node_t {
+ int key;
+ void *next;
+ void *prev;
+};
struct hash_Table_t {
// lock the hash table
// to ensure it's thread safe
pthread_mutex_t lock;
+ struct hash_Node_t *nodes[HASH_TABLE_SIZE];
+ int nNodes;
};
int hash_init(struct hash_Table_t *t);
+/**
+ * Insert an integer value into a hash table
+ *
+ * This routine does modify the struct but uses
+ * a mutex to maintain thread safety
+ */
int hash_insert(struct hash_Table_t *t, int key);
+/**
+ * Remove an integer value
+ *
+ * Also threadsafe
+ */
int hash_remove(struct hash_Table_t *t, int key);
+/**
+ * Check if the table contains a value
+ *
+ * _Should_ be safe from thread related problems
+ * here since it's strictly a read
+ */
int hash_contains(struct hash_Table_t *t, int key);
#endif
diff --git a/hash-table.test.c b/hash-table.test.c
new file mode 100644
index 0000000..d0a0b5e
--- /dev/null
+++ b/hash-table.test.c
@@ -0,0 +1,182 @@
+#include "hash-table.h"
+#include "hash-table.test.h"
+#include "test-lib.h"
+#include <pthread.h>
+#include <stdio.h>
+
+int hashInit() {
+ struct hash_Table_t t;
+ int r;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ for (int i = 0; i < HASH_TABLE_SIZE; i++) {
+ if (t.nodes[i] != NULL)
+ return -1;
+ }
+ // grab a lock to make sure the
+ // mutex is valid
+ return pthread_mutex_lock(&t.lock);
+}
+
+int hashInsert() {
+ struct hash_Table_t t;
+ int r;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ r = hash_insert(&t, 42);
+ return r;
+}
+
+int hashRemove() {
+ struct hash_Table_t t;
+ int r, big;
+ big = 100000;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ for (int i = 0; i < big; i++) {
+ r = hash_insert(&t, i);
+ if (r)
+ return r;
+ }
+ for (int i = 0; i < big; i++) {
+ r = hash_remove(&t, i);
+ if (r) {
+ return r;
+ }
+ }
+ return t.nNodes != 0;
+}
+
+int hashContains() {
+ struct hash_Table_t t;
+ int r;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ r = hash_insert(&t, 42);
+ if (r)
+ return r;
+ r = hash_contains(&t, 42);
+ return r != 1;
+}
+
+int hashContainsNotFound() {
+ struct hash_Table_t t;
+ int r, big;
+ big = 10000;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ for (int i = 0; i < big; i++) {
+ r = hash_insert(&t, i);
+ if (r)
+ return r;
+ }
+ r = hash_contains(&t, big + 1);
+ return r;
+}
+
+int hashContainsBigFound() {
+ struct hash_Table_t t;
+ int r, big;
+ big = 10000;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ for (int i = 0; i < big; i++) {
+ r = hash_insert(&t, i);
+ if (r)
+ return r;
+ }
+ r = hash_contains(&t, big / 2);
+ return r != 1;
+}
+
+void *hashInsertThreaded_handler(void *ctx) {
+ struct hash_Table_t *t = ctx;
+ for (int i = 0; i < 100000; i++) {
+ hash_insert(t, 100000 + i);
+ }
+ return NULL;
+}
+
+int hashInsertThreaded() {
+ pthread_t thread;
+ struct hash_Table_t t;
+ int r;
+ r = pthread_create(&thread, NULL, hashInsertThreaded_handler, &t);
+ if (r)
+ return r;
+
+ r = hash_init(&t);
+ if (r)
+ return r;
+ for (int i = 0; i < 100000; i++) {
+ r = hash_insert(&t, i);
+ if (r)
+ return r;
+ }
+ return pthread_join(thread, NULL);
+}
+
+int hashInsertDupe() {
+ struct hash_Table_t t;
+ int r;
+ r = hash_init(&t);
+ if (r)
+ return r;
+ r = hash_insert(&t, 42);
+ if (r)
+ return r;
+ r = hash_insert(&t, 42);
+ return r != DUPLICATE_KEY_ERR;
+}
+
+void *hailMary_handler(void *ctx) {
+ struct hash_Table_t *t = ctx;
+ int r;
+ for (int i = 0; i < 100000; i++) {
+ r = hash_insert(t, i);
+ }
+}
+
+int hailMary() {
+ pthread_t thread;
+ struct hash_Table_t t;
+ int r;
+
+ r = hash_init(&t);
+ if (r)
+ return r;
+ r = pthread_create(&thread, NULL, hailMary_handler, &t);
+ if (r)
+ return r;
+ r = pthread_join(thread, NULL);
+ printf("%d\n", t.nNodes);
+ if (r)
+ return r;
+ for (int i = 0; i < 100000; i++) {
+ r = hash_remove(&t, i);
+ if (r)
+ return r;
+ }
+}
+
+int hash_test() {
+ syncTest("hash_init", "Hash init success path failed", hashInit);
+ syncTest("hash_insert", "Hash insert success path failed", hashInsert);
+ syncTest("hash_remove", "Hash remove success path failed", hashRemove);
+ syncTest("hash_contains", "Hash contains success path failed", hashContains);
+ syncTest("hash_contains big", "Hash contains \"big\" success path failed",
+ hashContainsBigFound);
+ syncTest("hash_contains not found", "Hash contains not found failed",
+ hashContainsNotFound);
+ // syncTest("hash_insert multithread", "Hash insert multithread failed",
+ // hashInsertThreaded);
+ syncTest("hash_insert dupe", "Hash insert did not return correct error",
+ hashInsertDupe);
+ syncTest("Hail Mary", "Not shocking", hailMary);
+}
diff --git a/hash-table.test.h b/hash-table.test.h
new file mode 100644
index 0000000..8005b07
--- /dev/null
+++ b/hash-table.test.h
@@ -0,0 +1,6 @@
+#ifndef HASH_TABLE_TEST
+#define HASH_TABLE_TEST
+
+int hash_test();
+
+#endif
diff --git a/naive-watch.sh b/naive-watch.sh
index 80a53a2..f207376 100755
--- a/naive-watch.sh
+++ b/naive-watch.sh
@@ -1,10 +1,9 @@
+#!/usr/bin/env bash
+
test() {
- # Unit tests implemented in C
make test
- # Integration tests implemented
- # with node-fetch/jest
- # yarn jest
}
+
test
while sleep 5; do test; done
diff --git a/parser.c b/parser.c
index b0e949c..7559bfe 100644
--- a/parser.c
+++ b/parser.c
@@ -1,4 +1,27 @@
+#include "cjson/cJSON.h"
#include "game-state.h"
#include "parser.h"
-int parser_stringToState(char *reqBody, struct GameState_t *state) { return 0; }
+int parseYou(cJSON *you, struct GameState_t *state) {
+ int i = 0;
+ cJSON *health, *body, *coord, *x, *y;
+ health = cJSON_GetObjectItemCaseSensitive(you, "health");
+ body = cJSON_GetObjectItemCaseSensitive(you, "body");
+ state->ourHealth = health->valueint;
+ cJSON_ArrayForEach(coord, body) {
+ x = cJSON_GetObjectItemCaseSensitive(coord, "x");
+ y = cJSON_GetObjectItemCaseSensitive(coord, "y");
+ state->ourBody[i][0] = x->valueint;
+ state->ourBody[i++][1] = y->valueint;
+ };
+}
+
+int parser_stringToState(char *reqBody, struct GameState_t *state) {
+ cJSON *reqMonitor = cJSON_parse(reqBody);
+ cJSON *you;
+ if (reqMonitor == NULL) {
+ return JSON_PARSE_FAIL;
+ }
+ you = cJSON_GetObjectItemCaseSensitive(reqMonitor, "you");
+ parseYou(you, state);
+}
diff --git a/parser.h b/parser.h
index ffb2ff3..3b5f7fa 100644
--- a/parser.h
+++ b/parser.h
@@ -3,6 +3,8 @@
#include "game-state.h"
+#define JSON_PARSE_FAIL -1
+
int parser_stringToState(char *reqBody, struct GameState_t *state);
#endif
diff --git a/server.h b/server.h
index 42e7422..353e93a 100644
--- a/server.h
+++ b/server.h
@@ -2,9 +2,10 @@
#define CNAKE_SERVER_H
#define HTTP_OK 200
+#define HTTP_INTERNAL_SERVER_ERR 500
struct server_Ctx_t {
- int port;
+ char *port;
};
struct server_Req_t {