Skip to content

Commit 1463741

Browse files
chore: implement hashmap and remove STL map
1 parent c0f11e5 commit 1463741

File tree

3 files changed

+149
-78
lines changed

3 files changed

+149
-78
lines changed

include/common.hpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,32 @@
55
#include <stdint.h>
66

77
// typeof(((type *)0)->member) is used to determine the type of member in struct type.
8-
#define container_of(ptr, type, member) \
9-
({ \
10-
const typeof(((type *)0)->member) *__mptr = (ptr); \
11-
(type *)((char *)__mptr - offsetof(type, member)); \
12-
})
8+
// define container_of(ptr, type, member)
9+
// ({
10+
// const typeof(((type *)0)->member) *__mptr = (ptr);
11+
// (type *)((char *)__mptr - offsetof(type, member));
12+
// })
1313

14-
inline uint64_t str_hash(const uint8_t *data, size_t len) {
15-
uint32_t h = 0x811C9DC5;
14+
#define container_of(ptr, type, member) \
15+
reinterpret_cast<type*>(reinterpret_cast<char*>(ptr) - offsetof(type, member))
16+
17+
// Fowler-Noll-Vo hash
18+
inline uint64_t str_hash(const uint8_t* data, size_t len) {
19+
uint32_t h = 0x811C9DC5; // FNV-1a offset basis
1620
for (size_t i = 0; i < len; i++) {
17-
h = (h + data[i]) * 0x01000193;
21+
h = (h + data[i]) * 0x01000193; // FNV prime multiplication
1822
}
1923
return h;
2024
}
2125

26+
// Hash function (djb2 algorithm)
27+
inline unsigned long hash_function(const char* str) {
28+
unsigned long hash = 5381;
29+
int c;
30+
while ((c = *str++)) {
31+
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
32+
}
33+
return hash % HASH_TABLE_SIZE;
34+
}
35+
2236
#endif // COMMON_HPP_

include/hashmap.hpp

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <stdlib.h>
88

99
constexpr size_t kRehasingWork = 128;
10+
constexpr size_t kMaxLoadFactor = 8;
1011

1112
struct HNode {
1213
HNode *next = nullptr;
@@ -75,6 +76,17 @@ struct HTab {
7576
return node;
7677
}
7778

79+
bool foreach (bool (*f)(HNode *, void *), void *arg) {
80+
for (size_t i = 0; this->mask != 0 && i <= this->mask; i++) {
81+
for (HNode *node = this->tab[i]; node != nullptr; node = node->next) {
82+
if (!f(node, arg)) {
83+
return false;
84+
}
85+
}
86+
}
87+
return true;
88+
}
89+
7890
private:
7991
size_t get_position(uint64_t hcode) {
8092
// Example: If hcode is 7 ans mask is 3 (it means 4 nodes)
@@ -89,12 +101,51 @@ struct HMap {
89101
HTab older;
90102
size_t migrate_pos = 0;
91103

92-
HNode *lookup(HNode *key, bool (*eq)(HNode *, HNode *));
93-
void insert(HNode *node);
94-
HNode *hm_delete(HNode *key, bool (*eq)(HNode *, HNode *));
95-
void clear();
96-
size_t size();
97-
void foreach (bool (*f)(HNode *, void *), void *arg);
104+
HNode *lookup(HNode *key, bool (*eq)(HNode *, HNode *)) {
105+
help_rehashing();
106+
HNode **from = this->newer.lookup(key, eq);
107+
if (!from) {
108+
from = this->older.lookup(key, eq);
109+
}
110+
return from ? *from : nullptr;
111+
}
112+
113+
void insert(HNode *node) {
114+
if (!this->newer.tab) {
115+
this->newer.init(4);
116+
}
117+
this->newer.insert(node);
118+
119+
if (!this->older.tab) {
120+
size_t threshold = (this->newer.mask + 1) * kMaxLoadFactor;
121+
if (this->newer.size >= threshold) {
122+
trigger_rehashing();
123+
}
124+
}
125+
help_rehashing();
126+
}
127+
128+
HNode *hm_delete(HNode *key, bool (*eq)(HNode *, HNode *)) {
129+
help_rehashing();
130+
if (HNode **from = this->newer.lookup(key, eq)) {
131+
return this->newer.detach(from);
132+
}
133+
if (HNode **from = this->older.lookup(key, eq)) {
134+
return this->older.detach(from);
135+
}
136+
return nullptr;
137+
}
138+
139+
void clear() {
140+
free(this->newer.tab);
141+
free(this->older.tab);
142+
}
143+
144+
size_t size() { return this->newer.size + this->older.size; }
145+
146+
void foreach (bool (*f)(HNode *, void *), void *arg) {
147+
this->newer.foreach (f, arg) && this->older.foreach (f, arg);
148+
}
98149

99150
private:
100151
void help_rehashing() {
@@ -116,6 +167,13 @@ struct HMap {
116167
this->older = HTab{};
117168
}
118169
}
170+
171+
void trigger_rehashing() {
172+
assert(this->older.tab == nullptr);
173+
this->older = this->newer;
174+
this->newer.init((this->newer.mask + 1) * 2);
175+
this->migrate_pos = 0;
176+
}
119177
};
120178

121179
#endif // HASHMAP_HPP_

src/server.cpp

Lines changed: 63 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -13,65 +13,43 @@
1313

1414
#include <numeric>
1515
#include <string>
16-
#include <unordered_map>
1716

1817
#include "cacheX_protocol.hpp"
19-
20-
static std::vector<Conn *> fd2conn;
21-
22-
// TODO: Remove this
23-
static std::unordered_map<std::string, std::string> g_data;
24-
25-
typedef struct KeyValue {
26-
char *key;
27-
char *value;
28-
struct KeyValue *next;
29-
} KeyValue;
30-
31-
KeyValue *hash_table[HASH_TABLE_SIZE] = {nullptr};
32-
33-
// Hash function (djb2 algorithm)
34-
unsigned long hash_function(const char *str) {
35-
unsigned long hash = 5381;
36-
int c;
37-
while ((c = *str++)) {
38-
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
39-
}
40-
return hash % HASH_TABLE_SIZE;
18+
#include "common.hpp"
19+
#include "hashmap.hpp"
20+
21+
static struct {
22+
HMap db;
23+
std::vector<Conn *> fd2conn;
24+
} g_data;
25+
26+
struct LookupKey {
27+
struct HNode node;
28+
std::string key;
29+
};
30+
31+
struct Entry {
32+
struct HNode node;
33+
std::string key;
34+
std::string str;
35+
};
36+
37+
static Entry *entry_new() {
38+
Entry *ent = new Entry();
39+
return ent;
4140
}
4241

43-
void store_set_command(const char *key, const char *value) {
44-
unsigned long index = hash_function(key);
45-
46-
KeyValue *new_pair = (KeyValue *)malloc(sizeof(KeyValue));
47-
if (!new_pair) {
48-
fprintf(stderr, "[ERROR] Memory allocation failed for KeyValue\n");
49-
return;
50-
}
51-
52-
new_pair->key = strdup(key);
53-
new_pair->value = strdup(value);
54-
if (!new_pair->key || !new_pair->value) {
55-
fprintf(stderr, "[ERROR] Memory allocation failed for key/value\n");
56-
free(new_pair);
57-
return;
58-
}
59-
60-
new_pair->next = hash_table[index];
61-
hash_table[index] = new_pair;
42+
static void entry_del(Entry *ent) {
43+
// if (ent->type == T_ZSET) {
44+
// zset_clear(&ent->zset);
45+
// }
46+
delete ent;
6247
}
6348

64-
const char *fetch_get_command(const char *key) {
65-
unsigned long index = hash_function(key);
66-
KeyValue *entry = hash_table[index];
67-
68-
while (entry) {
69-
if (strcmp(entry->key, key) == 0) {
70-
return entry->value;
71-
}
72-
entry = entry->next;
73-
}
74-
return nullptr;
49+
static bool entry_eq(HNode *node, HNode *key) {
50+
struct Entry *ent = container_of(node, struct Entry, node);
51+
struct LookupKey *keydata = container_of(key, struct LookupKey, node);
52+
return ent->key == keydata->key;
7553
}
7654

7755
static void msg(int line_number, const char *format, ...) {
@@ -108,18 +86,39 @@ static int set_nonblocking(int sockfd) {
10886
}
10987

11088
static void process_request(std::vector<std::string> &cmd, Response &out) {
89+
LookupKey key;
11190
if (cmd.size() == 2 && cmd[0] == "GET") {
112-
auto it = g_data.find(cmd[1]);
113-
if (it == g_data.end()) {
91+
key.key.swap(cmd[1]);
92+
key.node.hcode = str_hash((uint8_t *)key.key.data(), key.key.size());
93+
HNode *node = g_data.db.lookup(&key.node, &entry_eq);
94+
if (!node) {
11495
out.status = RES_NX;
11596
return;
11697
} else {
117-
out.data.assign(it->second.begin(), it->second.end());
98+
Entry *ent = container_of(node, Entry, node);
99+
out.data.assign(ent->str.begin(), ent->str.end());
118100
}
119101
} else if (cmd.size() == 3 && cmd[0] == "SET") {
120-
g_data[cmd[1]] = std::move(cmd[2]);
102+
key.key.swap(cmd[1]);
103+
key.node.hcode = str_hash((uint8_t *)key.key.data(), key.key.size());
104+
HNode *node = g_data.db.lookup(&key.node, &entry_eq);
105+
if (node) {
106+
Entry *ent = container_of(node, Entry, node);
107+
ent->str.swap(cmd[2]);
108+
} else {
109+
Entry *ent = entry_new();
110+
ent->key.swap(key.key);
111+
ent->node.hcode = key.node.hcode;
112+
ent->str.swap(cmd[2]);
113+
g_data.db.insert(&ent->node);
114+
}
121115
} else if (cmd.size() == 2 && cmd[0] == "DEL") {
122-
g_data.erase(cmd[1]);
116+
key.key.swap(cmd[1]);
117+
key.node.hcode = str_hash((uint8_t *)key.key.data(), key.key.size());
118+
HNode *node = g_data.db.hm_delete(&key.node, &entry_eq);
119+
if (node) {
120+
entry_del(container_of(node, Entry, node));
121+
}
123122
} else {
124123
out.status = RES_ERR;
125124
fprintf(stderr, "[ERROR] Invalid command.\n");
@@ -317,15 +316,15 @@ void start_server() {
317316

318317
set_nonblocking(client_fd);
319318

320-
if (fd2conn.size() <= (size_t)client_fd) {
321-
fd2conn.resize(client_fd + 1);
319+
if (g_data.fd2conn.size() <= (size_t)client_fd) {
320+
g_data.fd2conn.resize(client_fd + 1);
322321
}
323-
if (fd2conn[client_fd] != nullptr) {
322+
if (g_data.fd2conn[client_fd] != nullptr) {
324323
fprintf(stderr,
325324
"[WARNING] Reusing file descriptor %d for new connection.\n",
326325
client_fd);
327326
}
328-
fd2conn[client_fd] = new Conn{client_fd, true, false, false, {}, {}};
327+
g_data.fd2conn[client_fd] = new Conn{client_fd, true, false, false, {}, {}};
329328
event.events = EPOLLIN | EPOLLET;
330329
event.data.fd = client_fd;
331330
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
@@ -338,7 +337,7 @@ void start_server() {
338337
continue;
339338
}
340339

341-
Conn *conn = fd2conn[fd];
340+
Conn *conn = g_data.fd2conn[fd];
342341
if (events[i].events & EPOLLIN) {
343342
handle_read(conn);
344343
}
@@ -348,7 +347,7 @@ void start_server() {
348347
if (conn->want_close) {
349348
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, conn->fd, nullptr);
350349
close(conn->fd);
351-
fd2conn[conn->fd] = nullptr;
350+
g_data.fd2conn[conn->fd] = nullptr;
352351
delete conn;
353352
}
354353
}

0 commit comments

Comments
 (0)