a lot of stuff tbh

This commit is contained in:
alessandrobason 2022-08-19 17:09:27 +01:00
parent d80773bb00
commit 8ddd9186b8
42 changed files with 6463 additions and 1593 deletions

View file

@ -1,16 +1,19 @@
add_library(colla STATIC add_library(colla STATIC
socket.c src/collatypes.h
tracelog.c src/vec.h
http.c src/win32_slim.h
strstream.c src/tracelog.h src/tracelog.c
str.c src/str.h src/str.c
coroutine.c src/hashmap.h src/hashmap.c
os.c src/utf8.h src/utf8.c
fs.c src/ini.h src/ini.c
file.c src/strstream.h src/strstream.c
dir.c src/os.h src/os.c
dirwatch.c src/file.h src/file.c
cthreads.c src/dir.h src/dir.c
src/socket.h src/socket.c
src/http.h src/http.c
src/cthreads.h src/cthreads.c
) )
if(MSVC) if(MSVC)
@ -20,4 +23,6 @@ else()
target_link_libraries(colla pthread) target_link_libraries(colla pthread)
target_compile_options(colla PRIVATE -Wall -Wextra -Wpedantic) target_compile_options(colla PRIVATE -Wall -Wextra -Wpedantic)
target_compile_definitions(colla PUBLIC _DEFAULT_SOURCE) target_compile_definitions(colla PUBLIC _DEFAULT_SOURCE)
endif() endif()
target_include_directories(colla PUBLIC src)

4431
colla.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -8,4 +8,4 @@ coroutine_t coInit() {
bool coIsDead(coroutine_t co) { bool coIsDead(coroutine_t co) {
return co.state == -1; return co.state == -1;
} }

View file

@ -130,4 +130,4 @@ __INC_COUNTER;
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -292,4 +292,4 @@ void stopWatchDir(dirwatch_t *ctx, bool immediately) {
free(opts); free(opts);
} }
#endif #endif

View file

@ -64,4 +64,4 @@ void stopWatchDir(dirwatch_t *ctx, bool immediately);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -12,7 +12,7 @@
#include <sys/stat.h> #include <sys/stat.h>
static int _modeToType(unsigned int mode) { static fsmode_t _modeToType(unsigned int mode) {
switch(mode & _S_IFMT) { switch(mode & _S_IFMT) {
case _S_IFDIR: return FS_MODE_DIR; case _S_IFDIR: return FS_MODE_DIR;
case _S_IFCHR: return FS_MODE_CHARACTER_DEVICE; case _S_IFCHR: return FS_MODE_CHARACTER_DEVICE;
@ -79,7 +79,7 @@ bool fsIsDir(const char *path) {
#else #else
#include <sys/stat.h> #include <sys/stat.h>
static int _modeToType(unsigned int mode) { static fsmode_t _modeToType(unsigned int mode) {
switch(mode & __S_IFMT) { switch(mode & __S_IFMT) {
case __S_IFDIR: return FS_MODE_DIR; case __S_IFDIR: return FS_MODE_DIR;
case __S_IFCHR: return FS_MODE_CHARACTER_DEVICE; case __S_IFCHR: return FS_MODE_CHARACTER_DEVICE;
@ -90,13 +90,13 @@ static int _modeToType(unsigned int mode) {
} }
fs_stat_t fsStat(file_t fp) { fs_stat_t fsStat(file_t fp) {
int fd = fileno((FILE*)fp.handle); int fd = fileno((FILE*)fp);
struct stat statbuf; struct stat statbuf;
int res = fstat(fd, &statbuf); int res = fstat(fd, &statbuf);
if(res == 0) { if(res == 0) {
return (fs_stat_t) { return (fs_stat_t) {
.type = _modeToType(statbuf.st_mode), .type = _modeToType(statbuf.st_mode),
.size = statbuf.st_size, .size = (uint64_t)statbuf.st_size,
.last_access = statbuf.st_atime, .last_access = statbuf.st_atime,
.last_modif = statbuf.st_mtime .last_modif = statbuf.st_mtime
}; };
@ -131,4 +131,4 @@ bool fsIsDir(const char *path) {
return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode); return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode);
} }
#endif #endif

View file

@ -9,16 +9,16 @@ extern "C" {
#include "file.h" #include "file.h"
enum { typedef enum {
FS_MODE_FILE, FS_MODE_FILE,
FS_MODE_DIR, FS_MODE_DIR,
FS_MODE_CHARACTER_DEVICE, FS_MODE_CHARACTER_DEVICE,
FS_MODE_FIFO, FS_MODE_FIFO,
FS_MODE_UKNOWN, FS_MODE_UKNOWN,
}; } fsmode_t;
typedef struct { typedef struct {
int type; fsmode_t type;
uint64_t size; uint64_t size;
int64_t last_access; int64_t last_access;
int64_t last_modif; int64_t last_modif;
@ -41,4 +41,4 @@ bool fsIsDir(const char *path);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

237
file.c
View file

@ -1,237 +0,0 @@
#include "file.h"
#include "tracelog.h"
#ifdef _WIN32
#include "win32_slim.h"
#include <stdlib.h>
static DWORD _toWin32Access(int mode) {
if(mode & FILE_READ) return GENERIC_READ;
if(mode & FILE_WRITE) return GENERIC_WRITE;
if(mode & FILE_BOTH) return GENERIC_READ | GENERIC_WRITE;
fatal("unrecognized access mode: %d", mode);
return 0;
}
static DWORD _toWin32Creation(int mode) {
if(mode & FILE_READ) return OPEN_EXISTING;
if(mode == (FILE_WRITE | FILE_CLEAR)) return CREATE_ALWAYS;
if(mode & FILE_WRITE) return OPEN_ALWAYS;
if(mode & FILE_BOTH) return OPEN_ALWAYS;
fatal("unrecognized creation mode: %d", mode);
return 0;
}
bool fileExists(const char *fname) {
DWORD dwAttrib = GetFileAttributesA(fname);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
file_t fileOpen(const char *fname, int mode) {
return (file_t) {
.handle = CreateFileA(fname,
_toWin32Access(mode),
0,
NULL,
_toWin32Creation(mode),
FILE_ATTRIBUTE_NORMAL,
NULL)
};
}
void fileClose(file_t *ctx) {
if(ctx->handle) {
CloseHandle((HANDLE)ctx->handle);
ctx->handle = NULL;
}
}
bool fileIsValid(file_t *ctx) {
return ctx->handle != INVALID_HANDLE_VALUE;
}
bool filePutc(file_t *ctx, char c) {
return fileWrite(ctx, &c, 1) == 1;
}
bool filePuts(file_t *ctx, const char *str) {
size_t len = strlen(str);
return fileWrite(ctx, str, len) == len;
}
bool filePutstr(file_t *ctx, str_t str) {
return fileWrite(ctx, str.buf, str.len) == str.len;
}
bool filePutview(file_t *ctx, strview_t view) {
return fileWrite(ctx, view.buf, view.len) == view.len;
}
size_t fileRead(file_t *ctx, void *buf, size_t len) {
DWORD bytes_read = 0;
BOOL result = ReadFile((HANDLE)ctx->handle, buf, (DWORD)len, &bytes_read, NULL);
return result == TRUE ? (size_t)bytes_read : 0;
}
size_t fileWrite(file_t *ctx, const void *buf, size_t len) {
DWORD bytes_read = 0;
BOOL result = WriteFile((HANDLE)ctx->handle, buf, (DWORD)len, &bytes_read, NULL);
return result == TRUE ? (size_t)bytes_read : 0;
}
bool fileSeekEnd(file_t *ctx) {
return SetFilePointerEx((HANDLE)ctx->handle, (LARGE_INTEGER){0}, NULL, FILE_END) == TRUE;
}
void fileRewind(file_t *ctx) {
SetFilePointerEx((HANDLE)ctx->handle, (LARGE_INTEGER){0}, NULL, FILE_BEGIN);
}
uint64_t fileTell(file_t *ctx) {
LARGE_INTEGER tell;
BOOL result = SetFilePointerEx((HANDLE)ctx->handle, (LARGE_INTEGER){0}, &tell, FILE_CURRENT);
return result == TRUE ? (uint64_t)tell.QuadPart : 0;
}
#else
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
const char *_toStdioMode(int mode) {
switch(mode) {
case FILE_READ: return "rb";
case FILE_BOTH: return "r+b";
case FILE_WRITE: return "wb";
default: fatal("mode not recognized: %d", mode); return "";
}
}
bool fileExists(const char *fname) {
return access(fname, F_OK) == 0;
}
file_t fileOpen(const char *fname, int mode) {
return (file_t) {
.handle = (void*) fopen(fname, _toStdioMode(mode)),
};
}
void fileClose(file_t *ctx) {
if(ctx->handle) {
fclose((FILE*)ctx->handle);
ctx->handle = NULL;
}
}
bool fileIsValid(file_t *ctx) {
return ctx->handle != NULL;
}
bool filePutc(file_t *ctx, char c) {
return fputc(c, (FILE*)ctx->handle) == c;
}
bool filePuts(file_t *ctx, const char *str) {
return fputs(str, (FILE*)ctx->handle) != EOF;
}
bool filePutstr(file_t *ctx, str_t str) {
return fileWrite(ctx, str.buf, str.len) == str.len;
}
bool filePutview(file_t *ctx, strview_t view) {
return fileWrite(ctx, view.buf, view.len) == view.len;
}
size_t fileRead(file_t *ctx, void *buf, size_t len) {
return fread(buf, 1, len, (FILE*)ctx->handle);
}
size_t fileWrite(file_t *ctx, const void *buf, size_t len) {
return fwrite(buf, 1, len, (FILE*)ctx->handle);
}
bool fileSeekEnd(file_t *ctx) {
return fseek((FILE*)ctx->handle, 0, SEEK_END) == 0;
}
void fileRewind(file_t *ctx) {
rewind((FILE*)ctx->handle);
}
uint64_t fileTell(file_t *ctx) {
return (uint64_t)ftell((FILE*)ctx->handle);
}
#endif
static fread_buf_t _readWholeInternal(file_t *ctx, bool null_terminated) {
fread_buf_t contents = {0};
uint64_t fsize = 0;
if(!fileSeekEnd(ctx)) {
err("file: couldn't read until end");
goto failed;
}
fsize = fileTell(ctx);
fileRewind(ctx);
contents.len = fsize;
contents.buf = malloc(fsize + null_terminated);
if(!contents.buf) {
err("file: couldn't allocate buffer");
goto failed;
}
size_t read = fileRead(ctx, contents.buf, fsize);
if(read != fsize) {
err("file: read wrong amount of bytes: %zu instead of %zu", read, fsize);
goto failed_free;
}
if(null_terminated) {
contents.buf[contents.len] = '\0';
}
failed:
return contents;
failed_free:
free(contents.buf);
return contents;
}
fread_buf_t fileReadWhole(const char *fname) {
file_t fp = fileOpen(fname, FILE_READ);
fread_buf_t contents = fileReadWholeFP(&fp);
fileClose(&fp);
return contents;
}
fread_buf_t fileReadWholeFP(file_t *ctx) {
return _readWholeInternal(ctx, false);
}
str_t fileReadWholeText(const char *fname) {
file_t fp = fileOpen(fname, FILE_READ);
if(!fileIsValid(&fp)) {
err("couldn't open file %s", fname);
return strInit();
}
str_t contents = fileReadWholeFPText(&fp);
fileClose(&fp);
return contents;
}
str_t fileReadWholeFPText(file_t *ctx) {
fread_buf_t contents = _readWholeInternal(ctx, true);
return (str_t) {
.buf = contents.buf,
.len = contents.len
};
}

54
file.h
View file

@ -1,54 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "str.h"
enum {
FILE_READ = 1 << 0,
FILE_WRITE = 1 << 1,
FILE_CLEAR = 1 << 2,
FILE_BOTH = 1 << 3
};
typedef struct {
void *handle;
} file_t;
typedef struct {
char *buf;
size_t len;
} fread_buf_t;
bool fileExists(const char *fname);
file_t fileOpen(const char *fname, int mode);
void fileClose(file_t *ctx);
bool fileIsValid(file_t *ctx);
bool filePutc(file_t *ctx, char c);
bool filePuts(file_t *ctx, const char *str);
bool filePutstr(file_t *ctx, str_t str);
bool filePutview(file_t *ctx, strview_t view);
size_t fileRead(file_t *ctx, void *buf, size_t len);
size_t fileWrite(file_t *ctx, const void *buf, size_t len);
bool fileSeekEnd(file_t *ctx);
void fileRewind(file_t *ctx);
uint64_t fileTell(file_t *ctx);
fread_buf_t fileReadWhole(const char *fname);
fread_buf_t fileReadWholeFP(file_t *ctx);
str_t fileReadWholeText(const char *fname);
str_t fileReadWholeFPText(file_t *ctx);
#ifdef __cplusplus
} // extern "C"
#endif

106
gen.lua Normal file
View file

@ -0,0 +1,106 @@
local outfile = "colla.h"
local colladir = "src/"
local function hasArg(value)
for k,v in pairs(arg) do
if v == value then
return true
end
end
return false
end
local use_namespace = hasArg("-namespace")
os.remove(outfile)
local function cat(f)
local fp = io.open(f)
local text = fp:read("a")
fp:close()
return text
end
str = [[
/*
colla.h -- All colla libraries in a single header
Do the following in *one* C file to create the implementation
#define COLLA_IMPL
Use the following in the same C file for options
#define COLLA_NO_THREADS // don't include the threads module
#define COLLA_NO_NET // don't include networking stuff
*/
]]
str = str .. cat(colladir .. "collatypes.h") .. "\n"
str = str .. cat(colladir .. "tracelog.h") .. "\n"
str = str .. cat(colladir .. "str.h") .. "\n"
str = str .. cat(colladir .. "vec.h") .. "\n"
str = str .. cat(colladir .. "hashmap.h") .. "\n"
str = str .. cat(colladir .. "utf8.h") .. "\n"
str = str .. cat(colladir .. "ini.h") .. "\n"
str = str .. cat(colladir .. "strstream.h") .. "\n"
str = str .. cat(colladir .. "win32_slim.h") .. "\n"
str = str .. cat(colladir .. "os.h") .. "\n"
str = str .. cat(colladir .. "file.h") .. "\n"
str = str .. cat(colladir .. "dir.h") .. "\n"
str = str .. "#ifndef COLLA_NO_NET\n"
str = str .. cat(colladir .. "socket.h") .. "\n"
str = str .. cat(colladir .. "http.h") .. "\n"
str = str .. "#endif // COLLA_NO_NET\n"
str = str .. "#if !defined(__TINYC__) && !defined(COLLA_NO_THREADS)\n"
str = str .. cat(colladir .. "cthreads.h") .. "\n"
str = str .. "#endif // !defined(__TINYC__) && !defined(COLLA_NO_THREADS)\n"
str = str .. "#ifdef COLLA_IMPL\n"
str = str .. cat(colladir .. "tracelog.c") .. "\n"
str = str .. cat(colladir .. "strstream.c") .. "\n"
str = str .. cat(colladir .. "str.c") .. "\n"
str = str .. cat(colladir .. "hashmap.c") .. "\n"
str = str .. cat(colladir .. "utf8.c") .. "\n"
str = str .. cat(colladir .. "ini.c") .. "\n"
str = str .. cat(colladir .. "os.c") .. "\n"
str = str .. cat(colladir .. "file.c") .. "\n"
str = str .. cat(colladir .. "dir.c") .. "\n"
str = str .. "#ifndef COLLA_NO_NET\n"
str = str .. cat(colladir .. "socket.c") .. "\n"
str = str .. cat(colladir .. "http.c") .. "\n"
str = str .. "#endif // COLLA_NO_NET\n"
str = str .. "#if !defined(__TINYC__) && !defined(COLLA_NO_THREADS)\n"
str = str .. cat(colladir .. "cthreads.c") .. "\n"
str = str .. "#endif // !defined(__TINYC__) && !defined(COLLA_NO_THREADS)\n"
str = str .. "#endif /* COLLA_IMPL */\n"
-- remove includes
str = str:gsub('#include "([^"]+)"', '/* #include "%1" */')
str = str .. string.format([[
/*
MIT License
Copyright (c) 1994-2019 Lua.org, PUC-Rio.
Copyright (c) 2020-%s snarmph.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
]], os.date("%Y"))
local f = io.open(outfile, "w")
f:write(str)
f:close()

21
src/collatypes.h Normal file
View file

@ -0,0 +1,21 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef size_t usize;
typedef ptrdiff_t isize;

View file

@ -195,4 +195,4 @@ bool mtxUnlock(cmutex_t ctx) {
return pthread_mutex_unlock((pthread_mutex_t *)ctx) == 0; return pthread_mutex_unlock((pthread_mutex_t *)ctx) == 0;
} }
#endif #endif

View file

@ -4,8 +4,8 @@
extern "C" { extern "C" {
#endif #endif
#include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
// == THREAD =========================================== // == THREAD ===========================================
@ -64,4 +64,4 @@ struct lock_t {
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -55,8 +55,8 @@ dir_t dirOpen(const char *path) {
str_ostream_t out = ostrInitLen(n + 3); str_ostream_t out = ostrInitLen(n + 3);
n = GetFullPathName(path, n, out.buf, NULL); n = GetFullPathName(path, n, out.buf, NULL);
assert(n > 0); assert(n > 0);
out.size += n; out.len += n;
switch(ostrBack(&out)) { switch(ostrBack(out)) {
case '\\': case '\\':
case '/': case '/':
case ':': case ':':
@ -70,12 +70,12 @@ dir_t dirOpen(const char *path) {
_dir_internal_t *dir = malloc(sizeof(_dir_internal_t)); _dir_internal_t *dir = malloc(sizeof(_dir_internal_t));
if(dir) { if(dir) {
wchar_t *wpath = strToWCHAR(ostrAsStr(&out)); wchar_t *wpath = strToWCHAR(ostrAsStr(out));
assert(wpath); assert(wpath);
*dir = _getFirst(wpath); *dir = _getFirst(wpath);
free(wpath); free(wpath);
} }
ostrFree(&out); ostrFree(out);
return dir; return dir;
} }
@ -91,17 +91,23 @@ bool dirValid(dir_t ctx) {
dir_entry_t *dirNext(dir_t ctx) { dir_entry_t *dirNext(dir_t ctx) {
_dir_internal_t *dir = (_dir_internal_t*)ctx; _dir_internal_t *dir = (_dir_internal_t*)ctx;
strFree(&dir->cur.name); strFree(dir->cur.name);
if(!dir->handle) return NULL; if(!dir->handle) return NULL;
dir->cur = dir->next; dir->cur = dir->next;
_getNext(dir); _getNext(dir);
return &dir->cur; return &dir->cur;
} }
void dirCreate(const char *path) {
CreateDirectoryA(path, NULL);
}
#else #else
#include <dirent.h> #include <dirent.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
// taken from https://sites.uclouvain.be/SystInfo/usr/include/dirent.h.html // taken from https://sites.uclouvain.be/SystInfo/usr/include/dirent.h.html
// hopefully shouldn't be needed // hopefully shouldn't be needed
@ -118,7 +124,7 @@ typedef struct {
} _dir_internal_t; } _dir_internal_t;
dir_t dirOpen(const char *path) { dir_t dirOpen(const char *path) {
_dir_internal_t *ctx = calloc(1, sizeof(_dir_internal_t)); _dir_internal_t *ctx = (_dir_internal_t *)calloc(1, sizeof(_dir_internal_t));
if(ctx) ctx->dir = opendir(path); if(ctx) ctx->dir = opendir(path);
return ctx; return ctx;
} }
@ -139,7 +145,7 @@ bool dirValid(dir_t ctx) {
dir_entry_t *dirNext(dir_t ctx) { dir_entry_t *dirNext(dir_t ctx) {
if(!ctx) return NULL; if(!ctx) return NULL;
_dir_internal_t *in = (_dir_internal_t *)ctx; _dir_internal_t *in = (_dir_internal_t *)ctx;
strFree(&in->next.name); strFree(in->next.name);
struct dirent *dp = readdir(in->dir); struct dirent *dp = readdir(in->dir);
if(!dp) return NULL; if(!dp) return NULL;
@ -149,8 +155,12 @@ dir_entry_t *dirNext(dir_t ctx) {
default: in->next.type = FS_TYPE_UNKNOWN; break; default: in->next.type = FS_TYPE_UNKNOWN; break;
} }
in->next.name = strInitStr(dp->d_name); in->next.name = strFromStr(dp->d_name);
return &in->next; return &in->next;
} }
#endif void dirCreate(const char *path) {
mkdir(path, 0700);
}
#endif

View file

@ -8,16 +8,16 @@ extern "C" {
typedef void *dir_t; typedef void *dir_t;
typedef struct { typedef enum {
int type;
str_t name;
} dir_entry_t;
enum {
FS_TYPE_UNKNOWN, FS_TYPE_UNKNOWN,
FS_TYPE_FILE, FS_TYPE_FILE,
FS_TYPE_DIR, FS_TYPE_DIR,
}; } fs_type_t;
typedef struct {
fs_type_t type;
str_t name;
} dir_entry_t;
dir_t dirOpen(const char *path); dir_t dirOpen(const char *path);
void dirClose(dir_t ctx); void dirClose(dir_t ctx);
@ -26,6 +26,8 @@ bool dirValid(dir_t ctx);
dir_entry_t *dirNext(dir_t ctx); dir_entry_t *dirNext(dir_t ctx);
void dirCreate(const char *path);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

292
src/file.c Normal file
View file

@ -0,0 +1,292 @@
#include "file.h"
#include "tracelog.h"
#ifdef _WIN32
#include "win32_slim.h"
#include <stdlib.h>
static DWORD _toWin32Access(int mode) {
if(mode & FILE_READ) return GENERIC_READ;
if(mode & FILE_WRITE) return GENERIC_WRITE;
if(mode & FILE_BOTH) return GENERIC_READ | GENERIC_WRITE;
fatal("unrecognized access mode: %d", mode);
return 0;
}
static DWORD _toWin32Creation(filemode_t mode) {
if(mode & FILE_READ) return OPEN_EXISTING;
if(mode == (FILE_WRITE | FILE_CLEAR)) return CREATE_ALWAYS;
if(mode & FILE_WRITE) return OPEN_ALWAYS;
if(mode & FILE_BOTH) return OPEN_ALWAYS;
fatal("unrecognized creation mode: %d", mode);
return 0;
}
bool fileExists(const char *fname) {
DWORD dwAttrib = GetFileAttributesA(fname);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
!(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
file_t fileOpen(const char *fname, filemode_t mode) {
return (file_t)CreateFileA(
fname,
_toWin32Access(mode),
0,
NULL,
_toWin32Creation(mode),
FILE_ATTRIBUTE_NORMAL,
NULL
);
}
void fileClose(file_t ctx) {
if (ctx) {
CloseHandle((HANDLE)ctx);
}
}
bool fileIsValid(file_t ctx) {
return (HANDLE)ctx != INVALID_HANDLE_VALUE;
}
bool filePutc(file_t ctx, char c) {
return fileWrite(ctx, &c, 1) == 1;
}
bool filePuts(file_t ctx, const char *str) {
usize len = strlen(str);
return fileWrite(ctx, str, len) == len;
}
bool filePutstr(file_t ctx, str_t str) {
return fileWrite(ctx, str.buf, str.len) == str.len;
}
bool filePutview(file_t ctx, strview_t view) {
return fileWrite(ctx, view.buf, view.len) == view.len;
}
usize fileRead(file_t ctx, void *buf, usize len) {
DWORD bytes_read = 0;
BOOL result = ReadFile((HANDLE)ctx, buf, (DWORD)len, &bytes_read, NULL);
return result == TRUE ? (usize)bytes_read : 0;
}
usize fileWrite(file_t ctx, const void *buf, usize len) {
DWORD bytes_read = 0;
BOOL result = WriteFile((HANDLE)ctx, buf, (DWORD)len, &bytes_read, NULL);
return result == TRUE ? (usize)bytes_read : 0;
}
bool fileSeekEnd(file_t ctx) {
return SetFilePointerEx((HANDLE)ctx, (LARGE_INTEGER){0}, NULL, FILE_END) == TRUE;
}
void fileRewind(file_t ctx) {
SetFilePointerEx((HANDLE)ctx, (LARGE_INTEGER){0}, NULL, FILE_BEGIN);
}
uint64 fileTell(file_t ctx) {
LARGE_INTEGER tell;
BOOL result = SetFilePointerEx((HANDLE)ctx, (LARGE_INTEGER){0}, &tell, FILE_CURRENT);
return result == TRUE ? (uint64)tell.QuadPart : 0;
}
#else
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
const char *_toStdioMode(filemode_t mode) {
switch(mode) {
case FILE_READ: return "rb";
case FILE_BOTH: return "r+b";
case FILE_WRITE: return "wb";
default: fatal("mode not recognized: %d", mode); return "";
}
}
bool fileExists(const char *fname) {
return access(fname, F_OK) == 0;
}
file_t fileOpen(const char *fname, filemode_t mode) {
return (file_t)(void*) fopen(fname, _toStdioMode(mode));
}
void fileClose(file_t ctx) {
if(ctx) {
fclose((FILE*)ctx);
}
}
bool fileIsValid(file_t ctx) {
return (FILE *)ctx != NULL;
}
bool filePutc(file_t ctx, char c) {
return fputc(c, (FILE*)ctx) == c;
}
bool filePuts(file_t ctx, const char *str) {
return fputs(str, (FILE*)ctx) != EOF;
}
bool filePutstr(file_t ctx, str_t str) {
return fileWrite(ctx, str.buf, str.len) == str.len;
}
bool filePutview(file_t ctx, strview_t view) {
return fileWrite(ctx, view.buf, view.len) == view.len;
}
usize fileRead(file_t ctx, void *buf, usize len) {
return fread(buf, 1, len, (FILE*)ctx);
}
usize fileWrite(file_t ctx, const void *buf, usize len) {
return fwrite(buf, 1, len, (FILE*)ctx);
}
bool fileSeekEnd(file_t ctx) {
return fseek((FILE*)ctx, 0, SEEK_END) == 0;
}
void fileRewind(file_t ctx) {
rewind((FILE*)ctx);
}
uint64 fileTell(file_t ctx) {
return (uint64)ftell((FILE*)ctx);
}
#endif
static str_t _readWholeInternalStr(file_t ctx) {
str_t contents = strInit();
uint64 fsize = 0;
usize read = 0;
if(!fileSeekEnd(ctx)) {
err("file: couldn't read until end");
goto failed;
}
fsize = fileTell(ctx);
fileRewind(ctx);
contents.buf = (char *)malloc(fsize + 1);
contents.len = fsize;
if(!contents.buf) {
err("file: couldn't allocate buffer");
goto failed;
}
read = fileRead(ctx, contents.buf, fsize);
if(read != fsize) {
err("file: read wrong amount of bytes: %zu instead of %zu", read, fsize);
goto failed_free;
}
contents.buf[contents.len] = '\0';
failed:
return contents;
failed_free:
strFree(contents);
return strInit();
}
static vec(uint8) _readWholeInternalVec(file_t ctx) {
vec(uint8) contents = NULL;
uint64 fsize = 0;
usize read = 0;
if(!fileSeekEnd(ctx)) {
err("file: couldn't read until end");
goto failed;
}
fsize = fileTell(ctx);
fileRewind(ctx);
vecReserve(contents, fsize);
if(!contents) {
err("file: couldn't allocate buffer");
goto failed;
}
read = fileRead(ctx, contents, fsize);
if(read != fsize) {
err("file: read wrong amount of bytes: %zu instead of %zu", read, fsize);
goto failed_free;
}
failed:
return contents;
failed_free:
vecFree(contents);
return contents;
}
vec(uint8) fileReadWhole(const char *fname) {
file_t fp = fileOpen(fname, FILE_READ);
if (!fileIsValid(fp)) return NULL;
vec(uint8) contents = fileReadWholeFP(fp);
fileClose(fp);
return contents;
}
vec(uint8) fileReadWholeFP(file_t ctx) {
return _readWholeInternalVec(ctx);
}
str_t fileReadWholeText(const char *fname) {
file_t fp = fileOpen(fname, FILE_READ);
if(!fileIsValid(fp)) {
err("couldn't open file %s", fname);
return strInit();
}
str_t contents = fileReadWholeTextFP(fp);
fileClose(fp);
return contents;
}
str_t fileReadWholeTextFP(file_t ctx) {
return _readWholeInternalStr(ctx);
}
bool fileWriteWhole(const char *fname, filebuf_t data) {
file_t fp = fileOpen(fname, FILE_WRITE);
if (!fileIsValid(fp)) {
err("couldn't open file %s", fname);
return false;
}
bool res = fileWriteWholeFP(fp, data);
fileClose(fp);
return res;
}
bool fileWriteWholeFP(file_t ctx, filebuf_t data) {
usize written = fileWrite(ctx, data.buf, data.len);
return written == data.len;
}
bool fileWriteWholeText(const char *fname, strview_t string) {
file_t fp = fileOpen(fname, FILE_WRITE);
if (!fileIsValid(fp)) {
err("couldn't open file %s", fname);
return false;
}
bool res = fileWriteWholeTextFP(fp, string);
fileClose(fp);
return res;
}
bool fileWriteWholeTextFP(file_t ctx, strview_t string) {
return fileWriteWholeFP(ctx, (filebuf_t){ (uint8 *)string.buf, string.len });
}

59
src/file.h Normal file
View file

@ -0,0 +1,59 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "collatypes.h"
#include "str.h"
#include "vec.h"
typedef enum {
FILE_READ = 1 << 0,
FILE_WRITE = 1 << 1,
FILE_CLEAR = 1 << 2,
FILE_BOTH = 1 << 3
} filemode_t;
typedef uintptr_t file_t;
typedef struct {
const uint8 *buf;
usize len;
} filebuf_t;
bool fileExists(const char *fname);
file_t fileOpen(const char *fname, filemode_t mode);
void fileClose(file_t ctx);
bool fileIsValid(file_t ctx);
bool filePutc(file_t ctx, char c);
bool filePuts(file_t ctx, const char *str);
bool filePutstr(file_t ctx, str_t str);
bool filePutview(file_t ctx, strview_t view);
usize fileRead(file_t ctx, void *buf, usize len);
usize fileWrite(file_t ctx, const void *buf, usize len);
bool fileSeekEnd(file_t ctx);
void fileRewind(file_t ctx);
uint64 fileTell(file_t ctx);
vec(uint8) fileReadWhole(const char *fname);
vec(uint8) fileReadWholeFP(file_t ctx);
str_t fileReadWholeText(const char *fname);
str_t fileReadWholeTextFP(file_t ctx);
bool fileWriteWhole(const char *fname, filebuf_t data);
bool fileWriteWholeFP(file_t ctx, filebuf_t data);
bool fileWriteWholeText(const char *fname, strview_t string);
bool fileWriteWholeTextFP(file_t ctx, strview_t string);
#ifdef __cplusplus
} // extern "C"
#endif

130
src/hashmap.c Normal file
View file

@ -0,0 +1,130 @@
#include "hashmap.h"
#include <string.h>
static uint64 hash_seed = 0;
hashmap_t hmInit(usize initial_cap) {
hashmap_t map = {0};
if (!initial_cap) initial_cap = 512;
vecReserve(map.nodes, initial_cap);
memset(map.nodes, 0, sizeof(hashnode_t) * initial_cap);
return map;
}
void hmFree(hashmap_t map) {
vecFree(map.nodes);
}
void hmSet(hashmap_t *map, uint64 hash, uint64 index) {
uint32 hm_index = hash % vecCap(map->nodes);
while (map->nodes[hm_index].hash) {
hashnode_t *node = &map->nodes[hm_index];
if (node->hash == hash) {
node->index = index;
return;
}
hm_index = (hm_index + 1) % vecCap(map->nodes);
}
map->nodes[hm_index].hash = hash;
map->nodes[hm_index].index = index;
_veclen(map->nodes)++;
float load_factor = (float)vecLen(map->nodes) / (float)vecCap(map->nodes);
if (load_factor > 0.75f) {
uint32 old_cap = vecCap(map->nodes);
vecReserve(map->nodes, old_cap);
for (usize i = old_cap; i < vecCap(map->nodes); ++i) {
map->nodes[i].hash = 0;
map->nodes[i].index = 0;
}
}
}
uint64 hmGet(hashmap_t map, uint64 hash) {
uint32 hm_index = hash % vecCap(map.nodes);
do {
hashnode_t *node = &map.nodes[hm_index];
if (node->hash == hash) {
return node->index;
}
hm_index = (hm_index + 1) % vecCap(map.nodes);
} while (map.nodes[hm_index].hash);
return 0;
}
void hmDelete(hashmap_t *map, uint64 hash) {
uint32 hm_index = hash % vecCap(map->nodes);
do {
hashnode_t *node = &map->nodes[hm_index];
if (node->hash == hash) {
node->hash = 0;
node->index = 0;
break;
}
hm_index = (hm_index + 1) % vecCap(map->nodes);
} while (map->nodes[hm_index].hash);
if(vecLen(map->nodes)) _veclen(map->nodes)--;
}
void hashSetSeed(uint64 new_seed) {
hash_seed = new_seed;
}
uint64 hash(const void *ptr, usize len) {
const uint64 m = 0xC6A4A7935BD1E995LLU;
const int r = 47;
uint64 h = hash_seed ^ (len * m);
const uint64 *data = (const uint64 *)ptr;
const uint64 *end = (len >> 3) + data;
while (data != end) {
uint64 k = *data++;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
const unsigned char * data2 = (const unsigned char *)data;
switch(len & 7) {
case 7: h ^= (uint64_t)(data2[6]) << 48;
case 6: h ^= (uint64_t)(data2[5]) << 40;
case 5: h ^= (uint64_t)(data2[4]) << 32;
case 4: h ^= (uint64_t)(data2[3]) << 24;
case 3: h ^= (uint64_t)(data2[2]) << 16;
case 2: h ^= (uint64_t)(data2[1]) << 8;
case 1: h ^= (uint64_t)(data2[0]);
h *= m;
};
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
uint64 hashStr(str_t str) {
return hash(str.buf, str.len);
}
uint64 hashView(strview_t view) {
return hash(view.buf, view.len);
}
uint64 hashCStr(const char *cstr) {
return hash(cstr, strlen(cstr));
}

49
src/hashmap.h Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include "collatypes.h"
#include "vec.h"
#include "str.h"
/*
Example usage:
hashSetSeed(time(NULL));
vec(const char *) strings = NULL;
hashmap_t map = hmInit(32);
// mapGet returns 0 in case it doesn't find anything, this way we don't need
// to check its return value
vecAppend(strings, "nil");
hmSet(&map, hashCStr("english"), vecAppend(strings, "hello"));
hmSet(&map, hashCStr("french"), vecAppend(strings, "bonjour"));
hmSet(&map, hashCStr("italian"), vecAppend(strings, "ciao"));
printf("english: %s\n", strings[hmGet(map, hashCStr("english"))]);
printf("french: %s\n", strings[hmGet(map, hashCStr("french"))]);
printf("italian: %s\n", strings[hmGet(map, hashCStr("italian"))]);
mapFree(map);
vecFree(strings);
*/
typedef struct {
uint64 hash;
uint64 index;
} hashnode_t;
typedef struct {
vec(hashnode_t) nodes;
} hashmap_t;
hashmap_t hmInit(usize initial_cap);
void hmFree(hashmap_t map);
void hmSet(hashmap_t *map, uint64 hash, uint64 index);
uint64 hmGet(hashmap_t map, uint64 hash);
void hmDelete(hashmap_t *map, uint64 hash);
void hashSetSeed(uint64 new_seed);
uint64 hash(const void *data, usize len);
uint64 hashStr(str_t str);
uint64 hashView(strview_t view);
uint64 hashCStr(const char *cstr);

View file

@ -7,41 +7,38 @@
#include "os.h" #include "os.h"
#include "tracelog.h" #include "tracelog.h"
#define T http_field_t
#define VEC_SHORT_NAME field_vec
#define VEC_DISABLE_ERASE_WHEN
#define VEC_NO_DECLARATION
#include "vec.h" #include "vec.h"
// == INTERNAL ================================================================ // == INTERNAL ================================================================
static void _setField(field_vec_t *fields, const char *key, const char *value) { static void _setField(vec(http_field_t) *fields_vec, const char *key, const char *value) {
// search if the field already exists vec(http_field_t) fields = *fields_vec;
for(size_t i = 0; i < fields->size; ++i) {
if(stricmp(fields->buf[i].key, key) == 0) { for (uint32 i = 0; i < vecLen(fields); ++i) {
// replace value if (stricmp(fields[i].key, key) == 0) {
char **curval = &fields->buf[i].value; char **curval = &fields[i].value;
size_t cur = strlen(*curval); usize curlen = strlen(*curval);
size_t new = strlen(value); usize newlen = strlen(value);
if(new > cur) { if(newlen > curlen) {
*curval = realloc(*curval, new + 1); *curval = (char *)realloc(*curval, newlen + 1);
} }
memcpy(*curval, value, new); memcpy(*curval, value, newlen);
(*curval)[new] = '\0'; (*curval)[newlen] = '\0';
return; return;
} }
} }
// otherwise, add it to the list // otherwise, add it to the list
http_field_t field; http_field_t field;
size_t klen = strlen(key); usize klen = strlen(key);
size_t vlen = strlen(value); usize vlen = strlen(value);
field.key = malloc(klen + 1); field.key = (char *)malloc(klen + 1);
field.value = malloc(vlen + 1); field.value = (char *)malloc(vlen + 1);
memcpy(field.key, key, klen); memcpy(field.key, key, klen);
memcpy(field.value, value, vlen); memcpy(field.value, value, vlen);
field.key[klen] = field.value[vlen] = '\0'; field.key[klen] = field.value[vlen] = '\0';
field_vecPush(fields, field); vecAppend(*fields_vec, field);
} }
// == HTTP VERSION ============================================================ // == HTTP VERSION ============================================================
@ -55,25 +52,25 @@ int httpVerNumber(http_version_t ver) {
http_request_t reqInit() { http_request_t reqInit() {
http_request_t req; http_request_t req;
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
reqSetUri(&req, "/"); reqSetUri(&req, strvInit("/"));
req.version = (http_version_t){1, 1}; req.version = (http_version_t){1, 1};
return req; return req;
} }
void reqFree(http_request_t *ctx) { void reqFree(http_request_t *ctx) {
for(size_t i = 0; i < ctx->fields.size; ++i) { for (uint32 i = 0; i < vecLen(ctx->fields); ++i) {
free(ctx->fields.buf[i].key); free(ctx->fields[i].key);
free(ctx->fields.buf[i].value); free(ctx->fields[i].value);
} }
field_vecFree(&ctx->fields); vecFree(ctx->fields);
free(ctx->uri); free(ctx->uri);
free(ctx->body); free(ctx->body);
memset(ctx, 0, sizeof(http_request_t)); memset(ctx, 0, sizeof(http_request_t));
} }
bool reqHasField(http_request_t *ctx, const char *key) { bool reqHasField(http_request_t *ctx, const char *key) {
for(size_t i = 0; i < ctx->fields.size; ++i) { for(uint32 i = 0; i < vecLen(ctx->fields); ++i) {
if(stricmp(ctx->fields.buf[i].key, key) == 0) { if(stricmp(ctx->fields[i].key, key) == 0) {
return true; return true;
} }
} }
@ -84,20 +81,17 @@ void reqSetField(http_request_t *ctx, const char *key, const char *value) {
_setField(&ctx->fields, key, value); _setField(&ctx->fields, key, value);
} }
void reqSetUri(http_request_t *ctx, const char *uri) { void reqSetUri(http_request_t *ctx, strview_t uri) {
if (uri == NULL) return; if (strvIsEmpty(uri)) return;
size_t len = strlen(uri); free(ctx->uri);
if(uri[0] != '/') { if (uri.buf[0] != '/') {
len += 1; ctx->uri = (char *)realloc(ctx->uri, uri.len + 1);
ctx->uri = realloc(ctx->uri, len + 1);
ctx->uri[0] = '/'; ctx->uri[0] = '/';
memcpy(ctx->uri + 1, uri, len); memcpy(ctx->uri + 1, uri.buf, uri.len);
ctx->uri[len] = '\0'; ctx->uri[uri.len] = '\0';
} }
else { else {
ctx->uri = realloc(ctx->uri, len + 1); ctx->uri = strvCopy(uri).buf;
memcpy(ctx->uri, uri, len);
ctx->uri[len] = '\0';
} }
} }
@ -118,8 +112,8 @@ str_ostream_t reqPrepare(http_request_t *ctx) {
method, ctx->uri, ctx->version.major, ctx->version.minor method, ctx->uri, ctx->version.major, ctx->version.minor
); );
for(size_t i = 0; i < ctx->fields.size; ++i) { for(uint32 i = 0; i < vecLen(ctx->fields); ++i) {
ostrPrintf(&out, "%s: %s\r\n", ctx->fields.buf[i].key, ctx->fields.buf[i].value); ostrPrintf(&out, "%s: %s\r\n", ctx->fields[i].key, ctx->fields[i].value);
} }
ostrAppendview(&out, strvInit("\r\n")); ostrAppendview(&out, strvInit("\r\n"));
@ -133,7 +127,7 @@ error:
str_t reqString(http_request_t *ctx) { str_t reqString(http_request_t *ctx) {
str_ostream_t out = reqPrepare(ctx); str_ostream_t out = reqPrepare(ctx);
return ostrMove(&out); return ostrAsStr(out);
} }
// == HTTP RESPONSE =========================================================== // == HTTP RESPONSE ===========================================================
@ -143,18 +137,18 @@ http_response_t resInit() {
} }
void resFree(http_response_t *ctx) { void resFree(http_response_t *ctx) {
for(size_t i = 0; i < ctx->fields.size; ++i) { for(uint32 i = 0; i < vecLen(ctx->fields); ++i) {
free(ctx->fields.buf[i].key); free(ctx->fields[i].key);
free(ctx->fields.buf[i].value); free(ctx->fields[i].value);
} }
field_vecFree(&ctx->fields); vecFree(ctx->fields);
strFree(&ctx->body); vecFree(ctx->body);
memset(ctx, 0, sizeof(http_response_t)); memset(ctx, 0, sizeof(http_response_t));
} }
bool resHasField(http_response_t *ctx, const char *key) { bool resHasField(http_response_t *ctx, const char *key) {
for(size_t i = 0; i < ctx->fields.size; ++i) { for(uint32 i = 0; i < vecLen(ctx->fields); ++i) {
if(stricmp(ctx->fields.buf[i].key, key) == 0) { if(stricmp(ctx->fields[i].key, key) == 0) {
return true; return true;
} }
} }
@ -162,9 +156,9 @@ bool resHasField(http_response_t *ctx, const char *key) {
} }
const char *resGetField(http_response_t *ctx, const char *field) { const char *resGetField(http_response_t *ctx, const char *field) {
for(size_t i = 0; i < ctx->fields.size; ++i) { for(uint32 i = 0; i < vecLen(ctx->fields); ++i) {
if(stricmp(ctx->fields.buf[i].key, field) == 0) { if(stricmp(ctx->fields[i].key, field) == 0) {
return ctx->fields.buf[i].value; return ctx->fields[i].value;
} }
} }
return NULL; return NULL;
@ -183,7 +177,7 @@ void resParse(http_response_t *ctx, const char *data) {
istrGetu8(&in, &ctx->version.major); istrGetu8(&in, &ctx->version.major);
istrSkip(&in, 1); // skip . istrSkip(&in, 1); // skip .
istrGetu8(&in, &ctx->version.minor); istrGetu8(&in, &ctx->version.minor);
istrGeti32(&in, &ctx->status_code); istrGeti32(&in, (int32*)&ctx->status_code);
istrIgnore(&in, '\n'); istrIgnore(&in, '\n');
istrSkip(&in, 1); // skip \n istrSkip(&in, 1); // skip \n
@ -193,8 +187,9 @@ void resParse(http_response_t *ctx, const char *data) {
const char *tran_encoding = resGetField(ctx, "transfer-encoding"); const char *tran_encoding = resGetField(ctx, "transfer-encoding");
if(tran_encoding == NULL || stricmp(tran_encoding, "chunked") != 0) { if(tran_encoding == NULL || stricmp(tran_encoding, "chunked") != 0) {
strview_t body = istrGetviewLen(&in, 0, SIZE_MAX); strview_t body = istrGetviewLen(&in, 0, SIZE_MAX);
strFree(&ctx->body); vecClear(ctx->body);
ctx->body = strvCopy(body); vecReserve(ctx->body, body.len);
memcpy(ctx->body, body.buf, body.len);
} }
else { else {
fatal("chunked encoding not implemented yet"); fatal("chunked encoding not implemented yet");
@ -207,10 +202,10 @@ void resParseFields(http_response_t *ctx, str_istream_t *in) {
do { do {
line = istrGetview(in, '\r'); line = istrGetview(in, '\r');
size_t pos = strvFind(line, ':', 0); usize pos = strvFind(line, ':', 0);
if(pos != STRV_NOT_FOUND) { if(pos != STRV_NOT_FOUND) {
strview_t key = strvSubstr(line, 0, pos); strview_t key = strvSub(line, 0, pos);
strview_t value = strvSubstr(line, pos + 2, SIZE_MAX); strview_t value = strvSub(line, pos + 2, SIZE_MAX);
char *key_str = NULL; char *key_str = NULL;
char *value_str = NULL; char *value_str = NULL;
@ -237,29 +232,28 @@ http_client_t hcliInit() {
} }
void hcliFree(http_client_t *ctx) { void hcliFree(http_client_t *ctx) {
strFree(&ctx->host_name); strFree(ctx->host_name);
memset(ctx, 0, sizeof(http_client_t)); memset(ctx, 0, sizeof(http_client_t));
} }
void hcliSetHost(http_client_t *ctx, const char *hostname) { void hcliSetHost(http_client_t *ctx, strview_t hostname) {
strview_t hostview = strvInit(hostname);
// if the hostname starts with http:// (case insensitive) // if the hostname starts with http:// (case insensitive)
if(strvICompare(strvSubstr(hostview, 0, 7), strvInit("http://")) == 0) { if(strvICompare(strvSub(hostname, 0, 7), strvInit("http://")) == 0) {
ctx->host_name = strvCopy(strvSubstr(hostview, 7, SIZE_MAX)); ctx->host_name = strvCopy(strvSub(hostname, 7, SIZE_MAX));
} }
else if(strvICompare(strvSubstr(hostview, 0, 8), strvInit("https://")) == 0) { else if(strvICompare(strvSub(hostname, 0, 8), strvInit("https://")) == 0) {
err("HTTPS protocol not yet supported"); err("HTTPS protocol not yet supported");
return; return;
} }
else { else {
// undefined protocol, use HTTP // undefined protocol, use HTTP
ctx->host_name = strvCopy(hostview); ctx->host_name = strvCopy(hostname);
} }
} }
http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) { http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
if (strBack(&ctx->host_name) == '/') { if (strBack(ctx->host_name) == '/') {
strPop(&ctx->host_name); ctx->host_name.buf[--ctx->host_name.len] = '\0';
} }
if(!reqHasField(req, "Host")) { if(!reqHasField(req, "Host")) {
reqSetField(req, "Host", ctx->host_name.buf); reqSetField(req, "Host", ctx->host_name.buf);
@ -269,7 +263,7 @@ http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
str_ostream_t out = ostrInitLen(20); str_ostream_t out = ostrInitLen(20);
ostrAppendu64(&out, strlen(req->body)); ostrAppendu64(&out, strlen(req->body));
reqSetField(req, "Content-Length", out.buf); reqSetField(req, "Content-Length", out.buf);
ostrFree(&out); ostrFree(out);
} }
else { else {
reqSetField(req, "Content-Length", "0"); reqSetField(req, "Content-Length", "0");
@ -321,9 +315,9 @@ http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
} while(read != 0); } while(read != 0);
// if the data received is not null terminated // if the data received is not null terminated
if(*(received.buf + received.size) != '\0') { if(*(received.buf + received.len) != '\0') {
ostrPutc(&received, '\0'); ostrPutc(&received, '\0');
received.size--; received.len--;
} }
resParse(&res, received.buf); resParse(&res, received.buf);
@ -341,12 +335,12 @@ error:
err("couldn't clean up sockets %s", skGetErrorString()); err("couldn't clean up sockets %s", skGetErrorString());
} }
skopen_error: skopen_error:
strFree(&req_str); strFree(req_str);
ostrFree(&received); ostrFree(received);
return res; return res;
} }
http_response_t httpGet(const char *hostname, const char *uri) { http_response_t httpGet(strview_t hostname, strview_t uri) {
http_request_t request = reqInit(); http_request_t request = reqInit();
request.method = REQ_GET; request.method = REQ_GET;
reqSetUri(&request, uri); reqSetUri(&request, uri);
@ -362,3 +356,17 @@ http_response_t httpGet(const char *hostname, const char *uri) {
return res; return res;
} }
url_split_t urlSplit(strview_t uri) {
url_split_t out = {0};
if (strvStartsWithView(uri, strvInit("https://"))) {
uri = strvRemovePrefix(uri, 8);
}
else if (strvStartsWithView(uri, strvInit("http://"))) {
uri = strvRemovePrefix(uri, 7);
}
out.host = strvSub(uri, 0, strvFind(uri, '/', 0));
out.uri = strvSub(uri, out.host.len, SIZE_MAX);
return out;
}

View file

@ -4,23 +4,23 @@
extern "C" { extern "C" {
#endif #endif
#include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h> #include <stdbool.h>
#include "collatypes.h"
#include "str.h" #include "str.h"
#include "strstream.h" #include "strstream.h"
#include "socket.h" #include "socket.h"
enum { typedef enum {
REQ_GET, REQ_GET,
REQ_POST, REQ_POST,
REQ_HEAD, REQ_HEAD,
REQ_PUT, REQ_PUT,
REQ_DELETE REQ_DELETE
}; } reqtype_t;
enum { typedef enum {
// 2xx: success // 2xx: success
STATUS_OK = 200, STATUS_OK = 200,
STATUS_CREATED = 201, STATUS_CREATED = 201,
@ -49,11 +49,11 @@ enum {
STATUS_SERVICE_NOT_AVAILABLE = 503, STATUS_SERVICE_NOT_AVAILABLE = 503,
STATUS_GATEWAY_TIMEOUT = 504, STATUS_GATEWAY_TIMEOUT = 504,
STATUS_VERSION_NOT_SUPPORTED = 505, STATUS_VERSION_NOT_SUPPORTED = 505,
}; } resstatus_t;
typedef struct { typedef struct {
uint8_t major; uint8 major;
uint8_t minor; uint8 minor;
} http_version_t; } http_version_t;
// translates a http_version_t to a single readable number (e.g. 1.1 -> 11, 1.0 -> 10, etc) // translates a http_version_t to a single readable number (e.g. 1.1 -> 11, 1.0 -> 10, etc)
@ -64,18 +64,14 @@ typedef struct {
char *value; char *value;
} http_field_t; } http_field_t;
#define T http_field_t
#define VEC_SHORT_NAME field_vec
#define VEC_DISABLE_ERASE_WHEN
#define VEC_NO_IMPLEMENTATION
#include "vec.h" #include "vec.h"
// == HTTP REQUEST ============================================================ // == HTTP REQUEST ============================================================
typedef struct { typedef struct {
int method; reqtype_t method;
http_version_t version; http_version_t version;
field_vec_t fields; vec(http_field_t) fields;
char *uri; char *uri;
char *body; char *body;
} http_request_t; } http_request_t;
@ -86,7 +82,7 @@ void reqFree(http_request_t *ctx);
bool reqHasField(http_request_t *ctx, const char *key); bool reqHasField(http_request_t *ctx, const char *key);
void reqSetField(http_request_t *ctx, const char *key, const char *value); void reqSetField(http_request_t *ctx, const char *key, const char *value);
void reqSetUri(http_request_t *ctx, const char *uri); void reqSetUri(http_request_t *ctx, strview_t uri);
str_ostream_t reqPrepare(http_request_t *ctx); str_ostream_t reqPrepare(http_request_t *ctx);
str_t reqString(http_request_t *ctx); str_t reqString(http_request_t *ctx);
@ -94,10 +90,10 @@ str_t reqString(http_request_t *ctx);
// == HTTP RESPONSE =========================================================== // == HTTP RESPONSE ===========================================================
typedef struct { typedef struct {
int status_code; resstatus_t status_code;
field_vec_t fields; vec(http_field_t) fields;
http_version_t version; http_version_t version;
str_t body; vec(uint8) body;
} http_response_t; } http_response_t;
http_response_t resInit(void); http_response_t resInit(void);
@ -113,20 +109,29 @@ void resParseFields(http_response_t *ctx, str_istream_t *in);
typedef struct { typedef struct {
str_t host_name; str_t host_name;
uint16_t port; uint16 port;
socket_t socket; socket_t socket;
} http_client_t; } http_client_t;
http_client_t hcliInit(void); http_client_t hcliInit(void);
void hcliFree(http_client_t *ctx); void hcliFree(http_client_t *ctx);
void hcliSetHost(http_client_t *ctx, const char *hostname); void hcliSetHost(http_client_t *ctx, strview_t hostname);
http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *request); http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *request);
// == HTTP ==================================================================== // == HTTP ====================================================================
http_response_t httpGet(const char *hostname, const char *uri); http_response_t httpGet(strview_t hostname, strview_t uri);
// == URL =====================================================================
typedef struct {
strview_t host;
strview_t uri;
} url_split_t;
url_split_t urlSplit(strview_t uri);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

323
src/ini.c Normal file
View file

@ -0,0 +1,323 @@
#include "ini.h"
#include "strstream.h"
#include "file.h"
#include "tracelog.h"
// == INI READER ========================================================================
static const iniopts_t default_opts = {0};
static initable_t *findTable(ini_t *ctx, strview_t name);
static inivalue_t *findValue(vec(inivalue_t) values, strview_t key);
static void addTable(ini_t *ctx, str_istream_t *in, const iniopts_t *options);
static void addValue(initable_t *table, str_istream_t *in, const iniopts_t *options);
void _iniParseInternal(ini_t *ini, const iniopts_t *options) {
// add root table
vecAppend(ini->tables, (initable_t){0});
str_istream_t in = istrInitLen(ini->text.buf, ini->text.len);
istrSkipWhitespace(&in);
while (!istrIsFinished(in)) {
switch(*in.cur) {
case '[':
addTable(ini, &in, options);
break;
case '#': case ';':
istrIgnore(&in, '\n');
break;
default:
addValue(&ini->tables[0], &in, options);
break;
}
istrSkipWhitespace(&in);
}
}
ini_t iniParse(const char *filename, const iniopts_t *options) {
ini_t ini = { .text = fileReadWholeText(filename) };
if (strIsEmpty(ini.text)) return ini;
if (!options) options = &default_opts;
_iniParseInternal(&ini, options);
return ini;
}
ini_t iniParseString(const char *inistr, const iniopts_t *options) {
ini_t ini = { .text = strFromStr(inistr) };
if (!options) options = &default_opts;
_iniParseInternal(&ini, options);
return ini;
}
void iniFree(ini_t ctx) {
strFree(ctx.text);
for (uint32 i = 0; i < vecLen(ctx.tables); ++i) {
vecFree(ctx.tables[i].values);
}
vecFree(ctx.tables);
}
initable_t *iniGetTable(ini_t *ctx, const char *name) {
if (!name) {
return &ctx->tables[0];
}
else {
return findTable(ctx, strvInit(name));
}
}
inivalue_t *iniGet(initable_t *ctx, const char *key) {
return ctx ? findValue(ctx->values, strvInit(key)) : NULL;
}
vec(strview_t) iniAsArray(const inivalue_t *value, char delim) {
if (!value) return NULL;
if (!delim) delim = ' ';
vec(strview_t) out = NULL;
strview_t v = value->value;
usize start = 0;
for (usize i = 0; i < v.len; ++i) {
if (v.buf[i] == delim) {
strview_t arr_val = strvTrim(strvSub(v, start, i));
if (!strvIsEmpty(arr_val)) vecAppend(out, arr_val);
start = i + 1;
}
}
strview_t last = strvTrim(strvSub(v, start, SIZE_MAX));
if (!strvIsEmpty(last)) vecAppend(out, last);
return out;
}
vec(strview_t) iniAsArrayU8(const inivalue_t *value, const char *delim) {
if (!value || !delim) return NULL;
rune cpdelim = utf8Decode(&delim);
vec(strview_t) out = NULL;
strview_t v = value->value;
const char *start = v.buf;
const char *buf = v.buf;
const char *prevbuf = buf;
for(rune cp = utf8Decode(&buf);
buf != (v.buf + v.len);
cp = utf8Decode(&buf)
) {
if (cp == cpdelim) {
usize start_pos = start - v.buf;
usize end_pos = prevbuf - v.buf;
strview_t arr_val = strvTrim(strvSub(v, start_pos, end_pos));
if (!strvIsEmpty(arr_val)) vecAppend(out, arr_val);
// buf has already gone to the next codepoint, skipping the delimiter
start = buf;
}
prevbuf = buf;
}
strview_t last = strvTrim(strvSub(v, start - v.buf, SIZE_MAX));
if (!strvIsEmpty(last)) vecAppend(out, last);
return out;
}
uint64 iniAsUInt(const inivalue_t *value) {
if (!value) return 0;
str_istream_t in = istrInitLen(value->value.buf, value->value.len);
uint64 val = 0;
if (!istrGetu64(&in, &val)) val = 0;
return val;
}
int64 iniAsInt(const inivalue_t *value) {
if (!value) return 0;
str_istream_t in = istrInitLen(value->value.buf, value->value.len);
int64 val = 0;
if (!istrGeti64(&in, &val)) val = 0;
return val;
}
double iniAsNum(const inivalue_t *value) {
if (!value) return 0.f;
str_istream_t in = istrInitLen(value->value.buf, value->value.len);
double val = 0;
if (!istrGetdouble(&in, &val)) val = 0;
return val;
}
// == INI WRITER ========================================================================
#include "strstream.h"
#include "file.h"
static const winiopts_t default_wopts = {0};
iniwriter_t winiInit() {
iniwriter_t out = {0};
vecAppend(out.tables, (winitable_t){0});
return out;
}
void winiFree(iniwriter_t ctx) {
for (winitable_t *tab = ctx.tables; tab != vecEnd(ctx.tables); ++tab) {
strFree(tab->key);
for (winivalue_t *val = tab->values; val != vecEnd(tab->values); ++val) {
strFree(val->key);
strFree(val->value);
}
vecFree(tab->values);
}
vecFree(ctx.tables);
}
str_t winiToString(iniwriter_t ctx, const winiopts_t *options) {
if (!options) options = &default_wopts;
str_ostream_t out = ostrInitLen(1024 * 20);
if (!options->no_discalimer) ostrPuts(&out, "# auto-generated by colla's ini.h, do not modify!\n");
// add root values
winitable_t *root = &ctx.tables[0];
for (winivalue_t *val = root->values; val != vecEnd(root->values); ++val) {
ostrPrintf(&out, "%s = %s\n", val->key.buf, val->value.buf);
}
if (root->values) ostrPuts(&out, "\n");
// add each table
for (usize i = 1; i < vecLen(ctx.tables); ++i) {
winitable_t *tab = &ctx.tables[i];
ostrPrintf(&out, "[%s]\n", tab->key.buf);
for (winivalue_t *val = tab->values; val != vecEnd(tab->values); ++val) {
ostrPrintf(&out, "%s = %s\n", val->key.buf, val->value.buf);
}
if ((i + 1) < vecLen(ctx.tables)) ostrPuts(&out, "\n");
}
return ostrAsStr(out);
}
void winiToFile(iniwriter_t ctx, const char *filename, const winiopts_t *options) {
if (!options) options = &default_wopts;
file_t fp = fileOpen(filename, FILE_WRITE);
if (!fileIsValid(fp)) {
err("couldn't write ini to file %s", filename);
return;
}
str_t string = winiToString(ctx, options);
fileWriteWholeTextFP(fp, strvInitStr(string));
strFree(string);
fileClose(fp);
}
winivalue_t *winiAddValEmpty(winitable_t *table) {
if (!table) return NULL;
vecAppend(table->values, (winivalue_t){0});
return &vecBack(table->values);
}
winivalue_t *winiAddVal(winitable_t *table, const char *key, const char *value) {
if (!table) return NULL;
winivalue_t val = { .key = strFromStr(key), .value = strFromStr(value) };
vecAppend(table->values, val);
return &vecBack(table->values);
}
winivalue_t *winiAddValStr(winitable_t *table, str_t key, str_t value) {
if (!table) return NULL;
winivalue_t val = { .key = key, .value = value };
vecAppend(table->values, val);
return &vecBack(table->values);
}
winivalue_t *winiAddValView(winitable_t *table, strview_t key, strview_t value) {
if (!table) return NULL;
winivalue_t val = { .key = strFromView(key), .value = strFromView(value) };
vecAppend(table->values, val);
return &vecBack(table->values);
}
winitable_t *winiAddTablEmpty(iniwriter_t *ctx) {
vecAppend(ctx->tables, (winitable_t){0});
return &vecBack(ctx->tables);
}
winitable_t *winiAddTab(iniwriter_t *ctx, const char *name) {
winitable_t tab = { .key = strFromStr(name) };
vecAppend(ctx->tables, tab);
return &vecBack(ctx->tables);
}
winitable_t *winiAddTabStr(iniwriter_t *ctx, str_t name) {
winitable_t tab = { .key = name };
vecAppend(ctx->tables, tab);
return &vecBack(ctx->tables);
}
winitable_t *winiAddTabView(iniwriter_t *ctx, strview_t name) {
winitable_t tab = { .key = strFromView(name) };
vecAppend(ctx->tables, tab);
return &vecBack(ctx->tables);
}
// == PRIVATE FUNCTIONS ========================================================
static initable_t *findTable(ini_t *ctx, strview_t name) {
if (strvIsEmpty(name)) return NULL;
for (uint32 i = 1; i < vecLen(ctx->tables); ++i) {
if (strvCompare(ctx->tables[i].name, name) == 0) {
return &ctx->tables[i];
}
}
return NULL;
}
static inivalue_t *findValue(vec(inivalue_t) values, strview_t key) {
if (strvIsEmpty(key)) return NULL;
for (uint32 i = 0; i < vecLen(values); ++i) {
if (strvCompare(values[i].key, key) == 0) {
return &values[i];
}
}
return NULL;
}
static void addTable(ini_t *ctx, str_istream_t *in, const iniopts_t *options) {
istrSkip(in, 1); // skip [
strview_t name = istrGetview(in, ']');
istrSkip(in, 1); // skip ]
initable_t *table = options->merge_duplicate_tables ? findTable(ctx, name) : NULL;
if (!table) {
vecAppend(ctx->tables, (initable_t){ name });
table = &vecBack(ctx->tables);
}
istrIgnore(in, '\n'); istrSkip(in, 1);
while (!istrIsFinished(*in)) {
switch (*in->cur) {
case '\n': case '\r':
return;
case '#': case ';':
istrIgnore(in, '\n');
break;
default:
addValue(table, in, options);
break;
}
}
}
static void addValue(initable_t *table, str_istream_t *in, const iniopts_t *options) {
if (!table) fatal("table is null");
strview_t key = strvTrim(istrGetview(in, '='));
istrSkip(in, 1);
strview_t value = strvTrim(istrGetview(in, '\n'));
// value might be until EOF, in that case no use in skipping
if (!istrIsFinished(*in)) istrSkip(in, 1); // skip newline
inivalue_t *new_value = options->merge_duplicate_keys ? findValue(table->values, key) : NULL;
if (!new_value) {
inivalue_t ini_val = (inivalue_t){ key, value };
vecAppend(table->values, ini_val);
}
else {
new_value->value = value;
}
}

86
src/ini.h Normal file
View file

@ -0,0 +1,86 @@
#pragma once
#include <stdbool.h>
#include "str.h"
#include "vec.h"
#include "utf8.h"
#ifdef __cplusplus
extern "C" {
#endif
// == INI READER ========================================================================
typedef struct {
strview_t key;
strview_t value;
} inivalue_t;
typedef struct {
strview_t name;
vec(inivalue_t) values;
} initable_t;
typedef struct {
str_t text;
vec(initable_t) tables;
} ini_t;
typedef struct {
bool merge_duplicate_tables;
bool merge_duplicate_keys;
} iniopts_t;
ini_t iniParse(const char *filename, const iniopts_t *options);
ini_t iniParseString(const char *inistr, const iniopts_t *options);
void iniFree(ini_t ctx);
initable_t *iniGetTable(ini_t *ctx, const char *name);
inivalue_t *iniGet(initable_t *ctx, const char *key);
vec(strview_t) iniAsArray(const inivalue_t *value, char delim);
// delim is expected to be a single utf8 character
vec(strview_t) iniAsArrayU8(const inivalue_t *value, const char *delim);
uint64 iniAsUInt(const inivalue_t *value);
int64 iniAsInt(const inivalue_t *value);
double iniAsNum(const inivalue_t *value);
// == INI WRITER ========================================================================
typedef struct {
str_t key;
str_t value;
} winivalue_t;
typedef struct {
str_t key;
vec(winivalue_t) values;
} winitable_t;
typedef struct {
vec(winitable_t) tables;
} iniwriter_t;
typedef struct {
bool no_discalimer;
} winiopts_t;
iniwriter_t winiInit();
void winiFree(iniwriter_t ctx);
str_t winiToString(iniwriter_t ctx, const winiopts_t *options);
void winiToFile(iniwriter_t ctx, const char *filename, const winiopts_t *options);
winivalue_t *winiAddValEmpty(winitable_t *table);
winivalue_t *winiAddVal(winitable_t *table, const char *key, const char *value);
winivalue_t *winiAddValStr(winitable_t *table, str_t key, str_t value);
winivalue_t *winiAddValView(winitable_t *table, strview_t key, strview_t value);
winitable_t *winiAddTablEmpty(iniwriter_t *ctx);
winitable_t *winiAddTab(iniwriter_t *ctx, const char *name);
winitable_t *winiAddTabStr(iniwriter_t *ctx, str_t name);
winitable_t *winiAddTabView(iniwriter_t *ctx, strview_t name);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -10,7 +10,7 @@
#include <lmcons.h> #include <lmcons.h>
// modified from netbsd source http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/pkgtools/libnbcompat/files/getdelim.c?only_with_tag=MAIN // modified from netbsd source http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/pkgtools/libnbcompat/files/getdelim.c?only_with_tag=MAIN
ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) { isize getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) {
char *ptr, *eptr; char *ptr, *eptr;
if(*buf == NULL || *bufsz == 0) { if(*buf == NULL || *bufsz == 0) {
@ -20,7 +20,7 @@ ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) {
} }
} }
ssize_t result = -1; isize result = -1;
// usually fgetc locks every read, using windows-specific // usually fgetc locks every read, using windows-specific
// _lock_file and _unlock_file will be faster // _lock_file and _unlock_file will be faster
_lock_file(fp); _lock_file(fp);
@ -29,7 +29,7 @@ ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) {
int c = _getc_nolock(fp); int c = _getc_nolock(fp);
if(c == -1) { if(c == -1) {
if(feof(fp)) { if(feof(fp)) {
ssize_t diff = (ssize_t)(ptr - *buf); isize diff = (isize)(ptr - *buf);
if(diff != 0) { if(diff != 0) {
*ptr = '\0'; *ptr = '\0';
result = diff; result = diff;
@ -47,7 +47,7 @@ ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) {
if((ptr + 2) >= eptr) { if((ptr + 2) >= eptr) {
char *nbuf; char *nbuf;
size_t nbufsz = *bufsz * 2; size_t nbufsz = *bufsz * 2;
ssize_t d = ptr - *buf; isize d = ptr - *buf;
if((nbuf = realloc(*buf, nbufsz)) == NULL) { if((nbuf = realloc(*buf, nbufsz)) == NULL) {
break; break;
} }
@ -63,7 +63,7 @@ ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) {
} }
// taken from netbsd source http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/pkgtools/libnbcompat/files/getline.c?only_with_tag=MAIN // taken from netbsd source http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/pkgtools/libnbcompat/files/getline.c?only_with_tag=MAIN
ssize_t getline(char **line_ptr, size_t *n, FILE *stream) { isize getline(char **line_ptr, size_t *n, FILE *stream) {
return getdelim(line_ptr, n, '\n', stream); return getdelim(line_ptr, n, '\n', stream);
} }
@ -74,7 +74,7 @@ str_t getUserName() {
if(!res) { if(!res) {
return strInit(); return strInit();
} }
return strInitBuf(buf, sz); return strFromBuf(buf, sz);
} }
#else #else
@ -82,14 +82,26 @@ str_t getUserName() {
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <ctype.h>
str_t getUserName() { int stricmp(const char *a, const char *b) {
char buf[255]; int result;
int res = getlogin_r(buf, sizeof(buf));
if(res) { if (a == b) {
return strInit(); return 0;
} }
return strInitStr(buf);
while ((result = tolower(*a) - tolower(*b++)) == 0) {
if (*a++ == '\0') {
break;
}
}
return result;
} }
#endif str_t getUserName() {
return strFromStr(getlogin());
}
#endif

View file

@ -7,25 +7,24 @@ extern "C" {
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include "str.h" #include "str.h"
#include "collatypes.h"
#ifdef _WIN32 #ifdef _WIN32
#include <stdio.h> #include <stdio.h>
#include "win32_slim.h" #include "win32_slim.h"
typedef SSIZE_T ssize_t; isize getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp);
ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp); isize getline(char **line_ptr, size_t *n, FILE *stream);
ssize_t getline(char **line_ptr, size_t *n, FILE *stream);
#define stricmp _stricmp #define stricmp _stricmp
#else #else
#define stricmp strcasecmp
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <sys/types.h> // ssize_t int stricmp(const char *a, const char *b);
#endif #endif
str_t getUserName(); str_t getUserName();
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -48,7 +48,7 @@ bool skCleanup() {
return SOCK_CALL(skCleanup()); return SOCK_CALL(skCleanup());
} }
socket_t skOpen(skType type) { socket_t skOpen(sktype_t type) {
int sock_type = 0; int sock_type = 0;
switch(type) { switch(type) {
@ -333,4 +333,4 @@ static int _posix_skGetError() {
static const char *_posix_skGetErrorString() { static const char *_posix_skGetErrorString() {
return strerror(errno); return strerror(errno);
} }
#endif #endif

View file

@ -36,7 +36,7 @@ typedef struct sockaddr_in sk_addrin_t;
typedef enum { typedef enum {
SOCK_TCP, SOCK_TCP,
SOCK_UDP, SOCK_UDP,
} skType; } sktype_t;
// == RAW SOCKETS ========================================== // == RAW SOCKETS ==========================================
@ -46,7 +46,7 @@ bool skInit(void);
bool skCleanup(void); bool skCleanup(void);
// Opens a socket, check socket_t with skValid // Opens a socket, check socket_t with skValid
socket_t skOpen(skType type); socket_t skOpen(sktype_t type);
// Opens a socket using 'protocol', options are // Opens a socket using 'protocol', options are
// ip, icmp, ggp, tcp, egp, pup, udp, hmp, xns-idp, rdp // ip, icmp, ggp, tcp, egp, pup, udp, hmp, xns-idp, rdp
// check socket_t with skValid // check socket_t with skValid
@ -114,4 +114,4 @@ typedef socket_t udpsock_t;
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -8,6 +8,7 @@
#include <stdio.h> #include <stdio.h>
#include "tracelog.h" #include "tracelog.h"
#include "strstream.h"
#ifdef _WIN32 #ifdef _WIN32
#include "win32_slim.h" #include "win32_slim.h"
@ -28,30 +29,38 @@ str_t strInit(void) {
}; };
} }
str_t strInitStr(const char *cstr) { str_t strFromStr(const char *cstr) {
return strInitBuf(cstr, strlen(cstr)); return cstr ? strFromBuf(cstr, strlen(cstr)) : strInit();
} }
str_t strInitView(strview_t view) { str_t strFromView(strview_t view) {
return strInitBuf(view.buf, view.len); return strFromBuf(view.buf, view.len);
} }
str_t strInitBuf(const char *buf, size_t len) { str_t strFromBuf(const char *buf, usize len) {
if (!buf) return strInit();
str_t str; str_t str;
str.len = len; str.len = len;
str.buf = malloc(len + 1); str.buf = (char *)malloc(len + 1);
memcpy(str.buf, buf, len); memcpy(str.buf, buf, len);
str.buf[len] = '\0'; str.buf[len] = '\0';
return str; return str;
} }
void strFree(str_t *ctx) { str_t strFromFmt(const char *fmt, ...) {
free(ctx->buf); str_ostream_t out = ostrInit();
ctx->buf = NULL; va_list va;
ctx->len = 0; va_start(va, fmt);
ostrPrintfV(&out, fmt, va);
va_end(va);
return ostrAsStr(out);
} }
str_t strFromWCHAR(const wchar_t *src, size_t len) { void strFree(str_t ctx) {
free(ctx.buf);
}
str_t strFromWCHAR(const wchar_t *src, usize len) {
if(len == 0) len = wcslen(src); if(len == 0) len = wcslen(src);
#ifdef _WIN32 #ifdef _WIN32
@ -62,7 +71,7 @@ str_t strFromWCHAR(const wchar_t *src, size_t len) {
NULL, 0, NULL, 0,
NULL, NULL NULL, NULL
); );
char *buf = malloc(result_len + 1); char *buf = (char *)malloc(result_len + 1);
if(buf) { if(buf) {
WideCharToMultiByte( WideCharToMultiByte(
CP_ACP, 0, CP_ACP, 0,
@ -77,23 +86,23 @@ str_t strFromWCHAR(const wchar_t *src, size_t len) {
.len = result_len .len = result_len
}; };
#else #else
size_t actual_len = len * sizeof(wchar_t); usize actual_len = len * sizeof(wchar_t);
size_t dest_len = len * 6; usize dest_len = len * 6;
char *dest = malloc(dest_len); char *dest = (char *)malloc(dest_len);
iconv_t cd = iconv_open("UTF-8", "WCHAR_T"); iconv_t cd = iconv_open("UTF-8", "WCHAR_T");
assert(cd); assert(cd);
size_t dest_left = dest_len; usize dest_left = dest_len;
char *dest_temp = dest; char *dest_temp = dest;
char *src_temp = (char*)src; char *src_temp = (char*)src;
size_t lost = iconv(cd, &src_temp, &actual_len, &dest_temp, &dest_left); usize lost = iconv(cd, &src_temp, &actual_len, &dest_temp, &dest_left);
assert(lost != ((size_t)-1)); assert(lost != ((usize)-1));
(void)lost; (void)lost;
dest_len -= dest_left; dest_len -= dest_left;
dest = realloc(dest, dest_len + 1); dest = (char *)realloc(dest, dest_len + 1);
dest[dest_len] = '\0'; dest[dest_len] = '\0';
iconv_close(cd); iconv_close(cd);
@ -113,7 +122,7 @@ wchar_t *strToWCHAR(str_t ctx) {
ctx.buf, (int)ctx.len, ctx.buf, (int)ctx.len,
NULL, 0 NULL, 0
); );
wchar_t *str = malloc(sizeof(wchar_t) * (len + 1)); wchar_t *str = (wchar_t *)malloc(sizeof(wchar_t) * (len + 1));
if(!str) return NULL; if(!str) return NULL;
len = MultiByteToWideChar( len = MultiByteToWideChar(
codepage, 0, codepage, 0,
@ -123,21 +132,21 @@ wchar_t *strToWCHAR(str_t ctx) {
str[len] = '\0'; str[len] = '\0';
return str; return str;
#else #else
size_t dest_len = ctx.len * sizeof(wchar_t); usize dest_len = ctx.len * sizeof(wchar_t);
char *dest = malloc(dest_len); char *dest = (char *)malloc(dest_len);
iconv_t cd = iconv_open("WCHAR_T", "UTF-8"); iconv_t cd = iconv_open("WCHAR_T", "UTF-8");
assert(cd); assert(cd);
size_t dest_left = dest_len; usize dest_left = dest_len;
char *dest_temp = dest; char *dest_temp = dest;
char *src_temp = ctx.buf; char *src_temp = ctx.buf;
size_t lost = iconv(cd, &src_temp, &ctx.len, &dest_temp, &dest_left); usize lost = iconv(cd, &src_temp, &ctx.len, &dest_temp, &dest_left);
assert(lost != ((size_t)-1)); assert(lost != ((usize)-1));
(void)lost; (void)lost;
dest_len -= dest_left; dest_len -= dest_left;
dest = realloc(dest, dest_len + 1); dest = (char *)realloc(dest, dest_len + 1);
dest[dest_len] = '\0'; dest[dest_len] = '\0';
iconv_close(cd); iconv_close(cd);
@ -146,83 +155,43 @@ wchar_t *strToWCHAR(str_t ctx) {
#endif #endif
} }
str_t strDup(str_t ctx) {
return strFromBuf(ctx.buf, ctx.len);
}
str_t strMove(str_t *ctx) { str_t strMove(str_t *ctx) {
str_t str = strInitBuf(ctx->buf, ctx->len); str_t out = *ctx;
ctx->buf = NULL; ctx->buf = NULL;
ctx->len = 0; ctx->len = 0;
return str; return out;
} }
str_t strDup(str_t ctx) { strview_t strGetView(str_t ctx) {
return strInitBuf(ctx.buf, ctx.len);
}
strview_t strGetView(str_t *ctx) {
return (strview_t) { return (strview_t) {
.buf = ctx->buf, .buf = ctx.buf,
.len = ctx->len .len = ctx.len
}; };
} }
char *strBegin(str_t *ctx) { char *strBegin(str_t ctx) {
return ctx->buf; return ctx.buf;
} }
char *strEnd(str_t *ctx) { char *strEnd(str_t ctx) {
return ctx->buf ? ctx->buf + ctx->len : NULL; return ctx.buf ? ctx.buf + ctx.len : NULL;
} }
char strBack(str_t *ctx) { char strBack(str_t ctx) {
return ctx->buf ? ctx->buf[ctx->len - 1] : '\0'; return ctx.buf ? ctx.buf[ctx.len - 1] : '\0';
} }
bool strIsEmpty(str_t *ctx) { bool strIsEmpty(str_t ctx) {
return ctx->len == 0; return ctx.len == 0;
}
void strAppend(str_t *ctx, const char *str) {
strAppendBuf(ctx, str, strlen(str));
}
void strAppendStr(str_t *ctx, str_t str) {
strAppendBuf(ctx, str.buf, str.len);
}
void strAppendView(str_t *ctx, strview_t view) {
strAppendBuf(ctx, view.buf, view.len);
}
void strAppendBuf(str_t *ctx, const char *buf, size_t len) {
size_t oldlen = ctx->len;
ctx->len += len;
ctx->buf = realloc(ctx->buf, ctx->len + 1);
memcpy(ctx->buf + oldlen, buf, len);
ctx->buf[ctx->len] = '\0';
}
void strAppendChars(str_t *ctx, char c, size_t count) {
size_t oldlen = ctx->len;
ctx->len += count;
ctx->buf = realloc(ctx->buf, ctx->len + 1);
memset(ctx->buf + oldlen, c, count);
ctx->buf[ctx->len] = '\0';
}
void strPush(str_t *ctx, char c) {
strAppendChars(ctx, c, 1);
}
char strPop(str_t *ctx) {
char c = strBack(ctx);
ctx->buf = realloc(ctx->buf, ctx->len);
ctx->len -= 1;
ctx->buf[ctx->len] = '\0';
return c;
} }
void strSwap(str_t *ctx, str_t *other) { void strSwap(str_t *ctx, str_t *other) {
char *buf = other->buf; char *buf = other->buf;
size_t len = other->len; usize len = other->len;
other->buf = ctx->buf; other->buf = ctx->buf;
other->len = ctx->len; other->len = ctx->len;
ctx->buf = buf; ctx->buf = buf;
@ -230,30 +199,29 @@ void strSwap(str_t *ctx, str_t *other) {
} }
void strReplace(str_t *ctx, char from, char to) { void strReplace(str_t *ctx, char from, char to) {
for(size_t i = 0; i < ctx->len; ++i) { for(usize i = 0; i < ctx->len; ++i) {
if(ctx->buf[i] == from) { if(ctx->buf[i] == from) {
ctx->buf[i] = to; ctx->buf[i] = to;
} }
} }
} }
str_t strSubstr(str_t *ctx, size_t pos, size_t len) { str_t strSubstr(str_t ctx, usize from, usize to) {
if(strIsEmpty(ctx)) return strInit(); if(strIsEmpty(ctx)) return strInit();
if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos; if (to > ctx.len) to = ctx.len;
return strInitBuf(ctx->buf + pos, len); if (from > to) from = to;
return strFromBuf(ctx.buf + from, to - from);
} }
strview_t strSubview(str_t *ctx, size_t pos, size_t len) { strview_t strSubview(str_t ctx, usize from, usize to) {
if(strIsEmpty(ctx)) return strvInit(NULL); if(strIsEmpty(ctx)) return strvInit(NULL);
if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos; if (to > ctx.len) to = ctx.len;
return (strview_t) { if (from > to) from = to;
.buf = ctx->buf + pos, return strvInitLen(ctx.buf + from, to - from);
.len = len
};
} }
void strLower(str_t *ctx) { void strLower(str_t *ctx) {
for(size_t i = 0; i < ctx->len; ++i) { for(usize i = 0; i < ctx->len; ++i) {
ctx->buf[i] = (char)tolower(ctx->buf[i]); ctx->buf[i] = (char)tolower(ctx->buf[i]);
} }
} }
@ -265,7 +233,7 @@ str_t strToLower(str_t ctx) {
} }
void strUpper(str_t *ctx) { void strUpper(str_t *ctx) {
for(size_t i = 0; i < ctx->len; ++i) { for(usize i = 0; i < ctx->len; ++i) {
ctx->buf[i] = (char)toupper(ctx->buf[i]); ctx->buf[i] = (char)toupper(ctx->buf[i]);
} }
} }
@ -286,7 +254,7 @@ strview_t strvInitStr(str_t str) {
return strvInitLen(str.buf, str.len); return strvInitLen(str.buf, str.len);
} }
strview_t strvInitLen(const char *buf, size_t size) { strview_t strvInitLen(const char *buf, usize size) {
return (strview_t) { return (strview_t) {
.buf = buf, .buf = buf,
.len = size .len = size
@ -301,49 +269,77 @@ char strvBack(strview_t ctx) {
return ctx.buf[ctx.len - 1]; return ctx.buf[ctx.len - 1];
} }
const char *strvBegin(strview_t *ctx) { const char *strvBegin(strview_t ctx) {
return ctx->buf; return ctx.buf;
} }
const char *strvEnd(strview_t *ctx) { const char *strvEnd(strview_t ctx) {
return ctx->buf + ctx->len; return ctx.buf + ctx.len;
} }
bool strvIsEmpty(strview_t ctx) { bool strvIsEmpty(strview_t ctx) {
return ctx.len == 0; return ctx.len == 0;
} }
void strvRemovePrefix(strview_t *ctx, size_t n) { strview_t strvRemovePrefix(strview_t ctx, usize n) {
ctx->buf += n; if (n > ctx.len) n = ctx.len;
ctx->len -= n; return (strview_t){
.buf = ctx.buf + n,
.len = ctx.len - n
};
} }
void strvRemoveSuffix(strview_t *ctx, size_t n) { strview_t strvRemoveSuffix(strview_t ctx, usize n) {
ctx->len -= n; if (n > ctx.len) n = ctx.len;
return (strview_t){
.buf = ctx.buf,
.len = ctx.len - n
};
} }
strview_t strvTrim(strview_t ctx) {
return strvTrimLeft(strvTrimRight(ctx));
}
strview_t strvTrimLeft(strview_t ctx) {
strview_t out = ctx;
for (usize i = 0; i < ctx.len && isspace(ctx.buf[i]); ++i) {
++out.buf;
--out.len;
}
return out;
}
strview_t strvTrimRight(strview_t ctx) {
strview_t out = ctx;
for (isize i = ctx.len - 1; i >= 0 && isspace(ctx.buf[i]); --i) {
--out.len;
}
return out;
}
str_t strvCopy(strview_t ctx) { str_t strvCopy(strview_t ctx) {
return strInitView(ctx); return strFromView(ctx);
} }
str_t strvCopyN(strview_t ctx, size_t count, size_t from) { str_t strvCopyN(strview_t ctx, usize count, usize from) {
size_t sz = ctx.len + 1 - from; usize sz = ctx.len + 1 - from;
count = min(count, sz); count = min(count, sz);
return strInitBuf(ctx.buf + from, count); return strFromBuf(ctx.buf + from, count);
} }
size_t strvCopyBuf(strview_t ctx, char *buf, size_t len, size_t from) { usize strvCopyBuf(strview_t ctx, char *buf, usize len, usize from) {
size_t sz = ctx.len + 1 - from; usize sz = ctx.len + 1 - from;
len = min(len, sz); len = min(len, sz);
memcpy(buf, ctx.buf + from, len); memcpy(buf, ctx.buf + from, len);
buf[len - 1] = '\0'; buf[len - 1] = '\0';
return len - 1; return len - 1;
} }
strview_t strvSubstr(strview_t ctx, size_t from, size_t len) { strview_t strvSub(strview_t ctx, usize from, usize to) {
if(from > ctx.len) from = ctx.len - len; if (to > ctx.len) to = ctx.len;
size_t sz = ctx.len - from; if (from > to) from = to;
return strvInitLen(ctx.buf + from, min(len, sz)); return strvInitLen(ctx.buf + from, to - from);
} }
int strvCompare(strview_t ctx, strview_t other) { int strvCompare(strview_t ctx, strview_t other) {
@ -355,7 +351,7 @@ int strvCompare(strview_t ctx, strview_t other) {
int strvICompare(strview_t ctx, strview_t other) { int strvICompare(strview_t ctx, strview_t other) {
if(ctx.len < other.len) return -1; if(ctx.len < other.len) return -1;
if(ctx.len > other.len) return 1; if(ctx.len > other.len) return 1;
for(size_t i = 0; i < ctx.len; ++i) { for(usize i = 0; i < ctx.len; ++i) {
int a = tolower(ctx.buf[i]); int a = tolower(ctx.buf[i]);
int b = tolower(other.buf[i]); int b = tolower(other.buf[i]);
if(a != b) return a - b; if(a != b) return a - b;
@ -382,7 +378,7 @@ bool strvEndsWithView(strview_t ctx, strview_t view) {
} }
bool strvContains(strview_t ctx, char c) { bool strvContains(strview_t ctx, char c) {
for(size_t i = 0; i < ctx.len; ++i) { for(usize i = 0; i < ctx.len; ++i) {
if(ctx.buf[i] == c) return true; if(ctx.buf[i] == c) return true;
} }
return false; return false;
@ -390,30 +386,30 @@ bool strvContains(strview_t ctx, char c) {
bool strvContainsView(strview_t ctx, strview_t view) { bool strvContainsView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false; if(ctx.len < view.len) return false;
size_t end = ctx.len - view.len; usize end = ctx.len - view.len;
for(size_t i = 0; i < end; ++i) { for(usize i = 0; i < end; ++i) {
if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return true; if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return true;
} }
return false; return false;
} }
size_t strvFind(strview_t ctx, char c, size_t from) { usize strvFind(strview_t ctx, char c, usize from) {
for(size_t i = from; i < ctx.len; ++i) { for(usize i = from; i < ctx.len; ++i) {
if(ctx.buf[i] == c) return i; if(ctx.buf[i] == c) return i;
} }
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindView(strview_t ctx, strview_t view, size_t from) { usize strvFindView(strview_t ctx, strview_t view, usize from) {
if(ctx.len < view.len) return SIZE_MAX; if(ctx.len < view.len) return SIZE_MAX;
size_t end = ctx.len - view.len; usize end = ctx.len - view.len;
for(size_t i = from; i < end; ++i) { for(usize i = from; i < end; ++i) {
if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return i; if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return i;
} }
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvRFind(strview_t ctx, char c, size_t from) { usize strvRFind(strview_t ctx, char c, usize from) {
if(from >= ctx.len) { if(from >= ctx.len) {
from = ctx.len - 1; from = ctx.len - 1;
} }
@ -426,7 +422,7 @@ size_t strvRFind(strview_t ctx, char c, size_t from) {
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvRFindView(strview_t ctx, strview_t view, size_t from) { usize strvRFindView(strview_t ctx, strview_t view, usize from) {
from = min(from, ctx.len); from = min(from, ctx.len);
if(view.len > ctx.len) { if(view.len > ctx.len) {
@ -440,24 +436,24 @@ size_t strvRFindView(strview_t ctx, strview_t view, size_t from) {
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindFirstOf(strview_t ctx, strview_t view, size_t from) { usize strvFindFirstOf(strview_t ctx, strview_t view, usize from) {
if(ctx.len < view.len) return SIZE_MAX; if(ctx.len < view.len) return SIZE_MAX;
for(size_t i = from; i < ctx.len; ++i) { for(usize i = from; i < ctx.len; ++i) {
for(size_t j = 0; j < view.len; ++j) { for(usize j = 0; j < view.len; ++j) {
if(ctx.buf[i] == view.buf[j]) return i; if(ctx.buf[i] == view.buf[j]) return i;
} }
} }
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from) { usize strvFindLastOf(strview_t ctx, strview_t view, usize from) {
if(from >= ctx.len) { if(from >= ctx.len) {
from = ctx.len - 1; from = ctx.len - 1;
} }
const char *buf = ctx.buf + from; const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) { for(; buf >= ctx.buf; --buf) {
for(size_t j = 0; j < view.len; ++j) { for(usize j = 0; j < view.len; ++j) {
if(*buf == view.buf[j]) return (buf - ctx.buf); if(*buf == view.buf[j]) return (buf - ctx.buf);
} }
} }
@ -465,16 +461,16 @@ size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from) {
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindFirstNot(strview_t ctx, char c, size_t from) { usize strvFindFirstNot(strview_t ctx, char c, usize from) {
size_t end = ctx.len - 1; usize end = ctx.len - 1;
for(size_t i = from; i < end; ++i) { for(usize i = from; i < end; ++i) {
if(ctx.buf[i] != c) return i; if(ctx.buf[i] != c) return i;
} }
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from) { usize strvFindFirstNotOf(strview_t ctx, strview_t view, usize from) {
for(size_t i = from; i < ctx.len; ++i) { for(usize i = from; i < ctx.len; ++i) {
if(!strvContains(view, ctx.buf[i])) { if(!strvContains(view, ctx.buf[i])) {
return i; return i;
} }
@ -482,7 +478,7 @@ size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from) {
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindLastNot(strview_t ctx, char c, size_t from) { usize strvFindLastNot(strview_t ctx, char c, usize from) {
if(from >= ctx.len) { if(from >= ctx.len) {
from = ctx.len - 1; from = ctx.len - 1;
} }
@ -497,7 +493,7 @@ size_t strvFindLastNot(strview_t ctx, char c, size_t from) {
return SIZE_MAX; return SIZE_MAX;
} }
size_t strvFindLastNotOf(strview_t ctx, strview_t view, size_t from) { usize strvFindLastNotOf(strview_t ctx, strview_t view, usize from) {
if(from >= ctx.len) { if(from >= ctx.len) {
from = ctx.len - 1; from = ctx.len - 1;
} }
@ -526,8 +522,8 @@ void strTest(void) {
s = strInitStr("hello world"); s = strInitStr("hello world");
printf("\"%s\" %zu\n", s.buf, s.len); printf("\"%s\" %zu\n", s.buf, s.len);
strFree(&s); strFree(&s);
uint8_t buf[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' }; uint8 buf[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };
s = strInitBuf((char *)buf, sizeof(buf)); s = strFromBuf((char *)buf, sizeof(buf));
printf("\"%s\" %zu\n", s.buf, s.len); printf("\"%s\" %zu\n", s.buf, s.len);
strFree(&s); strFree(&s);
} }
@ -569,7 +565,7 @@ void strTest(void) {
printf("\"%s\" %zu\n", s.buf, s.len); printf("\"%s\" %zu\n", s.buf, s.len);
strAppendView(&s, strvInit(", how is it ")); strAppendView(&s, strvInit(", how is it "));
printf("\"%s\" %zu\n", s.buf, s.len); printf("\"%s\" %zu\n", s.buf, s.len);
uint8_t buf[] = { 'g', 'o', 'i', 'n', 'g' }; uint8 buf[] = { 'g', 'o', 'i', 'n', 'g' };
strAppendBuf(&s, (char*)buf, sizeof(buf)); strAppendBuf(&s, (char*)buf, sizeof(buf));
printf("\"%s\" %zu\n", s.buf, s.len); printf("\"%s\" %zu\n", s.buf, s.len);
strAppendChars(&s, '?', 2); strAppendChars(&s, '?', 2);
@ -630,4 +626,4 @@ void strTest(void) {
strFree(&s); strFree(&s);
} }
#endif #endif

122
src/str.h Normal file
View file

@ -0,0 +1,122 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>
#include <wchar.h>
#include "collatypes.h"
#define STRV_NOT_FOUND SIZE_MAX
typedef struct str_t {
char *buf;
usize len;
} str_t;
typedef struct {
const char *buf;
usize len;
} strview_t;
// == STR_T ========================================================
str_t strInit(void);
str_t strFromStr(const char *cstr);
str_t strFromView(strview_t view);
str_t strFromBuf(const char *buf, usize len);
str_t strFromFmt(const char *fmt, ...);
str_t strFromWCHAR(const wchar_t *src, usize len);
wchar_t *strToWCHAR(str_t ctx);
void strFree(str_t ctx);
str_t strDup(str_t ctx);
str_t strMove(str_t *ctx);
strview_t strGetView(str_t ctx);
char *strBegin(str_t ctx);
char *strEnd(str_t ctx);
char strBack(str_t ctx);
bool strIsEmpty(str_t ctx);
void strReplace(str_t *ctx, char from, char to);
// if len == SIZE_MAX, copies until end
str_t strSubstr(str_t ctx, usize from, usize to);
// if len == SIZE_MAX, returns until end
strview_t strSubview(str_t ctx, usize from, usize to);
void strLower(str_t *ctx);
str_t strToLower(str_t ctx);
void strUpper(str_t *ctx);
str_t strToUpper(str_t ctx);
#ifdef STR_TESTING
void strTest(void);
#endif
// == STRVIEW_T ====================================================
strview_t strvInit(const char *cstr);
strview_t strvInitStr(str_t str);
strview_t strvInitLen(const char *buf, usize size);
char strvFront(strview_t ctx);
char strvBack(strview_t ctx);
const char *strvBegin(strview_t ctx);
const char *strvEnd(strview_t ctx);
// move view forward by n characters
strview_t strvRemovePrefix(strview_t ctx, usize n);
// move view backwards by n characters
strview_t strvRemoveSuffix(strview_t ctx, usize n);
// removes whitespace from the beginning and the end
strview_t strvTrim(strview_t ctx);
// removes whitespace from the beginning
strview_t strvTrimLeft(strview_t ctx);
// removes whitespace from the end
strview_t strvTrimRight(strview_t ctx);
bool strvIsEmpty(strview_t ctx);
str_t strvCopy(strview_t ctx);
str_t strvCopyN(strview_t ctx, usize count, usize from);
usize strvCopyBuf(strview_t ctx, char *buf, usize len, usize from);
strview_t strvSub(strview_t ctx, usize from, usize to);
int strvCompare(strview_t ctx, strview_t other);
int strvICompare(strview_t ctx, strview_t other);
bool strvStartsWith(strview_t ctx, char c);
bool strvStartsWithView(strview_t ctx, strview_t view);
bool strvEndsWith(strview_t ctx, char c);
bool strvEndsWithView(strview_t ctx, strview_t view);
bool strvContains(strview_t ctx, char c);
bool strvContainsView(strview_t ctx, strview_t view);
usize strvFind(strview_t ctx, char c, usize from);
usize strvFindView(strview_t ctx, strview_t view, usize from);
usize strvRFind(strview_t ctx, char c, usize from);
usize strvRFindView(strview_t ctx, strview_t view, usize from);
// Finds the first occurrence of any of the characters of 'view' in this view
usize strvFindFirstOf(strview_t ctx, strview_t view, usize from);
usize strvFindLastOf(strview_t ctx, strview_t view, usize from);
usize strvFindFirstNot(strview_t ctx, char c, usize from);
usize strvFindFirstNotOf(strview_t ctx, strview_t view, usize from);
usize strvFindLastNot(strview_t ctx, char c, usize from);
usize strvFindLastNotOf(strview_t ctx, strview_t view, usize from);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -5,6 +5,7 @@
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h>
#include <math.h> // HUGE_VALF #include <math.h> // HUGE_VALF
#include "tracelog.h" #include "tracelog.h"
@ -14,7 +15,7 @@ str_istream_t istrInit(const char *str) {
return istrInitLen(str, strlen(str)); return istrInitLen(str, strlen(str));
} }
str_istream_t istrInitLen(const char *str, size_t len) { str_istream_t istrInitLen(const char *str, usize len) {
str_istream_t res; str_istream_t res;
res.start = res.cur = str; res.start = res.cur = str;
res.size = len; res.size = len;
@ -26,8 +27,8 @@ char istrGet(str_istream_t *ctx) {
} }
void istrIgnore(str_istream_t *ctx, char delim) { void istrIgnore(str_istream_t *ctx, char delim) {
size_t position = ctx->cur - ctx->start; usize position = ctx->cur - ctx->start;
size_t i; usize i;
for(i = position; for(i = position;
i < ctx->size && *ctx->cur != delim; i < ctx->size && *ctx->cur != delim;
++i, ++ctx->cur); ++i, ++ctx->cur);
@ -37,8 +38,8 @@ char istrPeek(str_istream_t *ctx) {
return *ctx->cur; return *ctx->cur;
} }
void istrSkip(str_istream_t *ctx, size_t n) { void istrSkip(str_istream_t *ctx, usize n) {
size_t remaining = ctx->size - (ctx->cur - ctx->start); usize remaining = ctx->size - (ctx->cur - ctx->start);
if(n > remaining) { if(n > remaining) {
warn("skipping more then remaining: %zu -> %zu", n, remaining); warn("skipping more then remaining: %zu -> %zu", n, remaining);
return; return;
@ -46,8 +47,14 @@ void istrSkip(str_istream_t *ctx, size_t n) {
ctx->cur += n; ctx->cur += n;
} }
void istrRead(str_istream_t *ctx, char *buf, size_t len) { void istrSkipWhitespace(str_istream_t *ctx) {
size_t remaining = ctx->size - (ctx->cur - ctx->start); while (*ctx->cur && isspace(*ctx->cur)) {
++ctx->cur;
}
}
void istrRead(str_istream_t *ctx, char *buf, usize len) {
usize remaining = ctx->size - (ctx->cur - ctx->start);
if(len > remaining) { if(len > remaining) {
warn("istrRead: trying to read len %zu from remaining %zu", len, remaining); warn("istrRead: trying to read len %zu from remaining %zu", len, remaining);
return; return;
@ -56,8 +63,8 @@ void istrRead(str_istream_t *ctx, char *buf, size_t len) {
ctx->cur += len; ctx->cur += len;
} }
size_t istrReadMax(str_istream_t *ctx, char *buf, size_t len) { usize istrReadMax(str_istream_t *ctx, char *buf, usize len) {
size_t remaining = ctx->size - (ctx->cur - ctx->start); usize remaining = ctx->size - (ctx->cur - ctx->start);
len = remaining < len ? remaining : len; len = remaining < len ? remaining : len;
memcpy(buf, ctx->cur, len); memcpy(buf, ctx->cur, len);
ctx->cur += len; ctx->cur += len;
@ -68,16 +75,20 @@ void istrRewind(str_istream_t *ctx) {
ctx->cur = ctx->start; ctx->cur = ctx->start;
} }
size_t istrTell(str_istream_t *ctx) { usize istrTell(str_istream_t ctx) {
return ctx->cur - ctx->start; return ctx.cur - ctx.start;
} }
bool istrIsFinished(str_istream_t *ctx) { usize istrRemaining(str_istream_t ctx) {
return ctx->cur == (ctx->start + ctx->size + 1); return ctx.size - (ctx.cur - ctx.start);
}
bool istrIsFinished(str_istream_t ctx) {
return (usize)(ctx.cur - ctx.start) >= ctx.size;
} }
bool istrGetbool(str_istream_t *ctx, bool *val) { bool istrGetbool(str_istream_t *ctx, bool *val) {
size_t remaining = ctx->size - (ctx->cur - ctx->start); usize remaining = ctx->size - (ctx->cur - ctx->start);
if(strncmp(ctx->cur, "true", remaining) == 0) { if(strncmp(ctx->cur, "true", remaining) == 0) {
*val = true; *val = true;
return true; return true;
@ -89,9 +100,9 @@ bool istrGetbool(str_istream_t *ctx, bool *val) {
return false; return false;
} }
bool istrGetu8(str_istream_t *ctx, uint8_t *val) { bool istrGetu8(str_istream_t *ctx, uint8 *val) {
char *end = NULL; char *end = NULL;
*val = (uint8_t) strtoul(ctx->cur, &end, 0); *val = (uint8) strtoul(ctx->cur, &end, 0);
if(ctx->cur == end) { if(ctx->cur == end) {
warn("istrGetu8: no valid conversion could be performed"); warn("istrGetu8: no valid conversion could be performed");
@ -106,9 +117,9 @@ bool istrGetu8(str_istream_t *ctx, uint8_t *val) {
return true; return true;
} }
bool istrGetu16(str_istream_t *ctx, uint16_t *val) { bool istrGetu16(str_istream_t *ctx, uint16 *val) {
char *end = NULL; char *end = NULL;
*val = (uint16_t) strtoul(ctx->cur, &end, 0); *val = (uint16) strtoul(ctx->cur, &end, 0);
if(ctx->cur == end) { if(ctx->cur == end) {
warn("istrGetu16: no valid conversion could be performed"); warn("istrGetu16: no valid conversion could be performed");
@ -123,9 +134,9 @@ bool istrGetu16(str_istream_t *ctx, uint16_t *val) {
return true; return true;
} }
bool istrGetu32(str_istream_t *ctx, uint32_t *val) { bool istrGetu32(str_istream_t *ctx, uint32 *val) {
char *end = NULL; char *end = NULL;
*val = (uint32_t) strtoul(ctx->cur, &end, 0); *val = (uint32) strtoul(ctx->cur, &end, 0);
if(ctx->cur == end) { if(ctx->cur == end) {
warn("istrGetu32: no valid conversion could be performed"); warn("istrGetu32: no valid conversion could be performed");
@ -140,7 +151,7 @@ bool istrGetu32(str_istream_t *ctx, uint32_t *val) {
return true; return true;
} }
bool istrGetu64(str_istream_t *ctx, uint64_t *val) { bool istrGetu64(str_istream_t *ctx, uint64 *val) {
char *end = NULL; char *end = NULL;
*val = strtoull(ctx->cur, &end, 0); *val = strtoull(ctx->cur, &end, 0);
@ -157,9 +168,9 @@ bool istrGetu64(str_istream_t *ctx, uint64_t *val) {
return true; return true;
} }
bool istrGeti8(str_istream_t *ctx, int8_t *val) { bool istrGeti8(str_istream_t *ctx, int8 *val) {
char *end = NULL; char *end = NULL;
*val = (int8_t) strtol(ctx->cur, &end, 0); *val = (int8) strtol(ctx->cur, &end, 0);
if(ctx->cur == end) { if(ctx->cur == end) {
warn("istrGeti8: no valid conversion could be performed"); warn("istrGeti8: no valid conversion could be performed");
@ -174,9 +185,9 @@ bool istrGeti8(str_istream_t *ctx, int8_t *val) {
return true; return true;
} }
bool istrGeti16(str_istream_t *ctx, int16_t *val) { bool istrGeti16(str_istream_t *ctx, int16 *val) {
char *end = NULL; char *end = NULL;
*val = (int16_t) strtol(ctx->cur, &end, 0); *val = (int16) strtol(ctx->cur, &end, 0);
if(ctx->cur == end) { if(ctx->cur == end) {
warn("istrGeti16: no valid conversion could be performed"); warn("istrGeti16: no valid conversion could be performed");
@ -191,9 +202,9 @@ bool istrGeti16(str_istream_t *ctx, int16_t *val) {
return true; return true;
} }
bool istrGeti32(str_istream_t *ctx, int32_t *val) { bool istrGeti32(str_istream_t *ctx, int32 *val) {
char *end = NULL; char *end = NULL;
*val = (int32_t) strtol(ctx->cur, &end, 0); *val = (int32) strtol(ctx->cur, &end, 0);
if(ctx->cur == end) { if(ctx->cur == end) {
warn("istrGeti32: no valid conversion could be performed"); warn("istrGeti32: no valid conversion could be performed");
@ -208,7 +219,7 @@ bool istrGeti32(str_istream_t *ctx, int32_t *val) {
return true; return true;
} }
bool istrGeti64(str_istream_t *ctx, int64_t *val) { bool istrGeti64(str_istream_t *ctx, int64 *val) {
char *end = NULL; char *end = NULL;
*val = strtoll(ctx->cur, &end, 0); *val = strtoll(ctx->cur, &end, 0);
@ -259,7 +270,7 @@ bool istrGetdouble(str_istream_t *ctx, double *val) {
return true; return true;
} }
size_t istrGetstring(str_istream_t *ctx, char **val, char delim) { usize istrGetstring(str_istream_t *ctx, char **val, char delim) {
const char *from = ctx->cur; const char *from = ctx->cur;
istrIgnore(ctx, delim); istrIgnore(ctx, delim);
// if it didn't actually find it, it just reached the end of the string // if it didn't actually find it, it just reached the end of the string
@ -267,15 +278,15 @@ size_t istrGetstring(str_istream_t *ctx, char **val, char delim) {
*val = NULL; *val = NULL;
return 0; return 0;
} }
size_t len = ctx->cur - from; usize len = ctx->cur - from;
*val = malloc(len + 1); *val = (char *)malloc(len + 1);
memcpy(*val, from, len); memcpy(*val, from, len);
(*val)[len] = '\0'; (*val)[len] = '\0';
return len; return len;
} }
size_t istrGetstringBuf(str_istream_t *ctx, char *val, size_t len) { usize istrGetstringBuf(str_istream_t *ctx, char *val, usize len) {
size_t remaining = ctx->size - (ctx->cur - ctx->start); usize remaining = ctx->size - (ctx->cur - ctx->start);
len -= 1; len -= 1;
len = remaining < len ? remaining : len; len = remaining < len ? remaining : len;
memcpy(val, ctx->cur, len); memcpy(val, ctx->cur, len);
@ -287,86 +298,74 @@ size_t istrGetstringBuf(str_istream_t *ctx, char *val, size_t len) {
strview_t istrGetview(str_istream_t *ctx, char delim) { strview_t istrGetview(str_istream_t *ctx, char delim) {
const char *from = ctx->cur; const char *from = ctx->cur;
istrIgnore(ctx, delim); istrIgnore(ctx, delim);
// if it didn't actually find it, it just reached the end of the string usize len = ctx->cur - from;
if(*ctx->cur != delim) {
return strvInitLen(NULL, 0);
}
size_t len = ctx->cur - from;
return strvInitLen(from, len); return strvInitLen(from, len);
} }
strview_t istrGetviewLen(str_istream_t *ctx, size_t off, size_t len) { strview_t istrGetviewLen(str_istream_t *ctx, usize from, usize to) {
size_t remaining = ctx->size - (ctx->cur - ctx->start) - off; usize len = ctx->size - (ctx->cur - ctx->start) - from;
if(len > remaining) len = remaining; if (to > len) to = len;
return strvInitLen(ctx->cur + off, len); if (from > to) from = to;
return strvInitLen(ctx->cur + from, to - from);
} }
/* == OUTPUT STREAM =========================================== */ /* == OUTPUT STREAM =========================================== */
static void _ostrRealloc(str_ostream_t *ctx, size_t needed) { static void _ostrRealloc(str_ostream_t *ctx, usize needed) {
ctx->allocated = (ctx->allocated * 2) + needed; ctx->cap = (ctx->cap * 2) + needed;
ctx->buf = realloc(ctx->buf, ctx->allocated); ctx->buf = (char *)realloc(ctx->buf, ctx->cap);
} }
str_ostream_t ostrInit() { str_ostream_t ostrInit() {
return ostrInitLen(1); return ostrInitLen(1);
} }
str_ostream_t ostrInitLen(size_t initial_alloc) { str_ostream_t ostrInitLen(usize initial_alloc) {
str_ostream_t stream; str_ostream_t stream;
stream.buf = calloc(initial_alloc, 1); stream.buf = (char *)calloc(initial_alloc, 1);
stream.size = 0; stream.len = 0;
stream.allocated = initial_alloc; stream.cap = initial_alloc;
return stream; return stream;
} }
str_ostream_t ostrInitStr(const char *cstr, size_t len) { str_ostream_t ostrInitStr(const char *cstr, usize len) {
str_ostream_t stream; str_ostream_t stream;
stream.buf = malloc(len + 1); stream.buf = (char *)malloc(len + 1);
memcpy(stream.buf, cstr, len); memcpy(stream.buf, cstr, len);
stream.size = len; stream.len = len;
stream.allocated = len + 1; stream.cap = len + 1;
return stream; return stream;
} }
void ostrFree(str_ostream_t *ctx) { void ostrFree(str_ostream_t ctx) {
free(ctx->buf); free(ctx.buf);
ctx->buf = NULL;
ctx->size = 0;
ctx->allocated = 0;
} }
void ostrClear(str_ostream_t *ctx) { void ostrClear(str_ostream_t *ctx) {
ctx->size = 0; ctx->len = 0;
} }
str_t ostrMove(str_ostream_t *ctx) { char ostrBack(str_ostream_t ctx) {
str_t str = ostrAsStr(ctx); if(ctx.len == 0) return '\0';
*ctx = (str_ostream_t){0}; return ctx.buf[ctx.len - 1];
return str;
} }
char ostrBack(str_ostream_t *ctx) { str_t ostrAsStr(str_ostream_t ctx) {
if(ctx->size == 0) return '\0';
return ctx->buf[ctx->size - 1];
}
str_t ostrAsStr(str_ostream_t *ctx) {
return (str_t) { return (str_t) {
.buf = ctx->buf, .buf = ctx.buf,
.len = ctx->size .len = ctx.len
}; };
} }
strview_t ostrAsView(str_ostream_t *ctx) { strview_t ostrAsView(str_ostream_t ctx) {
return (strview_t) { return (strview_t) {
.buf = ctx->buf, .buf = ctx.buf,
.len = ctx->size .len = ctx.len
}; };
} }
void ostrReplace(str_ostream_t *ctx, char from, char to) { void ostrReplace(str_ostream_t *ctx, char from, char to) {
for(size_t i = 0; i < ctx->size; ++i) { for(usize i = 0; i < ctx->len; ++i) {
if(ctx->buf[i] == from) { if(ctx->buf[i] == from) {
ctx->buf[i] = to; ctx->buf[i] = to;
} }
@ -376,56 +375,69 @@ void ostrReplace(str_ostream_t *ctx, char from, char to) {
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...) { void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
ostrPrintfV(ctx, fmt, va);
va_end(va);
}
void ostrPrintfV(str_ostream_t *ctx, const char *fmt, va_list args) {
va_list vtemp;
int len;
usize remaining;
// vsnprintf returns the length of the formatted string, even if truncated // vsnprintf returns the length of the formatted string, even if truncated
// we use this to get the actual length of the formatted string // we use this to get the actual length of the formatted string
char buf[1]; va_copy(vtemp, args);
va_list vtemp; len = vsnprintf(NULL, 0, fmt, vtemp);
va_copy(vtemp, va);
int len = vsnprintf(buf, sizeof(buf), fmt, vtemp);
va_end(vtemp); va_end(vtemp);
if(len < 0) { if(len < 0) {
err("couldn't format string \"%s\"", fmt); err("couldn't format string \"%s\"", fmt);
goto error; goto error;
} }
size_t remaining = ctx->allocated - ctx->size; remaining = ctx->cap - ctx->len;
if(remaining < (size_t)len) { if(remaining < (usize)len) {
_ostrRealloc(ctx, len + 1); _ostrRealloc(ctx, len + 1);
remaining = ctx->allocated - ctx->size; remaining = ctx->cap - ctx->len;
} }
// actual formatting here // actual formatting here
len = vsnprintf(ctx->buf + ctx->size, remaining, fmt, va); va_copy(vtemp, args);
len = vsnprintf(ctx->buf + ctx->len, remaining, fmt, vtemp);
va_end(vtemp);
if(len < 0) { if(len < 0) {
err("couldn't format stringh \"%s\"", fmt); err("couldn't format stringh \"%s\"", fmt);
goto error; goto error;
} }
ctx->size += len; ctx->len += len;
error: error:
va_end(va); return;
} }
#define APPEND_BUF_LEN 20 #define APPEND_BUF_LEN 20
void ostrPutc(str_ostream_t *ctx, char c) { void ostrPutc(str_ostream_t *ctx, char c) {
ostrAppendchar(ctx, c); ostrAppendchar(ctx, c);
} }
void ostrPuts(str_ostream_t *ctx, const char *str) {
ostrAppendview(ctx, strvInit(str));
}
void ostrAppendbool(str_ostream_t *ctx, bool val) { void ostrAppendbool(str_ostream_t *ctx, bool val) {
ostrAppendview(ctx, strvInit(val ? "true" : "false")); ostrAppendview(ctx, strvInit(val ? "true" : "false"));
} }
void ostrAppendchar(str_ostream_t *ctx, char val) { void ostrAppendchar(str_ostream_t *ctx, char val) {
if(ctx->size >= ctx->allocated) { if(ctx->len >= ctx->cap) {
_ostrRealloc(ctx, 1); _ostrRealloc(ctx, 1);
} }
ctx->buf[ctx->size++] = val; ctx->buf[ctx->len++] = val;
ctx->buf[ctx->size] = '\0'; ctx->buf[ctx->len] = '\0';
} }
void ostrAppendu8(str_ostream_t *ctx, uint8_t val) { void ostrAppendu8(str_ostream_t *ctx, uint8 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%hhu", val); int len = snprintf(buf, sizeof(buf), "%hhu", val);
if(len <= 0) { if(len <= 0) {
@ -435,7 +447,7 @@ void ostrAppendu8(str_ostream_t *ctx, uint8_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendu16(str_ostream_t *ctx, uint16_t val) { void ostrAppendu16(str_ostream_t *ctx, uint16 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%hu", val); int len = snprintf(buf, sizeof(buf), "%hu", val);
if(len <= 0) { if(len <= 0) {
@ -445,7 +457,7 @@ void ostrAppendu16(str_ostream_t *ctx, uint16_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendu32(str_ostream_t *ctx, uint32_t val) { void ostrAppendu32(str_ostream_t *ctx, uint32 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%u", val); int len = snprintf(buf, sizeof(buf), "%u", val);
if(len <= 0) { if(len <= 0) {
@ -455,7 +467,7 @@ void ostrAppendu32(str_ostream_t *ctx, uint32_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendu64(str_ostream_t *ctx, uint64_t val) { void ostrAppendu64(str_ostream_t *ctx, uint64 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
#if _WIN32 #if _WIN32
int len = snprintf(buf, sizeof(buf), "%llu", val); int len = snprintf(buf, sizeof(buf), "%llu", val);
@ -469,7 +481,7 @@ void ostrAppendu64(str_ostream_t *ctx, uint64_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendi8(str_ostream_t *ctx, int8_t val) { void ostrAppendi8(str_ostream_t *ctx, int8 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%hhi", val); int len = snprintf(buf, sizeof(buf), "%hhi", val);
if(len <= 0) { if(len <= 0) {
@ -479,7 +491,7 @@ void ostrAppendi8(str_ostream_t *ctx, int8_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendi16(str_ostream_t *ctx, int16_t val) { void ostrAppendi16(str_ostream_t *ctx, int16 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%hi", val); int len = snprintf(buf, sizeof(buf), "%hi", val);
if(len <= 0) { if(len <= 0) {
@ -489,7 +501,7 @@ void ostrAppendi16(str_ostream_t *ctx, int16_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendi32(str_ostream_t *ctx, int32_t val) { void ostrAppendi32(str_ostream_t *ctx, int32 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%i", val); int len = snprintf(buf, sizeof(buf), "%i", val);
if(len <= 0) { if(len <= 0) {
@ -499,7 +511,7 @@ void ostrAppendi32(str_ostream_t *ctx, int32_t val) {
ostrAppendview(ctx, strvInitLen(buf, len)); ostrAppendview(ctx, strvInitLen(buf, len));
} }
void ostrAppendi64(str_ostream_t *ctx, int64_t val) { void ostrAppendi64(str_ostream_t *ctx, int64 val) {
char buf[APPEND_BUF_LEN]; char buf[APPEND_BUF_LEN];
#if _WIN32 #if _WIN32
int len = snprintf(buf, sizeof(buf), "%lli", val); int len = snprintf(buf, sizeof(buf), "%lli", val);
@ -534,10 +546,10 @@ void ostrAppenddouble(str_ostream_t *ctx, double val) {
} }
void ostrAppendview(str_ostream_t *ctx, strview_t view) { void ostrAppendview(str_ostream_t *ctx, strview_t view) {
if((ctx->allocated - ctx->size) <= view.len) { if((ctx->cap - ctx->len) <= view.len) {
_ostrRealloc(ctx, view.len + 1); _ostrRealloc(ctx, view.len + 1);
} }
memcpy(ctx->buf + ctx->size, view.buf, view.len); memcpy(ctx->buf + ctx->len, view.buf, view.len);
ctx->size += view.len; ctx->len += view.len;
ctx->buf[ctx->size] = '\0'; ctx->buf[ctx->len] = '\0';
} }

110
src/strstream.h Normal file
View file

@ -0,0 +1,110 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stddef.h>
#include <stdarg.h>
#include "collatypes.h"
#include "str.h"
/* == INPUT STREAM ============================================ */
typedef struct {
const char *start;
const char *cur;
usize size;
} str_istream_t;
// initialize with null-terminated string
str_istream_t istrInit(const char *str);
str_istream_t istrInitLen(const char *str, usize len);
// get the current character and advance
char istrGet(str_istream_t *ctx);
// get the current character but don't advance
char istrPeek(str_istream_t *ctx);
// ignore characters until the delimiter
void istrIgnore(str_istream_t *ctx, char delim);
// skip n characters
void istrSkip(str_istream_t *ctx, usize n);
// skips whitespace (' ', '\n', '\t', '\r')
void istrSkipWhitespace(str_istream_t *ctx);
// read len bytes into buffer, the buffer will not be null terminated
void istrRead(str_istream_t *ctx, char *buf, usize len);
// read a maximum of len bytes into buffer, the buffer will not be null terminated
// returns the number of bytes read
usize istrReadMax(str_istream_t *ctx, char *buf, usize len);
// returns to the beginning of the stream
void istrRewind(str_istream_t *ctx);
// returns the number of bytes read from beginning of stream
usize istrTell(str_istream_t ctx);
// returns the number of bytes left to read in the stream
usize istrRemaining(str_istream_t ctx);
// return true if the stream doesn't have any new bytes to read
bool istrIsFinished(str_istream_t ctx);
bool istrGetbool(str_istream_t *ctx, bool *val);
bool istrGetu8(str_istream_t *ctx, uint8 *val);
bool istrGetu16(str_istream_t *ctx, uint16 *val);
bool istrGetu32(str_istream_t *ctx, uint32 *val);
bool istrGetu64(str_istream_t *ctx, uint64 *val);
bool istrGeti8(str_istream_t *ctx, int8 *val);
bool istrGeti16(str_istream_t *ctx, int16 *val);
bool istrGeti32(str_istream_t *ctx, int32 *val);
bool istrGeti64(str_istream_t *ctx, int64 *val);
bool istrGetfloat(str_istream_t *ctx, float *val);
bool istrGetdouble(str_istream_t *ctx, double *val);
// get a string until a delimiter, the string is allocated by the function and should be freed
usize istrGetstring(str_istream_t *ctx, char **val, char delim);
// get a string of maximum size len, the string is not allocated by the function and will be null terminated
usize istrGetstringBuf(str_istream_t *ctx, char *val, usize len);
strview_t istrGetview(str_istream_t *ctx, char delim);
strview_t istrGetviewLen(str_istream_t *ctx, usize from, usize to);
/* == OUTPUT STREAM =========================================== */
typedef struct {
char *buf;
usize len;
usize cap;
} str_ostream_t;
str_ostream_t ostrInit(void);
str_ostream_t ostrInitLen(usize initial_alloc);
str_ostream_t ostrInitStr(const char *buf, usize len);
void ostrFree(str_ostream_t ctx);
void ostrClear(str_ostream_t *ctx);
char ostrBack(str_ostream_t ctx);
str_t ostrAsStr(str_ostream_t ctx);
strview_t ostrAsView(str_ostream_t ctx);
void ostrReplace(str_ostream_t *ctx, char from, char to);
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...);
void ostrPrintfV(str_ostream_t *ctx, const char *fmt, va_list args);
void ostrPutc(str_ostream_t *ctx, char c);
void ostrPuts(str_ostream_t *ctx, const char *str);
void ostrAppendbool(str_ostream_t *ctx, bool val);
void ostrAppendchar(str_ostream_t *ctx, char val);
void ostrAppendu8(str_ostream_t *ctx, uint8 val);
void ostrAppendu16(str_ostream_t *ctx, uint16 val);
void ostrAppendu32(str_ostream_t *ctx, uint32 val);
void ostrAppendu64(str_ostream_t *ctx, uint64 val);
void ostrAppendi8(str_ostream_t *ctx, int8 val);
void ostrAppendi16(str_ostream_t *ctx, int16 val);
void ostrAppendi32(str_ostream_t *ctx, int32 val);
void ostrAppendi64(str_ostream_t *ctx, int64 val);
void ostrAppendfloat(str_ostream_t *ctx, float val);
void ostrAppenddouble(str_ostream_t *ctx, double val);
void ostrAppendview(str_ostream_t *ctx, strview_t view);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -119,4 +119,4 @@ void traceLogVaList(int level, const char *fmt, va_list args) {
void traceUseNewline(bool newline) { void traceUseNewline(bool newline) {
use_newline = newline; use_newline = newline;
} }

View file

@ -31,4 +31,4 @@ void traceUseNewline(bool use_newline);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

172
src/utf8.c Normal file
View file

@ -0,0 +1,172 @@
#include "utf8.h"
static const uint8 masks[] = {
0x7f, // 0111-1111
0x1f, // 0001-1111
0x0f, // 0000-1111
0x07, // 0000-0111
0x03, // 0000-0011
0x01 // 0000-0001
};
struct {
uint8 mask;
uint8 result;
int octets;
} sizes[] = {
{ 0x80, 0x00, 1 }, // 1000-0000, 0000-0000
{ 0xE0, 0xC0, 2 }, // 1110-0000, 1100-0000
{ 0xF0, 0xE0, 3 }, // 1111-0000, 1110-0000
{ 0xF8, 0xF0, 4 }, // 1111-1000, 1111-0000
{ 0xFC, 0xF8, 5 }, // 1111-1100, 1111-1000
{ 0xFE, 0xF8, 6 }, // 1111-1110, 1111-1000
{ 0x80, 0x80, -1 }, // 1000-0000, 1000-0000
};
/*
UTF-8 codepoints are encoded using the first bits of the first character
byte 1 | byte 2 | byte 3 | byte 4
0xxx xxxx | | |
110x xxxx | 10xx xxxx | |
1110 xxxx | 10xx xxxx | 10xx xxxx |
1111 0xxx | 10xx xxxx | 10xx xxxx | 10xx xxxx
so when we decode it we first find the size of the codepoint (from 1 to 4)
then we apply the mask to the first byte to get the first character
then we keep shifting the rune left 6 and applying the next byte to the mask
until the codepoint is finished (size is 0)
## EXAMPLE
utf8 string () = 1110-0010 1000-0010 1010-1100
cp = 0000-0000 0000-0000 0000-0000 0000-0000
size = 3
mask = 0x0f -> 0000-1111
cp = *s & mask = 1110-0010 & 0000-1111 = 0000-0000 0000-0000 0000-0000 0000-0010
++s = 1000-0010
--size = 2
cp <<= 6 = 0000-0000 0000-0000 0000-0000 1000-0000
cp |= *s & 0x3f = 1000-0010 & 0011-1111 = 0000-0000 0000-0000 0000-0000 1000-0010
++s = 1010-1100
--size = 1
cp <<= 6 = 0000-0000 0000-0000 0010-0000 1000-0000
cp |= *s & 0x3f = 1010-1100 & 0011-1111 = 0000-0000 0000-0000 0010-0000 1010-1100
++s = ----------
final codepoint = 0010-0000 1010-1100
codepoint = 0010-0000 1010-1100
*/
rune utf8Decode(const char **char_str) {
uint8 **s = (uint8 **)char_str;
rune ch = 0;
// if is ascii
if (**s < 128) {
ch = **s;
++*s;
return ch;
}
int size = utf8Size((char *)*s);
if (size == -1) {
++*s;
return UTF8_INVALID;
}
uint8 mask = masks[size - 1];
ch = **s & mask;
++*s;
while(--size) {
ch <<= 6;
ch |= **s & 0x3f; // 0011-1111
++*s;
}
return ch;
}
/*
to encode a codepoint in a utf8 string we first need to find
the length of the codepoint
then we start from the rightmost byte and loop for each byte of the codepoint
using the length we got before until the first byte (which we skip)
> and (&) with 0x3f so we ignore the first to bits of the codepoint
> or (|) with 0x80 so we make sure that the first two bits are 10
> bitshift the codepoint right 6
finally, we apply the correct length-mask to the first byte
## EXAMPLE
ch = 0010-0000 1010-1100
ch < 0x10000
first = 0xe0 = 1110-0000
len = 3
str[2] = (ch & 0x3f) | 0x80 = 1010-1100 & 0011-1111 | 1000-0000
= 1010-1100
ch >>= 6 = 0010-0000 1010-1100 >> 6 = 1000-0010
str[1] = (ch & 0x3f) | 0x80 = 1000-0010 & 0011-1111 | 1000-000
= 1000-0010
ch >>= 6 = 1000-0010 >> 6 = 0000-0010
str[0] = ch | first_mask = 0000-0010 | 1111-0000
= 1111-0010
str = 1111-0010 1000-0010 1010-1100
utf8 = 1110-0010 1000-0010 1010-1100
*/
usize utf8Encode(char *str, rune codepoint) {
usize len = 0;
uint8 first;
if (codepoint < 0x80) { // 0000-0000 0000-0000 0000-0000 1000-0000
first = 0;
len = 1;
}
else if (codepoint < 0x800) { // 0000-0000 0000-0000 0000-1000 0000-0000
first = 0xc0; // 1100-0000
len = 2;
}
else if (codepoint < 0x10000) { // 0000-0000 0000-0001 0000-0000 0000-0000
first = 0xe0; // 1110-0000
len = 3;
}
else {
first = 0xf0; // 1111-0000
len = 4;
}
for (usize i = len - 1; i > 0; --i) {
// 0x3f -> 0011-1111
// 0x80 -> 1000-0000
str[i] = (codepoint & 0x3f) | 0x80;
codepoint >>= 6;
}
str[0] = (char)(codepoint | first);
return len;
}
int utf8Size(const char *str) {
uint8 c = (uint8)*str;
for(usize i = 0; i < (sizeof(sizes) / sizeof(*sizes)); ++i) {
if ((c & sizes[i].mask) == sizes[i].result) {
return sizes[i].octets;
}
}
return -1;
}
usize utf8CpSize(rune ch) {
if (ch < 0x80) return 1;
else if (ch < 0x800) return 2;
else if (ch < 0x10000) return 3;
return 4;
}

19
src/utf8.h Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include "collatypes.h"
typedef uint32 rune;
enum {
UTF8_MAX_SIZE = 4,
UTF8_INVALID = 0x80
};
// grabs the next UTF-8 codepoint and advances string ptr
rune utf8Decode(const char **str);
// encodes a codepoint as UTF-8 and returns the length
usize utf8Encode(char *str, rune ch);
// returns the size of the next UTF-8 codepoint
int utf8Size(const char *str);
// returns the size of a UTF-8 codepoint
usize utf8CpSize(rune ch);

69
src/vec.h Normal file
View file

@ -0,0 +1,69 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define vec(T) T *
#define vecFree(vec) ((vec) ? free(_vecheader(vec)),NULL : NULL)
#define vecCopy(src, dest) (vecFree(dest), vecAdd(dest, vecCount(src)), memcpy(dest, src, vecCount(src)))
#define vecAppend(vec, val) (_vecmaygrow(vec, 1), (vec)[_veclen(vec)] = (val), _veclen(vec)++)
#define vecLen(vec) ((vec) ? _veclen(vec) : 0)
#define vecCap(vec) ((vec) ? _veccap(vec) : 0)
#define vecBeg(vec) (vec)
#define vecEnd(vec) ((vec) ? (vec) + _veclen(vec) : NULL)
#define vecBack(vec) ((vec)[_veclen(vec) - 1])
#define vecAdd(vec, n) (_vecmaygrow(vec, (n)), _veclen(vec) += (size_type)(n), &(vec)[_veclen(vec)-(n)])
#define vecReserve(vec, n) (_vecmaygrow(vec, (n)))
#define vecShrink(vec) (_vecshrink((void **)&(vec), _veclen(vec), sizeof(*(vec))))
#define vecClear(vec) ((vec) ? _veclen(vec) = 0 : 0)
#define vecPop(vec) ((vec)[--_veclen(vec)])
// == IMPLEMENTATION ==========================================================================================
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
#ifndef size_type
#define size_type uint32_t
#endif
#define _vecheader(vec) ((size_type *)(vec) - 2)
#define _veccap(vec) _vecheader(vec)[0]
#define _veclen(vec) _vecheader(vec)[1]
#define _vecneedgrow(vec, n) ((vec) == NULL || _veclen(vec) + n >= _veccap(vec))
#define _vecmaygrow(vec, n) (_vecneedgrow(vec, (n)) ? _vecgrow(vec, (size_type)(n)) : (void)0)
#define _vecgrow(vec, n) _vecgrowimpl((void **)&(vec), (n), sizeof(*(vec)))
inline static void _vecgrowimpl(void **arr, size_type increment, size_type itemsize) {
int newcap = *arr ? 2 * _veccap(*arr) + increment : increment + 1;
void *ptr = realloc(*arr ? _vecheader(*arr) : 0, itemsize * newcap + sizeof(size_type) * 2);
assert(ptr);
if (ptr) {
if (!*arr) ((size_type *)ptr)[1] = 0;
*arr = (void *) ((size_type *)ptr + 2);
_veccap(*arr) = newcap;
}
}
inline static void _vecshrink(void **arr, size_type newcap, size_t itemsize) {
if (newcap == _veccap(*arr) || !*arr) return;
void *ptr = realloc(_vecheader(*arr), itemsize * newcap + sizeof(size_type) * 2);
assert(ptr);
if (ptr) {
*arr = (void *) ((size_type *)ptr + 2);
if (_veclen(*arr) < newcap) _veclen(*arr) = newcap;
_veccap(*arr) = newcap;
}
}
#ifdef __cplusplus
} // extern "C"
#endif

15
src/win32_slim.h Normal file
View file

@ -0,0 +1,15 @@
#pragma once
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef WIN32_EXTRA_LEAN
#define WIN32_EXTRA_LEAN
#endif
#include <windows.h>
#endif // _WIN32

128
str.h
View file

@ -1,128 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>
#include <wchar.h>
#define STRV_NOT_FOUND SIZE_MAX
typedef struct str_t {
char *buf;
size_t len;
} str_t;
typedef struct {
const char *buf;
size_t len;
} strview_t;
// == STR_T ========================================================
str_t strInit(void);
str_t strInitStr(const char *cstr);
str_t strInitView(strview_t view);
str_t strInitBuf(const char *buf, size_t len);
str_t strFromWCHAR(const wchar_t *src, size_t len);
wchar_t *strToWCHAR(str_t ctx);
void strFree(str_t *ctx);
str_t strMove(str_t *ctx);
str_t strDup(str_t ctx);
strview_t strGetView(str_t *ctx);
char *strBegin(str_t *ctx);
char *strEnd(str_t *ctx);
char strBack(str_t *ctx);
bool strIsEmpty(str_t *ctx);
void strAppend(str_t *ctx, const char *str);
void strAppendStr(str_t *ctx, str_t str);
void strAppendView(str_t *ctx, strview_t view);
void strAppendBuf(str_t *ctx, const char *buf, size_t len);
void strAppendChars(str_t *ctx, char c, size_t count);
void strPush(str_t *ctx, char c);
char strPop(str_t *ctx);
void strSwap(str_t *ctx, str_t *other);
void strReplace(str_t *ctx, char from, char to);
// if len == SIZE_MAX, copies until end
str_t strSubstr(str_t *ctx, size_t pos, size_t len);
// if len == SIZE_MAX, returns until end
strview_t strSubview(str_t *ctx, size_t pos, size_t len);
void strLower(str_t *ctx);
str_t strToLower(str_t ctx);
void strUpper(str_t *ctx);
str_t strToUpper(str_t ctx);
#ifdef STR_TESTING
void strTest(void);
#endif
// == STRVIEW_T ====================================================
strview_t strvInit(const char *cstr);
strview_t strvInitStr(str_t str);
strview_t strvInitLen(const char *buf, size_t size);
char strvFront(strview_t ctx);
char strvBack(strview_t ctx);
const char *strvBegin(strview_t *ctx);
const char *strvEnd(strview_t *ctx);
// move view forward by n characters
void strvRemovePrefix(strview_t *ctx, size_t n);
// move view backwards by n characters
void strvRemoveSuffix(strview_t *ctx, size_t n);
bool strvIsEmpty(strview_t ctx);
str_t strvCopy(strview_t ctx);
str_t strvCopyN(strview_t ctx, size_t count, size_t from);
size_t strvCopyBuf(strview_t ctx, char *buf, size_t len, size_t from);
strview_t strvSubstr(strview_t ctx, size_t from, size_t len);
int strvCompare(strview_t ctx, strview_t other);
int strvICompare(strview_t ctx, strview_t other);
bool strvStartsWith(strview_t ctx, char c);
bool strvStartsWithView(strview_t ctx, strview_t view);
bool strvEndsWith(strview_t ctx, char c);
bool strvEndsWithView(strview_t ctx, strview_t view);
bool strvContains(strview_t ctx, char c);
bool strvContainsView(strview_t ctx, strview_t view);
size_t strvFind(strview_t ctx, char c, size_t from);
size_t strvFindView(strview_t ctx, strview_t view, size_t from);
size_t strvRFind(strview_t ctx, char c, size_t from);
size_t strvRFindView(strview_t ctx, strview_t view, size_t from);
// Finds the first occurrence of any of the characters of 'view' in this view
size_t strvFindFirstOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindFirstNot(strview_t ctx, char c, size_t from);
size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindLastNot(strview_t ctx, char c, size_t from);
size_t strvFindLastNotOf(strview_t ctx, strview_t view, size_t from);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -1,104 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "str.h"
/* == INPUT STREAM ============================================ */
typedef struct {
const char *start;
const char *cur;
size_t size;
} str_istream_t;
// initialize with null-terminated string
str_istream_t istrInit(const char *str);
str_istream_t istrInitLen(const char *str, size_t len);
// get the current character and advance
char istrGet(str_istream_t *ctx);
// get the current character but don't advance
char istrPeek(str_istream_t *ctx);
// ignore characters until the delimiter
void istrIgnore(str_istream_t *ctx, char delim);
// skip n characters
void istrSkip(str_istream_t *ctx, size_t n);
// read len bytes into buffer, the buffer will not be null terminated
void istrRead(str_istream_t *ctx, char *buf, size_t len);
// read a maximum of len bytes into buffer, the buffer will not be null terminated
// returns the number of bytes read
size_t istrReadMax(str_istream_t *ctx, char *buf, size_t len);
// return to the beginning of the stream
void istrRewind(str_istream_t *ctx);
// return the number of bytes read from beginning of stream
size_t istrTell(str_istream_t *ctx);
// return true if the stream doesn't have any new bytes to read
bool istrIsFinished(str_istream_t *ctx);
bool istrGetbool(str_istream_t *ctx, bool *val);
bool istrGetu8(str_istream_t *ctx, uint8_t *val);
bool istrGetu16(str_istream_t *ctx, uint16_t *val);
bool istrGetu32(str_istream_t *ctx, uint32_t *val);
bool istrGetu64(str_istream_t *ctx, uint64_t *val);
bool istrGeti8(str_istream_t *ctx, int8_t *val);
bool istrGeti16(str_istream_t *ctx, int16_t *val);
bool istrGeti32(str_istream_t *ctx, int32_t *val);
bool istrGeti64(str_istream_t *ctx, int64_t *val);
bool istrGetfloat(str_istream_t *ctx, float *val);
bool istrGetdouble(str_istream_t *ctx, double *val);
// get a string until a delimiter, the string is allocated by the function and should be freed
size_t istrGetstring(str_istream_t *ctx, char **val, char delim);
// get a string of maximum size len, the string is not allocated by the function and will be null terminated
size_t istrGetstringBuf(str_istream_t *ctx, char *val, size_t len);
strview_t istrGetview(str_istream_t *ctx, char delim);
strview_t istrGetviewLen(str_istream_t *ctx, size_t off, size_t len);
/* == OUTPUT STREAM =========================================== */
typedef struct {
char *buf;
size_t size;
size_t allocated;
} str_ostream_t;
str_ostream_t ostrInit(void);
str_ostream_t ostrInitLen(size_t initial_alloc);
str_ostream_t ostrInitStr(const char *buf, size_t len);
void ostrFree(str_ostream_t *ctx);
void ostrClear(str_ostream_t *ctx);
str_t ostrMove(str_ostream_t *ctx);
char ostrBack(str_ostream_t *ctx);
str_t ostrAsStr(str_ostream_t *ctx);
strview_t ostrAsView(str_ostream_t *ctx);
void ostrReplace(str_ostream_t *ctx, char from, char to);
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...);
void ostrPutc(str_ostream_t *ctx, char c);
void ostrAppendbool(str_ostream_t *ctx, bool val);
void ostrAppendchar(str_ostream_t *ctx, char val);
void ostrAppendu8(str_ostream_t *ctx, uint8_t val);
void ostrAppendu16(str_ostream_t *ctx, uint16_t val);
void ostrAppendu32(str_ostream_t *ctx, uint32_t val);
void ostrAppendu64(str_ostream_t *ctx, uint64_t val);
void ostrAppendi8(str_ostream_t *ctx, int8_t val);
void ostrAppendi16(str_ostream_t *ctx, int16_t val);
void ostrAppendi32(str_ostream_t *ctx, int32_t val);
void ostrAppendi64(str_ostream_t *ctx, int64_t val);
void ostrAppendfloat(str_ostream_t *ctx, float val);
void ostrAppenddouble(str_ostream_t *ctx, double val);
void ostrAppendview(str_ostream_t *ctx, strview_t view);
#ifdef __cplusplus
} // extern "C"
#endif

603
vec.h
View file

@ -1,603 +0,0 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
/*
* Basic usage:
* #define T int
* // optional, if not defined all the names will be longer, e.g.
* // vec_int_t v = vec_intInit();
* #define VEC_SHORT_NAME veci
* #include <vec.h> // undefines T
* [...]
* veci_t v = veciInit();
* veciPush(&v, 10);
* veciEraseAt(&v, 0);
* veciFree(&v);
*/
typedef int( *vec_cmp_fn_t)(const void *, const void *);
#ifndef T
#error "Must define T before including vec.h"
#endif
#define VEC_CAT(a, b) a ## b
#define VEC_PASTE(a, b) VEC_CAT(a, b)
#define TYPE(prefix, type) VEC_PASTE(VEC_PASTE(prefix, _), VEC_PASTE(type, _t))
#define ITER(prefix, type) VEC_PASTE(VEC_PASTE(prefix, _), VEC_PASTE(type, _it_t))
#ifdef VEC_SHORT_NAME
#define VEC_T VEC_PASTE(VEC_SHORT_NAME, _t)
#define VEC_IT_T VEC_PASTE(VEC_SHORT_NAME, _it_t)
#define VEC VEC_SHORT_NAME
#else
#define VEC_T TYPE(vec, T)
#define VEC_IT_T ITER(vec, T)
#define VEC VEC_PASTE(vec, VEC_PASTE(_, T))
#endif
#define FUN(postfix) VEC_PASTE(VEC, postfix)
#ifndef VEC_NO_DECLARATION
typedef struct {
T *buf;
size_t size;
size_t allocated;
} VEC_T;
#define vec_foreach(type, name, it, vec) \
for(type *it = VEC_PASTE(name, Beg)(&vec); it < VEC_PASTE(name, End)(&vec); ++it)
VEC_T FUN(Init)(void);
VEC_T FUN(InitArr)(T *arr, size_t len);
void FUN(Free)(VEC_T *ctx);
VEC_T FUN(Move)(VEC_T *ctx);
VEC_T FUN(Copy)(VEC_T *ctx);
T *FUN(Beg)(VEC_T *ctx);
T *FUN(End)(VEC_T *ctx);
T *FUN(Back)(VEC_T *ctx);
bool FUN(Empty)(VEC_T *ctx);
void FUN(Realloc)(VEC_T *ctx, size_t needed);
void FUN(Reserve)(VEC_T *ctx, size_t newsize);
void FUN(ShrinkToFit)(VEC_T *ctx);
void FUN(Clear)(VEC_T *ctx);
void FUN(ClearZero)(VEC_T *ctx);
void FUN(InsertAt)(VEC_T *ctx, size_t index, T val);
void FUN(InsertAfter)(VEC_T *ctx, T *it, T val);
void FUN(InsertBefore)(VEC_T *ctx, T *it, T val);
void FUN(InsertAtSw)(VEC_T *ctx, size_t index, T val);
void FUN(InsertAfterSw)(VEC_T *ctx, T *it, T val);
void FUN(InsertBeforeSw)(VEC_T *ctx, T *it, T val);
// swaps with last
void FUN(Erase)(VEC_T *ctx, T *it);
void FUN(EraseAt)(VEC_T *ctx, size_t index);
// moves whole array back one
void FUN(EraseMv)(VEC_T *ctx, T *it);
void FUN(EraseAtMv)(VEC_T *ctx, size_t index);
void FUN(Push)(VEC_T *ctx, T val);
void FUN(PushRef)(VEC_T *ctx, T *val);
T FUN(Pop)(VEC_T *ctx);
void FUN(Resize)(VEC_T *ctx, size_t newcount, T val);
void FUN(ResizeRef)(VEC_T *ctx, size_t newcount, T *val);
void FUN(EraseWhen)(VEC_T *ctx, T val);
typedef bool (*TYPE(vec_erase_fn, T))(const T *val, void *udata);
void FUN(EraseIf)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata);
void FUN(EraseIfMv)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata);
// typedef int (*TYPE(vec_sort_fn, T))(const T *a, const T *b);
void FUN(Sort)(VEC_T *ctx, vec_cmp_fn_t cmp);
#endif // VEC_NO_DECLARATION
#ifndef VEC_NO_IMPLEMENTATION
inline
VEC_T FUN(Init)(void) {
return (VEC_T){
.buf = NULL,
.size = 0,
.allocated = 0
};
}
inline
VEC_T FUN(InitArr)(T *arr, size_t len) {
VEC_T v = FUN(Init)();
FUN(Realloc)(&v, len);
memcpy(v.buf, arr, len * sizeof(T));
v.size = len;
return v;
}
inline
void FUN(Free)(VEC_T *ctx) {
free(ctx->buf);
ctx->buf = NULL;
ctx->size = 0;
ctx->allocated = 0;
}
inline
VEC_T FUN(Move)(VEC_T *ctx) {
VEC_T mv = *ctx;
ctx->buf = NULL;
FUN(Free)(ctx);
return mv;
}
inline
VEC_T FUN(Copy)(VEC_T *ctx) {
VEC_T cp = FUN(Init)();
if(ctx->buf) {
FUN(Reserve)(&cp, ctx->size);
memcpy(cp.buf, ctx->buf, ctx->size * sizeof(T));
cp.size = ctx->size;
}
return cp;
}
inline
T *FUN(Beg)(VEC_T *ctx) {
return ctx->buf;
}
inline
T *FUN(End)(VEC_T *ctx) {
return ctx->buf + ctx->size;
}
inline
T *FUN(Back)(VEC_T *ctx) {
return ctx->buf ? &ctx->buf[ctx->size - 1] : NULL;
}
inline
bool FUN(Empty)(VEC_T *ctx) {
return ctx->buf ? ctx->size == 0 : true;
}
inline
void FUN(Realloc)(VEC_T *ctx, size_t needed) {
if((ctx->size + needed) >= ctx->allocated) {
ctx->allocated = (ctx->allocated * 2) + needed;
ctx->buf = (T *)realloc(ctx->buf, ctx->allocated * sizeof(T));
}
}
inline
void FUN(Reserve)(VEC_T *ctx, size_t newsize) {
if(ctx->allocated < newsize) {
ctx->allocated = newsize;
ctx->buf = (T *)realloc(ctx->buf, ctx->allocated * sizeof(T));
}
}
inline
void FUN(ShrinkToFit)(VEC_T *ctx) {
ctx->allocated = ctx->size;
ctx->buf = (T *)realloc(ctx->buf, ctx->allocated * sizeof(T));
}
inline
void FUN(Clear)(VEC_T *ctx) {
ctx->size = 0;
}
inline
void FUN(ClearZero)(VEC_T *ctx) {
ctx->size = 0;
memset(ctx->buf, 0, ctx->allocated * sizeof(T));
}
inline
void FUN(InsertAt)(VEC_T *ctx, size_t index, T val) {
FUN(Realloc)(ctx, 1);
for(size_t i = ctx->size; i > index; --i) {
ctx->buf[i] = ctx->buf[i - 1];
}
ctx->buf[index] = val;
ctx->size++;
}
inline
void FUN(InsertAfter)(VEC_T *ctx, T *it, T val) {
size_t index = it - ctx->buf;
// insertAt acts as insertBefore, so we just add 1
FUN(InsertAt)(ctx, index + 1, val);
}
inline
void FUN(InsertBefore)(VEC_T *ctx, T *it, T val) {
size_t index = it - ctx->buf;
FUN(InsertAt)(ctx, index, val);
}
inline
void FUN(InsertAtSw)(VEC_T *ctx, size_t index, T val) {
FUN(Realloc)(ctx, 1);
ctx->buf[ctx->size] = ctx->buf[index];
ctx->buf[index] = val;
ctx->size++;
}
inline
void FUN(InsertAfterSw)(VEC_T *ctx, T *it, T val) {
size_t index = it - ctx->buf;
// insertAt acts as insertBefore, so we just add 1
FUN(InsertAtSw)(ctx, index + 1, val);
}
inline
void FUN(InsertBeforeSw)(VEC_T *ctx, T *it, T val) {
size_t index = it - ctx->buf;
FUN(InsertAtSw)(ctx, index, val);
}
inline
void FUN(Erase)(VEC_T *ctx, T *it) {
size_t index = it - ctx->buf;
FUN(EraseAt)(ctx, index);
}
inline
void FUN(EraseAt)(VEC_T *ctx, size_t index) {
ctx->size--;
ctx->buf[index] = ctx->buf[ctx->size];
}
inline
void FUN(EraseMv)(VEC_T *ctx, T *it) {
size_t index = it - ctx->buf;
FUN(EraseAtMv)(ctx, index);
}
inline
void FUN(EraseAtMv)(VEC_T *ctx, size_t index) {
ctx->size--;
for(size_t i = index; i < ctx->size; ++i) {
ctx->buf[i] = ctx->buf[i + 1];
}
}
inline
void FUN(Push)(VEC_T *ctx, T val) {
FUN(Realloc)(ctx, 1);
ctx->buf[ctx->size] = val;
ctx->size++;
}
inline
void FUN(PushRef)(VEC_T *ctx, T *val) {
FUN(Realloc)(ctx, 1);
ctx->buf[ctx->size] = *val;
ctx->size++;
}
inline
T FUN(Pop)(VEC_T *ctx) {
ctx->size--;
return ctx->buf[ctx->size];
}
inline
void FUN(Resize)(VEC_T *ctx, size_t newcount, T val) {
if(newcount <= ctx->size) {
ctx->size = newcount;
return;
}
FUN(Realloc)(ctx, newcount - ctx->size);
for(size_t i = ctx->size; i < newcount; ++i) {
ctx->buf[i] = val;
}
ctx->size = newcount;
}
inline
void FUN(ResizeRef)(VEC_T *ctx, size_t newcount, T *val) {
if(newcount <= ctx->size) {
ctx->size = newcount;
}
FUN(Realloc)(ctx, newcount - ctx->size);
if(val) {
for(size_t i = ctx->size; i < newcount; ++i) {
ctx->buf[i] = *val;
}
}
ctx->size = newcount;
}
#ifndef VEC_DISABLE_ERASE_WHEN
inline
void FUN(EraseWhen)(VEC_T *ctx, T val) {
for(size_t i = 0; i < ctx->size; ++i) {
if(ctx->buf[i] == val) {
FUN(EraseAt)(ctx, i);
--i;
}
}
}
#endif
inline
void FUN(EraseIf)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata) {
for(size_t i = 0; i < ctx->size; ++i) {
if(cmp(&ctx->buf[i], udata)) {
FUN(EraseAt)(ctx, i);
--i;
}
}
}
inline
void FUN(EraseIfMv)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata) {
for(size_t i = 0; i < ctx->size; ++i) {
if(cmp(&ctx->buf[i], udata)) {
FUN(EraseAtMv)(ctx, i);
--i;
}
}
}
inline
void FUN(Sort)(VEC_T *ctx, vec_cmp_fn_t cmp) {
qsort(ctx->buf, ctx->size, sizeof(T), cmp);
}
#endif // VEC_NO_IMPLEMENTATION
#undef FUN
#undef VEC
#undef VEC_T
#undef TYPE
#undef VEC_SHORT_NAME
#undef T
#undef VEC_DISABLE_ERASE_WHEN
#undef VEC_NO_DECLARATION
#undef VEC_NO_IMPLEMENTATION
#if 0
// vec.h testing:
#define T int
#define VEC_SHORT_NAME veci
#include <vec.h>
#define foreach(it, vec) vec_foreach(int, veci, it, vec)
#define T char
#define VEC_SHORT_NAME vecc
#include <vec.h>
#include <tracelog.h>
#define PRINTVALS(v, s) \
printf("{ "); \
for(size_t i = 0; i < v.size; ++i) \
printf(s " ", v.buf[i]); \
printf("}\n");
#define PRINTVEC(v, s) \
printf(#v ": {\n"); \
printf("\tsize: %zu\n", v.size); \
printf("\tallocated: %zu\n", v.allocated); \
printf("\tvalues:"); \
PRINTVALS(v, s); \
printf("}\n"); \
#define PRINTVECI(v) PRINTVEC(v, "%d")
#define PRINTVALSI(v) PRINTVALS(v, "%d")
bool veciEraseEven(const int *val, void *udata) {
return *val % 2 == 0;
}
bool veciEraseHigher(const int *val, void *udata) {
return *val > 8;
}
int main() {
debug("== TESTING INIT ===========================");
{
veci_t v = veciInit();
PRINTVECI(v);
veciFree(&v);
v = veciInitArr((int[]){1, 2, 3, 4}, 4);
veciPush(&v, 25);
veciPush(&v, 13);
PRINTVECI(v);
veciFree(&v);
}
debug("== TESTING MOVE/COPY ======================");
{
veci_t a = veciInitArr((int[]){1, 2, 3, 4}, 4);
info("before move");
PRINTVECI(a);
info("after move");
veci_t b = veciMove(&a);
PRINTVECI(a);
PRINTVECI(b);
veciFree(&a);
veciFree(&b);
a = veciInitArr((int[]){1, 2, 3, 4}, 4);
b = veciCopy(&a);
info("copied");
PRINTVECI(a);
PRINTVECI(b);
info("modified b");
b.buf[2] = 9;
PRINTVECI(a);
PRINTVECI(b);
veciFree(&a);
veciFree(&b);
}
debug("== TESTING BACK ===========================");
{
vecc_t v = veccInitArr((char[]){'a', 'b', 'c', 'd', 'e', 'f'}, 6);
PRINTVEC(v, "%c");
info("The last character is '%c'.", *veccBack(&v));
veccFree(&v);
}
debug("== TESTING EMPTY ==========================");
{
veci_t v = veciInit();
info("Initially, vecEmpty(): %s", veciEmpty(&v) ? "true":"false");
veciPush(&v, 42);
info("After adding elements, vecEmpty(): %s", veciEmpty(&v) ? "true":"false");
veciFree(&v);
}
debug("== TESTING RESERVE/SHRINK_TO_FIT/CLEAR ====");
{
veci_t v = veciInit();
info("Default capacity: %zu", v.allocated);
veciResize(&v, 100, 0);
info("100 elements: %zu", v.allocated);
veciResize(&v, 50, 0);
info("after resize(50): %zu", v.allocated);
veciShrinkToFit(&v);
info("after shrinkToFit(): %zu", v.allocated);
veciClear(&v);
info("after clear(): %zu", v.allocated);
veciShrinkToFit(&v);
info("after shrinkToFit(): %zu", v.allocated);
for(int i = 1000; i < 1300; ++i) {
veciPush(&v, i);
}
info("after adding 300 elements: %zu", v.allocated);
veciShrinkToFit(&v);
info("after shrinkToFit(): %zu", v.allocated);
veciFree(&v);
}
debug("== TESTING ITERATORS ======================");
{
veci_t v = veciInitArr((int[]){1, 2, 3, 4, 5}, 5);
PRINTVECI(v);
info("foreach:");
for(int *it = veciBeg(&v); it != veciEnd(&v); ++it) {
printf("\t*it: %d\n", *it);
}
veciFree(&v);
}
debug("== TESTING INSERT =========================");
{
veci_t v = veciInit();
info("init with 3 100");
veciResize(&v, 3, 100);
PRINTVALSI(v);
info("insert 200 at 0");
veciInsertAt(&v, 0, 200);
PRINTVALSI(v);
info("insert 300 before beginning");
veciInsertBefore(&v, veciBeg(&v), 300);
PRINTVALSI(v);
info("insert 400 after beg + 1");
veciInsertAfter(&v, veciBeg(&v) + 1, 400);
PRINTVALSI(v);
info("insert swap 500 at 3");
veciInsertAtSw(&v, 3, 500);
PRINTVALSI(v);
info("insert swap 600 before beg");
veciInsertBeforeSw(&v, veciBeg(&v), 600);
PRINTVALSI(v);
info("insert swap 700 after end - 4");
veciInsertAfterSw(&v, veciEnd(&v) - 4, 700);
PRINTVALSI(v);
veciFree(&v);
}
debug("== TESTING ERASE ==========================");
{
veci_t v = veciInitArr((int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 10);
info("initializing with number from 0 to 9");
PRINTVALSI(v);
info("erasing beginning");
veciErase(&v, veciBeg(&v));
PRINTVALSI(v);
info("erasing index 5");
veciEraseAt(&v, 5);
PRINTVALSI(v);
info("erasing mv end - 3");
veciEraseMv(&v, veciEnd(&v) - 3);
PRINTVALSI(v);
info("erasing mv index 1");
veciEraseAtMv(&v, 1);
PRINTVALSI(v);
info("erasing mv all even numbers");
veciEraseIfMv(&v, veciEraseEven, NULL);
PRINTVALSI(v);
info("erasing numbers higher than 8");
veciEraseIf(&v, veciEraseHigher, NULL);
PRINTVALSI(v);
veciFree(&v);
}
debug("== TESTING CLEAR_ZERO =====================");
{
veci_t v = veciInitArr((int[]){0, 1, 2, 3, 4, 5}, 6);
info("initialized from 0 to 6");
PRINTVECI(v);
info("clearZero");
size_t oldsize = v.size;
veciClearZero(&v);
for(int i = 0; i < oldsize; ++i) {
printf("\t%d > %d\n", i, v.buf[i]);
}
}
debug("== TESTING PUSH/PUSH_REF ==================");
{
veci_t v = veciInit();
info("pushing 10");
veciPush(&v, 10);
int value = 50;
info("pushing reference to value: %d", value);
veciPushRef(&v, &value);
info("vector holds: ");
printf("> ");
foreach(it, v) {
printf("%d ", *it);
}
printf("\n");
veciFree(&v);
}
debug("== TESTING POP ============================");
{
vecc_t v = veccInitArr("hello world!", 12);
info("initialized with %.*s", (int)v.size, v.buf);
while(!veccEmpty(&v)) {
printf("pooped '%c'\n", veccPop(&v));
printf("> [%.*s]\n", (int)v.size, v.buf);
}
veccFree(&v);
}
}
#endif
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -1,57 +0,0 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef WIN32_EXTRA_LEAN
#define WIN32_EXTRA_LEAN
#endif
#define NOIME
#define NOWINRES
#define NOGDICAPMASKS
#define NOVIRTUALKEYCODES
#define NOWINMESSAGES
#define NOWINSTYLES
#define NOSYSMETRICS
#define NOMENUS
#define NOICONS
#define NOKEYSTATES
#define NOSYSCOMMANDS
#define NORASTEROPS
#define NOSHOWWINDOW
#define OEMRESOURCE
#define NOATOM
#define NOCLIPBOARD
#define NOCOLOR
#define NOCTLMGR
#define NODRAWTEXT
#define NOGDI
#define NOUSER
#define NOMB
#define NOMEMMGR
#define NOMETAFILE
#define NOMINMAX
#define NOMSG
#define NOOPENFILE
#define NOSCROLL
#define NOSERVICE
#define NOSOUND
#define NOTEXTMETRIC
#define NOWH
#define NOWINOFFSETS
#define NOCOMM
#define NOKANJI
#define NOHELP
#define NOPROFILER
#define NODEFERWINDOWPOS
#define NOMCX
#define NOIME
#define NOPROXYSTUB
#define NOIMAGE
#define NO
#define NOTAPE
#define ANSI_ONLY
#include <windows.h>