Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: txus/shitdb
base: c2523a84ee
...
head fork: txus/shitdb
compare: efdb964ba3
Checking mergeability… Don't worry, you can still create the pull request.
  • 9 commits
  • 14 files changed
  • 0 commit comments
  • 1 contributor
View
15 README.md
@@ -5,7 +5,7 @@ Shitty key-value store in C inspired by Redis.
## Usage
$ make
- $ bin/shitdb
+ $ bin/shitdb [port]
Example session:
@@ -26,19 +26,28 @@ Example session:
* Integer: 2834
* String: "yuhuuuu"
* Array: [42,"foo",9999]
+* Hashes: (no literal representation)
-## Instructions
+## Commands
* GET [key]: Returns the content of a key.
* SET [key] [value]: Sets key to a value of any of the basic types.
+* DELETE [key]: Deletes a key.
+* CLEAR: Clears all keys.
* QUIT: Ends the connection.
-### Array instructions
+### Array commands
* APUSH [key] [value]: Pushes value to the array located at key.
* APOP [key]: Pops the array located at key and returns the popped value.
+* AAT [key] [index]: Gets the array element at a certain index.
* ACOUNT [key]: Returns the number of elements in the array.
+### Hash commands
+
+* HSET [key] [hashkey] [value]: Sets the hashkey in the Hash object in key to a value.
+* HGET [key] [hashkey]: Gets the value of the hashkey in the Hash object in key.
+
## Contributing
1. Fork it
View
16 bin/shitdb.c
@@ -1,12 +1,26 @@
#include <shitdb/db.h>
#include <shitdb/server.h>
+
#define DEFAULT_PORT 4999
+static int
+resolve_port(int argc, char **argv)
+{
+ int port = 0;
+ if(argc == 2 && (port = atoi(argv[1]))) {
+ return port;
+ } else {
+ return DEFAULT_PORT;
+ }
+}
+
int main(int argc, char **argv)
{
DB *db = DB_create();
- Server_start(db, DEFAULT_PORT);
+ int port = resolve_port(argc, argv);
+
+ Server_start(db, port);
return 0;
}
View
5 src/shitdb/bstrlib.h
@@ -39,6 +39,11 @@ typedef const struct tagbstring * const_bstring;
/* Copy functions */
#define cstr2bstr bfromcstr
+
+/* Custom defines */
+#define S(A) bfromcstr(A)
+#define eql(A, B) bstrcmp(A, S(B)) == 0
+
extern bstring bfromcstr (const char * str);
extern bstring bfromcstralloc (int mlen, const char * str);
extern bstring blk2bstr (const void * blk, int len);
View
1  src/shitdb/darray.h
@@ -22,6 +22,7 @@ int DArray_push(DArray *array, void *el);
void *DArray_pop(DArray *array);
void DArray_clear_destroy(DArray *array);
+#define DArray_at(A,I) ((A)->contents[(I)])
#define DArray_last(A) ((A)->contents[(A)->end - 1])
#define DArray_first(A) ((A)->contents[0])
#define DArray_end(A) ((A)->end)
View
63 src/shitdb/db.c
@@ -1,14 +1,26 @@
#include <stdlib.h>
#include <shitdb/dbg.h>
+#include <shitdb/hashmap.h>
#include <shitdb/db.h>
+static void destroy_object(void *obj)
+{
+ Object_destroy((Object*)obj);
+}
+
+static int destroy_object_node(HashmapNode *node)
+{
+ destroy_object((Object*)node);
+ return 0;
+}
+
DB*
DB_create()
{
DB *db = calloc(1, sizeof(DB));
check_mem(db);
- db->map = Hashmap_create(NULL, NULL);
+ db->map = Hashmap_create(NULL, NULL, destroy_object);
return db;
error:
@@ -16,6 +28,18 @@ DB_create()
}
void
+DB_clear(DB *db)
+{
+ Hashmap_traverse(db->map, destroy_object_node);
+}
+
+void
+DB_delete(DB *db, bstring key)
+{
+ Hashmap_delete(db->map, key);
+}
+
+void
DB_set(DB *db, bstring key, Object *value)
{
Hashmap_set(db->map, key, value);
@@ -35,6 +59,12 @@ void
DB_apush(DB *db, bstring key, Object *value)
{
Object *array = (Object*)Hashmap_get(db->map, key);
+
+ if (array == NULL) {
+ array = Object_create_array(DArray_create(sizeof(Object*), 1));
+ Hashmap_set(db->map, key, array);
+ }
+
DArray_push(array->value.as_array, value);
}
@@ -46,6 +76,13 @@ DB_apop(DB *db, bstring key)
}
Object*
+DB_aat(DB *db, bstring key, int index)
+{
+ Object *array = (Object*)Hashmap_get(db->map, key);
+ return (Object*)DArray_at(array->value.as_array, index);
+}
+
+Object*
DB_acount(DB *db, bstring key)
{
Object *array = (Object*)Hashmap_get(db->map, key);
@@ -53,3 +90,27 @@ DB_acount(DB *db, bstring key)
return Object_create_integer(count);
}
+void
+DB_hset(DB *db, bstring key, bstring hkey, Object *value)
+{
+ Object *hash = (Object*)Hashmap_get(db->map, key);
+
+ if (hash == NULL) {
+ hash = Object_create_hash(Hashmap_create(NULL, NULL, destroy_object));
+ Hashmap_set(db->map, key, hash);
+ }
+
+ Hashmap_set(hash->value.as_hash, hkey, value);
+}
+
+Object*
+DB_hget(DB *db, bstring key, bstring hkey)
+{
+ Object *hash = (Object*)Hashmap_get(db->map, key);
+ if (hash == NULL) {
+ return NULL;
+ } else {
+ return Hashmap_get(hash->value.as_hash, hkey);
+ }
+}
+
View
5 src/shitdb/db.h
@@ -13,10 +13,15 @@ DB* DB_create();
#define DB_destroy(db) Hashmap_destroy(db->map); free(db);
void DB_set(DB *db, bstring key, Object *value);
Object* DB_get(DB *db, bstring key);
+void DB_delete(DB *db, bstring key);
+void DB_clear(DB *db);
// Array
void DB_apush(DB *db, bstring key, Object *value);
Object* DB_apop(DB *db, bstring key);
+Object* DB_aat(DB *db, bstring key, int index);
Object* DB_acount(DB *db, bstring key);
+void DB_hset(DB *db, bstring key, bstring hkey, Object *value);
+Object* DB_hget(DB *db, bstring key, bstring hkey);
#endif
View
12 src/shitdb/hashmap.c
@@ -14,6 +14,11 @@ static int default_compare(void *a, void *b)
return bstrcmp((bstring)a, (bstring)b);
}
+static void default_destroy(void *node)
+{
+ free(node);
+}
+
static uint32_t default_hash(void *a)
{
size_t len = blength((bstring)a);
@@ -35,13 +40,14 @@ static uint32_t default_hash(void *a)
return hash;
}
-Hashmap *Hashmap_create(Hashmap_compare compare, Hashmap_hash hash)
+Hashmap *Hashmap_create(Hashmap_compare compare, Hashmap_hash hash, Hashmap_destroy_cb destroy)
{
Hashmap *map = calloc(1, sizeof(Hashmap));
check_mem(map);
map->compare = compare == NULL ? default_compare : compare;
map->hash = hash == NULL ? default_hash : hash;
+ map->destroy = destroy == NULL ? default_destroy : destroy;
map->buckets = DArray_create(sizeof(DArray *), DEFAULT_NUMBER_OF_BUCKETS);
map->buckets->end = map->buckets->max;
check_mem(map->buckets);
@@ -67,7 +73,7 @@ void Hashmap_destroy(Hashmap *map)
DArray *bucket = DArray_get(map->buckets, i);
if(bucket) {
for(j = 0; j < DArray_count(bucket); j++) {
- free(DArray_get(bucket, j));
+ map->destroy(DArray_get(bucket, j));
}
DArray_destroy(bucket);
}
@@ -197,7 +203,7 @@ void *Hashmap_delete(Hashmap *map, void *key)
HashmapNode *node = DArray_get(bucket, i);
void *data = node->data;
- free(node);
+ map->destroy(node);
HashmapNode *ending = DArray_pop(bucket);
if(ending != node) {
View
4 src/shitdb/hashmap.h
@@ -8,11 +8,13 @@
typedef int (*Hashmap_compare)(void *a, void *b);
typedef uint32_t (*Hashmap_hash)(void *key);
+typedef void (*Hashmap_destroy_cb)(void *node);
typedef struct Hashmap {
DArray *buckets;
Hashmap_compare compare;
Hashmap_hash hash;
+ Hashmap_destroy_cb destroy;
} Hashmap;
typedef struct HashmapNode {
@@ -23,7 +25,7 @@ typedef struct HashmapNode {
typedef int (*Hashmap_traverse_cb)(HashmapNode *node);
-Hashmap *Hashmap_create(Hashmap_compare compare, Hashmap_hash);
+Hashmap *Hashmap_create(Hashmap_compare, Hashmap_hash, Hashmap_destroy_cb);
void Hashmap_destroy(Hashmap *map);
int Hashmap_set(Hashmap *map, void *key, void *data);
View
36 src/shitdb/object.c
@@ -15,6 +15,31 @@ Object_allocate()
return NULL;
}
+void
+Object_destroy(Object *object)
+{
+ switch(object->type) {
+ case tString:
+ bdestroy(object->value.as_string);
+ break;
+ case tArray: {
+ DArray *ary = object->value.as_array;
+ Object *obj = NULL;
+ while((obj = (Object*)DArray_pop(ary)) != NULL) {
+ Object_destroy(obj);
+ }
+ break;
+ }
+ case tHash: {
+ Hashmap_destroy(object->value.as_hash);
+ }
+ default:
+ break;
+ }
+
+ free(object);
+}
+
Object*
Object_create_integer(int value)
{
@@ -42,6 +67,15 @@ Object_create_array(DArray *array)
return obj;
}
+Object*
+Object_create_hash(Hashmap *hash)
+{
+ Object *obj = Object_allocate();
+ obj->type = tHash;
+ obj->value.as_hash = hash;
+ return obj;
+}
+
bstring
Object_to_string(Object *object)
{
@@ -85,7 +119,7 @@ Object_to_string(Object *object)
}
}
- return bfromcstr(str);
+ return S(str);
}
Object*
View
6 src/shitdb/object.h
@@ -3,12 +3,14 @@
#include <shitdb/bstrlib.h>
#include <shitdb/darray.h>
+#include <shitdb/hashmap.h>
typedef enum {
tNil,
tInteger,
tString,
tArray,
+ tHash,
} Type;
typedef struct Object {
@@ -17,6 +19,7 @@ typedef struct Object {
int as_integer;
bstring as_string;
DArray *as_array;
+ Hashmap *as_hash;
} value;
} Object;
@@ -24,7 +27,8 @@ Object *Object_allocate();
Object *Object_create_integer(int value);
Object *Object_create_string(bstring value);
Object *Object_create_array(DArray *array);
-Object *Object_destroy(Object *object);
+Object *Object_create_hash(Hashmap *hash);
+void Object_destroy(Object *object);
bstring Object_to_string(Object *object);
Object *String_to_object(bstring string);
View
85 src/shitdb/server.c
@@ -11,15 +11,27 @@
int Command_arity(bstring cmd)
{
- if(bstrcmp(cmd, bfromcstr("GET")) == 0) {
+ if(eql(cmd, "GET")) {
return 1;
- } else if (bstrcmp(cmd, bfromcstr("SET")) == 0) {
+ } else if (eql(cmd, "SET")) {
return 2;
- } else if (bstrcmp(cmd, bfromcstr("APUSH")) == 0) {
+ } else if (eql(cmd, "DELETE")) {
+ return 1;
+ } else if (eql(cmd, "APUSH")) {
+ return 2;
+ } else if (eql(cmd, "APOP")) {
+ return 1;
+ } else if (eql(cmd, "AAT")) {
return 2;
- } else if (bstrcmp(cmd, bfromcstr("APOP")) == 0) {
+ } else if (eql(cmd, "ACOUNT")) {
return 1;
- } else if (bstrcmp(cmd, bfromcstr("QUIT")) == 0) {
+ } else if (eql(cmd, "HSET")) {
+ return 3;
+ } else if (eql(cmd, "HGET")) {
+ return 2;
+ } else if (eql(cmd, "CLEAR")) {
+ return 0;
+ } else if (eql(cmd, "QUIT")) {
return 0;
} else {
return -1;
@@ -40,30 +52,73 @@ int Server_execute(DB *db, bstring command, Object *result)
ptr++;
- if(bstrcmp(cmd, bfromcstr("GET")) == 0) {
+ if(eql(cmd, "GET")) {
Object *ret = DB_get(db, *ptr);
- memcpy(result, ret, sizeof(Object));
- } else if (bstrcmp(cmd, bfromcstr("SET")) == 0) {
+ if (ret) {
+ memcpy(result, ret, sizeof(Object));
+ } else {
+ return 2;
+ }
+ } else if (eql(cmd, "SET")) {
bstring key = *ptr;
ptr++;
Object *value = String_to_object(*ptr);
check(value, "Invalid value to SET.");
DB_set(db, key, value);
- } else if (bstrcmp(cmd, bfromcstr("APUSH")) == 0) {
+ } else if (eql(cmd, "DELETE")) {
+ DB_delete(db, *ptr);
+ } else if (eql(cmd, "APUSH")) {
bstring key = *ptr;
ptr++;
Object *value = String_to_object(*ptr);
check(value, "Invalid value to APUSH.");
DB_apush(db, key, value);
- } else if (bstrcmp(cmd, bfromcstr("APOP")) == 0) {
+ } else if (eql(cmd, "AAT")) {
+ bstring key = *ptr;
+ ptr++;
+ int index = atoi(bdata(*ptr));
+
+ Object *ret = DB_aat(db, key, index);
+ if (ret) {
+ memcpy(result, ret, sizeof(Object));
+ } else {
+ return 2;
+ }
+ } else if (eql(cmd, "ACOUNT")) {
+ bstring key = *ptr;
+ Object *ret = DB_acount(db, key);
+ memcpy(result, ret, sizeof(Object));
+ } else if (eql(cmd, "APOP")) {
bstring key = *ptr;
ptr++;
Object *ret = DB_apop(db, key);
+ if (ret) {
+ memcpy(result, ret, sizeof(Object));
+ } else {
+ return 2;
+ }
+ } else if (eql(cmd, "HSET")) {
+ bstring key = *ptr;
+ ptr++;
+ bstring hkey = *ptr;
+ ptr++;
+ Object *value = String_to_object(*ptr);
+ check(value, "Invalid value to HSET.");
+
+ DB_hset(db, key, hkey, value);
+ } else if (eql(cmd, "HGET")) {
+ bstring key = *ptr;
+ ptr++;
+ bstring hkey = *ptr;
+
+ Object *ret = DB_hget(db, key, hkey);
memcpy(result, ret, sizeof(Object));
- } else if (bstrcmp(cmd, bfromcstr("QUIT")) == 0) {
+ } else if (eql(cmd, "CLEAR")) {
+ DB_clear(db);
+ } else if (eql(cmd, "QUIT")) {
return -1;
}
return 0;
@@ -142,19 +197,21 @@ void Server_start(DB *db, int port)
char buf[256];
while((received = recv(accept_fd, buf, 255, 0))) {
Object *result = Object_allocate();
- rc = Server_execute(db, chomp(bfromcstr(buf)), result);
+ rc = Server_execute(db, chomp(S(buf)), result);
if(rc == 0) {
if (result->type != tNil) {
bstring ret = Object_to_string(result);
+ bconchar(ret, '\n');
send(accept_fd, bdata(ret), blength(ret), 0);
- send(accept_fd, "\n", 2, 0);
} else {
send(accept_fd, "OK\n", 4, 0);
}
} else if (rc == 1) {
send(accept_fd, "ERROR\n", 7, 0);
+ } else if (rc == 2) {
+ send(accept_fd, "NOTFOUND\n", 10, 0);
} else if (rc == -1) {
- send(accept_fd, "Bye!\n", 6, 0);
+ send(accept_fd, "BYE\n", 5, 0);
// Close the connection
close(accept_fd);
goto again;
View
70 tests/db_tests.c
@@ -12,59 +12,92 @@ char *test_create()
char *test_set()
{
- Object *str = Object_create_string(bfromcstr("bar"));
+ Object *str = Object_create_string(S("bar"));
Object *number = Object_create_integer(42);
- Object *array = String_to_object(bfromcstr("[42, \"hey\"]"));
+ Object *array = String_to_object(S("[42, \"hey\"]"));
- DB_set(db, bfromcstr("foo"), str);
- DB_set(db, bfromcstr("number"), number);
- DB_set(db, bfromcstr("array"), array);
+ DB_set(db, S("foo"), str);
+ DB_set(db, S("number"), number);
+ DB_set(db, S("array"), array);
return NULL;
}
char *test_get()
{
- Object *bar = DB_get(db, bfromcstr("foo"));
+ Object *bar = DB_get(db, S("foo"));
bstring barstr = Object_to_string(bar);
- mu_assert(bstrcmp(barstr, bfromcstr("\"bar\"")) == 0, "Strings mismatch");
+ mu_assert(eql(barstr, "\"bar\""), "Strings mismatch");
- Object *number = DB_get(db, bfromcstr("number"));
+ Object *number = DB_get(db, S("number"));
bstring numberstr = Object_to_string(number);
- mu_assert(bstrcmp(numberstr, bfromcstr("42")) == 0, "Integer mismatch");
+ mu_assert(eql(numberstr, "42"), "Integer mismatch");
- Object *ary = DB_get(db, bfromcstr("array"));
+ Object *ary = DB_get(db, S("array"));
bstring arystr = Object_to_string(ary);
- mu_assert(bstrcmp(arystr, bfromcstr("[42, \"hey\"]")) == 0, "Array mismatch");
+ mu_assert(eql(arystr, "[42, \"hey\"]"), "Array mismatch");
return NULL;
}
char *test_apush()
{
- Object *number = Object_create_integer(999);
- DB_apush(db, bfromcstr("array"), number);
+ Object *integer = Object_create_integer(999);
+ DB_apush(db, S("newarray"), integer);
return NULL;
}
char *test_apop()
{
- Object *integer = DB_apop(db, bfromcstr("array"));
+ Object *integer = DB_apop(db, S("newarray"));
bstring integerstr = Object_to_string(integer);
- mu_assert(bstrcmp(integerstr, bfromcstr("999")) == 0, "Wrong popped value.");
+ mu_assert(eql(integerstr, "999"), "Wrong popped value.");
+ return NULL;
+}
+
+char *test_aat()
+{
+ Object *integer = DB_aat(db, S("array"), 0);
+ bstring integerstr = Object_to_string(integer);
+
+ mu_assert(eql(integerstr, "42"), "Wrong value at index.");
return NULL;
}
char *test_acount()
{
- Object *count = DB_acount(db, bfromcstr("array"));
+ Object *count = DB_acount(db, S("array"));
bstring countstr = Object_to_string(count);
- mu_assert(bstrcmp(countstr, bfromcstr("2")) == 0, "Wrong array count.");
+ mu_assert(eql(countstr, "2"), "Wrong array count.");
+ return NULL;
+}
+
+char *test_hset()
+{
+ Object *integer1 = Object_create_integer(999);
+ Object *integer2 = Object_create_integer(1);
+
+ DB_hset(db, S("hash"), bfromcstr("foo"), integer1);
+ DB_hset(db, S("hash"), bfromcstr("bar"), integer2);
+
+ return NULL;
+}
+
+char *test_hget()
+{
+ Object *integer1 = DB_hget(db, S("hash"), bfromcstr("foo"));
+ Object *integer2 = DB_hget(db, S("hash"), bfromcstr("bar"));
+ bstring integer1str = Object_to_string(integer1);
+ bstring integer2str = Object_to_string(integer2);
+
+ mu_assert(eql(integer1str, "999"), "Wrong hash value on creation");
+ mu_assert(eql(integer2str, "1"), "Wrong hash value on update");
+
return NULL;
}
@@ -83,7 +116,10 @@ char *all_tests() {
mu_run_test(test_get);
mu_run_test(test_apush);
mu_run_test(test_apop);
+ mu_run_test(test_aat);
mu_run_test(test_acount);
+ mu_run_test(test_hset);
+ mu_run_test(test_hget);
mu_run_test(test_destroy);
return NULL;
View
2  tests/hashmap_tests.c
@@ -33,7 +33,7 @@ static int traverse_fail_cb(HashmapNode *node)
char *test_create()
{
- map = Hashmap_create(NULL, NULL);
+ map = Hashmap_create(NULL, NULL, NULL);
mu_assert(map != NULL, "Failed to create map.");
return NULL;
View
12 tests/object_tests.c
@@ -6,19 +6,19 @@ char *test_to_string()
Object *integer = Object_create_integer(123);
bstring int_result = Object_to_string(integer);
- Object *string = Object_create_string(bfromcstr("hello"));
+ Object *string = Object_create_string(S("hello"));
bstring str_result = Object_to_string(string);
- mu_assert(bstrcmp(int_result, bfromcstr("123")) == 0, "Integer to string mismatch");
- mu_assert(bstrcmp(str_result, bfromcstr("\"hello\"")) == 0, "String obj to string mismatch");
+ mu_assert(eql(int_result, "123"), "Integer to string mismatch");
+ mu_assert(eql(str_result, "\"hello\""), "String obj to string mismatch");
return NULL;
}
char *test_to_object()
{
- bstring integer = bfromcstr("1");
- bstring string = bfromcstr("\"hello\"");
+ bstring integer = S("1");
+ bstring string = S("\"hello\"");
Object *obj_int = String_to_object(integer);
Object *obj_str = String_to_object(string);
@@ -27,7 +27,7 @@ char *test_to_object()
mu_assert(obj_int->value.as_integer == 1, "Integer ot object did not get correct value");
mu_assert(obj_str->type == tString, "String to object is not of type String");
- mu_assert(bstrcmp(obj_str->value.as_string, bfromcstr("hello")) == 0, "String to object did not get correct value");
+ mu_assert(eql(obj_str->value.as_string, "hello"), "String to object did not get correct value");
return NULL;
}

No commit comments for this range

Something went wrong with that request. Please try again.