dir -> directory walker similar to dirent
    dirwatch -> lets you watch a directory for changes in another thread
    vec -> generic vector
This commit is contained in:
snarmph 2021-10-25 01:32:31 +01:00
parent aa08240ec9
commit 59b55c7f6c
26 changed files with 4037 additions and 2726 deletions

View file

@ -3,16 +3,20 @@ add_library(Colla STATIC
tracelog.c
http.c
strstream.c
strview.c
str.c
coroutine.c
os.c
fs.c
file.c
dir.c
dirwatch.c
)
IF (WIN32)
if(MSVC)
target_link_libraries(Colla ws2_32.lib)
ELSE()
# posix
ENDIF()
target_compile_options(Colla PRIVATE /W4)
else()
target_link_libraries(Colla pthread)
target_compile_options(Colla PRIVATE -Wall -Wextra -Wpedantic)
target_compile_definitions(Colla PUBLIC _DEFAULT_SOURCE)
endif()

147
dir.c Normal file
View file

@ -0,0 +1,147 @@
#include "dir.h"
#include "tracelog.h"
#ifdef _WIN32
#define VC_EXTRALEAN
#include <windows.h>
#include <stdlib.h>
#include <assert.h>
#include "strstream.h"
typedef struct {
dir_entry_t cur;
dir_entry_t next;
HANDLE handle;
} _dir_internal_t;
static dir_entry_t _fillDirEntry(WIN32_FIND_DATAW *data) {
return (dir_entry_t) {
.type =
data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ?
FS_TYPE_DIR : FS_TYPE_FILE,
.name = strFromWCHAR(data->cFileName, 0)
};
}
static _dir_internal_t _getFirst(const wchar_t *path) {
_dir_internal_t res = {0};
WIN32_FIND_DATAW data = {0};
res.handle = FindFirstFileW(path, &data);
if(res.handle != INVALID_HANDLE_VALUE) {
res.next = _fillDirEntry(&data);
}
return res;
}
static void _getNext(_dir_internal_t *ctx) {
WIN32_FIND_DATAW data = {0};
BOOL result = FindNextFileW(ctx->handle, &data);
if(!result) {
if(GetLastError() == ERROR_NO_MORE_FILES) {
FindClose(ctx->handle);
ctx->handle = NULL;
return;
}
}
ctx->next = _fillDirEntry(&data);
}
dir_t dirOpen(const char *path) {
DWORD n = GetFullPathName(path, 0, NULL, NULL);
str_ostream_t out = ostrInitLen(n + 3);
n = GetFullPathName(path, n, out.buf, NULL);
assert(n > 0);
out.size += n;
switch(ostrBack(&out)) {
case '\\':
case '/':
case ':':
// directory ends in path separator
// NOP
break;
default:
ostrPutc(&out, '\\');
}
ostrPutc(&out, '*');
_dir_internal_t *dir = malloc(sizeof(_dir_internal_t));
if(dir) {
wchar_t *wpath = strToWCHAR(ostrAsStr(&out));
assert(wpath);
*dir = _getFirst(wpath);
free(wpath);
}
ostrFree(&out);
return dir;
}
dir_entry_t *dirNext(dir_t ctx) {
_dir_internal_t *dir = (_dir_internal_t*)ctx;
strFree(&dir->cur.name);
if(!dir->handle) return NULL;
dir->cur = dir->next;
_getNext(dir);
return &dir->cur;
}
void dirClose(dir_t ctx) {
free(ctx);
}
#else
#include <dirent.h>
#include <stdlib.h>
// taken from https://sites.uclouvain.be/SystInfo/usr/include/dirent.h.html
// hopefully shouldn't be needed
#ifndef DT_DIR
#define DT_DIR 4
#endif
#ifndef DT_REG
#define DT_REG 8
#endif
typedef struct {
DIR *dir;
dir_entry_t next;
} _dir_internal_t;
dir_t dirOpen(const char *path) {
_dir_internal_t *ctx = calloc(1, sizeof(_dir_internal_t));
if(ctx) ctx->dir = opendir(path);
return ctx;
}
void dirClose(dir_t ctx) {
if(ctx) {
_dir_internal_t *in = (_dir_internal_t *)ctx;
closedir(in->dir);
free(in);
}
}
dir_entry_t *dirNext(dir_t ctx) {
if(!ctx) return NULL;
_dir_internal_t *in = (_dir_internal_t *)ctx;
strFree(&in->next.name);
struct dirent *dp = readdir(in->dir);
if(!dp) return NULL;
switch(dp->d_type) {
case DT_DIR: in->next.type = FS_TYPE_DIR; break;
case DT_REG: in->next.type = FS_TYPE_FILE; break;
default: in->next.type = FS_TYPE_UNKNOWN; break;
}
in->next.name = strInitStr(dp->d_name);
return &in->next;
}
#endif

29
dir.h Normal file
View file

@ -0,0 +1,29 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "str.h"
typedef void *dir_t;
typedef struct {
int type;
str_t name;
} dir_entry_t;
enum {
FS_TYPE_UNKNOWN,
FS_TYPE_FILE,
FS_TYPE_DIR,
};
dir_t dirOpen(const char *path);
void dirClose(dir_t ctx);
dir_entry_t *dirNext(dir_t ctx);
#ifdef __cplusplus
} // extern "C"
#endif

300
dirwatch.c Normal file
View file

@ -0,0 +1,300 @@
#include "dirwatch.h"
#include <stdint.h>
#include <assert.h>
#include "tracelog.h"
#ifdef _WIN32
#define VC_EXTRALEAN
#include <windows.h>
#include "str.h"
typedef struct {
const char *path;
dirwatch_cb_t on_event;
BOOL should_continue;
HANDLE stop_event;
} __dirwatch_internal_t;
static DWORD watchDirThread(void *cdata) {
__dirwatch_internal_t *desc = (__dirwatch_internal_t*)cdata;
// stop_event is called from another thread when watchDirThread should exit
desc->stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE file = CreateFile(
desc->path,
FILE_LIST_DIRECTORY,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
NULL
);
assert(file != INVALID_HANDLE_VALUE);
OVERLAPPED overlapped = {0};
overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL);
uint8_t change_buf[1024];
BOOL success = ReadDirectoryChangesW(
file, change_buf, sizeof(change_buf), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &overlapped, NULL
);
assert(success);
HANDLE events[2] = {
overlapped.hEvent,
desc->stop_event
};
WCHAR old_name[MAX_PATH];
dirwatch_file_t data = {0};
// if the variable is 32bit aligned, a read/write is already atomice
// https://docs.microsoft.com/en-us/windows/win32/sync/interlocked-variable-access
while(desc->should_continue) {
DWORD result = WaitForMultipleObjects(2, events, FALSE, INFINITE);
if(result == WAIT_OBJECT_0) {
DWORD bytes_transferred;
GetOverlappedResult(file, &overlapped, &bytes_transferred, FALSE);
FILE_NOTIFY_INFORMATION *event = (FILE_NOTIFY_INFORMATION*)change_buf;
for(;;) {
DWORD name_len = event->FileNameLength / sizeof(wchar_t);
switch(event->Action) {
case FILE_ACTION_ADDED:
data.name = strFromWCHAR(event->FileName, name_len).buf;
data.oldname = NULL;
desc->on_event(desc->path, DIRWATCH_FILE_ADDED, data);
break;
case FILE_ACTION_REMOVED:
data.name = strFromWCHAR(event->FileName, name_len).buf;
data.oldname = NULL;
desc->on_event(desc->path, DIRWATCH_FILE_REMOVED, data);
break;
case FILE_ACTION_RENAMED_OLD_NAME:
memcpy(old_name, event->FileName, event->FileNameLength);
old_name[event->FileNameLength] = '\0';
break;
case FILE_ACTION_RENAMED_NEW_NAME:
data.name = strFromWCHAR(event->FileName, name_len).buf;
data.oldname = strFromWCHAR(old_name, 0).buf;
desc->on_event(desc->path, DIRWATCH_FILE_RENAMED, data);
break;
}
if(data.name) free(data.name);
if(data.oldname) free(data.oldname);
data = (dirwatch_file_t){0};
if(event->NextEntryOffset) {
*((uint8_t**)&event) += event->NextEntryOffset;
}
else {
break;
}
}
success = ReadDirectoryChangesW(
file, change_buf, sizeof(change_buf), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_LAST_WRITE,
NULL, &overlapped, NULL
);
assert(success);
}
// stop_event
else if(result == WAIT_OBJECT_0 + 1) {
warn("stopping");
break;
}
}
return 0;
}
dirwatch_t watchDir(dirwatch_desc_t desc) {
dirwatch_t dir = {0};
__dirwatch_internal_t *opts = HeapAlloc(
GetProcessHeap(),
0,
sizeof(__dirwatch_internal_t)
);
opts->path = desc.path;
opts->on_event = desc.on_event;
opts->should_continue = true;
dir.desc = (dirwatch_desc_t *)opts;
dir.handle = CreateThread(
NULL,
0,
watchDirThread,
(void *)dir.desc,
0,
NULL
);
if(dir.handle) {
info("watching %s", desc.path);
}
return dir;
}
void waitForWatchDir(dirwatch_t *ctx) {
if(!ctx->handle) return;
WaitForSingleObject((HANDLE)ctx->handle, INFINITE);
HeapFree(GetProcessHeap(), 0, ctx->desc);
}
void stopWatchDir(dirwatch_t *ctx, bool immediately) {
(void)immediately;
__dirwatch_internal_t *opts = (__dirwatch_internal_t *)ctx->desc;
opts->should_continue = false;
if(immediately) {
if(!SetEvent(opts->stop_event)) {
err("couldn't signal event stop_event: %d", GetLastError());
}
}
WaitForSingleObject((HANDLE)ctx->handle, INFINITE);
HeapFree(GetProcessHeap(), 0, ctx->desc);
}
#else
#include <sys/inotify.h>
#include <stdio.h>
#include <stdlib.h> // malloc
#include <unistd.h> // read
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <linux/limits.h> // MAX_PATH
#include <stdatomic.h>
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
#define ERROR(str) { err(str ": %s", strerror(errno)); goto error; }
typedef struct {
const char *path;
dirwatch_cb_t on_event;
atomic_bool should_continue;
int fd;
int wd;
} __dirwatch_internal_t;
static void *watchDirThread(void *cdata) {
__dirwatch_internal_t *desc = (__dirwatch_internal_t *)cdata;
info("watching %s", desc->path);
int length/*, fd, wd*/;
char buffer[BUF_LEN];
desc->fd = inotify_init();
if(desc->fd < 0) {
ERROR("inotify_init failed");
}
desc->wd = inotify_add_watch(desc->fd, desc->path, IN_MOVE | IN_CREATE | IN_DELETE);
char old_path[PATH_MAX] = {0};
dirwatch_file_t data = {0};
while(desc->should_continue) {
length = (int)read(desc->fd, buffer, BUF_LEN);
if(length < 0) {
ERROR("couldn't read");
}
for(int i = 0; i < length;) {
struct inotify_event *event = (struct inotify_event *) &buffer[i];
if(event->len) {
uint32_t e = event->mask;
// bool is_dir = e & IN_ISDIR;
if(e & IN_CREATE) {
data.name = event->name;
desc->on_event(desc->path, DIRWATCH_FILE_ADDED, data);
}
else if(e & IN_DELETE) {
data.name = event->name;
desc->on_event(desc->path, DIRWATCH_FILE_REMOVED, data);
}
else if(e & IN_MOVED_FROM) {
memcpy(old_path, event->name, event->len);
old_path[event->len] = '\0';
}
else if(e & IN_MOVED_TO) {
data.oldname = old_path;
data.name = event->name;
desc->on_event(desc->path, DIRWATCH_FILE_RENAMED, data);
}
}
i += EVENT_SIZE + event->len;
}
}
inotify_rm_watch(desc->fd, desc->wd);
close(desc->fd);
return (void*)0;
error:
return (void*)1;
}
dirwatch_t watchDir(dirwatch_desc_t desc) {
dirwatch_t dir = {0};
__dirwatch_internal_t *opts = malloc(sizeof(__dirwatch_internal_t));
opts->path = desc.path;
opts->on_event = desc.on_event;
opts->fd = 0;
opts->wd = 0;
opts->should_continue = true;
dir.desc = (dirwatch_desc_t *)opts;
pthread_t thread;
pthread_create(&thread, NULL, watchDirThread, opts);
dir.handle = (void *)thread;
return dir;
}
void waitForWatchDir(dirwatch_t *ctx) {
pthread_join((pthread_t)ctx->handle, NULL);
free(ctx->desc);
}
void stopWatchDir(dirwatch_t *ctx, bool immediately) {
__dirwatch_internal_t *opts = (__dirwatch_internal_t *)ctx->desc;
opts->should_continue = false;
// this one might get called in the thread first, but it doesn't matter
if(immediately) {
inotify_rm_watch(opts->fd, opts->wd);
close(opts->fd);
}
pthread_join((pthread_t)ctx->handle, NULL);
free(opts);
}
#endif

62
dirwatch.h Normal file
View file

@ -0,0 +1,62 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/*
* Basic usage:
*
* // this function will be called from another thread every time
* // something happens to 'path'
* void onEvent(const char *path, int action, dirwatch_file_t file) {
* switch(action) {
* case DIRWATCH_FILE_ADDED: [do something] break;
* case DIRWATCH_FILE_REMOVED: [do something] break;
* case DIRWATCH_FILE_RENAMED: [do something] break;
* }
* }
*
* int main() {
* dirwatch_t dir = watchDir((dirwatch_desc_t){
* .dirname = "watch/",
* .on_event = onEvent
* });
*
* waitForWatchDir(&dir);
* }
*/
#include <stdbool.h>
enum {
DIRWATCH_FILE_ADDED,
DIRWATCH_FILE_REMOVED,
DIRWATCH_FILE_RENAMED,
};
typedef struct {
char *name;
char *oldname;
} dirwatch_file_t;
typedef void (*dirwatch_cb_t)(const char *path, int action, dirwatch_file_t data);
typedef struct {
const char *path;
dirwatch_cb_t on_event;
} dirwatch_desc_t;
typedef struct {
void *handle;
dirwatch_desc_t *desc;
} dirwatch_t;
// Creates a thread and starts watching the folder specified by desc
// if any action happend on_event will be called from this thread
dirwatch_t watchDir(dirwatch_desc_t desc);
// waits forever
void waitForWatchDir(dirwatch_t *ctx);
// stops dirwatch thread, if immediately is true, it will try to close it right away
// otherwise it might wait for one last event
void stopWatchDir(dirwatch_t *ctx, bool immediately);

9
file.h
View file

@ -1,8 +1,11 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "str.h"
#include "strview.h"
enum {
FILE_READ, FILE_WRITE, FILE_BOTH
@ -29,3 +32,7 @@ bool fileSeekEnd(file_t *ctx);
void fileRewind(file_t *ctx);
uint64_t fileTell(file_t *ctx);
#ifdef __cplusplus
} // extern "C"
#endif

18
fs.c
View file

@ -25,11 +25,12 @@ static int _modeToType(unsigned int mode) {
fs_stat_t fsStat(file_t fp) {
TCHAR path[MAX_PATH];
DWORD pathlen = GetFinalPathNameByHandle(
GetFinalPathNameByHandle(
(HANDLE)fp.handle,
path,
MAX_PATH,
0);
0
);
struct stat statbuf;
int res = stat(path, &statbuf);
@ -69,6 +70,13 @@ fs_time_t fsAsTime(int64_t timer) {
}
}
bool fsIsDir(const char *path) {
DWORD attr = GetFileAttributes(path);
return attr != INVALID_FILE_ATTRIBUTES &&
attr & FILE_ATTRIBUTE_DIRECTORY;
}
#else
#include <sys/stat.h>
@ -118,4 +126,10 @@ fs_time_t fsAsTime(int64_t timer) {
return (fs_time_t) { 0 };
}
}
bool fsIsDir(const char *path) {
struct stat statbuf;
return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode);
}
#endif

10
fs.h
View file

@ -1,5 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
@ -32,3 +36,9 @@ typedef struct {
fs_stat_t fsStat(file_t fp);
fs_time_t fsAsTime(int64_t time);
bool fsIsDir(const char *path);
#ifdef __cplusplus
} // extern "C"
#endif

96
http.c
View file

@ -7,17 +7,20 @@
#include "os.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"
// == INTERNAL ================================================================
static void _setField(http_field_t **fields_ptr, int *fields_count_ptr, const char *key, const char *value) {
http_field_t *fields = *fields_ptr;
int fields_count = *fields_count_ptr;
static void _setField(field_vec_t *fields, const char *key, const char *value) {
// search if the field already exists
for(int i = 0; i < fields_count; ++i) {
if(stricmp(fields[i].key, key) == 0) {
for(size_t i = 0; i < fields->size; ++i) {
if(stricmp(fields->buf[i].key, key) == 0) {
// replace value
char **curval = &fields[i].value;
char **curval = &fields->buf[i].value;
size_t cur = strlen(*curval);
size_t new = strlen(value);
if(new > cur) {
@ -29,17 +32,16 @@ static void _setField(http_field_t **fields_ptr, int *fields_count_ptr, const ch
}
}
// otherwise, add it to the list
(*fields_count_ptr)++;;
(*fields_ptr) = realloc(fields, sizeof(http_field_t) * (*fields_count_ptr));
http_field_t *field = &(*fields_ptr)[(*fields_count_ptr) - 1];
http_field_t field;
size_t klen = strlen(key);
size_t vlen = strlen(value);
field->key = malloc(klen + 1);
field->value = malloc(vlen + 1);
memcpy(field->key, key, klen);
memcpy(field->value, value, vlen);
field->key[klen] = field->value[vlen] = '\0';
field.key = malloc(klen + 1);
field.value = malloc(vlen + 1);
memcpy(field.key, key, klen);
memcpy(field.value, value, vlen);
field.key[klen] = field.value[vlen] = '\0';
field_vecPush(fields, field);
}
// == HTTP VERSION ============================================================
@ -59,19 +61,19 @@ http_request_t reqInit() {
}
void reqFree(http_request_t *ctx) {
for(int i = 0; i < ctx->field_count; ++i) {
free(ctx->fields[i].key);
free(ctx->fields[i].value);
for(size_t i = 0; i < ctx->fields.size; ++i) {
free(ctx->fields.buf[i].key);
free(ctx->fields.buf[i].value);
}
free(ctx->fields);
field_vecFree(&ctx->fields);
free(ctx->uri);
free(ctx->body);
memset(ctx, 0, sizeof(http_request_t));
}
bool reqHasField(http_request_t *ctx, const char *key) {
for(int i = 0; i < ctx->field_count; ++i) {
if(stricmp(ctx->fields[i].key, key) == 0) {
for(size_t i = 0; i < ctx->fields.size; ++i) {
if(stricmp(ctx->fields.buf[i].key, key) == 0) {
return true;
}
}
@ -79,7 +81,7 @@ bool reqHasField(http_request_t *ctx, const char *key) {
}
void reqSetField(http_request_t *ctx, const char *key, const char *value) {
_setField(&ctx->fields, &ctx->field_count, key, value);
_setField(&ctx->fields, key, value);
}
void reqSetUri(http_request_t *ctx, const char *uri) {
@ -116,8 +118,8 @@ str_ostream_t reqPrepare(http_request_t *ctx) {
method, ctx->uri, ctx->version.major, ctx->version.minor
);
for(int i = 0; i < ctx->field_count; ++i) {
ostrPrintf(&out, "%s: %s\r\n", ctx->fields[i].key, ctx->fields[i].value);
for(size_t i = 0; i < ctx->fields.size; ++i) {
ostrPrintf(&out, "%s: %s\r\n", ctx->fields.buf[i].key, ctx->fields.buf[i].value);
}
ostrAppendview(&out, strvInit("\r\n"));
@ -129,9 +131,9 @@ error:
return out;
}
size_t reqString(http_request_t *ctx, char **str) {
str_t reqString(http_request_t *ctx) {
str_ostream_t out = reqPrepare(ctx);
return ostrMove(&out, str);
return ostrMove(&out);
}
// == HTTP RESPONSE ===========================================================
@ -143,18 +145,18 @@ http_response_t resInit() {
}
void resFree(http_response_t *ctx) {
for(int i = 0; i < ctx->field_count; ++i) {
free(ctx->fields[i].key);
free(ctx->fields[i].value);
for(size_t i = 0; i < ctx->fields.size; ++i) {
free(ctx->fields.buf[i].key);
free(ctx->fields.buf[i].value);
}
free(ctx->fields);
field_vecFree(&ctx->fields);
free(ctx->body);
memset(ctx, 0, sizeof(http_response_t));
}
bool resHasField(http_response_t *ctx, const char *key) {
for(int i = 0; i < ctx->field_count; ++i) {
if(stricmp(ctx->fields[i].key, key) == 0) {
for(size_t i = 0; i < ctx->fields.size; ++i) {
if(stricmp(ctx->fields.buf[i].key, key) == 0) {
return true;
}
}
@ -162,9 +164,9 @@ bool resHasField(http_response_t *ctx, const char *key) {
}
const char *resGetField(http_response_t *ctx, const char *field) {
for(int i = 0; i < ctx->field_count; ++i) {
if(stricmp(ctx->fields[i].key, field) == 0) {
return ctx->fields[i].value;
for(size_t i = 0; i < ctx->fields.size; ++i) {
if(stricmp(ctx->fields.buf[i].key, field) == 0) {
return ctx->fields.buf[i].value;
}
}
return NULL;
@ -194,7 +196,7 @@ void resParse(http_response_t *ctx, const char *data) {
if(tran_encoding == NULL || stricmp(tran_encoding, "chunked") != 0) {
strview_t body = istrGetviewLen(&in, 0, SIZE_MAX);
free(ctx->body);
strvCopy(body, &ctx->body);
ctx->body = strvCopy(body).buf;
}
else {
fatal("chunked encoding not implemented yet");
@ -215,10 +217,10 @@ void resParseFields(http_response_t *ctx, str_istream_t *in) {
char *key_str = NULL;
char *value_str = NULL;
strvCopy(key, &key_str);
strvCopy(value, &value_str);
key_str = strvCopy(key).buf;
value_str = strvCopy(value).buf;
_setField(&ctx->fields, &ctx->field_count, key_str, value_str);
_setField(&ctx->fields, key_str, value_str);
free(key_str);
free(value_str);
@ -246,7 +248,7 @@ void hcliSetHost(http_client_t *ctx, const char *hostname) {
strview_t hostview = strvInit(hostname);
// if the hostname starts with http:// (case insensitive)
if(strvICompare(strvSubstr(hostview, 0, 7), strvInit("http://")) == 0) {
strvCopy(strvSubstr(hostview, 7, SIZE_MAX), &ctx->host_name);
ctx->host_name = strvCopy(strvSubstr(hostview, 7, SIZE_MAX)).buf;
}
else if(strvICompare(strvSubstr(hostview, 0, 8), strvInit("https://")) == 0) {
err("HTTPS protocol not yet supported");
@ -254,7 +256,7 @@ void hcliSetHost(http_client_t *ctx, const char *hostname) {
}
else {
// undefined protocol, use HTTP
strvCopy(hostview, &ctx->host_name);
ctx->host_name = strvCopy(hostview).buf;
}
}
@ -281,7 +283,7 @@ http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
}
http_response_t res = resInit();
char *request_str = NULL;
str_t req_str = strInit();
str_ostream_t received = ostrInitLen(1024);
if(!skInit()) {
@ -296,13 +298,13 @@ http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
}
if(skConnect(ctx->socket, ctx->host_name, ctx->port)) {
size_t len = reqString(req, &request_str);
if(len == 0) {
req_str = reqString(req);
if(req_str.len == 0) {
err("couldn't get string from request");
goto error;
}
if(skSend(ctx->socket, request_str, (int)len) == SOCKET_ERROR) {
if(skSend(ctx->socket, req_str.buf, (int)req_str.len) == SOCKET_ERROR) {
err("couldn't send request to socket: %s", skGetErrorString());
goto error;
}
@ -336,7 +338,7 @@ error:
err("couldn't clean up sockets %s", skGetErrorString());
}
skopen_error:
free(request_str);
strFree(&req_str);
ostrFree(&received);
return res;
}

16
http.h
View file

@ -8,8 +8,8 @@ extern "C" {
#include <stddef.h>
#include <stdbool.h>
#include "str.h"
#include "strstream.h"
#include "strview.h"
#include "socket.h"
enum {
@ -64,13 +64,18 @@ typedef struct {
char *value;
} 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"
// == HTTP REQUEST ============================================================
typedef struct {
int method;
http_version_t version;
http_field_t *fields;
int field_count;
field_vec_t fields;
char *uri;
char *body;
} http_request_t;
@ -84,14 +89,13 @@ void reqSetField(http_request_t *ctx, const char *key, const char *value);
void reqSetUri(http_request_t *ctx, const char *uri);
str_ostream_t reqPrepare(http_request_t *ctx);
size_t reqString(http_request_t *ctx, char **str);
str_t reqString(http_request_t *ctx);
// == HTTP RESPONSE ===========================================================
typedef struct {
int status_code;
http_field_t *fields;
int field_count;
field_vec_t fields;
http_version_t version;
char *body;
} http_response_t;

2
os.c
View file

@ -36,7 +36,7 @@ ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp) {
}
break;
}
*ptr++ = c;
*ptr++ = (char)c;
if(c == delimiter) {
*ptr = '\0';
result = ptr - *buf;

View file

@ -1,5 +0,0 @@
#pragma once
#include <stddef.h> // size_t
#define slice_t(type) struct { type buf; size_t len; }

View file

@ -51,7 +51,7 @@ bool skCleanup() {
}
socket_t skOpen(skType type) {
int sock_type;
int sock_type = 0;
switch(type) {
case SOCK_TCP: sock_type = SOCK_STREAM; break;
@ -181,11 +181,11 @@ int skReceivePro(socket_t sock, char *buf, int len, int flags) {
}
int skReceiveFrom(socket_t sock, char *buf, int len, sk_addrin_t *from) {
int fromlen = sizeof(sk_addr_t);
socket_len_t fromlen = sizeof(sk_addr_t);
return skReceiveFromPro(sock, buf, len, 0, (sk_addr_t*)from, &fromlen);
}
int skReceiveFromPro(socket_t sock, char *buf, int len, int flags, sk_addr_t *from, int *fromlen) {
int skReceiveFromPro(socket_t sock, char *buf, int len, int flags, sk_addr_t *from, socket_len_t *fromlen) {
return recvfrom(sock, buf, len, flags, from, fromlen);
}

View file

@ -93,7 +93,7 @@ int skReceivePro(socket_t sock, char *buf, int len, int flags);
// Receives a datagram and stores the source address.
int skReceiveFrom(socket_t sock, char *buf, int len, sk_addrin_t *from);
// Receives a datagram and stores the source address.
int skReceiveFromPro(socket_t sock, char *buf, int len, int flags, sk_addr_t *from, int *fromlen);
int skReceiveFromPro(socket_t sock, char *buf, int len, int flags, sk_addr_t *from, socket_len_t *fromlen);
// Checks that a opened socket is valid, returns true on success
bool skIsValid(socket_t sock);

357
str.c
View file

@ -2,7 +2,25 @@
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <assert.h>
#include <stdio.h>
#include "tracelog.h"
#ifdef _WIN32
#define VC_EXTRALEAN
#include <windows.h>
#else
#include <iconv.h>
#endif
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
// == STR_T ========================================================
str_t strInit(void) {
return (str_t) {
@ -34,6 +52,99 @@ void strFree(str_t *ctx) {
ctx->len = 0;
}
str_t strFromWCHAR(const wchar_t *src, size_t len) {
if(len == 0) len = wcslen(src);
#ifdef _WIN32
// TODO CP_ACP should be CP_UTF8 but if i put CP_UTF8 it doesn't work??
int result_len = WideCharToMultiByte(
CP_ACP, 0,
src, (int)len,
NULL, 0,
NULL, NULL
);
char *buf = malloc(result_len + 1);
if(buf) {
WideCharToMultiByte(
CP_ACP, 0,
src, (int)len,
buf, result_len,
NULL, NULL
);
buf[result_len] = '\0';
}
return (str_t) {
.buf = buf,
.len = result_len
};
#else
size_t actual_len = len * sizeof(wchar_t);
size_t dest_len = len * 6;
char *dest = malloc(dest_len);
iconv_t cd = iconv_open("UTF-8", "WCHAR_T");
assert(cd);
size_t dest_left = dest_len;
char *dest_temp = dest;
char *src_temp = (char*)src;
size_t lost = iconv(cd, &src_temp, &actual_len, &dest_temp, &dest_left);
assert(lost != ((size_t)-1));
dest_len -= dest_left;
dest = realloc(dest, dest_len + 1);
dest[dest_len] = '\0';
iconv_close(cd);
return (str_t){
.buf = dest,
.len = dest_len
};
#endif
}
wchar_t *strToWCHAR(str_t ctx) {
#ifdef _WIN32
UINT codepage = CP_ACP;
int len = MultiByteToWideChar(
codepage, 0,
ctx.buf, (int)ctx.len,
NULL, 0
);
wchar_t *str = malloc(sizeof(wchar_t) * (len + 1));
if(!str) return NULL;
len = MultiByteToWideChar(
codepage, 0,
ctx.buf, (int)ctx.len,
str, len
);
str[len] = '\0';
return str;
#else
size_t dest_len = ctx.len * sizeof(wchar_t);
char *dest = malloc(dest_len);
iconv_t cd = iconv_open("WCHAR_T", "UTF-8");
assert(cd);
size_t dest_left = dest_len;
char *dest_temp = dest;
char *src_temp = ctx.buf;
size_t lost = iconv(cd, &src_temp, &ctx.len, &dest_temp, &dest_left);
assert(lost != ((size_t)-1));
dest_len -= dest_left;
dest = realloc(dest, dest_len + 1);
dest[dest_len] = '\0';
iconv_close(cd);
return (wchar_t *)dest;
#endif
}
str_t strMove(str_t *ctx) {
str_t str = strInitBuf(ctx->buf, ctx->len);
ctx->buf = NULL;
@ -117,7 +228,13 @@ void strSwap(str_t *ctx, str_t *other) {
ctx->len = len;
}
#include "tracelog.h"
void strReplace(str_t *ctx, char from, char to) {
for(size_t i = 0; i < ctx->len; ++i) {
if(ctx->buf[i] == from) {
ctx->buf[i] = to;
}
}
}
str_t strSubstr(str_t *ctx, size_t pos, size_t len) {
if(strIsEmpty(ctx)) return strInit();
@ -136,7 +253,7 @@ strview_t strSubview(str_t *ctx, size_t pos, size_t len) {
void strLower(str_t *ctx) {
for(size_t i = 0; i < ctx->len; ++i) {
ctx->buf[i] = tolower(ctx->buf[i]);
ctx->buf[i] = (char)tolower(ctx->buf[i]);
}
}
@ -146,6 +263,242 @@ str_t strToLower(str_t ctx) {
return str;
}
// == STRVIEW_T ====================================================
strview_t strvInit(const char *cstr) {
return strvInitLen(cstr, cstr ? strlen(cstr) : 0);
}
strview_t strvInitStr(str_t str) {
return strvInitLen(str.buf, str.len);
}
strview_t strvInitLen(const char *buf, size_t size) {
return (strview_t) {
.buf = buf,
.len = size
};
}
char strvFront(strview_t ctx) {
return ctx.buf[0];
}
char strvBack(strview_t ctx) {
return ctx.buf[ctx.len - 1];
}
const char *strvBegin(strview_t *ctx) {
return ctx->buf;
}
const char *strvEnd(strview_t *ctx) {
return ctx->buf + ctx->len;
}
bool strvIsEmpty(strview_t ctx) {
return ctx.len == 0;
}
void strvRemovePrefix(strview_t *ctx, size_t n) {
ctx->buf += n;
ctx->len -= n;
}
void strvRemoveSuffix(strview_t *ctx, size_t n) {
ctx->len -= n;
}
str_t strvCopy(strview_t ctx) {
return strInitView(ctx);
}
str_t strvCopyN(strview_t ctx, size_t count, size_t from) {
size_t sz = ctx.len + 1 - from;
count = min(count, sz);
return strInitBuf(ctx.buf + from, count);
}
size_t strvCopyBuf(strview_t ctx, char *buf, size_t len, size_t from) {
size_t sz = ctx.len + 1 - from;
len = min(len, sz);
memcpy(buf, ctx.buf + from, len);
buf[len - 1] = '\0';
return len - 1;
}
strview_t strvSubstr(strview_t ctx, size_t from, size_t len) {
if(from > ctx.len) from = ctx.len - len;
size_t sz = ctx.len - from;
return strvInitLen(ctx.buf + from, min(len, sz));
}
int strvCompare(strview_t ctx, strview_t other) {
if(ctx.len < other.len) return -1;
if(ctx.len > other.len) return 1;
return memcmp(ctx.buf, other.buf, ctx.len);
}
int strvICompare(strview_t ctx, strview_t other) {
if(ctx.len < other.len) return -1;
if(ctx.len > other.len) return 1;
for(size_t i = 0; i < ctx.len; ++i) {
int a = tolower(ctx.buf[i]);
int b = tolower(other.buf[i]);
if(a != b) return a - b;
}
return 0;
}
bool strvStartsWith(strview_t ctx, char c) {
return strvFront(ctx) == c;
}
bool strvStartsWithView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false;
return memcmp(ctx.buf, view.buf, view.len) == 0;
}
bool strvEndsWith(strview_t ctx, char c) {
return strvBack(ctx) == c;
}
bool strvEndsWithView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false;
return memcmp(ctx.buf + ctx.len - view.len, view.buf, view.len) == 0;
}
bool strvContains(strview_t ctx, char c) {
for(size_t i = 0; i < ctx.len; ++i) {
if(ctx.buf[i] == c) return true;
}
return false;
}
bool strvContainsView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false;
size_t end = ctx.len - view.len;
for(size_t i = 0; i < end; ++i) {
if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return true;
}
return false;
}
size_t strvFind(strview_t ctx, char c, size_t from) {
for(size_t i = from; i < ctx.len; ++i) {
if(ctx.buf[i] == c) return i;
}
return SIZE_MAX;
}
size_t strvFindView(strview_t ctx, strview_t view, size_t from) {
if(ctx.len < view.len) return SIZE_MAX;
size_t end = ctx.len - view.len;
for(size_t i = from; i < end; ++i) {
if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return i;
}
return SIZE_MAX;
}
size_t strvRFind(strview_t ctx, char c, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(*buf == c) return (buf - ctx.buf);
}
return SIZE_MAX;
}
size_t strvRFindView(strview_t ctx, strview_t view, size_t from) {
from = min(from, ctx.len);
if(view.len > ctx.len) {
from -= view.len;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(memcmp(buf, view.buf, view.len) == 0) return (buf - ctx.buf);
}
return SIZE_MAX;
}
size_t strvFindFirstOf(strview_t ctx, strview_t view, size_t from) {
if(ctx.len < view.len) return SIZE_MAX;
for(size_t i = from; i < ctx.len; ++i) {
for(size_t j = 0; j < view.len; ++j) {
if(ctx.buf[i] == view.buf[j]) return i;
}
}
return SIZE_MAX;
}
size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
for(size_t j = 0; j < view.len; ++j) {
if(*buf == view.buf[j]) return (buf - ctx.buf);
}
}
return SIZE_MAX;
}
size_t strvFindFirstNot(strview_t ctx, char c, size_t from) {
size_t end = ctx.len - 1;
for(size_t i = from; i < end; ++i) {
if(ctx.buf[i] != c) return i;
}
return SIZE_MAX;
}
size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from) {
for(size_t i = from; i < ctx.len; ++i) {
if(!strvContains(view, ctx.buf[i])) {
return i;
}
}
return SIZE_MAX;
}
size_t strvFindLastNot(strview_t ctx, char c, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(*buf != c) {
return buf - ctx.buf;
}
}
return SIZE_MAX;
}
size_t strvFindLastNotOf(strview_t ctx, strview_t view, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(!strvContains(view, *buf)) {
return buf - ctx.buf;
}
}
return SIZE_MAX;
}
#ifdef STR_TESTING
#include <stdio.h>
#include "tracelog.h"

80
str.h
View file

@ -4,23 +4,35 @@
extern "C" {
#endif
#include "slice.h"
#include "strview.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>
#include <wchar.h>
// #include "strview.h"
#define STR_TESTING
#define STRV_NOT_FOUND SIZE_MAX
// typedef struct {
// char *buf;
// size_t len;
// } str_t;
typedef struct {
char *buf;
size_t len;
} str_t;
typedef slice_t(char *) 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);
@ -46,6 +58,8 @@ 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
@ -58,6 +72,56 @@ str_t strToLower(str_t ctx);
void strTest(void);
#endif
// == STRVIEW_T ====================================================
strview_t strvInit(const char *cstr);
strview_t strvInitStr(str_t str);
strview_t strvInitLen(const char *buf, size_t size);
char strvFront(strview_t ctx);
char strvBack(strview_t ctx);
const char *strvBegin(strview_t *ctx);
const char *strvEnd(strview_t *ctx);
// move view forward by n characters
void strvRemovePrefix(strview_t *ctx, size_t n);
// move view backwards by n characters
void strvRemoveSuffix(strview_t *ctx, size_t n);
bool strvIsEmpty(strview_t ctx);
str_t strvCopy(strview_t ctx);
str_t strvCopyN(strview_t ctx, size_t count, size_t from);
size_t strvCopyBuf(strview_t ctx, char *buf, size_t len, size_t from);
strview_t strvSubstr(strview_t ctx, size_t from, size_t len);
int strvCompare(strview_t ctx, strview_t other);
int strvICompare(strview_t ctx, strview_t other);
bool strvStartsWith(strview_t ctx, char c);
bool strvStartsWithView(strview_t ctx, strview_t view);
bool strvEndsWith(strview_t ctx, char c);
bool strvEndsWithView(strview_t ctx, strview_t view);
bool strvContains(strview_t ctx, char c);
bool strvContainsView(strview_t ctx, strview_t view);
size_t strvFind(strview_t ctx, char c, size_t from);
size_t strvFindView(strview_t ctx, strview_t view, size_t from);
size_t strvRFind(strview_t ctx, char c, size_t from);
size_t strvRFindView(strview_t ctx, strview_t view, size_t from);
// Finds the first occurrence of any of the characters of 'view' in this view
size_t strvFindFirstOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindFirstNot(strview_t ctx, char c, size_t from);
size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindLastNot(strview_t ctx, char c, size_t from);
size_t strvFindLastNotOf(strview_t ctx, strview_t view, size_t from);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -303,8 +303,8 @@ strview_t istrGetviewLen(str_istream_t *ctx, size_t off, size_t len) {
/* == OUTPUT STREAM =========================================== */
static void _ostrRealloc(str_ostream_t *ctx, size_t atlest) {
ctx->allocated = (ctx->allocated * 2) + atlest;
static void _ostrRealloc(str_ostream_t *ctx, size_t needed) {
ctx->allocated = (ctx->allocated * 2) + needed;
ctx->buf = realloc(ctx->buf, ctx->allocated);
}
@ -329,22 +329,48 @@ str_ostream_t ostrInitStr(const char *cstr, size_t len) {
return stream;
}
char ostrBack(str_ostream_t *ctx) {
return ctx->buf[ctx->size - 1];
}
void ostrFree(str_ostream_t *ctx) {
free(ctx->buf);
ctx->buf = NULL;
ctx->size = 0;
ctx->allocated = 0;
}
size_t ostrMove(str_ostream_t *ctx, char **str) {
*str = ctx->buf;
ctx->buf = NULL;
size_t sz = ctx->size;
ostrFree(ctx);
return sz;
void ostrClear(str_ostream_t *ctx) {
ctx->size = 0;
}
str_t ostrMove(str_ostream_t *ctx) {
str_t str = ostrAsStr(ctx);
*ctx = (str_ostream_t){0};
return str;
}
char ostrBack(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) {
.buf = ctx->buf,
.len = ctx->size
};
}
strview_t ostrAsView(str_ostream_t *ctx) {
return (strview_t) {
.buf = ctx->buf,
.len = ctx->size
};
}
void ostrReplace(str_ostream_t *ctx, char from, char to) {
for(size_t i = 0; i < ctx->size; ++i) {
if(ctx->buf[i] == from) {
ctx->buf[i] = to;
}
}
}
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...) {
@ -384,17 +410,21 @@ error:
#define APPEND_BUF_LEN 20
void ostrPutc(str_ostream_t *ctx, char c) {
if(ctx->size >= ctx->allocated) {
_ostrRealloc(ctx, 1);
}
ctx->buf[ctx->size++] = c;
ctx->buf[ctx->size] = '\0';
ostrAppendchar(ctx, c);
}
void ostrAppendbool(str_ostream_t *ctx, bool val) {
ostrAppendview(ctx, strvInit(val ? "true" : "false"));
}
void ostrAppendchar(str_ostream_t *ctx, char val) {
if(ctx->size >= ctx->allocated) {
_ostrRealloc(ctx, 1);
}
ctx->buf[ctx->size++] = val;
ctx->buf[ctx->size] = '\0';
}
void ostrAppendu8(str_ostream_t *ctx, uint8_t val) {
char buf[APPEND_BUF_LEN];
int len = snprintf(buf, sizeof(buf), "%hhu", val);

View file

@ -8,7 +8,7 @@ extern "C" {
#include <stdbool.h>
#include <stddef.h>
#include "strview.h"
#include "str.h"
/* == INPUT STREAM ============================================ */
@ -73,13 +73,20 @@ str_ostream_t ostrInitLen(size_t initial_alloc);
str_ostream_t ostrInitStr(const char *buf, size_t len);
void ostrFree(str_ostream_t *ctx);
size_t ostrMove(str_ostream_t *ctx, char **str);
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);

250
strview.c
View file

@ -1,250 +0,0 @@
#include "strview.h"
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include "tracelog.h"
#include <stdio.h>
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
strview_t strvInit(const char *cstr) {
return strvInitLen(cstr, cstr ? strlen(cstr) : 0);
}
strview_t strvInitLen(const char *buf, size_t size) {
strview_t view;
view.buf = buf;
view.len = size;
return view;
}
char strvFront(strview_t ctx) {
return ctx.buf[0];
}
char strvBack(strview_t ctx) {
return ctx.buf[ctx.len - 1];
}
const char *strvBegin(strview_t *ctx) {
return ctx->buf;
}
const char *strvEnd(strview_t *ctx) {
return ctx->buf + ctx->len;
}
bool strvIsEmpty(strview_t ctx) {
return ctx.len == 0;
}
void strvRemovePrefix(strview_t *ctx, size_t n) {
ctx->buf += n;
ctx->len -= n;
}
void strvRemoveSuffix(strview_t *ctx, size_t n) {
ctx->len -= n;
}
size_t strvCopy(strview_t ctx, char **buf) {
*buf = malloc(ctx.len + 1);
memcpy(*buf, ctx.buf, ctx.len);
(*buf)[ctx.len] = '\0';
return ctx.len;
}
size_t strvCopyN(strview_t ctx, char **buf, size_t count, size_t from) {
size_t sz = ctx.len + 1 - from;
count = min(count, sz);
*buf = malloc(count + 1);
memcpy(*buf, ctx.buf + from, count);
(*buf)[count] = '\0';
return count;
}
size_t strvCopyBuf(strview_t ctx, char *buf, size_t len, size_t from) {
size_t sz = ctx.len + 1 - from;
len = min(len, sz);
memcpy(buf, ctx.buf + from, len);
buf[len - 1] = '\0';
return len - 1;
}
strview_t strvSubstr(strview_t ctx, size_t from, size_t len) {
if(from > ctx.len) from = ctx.len - len;
size_t sz = ctx.len - from;
return strvInitLen(ctx.buf + from, min(len, sz));
}
int strvCompare(strview_t ctx, strview_t other) {
if(ctx.len < other.len) return -1;
if(ctx.len > other.len) return 1;
return memcmp(ctx.buf, other.buf, ctx.len);
}
int strvICompare(strview_t ctx, strview_t other) {
if(ctx.len < other.len) return -1;
if(ctx.len > other.len) return 1;
for(size_t i = 0; i < ctx.len; ++i) {
char a = tolower(ctx.buf[i]);
char b = tolower(other.buf[i]);
if(a != b) return a - b;
}
return 0;
}
bool strvStartsWith(strview_t ctx, char c) {
return strvFront(ctx) == c;
}
bool strvStartsWithView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false;
return memcmp(ctx.buf, view.buf, view.len) == 0;
}
bool strvEndsWith(strview_t ctx, char c) {
return strvBack(ctx) == c;
}
bool strvEndsWithView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false;
return memcmp(ctx.buf + ctx.len - view.len, view.buf, view.len) == 0;
}
bool strvContains(strview_t ctx, char c) {
for(size_t i = 0; i < ctx.len; ++i) {
if(ctx.buf[i] == c) return true;
}
return false;
}
bool strvContainsView(strview_t ctx, strview_t view) {
if(ctx.len < view.len) return false;
size_t end = ctx.len - view.len;
for(size_t i = 0; i < end; ++i) {
if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return true;
}
return false;
}
size_t strvFind(strview_t ctx, char c, size_t from) {
for(size_t i = from; i < ctx.len; ++i) {
if(ctx.buf[i] == c) return i;
}
return SIZE_MAX;
}
size_t strvFindView(strview_t ctx, strview_t view, size_t from) {
if(ctx.len < view.len) return SIZE_MAX;
size_t end = ctx.len - view.len;
for(size_t i = from; i < end; ++i) {
if(memcmp(ctx.buf + i, view.buf, view.len) == 0) return i;
}
return SIZE_MAX;
}
size_t strvRFind(strview_t ctx, char c, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(*buf == c) return (buf - ctx.buf);
}
return SIZE_MAX;
}
size_t strvRFindView(strview_t ctx, strview_t view, size_t from) {
from = min(from, ctx.len);
if(view.len > ctx.len) {
from -= view.len;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(memcmp(buf, view.buf, view.len) == 0) return (buf - ctx.buf);
}
return SIZE_MAX;
}
size_t strvFindFirstOf(strview_t ctx, strview_t view, size_t from) {
if(ctx.len < view.len) return SIZE_MAX;
for(size_t i = from; i < ctx.len; ++i) {
for(size_t j = 0; j < view.len; ++j) {
if(ctx.buf[i] == view.buf[j]) return i;
}
}
return SIZE_MAX;
}
size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
for(size_t j = 0; j < view.len; ++j) {
if(*buf == view.buf[j]) return (buf - ctx.buf);
}
}
return SIZE_MAX;
}
size_t strvFindFirstNot(strview_t ctx, char c, size_t from) {
size_t end = ctx.len - 1;
for(size_t i = from; i < end; ++i) {
if(ctx.buf[i] != c) return i;
}
return SIZE_MAX;
}
size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from) {
for(size_t i = from; i < ctx.len; ++i) {
if(!strvContains(view, ctx.buf[i])) {
return i;
}
}
return SIZE_MAX;
}
size_t strvFindLastNot(strview_t ctx, char c, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(*buf != c) {
return buf - ctx.buf;
}
}
return SIZE_MAX;
}
size_t strvFindLastNotOf(strview_t ctx, strview_t view, size_t from) {
if(from >= ctx.len) {
from = ctx.len - 1;
}
const char *buf = ctx.buf + from;
for(; buf >= ctx.buf; --buf) {
if(!strvContains(view, *buf)) {
return buf - ctx.buf;
}
}
return SIZE_MAX;
}

View file

@ -1,71 +0,0 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>
#include "slice.h"
#define STRV_NOT_FOUND SIZE_MAX
// typedef struct {
// const char *buf;
// size_t size;
// } strview_t;
typedef slice_t(const char *) strview_t;
strview_t strvInit(const char *cstr);
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);
size_t strvCopy(strview_t ctx, char **buf);
size_t strvCopyN(strview_t ctx, char **buf, size_t count, size_t from);
size_t strvCopyBuf(strview_t ctx, char *buf, size_t len, size_t from);
strview_t strvSubstr(strview_t ctx, size_t from, size_t len);
int strvCompare(strview_t ctx, strview_t other);
int strvICompare(strview_t ctx, strview_t other);
bool strvStartsWith(strview_t ctx, char c);
bool strvStartsWithView(strview_t ctx, strview_t view);
bool strvEndsWith(strview_t ctx, char c);
bool strvEndsWithView(strview_t ctx, strview_t view);
bool strvContains(strview_t ctx, char c);
bool strvContainsView(strview_t ctx, strview_t view);
size_t strvFind(strview_t ctx, char c, size_t from);
size_t strvFindView(strview_t ctx, strview_t view, size_t from);
size_t strvRFind(strview_t ctx, char c, size_t from);
size_t strvRFindView(strview_t ctx, strview_t view, size_t from);
// Finds the first occurrence of any of the characters of 'view' in this view
size_t strvFindFirstOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindLastOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindFirstNot(strview_t ctx, char c, size_t from);
size_t strvFindFirstNotOf(strview_t ctx, strview_t view, size_t from);
size_t strvFindLastNot(strview_t ctx, char c, size_t from);
size_t strvFindLastNotOf(strview_t ctx, strview_t view, size_t from);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -62,7 +62,7 @@ void traceLog(LogLevel level, const char *fmt, ...) {
case LogWarning: beg = BOLD YELLOW "[WARNING]: " RESET; break;
case LogError: beg = BOLD RED "[ERROR]: " RESET; break;
case LogFatal: beg = BOLD RED "[FATAL]: " RESET; break;
default: break;
default: beg = ""; break;
}
size_t offset = strlen(beg);

View file

@ -19,6 +19,7 @@ typedef enum {
void traceLog(LogLevel level, const char *fmt, ...);
void traceUseNewline(bool use_newline);
#define tall(...) traceLog(LogAll, __VA_ARGS__)
#define trace(...) traceLog(LogTrace, __VA_ARGS__)
#define debug(...) traceLog(LogDebug, __VA_ARGS__)
#define info(...) traceLog(LogInfo, __VA_ARGS__)

603
vec.h Normal file
View file

@ -0,0 +1,603 @@
#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