init commit

This commit is contained in:
Andrew Gundersen 2022-05-02 08:00:44 -05:00
commit 9904bd2212
16 changed files with 846 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.o

1
README.md Normal file
View file

@ -0,0 +1 @@
Addon Libraries to C Programming Language

21
hashtable/Makefile Normal file
View file

@ -0,0 +1,21 @@
CC=gcc
CFLAGS=-Wall -Wextra
OBJS=test.o hashtable.o llist.o
all: test
test: $(OBJS)
gcc -o $@ $^
test.o: test.c http.h
hashtable.o: hashtable.c hashtable.h
llist.o: llist.c llist.h
clean:
rm -f $(OBJS)
rm -f test
.PHONY: all, clean

153
hashtable/hashtable.c Normal file
View file

@ -0,0 +1,153 @@
/**
* hashtable.c - A basic hashtable implementation
*
*/
#include <string.h>
#include <stdlib.h>
#include "llist.h"
#include "hashtable.h"
#define SIZE 128
typedef struct
{
void* key;
void* data;
}
HTEntry;
/**
* Simple hash function used
*/
int hash(char* key, int ht_size)
{
int h = 0;
// basic modulo hash
for (size_t i = 0; i < strlen(key); i++)
{
h = (31 * h + key[i]) % ht_size;
}
return h;
}
/**
* Compare function for searching a bucket
*/
int compare(void* data, void* arg)
{
char* key = arg;
HTEntry* entry = data;
return strcmp(entry->key, key);
}
/**
* Delete HT entry from bucket
*/
int delete_entry(void* data, void* arg)
{
(void) arg;
HTEntry* entry = data;
free(entry);
return 0;
}
/**
* Create a new hashtable
*/
HT* ht_create(void)
{
HT* ht = malloc(sizeof(HT));
ht->size = SIZE;
ht->entries = 0;
ht->store = malloc(ht->size * sizeof(LL*));
for (int i = 0; i < ht->size; i++)
{
ht->store[i] = ll_create();
}
return ht;
}
/**
* Add a new key value pair to a hashtable
*/
int ht_put(HT* ht, char* key, void* data)
{
int addr = hash(key, ht->size);
LL* bucket = ht->store[addr];
HTEntry* entry = malloc(sizeof(HTEntry));
entry->key = strdup(key);
entry->data = data;
ll_push(bucket, entry);
ht->entries++;
return 0;
}
/**
* Get a value from a hashtable with a key
*/
void* ht_get(HT* ht, char* key)
{
int addr = hash(key, ht->size);
LL* bucket = ht->store[addr];
HTEntry* entry = ll_search(bucket, compare, (void*) key);
if (entry == NULL)
{
return NULL;
}
return entry->data;
}
/**
* Delete a key value pair from a hashtable
*/
void* ht_delete(HT* ht, char* key)
{
int addr = hash(key, ht->size);
LL* bucket = ht->store[addr];
HTEntry* entry = ll_delete(bucket, compare, key);
if (entry == NULL)
{
return NULL;
}
ht->entries--;
return entry->data;
}
/**
* Destroy/free an entire hashtable
*/
int ht_free(HT* ht)
{
for (int i = 0; i < ht->size; i++)
{
LL* bucket = ht->store[i];
ll_loop(bucket, delete_entry, NULL);
ll_free(bucket);
}
free(ht);
return 0;
}

24
hashtable/hashtable.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef _HASHTABLE_H_
#define _HASHTABLE_H_
#include "llist.h"
typedef struct
{
int size;
int entries;
LL** store;
}
HT;
HT* ht_create(void);
int ht_put(HT* ht, char* key, void* data);
void* ht_get(HT* ht, char* key);
void* ht_delete(HT* ht, char* key);
int ht_free(HT* ht);
#endif

34
hashtable/test.c Normal file
View file

@ -0,0 +1,34 @@
#include <stdio.h>
#include <assert.h>
#include "hashtable.h"
int main(void)
{
puts("Creating new hashtable...");
HT* ht = ht_create();
char* login_data = "This is the login data!";
char* register_data = "This is the register data!";
puts("Putting in some data...");
ht_put(ht, "/login", login_data);
ht_put(ht, "/register", register_data);
puts("Getting values and asserting they are correct...");
assert(login_data == ht_get(ht, "/login"));
assert(register_data == ht_get(ht, "/register"));
puts("Checking that delete works...");
ht_delete(ht, "/login");
assert(ht_get(ht, "/login") == NULL);
puts("Tests complete!");
return 0;
}

25
http/Makefile Normal file
View file

@ -0,0 +1,25 @@
CC=gcc
CFLAGS=-Wall -Wextra
OBJS=test.o http.o net.o hashtable.o llist.o
all: test
test: $(OBJS)
gcc -o $@ $^
test.o: test.c http.h
http.o: http.c http.h
net.o: net.c net.h
hashtable.o: hashtable.c hashtable.h
llist.o: llist.c llist.h
clean:
rm -f $(OBJS)
rm -f test
.PHONY: all, clean

137
http/http.c Normal file
View file

@ -0,0 +1,137 @@
/**
* http.c - Basic HTTP framework implentation
*
* hashtable.c for routing
* listen.c for socket creation
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include "hashtable.h"
#include "listen.h"
#include "http.h"
#define BUF_SIZE 65536
#define HTTP_RES "HTTP/1.1 %d MESSAGE\n"\
"Connection: close\n"\
"Content-Length: %d\n"\
"Content-Type: %s\n"\
"\n"\
"%s"
HT* routes;
void use(char* path, Handler handler)
{
if (routes == NULL)
{
routes = ht_create();
}
// TODO: handle for key exist
ht_put(routes, path, (void*) handler);
}
void send_response(int client, Res* res)
{
char res_raw[BUF_SIZE];
sprintf( res_raw,
HTTP_RES,
res->status,
res->content_length,
res->content_type,
res->body) ;
int num_sent = send(client, res_raw, strlen(res_raw), 0);
if (num_sent < 0)
{
perror("send");
}
}
void fallback_handler(Res* res)
{
char* content = "Bad request\n";
res->status = 400;
res->content_length = strlen(content);
res->content_type = "text/html";
res->body = content;
}
void parse_req_raw(char* raw, Req* req)
{
printf("%s\n", raw);
sscanf(raw, "%s %s", req->type, req->path);
}
void handle_request(int client)
{
char req_raw[BUF_SIZE];
Req req;
Res res;
int num_recvd = recv(client, req_raw, BUF_SIZE - 1, 0);
if (num_recvd < 0)
{
perror("recv");
return;
}
parse_req_raw(req_raw, &req); // TODO: handle for parse.
printf("Request: %s, %s\n", req.type, req.path);
Handler handler = ht_get(routes, req.path);
if (handler == NULL)
{
fallback_handler(&res);
}
else
{
handler(&req, &res);
}
send_response(client, &res);
}
int http_start(char* port)
{
int client, server;
if ((server = init_server_socket(port)) < 0)
{
puts("Error when establishing the server.");
exit(1);
}
printf("Waiting for connections on port %s...\n", port);
while(1)
{
client = accept(server, (struct sockaddr*) NULL, NULL);
if (client == -1)
{
perror("accept");
continue;
}
printf("Client connected.\n");
handle_request(client);
close(client);
}
return 0;
}

31
http/http.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef _HTTP_H_
#define _HTTP_H_
#define PORT "3000"
typedef struct
{
char type[100];
char path[200];
}
Req;
typedef struct
{
int status;
char* content_type;
int content_length;
char* body;
}
Res;
/* User defined handlers must be this shape */
typedef void (*Handler)(Req* req, Res* res);
/* Add an api endpoint */
void use(char* path, Handler handler);
/* Start the server */
int http_start(char* port);
#endif

62
http/listen.c Normal file
View file

@ -0,0 +1,62 @@
/**
* listen.c - Create and bind a server socket to a port
*
*/
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include "listen.h"
#define QUEUE 8
int init_server_socket(char* port)
{
int fd, yes = 1;
struct addrinfo hints, *servinfo, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, port, &hints, &servinfo);
for (p = servinfo; p != NULL; p = p->ai_next)
{
if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
continue;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
close(fd);
freeaddrinfo(servinfo);
return -1;
}
if (bind(fd, p->ai_addr, p->ai_addrlen) == -1)
{
close(fd);
continue;
}
break;
}
freeaddrinfo(servinfo);
if (p == NULL) { return -1; }
if (listen(fd, QUEUE) == -1)
{
close(fd);
return -1;
}
return fd;
}

6
http/listen.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef _LISTEN_H_
#define _LISTEN_H_
int init_server_socket(char *port);
#endif

48
http/main.c Normal file
View file

@ -0,0 +1,48 @@
/**
* main.c - HTTP server that uses http.c framework
*
* curl http://localhost:3000/api/login
*
*/
#include <stdio.h>
#include <string.h>
#include "http.h"
void login(Req* req, Res* res)
{
(void) req;
char* content = "Login works!\n";
res->status = 200;
res->content_length = strlen(content);
res->content_type = "text/html";
res->body = content;
}
void signup(Req* req, Res* res)
{
(void) req;
char* content = "Sign Up works!\n";
res->status = 200;
res->content_length = strlen(content);
res->content_type = "text/html";
res->body = content;
}
/**
* Register the endpoints and start the server.
*/
int main(void)
{
puts("Registering endpoints");
use("/signup", &signup);
use("/login", &login);
http_start(PORT);
}

19
llist/Makefile Normal file
View file

@ -0,0 +1,19 @@
CC=gcc
CFLAGS=-Wall -Wextra
OBJS=test.o llist.o
all: test
test: $(OBJS)
gcc -o $@ $^
test.o: test.c
llist.o: llist.c llist.h
clean:
rm -f $(OBJS)
rm -f test
.PHONY: all, clean

174
llist/llist.c Normal file
View file

@ -0,0 +1,174 @@
/**
* llist.c - a basic linked list implementation.
*
* Used for the hashtable.
*
*/
#include <stdlib.h>
#include <stdio.h>
#include "llist.h"
/**
* Node in the linked list
*/
typedef struct LLNodeT
{
void* data;
struct LLNodeT* next;
}
LLNode;
/**
* Create a new linked list
*/
LL* ll_create(void)
{
LL* ll = malloc(sizeof(LL));
ll->entries = 0;
return ll;
}
/**
* Add item to beginning of a linked list
*/
int ll_push(LL* ll, void* data)
{
LLNode* n = malloc(sizeof(*n));
n->data = data;
// Reassign the head
n->next = ll->head;
ll->head = n;
ll->entries++;
return 0;
}
/**
* Add item to end of a linked list
*/
int ll_append(LL* ll, void* data)
{
LLNode* tail = ll->head;
// Just push if there are no nodes yet
if (tail == NULL)
{
return ll_push(ll, data);
}
// Travserse list to find tail
while (tail->next != NULL)
{
tail = tail->next;
}
// Create new node and assign it as tail
LLNode* n = malloc(sizeof(LLNode));
n->data = data;
tail->next = n;
ll->entries++;
return 0;
}
/**
* Get an item from the list via cb function
*/
void* ll_search(LL* ll, LLCb cb, void* arg)
{
LLNode* n = ll->head;
// Keep going until tail is reached
while (n != NULL)
{
if (cb(n->data, arg) == 0)
{
break;
}
n = n->next;
}
if (n == NULL)
{
return NULL;
}
return n->data;
}
/**
* Find and delete an entry from a linked list
*/
void* ll_delete(LL* ll, LLCb cb, void* arg)
{
LLNode* n = ll->head, *prev = NULL;
while (n != NULL)
{
if (cb(n->data, arg) == 0)
{
void* data = n->data;
// We are at the head
if (prev == NULL)
{
ll->head = n->next;
free(n);
}
else
{
prev->next = n->next;
free(n);
}
return data;
}
prev = n;
n = n->next;
}
return NULL;
}
/**
* Apply a function to every node in a linked list
*/
int ll_loop(LL* ll, LLCb cb, void* arg)
{
LLNode* n = ll->head, *next;
while (n != NULL)
{
next = n->next;
cb(n->data, arg);
n = next;
}
return 0;
}
/**
* Free a linked list
*/
void ll_free(LL* ll)
{
LLNode* n = ll->head, *next;
while (n != NULL)
{
next = n->next;
free(n);
n = next;
}
free(ll);
}

27
llist/llist.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef _LLIST_H_
#define _LLIST_H_
typedef struct
{
struct LLNodeT* head;
int entries;
}
LL;
typedef int (*LLCb)(void*, void*);
LL* ll_create(void);
int ll_push(LL* ll, void* data);
int ll_append(LL* ll, void* data);
void* ll_search(LL* ll, LLCb cb, void* arg);
void* ll_delete(LL* ll, LLCb cb, void* arg);
int ll_loop(LL* ll, LLCb cb, void* arg);
void ll_free(LL* ll);
#endif

83
llist/test.c Normal file
View file

@ -0,0 +1,83 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "llist.h"
typedef struct
{
char* name;
char* team;
int age;
}
Player;
Player* create_player(char* name, char* team, int age)
{
Player* p = malloc(sizeof(*p));
p->team = strdup(team);
p->name = strdup(name);
p->age = age;
return p;
}
int find_player(void* data, void* arg)
{
Player* player = data;
return strcmp(player->name, (char*) arg);
}
int change_team(void* data, void* arg)
{
Player* player = data;
player->team = (char*) arg;
return 0;
}
int main(void)
{
puts("Creating new linked list...");
LL* ll = ll_create();
puts("Pushing some data...");
Player* lebron = create_player("Lebron James", "Lakers", 37);
Player* giannis = create_player("Giannis Antetokounmpo", "Bucks", 27);
ll_push(ll, lebron);
ll_push(ll, giannis);
puts("Finding the data that was pushed...");
assert(ll_search(ll, find_player, "Lebron James") == lebron);
puts("Appending some data...");
Player* kd = create_player("Kevin Durant", "Nets", 33);
ll_append(ll, kd);
puts("Finding the data that was appended...");
assert(ll_search(ll, find_player, "Kevin Durant") == kd);
puts("Looping over linked list and applying change...");
ll_loop(ll, change_team, "Bucks");
Player* kd_ref = ll_search(ll, find_player, "Kevin Durant");
assert(strcmp(kd_ref->team, "Bucks") == 0);
puts("Deleting data from linked list...");
ll_delete(ll, find_player, "Lebron James");
assert(ll_search(ll, find_player, "Lebron James") == NULL);
puts("Tests complete!");
return 0;
}