-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkvcached.c
169 lines (139 loc) · 3.87 KB
/
kvcached.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#include <stdio.h>
#include <errno.h>
#define NTWK_IMPL
#include "ntwk.h"
typedef struct Map {
struct Map *child[4];
s8 key, val;
bool deleted;
} Map;
typedef struct {
Arena scratch;
Map *map;
} Context;
Map *map_upsert(Map **m, s8 key, bool make);
// NOTE: val must be allocated from plain malloc to be freed
// NOTE: Can only fail if key does not exist
int map_delete(Map **m, s8 key, bool free_val);
Map *map_upsert(Map **m, s8 key, bool make) {
Map *least_deleted = NULL;
for (u64 h = s8_hash(key); *m; h <<= 2) {
if (s8_equals(key, (*m)->key)) return *m;
if ((*m)->deleted && least_deleted == NULL) {
least_deleted = *m;
}
m = &(*m)->child[h>>62];
}
if (!make) return NULL;
if (least_deleted != NULL) *m = least_deleted;
else *m = new(NULL, Map, 1);
(*m)->key = s8_copy(NULL, key);
return *m;
}
// NOTE: val must be allocated from plain malloc to be freed
// NOTE: Can only fail if key does not exist
int map_delete(Map **m, s8 key, bool free_val) {
Map *d = map_upsert(m, key, false);
if (d == NULL) return 1;
free(d->key.buf);
d->deleted = true;
if (free_val) free(d->val.buf);
d->key = d->val = (s8) {0};
return 0;
}
// struct ev_io *on_connect(i32 sock, Server *s) {
// return malloc(sizeof(struct ev_io));
// }
// void on_disconnect(i32 sock, Server *s, struct ev_io *cio) {
// free(cio);
// }
int on_recv(i32 sock, Server *s) {
Context *ctx = s->data;
int status = 0;
u8 command = recv_u8(sock, &status);
if (status) return status;
switch (command) {
case 'S': {
s8 key = recv_s8(&ctx->scratch, sock, &status);
if (status) break;
if (key.buf == NULL) break;
s8 val = recv_s8(&ctx->scratch, sock, &status);
if (status) break;
if (val.buf == NULL) break;
/* NOTE: Order of success messages depends on s8_copy not failing (it can't
* right now) */
Map *m = map_upsert(&ctx->map, key, true);
if (m->val.buf != NULL) {
free(m->val.buf);
send_s8(sock, s8("Successfully changed value of key.\n"));
} else send_s8(sock, s8("Successfully stored value in new key.\n"));
m->val = s8_copy(NULL, val);
// printf("Stored key ");
// printf("\"");
// s8_print(m->key);
// printf("\"");
// printf(" with value ");
// printf("\"");
// s8_print(m->val);
// printf("\"");
// printf("\n");
printf("Stored key.\n");
} break;
case 'G': {
s8 key = recv_s8(&ctx->scratch, sock, &status);
if (status) break;
if (key.buf == NULL) break;
Map *m = map_upsert(&ctx->map, key, false);
if (m == NULL) {
send_err(sock, s8("Error: No such key.\n"));
printf("Sent error.\n");
break;
}
send_s8(sock, m->val);
printf("Sent key.\n");
} break;
case 'D': {
s8 key = recv_s8(&ctx->scratch, sock, &status);
if (status) break;
if (key.buf == NULL) break;
if (map_delete(&ctx->map, key, true)) {
send_err(sock, s8("Error: No such key.\n"));
printf("Sent error.\n");
} else {
send_s8(sock, s8("Successfully deleted key.\n"));
printf("Deleted key.\n");
}
} break;
case 'C': {
// TODO: Traverse tree to free memory
ctx->map = NULL;
send_s8(sock, s8("Successfully cleared cache.\n"));
printf("Cleared cache.\n");
} break;
default: {
fprintf(stderr, "Warning: Client sent unrecognized request %d.\n", command);
send_err(sock, s8("Error: Unrecognized or invalid request.\n"));
}
}
return status;
}
void usage_err(char *argv[]) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
int main(int argc, char *argv[]) {
if (argc != 2) usage_err(argv);
Context ctx = {
.scratch = new_arena(20 * GiB),
};
Server s = {
.port = atoi(argv[1]),
.on_recv = on_recv,
.data = &ctx,
// .on_connect = on_connect,
// .on_disconnect = on_disconnect,
};
if (s.port == 0) usage_err(argv);
start_server(s);
return 0;
}