Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
cs744-project1-final
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
Naman Dixit
cs744-project1-final
Commits
d652def6
Commit
d652def6
authored
Oct 23, 2019
by
Naman Dixit
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Done, hopefully
parents
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
2742 additions
and
0 deletions
+2742
-0
.gitignore
.gitignore
+4
-0
README.txt
README.txt
+54
-0
client/Makefile
client/Makefile
+15
-0
client/client.c
client/client.c
+193
-0
common/aux.h
common/aux.h
+663
-0
common/xml.h
common/xml.h
+609
-0
server/Makefile
server/Makefile
+15
-0
server/cache.c
server/cache.c
+164
-0
server/command_buffer.c
server/command_buffer.c
+172
-0
server/dump.c
server/dump.c
+95
-0
server/result_buffer.c
server/result_buffer.c
+67
-0
server/server.c
server/server.c
+454
-0
server/storage.c
server/storage.c
+237
-0
No files found.
.gitignore
0 → 100644
View file @
d652def6
server/data_store
server/server
client/client
README.txt
0 → 100644
View file @
d652def6
Team members:
Naman Dixit
19305R005
namandixit@cse.iitb.ac.in
Varad Bhatnagar
19305R005
varadhbhatnagar@cse.iitb.ac.in
Instructions:
Server:
Compilation: Run `make` in "server/" directory.
Execution: Run `./server` in "server/" directory.
It supports the following command line parameters:
1. -port=XXX (the port number on which the server
will listen)
2. -threadPoolSize=YYY (number of threads in the
thread pool)
3. -numSetsInCache=ZZZ (number of sets in the cache)
4. -sizeOfSet=AAA (associativity of sets)
Storage: The server stores the data in "${PWD}/data_store/" directory.
A file of special note is "${PWD}/data_store/kvstore.xml" which
contains an XML dump of the stored key-value pairs. This is the
file to be used for grading, and is generated in a lazy fashion
as instructed here:
moodle.iitb.ac.in/mod/forum/discuss.php?d=125057#p191609
Logs: When the server start, it prints a log message:
Log: Waiting for connection on port <port>...
When a new connection is established, it prints:
Log: Connection made: client_fd=<client_fd>
If there are any errors, it will quit after printing a message
prefixed with "Error:".
Client:
Compilation: Run `make` in "client/" directory.
Execution: The client can be launched inside "client/" directory
in either:
1. interactive mode: `./client -i`
2. batch mode: `./client <input_file> <output_file>`
It supports the following command line parameters:
1. -i (run in interactive mode)
2. -port=XXX (the port number on which the client
will connect)
Logs: When the server start, it prints a log message:
Log: Starting communication with server on port <port>...
If there are any errors, it will quit after printing a message
prefixed with "Error:". If wrong command line parameters are
given, it quits after printing a help message prefixed with
"Help:".
client/Makefile
0 → 100644
View file @
d652def6
ClientSource
:=
client.c
ClientTarget
:=
client
.PHONY
:
all ClientTarget
all
:
ClientTarget
ClientTarget
:
@
echo
"Building client..."
@
gcc
-g3
-O0
-fno-strict-aliasing
-fwrapv
-msse2
-I
../common
\
--std
=
c11
-DBUILD_INTERNAL
-DBUILD_SLOW
-D_POSIX_C_SOURCE
=
200809L
\
-Wall
-Wextra
-Wpedantic
-pedantic-errors
-Werror
\
$(ClientSource)
-o
${ClientTarget}
\
-Wl
,-rpath
=
\$
$ORIGIN
-Wl
,-z,origin
-Wl
,--enable-new-dtags
\
-static-libgcc
client/client.c
0 → 100644
View file @
d652def6
/*
* Creator: Naman Dixit
* Notice: © Copyright 2019 Naman Dixit
*/
#include "aux.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <ctype.h>
#if 0
# define log(...) printf("Log: " __VA_ARGS__)
#else
# define log(...)
#endif
#include "xml.h"
Char
*
tokenGet
(
Char
*
line
,
Char
separator
,
Size
*
position
)
{
Char
*
elem
=
NULL
;
Size
i
=
*
position
;
Size
line_len
=
strlen
(
line
);
while
((
i
<
line_len
)
&&
(
line
[
i
]
!=
'\n'
)
&&
(
line
[
i
]
!=
separator
))
{
sbufPrint
(
elem
,
"%c"
,
line
[
i
]);
i
++
;
}
sbufPrint
(
elem
,
"%c"
,
'\0'
);
*
position
=
i
+
1
;
return
elem
;
}
int
main
(
int
argc
,
char
**
argv
)
{
B32
interactive_mode
=
false
;
Char
*
port
=
NULL
;
for
(
Size
i
=
1
;
i
<
(
Size
)
argc
;
i
++
)
{
Size
arg_pos
=
0
;
if
((
arg_pos
=
strprefix
(
"-i"
,
argv
[
i
])))
{
interactive_mode
=
true
;
}
else
if
((
arg_pos
=
strprefix
(
"--interactive"
,
argv
[
i
])))
{
interactive_mode
=
true
;
}
else
if
((
arg_pos
=
strprefix
(
"-port="
,
argv
[
i
])))
{
port
=
&
(
argv
[
i
][
arg_pos
]);
}
}
if
(
port
==
NULL
)
port
=
"8080"
;
FILE
*
file_input
=
NULL
,
*
file_output
=
NULL
;
if
(
interactive_mode
==
false
)
{
if
(
argc
!=
3
)
{
printf
(
"Help: Incorrect command line parameters
\n
"
);
printf
(
"Help: See README.txt file for info on correct invocation
\n
"
);
exit
(
-
1
);
}
Char
*
file_input_name
=
argv
[
1
];
Char
*
file_output_name
=
argv
[
2
];
if
((
file_input
=
fopen
(
file_input_name
,
"r"
))
==
NULL
)
{
fprintf
(
stderr
,
"Error: The input file %s cannot be opened
\n
"
,
file_input_name
);
exit
(
-
1
);
}
if
((
file_output
=
fopen
(
file_output_name
,
"w"
))
==
NULL
)
{
fprintf
(
stderr
,
"Error: The output file %s cannot be opened
\n
"
,
file_output_name
);
exit
(
-
1
);
}
}
else
{
file_input
=
stdin
;
file_output
=
stdout
;
}
Sint
sock_fd
=
socket
(
AF_INET
,
SOCK_STREAM
,
0
);
struct
addrinfo
hints
=
{.
ai_family
=
AF_INET
,
.
ai_socktype
=
SOCK_STREAM
};
struct
addrinfo
*
result
=
NULL
;
Sint
s
=
getaddrinfo
(
NULL
,
"8080"
,
&
hints
,
&
result
);
if
(
s
!=
0
)
{
fprintf
(
stderr
,
"Error: getaddrinfo: %s
\n
"
,
gai_strerror
(
s
));
exit
(
-
1
);
}
while
(
connect
(
sock_fd
,
result
->
ai_addr
,
result
->
ai_addrlen
)
==
-
1
)
{
fprintf
(
stderr
,
"Error: Couldn't connect on port %s, trying again in one second...
\n
"
,
port
);
sleep
(
1
);
}
printf
(
"Log: Starting communication with server on port %s...
\n
"
,
port
);
while
(
true
)
{
if
(
interactive_mode
==
true
)
{
printf
(
"===QUERY: "
);
}
Char
*
command
=
NULL
;
{
// Read command
Sint
c
=
0
;
while
(((
c
=
getc
(
file_input
))
!=
'\n'
)
&&
(
c
!=
EOF
))
{
sbufPrint
(
command
,
"%c"
,
(
Char
)
c
);
}
if
(
interactive_mode
&&
(
c
==
EOF
))
{
printf
(
"
\n
"
);
break
;
}
if
(
command
==
NULL
)
{
break
;
}
sbufPrint
(
command
,
"%c"
,
'\0'
);
}
if
(
interactive_mode
==
true
)
{
printf
(
"RESPONSE: "
);
}
Char
*
xml_request_message
=
NULL
;
// Response message will be of proper legth, since server will make sure of that.
Size
response_size
=
KiB
(
300
);
Char
*
xml_response_message
=
calloc
(
response_size
,
sizeof
(
*
xml_response_message
));
Size
index
=
0
;
Char
*
query_type
=
tokenGet
(
command
,
','
,
&
index
);
Char
*
query_key
=
tokenGet
(
command
,
','
,
&
index
);
if
(
strcmp
(
query_type
,
"GET"
)
==
0
)
{
xml_request_message
=
xmlCreateMessage
(
XML_Message_Kind_GET
,
query_key
,
NULL
,
NULL
);
write
(
sock_fd
,
xml_request_message
,
strlen
(
xml_request_message
));
read
(
sock_fd
,
xml_response_message
,
response_size
);
}
else
if
(
strcmp
(
query_type
,
"SET"
)
==
0
)
{
Char
*
query_value
=
tokenGet
(
command
,
'\n'
,
&
index
);
xml_request_message
=
xmlCreateMessage
(
XML_Message_Kind_PUT
,
query_key
,
query_value
,
NULL
);
sbufDelete
(
query_value
);
write
(
sock_fd
,
xml_request_message
,
strlen
(
xml_request_message
));
read
(
sock_fd
,
xml_response_message
,
response_size
);
}
else
if
(
strcmp
(
query_type
,
"DEL"
)
==
0
)
{
xml_request_message
=
xmlCreateMessage
(
XML_Message_Kind_DELETE
,
query_key
,
NULL
,
NULL
);
write
(
sock_fd
,
xml_request_message
,
strlen
(
xml_request_message
));
read
(
sock_fd
,
xml_response_message
,
response_size
);
}
sbufDelete
(
query_type
);
sbufDelete
(
query_key
);
XML_Message
msg
=
xmlParseMessage
(
xml_response_message
);
switch
(
msg
.
kind
)
{
case
XML_Message_Kind_RESP_GET
:
{
fprintf
(
file_output
,
"%s,%s
\n
"
,
msg
.
key
,
msg
.
value
);
}
break
;
case
XML_Message_Kind_RESP_PUT_DELETE
:
{
fprintf
(
file_output
,
"success
\n
"
);
}
break
;
case
XML_Message_Kind_RESP_ERROR
:
{
fprintf
(
file_output
,
"error,%s
\n
"
,
msg
.
error
);
}
break
;
default:
{
}
break
;
}
sbufDelete
(
xml_request_message
);
free
(
xml_response_message
);
free
(
msg
.
key
);
free
(
msg
.
value
);
free
(
msg
.
error
);
}
return
0
;
}
common/aux.h
0 → 100644
View file @
d652def6
#if !defined(AUX_H_INCLUDE_GUARD)
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdalign.h>
typedef
int8_t
S8
;
typedef
int16_t
S16
;
typedef
int32_t
S32
;
typedef
int64_t
S64
;
typedef
int
Sint
;
typedef
uint8_t
U8
;
typedef
uint16_t
U16
;
typedef
uint32_t
U32
;
typedef
uint64_t
U64
;
typedef
unsigned
Uint
;
typedef
size_t
Size
;
typedef
uintptr_t
Uptr
;
typedef
intptr_t
Sptr
;
typedef
ptrdiff_t
Dptr
;
typedef
float
F32
;
typedef
double
F64
;
typedef
U8
B8
;
typedef
U16
B16
;
typedef
U32
B32
;
typedef
U64
B64
;
# define true 1U
# define false 0U
typedef
unsigned
char
Byte
;
typedef
char
Char
;
# define elemin(array) (sizeof(array)/sizeof((array)[0]))
# if !defined(max)
# define max(x, y) ((x) >= (y) ? (x) : (y))
# endif
# if !defined(min)
# define min(x, y) ((x) <= (y) ? (x) : (y))
# endif
# define KiB(x) ( (x) * 1024ULL)
# define MiB(x) (KiB(x) * 1024ULL)
# define GiB(x) (MiB(x) * 1024ULL)
# define TiB(x) (GiB(x) * 1024ULL)
# define THOUSAND 1000L
# define MILLION 1000000L
# define BILLION 1000000000L
# define unused_variable(var) (void)var
# define global_variable static
# define persistent_value static
# define internal_function static
# define header_function static inline
#define MEM_MAX_ALIGN_MINUS_ONE (alignof(max_align_t) - 1u)
#define memAlignUp(p) (((p) + MEM_MAX_ALIGN_MINUS_ONE) & (~ MEM_MAX_ALIGN_MINUS_ONE))
#define memAlignDown(p) (mem_ALIGN_UP((p) - MEM_MAX_ALIGN_MINUS_ONE))
typedef
enum
MemAllocMode
{
MemAllocMode_NONE
,
MemAllocMode_CREATE
,
MemAllocMode_ALLOC
,
MemAllocMode_REALLOC
,
MemAllocMode_DEALLOC
,
MemAllocMode_DEALLOC_ALL
,
}
MemAllocMode
;
# define MEM_ALLOCATOR(allocator) \
void* allocator(MemAllocMode mode, \
Size size, void* old_ptr, \
void *data)
typedef
MEM_ALLOCATOR
(
MemAllocator
);
struct
MemCRT_Header
{
Size
size
;
};
# define memCRTAlloc(size) memCRT(MemAllocMode_ALLOC, size, NULL, NULL)
# define memCRTRealloc(ptr, size) memCRT(MemAllocMode_REALLOC, size, ptr, NULL)
# define memCRTDealloc(ptr) memCRT(MemAllocMode_DEALLOC, 0, ptr, NULL)
header_function
MEM_ALLOCATOR
(
memCRT
)
{
unused_variable
(
data
);
switch
(
mode
)
{
case
MemAllocMode_CREATE
:
{
// NOTE(naman): Not needed for now
}
break
;
case
MemAllocMode_ALLOC
:
{
Size
memory_size
=
memAlignUp
(
size
);
Size
header_size
=
memAlignUp
(
sizeof
(
struct
MemCRT_Header
));
Size
total_size
=
memory_size
+
header_size
;
Byte
*
mem
=
malloc
(
total_size
);
struct
MemCRT_Header
*
header
=
(
struct
MemCRT_Header
*
)
mem
;
header
->
size
=
memory_size
;
Byte
*
result
=
mem
+
header_size
;
memset
(
result
,
0
,
memory_size
);
return
result
;
}
break
;
case
MemAllocMode_REALLOC
:
{
Size
memory_size
=
memAlignUp
(
size
);
Size
header_size
=
memAlignUp
(
sizeof
(
struct
MemCRT_Header
));
Size
total_size
=
memory_size
+
header_size
;
Byte
*
mem
=
malloc
(
total_size
);
struct
MemCRT_Header
*
header
=
(
struct
MemCRT_Header
*
)
mem
;
header
->
size
=
memory_size
;
Byte
*
result
=
mem
+
header_size
;
if
(
old_ptr
!=
NULL
)
{
Byte
*
previous_mem
=
(
Byte
*
)
old_ptr
-
header_size
;
struct
MemCRT_Header
*
previous_header
=
(
struct
MemCRT_Header
*
)
previous_mem
;
Size
previous_size
=
previous_header
->
size
;
memcpy
(
result
,
old_ptr
,
previous_size
);
memset
(
result
+
previous_size
,
0
,
memory_size
-
previous_size
);
memCRTDealloc
(
old_ptr
);
}
else
{
memset
(
result
,
0
,
memory_size
);
}
return
result
;
}
break
;
case
MemAllocMode_DEALLOC
:
{
if
(
old_ptr
==
NULL
)
{
return
NULL
;
}
Size
header_size
=
memAlignUp
(
sizeof
(
struct
MemCRT_Header
));
Byte
*
mem
=
(
Byte
*
)
old_ptr
-
header_size
;
free
(
mem
);
}
break
;
case
MemAllocMode_DEALLOC_ALL
:
{
// TODO(naman): Maybe we should use a off-the-shelf malloc that allows this?
}
break
;
case
MemAllocMode_NONE
:
{
}
break
;
}
return
NULL
;
}
/* ==============
* Strechy Buffer
*/
/* API ----------------------------------------
* Size sbufAdd (T *ptr, T elem)
* void sbufDelete (T *ptr)
* T* sbufEnd (T *ptr)
*
* Size sbufSizeof (T *ptr)
* Size sbufElemin (T *ptr)
* Size sbufMaxSizeof (T *ptr)
* Size sbufMaxElemin (T *ptr)
*/
typedef
struct
Sbuf_Header
{
Size
cap
;
// NOTE(naman): Maximum number of elements that can be stored
Size
len
;
// NOTE(naman): Count of elements actually stored
Byte
buf
[];
}
Sbuf_Header
;
# define sbuf_GetHeader(sb) ((Sbuf_Header*)(void*)((Byte*)(sb) - offsetof(Sbuf_Header, buf)))
# define sbuf_Len(sb) ((sb) ? sbuf_GetHeader(sb)->len : 0U)
# define sbuf_Cap(sb) ((sb) ? sbuf_GetHeader(sb)->cap : 0U)
# define sbufAdd(sb, ...) ((sb) = sbuf_Grow((sb), sizeof(*(sb))), \
(sb)[sbuf_Len(sb)] = (__VA_ARGS__), \
((sbuf_GetHeader(sb))->len)++)
# define sbufDelete(sb) ((sb) ? \
(memCRTDealloc(sbuf_GetHeader(sb)), (sb) = NULL) : \
0)
# define sbufClear(sb) ((sb) ? \
(memset((sb), 0, sbufSizeof(sb)), \
sbuf_GetHeader(sb)->len = 0) : \
0)
# define sbufResize(sb, n) (((n) > sbufMaxElemin(sb)) ? \
((sb) = sbuf_Resize(sb, n, sizeof(*(sb)))) : \
0)
# define sbufSizeof(sb) (sbuf_Len(sb) * sizeof(*(sb)))
# define sbufElemin(sb) (sbuf_Len(sb))
# define sbufMaxSizeof(sb) (sbuf_Cap(sb) * sizeof(*(sb)))
# define sbufMaxElemin(sb) (sbuf_Cap(sb))
# define sbufEnd(sb) ((sb) + sbuf_Len(sb))
#define sbufPrint(sb, ...) ((sb) = sbuf_Print((sb), __VA_ARGS__))
#define sbufUnsortedDelete(sb, i, z) (((sb)[(i)] = (sb)[sbuf_Len(sb) - 1]), \
((sb)[sbuf_Len(sb) - 1] = (z)), \
((sbuf_GetHeader(sb)->len)--))
# if defined(COMPILER_CLANG)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wcast-align"
# endif
header_function
void
*
sbuf_Grow
(
void
*
buf
,
Size
elem_size
)
{
if
((
sbuf_Len
(
buf
)
+
1
)
<=
sbuf_Cap
(
buf
))
{
return
buf
;
}
else
{
Size
new_cap
=
max
(
2
*
sbuf_Cap
(
buf
),
4
);
Size
new_size
=
(
new_cap
*
elem_size
)
+
sizeof
(
Sbuf_Header
);
Sbuf_Header
*
new_header
=
NULL
;
if
(
buf
!=
NULL
)
{
new_header
=
memCRTRealloc
(
sbuf_GetHeader
(
buf
),
new_size
);
}
else
{
new_header
=
memCRTAlloc
(
new_size
);
}
new_header
->
cap
=
new_cap
;
return
new_header
->
buf
;
}
}
header_function
void
*
sbuf_Resize
(
void
*
buf
,
Size
elem_count
,
Size
elem_size
)
{
Size
new_cap
=
elem_count
;
Size
new_size
=
(
new_cap
*
elem_size
)
+
sizeof
(
Sbuf_Header
);
Sbuf_Header
*
new_header
=
NULL
;
if
(
buf
!=
NULL
)
{
new_header
=
memCRTRealloc
(
sbuf_GetHeader
(
buf
),
new_size
);
}
else
{
new_header
=
memCRTAlloc
(
new_size
);
}
new_header
->
cap
=
new_cap
;
return
new_header
->
buf
;
}
header_function
Char
*
sbuf_Print
(
Char
*
buf
,
const
Char
*
fmt
,
...)
{
va_list
args
;
va_start
(
args
,
fmt
);
Size
cap
=
sbufMaxElemin
(
buf
)
-
sbufElemin
(
buf
);
Size
n
=
1
+
vsnprintf
(
sbufEnd
(
buf
),
cap
,
fmt
,
args
);
va_end
(
args
);
if
(
n
>
cap
)
{
sbufResize
(
buf
,
n
+
sbufElemin
(
buf
));
va_start
(
args
,
fmt
);
size_t
new_cap
=
sbufMaxElemin
(
buf
)
-
sbufElemin
(
buf
);
n
=
1
+
vsnprintf
(
sbufEnd
(
buf
),
new_cap
,
fmt
,
args
);
va_end
(
args
);
}
sbuf_GetHeader
(
buf
)
->
len
+=
(
n
-
1
);
return
buf
;
}
header_function
U64
hashFNV1a
(
Char
*
str
)
{
U64
hash
=
0xcbf29ce484222325
;
// FNV_offset_basis
for
(
Size
i
=
0
;
str
[
i
]
!=
'\0'
;
i
++
)
{
hash
=
hash
^
str
[
i
];
hash
=
hash
*
0x100000001b3
;
// FNV_prime
}
return
hash
;
}
/* __builtin_clzll(x) returns the leading number of 0-bits in x, starting from
* most significant position.
*
* If b is the bit-width of the number,
* p is the closest lower power of two and
* lz is the number of leading 0-bits; then
* then a number between 2^p and 2^(p+1) has the form: (b-p-1 0-bits) 1 (p bits)
*
* => lz = b-p-1
* => p = b-(lz+1)
*
* Thus, the rounded-down log2 of the number is b-(lz+1).
*/
header_function
U64
u64Log2
(
U64
x
)
{
U64
result
=
64ULL
-
((
U64
)
__builtin_clzll
(
x
)
+
1ULL
);
return
result
;
}
/* Linear Congruential Generator
*
* If x is the last random number,
* m is a number greater than zero that is a power of two,
* a is a number between 0 and m,
* then the next random number is ((x * a) % m).
*
* Unfortunately, the lower bits don't have enought randomness in them. The LSB doesn't
* change at all, the second LSB alternates, the one after that toggles every 2 turns and so
* on. Therefore, we try to get rid of the LSBs by pulling in some MSBs.
*
* We do the multiplcation twice because Chi-Square Test indicated that this method
* gives better randomness. Don't ask.
*
* NOTE(naman): Seed should be an odd number or the randomness might drop drastically.
*/
header_function
U64
u64Rand
(
U64
seed
)
{
U64
previous
=
seed
;
if
(
previous
==
0
)
{
// This seed has been tested and should be preferred in normal circumstances.
previous
=
2531011ULL
;
}
U64
a
=
214013ULL
;
__uint128_t
product
=
(
__uint128_t
)
previous
*
(
__uint128_t
)
a
;
U64
upper
=
product
>>
64
,
lower
=
(
U64
)
product
;
U64
log_upper
=
u64Log2
(
upper
);
U64
shift_amount
=
64
-
(
log_upper
+
1
);
upper
=
(
upper
<<
shift_amount
)
|
(
lower
>>
log_upper
);
U64
result
=
upper
*
a
;
return
result
;
}
/* ==========================
* Pointer-Pointer Hash Map
*/
typedef
struct
Hashmap
{
struct
HashmapKeys
{
U64
hash
;
Uptr
key
;
}
*
keys
;
Uptr
*
values
;
MemAllocator
*
allocator
;
Size
total_slots
,
filled_slots
;
U64
a
,
b
,
m
;
/* Hashing constants */
U64
r
;
/* Last random number */
}
Hashmap
;
typedef
enum
HM_Flag
{
HM_Flag_EMPTY
,
HM_Flag_FILLED
,
HM_Flag_VACATED
,
// Previously filled
}
HM_Flag
;
/* API ---------------------------------------------------
* Hashmap hmCreate (MemAllocator allocator,
* Size min_slots);
* void hmDelete (Hashmap hm);
* void* hmInsert (Hashmap *hm, void *key, void *value);
* Uptr hmInsertI (Hashmap *hm, Uptr key, Uptr value);
* void* hmLookup (Hashmap *hm, void *key);
* Uptr hmLookupI (Hashmap *hm, Uptr key);
* void* hmRemove (Hashmap *hm, void *key);
* Uptr hmRemoveI (Hashmap *hm, Uptr key);
*/
#define hm_GetFlag(hash) ((hash) >> 62)
#define hm_GetHash(hash) ((hash) & 0x3FFFFFFFFFFFFFFFULL)
#define hm_SetFlag(hash, flag) (hm_GetHash(hash) | (((U64)(flag)) << 62))
/*
* https://en.wikipedia.org/wiki/Universal_hashing#Avoiding_modular_arithmetic
* w is number of bits in machine word (64 in our case)
* s is the number of buckets/bins (slots in the hash table) to which the
* universe of hashable objects is to be mapped
* m is log2(s) (=> m = 2^s) and is equal to the number of bits in the final hash
* a is a random odd positive integer < 2^w (fitting in w bits)
* b is a random non-negative integer < 2^(w-m) (fitting in (w-m) bits)
*
* r is the last random number generated and is just an implementation detail.
*/
header_function
void
hm_UpdateConstants
(
Hashmap
*
hm
)
{
hm
->
m
=
hm
->
m
+
1
;
hm
->
total_slots
=
1ULL
<<
(
hm
->
m
);
do
{
hm
->
r
=
u64Rand
(
hm
->
r
);
hm
->
a
=
hm
->
r
;
}
while
((
hm
->
a
&
0x01
)
!=
0x01
);
// Make sure that hm.a is odd
hm
->
r
=
u64Rand
(
hm
->
r
);
// b should be (64 - m) bits long
hm
->
b
=
hm
->
r
>>
hm
->
m
;
}
header_function
U64
hm_Hash
(
Hashmap
*
hm
,
Uptr
key
)
{
U64
result
=
((
hm
->
a
*
key
)
+
hm
->
b
)
&
(
0xFFFFFFFFFFFFFFFFULL
>>
(
64
-
hm
->
m
));
return
result
;
}
header_function
Hashmap
hmCreate
(
MemAllocator
allocator
,
Size
min_slots
)
{
Hashmap
hm
=
{
0
};
hm
.
allocator
=
allocator
;
hm
.
m
=
u64Log2
(
min_slots
);
// Log of closest lower power of two
// This will make m log of closest upper power of two
hm_UpdateConstants
(
&
hm
);
hm
.
keys
=
allocator
(
MemAllocMode_ALLOC
,
(
hm
.
total_slots
)
*
sizeof
(
*
(
hm
.
keys
)),
NULL
,
NULL
);
hm
.
values
=
allocator
(
MemAllocMode_ALLOC
,
(
hm
.
total_slots
)
*
sizeof
(
*
(
hm
.
values
)),
NULL
,
NULL
);
// For NULL keys
hm
.
keys
[
0
].
hash
=
hm_SetFlag
(
hm
.
keys
[
0
].
hash
,
HM_Flag_FILLED
);
hm
.
filled_slots
=
1
;
return
hm
;
}
header_function
void
hmDelete
(
Hashmap
hm
)
{
hm
.
allocator
(
MemAllocMode_DEALLOC
,
0
,
hm
.
keys
,
NULL
);
hm
.
allocator
(
MemAllocMode_DEALLOC
,
0
,
hm
.
values
,
NULL
);
}
header_function
Size
hm_LinearProbeSearch
(
Hashmap
*
hm
,
Uptr
key
)
{
if
(
key
==
0
)
return
0
;
U64
hash
=
hm_Hash
(
hm
,
key
);
U64
hash_real
=
hm_GetHash
(
hash
);
Size
index
=
0
,
i
=
0
;