a lot of stuff tbh
This commit is contained in:
parent
d80773bb00
commit
8ddd9186b8
42 changed files with 6463 additions and 1593 deletions
|
|
@ -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)
|
||||||
|
|
@ -21,3 +24,5 @@ else()
|
||||||
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)
|
||||||
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
@ -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;
|
||||||
237
file.c
237
file.c
|
|
@ -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
54
file.h
|
|
@ -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
106
gen.lua
Normal 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
21
src/collatypes.h
Normal 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;
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
// == THREAD ===========================================
|
// == THREAD ===========================================
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dirCreate(const char *path) {
|
||||||
|
mkdir(path, 0700);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -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
292
src/file.c
Normal 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
59
src/file.h
Normal 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
130
src/hashmap.c
Normal 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
49
src/hashmap.h
Normal 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);
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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,19 +109,28 @@ 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"
|
||||||
323
src/ini.c
Normal file
323
src/ini.c
Normal 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
86
src/ini.h
Normal 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
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
int stricmp(const char *a, const char *b) {
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (a == b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((result = tolower(*a) - tolower(*b++)) == 0) {
|
||||||
|
if (*a++ == '\0') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
str_t getUserName() {
|
str_t getUserName() {
|
||||||
char buf[255];
|
return strFromStr(getlogin());
|
||||||
int res = getlogin_r(buf, sizeof(buf));
|
|
||||||
if(res) {
|
|
||||||
return strInit();
|
|
||||||
}
|
|
||||||
return strInitStr(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -7,21 +7,20 @@ 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();
|
||||||
|
|
@ -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) {
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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);
|
||||||
122
src/str.h
Normal file
122
src/str.h
Normal 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
|
||||||
|
|
@ -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
110
src/strstream.h
Normal 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
|
||||||
172
src/utf8.c
Normal file
172
src/utf8.c
Normal 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
19
src/utf8.h
Normal 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
69
src/vec.h
Normal 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
15
src/win32_slim.h
Normal 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
128
str.h
|
|
@ -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
|
|
||||||
104
strstream.h
104
strstream.h
|
|
@ -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
603
vec.h
|
|
@ -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
|
|
||||||
57
win32_slim.h
57
win32_slim.h
|
|
@ -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>
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue