Skip to content

Commit 3454fb7

Browse files
committed
feat(hashtable): add deleting hash table
fix(commands): invalid writing "unknown command" fix(commands): invalid argument count checking for HSET command feat(hashtable): change grow_hashtable method as resize_hashtable method feat(commands): add HDEL command chore: remove unused headers from some files chore(hashtable): move add_fv_to_hashtable to memory file perf(hashtable): remove strlen method from add_fv_to_hashtable method
1 parent 804df73 commit 3454fb7

File tree

9 files changed

+222
-120
lines changed

9 files changed

+222
-120
lines changed

headers/commands.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44

55
#include <stdint.h>
66

7-
#include <unistd.h>
8-
97
#define WRONG_ARGUMENT_ERROR(client, name, len) (_write((client), "-Wrong argument count for '" name "' command\r\n", 38 + (len)))
108

119
struct Subcommand {
@@ -52,6 +50,7 @@ extern struct Command cmd_time;
5250
/* /GENERIC COMMANDS */
5351

5452
/* HASHTABLE COMMANDS */
53+
extern struct Command cmd_hdel;
5554
extern struct Command cmd_hget;
5655
extern struct Command cmd_hlen;
5756
extern struct Command cmd_hset;

headers/hashtable.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include "telly.h"
34
#include "utils.h"
45

56
#include <stdint.h>
@@ -9,6 +10,7 @@ struct FVPair {
910
value_t value;
1011
enum TellyTypes type;
1112
struct FVPair *next;
13+
uint64_t hash;
1214
};
1315

1416
struct HashTableSize {
@@ -21,14 +23,17 @@ struct HashTable {
2123
struct FVPair **fvs;
2224
struct HashTableSize size;
2325
double grow_factor;
26+
double shrink_factor;
2427
};
2528

2629
uint64_t hash(char *key);
27-
struct HashTable *create_hashtable(uint32_t default_size, double grow_factor);
30+
struct HashTable *create_hashtable(uint32_t default_size, const double grow_factor, const double shrink_factor);
31+
void resize_hashtable(struct HashTable *table, const uint32_t size);
2832
struct FVPair *get_fv_from_hashtable(struct HashTable *table, char *name);
2933
void free_hashtable(struct HashTable *table);
3034

3135
void set_fv_value(struct FVPair *fv, void *value);
3236
void free_fv(struct FVPair *fv);
3337

34-
void add_fv_to_hashtable(struct HashTable *table, char *name, void *value, enum TellyTypes type);
38+
void add_fv_to_hashtable(struct HashTable *table, const string_t name, void *value, const enum TellyTypes type);
39+
bool del_fv_to_hashtable(struct HashTable *table, const string_t name);

src/commands/hashtable/hdel.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#include "../../../headers/telly.h"
2+
#include "../../../headers/server.h"
3+
#include "../../../headers/database.h"
4+
#include "../../../headers/commands.h"
5+
#include "../../../headers/hashtable.h"
6+
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <stdint.h>
10+
#include <stdbool.h>
11+
12+
static void run(struct Client *client, respdata_t *data) {
13+
if (data->count < 3) {
14+
if (client) WRONG_ARGUMENT_ERROR(client, "HDEL", 4);
15+
return;
16+
}
17+
18+
const string_t key = data->value.array[1]->value.string;
19+
struct KVPair *kv = get_data(key.value);
20+
struct HashTable *table;
21+
22+
if (kv) {
23+
if (kv->type == TELLY_HASHTABLE) {
24+
table = kv->value->hashtable;
25+
} else if (client) {
26+
_write(client, "-Invalid type for 'HDEL' command\r\n", 34);
27+
return;
28+
}
29+
} else if (client) {
30+
_write(client, ":0\r\n", 4);
31+
return;
32+
}
33+
34+
uint32_t deleted = 0;
35+
36+
for (uint32_t i = 1; i < data->count; ++i) {
37+
const string_t name = data->value.array[i]->value.string;
38+
39+
if (del_fv_to_hashtable(table, name)) deleted += 1;
40+
}
41+
42+
if (client) {
43+
const uint32_t buf_len = 3 + get_digit_count(deleted);
44+
char buf[buf_len + 1];
45+
sprintf(buf, ":%d\r\n", deleted);
46+
47+
_write(client, buf, buf_len);
48+
}
49+
}
50+
51+
struct Command cmd_hdel = {
52+
.name = "HDEL",
53+
.summary = "Deletes field(s) of the hash table for the key. If hash table does not exist, creates it.",
54+
.since = "0.1.5",
55+
.complexity = "O(N) where N is field name count",
56+
.subcommands = NULL,
57+
.subcommand_count = 0,
58+
.run = run
59+
};

src/commands/hashtable/hset.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
#include <stdbool.h>
1111

1212
static void run(struct Client *client, respdata_t *data) {
13-
if (client && (data->count == 2 || data->count % 2 != 0)) {
14-
WRONG_ARGUMENT_ERROR(client, "HSET", 4);
13+
if (data->count == 2 || data->count % 2 != 0) {
14+
if (client) WRONG_ARGUMENT_ERROR(client, "HSET", 4);
1515
return;
1616
}
1717

@@ -22,16 +22,17 @@ static void run(struct Client *client, respdata_t *data) {
2222
if (kv && kv->type == TELLY_HASHTABLE) {
2323
table = kv->value->hashtable;
2424
} else {
25-
table = create_hashtable(16, 0.5);
25+
table = create_hashtable(16, 0.5, 0.75);
26+
2627
set_data(kv, key, (value_t) {
2728
.hashtable = table
2829
}, TELLY_HASHTABLE);
2930
}
3031

31-
const uint64_t fv_count = (data->count / 2) - 1;
32+
const uint32_t fv_count = (data->count / 2) - 1;
3233

3334
for (uint32_t i = 1; i <= fv_count; ++i) {
34-
char *name = data->value.array[i * 2]->value.string.value;
35+
const string_t name = data->value.array[i * 2]->value.string;
3536
char *value = data->value.array[i * 2 + 1]->value.string.value;
3637

3738
bool is_true = streq(value, "true");
@@ -51,7 +52,7 @@ static void run(struct Client *client, respdata_t *data) {
5152
if (client) {
5253
const uint32_t buf_len = 3 + get_digit_count(fv_count);
5354
char buf[buf_len + 1];
54-
sprintf(buf, ":%ld\r\n", fv_count);
55+
sprintf(buf, ":%d\r\n", fv_count);
5556

5657
_write(client, buf, buf_len);
5758
}
@@ -61,7 +62,7 @@ struct Command cmd_hset = {
6162
.name = "HSET",
6263
.summary = "Sets field(s) of the hash table for the key. If hash table does not exist, creates it.",
6364
.since = "0.1.3",
64-
.complexity = "O(1)",
65+
.complexity = "O(N) where N is field name-value pair count",
6566
.subcommands = NULL,
6667
.subcommand_count = 0,
6768
.run = run

src/hashtable/grow.c

Lines changed: 0 additions & 88 deletions
This file was deleted.

src/hashtable/hashtable.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,44 @@ uint64_t hash(char *key) {
1212
return hash;
1313
}
1414

15-
struct HashTable *create_hashtable(const uint32_t default_size, const double grow_factor) {
15+
struct HashTable *create_hashtable(const uint32_t default_size, const double grow_factor, const double shrink_factor) {
1616
struct HashTable *table = malloc(sizeof(struct HashTable));
1717
table->fvs = calloc(default_size, sizeof(struct FVPair *));
1818
table->size.allocated = default_size;
1919
table->size.all = 0;
2020
table->size.filled = 0;
2121
table->grow_factor = grow_factor;
22+
table->shrink_factor = shrink_factor;
2223

2324
return table;
2425
}
2526

27+
void resize_hashtable(struct HashTable *table, const uint32_t size) {
28+
struct FVPair **fvs = calloc(size, sizeof(struct FVPair *));
29+
table->size.filled = 0;
30+
31+
for (uint32_t i = 0; i < table->size.allocated; ++i) {
32+
struct FVPair *fv = table->fvs[i];
33+
34+
while (fv) {
35+
struct FVPair *next = fv->next;
36+
fv->next = NULL;
37+
const uint32_t index = fv->hash % size;
38+
struct FVPair **area = &fvs[index];
39+
40+
if (!*area) table->size.filled += 1;
41+
while (*area) area = &(*area)->next;
42+
43+
*area = fv;
44+
fv = next;
45+
}
46+
}
47+
48+
free(table->fvs);
49+
table->size.allocated = size;
50+
table->fvs = fvs;
51+
}
52+
2653
struct FVPair *get_fv_from_hashtable(struct HashTable *table, char *name) {
2754
const uint32_t index = hash(name) % table->size.allocated;
2855
struct FVPair *fv = table->fvs[index];

src/hashtable/memory.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#include "../../headers/hashtable.h"
2+
#include "../../headers/utils.h"
3+
4+
#include <string.h>
5+
#include <stdlib.h>
6+
#include <stdint.h>
7+
#include <stdbool.h>
8+
9+
void add_fv_to_hashtable(struct HashTable *table, const string_t name, void *value, const enum TellyTypes type) {
10+
const uint64_t hashed = hash(name.value);
11+
uint32_t index = hashed % table->size.allocated;
12+
13+
table->size.all += 1;
14+
15+
struct FVPair *fv;
16+
bool found = false;
17+
18+
if ((fv = table->fvs[index])) {
19+
do {
20+
if (streq(fv->name.value, name.value)) {
21+
found = true;
22+
break;
23+
} else if (fv->next) fv = fv->next;
24+
else break;
25+
} while (fv);
26+
} else {
27+
table->size.filled += 1;
28+
}
29+
30+
if (table->size.allocated == table->size.filled) {
31+
resize_hashtable(table, (table->size.allocated * (1 + table->grow_factor)));
32+
index = hashed % table->size.allocated;
33+
34+
if (!table->fvs[index]) table->size.filled += 1;
35+
}
36+
37+
if (fv) {
38+
if (found) {
39+
if (fv->type == TELLY_STR && type != TELLY_STR) free(fv->value.string.value);
40+
fv->type = type;
41+
set_fv_value(fv, value);
42+
} else {
43+
fv->next = malloc(sizeof(struct FVPair));
44+
fv = fv->next;
45+
}
46+
} else {
47+
fv = malloc(sizeof(struct FVPair));
48+
table->fvs[index] = fv;
49+
}
50+
51+
fv->type = type;
52+
fv->hash = hashed;
53+
fv->next = NULL;
54+
55+
const uint32_t size = name.len + 1;
56+
fv->name.len = name.len;
57+
fv->name.value = malloc(size);
58+
memcpy(fv->name.value, name.value, size);
59+
60+
set_fv_value(fv, value);
61+
}
62+
63+
bool del_fv_to_hashtable(struct HashTable *table, const string_t name) {
64+
const uint64_t hashed = hash(name.value);
65+
uint32_t index = hashed % table->size.allocated;
66+
67+
struct FVPair *fv;
68+
struct FVPair *prev = NULL;
69+
70+
if ((fv = table->fvs[index])) {
71+
do {
72+
if (streq(fv->name.value, name.value)) {
73+
// b is element will be deleted
74+
if (prev) { // a b a or a a b
75+
prev->next = fv->next;
76+
} else if (!fv->next) { // b or ~~a a b~~
77+
table->size.filled -= 1;
78+
table->fvs[index] = NULL;
79+
80+
const uint32_t size = (table->size.allocated * table->shrink_factor);
81+
82+
if (size == table->size.filled) {
83+
resize_hashtable(table, size);
84+
}
85+
} else { // b a a
86+
table->fvs[index] = fv->next;
87+
}
88+
89+
table->size.all -= 1;
90+
free_fv(fv);
91+
return true;
92+
} else if (fv->next) {
93+
prev = fv;
94+
fv = fv->next;
95+
} else {
96+
return false;
97+
}
98+
} while (fv);
99+
}
100+
101+
return false;
102+
}

0 commit comments

Comments
 (0)