added:
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:
parent
aa08240ec9
commit
59b55c7f6c
26 changed files with 4037 additions and 2726 deletions
|
|
@ -1,18 +1,22 @@
|
||||||
add_library(Colla STATIC
|
add_library(Colla STATIC
|
||||||
socket.c
|
socket.c
|
||||||
tracelog.c
|
tracelog.c
|
||||||
http.c
|
http.c
|
||||||
strstream.c
|
strstream.c
|
||||||
strview.c
|
str.c
|
||||||
str.c
|
coroutine.c
|
||||||
coroutine.c
|
os.c
|
||||||
os.c
|
fs.c
|
||||||
fs.c
|
file.c
|
||||||
file.c
|
dir.c
|
||||||
)
|
dirwatch.c
|
||||||
|
)
|
||||||
IF (WIN32)
|
|
||||||
target_link_libraries(Colla ws2_32.lib)
|
if(MSVC)
|
||||||
ELSE()
|
target_link_libraries(Colla ws2_32.lib)
|
||||||
# posix
|
target_compile_options(Colla PRIVATE /W4)
|
||||||
ENDIF()
|
else()
|
||||||
|
target_link_libraries(Colla pthread)
|
||||||
|
target_compile_options(Colla PRIVATE -Wall -Wextra -Wpedantic)
|
||||||
|
target_compile_definitions(Colla PUBLIC _DEFAULT_SOURCE)
|
||||||
|
endif()
|
||||||
20
coroutine.c
20
coroutine.c
|
|
@ -1,11 +1,11 @@
|
||||||
#include "coroutine.h"
|
#include "coroutine.h"
|
||||||
|
|
||||||
coroutine_t coInit() {
|
coroutine_t coInit() {
|
||||||
return (coroutine_t) {
|
return (coroutine_t) {
|
||||||
.state = 0
|
.state = 0
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool coIsDead(coroutine_t co) {
|
bool coIsDead(coroutine_t co) {
|
||||||
return co.state == -1;
|
return co.state == -1;
|
||||||
}
|
}
|
||||||
264
coroutine.h
264
coroutine.h
|
|
@ -1,133 +1,133 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h> // bool
|
#include <stdbool.h> // bool
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
#include "tracelog.h" // fatal
|
#include "tracelog.h" // fatal
|
||||||
|
|
||||||
// heavily inspired by https://gist.github.com/Enichan/5f01140530ff0133fde19c9549a2a973
|
// heavily inspired by https://gist.github.com/Enichan/5f01140530ff0133fde19c9549a2a973
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Usage example:
|
// Usage example:
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int result;
|
int result;
|
||||||
coroutine_t co;
|
coroutine_t co;
|
||||||
} co_int_t;
|
} co_int_t;
|
||||||
|
|
||||||
bool coVoid(co_void_t *co) {
|
bool coVoid(co_void_t *co) {
|
||||||
costate(co_nostate);
|
costate(co_nostate);
|
||||||
coroutine({
|
coroutine({
|
||||||
printf("hello"); yield();
|
printf("hello"); yield();
|
||||||
printf("world"); yield();
|
printf("world"); yield();
|
||||||
printf("how"); yield();
|
printf("how"); yield();
|
||||||
printf("is"); yield();
|
printf("is"); yield();
|
||||||
printf("it"); yield();
|
printf("it"); yield();
|
||||||
printf("going?\n");
|
printf("going?\n");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool countToTen(co_int_t *co) {
|
bool countToTen(co_int_t *co) {
|
||||||
costate(int i; int ii;);
|
costate(int i; int ii;);
|
||||||
coroutine({
|
coroutine({
|
||||||
for(self.i = 0; self.i < 10; ++self.i) {
|
for(self.i = 0; self.i < 10; ++self.i) {
|
||||||
self.ii += self.i;
|
self.ii += self.i;
|
||||||
yieldVal(self.ii);
|
yieldVal(self.ii);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
co_void_t covoid = {0};
|
co_void_t covoid = {0};
|
||||||
while(coVoid(&covoid)) {
|
while(coVoid(&covoid)) {
|
||||||
printf(" ");
|
printf(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
co_int_t coint;
|
co_int_t coint;
|
||||||
coint.co = coInit();
|
coint.co = coInit();
|
||||||
while(countToTen(&coint)) {
|
while(countToTen(&coint)) {
|
||||||
printf("%d ", coint.result);
|
printf("%d ", coint.result);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
// reset coroutine for next call
|
// reset coroutine for next call
|
||||||
coint.co = coInit();
|
coint.co = coInit();
|
||||||
while(countToTen(&coint)) {
|
while(countToTen(&coint)) {
|
||||||
printf("%d ", coint.result);
|
printf("%d ", coint.result);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int state;
|
int state;
|
||||||
} coroutine_t;
|
} coroutine_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
coroutine_t co;
|
coroutine_t co;
|
||||||
} co_void_t;
|
} co_void_t;
|
||||||
|
|
||||||
coroutine_t coInit();
|
coroutine_t coInit();
|
||||||
bool coIsDead(coroutine_t co);
|
bool coIsDead(coroutine_t co);
|
||||||
|
|
||||||
#define COVAR co
|
#define COVAR co
|
||||||
#define COSELF self
|
#define COSELF self
|
||||||
|
|
||||||
#define costate(...) \
|
#define costate(...) \
|
||||||
typedef struct { bool init; __VA_ARGS__ } COSTATE; \
|
typedef struct { bool init; __VA_ARGS__ } COSTATE; \
|
||||||
static COSTATE self = {0}
|
static COSTATE self = {0}
|
||||||
|
|
||||||
#define co_nostate { char __dummy__; }
|
#define co_nostate { char __dummy__; }
|
||||||
|
|
||||||
#define yieldBreak() \
|
#define yieldBreak() \
|
||||||
COVAR->co.state = -1; \
|
COVAR->co.state = -1; \
|
||||||
self.init = false; \
|
self.init = false; \
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#define coroutine(...) \
|
#define coroutine(...) \
|
||||||
if(!self.init) { \
|
if(!self.init) { \
|
||||||
if(COVAR->co.state != 0) { \
|
if(COVAR->co.state != 0) { \
|
||||||
fatal("coroutine not initialized in %s:%d,\n" \
|
fatal("coroutine not initialized in %s:%d,\n" \
|
||||||
"did you forget to do '= {0};'?", \
|
"did you forget to do '= {0};'?", \
|
||||||
__FILE__, __LINE__); \
|
__FILE__, __LINE__); \
|
||||||
} \
|
} \
|
||||||
memset(&self, 0, sizeof(self)); \
|
memset(&self, 0, sizeof(self)); \
|
||||||
self.init = true; \
|
self.init = true; \
|
||||||
} \
|
} \
|
||||||
switch(COVAR->co.state) { \
|
switch(COVAR->co.state) { \
|
||||||
case 0:; \
|
case 0:; \
|
||||||
__VA_ARGS__ \
|
__VA_ARGS__ \
|
||||||
} \
|
} \
|
||||||
yieldBreak();
|
yieldBreak();
|
||||||
|
|
||||||
#define yield() _yield(__COUNTER__)
|
#define yield() _yield(__COUNTER__)
|
||||||
#define _yield(count) \
|
#define _yield(count) \
|
||||||
do { \
|
do { \
|
||||||
COVAR->co.state = count; \
|
COVAR->co.state = count; \
|
||||||
return true; \
|
return true; \
|
||||||
case count:; \
|
case count:; \
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
#define yieldVal(v) _yieldVal(v, __COUNTER__)
|
#define yieldVal(v) _yieldVal(v, __COUNTER__)
|
||||||
#define _yieldVal(v, count) \
|
#define _yieldVal(v, count) \
|
||||||
do { \
|
do { \
|
||||||
COVAR->co.state = count; \
|
COVAR->co.state = count; \
|
||||||
COVAR->result = v; \
|
COVAR->result = v; \
|
||||||
return true; \
|
return true; \
|
||||||
case count:; \
|
case count:; \
|
||||||
} while(0);
|
} while(0);
|
||||||
|
|
||||||
// increment __COUNTER__ past 0 so we don't get double case errors
|
// increment __COUNTER__ past 0 so we don't get double case errors
|
||||||
#define CONCAT_IMPL(x, y) x##y
|
#define CONCAT_IMPL(x, y) x##y
|
||||||
#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
|
#define MACRO_CONCAT(x, y) CONCAT_IMPL(x, y)
|
||||||
#define __INC_COUNTER int MACRO_CONCAT(__counter_tmp_, __COUNTER__)
|
#define __INC_COUNTER int MACRO_CONCAT(__counter_tmp_, __COUNTER__)
|
||||||
__INC_COUNTER;
|
__INC_COUNTER;
|
||||||
|
|
||||||
#undef CONCAT_IMPL
|
#undef CONCAT_IMPL
|
||||||
#undef MACRO_CONCAT
|
#undef MACRO_CONCAT
|
||||||
#undef __INC_COUNTER
|
#undef __INC_COUNTER
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
147
dir.c
Normal file
147
dir.c
Normal 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
29
dir.h
Normal 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
300
dirwatch.c
Normal 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
62
dirwatch.h
Normal 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);
|
||||||
11
file.h
11
file.h
|
|
@ -1,8 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "str.h"
|
#include "str.h"
|
||||||
#include "strview.h"
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
FILE_READ, FILE_WRITE, FILE_BOTH
|
FILE_READ, FILE_WRITE, FILE_BOTH
|
||||||
|
|
@ -28,4 +31,8 @@ size_t fileWrite(file_t *ctx, const void *buf, size_t len);
|
||||||
bool fileSeekEnd(file_t *ctx);
|
bool fileSeekEnd(file_t *ctx);
|
||||||
void fileRewind(file_t *ctx);
|
void fileRewind(file_t *ctx);
|
||||||
|
|
||||||
uint64_t fileTell(file_t *ctx);
|
uint64_t fileTell(file_t *ctx);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
254
fs.c
254
fs.c
|
|
@ -1,121 +1,135 @@
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "tracelog.h"
|
#include "tracelog.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define VC_EXTRALEAN
|
#define VC_EXTRALEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
static int _modeToType(unsigned int mode) {
|
static int _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;
|
||||||
case _S_IFREG: return FS_MODE_FILE;
|
case _S_IFREG: return FS_MODE_FILE;
|
||||||
case _S_IFIFO: return FS_MODE_FIFO;
|
case _S_IFIFO: return FS_MODE_FIFO;
|
||||||
default: return FS_MODE_UKNOWN;
|
default: return FS_MODE_UKNOWN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs_stat_t fsStat(file_t fp) {
|
fs_stat_t fsStat(file_t fp) {
|
||||||
TCHAR path[MAX_PATH];
|
TCHAR path[MAX_PATH];
|
||||||
DWORD pathlen = GetFinalPathNameByHandle(
|
GetFinalPathNameByHandle(
|
||||||
(HANDLE)fp.handle,
|
(HANDLE)fp.handle,
|
||||||
path,
|
path,
|
||||||
MAX_PATH,
|
MAX_PATH,
|
||||||
0);
|
0
|
||||||
|
);
|
||||||
struct stat statbuf;
|
|
||||||
int res = stat(path, &statbuf);
|
struct stat statbuf;
|
||||||
if(res == 0) {
|
int res = stat(path, &statbuf);
|
||||||
return (fs_stat_t) {
|
if(res == 0) {
|
||||||
.type = _modeToType(statbuf.st_mode),
|
return (fs_stat_t) {
|
||||||
.size = statbuf.st_size,
|
.type = _modeToType(statbuf.st_mode),
|
||||||
.last_access = statbuf.st_atime,
|
.size = statbuf.st_size,
|
||||||
.last_modif = statbuf.st_mtime
|
.last_access = statbuf.st_atime,
|
||||||
};
|
.last_modif = statbuf.st_mtime
|
||||||
}
|
};
|
||||||
else {
|
}
|
||||||
return (fs_stat_t) { 0 };
|
else {
|
||||||
}
|
return (fs_stat_t) { 0 };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fs_time_t fsAsTime(int64_t timer) {
|
|
||||||
struct tm t;
|
fs_time_t fsAsTime(int64_t timer) {
|
||||||
errno_t error = localtime_s(&t, &timer);
|
struct tm t;
|
||||||
|
errno_t error = localtime_s(&t, &timer);
|
||||||
if(error == 0) {
|
|
||||||
return (fs_time_t) {
|
if(error == 0) {
|
||||||
.year = t.tm_year + 1900,
|
return (fs_time_t) {
|
||||||
.month = t.tm_mon + 1,
|
.year = t.tm_year + 1900,
|
||||||
.day = t.tm_mday,
|
.month = t.tm_mon + 1,
|
||||||
.hour = t.tm_hour,
|
.day = t.tm_mday,
|
||||||
.minutes = t.tm_min,
|
.hour = t.tm_hour,
|
||||||
.seconds = t.tm_sec,
|
.minutes = t.tm_min,
|
||||||
.daylight_saving = t.tm_isdst > 0
|
.seconds = t.tm_sec,
|
||||||
};
|
.daylight_saving = t.tm_isdst > 0
|
||||||
}
|
};
|
||||||
else {
|
}
|
||||||
char buf[128];
|
else {
|
||||||
strerror_s(buf, sizeof(buf), error);
|
char buf[128];
|
||||||
err("%s", buf);
|
strerror_s(buf, sizeof(buf), error);
|
||||||
return (fs_time_t) { 0 };
|
err("%s", buf);
|
||||||
}
|
return (fs_time_t) { 0 };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#else
|
|
||||||
#include <sys/stat.h>
|
bool fsIsDir(const char *path) {
|
||||||
|
DWORD attr = GetFileAttributes(path);
|
||||||
static int _modeToType(unsigned int mode) {
|
|
||||||
switch(mode & __S_IFMT) {
|
return attr != INVALID_FILE_ATTRIBUTES &&
|
||||||
case __S_IFDIR: return FS_MODE_DIR;
|
attr & FILE_ATTRIBUTE_DIRECTORY;
|
||||||
case __S_IFCHR: return FS_MODE_CHARACTER_DEVICE;
|
}
|
||||||
case __S_IFREG: return FS_MODE_FILE;
|
|
||||||
case __S_IFIFO: return FS_MODE_FIFO;
|
#else
|
||||||
default: return FS_MODE_UKNOWN;
|
#include <sys/stat.h>
|
||||||
}
|
|
||||||
}
|
static int _modeToType(unsigned int mode) {
|
||||||
|
switch(mode & __S_IFMT) {
|
||||||
fs_stat_t fsStat(file_t fp) {
|
case __S_IFDIR: return FS_MODE_DIR;
|
||||||
int fd = fileno((FILE*)fp.handle);
|
case __S_IFCHR: return FS_MODE_CHARACTER_DEVICE;
|
||||||
struct stat statbuf;
|
case __S_IFREG: return FS_MODE_FILE;
|
||||||
int res = fstat(fd, &statbuf);
|
case __S_IFIFO: return FS_MODE_FIFO;
|
||||||
if(res == 0) {
|
default: return FS_MODE_UKNOWN;
|
||||||
return (fs_stat_t) {
|
}
|
||||||
.type = _modeToType(statbuf.st_mode),
|
}
|
||||||
.size = statbuf.st_size,
|
|
||||||
.last_access = statbuf.st_atime,
|
fs_stat_t fsStat(file_t fp) {
|
||||||
.last_modif = statbuf.st_mtime
|
int fd = fileno((FILE*)fp.handle);
|
||||||
};
|
struct stat statbuf;
|
||||||
}
|
int res = fstat(fd, &statbuf);
|
||||||
else {
|
if(res == 0) {
|
||||||
return (fs_stat_t) { 0 };
|
return (fs_stat_t) {
|
||||||
}
|
.type = _modeToType(statbuf.st_mode),
|
||||||
}
|
.size = statbuf.st_size,
|
||||||
|
.last_access = statbuf.st_atime,
|
||||||
fs_time_t fsAsTime(int64_t timer) {
|
.last_modif = statbuf.st_mtime
|
||||||
struct tm *t = localtime(&timer);
|
};
|
||||||
|
}
|
||||||
if(t) {
|
else {
|
||||||
return (fs_time_t) {
|
return (fs_stat_t) { 0 };
|
||||||
.year = t->tm_year + 1900,
|
}
|
||||||
.month = t->tm_mon + 1,
|
}
|
||||||
.day = t->tm_mday,
|
|
||||||
.hour = t->tm_hour,
|
fs_time_t fsAsTime(int64_t timer) {
|
||||||
.minutes = t->tm_min,
|
struct tm *t = localtime(&timer);
|
||||||
.seconds = t->tm_sec,
|
|
||||||
.daylight_saving = t->tm_isdst > 0
|
if(t) {
|
||||||
};
|
return (fs_time_t) {
|
||||||
}
|
.year = t->tm_year + 1900,
|
||||||
else {
|
.month = t->tm_mon + 1,
|
||||||
err("%s", strerror(errno));
|
.day = t->tm_mday,
|
||||||
return (fs_time_t) { 0 };
|
.hour = t->tm_hour,
|
||||||
}
|
.minutes = t->tm_min,
|
||||||
}
|
.seconds = t->tm_sec,
|
||||||
|
.daylight_saving = t->tm_isdst > 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err("%s", strerror(errno));
|
||||||
|
return (fs_time_t) { 0 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fsIsDir(const char *path) {
|
||||||
|
struct stat statbuf;
|
||||||
|
return stat(path, &statbuf) == 0 && S_ISDIR(statbuf.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
78
fs.h
78
fs.h
|
|
@ -1,34 +1,44 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#ifdef __cplusplus
|
||||||
#include <stdbool.h>
|
extern "C" {
|
||||||
|
#endif
|
||||||
#include "file.h"
|
|
||||||
|
#include <stdint.h>
|
||||||
enum {
|
#include <stdbool.h>
|
||||||
FS_MODE_FILE,
|
|
||||||
FS_MODE_DIR,
|
#include "file.h"
|
||||||
FS_MODE_CHARACTER_DEVICE,
|
|
||||||
FS_MODE_FIFO,
|
enum {
|
||||||
FS_MODE_UKNOWN,
|
FS_MODE_FILE,
|
||||||
};
|
FS_MODE_DIR,
|
||||||
|
FS_MODE_CHARACTER_DEVICE,
|
||||||
typedef struct {
|
FS_MODE_FIFO,
|
||||||
int type;
|
FS_MODE_UKNOWN,
|
||||||
uint64_t size;
|
};
|
||||||
int64_t last_access;
|
|
||||||
int64_t last_modif;
|
typedef struct {
|
||||||
} fs_stat_t;
|
int type;
|
||||||
|
uint64_t size;
|
||||||
typedef struct {
|
int64_t last_access;
|
||||||
int year;
|
int64_t last_modif;
|
||||||
int month;
|
} fs_stat_t;
|
||||||
int day;
|
|
||||||
int hour;
|
typedef struct {
|
||||||
int minutes;
|
int year;
|
||||||
int seconds;
|
int month;
|
||||||
bool daylight_saving;
|
int day;
|
||||||
} fs_time_t;
|
int hour;
|
||||||
|
int minutes;
|
||||||
fs_stat_t fsStat(file_t fp);
|
int seconds;
|
||||||
fs_time_t fsAsTime(int64_t time);
|
bool daylight_saving;
|
||||||
|
} fs_time_t;
|
||||||
|
|
||||||
|
fs_stat_t fsStat(file_t fp);
|
||||||
|
fs_time_t fsAsTime(int64_t time);
|
||||||
|
|
||||||
|
bool fsIsDir(const char *path);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
720
http.c
720
http.c
|
|
@ -1,359 +1,361 @@
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "tracelog.h"
|
#include "tracelog.h"
|
||||||
|
|
||||||
// == INTERNAL ================================================================
|
#define T http_field_t
|
||||||
|
#define VEC_SHORT_NAME field_vec
|
||||||
static void _setField(http_field_t **fields_ptr, int *fields_count_ptr, const char *key, const char *value) {
|
#define VEC_DISABLE_ERASE_WHEN
|
||||||
http_field_t *fields = *fields_ptr;
|
#define VEC_NO_DECLARATION
|
||||||
int fields_count = *fields_count_ptr;
|
#include "vec.h"
|
||||||
|
|
||||||
// search if the field already exists
|
// == INTERNAL ================================================================
|
||||||
for(int i = 0; i < fields_count; ++i) {
|
|
||||||
if(stricmp(fields[i].key, key) == 0) {
|
static void _setField(field_vec_t *fields, const char *key, const char *value) {
|
||||||
// replace value
|
// search if the field already exists
|
||||||
char **curval = &fields[i].value;
|
for(size_t i = 0; i < fields->size; ++i) {
|
||||||
size_t cur = strlen(*curval);
|
if(stricmp(fields->buf[i].key, key) == 0) {
|
||||||
size_t new = strlen(value);
|
// replace value
|
||||||
if(new > cur) {
|
char **curval = &fields->buf[i].value;
|
||||||
*curval = realloc(*curval, new + 1);
|
size_t cur = strlen(*curval);
|
||||||
}
|
size_t new = strlen(value);
|
||||||
memcpy(*curval, value, new);
|
if(new > cur) {
|
||||||
(*curval)[new] = '\0';
|
*curval = realloc(*curval, new + 1);
|
||||||
return;
|
}
|
||||||
}
|
memcpy(*curval, value, new);
|
||||||
}
|
(*curval)[new] = '\0';
|
||||||
// otherwise, add it to the list
|
return;
|
||||||
(*fields_count_ptr)++;;
|
}
|
||||||
(*fields_ptr) = realloc(fields, sizeof(http_field_t) * (*fields_count_ptr));
|
}
|
||||||
http_field_t *field = &(*fields_ptr)[(*fields_count_ptr) - 1];
|
// otherwise, add it to the list
|
||||||
|
http_field_t field;
|
||||||
size_t klen = strlen(key);
|
size_t klen = strlen(key);
|
||||||
size_t vlen = strlen(value);
|
size_t vlen = strlen(value);
|
||||||
field->key = malloc(klen + 1);
|
field.key = malloc(klen + 1);
|
||||||
field->value = malloc(vlen + 1);
|
field.value = 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);
|
||||||
// == HTTP VERSION ============================================================
|
}
|
||||||
|
|
||||||
int httpVerNumber(http_version_t ver) {
|
// == HTTP VERSION ============================================================
|
||||||
return (ver.major * 10) + ver.minor;
|
|
||||||
}
|
int httpVerNumber(http_version_t ver) {
|
||||||
|
return (ver.major * 10) + ver.minor;
|
||||||
// == HTTP REQUEST ============================================================
|
}
|
||||||
|
|
||||||
http_request_t reqInit() {
|
// == HTTP REQUEST ============================================================
|
||||||
http_request_t req;
|
|
||||||
memset(&req, 0, sizeof(req));
|
http_request_t reqInit() {
|
||||||
reqSetUri(&req, "/");
|
http_request_t req;
|
||||||
req.version = (http_version_t){1, 1};
|
memset(&req, 0, sizeof(req));
|
||||||
return req;
|
reqSetUri(&req, "/");
|
||||||
}
|
req.version = (http_version_t){1, 1};
|
||||||
|
return req;
|
||||||
void reqFree(http_request_t *ctx) {
|
}
|
||||||
for(int i = 0; i < ctx->field_count; ++i) {
|
|
||||||
free(ctx->fields[i].key);
|
void reqFree(http_request_t *ctx) {
|
||||||
free(ctx->fields[i].value);
|
for(size_t i = 0; i < ctx->fields.size; ++i) {
|
||||||
}
|
free(ctx->fields.buf[i].key);
|
||||||
free(ctx->fields);
|
free(ctx->fields.buf[i].value);
|
||||||
free(ctx->uri);
|
}
|
||||||
free(ctx->body);
|
field_vecFree(&ctx->fields);
|
||||||
memset(ctx, 0, sizeof(http_request_t));
|
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) {
|
bool reqHasField(http_request_t *ctx, const char *key) {
|
||||||
return true;
|
for(size_t i = 0; i < ctx->fields.size; ++i) {
|
||||||
}
|
if(stricmp(ctx->fields.buf[i].key, key) == 0) {
|
||||||
}
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
void reqSetField(http_request_t *ctx, const char *key, const char *value) {
|
}
|
||||||
_setField(&ctx->fields, &ctx->field_count, key, value);
|
|
||||||
}
|
void reqSetField(http_request_t *ctx, const char *key, const char *value) {
|
||||||
|
_setField(&ctx->fields, key, value);
|
||||||
void reqSetUri(http_request_t *ctx, const char *uri) {
|
}
|
||||||
if(uri == NULL) return;
|
|
||||||
size_t len = strlen(uri);
|
void reqSetUri(http_request_t *ctx, const char *uri) {
|
||||||
if(uri[0] != '/') {
|
if(uri == NULL) return;
|
||||||
len += 1;
|
size_t len = strlen(uri);
|
||||||
ctx->uri = realloc(ctx->uri, len + 1);
|
if(uri[0] != '/') {
|
||||||
ctx->uri[0] = '/';
|
len += 1;
|
||||||
memcpy(ctx->uri + 1, uri, len);
|
ctx->uri = realloc(ctx->uri, len + 1);
|
||||||
ctx->uri[len] = '\0';
|
ctx->uri[0] = '/';
|
||||||
}
|
memcpy(ctx->uri + 1, uri, len);
|
||||||
else {
|
ctx->uri[len] = '\0';
|
||||||
ctx->uri = realloc(ctx->uri, len + 1);
|
}
|
||||||
memcpy(ctx->uri, uri, len);
|
else {
|
||||||
ctx->uri[len] = '\0';
|
ctx->uri = realloc(ctx->uri, len + 1);
|
||||||
}
|
memcpy(ctx->uri, uri, len);
|
||||||
}
|
ctx->uri[len] = '\0';
|
||||||
|
}
|
||||||
str_ostream_t reqPrepare(http_request_t *ctx) {
|
}
|
||||||
str_ostream_t out = ostrInitLen(1024);
|
|
||||||
|
str_ostream_t reqPrepare(http_request_t *ctx) {
|
||||||
const char *method = NULL;
|
str_ostream_t out = ostrInitLen(1024);
|
||||||
switch(ctx->method) {
|
|
||||||
case REQ_GET: method = "GET"; break;
|
const char *method = NULL;
|
||||||
case REQ_POST: method = "POST"; break;
|
switch(ctx->method) {
|
||||||
case REQ_HEAD: method = "HEAD"; break;
|
case REQ_GET: method = "GET"; break;
|
||||||
case REQ_PUT: method = "PUT"; break;
|
case REQ_POST: method = "POST"; break;
|
||||||
case REQ_DELETE: method = "DELETE"; break;
|
case REQ_HEAD: method = "HEAD"; break;
|
||||||
default: err("unrecognized method: %d", method); goto error;
|
case REQ_PUT: method = "PUT"; break;
|
||||||
}
|
case REQ_DELETE: method = "DELETE"; break;
|
||||||
|
default: err("unrecognized method: %d", method); goto error;
|
||||||
ostrPrintf(&out, "%s %s HTTP/%hhu.%hhu\r\n",
|
}
|
||||||
method, ctx->uri, ctx->version.major, ctx->version.minor
|
|
||||||
);
|
ostrPrintf(&out, "%s %s HTTP/%hhu.%hhu\r\n",
|
||||||
|
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"));
|
}
|
||||||
if(ctx->body) {
|
|
||||||
ostrAppendview(&out, strvInit(ctx->body));
|
ostrAppendview(&out, strvInit("\r\n"));
|
||||||
}
|
if(ctx->body) {
|
||||||
|
ostrAppendview(&out, strvInit(ctx->body));
|
||||||
error:
|
}
|
||||||
return out;
|
|
||||||
}
|
error:
|
||||||
|
return out;
|
||||||
size_t reqString(http_request_t *ctx, char **str) {
|
}
|
||||||
str_ostream_t out = reqPrepare(ctx);
|
|
||||||
return ostrMove(&out, str);
|
str_t reqString(http_request_t *ctx) {
|
||||||
}
|
str_ostream_t out = reqPrepare(ctx);
|
||||||
|
return ostrMove(&out);
|
||||||
// == HTTP RESPONSE ===========================================================
|
}
|
||||||
|
|
||||||
http_response_t resInit() {
|
// == HTTP RESPONSE ===========================================================
|
||||||
http_response_t res;
|
|
||||||
memset(&res, 0, sizeof(res));
|
http_response_t resInit() {
|
||||||
return res;
|
http_response_t res;
|
||||||
}
|
memset(&res, 0, sizeof(res));
|
||||||
|
return res;
|
||||||
void resFree(http_response_t *ctx) {
|
}
|
||||||
for(int i = 0; i < ctx->field_count; ++i) {
|
|
||||||
free(ctx->fields[i].key);
|
void resFree(http_response_t *ctx) {
|
||||||
free(ctx->fields[i].value);
|
for(size_t i = 0; i < ctx->fields.size; ++i) {
|
||||||
}
|
free(ctx->fields.buf[i].key);
|
||||||
free(ctx->fields);
|
free(ctx->fields.buf[i].value);
|
||||||
free(ctx->body);
|
}
|
||||||
memset(ctx, 0, sizeof(http_response_t));
|
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) {
|
bool resHasField(http_response_t *ctx, const char *key) {
|
||||||
return true;
|
for(size_t i = 0; i < ctx->fields.size; ++i) {
|
||||||
}
|
if(stricmp(ctx->fields.buf[i].key, key) == 0) {
|
||||||
}
|
return true;
|
||||||
return false;
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
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) {
|
const char *resGetField(http_response_t *ctx, const char *field) {
|
||||||
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;
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
void resParse(http_response_t *ctx, const char *data) {
|
}
|
||||||
str_istream_t in = istrInit(data);
|
|
||||||
|
void resParse(http_response_t *ctx, const char *data) {
|
||||||
char hp[5];
|
str_istream_t in = istrInit(data);
|
||||||
istrGetstringBuf(&in, hp, 5);
|
|
||||||
if(stricmp(hp, "http") != 0) {
|
char hp[5];
|
||||||
err("response doesn't start with 'HTTP', instead with %c%c%c%c", hp[0], hp[1], hp[2], hp[3]);
|
istrGetstringBuf(&in, hp, 5);
|
||||||
return;
|
if(stricmp(hp, "http") != 0) {
|
||||||
}
|
err("response doesn't start with 'HTTP', instead with %c%c%c%c", hp[0], hp[1], hp[2], hp[3]);
|
||||||
istrSkip(&in, 1); // skip /
|
return;
|
||||||
istrGetu8(&in, &ctx->version.major);
|
}
|
||||||
istrSkip(&in, 1); // skip .
|
istrSkip(&in, 1); // skip /
|
||||||
istrGetu8(&in, &ctx->version.minor);
|
istrGetu8(&in, &ctx->version.major);
|
||||||
istrGeti32(&in, &ctx->status_code);
|
istrSkip(&in, 1); // skip .
|
||||||
|
istrGetu8(&in, &ctx->version.minor);
|
||||||
istrIgnore(&in, '\n');
|
istrGeti32(&in, &ctx->status_code);
|
||||||
istrSkip(&in, 1); // skip \n
|
|
||||||
|
istrIgnore(&in, '\n');
|
||||||
resParseFields(ctx, &in);
|
istrSkip(&in, 1); // skip \n
|
||||||
|
|
||||||
const char *tran_encoding = resGetField(ctx, "transfer-encoding");
|
resParseFields(ctx, &in);
|
||||||
if(tran_encoding == NULL || stricmp(tran_encoding, "chunked") != 0) {
|
|
||||||
strview_t body = istrGetviewLen(&in, 0, SIZE_MAX);
|
const char *tran_encoding = resGetField(ctx, "transfer-encoding");
|
||||||
free(ctx->body);
|
if(tran_encoding == NULL || stricmp(tran_encoding, "chunked") != 0) {
|
||||||
strvCopy(body, &ctx->body);
|
strview_t body = istrGetviewLen(&in, 0, SIZE_MAX);
|
||||||
}
|
free(ctx->body);
|
||||||
else {
|
ctx->body = strvCopy(body).buf;
|
||||||
fatal("chunked encoding not implemented yet");
|
}
|
||||||
}
|
else {
|
||||||
}
|
fatal("chunked encoding not implemented yet");
|
||||||
|
}
|
||||||
void resParseFields(http_response_t *ctx, str_istream_t *in) {
|
}
|
||||||
strview_t line;
|
|
||||||
|
void resParseFields(http_response_t *ctx, str_istream_t *in) {
|
||||||
do {
|
strview_t line;
|
||||||
line = istrGetview(in, '\r');
|
|
||||||
|
do {
|
||||||
size_t pos = strvFind(line, ':', 0);
|
line = istrGetview(in, '\r');
|
||||||
if(pos != STRV_NOT_FOUND) {
|
|
||||||
strview_t key = strvSubstr(line, 0, pos);
|
size_t pos = strvFind(line, ':', 0);
|
||||||
strview_t value = strvSubstr(line, pos + 2, SIZE_MAX);
|
if(pos != STRV_NOT_FOUND) {
|
||||||
|
strview_t key = strvSubstr(line, 0, pos);
|
||||||
char *key_str = NULL;
|
strview_t value = strvSubstr(line, pos + 2, SIZE_MAX);
|
||||||
char *value_str = NULL;
|
|
||||||
|
char *key_str = NULL;
|
||||||
strvCopy(key, &key_str);
|
char *value_str = NULL;
|
||||||
strvCopy(value, &value_str);
|
|
||||||
|
key_str = strvCopy(key).buf;
|
||||||
_setField(&ctx->fields, &ctx->field_count, key_str, value_str);
|
value_str = strvCopy(value).buf;
|
||||||
|
|
||||||
free(key_str);
|
_setField(&ctx->fields, key_str, value_str);
|
||||||
free(value_str);
|
|
||||||
}
|
free(key_str);
|
||||||
|
free(value_str);
|
||||||
istrSkip(in, 2); // skip \r\n
|
}
|
||||||
} while(line.len > 2);
|
|
||||||
}
|
istrSkip(in, 2); // skip \r\n
|
||||||
|
} while(line.len > 2);
|
||||||
// == HTTP CLIENT =============================================================
|
}
|
||||||
|
|
||||||
http_client_t hcliInit() {
|
// == HTTP CLIENT =============================================================
|
||||||
http_client_t client;
|
|
||||||
memset(&client, 0, sizeof(client));
|
http_client_t hcliInit() {
|
||||||
client.port = 80;
|
http_client_t client;
|
||||||
return client;
|
memset(&client, 0, sizeof(client));
|
||||||
}
|
client.port = 80;
|
||||||
|
return client;
|
||||||
void hcliFree(http_client_t *ctx) {
|
}
|
||||||
free(ctx->host_name);
|
|
||||||
memset(ctx, 0, sizeof(http_client_t));
|
void hcliFree(http_client_t *ctx) {
|
||||||
}
|
free(ctx->host_name);
|
||||||
|
memset(ctx, 0, sizeof(http_client_t));
|
||||||
void hcliSetHost(http_client_t *ctx, const char *hostname) {
|
}
|
||||||
strview_t hostview = strvInit(hostname);
|
|
||||||
// if the hostname starts with http:// (case insensitive)
|
void hcliSetHost(http_client_t *ctx, const char *hostname) {
|
||||||
if(strvICompare(strvSubstr(hostview, 0, 7), strvInit("http://")) == 0) {
|
strview_t hostview = strvInit(hostname);
|
||||||
strvCopy(strvSubstr(hostview, 7, SIZE_MAX), &ctx->host_name);
|
// if the hostname starts with http:// (case insensitive)
|
||||||
}
|
if(strvICompare(strvSubstr(hostview, 0, 7), strvInit("http://")) == 0) {
|
||||||
else if(strvICompare(strvSubstr(hostview, 0, 8), strvInit("https://")) == 0) {
|
ctx->host_name = strvCopy(strvSubstr(hostview, 7, SIZE_MAX)).buf;
|
||||||
err("HTTPS protocol not yet supported");
|
}
|
||||||
return;
|
else if(strvICompare(strvSubstr(hostview, 0, 8), strvInit("https://")) == 0) {
|
||||||
}
|
err("HTTPS protocol not yet supported");
|
||||||
else {
|
return;
|
||||||
// undefined protocol, use HTTP
|
}
|
||||||
strvCopy(hostview, &ctx->host_name);
|
else {
|
||||||
}
|
// undefined protocol, use HTTP
|
||||||
}
|
ctx->host_name = strvCopy(hostview).buf;
|
||||||
|
}
|
||||||
http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
|
}
|
||||||
if(!reqHasField(req, "Host")) {
|
|
||||||
reqSetField(req, "Host", ctx->host_name);
|
http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *req) {
|
||||||
}
|
if(!reqHasField(req, "Host")) {
|
||||||
if(!reqHasField(req, "Content-Length")) {
|
reqSetField(req, "Host", ctx->host_name);
|
||||||
if(req->body) {
|
}
|
||||||
str_ostream_t out = ostrInitLen(20);
|
if(!reqHasField(req, "Content-Length")) {
|
||||||
ostrAppendu64(&out, strlen(req->body));
|
if(req->body) {
|
||||||
reqSetField(req, "Content-Length", out.buf);
|
str_ostream_t out = ostrInitLen(20);
|
||||||
ostrFree(&out);
|
ostrAppendu64(&out, strlen(req->body));
|
||||||
}
|
reqSetField(req, "Content-Length", out.buf);
|
||||||
else {
|
ostrFree(&out);
|
||||||
reqSetField(req, "Content-Length", "0");
|
}
|
||||||
}
|
else {
|
||||||
}
|
reqSetField(req, "Content-Length", "0");
|
||||||
if(req->method == REQ_POST && !reqHasField(req, "Content-Type")) {
|
}
|
||||||
reqSetField(req, "Content-Type", "application/x-www-form-urlencoded");
|
}
|
||||||
}
|
if(req->method == REQ_POST && !reqHasField(req, "Content-Type")) {
|
||||||
if(httpVerNumber(req->version) >= 11 && !reqHasField(req, "Connection")) {
|
reqSetField(req, "Content-Type", "application/x-www-form-urlencoded");
|
||||||
reqSetField(req, "Connection", "close");
|
}
|
||||||
}
|
if(httpVerNumber(req->version) >= 11 && !reqHasField(req, "Connection")) {
|
||||||
|
reqSetField(req, "Connection", "close");
|
||||||
http_response_t res = resInit();
|
}
|
||||||
char *request_str = NULL;
|
|
||||||
str_ostream_t received = ostrInitLen(1024);
|
http_response_t res = resInit();
|
||||||
|
str_t req_str = strInit();
|
||||||
if(!skInit()) {
|
str_ostream_t received = ostrInitLen(1024);
|
||||||
err("couldn't initialize sockets %s", skGetErrorString());
|
|
||||||
goto skopen_error;
|
if(!skInit()) {
|
||||||
}
|
err("couldn't initialize sockets %s", skGetErrorString());
|
||||||
|
goto skopen_error;
|
||||||
ctx->socket = skOpen(SOCK_TCP);
|
}
|
||||||
if(ctx->socket == INVALID_SOCKET) {
|
|
||||||
err("couldn't open socket %s", skGetErrorString());
|
ctx->socket = skOpen(SOCK_TCP);
|
||||||
goto error;
|
if(ctx->socket == INVALID_SOCKET) {
|
||||||
}
|
err("couldn't open socket %s", skGetErrorString());
|
||||||
|
goto error;
|
||||||
if(skConnect(ctx->socket, ctx->host_name, ctx->port)) {
|
}
|
||||||
size_t len = reqString(req, &request_str);
|
|
||||||
if(len == 0) {
|
if(skConnect(ctx->socket, ctx->host_name, ctx->port)) {
|
||||||
err("couldn't get string from request");
|
req_str = reqString(req);
|
||||||
goto error;
|
if(req_str.len == 0) {
|
||||||
}
|
err("couldn't get string from request");
|
||||||
|
goto error;
|
||||||
if(skSend(ctx->socket, request_str, (int)len) == SOCKET_ERROR) {
|
}
|
||||||
err("couldn't send request to socket: %s", skGetErrorString());
|
|
||||||
goto 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;
|
||||||
char buffer[1024];
|
}
|
||||||
int read = 0;
|
|
||||||
do {
|
char buffer[1024];
|
||||||
read = skReceive(ctx->socket, buffer, sizeof(buffer));
|
int read = 0;
|
||||||
if(read == -1) {
|
do {
|
||||||
err("couldn't get the data from the server: %s", skGetErrorString());
|
read = skReceive(ctx->socket, buffer, sizeof(buffer));
|
||||||
goto error;
|
if(read == -1) {
|
||||||
}
|
err("couldn't get the data from the server: %s", skGetErrorString());
|
||||||
ostrAppendview(&received, strvInitLen(buffer, read));
|
goto error;
|
||||||
} while(read != 0);
|
}
|
||||||
|
ostrAppendview(&received, strvInitLen(buffer, read));
|
||||||
// if the data received is not null terminated
|
} while(read != 0);
|
||||||
if(*(received.buf + received.size) != '\0') {
|
|
||||||
ostrPutc(&received, '\0');
|
// if the data received is not null terminated
|
||||||
received.size--;
|
if(*(received.buf + received.size) != '\0') {
|
||||||
}
|
ostrPutc(&received, '\0');
|
||||||
|
received.size--;
|
||||||
resParse(&res, received.buf);
|
}
|
||||||
}
|
|
||||||
|
resParse(&res, received.buf);
|
||||||
if(!skClose(ctx->socket)) {
|
}
|
||||||
err("Couldn't close socket");
|
|
||||||
}
|
if(!skClose(ctx->socket)) {
|
||||||
|
err("Couldn't close socket");
|
||||||
error:
|
}
|
||||||
if(!skCleanup()) {
|
|
||||||
err("couldn't clean up sockets %s", skGetErrorString());
|
error:
|
||||||
}
|
if(!skCleanup()) {
|
||||||
skopen_error:
|
err("couldn't clean up sockets %s", skGetErrorString());
|
||||||
free(request_str);
|
}
|
||||||
ostrFree(&received);
|
skopen_error:
|
||||||
return res;
|
strFree(&req_str);
|
||||||
}
|
ostrFree(&received);
|
||||||
|
return res;
|
||||||
http_response_t httpGet(const char *hostname, const char *uri) {
|
}
|
||||||
http_request_t request = reqInit();
|
|
||||||
request.method = REQ_GET;
|
http_response_t httpGet(const char *hostname, const char *uri) {
|
||||||
reqSetUri(&request, uri);
|
http_request_t request = reqInit();
|
||||||
|
request.method = REQ_GET;
|
||||||
http_client_t client = hcliInit();
|
reqSetUri(&request, uri);
|
||||||
hcliSetHost(&client, hostname);
|
|
||||||
|
http_client_t client = hcliInit();
|
||||||
http_response_t res = hcliSendRequest(&client, &request);
|
hcliSetHost(&client, hostname);
|
||||||
|
|
||||||
reqFree(&request);
|
http_response_t res = hcliSendRequest(&client, &request);
|
||||||
hcliFree(&client);
|
|
||||||
|
reqFree(&request);
|
||||||
return res;
|
hcliFree(&client);
|
||||||
}
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
258
http.h
258
http.h
|
|
@ -1,128 +1,132 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "strstream.h"
|
#include "str.h"
|
||||||
#include "strview.h"
|
#include "strstream.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
REQ_GET,
|
REQ_GET,
|
||||||
REQ_POST,
|
REQ_POST,
|
||||||
REQ_HEAD,
|
REQ_HEAD,
|
||||||
REQ_PUT,
|
REQ_PUT,
|
||||||
REQ_DELETE
|
REQ_DELETE
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
// 2xx: success
|
// 2xx: success
|
||||||
STATUS_OK = 200,
|
STATUS_OK = 200,
|
||||||
STATUS_CREATED = 201,
|
STATUS_CREATED = 201,
|
||||||
STATUS_ACCEPTED = 202,
|
STATUS_ACCEPTED = 202,
|
||||||
STATUS_NO_CONTENT = 204,
|
STATUS_NO_CONTENT = 204,
|
||||||
STATUS_RESET_CONTENT = 205,
|
STATUS_RESET_CONTENT = 205,
|
||||||
STATUS_PARTIAL_CONTENT = 206,
|
STATUS_PARTIAL_CONTENT = 206,
|
||||||
|
|
||||||
// 3xx: redirection
|
// 3xx: redirection
|
||||||
STATUS_MULTIPLE_CHOICES = 300,
|
STATUS_MULTIPLE_CHOICES = 300,
|
||||||
STATUS_MOVED_PERMANENTLY = 301,
|
STATUS_MOVED_PERMANENTLY = 301,
|
||||||
STATUS_MOVED_TEMPORARILY = 302,
|
STATUS_MOVED_TEMPORARILY = 302,
|
||||||
STATUS_NOT_MODIFIED = 304,
|
STATUS_NOT_MODIFIED = 304,
|
||||||
|
|
||||||
// 4xx: client error
|
// 4xx: client error
|
||||||
STATUS_BAD_REQUEST = 400,
|
STATUS_BAD_REQUEST = 400,
|
||||||
STATUS_UNAUTHORIZED = 401,
|
STATUS_UNAUTHORIZED = 401,
|
||||||
STATUS_FORBIDDEN = 403,
|
STATUS_FORBIDDEN = 403,
|
||||||
STATUS_NOT_FOUND = 404,
|
STATUS_NOT_FOUND = 404,
|
||||||
STATUS_RANGE_NOT_SATISFIABLE = 407,
|
STATUS_RANGE_NOT_SATISFIABLE = 407,
|
||||||
|
|
||||||
// 5xx: server error
|
// 5xx: server error
|
||||||
STATUS_INTERNAL_SERVER_ERROR = 500,
|
STATUS_INTERNAL_SERVER_ERROR = 500,
|
||||||
STATUS_NOT_IMPLEMENTED = 501,
|
STATUS_NOT_IMPLEMENTED = 501,
|
||||||
STATUS_BAD_GATEWAY = 502,
|
STATUS_BAD_GATEWAY = 502,
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t major;
|
uint8_t major;
|
||||||
uint8_t minor;
|
uint8_t 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)
|
||||||
int httpVerNumber(http_version_t ver);
|
int httpVerNumber(http_version_t ver);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *key;
|
char *key;
|
||||||
char *value;
|
char *value;
|
||||||
} http_field_t;
|
} http_field_t;
|
||||||
|
|
||||||
// == HTTP REQUEST ============================================================
|
#define T http_field_t
|
||||||
|
#define VEC_SHORT_NAME field_vec
|
||||||
typedef struct {
|
#define VEC_DISABLE_ERASE_WHEN
|
||||||
int method;
|
#define VEC_NO_IMPLEMENTATION
|
||||||
http_version_t version;
|
#include "vec.h"
|
||||||
http_field_t *fields;
|
|
||||||
int field_count;
|
// == HTTP REQUEST ============================================================
|
||||||
char *uri;
|
|
||||||
char *body;
|
typedef struct {
|
||||||
} http_request_t;
|
int method;
|
||||||
|
http_version_t version;
|
||||||
http_request_t reqInit(void);
|
field_vec_t fields;
|
||||||
void reqFree(http_request_t *ctx);
|
char *uri;
|
||||||
|
char *body;
|
||||||
bool reqHasField(http_request_t *ctx, const char *key);
|
} http_request_t;
|
||||||
|
|
||||||
void reqSetField(http_request_t *ctx, const char *key, const char *value);
|
http_request_t reqInit(void);
|
||||||
void reqSetUri(http_request_t *ctx, const char *uri);
|
void reqFree(http_request_t *ctx);
|
||||||
|
|
||||||
str_ostream_t reqPrepare(http_request_t *ctx);
|
bool reqHasField(http_request_t *ctx, const char *key);
|
||||||
size_t reqString(http_request_t *ctx, char **str);
|
|
||||||
|
void reqSetField(http_request_t *ctx, const char *key, const char *value);
|
||||||
// == HTTP RESPONSE ===========================================================
|
void reqSetUri(http_request_t *ctx, const char *uri);
|
||||||
|
|
||||||
typedef struct {
|
str_ostream_t reqPrepare(http_request_t *ctx);
|
||||||
int status_code;
|
str_t reqString(http_request_t *ctx);
|
||||||
http_field_t *fields;
|
|
||||||
int field_count;
|
// == HTTP RESPONSE ===========================================================
|
||||||
http_version_t version;
|
|
||||||
char *body;
|
typedef struct {
|
||||||
} http_response_t;
|
int status_code;
|
||||||
|
field_vec_t fields;
|
||||||
http_response_t resInit(void);
|
http_version_t version;
|
||||||
void resFree(http_response_t *ctx);
|
char *body;
|
||||||
|
} http_response_t;
|
||||||
bool resHasField(http_response_t *ctx, const char *key);
|
|
||||||
const char *resGetField(http_response_t *ctx, const char *field);
|
http_response_t resInit(void);
|
||||||
|
void resFree(http_response_t *ctx);
|
||||||
void resParse(http_response_t *ctx, const char *data);
|
|
||||||
void resParseFields(http_response_t *ctx, str_istream_t *in);
|
bool resHasField(http_response_t *ctx, const char *key);
|
||||||
|
const char *resGetField(http_response_t *ctx, const char *field);
|
||||||
// == HTTP CLIENT =============================================================
|
|
||||||
|
void resParse(http_response_t *ctx, const char *data);
|
||||||
typedef struct {
|
void resParseFields(http_response_t *ctx, str_istream_t *in);
|
||||||
char *host_name;
|
|
||||||
uint16_t port;
|
// == HTTP CLIENT =============================================================
|
||||||
socket_t socket;
|
|
||||||
} http_client_t;
|
typedef struct {
|
||||||
|
char *host_name;
|
||||||
http_client_t hcliInit(void);
|
uint16_t port;
|
||||||
void hcliFree(http_client_t *ctx);
|
socket_t socket;
|
||||||
|
} http_client_t;
|
||||||
void hcliSetHost(http_client_t *ctx, const char *hostname);
|
|
||||||
http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *request);
|
http_client_t hcliInit(void);
|
||||||
|
void hcliFree(http_client_t *ctx);
|
||||||
// == HTTP ====================================================================
|
|
||||||
|
void hcliSetHost(http_client_t *ctx, const char *hostname);
|
||||||
http_response_t httpGet(const char *hostname, const char *uri);
|
http_response_t hcliSendRequest(http_client_t *ctx, http_request_t *request);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
// == HTTP ====================================================================
|
||||||
} // extern "C"
|
|
||||||
|
http_response_t httpGet(const char *hostname, const char *uri);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
132
os.c
132
os.c
|
|
@ -1,67 +1,67 @@
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define _BUFSZ 128
|
#define _BUFSZ 128
|
||||||
|
|
||||||
// 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) {
|
ssize_t 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) {
|
||||||
*bufsz = _BUFSZ;
|
*bufsz = _BUFSZ;
|
||||||
if((*buf = malloc(*bufsz)) == NULL) {
|
if((*buf = malloc(*bufsz)) == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t result = -1;
|
ssize_t 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);
|
||||||
|
|
||||||
for(ptr = *buf, eptr = *buf + *bufsz;;) {
|
for(ptr = *buf, eptr = *buf + *bufsz;;) {
|
||||||
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);
|
ssize_t diff = (ssize_t)(ptr - *buf);
|
||||||
if(diff != 0) {
|
if(diff != 0) {
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
result = diff;
|
result = diff;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*ptr++ = c;
|
*ptr++ = (char)c;
|
||||||
if(c == delimiter) {
|
if(c == delimiter) {
|
||||||
*ptr = '\0';
|
*ptr = '\0';
|
||||||
result = ptr - *buf;
|
result = ptr - *buf;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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;
|
ssize_t d = ptr - *buf;
|
||||||
if((nbuf = realloc(*buf, nbufsz)) == NULL) {
|
if((nbuf = realloc(*buf, nbufsz)) == NULL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*buf = nbuf;
|
*buf = nbuf;
|
||||||
*bufsz = nbufsz;
|
*bufsz = nbufsz;
|
||||||
eptr = nbuf + nbufsz;
|
eptr = nbuf + nbufsz;
|
||||||
ptr = nbuf + d;
|
ptr = nbuf + d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_unlock_file(fp);
|
_unlock_file(fp);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) {
|
ssize_t getline(char **line_ptr, size_t *n, FILE *stream) {
|
||||||
return getdelim(line_ptr, n, '\n', stream);
|
return getdelim(line_ptr, n, '\n', stream);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
50
os.h
50
os.h
|
|
@ -1,26 +1,26 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <BaseTsd.h> // SSIZE_T
|
#include <BaseTsd.h> // SSIZE_T
|
||||||
typedef SSIZE_T ssize_t;
|
typedef SSIZE_T ssize_t;
|
||||||
ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp);
|
ssize_t getdelim(char **buf, size_t *bufsz, int delimiter, FILE *fp);
|
||||||
ssize_t 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
|
#define stricmp strcasecmp
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/types.h> // ssize_t
|
#include <sys/types.h> // ssize_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
5
slice.h
5
slice.h
|
|
@ -1,5 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stddef.h> // size_t
|
|
||||||
|
|
||||||
#define slice_t(type) struct { type buf; size_t len; }
|
|
||||||
674
socket.c
674
socket.c
|
|
@ -1,338 +1,338 @@
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "tracelog.h"
|
#include "tracelog.h"
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// VERY MUCH NOT THREAD SAFE
|
// VERY MUCH NOT THREAD SAFE
|
||||||
static int initialize_count = 0;
|
static int initialize_count = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SOCK_WINDOWS
|
#if SOCK_WINDOWS
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
static bool _win_skInit();
|
static bool _win_skInit();
|
||||||
static bool _win_skCleanup();
|
static bool _win_skCleanup();
|
||||||
static int _win_skGetError();
|
static int _win_skGetError();
|
||||||
static const char *_win_skGetErrorString();
|
static const char *_win_skGetErrorString();
|
||||||
|
|
||||||
#define SOCK_CALL(fun) _win_##fun
|
#define SOCK_CALL(fun) _win_##fun
|
||||||
|
|
||||||
#elif SOCK_POSIX
|
#elif SOCK_POSIX
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h> // strerror
|
#include <string.h> // strerror
|
||||||
|
|
||||||
#define INVALID_SOCKET (-1)
|
#define INVALID_SOCKET (-1)
|
||||||
#define SOCKET_ERROR (-1)
|
#define SOCKET_ERROR (-1)
|
||||||
|
|
||||||
static bool _posix_skInit();
|
static bool _posix_skInit();
|
||||||
static bool _posix_skCleanup();
|
static bool _posix_skCleanup();
|
||||||
static int _posix_skGetError();
|
static int _posix_skGetError();
|
||||||
static const char *_posix_skGetErrorString();
|
static const char *_posix_skGetErrorString();
|
||||||
|
|
||||||
#define SOCK_CALL(fun) _posix_##fun
|
#define SOCK_CALL(fun) _posix_##fun
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool skInit() {
|
bool skInit() {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
++initialize_count;
|
++initialize_count;
|
||||||
#endif
|
#endif
|
||||||
return SOCK_CALL(skInit());
|
return SOCK_CALL(skInit());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skCleanup() {
|
bool skCleanup() {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
--initialize_count;
|
--initialize_count;
|
||||||
#endif
|
#endif
|
||||||
return SOCK_CALL(skCleanup());
|
return SOCK_CALL(skCleanup());
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t skOpen(skType type) {
|
socket_t skOpen(skType type) {
|
||||||
int sock_type;
|
int sock_type = 0;
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case SOCK_TCP: sock_type = SOCK_STREAM; break;
|
case SOCK_TCP: sock_type = SOCK_STREAM; break;
|
||||||
case SOCK_UDP: sock_type = SOCK_DGRAM; break;
|
case SOCK_UDP: sock_type = SOCK_DGRAM; break;
|
||||||
default: fatal("skType not recognized: %d", type); break;
|
default: fatal("skType not recognized: %d", type); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return skOpenPro(AF_INET, sock_type, 0);
|
return skOpenPro(AF_INET, sock_type, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t skOpenEx(const char *protocol) {
|
socket_t skOpenEx(const char *protocol) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if(initialize_count <= 0) {
|
if(initialize_count <= 0) {
|
||||||
fatal("skInit has not been called");
|
fatal("skInit has not been called");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
struct protoent *proto = getprotobyname(protocol);
|
struct protoent *proto = getprotobyname(protocol);
|
||||||
if(!proto) {
|
if(!proto) {
|
||||||
return INVALID_SOCKET;
|
return INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
return skOpenPro(AF_INET, SOCK_STREAM, proto->p_proto);
|
return skOpenPro(AF_INET, SOCK_STREAM, proto->p_proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t skOpenPro(int af, int type, int protocol) {
|
socket_t skOpenPro(int af, int type, int protocol) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if(initialize_count <= 0) {
|
if(initialize_count <= 0) {
|
||||||
fatal("skInit has not been called");
|
fatal("skInit has not been called");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return socket(af, type, protocol);
|
return socket(af, type, protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_addrin_t skAddrinInit(const char *ip, uint16_t port) {
|
sk_addrin_t skAddrinInit(const char *ip, uint16_t port) {
|
||||||
sk_addrin_t addr;
|
sk_addrin_t addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
addr.sin_addr.s_addr = inet_addr(ip);
|
addr.sin_addr.s_addr = inet_addr(ip);
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skClose(socket_t sock) {
|
bool skClose(socket_t sock) {
|
||||||
#if SOCK_WINDOWS
|
#if SOCK_WINDOWS
|
||||||
int error = closesocket(sock);
|
int error = closesocket(sock);
|
||||||
#elif SOCK_POSIX
|
#elif SOCK_POSIX
|
||||||
int error = close(sock);
|
int error = close(sock);
|
||||||
#endif
|
#endif
|
||||||
sock = INVALID_SOCKET;
|
sock = INVALID_SOCKET;
|
||||||
return error != SOCKET_ERROR;
|
return error != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skBind(socket_t sock, const char *ip, uint16_t port) {
|
bool skBind(socket_t sock, const char *ip, uint16_t port) {
|
||||||
sk_addrin_t addr;
|
sk_addrin_t addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
// TODO use inet_pton instead
|
// TODO use inet_pton instead
|
||||||
addr.sin_addr.s_addr = inet_addr(ip);
|
addr.sin_addr.s_addr = inet_addr(ip);
|
||||||
|
|
||||||
addr.sin_port = htons(port);
|
addr.sin_port = htons(port);
|
||||||
|
|
||||||
return skBindPro(sock, (sk_addr_t *) &addr, sizeof(addr));
|
return skBindPro(sock, (sk_addr_t *) &addr, sizeof(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skBindPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen) {
|
bool skBindPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen) {
|
||||||
return bind(sock, name, namelen) != SOCKET_ERROR;
|
return bind(sock, name, namelen) != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skListen(socket_t sock) {
|
bool skListen(socket_t sock) {
|
||||||
return skListenPro(sock, 1);
|
return skListenPro(sock, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skListenPro(socket_t sock, int backlog) {
|
bool skListenPro(socket_t sock, int backlog) {
|
||||||
return listen(sock, backlog) != SOCKET_ERROR;
|
return listen(sock, backlog) != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t skAccept(socket_t sock) {
|
socket_t skAccept(socket_t sock) {
|
||||||
sk_addrin_t addr;
|
sk_addrin_t addr;
|
||||||
socket_len_t addr_size = (socket_len_t)sizeof(addr);
|
socket_len_t addr_size = (socket_len_t)sizeof(addr);
|
||||||
return skAcceptPro(sock, (sk_addr_t *) &addr, &addr_size);
|
return skAcceptPro(sock, (sk_addr_t *) &addr, &addr_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
socket_t skAcceptPro(socket_t sock, sk_addr_t *addr, socket_len_t *addrlen) {
|
socket_t skAcceptPro(socket_t sock, sk_addr_t *addr, socket_len_t *addrlen) {
|
||||||
return accept(sock, addr, addrlen);
|
return accept(sock, addr, addrlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skConnect(socket_t sock, const char *server, unsigned short server_port) {
|
bool skConnect(socket_t sock, const char *server, unsigned short server_port) {
|
||||||
// TODO use getaddrinfo insetad
|
// TODO use getaddrinfo insetad
|
||||||
struct hostent *host = gethostbyname(server);
|
struct hostent *host = gethostbyname(server);
|
||||||
// if gethostbyname fails, inet_addr will also fail and return an easier to debug error
|
// if gethostbyname fails, inet_addr will also fail and return an easier to debug error
|
||||||
const char *address = server;
|
const char *address = server;
|
||||||
if(host) {
|
if(host) {
|
||||||
address = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
|
address = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_addrin_t addr;
|
sk_addrin_t addr;
|
||||||
addr.sin_family = AF_INET;
|
addr.sin_family = AF_INET;
|
||||||
addr.sin_addr.s_addr = inet_addr(address);
|
addr.sin_addr.s_addr = inet_addr(address);
|
||||||
addr.sin_port = htons(server_port);
|
addr.sin_port = htons(server_port);
|
||||||
|
|
||||||
return skConnectPro(sock, (sk_addr_t *) &addr, sizeof(addr));
|
return skConnectPro(sock, (sk_addr_t *) &addr, sizeof(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skConnectPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen) {
|
bool skConnectPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen) {
|
||||||
return connect(sock, name, namelen) != SOCKET_ERROR;
|
return connect(sock, name, namelen) != SOCKET_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int skSend(socket_t sock, const char *buf, int len) {
|
int skSend(socket_t sock, const char *buf, int len) {
|
||||||
return skSendPro(sock, buf, len, 0);
|
return skSendPro(sock, buf, len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int skSendPro(socket_t sock, const char *buf, int len, int flags) {
|
int skSendPro(socket_t sock, const char *buf, int len, int flags) {
|
||||||
return send(sock, buf, len, flags);
|
return send(sock, buf, len, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int skSendTo(socket_t sock, const char *buf, int len, const sk_addrin_t *to) {
|
int skSendTo(socket_t sock, const char *buf, int len, const sk_addrin_t *to) {
|
||||||
return skSendToPro(sock, buf, len, 0, (sk_addr_t*) to, sizeof(sk_addrin_t));
|
return skSendToPro(sock, buf, len, 0, (sk_addr_t*) to, sizeof(sk_addrin_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
int skSendToPro(socket_t sock, const char *buf, int len, int flags, const sk_addr_t *to, int tolen) {
|
int skSendToPro(socket_t sock, const char *buf, int len, int flags, const sk_addr_t *to, int tolen) {
|
||||||
return sendto(sock, buf, len, flags, to, tolen);
|
return sendto(sock, buf, len, flags, to, tolen);
|
||||||
}
|
}
|
||||||
|
|
||||||
int skReceive(socket_t sock, char *buf, int len) {
|
int skReceive(socket_t sock, char *buf, int len) {
|
||||||
return skReceivePro(sock, buf, len, 0);
|
return skReceivePro(sock, buf, len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int skReceivePro(socket_t sock, char *buf, int len, int flags) {
|
int skReceivePro(socket_t sock, char *buf, int len, int flags) {
|
||||||
return recv(sock, buf, len, flags);
|
return recv(sock, buf, len, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int skReceiveFrom(socket_t sock, char *buf, int len, sk_addrin_t *from) {
|
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);
|
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);
|
return recvfrom(sock, buf, len, flags, from, fromlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool skIsValid(socket_t sock) {
|
bool skIsValid(socket_t sock) {
|
||||||
return sock != INVALID_SOCKET;
|
return sock != INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
int skGetError() {
|
int skGetError() {
|
||||||
return SOCK_CALL(skGetError());
|
return SOCK_CALL(skGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *skGetErrorString() {
|
const char *skGetErrorString() {
|
||||||
return SOCK_CALL(skGetErrorString());
|
return SOCK_CALL(skGetErrorString());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SOCK_WINDOWS
|
#ifdef SOCK_WINDOWS
|
||||||
static bool _win_skInit() {
|
static bool _win_skInit() {
|
||||||
WSADATA w;
|
WSADATA w;
|
||||||
int error = WSAStartup(0x0202, &w);
|
int error = WSAStartup(0x0202, &w);
|
||||||
return error == 0;
|
return error == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _win_skCleanup() {
|
static bool _win_skCleanup() {
|
||||||
return WSACleanup() == 0;
|
return WSACleanup() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _win_skGetError() {
|
static int _win_skGetError() {
|
||||||
return WSAGetLastError();
|
return WSAGetLastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *_win_skGetErrorString() {
|
static const char *_win_skGetErrorString() {
|
||||||
switch(_win_skGetError()) {
|
switch(_win_skGetError()) {
|
||||||
case WSA_INVALID_HANDLE: return "Specified event object handle is invalid.";
|
case WSA_INVALID_HANDLE: return "Specified event object handle is invalid.";
|
||||||
case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available.";
|
case WSA_NOT_ENOUGH_MEMORY: return "Insufficient memory available.";
|
||||||
case WSA_INVALID_PARAMETER: return "One or more parameters are invalid.";
|
case WSA_INVALID_PARAMETER: return "One or more parameters are invalid.";
|
||||||
case WSA_OPERATION_ABORTED: return "Overlapped operation aborted.";
|
case WSA_OPERATION_ABORTED: return "Overlapped operation aborted.";
|
||||||
case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state.";
|
case WSA_IO_INCOMPLETE: return "Overlapped I/O event object not in signaled state.";
|
||||||
case WSA_IO_PENDING: return "Overlapped operations will complete later.";
|
case WSA_IO_PENDING: return "Overlapped operations will complete later.";
|
||||||
case WSAEINTR: return "Interrupted function call.";
|
case WSAEINTR: return "Interrupted function call.";
|
||||||
case WSAEBADF: return "File handle is not valid.";
|
case WSAEBADF: return "File handle is not valid.";
|
||||||
case WSAEACCES: return "Permission denied.";
|
case WSAEACCES: return "Permission denied.";
|
||||||
case WSAEFAULT: return "Bad address.";
|
case WSAEFAULT: return "Bad address.";
|
||||||
case WSAEINVAL: return "Invalid argument.";
|
case WSAEINVAL: return "Invalid argument.";
|
||||||
case WSAEMFILE: return "Too many open files.";
|
case WSAEMFILE: return "Too many open files.";
|
||||||
case WSAEWOULDBLOCK: return "Resource temporarily unavailable.";
|
case WSAEWOULDBLOCK: return "Resource temporarily unavailable.";
|
||||||
case WSAEINPROGRESS: return "Operation now in progress.";
|
case WSAEINPROGRESS: return "Operation now in progress.";
|
||||||
case WSAEALREADY: return "Operation already in progress.";
|
case WSAEALREADY: return "Operation already in progress.";
|
||||||
case WSAENOTSOCK: return "Socket operation on nonsocket.";
|
case WSAENOTSOCK: return "Socket operation on nonsocket.";
|
||||||
case WSAEDESTADDRREQ: return "Destination address required.";
|
case WSAEDESTADDRREQ: return "Destination address required.";
|
||||||
case WSAEMSGSIZE: return "Message too long.";
|
case WSAEMSGSIZE: return "Message too long.";
|
||||||
case WSAEPROTOTYPE: return "Protocol wrong type for socket.";
|
case WSAEPROTOTYPE: return "Protocol wrong type for socket.";
|
||||||
case WSAENOPROTOOPT: return "Bad protocol option.";
|
case WSAENOPROTOOPT: return "Bad protocol option.";
|
||||||
case WSAEPROTONOSUPPORT: return "Protocol not supported.";
|
case WSAEPROTONOSUPPORT: return "Protocol not supported.";
|
||||||
case WSAESOCKTNOSUPPORT: return "Socket type not supported.";
|
case WSAESOCKTNOSUPPORT: return "Socket type not supported.";
|
||||||
case WSAEOPNOTSUPP: return "Operation not supported.";
|
case WSAEOPNOTSUPP: return "Operation not supported.";
|
||||||
case WSAEPFNOSUPPORT: return "Protocol family not supported.";
|
case WSAEPFNOSUPPORT: return "Protocol family not supported.";
|
||||||
case WSAEAFNOSUPPORT: return "Address family not supported by protocol family.";
|
case WSAEAFNOSUPPORT: return "Address family not supported by protocol family.";
|
||||||
case WSAEADDRINUSE: return "Address already in use.";
|
case WSAEADDRINUSE: return "Address already in use.";
|
||||||
case WSAEADDRNOTAVAIL: return "Cannot assign requested address.";
|
case WSAEADDRNOTAVAIL: return "Cannot assign requested address.";
|
||||||
case WSAENETDOWN: return "Network is down.";
|
case WSAENETDOWN: return "Network is down.";
|
||||||
case WSAENETUNREACH: return "Network is unreachable.";
|
case WSAENETUNREACH: return "Network is unreachable.";
|
||||||
case WSAENETRESET: return "Network dropped connection on reset.";
|
case WSAENETRESET: return "Network dropped connection on reset.";
|
||||||
case WSAECONNABORTED: return "Software caused connection abort.";
|
case WSAECONNABORTED: return "Software caused connection abort.";
|
||||||
case WSAECONNRESET: return "Connection reset by peer.";
|
case WSAECONNRESET: return "Connection reset by peer.";
|
||||||
case WSAENOBUFS: return "No buffer space available.";
|
case WSAENOBUFS: return "No buffer space available.";
|
||||||
case WSAEISCONN: return "Socket is already connected.";
|
case WSAEISCONN: return "Socket is already connected.";
|
||||||
case WSAENOTCONN: return "Socket is not connected.";
|
case WSAENOTCONN: return "Socket is not connected.";
|
||||||
case WSAESHUTDOWN: return "Cannot send after socket shutdown.";
|
case WSAESHUTDOWN: return "Cannot send after socket shutdown.";
|
||||||
case WSAETOOMANYREFS: return "Too many references.";
|
case WSAETOOMANYREFS: return "Too many references.";
|
||||||
case WSAETIMEDOUT: return "Connection timed out.";
|
case WSAETIMEDOUT: return "Connection timed out.";
|
||||||
case WSAECONNREFUSED: return "Connection refused.";
|
case WSAECONNREFUSED: return "Connection refused.";
|
||||||
case WSAELOOP: return "Cannot translate name.";
|
case WSAELOOP: return "Cannot translate name.";
|
||||||
case WSAENAMETOOLONG: return "Name too long.";
|
case WSAENAMETOOLONG: return "Name too long.";
|
||||||
case WSAEHOSTDOWN: return "Host is down.";
|
case WSAEHOSTDOWN: return "Host is down.";
|
||||||
case WSAEHOSTUNREACH: return "No route to host.";
|
case WSAEHOSTUNREACH: return "No route to host.";
|
||||||
case WSAENOTEMPTY: return "Directory not empty.";
|
case WSAENOTEMPTY: return "Directory not empty.";
|
||||||
case WSAEPROCLIM: return "Too many processes.";
|
case WSAEPROCLIM: return "Too many processes.";
|
||||||
case WSAEUSERS: return "User quota exceeded.";
|
case WSAEUSERS: return "User quota exceeded.";
|
||||||
case WSAEDQUOT: return "Disk quota exceeded.";
|
case WSAEDQUOT: return "Disk quota exceeded.";
|
||||||
case WSAESTALE: return "Stale file handle reference.";
|
case WSAESTALE: return "Stale file handle reference.";
|
||||||
case WSAEREMOTE: return "Item is remote.";
|
case WSAEREMOTE: return "Item is remote.";
|
||||||
case WSASYSNOTREADY: return "Network subsystem is unavailable.";
|
case WSASYSNOTREADY: return "Network subsystem is unavailable.";
|
||||||
case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range.";
|
case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range.";
|
||||||
case WSANOTINITIALISED: return "Successful WSAStartup not yet performed.";
|
case WSANOTINITIALISED: return "Successful WSAStartup not yet performed.";
|
||||||
case WSAEDISCON: return "Graceful shutdown in progress.";
|
case WSAEDISCON: return "Graceful shutdown in progress.";
|
||||||
case WSAENOMORE: return "No more results.";
|
case WSAENOMORE: return "No more results.";
|
||||||
case WSAECANCELLED: return "Call has been canceled.";
|
case WSAECANCELLED: return "Call has been canceled.";
|
||||||
case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid.";
|
case WSAEINVALIDPROCTABLE: return "Procedure call table is invalid.";
|
||||||
case WSAEINVALIDPROVIDER: return "Service provider is invalid.";
|
case WSAEINVALIDPROVIDER: return "Service provider is invalid.";
|
||||||
case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize.";
|
case WSAEPROVIDERFAILEDINIT: return "Service provider failed to initialize.";
|
||||||
case WSASYSCALLFAILURE: return "System call failure.";
|
case WSASYSCALLFAILURE: return "System call failure.";
|
||||||
case WSASERVICE_NOT_FOUND: return "Service not found.";
|
case WSASERVICE_NOT_FOUND: return "Service not found.";
|
||||||
case WSATYPE_NOT_FOUND: return "Class type not found.";
|
case WSATYPE_NOT_FOUND: return "Class type not found.";
|
||||||
case WSA_E_NO_MORE: return "No more results.";
|
case WSA_E_NO_MORE: return "No more results.";
|
||||||
case WSA_E_CANCELLED: return "Call was canceled.";
|
case WSA_E_CANCELLED: return "Call was canceled.";
|
||||||
case WSAEREFUSED: return "Database query was refused.";
|
case WSAEREFUSED: return "Database query was refused.";
|
||||||
case WSAHOST_NOT_FOUND: return "Host not found.";
|
case WSAHOST_NOT_FOUND: return "Host not found.";
|
||||||
case WSATRY_AGAIN: return "Nonauthoritative host not found.";
|
case WSATRY_AGAIN: return "Nonauthoritative host not found.";
|
||||||
case WSANO_RECOVERY: return "This is a nonrecoverable error.";
|
case WSANO_RECOVERY: return "This is a nonrecoverable error.";
|
||||||
case WSANO_DATA: return "Valid name, no data record of requested type.";
|
case WSANO_DATA: return "Valid name, no data record of requested type.";
|
||||||
case WSA_QOS_RECEIVERS: return "QoS receivers.";
|
case WSA_QOS_RECEIVERS: return "QoS receivers.";
|
||||||
case WSA_QOS_SENDERS: return "QoS senders.";
|
case WSA_QOS_SENDERS: return "QoS senders.";
|
||||||
case WSA_QOS_NO_SENDERS: return "No QoS senders.";
|
case WSA_QOS_NO_SENDERS: return "No QoS senders.";
|
||||||
case WSA_QOS_NO_RECEIVERS: return "QoS no receivers.";
|
case WSA_QOS_NO_RECEIVERS: return "QoS no receivers.";
|
||||||
case WSA_QOS_REQUEST_CONFIRMED: return "QoS request confirmed.";
|
case WSA_QOS_REQUEST_CONFIRMED: return "QoS request confirmed.";
|
||||||
case WSA_QOS_ADMISSION_FAILURE: return "QoS admission error.";
|
case WSA_QOS_ADMISSION_FAILURE: return "QoS admission error.";
|
||||||
case WSA_QOS_POLICY_FAILURE: return "QoS policy failure.";
|
case WSA_QOS_POLICY_FAILURE: return "QoS policy failure.";
|
||||||
case WSA_QOS_BAD_STYLE: return "QoS bad style.";
|
case WSA_QOS_BAD_STYLE: return "QoS bad style.";
|
||||||
case WSA_QOS_BAD_OBJECT: return "QoS bad object.";
|
case WSA_QOS_BAD_OBJECT: return "QoS bad object.";
|
||||||
case WSA_QOS_TRAFFIC_CTRL_ERROR: return "QoS traffic control error.";
|
case WSA_QOS_TRAFFIC_CTRL_ERROR: return "QoS traffic control error.";
|
||||||
case WSA_QOS_GENERIC_ERROR: return "QoS generic error.";
|
case WSA_QOS_GENERIC_ERROR: return "QoS generic error.";
|
||||||
case WSA_QOS_ESERVICETYPE: return "QoS service type error.";
|
case WSA_QOS_ESERVICETYPE: return "QoS service type error.";
|
||||||
case WSA_QOS_EFLOWSPEC: return "QoS flowspec error.";
|
case WSA_QOS_EFLOWSPEC: return "QoS flowspec error.";
|
||||||
case WSA_QOS_EPROVSPECBUF: return "Invalid QoS provider buffer.";
|
case WSA_QOS_EPROVSPECBUF: return "Invalid QoS provider buffer.";
|
||||||
case WSA_QOS_EFILTERSTYLE: return "Invalid QoS filter style.";
|
case WSA_QOS_EFILTERSTYLE: return "Invalid QoS filter style.";
|
||||||
case WSA_QOS_EFILTERTYPE: return "Invalid QoS filter type.";
|
case WSA_QOS_EFILTERTYPE: return "Invalid QoS filter type.";
|
||||||
case WSA_QOS_EFILTERCOUNT: return "Incorrect QoS filter count.";
|
case WSA_QOS_EFILTERCOUNT: return "Incorrect QoS filter count.";
|
||||||
case WSA_QOS_EOBJLENGTH: return "Invalid QoS object length.";
|
case WSA_QOS_EOBJLENGTH: return "Invalid QoS object length.";
|
||||||
case WSA_QOS_EFLOWCOUNT: return "Incorrect QoS flow count.";
|
case WSA_QOS_EFLOWCOUNT: return "Incorrect QoS flow count.";
|
||||||
case WSA_QOS_EUNKOWNPSOBJ: return "Unrecognized QoS object.";
|
case WSA_QOS_EUNKOWNPSOBJ: return "Unrecognized QoS object.";
|
||||||
case WSA_QOS_EPOLICYOBJ: return "Invalid QoS policy object.";
|
case WSA_QOS_EPOLICYOBJ: return "Invalid QoS policy object.";
|
||||||
case WSA_QOS_EFLOWDESC: return "Invalid QoS flow descriptor.";
|
case WSA_QOS_EFLOWDESC: return "Invalid QoS flow descriptor.";
|
||||||
case WSA_QOS_EPSFLOWSPEC: return "Invalid QoS provider-specific flowspec.";
|
case WSA_QOS_EPSFLOWSPEC: return "Invalid QoS provider-specific flowspec.";
|
||||||
case WSA_QOS_EPSFILTERSPEC: return "Invalid QoS provider-specific filterspec.";
|
case WSA_QOS_EPSFILTERSPEC: return "Invalid QoS provider-specific filterspec.";
|
||||||
case WSA_QOS_ESDMODEOBJ: return "Invalid QoS shape discard mode object.";
|
case WSA_QOS_ESDMODEOBJ: return "Invalid QoS shape discard mode object.";
|
||||||
case WSA_QOS_ESHAPERATEOBJ: return "Invalid QoS shaping rate object.";
|
case WSA_QOS_ESHAPERATEOBJ: return "Invalid QoS shaping rate object.";
|
||||||
case WSA_QOS_RESERVED_PETYPE: return "Reserved policy QoS element type.";
|
case WSA_QOS_RESERVED_PETYPE: return "Reserved policy QoS element type.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "(nothing)";
|
return "(nothing)";
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static bool _posix_skInit() {
|
static bool _posix_skInit() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _posix_skCleanup() {
|
static bool _posix_skCleanup() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _posix_skGetError() {
|
static int _posix_skGetError() {
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *_posix_skGetErrorString() {
|
static const char *_posix_skGetErrorString() {
|
||||||
return strerror(errno);
|
return strerror(errno);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
228
socket.h
228
socket.h
|
|
@ -1,115 +1,115 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define SOCK_WINDOWS 1
|
#define SOCK_WINDOWS 1
|
||||||
#else
|
#else
|
||||||
#define SOCK_POSIX 1
|
#define SOCK_POSIX 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if SOCK_WINDOWS
|
#if SOCK_WINDOWS
|
||||||
#pragma warning(disable:4996) // _WINSOCK_DEPRECATED_NO_WARNINGS
|
#pragma warning(disable:4996) // _WINSOCK_DEPRECATED_NO_WARNINGS
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
typedef SOCKET socket_t;
|
typedef SOCKET socket_t;
|
||||||
typedef int socket_len_t;
|
typedef int socket_len_t;
|
||||||
#elif SOCK_POSIX
|
#elif SOCK_POSIX
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
typedef int socket_t;
|
typedef int socket_t;
|
||||||
typedef uint32_t socket_len_t;
|
typedef uint32_t socket_len_t;
|
||||||
#define INVALID_SOCKET (-1)
|
#define INVALID_SOCKET (-1)
|
||||||
#define SOCKET_ERROR (-1)
|
#define SOCKET_ERROR (-1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct sockaddr sk_addr_t;
|
typedef struct sockaddr sk_addr_t;
|
||||||
typedef struct sockaddr_in sk_addrin_t;
|
typedef struct sockaddr_in sk_addrin_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SOCK_TCP,
|
SOCK_TCP,
|
||||||
SOCK_UDP,
|
SOCK_UDP,
|
||||||
} skType;
|
} skType;
|
||||||
|
|
||||||
// == RAW SOCKETS ==========================================
|
// == RAW SOCKETS ==========================================
|
||||||
|
|
||||||
// Initialize sockets, returns true on success
|
// Initialize sockets, returns true on success
|
||||||
bool skInit(void);
|
bool skInit(void);
|
||||||
// Terminates sockets, returns true on success
|
// Terminates sockets, returns true on success
|
||||||
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 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
|
||||||
socket_t skOpenEx(const char *protocol);
|
socket_t skOpenEx(const char *protocol);
|
||||||
// Opens a socket, check socket_t with skValid
|
// Opens a socket, check socket_t with skValid
|
||||||
socket_t skOpenPro(int af, int type, int protocol);
|
socket_t skOpenPro(int af, int type, int protocol);
|
||||||
|
|
||||||
// Fill out a sk_addrin_t structure with "ip" and "port"
|
// Fill out a sk_addrin_t structure with "ip" and "port"
|
||||||
sk_addrin_t skAddrinInit(const char *ip, uint16_t port);
|
sk_addrin_t skAddrinInit(const char *ip, uint16_t port);
|
||||||
|
|
||||||
// Closes a socket, returns true on success
|
// Closes a socket, returns true on success
|
||||||
bool skClose(socket_t sock);
|
bool skClose(socket_t sock);
|
||||||
|
|
||||||
// Associate a local address with a socket
|
// Associate a local address with a socket
|
||||||
bool skBind(socket_t sock, const char *ip, uint16_t port);
|
bool skBind(socket_t sock, const char *ip, uint16_t port);
|
||||||
// Associate a local address with a socket
|
// Associate a local address with a socket
|
||||||
bool skBindPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen);
|
bool skBindPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen);
|
||||||
|
|
||||||
// Place a socket in a state in which it is listening for an incoming connection
|
// Place a socket in a state in which it is listening for an incoming connection
|
||||||
bool skListen(socket_t sock);
|
bool skListen(socket_t sock);
|
||||||
// Place a socket in a state in which it is listening for an incoming connection
|
// Place a socket in a state in which it is listening for an incoming connection
|
||||||
bool skListenPro(socket_t sock, int backlog);
|
bool skListenPro(socket_t sock, int backlog);
|
||||||
|
|
||||||
// Permits an incoming connection attempt on a socket
|
// Permits an incoming connection attempt on a socket
|
||||||
socket_t skAccept(socket_t sock);
|
socket_t skAccept(socket_t sock);
|
||||||
// Permits an incoming connection attempt on a socket
|
// Permits an incoming connection attempt on a socket
|
||||||
socket_t skAcceptPro(socket_t sock, sk_addr_t *addr, socket_len_t *addrlen);
|
socket_t skAcceptPro(socket_t sock, sk_addr_t *addr, socket_len_t *addrlen);
|
||||||
|
|
||||||
// Connects to a server (e.g. "127.0.0.1" or "google.com") with a port(e.g. 1234), returns true on success
|
// Connects to a server (e.g. "127.0.0.1" or "google.com") with a port(e.g. 1234), returns true on success
|
||||||
bool skConnect(socket_t sock, const char *server, unsigned short server_port);
|
bool skConnect(socket_t sock, const char *server, unsigned short server_port);
|
||||||
// Connects to a server, returns true on success
|
// Connects to a server, returns true on success
|
||||||
bool skConnectPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen);
|
bool skConnectPro(socket_t sock, const sk_addr_t *name, socket_len_t namelen);
|
||||||
|
|
||||||
// Sends data on a socket, returns true on success
|
// Sends data on a socket, returns true on success
|
||||||
int skSend(socket_t sock, const char *buf, int len);
|
int skSend(socket_t sock, const char *buf, int len);
|
||||||
// Sends data on a socket, returns true on success
|
// Sends data on a socket, returns true on success
|
||||||
int skSendPro(socket_t sock, const char *buf, int len, int flags);
|
int skSendPro(socket_t sock, const char *buf, int len, int flags);
|
||||||
// Sends data to a specific destination
|
// Sends data to a specific destination
|
||||||
int skSendTo(socket_t sock, const char *buf, int len, const sk_addrin_t *to);
|
int skSendTo(socket_t sock, const char *buf, int len, const sk_addrin_t *to);
|
||||||
// Sends data to a specific destination
|
// Sends data to a specific destination
|
||||||
int skSendToPro(socket_t sock, const char *buf, int len, int flags, const sk_addr_t *to, int tolen);
|
int skSendToPro(socket_t sock, const char *buf, int len, int flags, const sk_addr_t *to, int tolen);
|
||||||
// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
|
// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
|
||||||
int skReceive(socket_t sock, char *buf, int len);
|
int skReceive(socket_t sock, char *buf, int len);
|
||||||
// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
|
// Receives data from a socket, returns byte count on success, 0 on connection close or -1 on error
|
||||||
int skReceivePro(socket_t sock, char *buf, int len, int flags);
|
int skReceivePro(socket_t sock, char *buf, int len, int flags);
|
||||||
// Receives a datagram and stores the source address.
|
// Receives a datagram and stores the source address.
|
||||||
int skReceiveFrom(socket_t sock, char *buf, int len, sk_addrin_t *from);
|
int skReceiveFrom(socket_t sock, char *buf, int len, sk_addrin_t *from);
|
||||||
// Receives a datagram and stores the source address.
|
// 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
|
// Checks that a opened socket is valid, returns true on success
|
||||||
bool skIsValid(socket_t sock);
|
bool skIsValid(socket_t sock);
|
||||||
|
|
||||||
// Returns latest socket error, returns 0 if there is no error
|
// Returns latest socket error, returns 0 if there is no error
|
||||||
int skGetError(void);
|
int skGetError(void);
|
||||||
// Returns a human-readable string from a skGetError
|
// Returns a human-readable string from a skGetError
|
||||||
const char *skGetErrorString(void);
|
const char *skGetErrorString(void);
|
||||||
|
|
||||||
// == UDP SOCKETS ==========================================
|
// == UDP SOCKETS ==========================================
|
||||||
|
|
||||||
typedef socket_t udpsock_t;
|
typedef socket_t udpsock_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
885
str.c
885
str.c
|
|
@ -1,267 +1,620 @@
|
||||||
#include "str.h"
|
#include "str.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <limits.h>
|
||||||
|
#include <ctype.h>
|
||||||
str_t strInit(void) {
|
#include <assert.h>
|
||||||
return (str_t) {
|
#include <stdio.h>
|
||||||
.buf = NULL,
|
|
||||||
.len = 0
|
#include "tracelog.h"
|
||||||
};
|
|
||||||
}
|
#ifdef _WIN32
|
||||||
|
#define VC_EXTRALEAN
|
||||||
str_t strInitStr(const char *cstr) {
|
#include <windows.h>
|
||||||
return strInitBuf(cstr, strlen(cstr));
|
#else
|
||||||
}
|
#include <iconv.h>
|
||||||
|
#endif
|
||||||
str_t strInitView(strview_t view) {
|
|
||||||
return strInitBuf(view.buf, view.len);
|
#ifndef min
|
||||||
}
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
#endif
|
||||||
str_t strInitBuf(const char *buf, size_t len) {
|
|
||||||
str_t str;
|
// == STR_T ========================================================
|
||||||
str.len = len;
|
|
||||||
str.buf = malloc(len + 1);
|
str_t strInit(void) {
|
||||||
memcpy(str.buf, buf, len);
|
return (str_t) {
|
||||||
str.buf[len] = '\0';
|
.buf = NULL,
|
||||||
return str;
|
.len = 0
|
||||||
}
|
};
|
||||||
|
}
|
||||||
void strFree(str_t *ctx) {
|
|
||||||
free(ctx->buf);
|
str_t strInitStr(const char *cstr) {
|
||||||
ctx->buf = NULL;
|
return strInitBuf(cstr, strlen(cstr));
|
||||||
ctx->len = 0;
|
}
|
||||||
}
|
|
||||||
|
str_t strInitView(strview_t view) {
|
||||||
str_t strMove(str_t *ctx) {
|
return strInitBuf(view.buf, view.len);
|
||||||
str_t str = strInitBuf(ctx->buf, ctx->len);
|
}
|
||||||
ctx->buf = NULL;
|
|
||||||
ctx->len = 0;
|
str_t strInitBuf(const char *buf, size_t len) {
|
||||||
return str;
|
str_t str;
|
||||||
}
|
str.len = len;
|
||||||
|
str.buf = malloc(len + 1);
|
||||||
str_t strDup(str_t ctx) {
|
memcpy(str.buf, buf, len);
|
||||||
return strInitBuf(ctx.buf, ctx.len);
|
str.buf[len] = '\0';
|
||||||
}
|
return str;
|
||||||
|
}
|
||||||
strview_t strGetView(str_t *ctx) {
|
|
||||||
return (strview_t) {
|
void strFree(str_t *ctx) {
|
||||||
.buf = ctx->buf,
|
free(ctx->buf);
|
||||||
.len = ctx->len
|
ctx->buf = NULL;
|
||||||
};
|
ctx->len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *strBegin(str_t *ctx) {
|
str_t strFromWCHAR(const wchar_t *src, size_t len) {
|
||||||
return ctx->buf;
|
if(len == 0) len = wcslen(src);
|
||||||
}
|
|
||||||
|
#ifdef _WIN32
|
||||||
char *strEnd(str_t *ctx) {
|
// TODO CP_ACP should be CP_UTF8 but if i put CP_UTF8 it doesn't work??
|
||||||
return ctx->buf ? ctx->buf + ctx->len : NULL;
|
int result_len = WideCharToMultiByte(
|
||||||
}
|
CP_ACP, 0,
|
||||||
|
src, (int)len,
|
||||||
char strBack(str_t *ctx) {
|
NULL, 0,
|
||||||
return ctx->buf ? ctx->buf[ctx->len - 1] : '\0';
|
NULL, NULL
|
||||||
}
|
);
|
||||||
|
char *buf = malloc(result_len + 1);
|
||||||
bool strIsEmpty(str_t *ctx) {
|
if(buf) {
|
||||||
return ctx->len == 0;
|
WideCharToMultiByte(
|
||||||
}
|
CP_ACP, 0,
|
||||||
|
src, (int)len,
|
||||||
void strAppend(str_t *ctx, const char *str) {
|
buf, result_len,
|
||||||
strAppendBuf(ctx, str, strlen(str));
|
NULL, NULL
|
||||||
}
|
);
|
||||||
|
buf[result_len] = '\0';
|
||||||
void strAppendStr(str_t *ctx, str_t str) {
|
}
|
||||||
strAppendBuf(ctx, str.buf, str.len);
|
return (str_t) {
|
||||||
}
|
.buf = buf,
|
||||||
|
.len = result_len
|
||||||
void strAppendView(str_t *ctx, strview_t view) {
|
};
|
||||||
strAppendBuf(ctx, view.buf, view.len);
|
#else
|
||||||
}
|
size_t actual_len = len * sizeof(wchar_t);
|
||||||
|
|
||||||
void strAppendBuf(str_t *ctx, const char *buf, size_t len) {
|
size_t dest_len = len * 6;
|
||||||
size_t oldlen = ctx->len;
|
char *dest = malloc(dest_len);
|
||||||
ctx->len += len;
|
|
||||||
ctx->buf = realloc(ctx->buf, ctx->len + 1);
|
iconv_t cd = iconv_open("UTF-8", "WCHAR_T");
|
||||||
memcpy(ctx->buf + oldlen, buf, len);
|
assert(cd);
|
||||||
ctx->buf[ctx->len] = '\0';
|
|
||||||
}
|
size_t dest_left = dest_len;
|
||||||
|
char *dest_temp = dest;
|
||||||
void strAppendChars(str_t *ctx, char c, size_t count) {
|
char *src_temp = (char*)src;
|
||||||
size_t oldlen = ctx->len;
|
size_t lost = iconv(cd, &src_temp, &actual_len, &dest_temp, &dest_left);
|
||||||
ctx->len += count;
|
assert(lost != ((size_t)-1));
|
||||||
ctx->buf = realloc(ctx->buf, ctx->len + 1);
|
|
||||||
memset(ctx->buf + oldlen, c, count);
|
dest_len -= dest_left;
|
||||||
ctx->buf[ctx->len] = '\0';
|
dest = realloc(dest, dest_len + 1);
|
||||||
}
|
dest[dest_len] = '\0';
|
||||||
|
|
||||||
void strPush(str_t *ctx, char c) {
|
iconv_close(cd);
|
||||||
strAppendChars(ctx, c, 1);
|
|
||||||
}
|
return (str_t){
|
||||||
|
.buf = dest,
|
||||||
char strPop(str_t *ctx) {
|
.len = dest_len
|
||||||
char c = strBack(ctx);
|
};
|
||||||
ctx->buf = realloc(ctx->buf, ctx->len);
|
#endif
|
||||||
ctx->len -= 1;
|
}
|
||||||
ctx->buf[ctx->len] = '\0';
|
|
||||||
return c;
|
wchar_t *strToWCHAR(str_t ctx) {
|
||||||
}
|
#ifdef _WIN32
|
||||||
|
UINT codepage = CP_ACP;
|
||||||
void strSwap(str_t *ctx, str_t *other) {
|
int len = MultiByteToWideChar(
|
||||||
char *buf = other->buf;
|
codepage, 0,
|
||||||
size_t len = other->len;
|
ctx.buf, (int)ctx.len,
|
||||||
other->buf = ctx->buf;
|
NULL, 0
|
||||||
other->len = ctx->len;
|
);
|
||||||
ctx->buf = buf;
|
wchar_t *str = malloc(sizeof(wchar_t) * (len + 1));
|
||||||
ctx->len = len;
|
if(!str) return NULL;
|
||||||
}
|
len = MultiByteToWideChar(
|
||||||
|
codepage, 0,
|
||||||
#include "tracelog.h"
|
ctx.buf, (int)ctx.len,
|
||||||
|
str, len
|
||||||
str_t strSubstr(str_t *ctx, size_t pos, size_t len) {
|
);
|
||||||
if(strIsEmpty(ctx)) return strInit();
|
str[len] = '\0';
|
||||||
if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos;
|
return str;
|
||||||
return strInitBuf(ctx->buf + pos, len);
|
#else
|
||||||
}
|
size_t dest_len = ctx.len * sizeof(wchar_t);
|
||||||
|
char *dest = malloc(dest_len);
|
||||||
strview_t strSubview(str_t *ctx, size_t pos, size_t len) {
|
|
||||||
if(strIsEmpty(ctx)) return strvInit(NULL);
|
iconv_t cd = iconv_open("WCHAR_T", "UTF-8");
|
||||||
if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos;
|
assert(cd);
|
||||||
return (strview_t) {
|
|
||||||
.buf = ctx->buf + pos,
|
size_t dest_left = dest_len;
|
||||||
.len = 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));
|
||||||
void strLower(str_t *ctx) {
|
|
||||||
for(size_t i = 0; i < ctx->len; ++i) {
|
dest_len -= dest_left;
|
||||||
ctx->buf[i] = tolower(ctx->buf[i]);
|
dest = realloc(dest, dest_len + 1);
|
||||||
}
|
dest[dest_len] = '\0';
|
||||||
}
|
|
||||||
|
iconv_close(cd);
|
||||||
str_t strToLower(str_t ctx) {
|
|
||||||
str_t str = strDup(ctx);
|
return (wchar_t *)dest;
|
||||||
strLower(&str);
|
#endif
|
||||||
return str;
|
}
|
||||||
}
|
|
||||||
|
str_t strMove(str_t *ctx) {
|
||||||
#ifdef STR_TESTING
|
str_t str = strInitBuf(ctx->buf, ctx->len);
|
||||||
#include <stdio.h>
|
ctx->buf = NULL;
|
||||||
#include "tracelog.h"
|
ctx->len = 0;
|
||||||
|
return str;
|
||||||
void strTest(void) {
|
}
|
||||||
str_t s;
|
|
||||||
debug("== testing init =================");
|
str_t strDup(str_t ctx) {
|
||||||
{
|
return strInitBuf(ctx.buf, ctx.len);
|
||||||
s = strInit();
|
}
|
||||||
printf("%s %zu\n", s.buf, s.len);
|
|
||||||
strFree(&s);
|
strview_t strGetView(str_t *ctx) {
|
||||||
s = strInitStr("hello world");
|
return (strview_t) {
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
.buf = ctx->buf,
|
||||||
strFree(&s);
|
.len = ctx->len
|
||||||
uint8_t buf[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };
|
};
|
||||||
s = strInitBuf((char *)buf, sizeof(buf));
|
}
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
|
||||||
strFree(&s);
|
char *strBegin(str_t *ctx) {
|
||||||
}
|
return ctx->buf;
|
||||||
debug("== testing view =================");
|
}
|
||||||
{
|
|
||||||
s = strInitStr("hello world");
|
char *strEnd(str_t *ctx) {
|
||||||
strview_t view = strGetView(&s);
|
return ctx->buf ? ctx->buf + ctx->len : NULL;
|
||||||
printf("\"%.*s\" %zu\n", (int)view.len, view.buf, view.len);
|
}
|
||||||
strFree(&s);
|
|
||||||
}
|
char strBack(str_t *ctx) {
|
||||||
debug("== testing begin/end ============");
|
return ctx->buf ? ctx->buf[ctx->len - 1] : '\0';
|
||||||
{
|
}
|
||||||
s = strInitStr("hello world");
|
|
||||||
char *beg = strBegin(&s);
|
bool strIsEmpty(str_t *ctx) {
|
||||||
char *end = strEnd(&s);
|
return ctx->len == 0;
|
||||||
printf("[ ");
|
}
|
||||||
for(; beg < end; ++beg) {
|
|
||||||
printf("%c ", *beg);
|
void strAppend(str_t *ctx, const char *str) {
|
||||||
}
|
strAppendBuf(ctx, str, strlen(str));
|
||||||
printf("]\n");
|
}
|
||||||
strFree(&s);
|
|
||||||
}
|
void strAppendStr(str_t *ctx, str_t str) {
|
||||||
debug("== testing back/isempty =========");
|
strAppendBuf(ctx, str.buf, str.len);
|
||||||
{
|
}
|
||||||
s = strInitStr("hello world");
|
|
||||||
printf("[ ");
|
void strAppendView(str_t *ctx, strview_t view) {
|
||||||
while(!strIsEmpty(&s)) {
|
strAppendBuf(ctx, view.buf, view.len);
|
||||||
printf("%c ", strBack(&s));
|
}
|
||||||
strPop(&s);
|
|
||||||
}
|
void strAppendBuf(str_t *ctx, const char *buf, size_t len) {
|
||||||
printf("]\n");
|
size_t oldlen = ctx->len;
|
||||||
strFree(&s);
|
ctx->len += len;
|
||||||
}
|
ctx->buf = realloc(ctx->buf, ctx->len + 1);
|
||||||
debug("== testing append ===============");
|
memcpy(ctx->buf + oldlen, buf, len);
|
||||||
{
|
ctx->buf[ctx->len] = '\0';
|
||||||
s = strInitStr("hello ");
|
}
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
|
||||||
strAppend(&s, "world");
|
void strAppendChars(str_t *ctx, char c, size_t count) {
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
size_t oldlen = ctx->len;
|
||||||
strAppendView(&s, strvInit(", how is it "));
|
ctx->len += count;
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
ctx->buf = realloc(ctx->buf, ctx->len + 1);
|
||||||
uint8_t buf[] = { 'g', 'o', 'i', 'n', 'g' };
|
memset(ctx->buf + oldlen, c, count);
|
||||||
strAppendBuf(&s, (char*)buf, sizeof(buf));
|
ctx->buf[ctx->len] = '\0';
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
}
|
||||||
strAppendChars(&s, '?', 2);
|
|
||||||
printf("\"%s\" %zu\n", s.buf, s.len);
|
void strPush(str_t *ctx, char c) {
|
||||||
strFree(&s);
|
strAppendChars(ctx, c, 1);
|
||||||
}
|
}
|
||||||
debug("== testing push/pop =============");
|
|
||||||
{
|
char strPop(str_t *ctx) {
|
||||||
s = strInit();
|
char c = strBack(ctx);
|
||||||
str_t s2 = strInitStr("hello world");
|
ctx->buf = realloc(ctx->buf, ctx->len);
|
||||||
|
ctx->len -= 1;
|
||||||
printf("%-14s %-14s\n", "s", "s2");
|
ctx->buf[ctx->len] = '\0';
|
||||||
printf("----------------------------\n");
|
return c;
|
||||||
while(!strIsEmpty(&s2)) {
|
}
|
||||||
printf("%-14s %-14s\n", s.buf, s2.buf);
|
|
||||||
strPush(&s, strPop(&s2));
|
void strSwap(str_t *ctx, str_t *other) {
|
||||||
}
|
char *buf = other->buf;
|
||||||
printf("%-14s %-14s\n", s.buf, s2.buf);
|
size_t len = other->len;
|
||||||
|
other->buf = ctx->buf;
|
||||||
strFree(&s);
|
other->len = ctx->len;
|
||||||
strFree(&s2);
|
ctx->buf = buf;
|
||||||
}
|
ctx->len = len;
|
||||||
debug("== testing swap =================");
|
}
|
||||||
{
|
|
||||||
s = strInitStr("hello");
|
void strReplace(str_t *ctx, char from, char to) {
|
||||||
str_t s2 = strInitStr("world");
|
for(size_t i = 0; i < ctx->len; ++i) {
|
||||||
printf("%-8s %-8s\n", "s", "s2");
|
if(ctx->buf[i] == from) {
|
||||||
printf("----------------\n");
|
ctx->buf[i] = to;
|
||||||
printf("%-8s %-8s\n", s.buf, s2.buf);
|
}
|
||||||
strSwap(&s, &s2);
|
}
|
||||||
printf("%-8s %-8s\n", s.buf, s2.buf);
|
}
|
||||||
|
|
||||||
strFree(&s);
|
str_t strSubstr(str_t *ctx, size_t pos, size_t len) {
|
||||||
strFree(&s2);
|
if(strIsEmpty(ctx)) return strInit();
|
||||||
}
|
if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos;
|
||||||
debug("== testing substr ===============");
|
return strInitBuf(ctx->buf + pos, len);
|
||||||
{
|
}
|
||||||
s = strInitStr("hello world");
|
|
||||||
printf("s: %s\n", s.buf);
|
strview_t strSubview(str_t *ctx, size_t pos, size_t len) {
|
||||||
|
if(strIsEmpty(ctx)) return strvInit(NULL);
|
||||||
printf("-- string\n");
|
if(len == SIZE_MAX || (pos + len) > ctx->len) len = ctx->len - pos;
|
||||||
str_t s2 = strSubstr(&s, 0, 5);
|
return (strview_t) {
|
||||||
printf("0..5: \"%s\"\n", s2.buf);
|
.buf = ctx->buf + pos,
|
||||||
strFree(&s2);
|
.len = len
|
||||||
|
};
|
||||||
s2 = strSubstr(&s, 5, SIZE_MAX);
|
}
|
||||||
printf("6..SIZE_MAX: \"%s\"\n", s2.buf);
|
|
||||||
strFree(&s2);
|
void strLower(str_t *ctx) {
|
||||||
|
for(size_t i = 0; i < ctx->len; ++i) {
|
||||||
printf("-- view\n");
|
ctx->buf[i] = (char)tolower(ctx->buf[i]);
|
||||||
strview_t v = strSubview(&s, 0, 5);
|
}
|
||||||
printf("0..5: \"%.*s\"\n", (int)v.len, v.buf);
|
}
|
||||||
v = strSubview(&s, 5, SIZE_MAX);
|
|
||||||
printf("6..SIZE_MAX: \"%.*s\"\n", (int)v.len, v.buf);
|
str_t strToLower(str_t ctx) {
|
||||||
|
str_t str = strDup(ctx);
|
||||||
strFree(&s);
|
strLower(&str);
|
||||||
}
|
return str;
|
||||||
|
}
|
||||||
strFree(&s);
|
|
||||||
}
|
// == 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"
|
||||||
|
|
||||||
|
void strTest(void) {
|
||||||
|
str_t s;
|
||||||
|
debug("== testing init =================");
|
||||||
|
{
|
||||||
|
s = strInit();
|
||||||
|
printf("%s %zu\n", s.buf, s.len);
|
||||||
|
strFree(&s);
|
||||||
|
s = strInitStr("hello world");
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
strFree(&s);
|
||||||
|
uint8_t buf[] = { 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd' };
|
||||||
|
s = strInitBuf((char *)buf, sizeof(buf));
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
|
debug("== testing view =================");
|
||||||
|
{
|
||||||
|
s = strInitStr("hello world");
|
||||||
|
strview_t view = strGetView(&s);
|
||||||
|
printf("\"%.*s\" %zu\n", (int)view.len, view.buf, view.len);
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
|
debug("== testing begin/end ============");
|
||||||
|
{
|
||||||
|
s = strInitStr("hello world");
|
||||||
|
char *beg = strBegin(&s);
|
||||||
|
char *end = strEnd(&s);
|
||||||
|
printf("[ ");
|
||||||
|
for(; beg < end; ++beg) {
|
||||||
|
printf("%c ", *beg);
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
|
debug("== testing back/isempty =========");
|
||||||
|
{
|
||||||
|
s = strInitStr("hello world");
|
||||||
|
printf("[ ");
|
||||||
|
while(!strIsEmpty(&s)) {
|
||||||
|
printf("%c ", strBack(&s));
|
||||||
|
strPop(&s);
|
||||||
|
}
|
||||||
|
printf("]\n");
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
|
debug("== testing append ===============");
|
||||||
|
{
|
||||||
|
s = strInitStr("hello ");
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
strAppend(&s, "world");
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
strAppendView(&s, strvInit(", how is it "));
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
uint8_t buf[] = { 'g', 'o', 'i', 'n', 'g' };
|
||||||
|
strAppendBuf(&s, (char*)buf, sizeof(buf));
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
strAppendChars(&s, '?', 2);
|
||||||
|
printf("\"%s\" %zu\n", s.buf, s.len);
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
|
debug("== testing push/pop =============");
|
||||||
|
{
|
||||||
|
s = strInit();
|
||||||
|
str_t s2 = strInitStr("hello world");
|
||||||
|
|
||||||
|
printf("%-14s %-14s\n", "s", "s2");
|
||||||
|
printf("----------------------------\n");
|
||||||
|
while(!strIsEmpty(&s2)) {
|
||||||
|
printf("%-14s %-14s\n", s.buf, s2.buf);
|
||||||
|
strPush(&s, strPop(&s2));
|
||||||
|
}
|
||||||
|
printf("%-14s %-14s\n", s.buf, s2.buf);
|
||||||
|
|
||||||
|
strFree(&s);
|
||||||
|
strFree(&s2);
|
||||||
|
}
|
||||||
|
debug("== testing swap =================");
|
||||||
|
{
|
||||||
|
s = strInitStr("hello");
|
||||||
|
str_t s2 = strInitStr("world");
|
||||||
|
printf("%-8s %-8s\n", "s", "s2");
|
||||||
|
printf("----------------\n");
|
||||||
|
printf("%-8s %-8s\n", s.buf, s2.buf);
|
||||||
|
strSwap(&s, &s2);
|
||||||
|
printf("%-8s %-8s\n", s.buf, s2.buf);
|
||||||
|
|
||||||
|
strFree(&s);
|
||||||
|
strFree(&s2);
|
||||||
|
}
|
||||||
|
debug("== testing substr ===============");
|
||||||
|
{
|
||||||
|
s = strInitStr("hello world");
|
||||||
|
printf("s: %s\n", s.buf);
|
||||||
|
|
||||||
|
printf("-- string\n");
|
||||||
|
str_t s2 = strSubstr(&s, 0, 5);
|
||||||
|
printf("0..5: \"%s\"\n", s2.buf);
|
||||||
|
strFree(&s2);
|
||||||
|
|
||||||
|
s2 = strSubstr(&s, 5, SIZE_MAX);
|
||||||
|
printf("6..SIZE_MAX: \"%s\"\n", s2.buf);
|
||||||
|
strFree(&s2);
|
||||||
|
|
||||||
|
printf("-- view\n");
|
||||||
|
strview_t v = strSubview(&s, 0, 5);
|
||||||
|
printf("0..5: \"%.*s\"\n", (int)v.len, v.buf);
|
||||||
|
v = strSubview(&s, 5, SIZE_MAX);
|
||||||
|
printf("6..SIZE_MAX: \"%.*s\"\n", (int)v.len, v.buf);
|
||||||
|
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
strFree(&s);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
188
str.h
188
str.h
|
|
@ -1,63 +1,127 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "slice.h"
|
#include <stdint.h>
|
||||||
#include "strview.h"
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
#define STR_TESTING
|
#include <limits.h>
|
||||||
|
#include <wchar.h>
|
||||||
// typedef struct {
|
// #include "strview.h"
|
||||||
// char *buf;
|
|
||||||
// size_t len;
|
#define STRV_NOT_FOUND SIZE_MAX
|
||||||
// } str_t;
|
|
||||||
|
typedef struct {
|
||||||
typedef slice_t(char *) str_t;
|
char *buf;
|
||||||
|
size_t len;
|
||||||
str_t strInit(void);
|
} str_t;
|
||||||
str_t strInitStr(const char *cstr);
|
|
||||||
str_t strInitView(strview_t view);
|
typedef struct {
|
||||||
str_t strInitBuf(const char *buf, size_t len);
|
const char *buf;
|
||||||
|
size_t len;
|
||||||
void strFree(str_t *ctx);
|
} strview_t;
|
||||||
|
|
||||||
str_t strMove(str_t *ctx);
|
// == STR_T ========================================================
|
||||||
str_t strDup(str_t ctx);
|
|
||||||
|
str_t strInit(void);
|
||||||
strview_t strGetView(str_t *ctx);
|
str_t strInitStr(const char *cstr);
|
||||||
|
str_t strInitView(strview_t view);
|
||||||
char *strBegin(str_t *ctx);
|
str_t strInitBuf(const char *buf, size_t len);
|
||||||
char *strEnd(str_t *ctx);
|
|
||||||
|
str_t strFromWCHAR(const wchar_t *src, size_t len);
|
||||||
char strBack(str_t *ctx);
|
wchar_t *strToWCHAR(str_t ctx);
|
||||||
|
|
||||||
bool strIsEmpty(str_t *ctx);
|
void strFree(str_t *ctx);
|
||||||
|
|
||||||
void strAppend(str_t *ctx, const char *str);
|
str_t strMove(str_t *ctx);
|
||||||
void strAppendStr(str_t *ctx, str_t str);
|
str_t strDup(str_t ctx);
|
||||||
void strAppendView(str_t *ctx, strview_t view);
|
|
||||||
void strAppendBuf(str_t *ctx, const char *buf, size_t len);
|
strview_t strGetView(str_t *ctx);
|
||||||
void strAppendChars(str_t *ctx, char c, size_t count);
|
|
||||||
|
char *strBegin(str_t *ctx);
|
||||||
void strPush(str_t *ctx, char c);
|
char *strEnd(str_t *ctx);
|
||||||
char strPop(str_t *ctx);
|
|
||||||
|
char strBack(str_t *ctx);
|
||||||
void strSwap(str_t *ctx, str_t *other);
|
|
||||||
|
bool strIsEmpty(str_t *ctx);
|
||||||
// if len == SIZE_MAX, copies until end
|
|
||||||
str_t strSubstr(str_t *ctx, size_t pos, size_t len);
|
void strAppend(str_t *ctx, const char *str);
|
||||||
// if len == SIZE_MAX, returns until end
|
void strAppendStr(str_t *ctx, str_t str);
|
||||||
strview_t strSubview(str_t *ctx, size_t pos, size_t len);
|
void strAppendView(str_t *ctx, strview_t view);
|
||||||
|
void strAppendBuf(str_t *ctx, const char *buf, size_t len);
|
||||||
void strLower(str_t *ctx);
|
void strAppendChars(str_t *ctx, char c, size_t count);
|
||||||
str_t strToLower(str_t ctx);
|
|
||||||
|
void strPush(str_t *ctx, char c);
|
||||||
#ifdef STR_TESTING
|
char strPop(str_t *ctx);
|
||||||
void strTest(void);
|
|
||||||
#endif
|
void strSwap(str_t *ctx, str_t *other);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
void strReplace(str_t *ctx, char from, char to);
|
||||||
} // extern "C"
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
#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
|
#endif
|
||||||
1054
strstream.c
1054
strstream.c
File diff suppressed because it is too large
Load diff
199
strstream.h
199
strstream.h
|
|
@ -1,97 +1,104 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "strview.h"
|
#include "str.h"
|
||||||
|
|
||||||
/* == INPUT STREAM ============================================ */
|
/* == INPUT STREAM ============================================ */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *start;
|
const char *start;
|
||||||
const char *cur;
|
const char *cur;
|
||||||
size_t size;
|
size_t size;
|
||||||
} str_istream_t;
|
} str_istream_t;
|
||||||
|
|
||||||
// initialize with null-terminated string
|
// initialize with null-terminated string
|
||||||
str_istream_t istrInit(const char *str);
|
str_istream_t istrInit(const char *str);
|
||||||
str_istream_t istrInitLen(const char *str, size_t len);
|
str_istream_t istrInitLen(const char *str, size_t len);
|
||||||
|
|
||||||
// get the current character and advance
|
// get the current character and advance
|
||||||
char istrGet(str_istream_t *ctx);
|
char istrGet(str_istream_t *ctx);
|
||||||
// get the current character but don't advance
|
// get the current character but don't advance
|
||||||
char istrPeek(str_istream_t *ctx);
|
char istrPeek(str_istream_t *ctx);
|
||||||
// ignore characters until the delimiter
|
// ignore characters until the delimiter
|
||||||
void istrIgnore(str_istream_t *ctx, char delim);
|
void istrIgnore(str_istream_t *ctx, char delim);
|
||||||
// skip n characters
|
// skip n characters
|
||||||
void istrSkip(str_istream_t *ctx, size_t n);
|
void istrSkip(str_istream_t *ctx, size_t n);
|
||||||
// read len bytes into buffer, the buffer will not be null terminated
|
// read len bytes into buffer, the buffer will not be null terminated
|
||||||
void istrRead(str_istream_t *ctx, char *buf, size_t len);
|
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
|
// read a maximum of len bytes into buffer, the buffer will not be null terminated
|
||||||
// returns the number of bytes read
|
// returns the number of bytes read
|
||||||
size_t istrReadMax(str_istream_t *ctx, char *buf, size_t len);
|
size_t istrReadMax(str_istream_t *ctx, char *buf, size_t len);
|
||||||
// return to the beginning of the stream
|
// return to the beginning of the stream
|
||||||
void istrRewind(str_istream_t *ctx);
|
void istrRewind(str_istream_t *ctx);
|
||||||
// return the number of bytes read from beginning of stream
|
// return the number of bytes read from beginning of stream
|
||||||
size_t istrTell(str_istream_t *ctx);
|
size_t istrTell(str_istream_t *ctx);
|
||||||
// return true if the stream doesn't have any new bytes to read
|
// return true if the stream doesn't have any new bytes to read
|
||||||
bool istrIsFinished(str_istream_t *ctx);
|
bool istrIsFinished(str_istream_t *ctx);
|
||||||
|
|
||||||
bool istrGetbool(str_istream_t *ctx, bool *val);
|
bool istrGetbool(str_istream_t *ctx, bool *val);
|
||||||
bool istrGetu8(str_istream_t *ctx, uint8_t *val);
|
bool istrGetu8(str_istream_t *ctx, uint8_t *val);
|
||||||
bool istrGetu16(str_istream_t *ctx, uint16_t *val);
|
bool istrGetu16(str_istream_t *ctx, uint16_t *val);
|
||||||
bool istrGetu32(str_istream_t *ctx, uint32_t *val);
|
bool istrGetu32(str_istream_t *ctx, uint32_t *val);
|
||||||
bool istrGetu64(str_istream_t *ctx, uint64_t *val);
|
bool istrGetu64(str_istream_t *ctx, uint64_t *val);
|
||||||
bool istrGeti8(str_istream_t *ctx, int8_t *val);
|
bool istrGeti8(str_istream_t *ctx, int8_t *val);
|
||||||
bool istrGeti16(str_istream_t *ctx, int16_t *val);
|
bool istrGeti16(str_istream_t *ctx, int16_t *val);
|
||||||
bool istrGeti32(str_istream_t *ctx, int32_t *val);
|
bool istrGeti32(str_istream_t *ctx, int32_t *val);
|
||||||
bool istrGeti64(str_istream_t *ctx, int64_t *val);
|
bool istrGeti64(str_istream_t *ctx, int64_t *val);
|
||||||
bool istrGetfloat(str_istream_t *ctx, float *val);
|
bool istrGetfloat(str_istream_t *ctx, float *val);
|
||||||
bool istrGetdouble(str_istream_t *ctx, double *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
|
// 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);
|
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
|
// 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);
|
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);
|
||||||
strview_t istrGetviewLen(str_istream_t *ctx, size_t off, size_t len);
|
strview_t istrGetviewLen(str_istream_t *ctx, size_t off, size_t len);
|
||||||
|
|
||||||
/* == OUTPUT STREAM =========================================== */
|
/* == OUTPUT STREAM =========================================== */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *buf;
|
char *buf;
|
||||||
size_t size;
|
size_t size;
|
||||||
size_t allocated;
|
size_t allocated;
|
||||||
} str_ostream_t;
|
} str_ostream_t;
|
||||||
|
|
||||||
str_ostream_t ostrInit(void);
|
str_ostream_t ostrInit(void);
|
||||||
str_ostream_t ostrInitLen(size_t initial_alloc);
|
str_ostream_t ostrInitLen(size_t initial_alloc);
|
||||||
str_ostream_t ostrInitStr(const char *buf, size_t len);
|
str_ostream_t ostrInitStr(const char *buf, size_t len);
|
||||||
|
|
||||||
void ostrFree(str_ostream_t *ctx);
|
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);
|
||||||
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...);
|
|
||||||
|
char ostrBack(str_ostream_t *ctx);
|
||||||
void ostrPutc(str_ostream_t *ctx, char c);
|
str_t ostrAsStr(str_ostream_t *ctx);
|
||||||
|
strview_t ostrAsView(str_ostream_t *ctx);
|
||||||
void ostrAppendbool(str_ostream_t *ctx, bool val);
|
|
||||||
void ostrAppendu8(str_ostream_t *ctx, uint8_t val);
|
void ostrReplace(str_ostream_t *ctx, char from, char to);
|
||||||
void ostrAppendu16(str_ostream_t *ctx, uint16_t val);
|
|
||||||
void ostrAppendu32(str_ostream_t *ctx, uint32_t val);
|
void ostrPrintf(str_ostream_t *ctx, const char *fmt, ...);
|
||||||
void ostrAppendu64(str_ostream_t *ctx, uint64_t val);
|
void ostrPutc(str_ostream_t *ctx, char c);
|
||||||
void ostrAppendi8(str_ostream_t *ctx, int8_t val);
|
|
||||||
void ostrAppendi16(str_ostream_t *ctx, int16_t val);
|
void ostrAppendbool(str_ostream_t *ctx, bool val);
|
||||||
void ostrAppendi32(str_ostream_t *ctx, int32_t val);
|
void ostrAppendchar(str_ostream_t *ctx, char val);
|
||||||
void ostrAppendi64(str_ostream_t *ctx, int64_t val);
|
void ostrAppendu8(str_ostream_t *ctx, uint8_t val);
|
||||||
void ostrAppendfloat(str_ostream_t *ctx, float val);
|
void ostrAppendu16(str_ostream_t *ctx, uint16_t val);
|
||||||
void ostrAppenddouble(str_ostream_t *ctx, double val);
|
void ostrAppendu32(str_ostream_t *ctx, uint32_t val);
|
||||||
void ostrAppendview(str_ostream_t *ctx, strview_t view);
|
void ostrAppendu64(str_ostream_t *ctx, uint64_t val);
|
||||||
|
void ostrAppendi8(str_ostream_t *ctx, int8_t val);
|
||||||
#ifdef __cplusplus
|
void ostrAppendi16(str_ostream_t *ctx, int16_t val);
|
||||||
} // extern "C"
|
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
|
#endif
|
||||||
250
strview.c
250
strview.c
|
|
@ -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;
|
|
||||||
}
|
|
||||||
71
strview.h
71
strview.h
|
|
@ -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
|
|
||||||
180
tracelog.c
180
tracelog.c
|
|
@ -1,91 +1,91 @@
|
||||||
#include "tracelog.h"
|
#include "tracelog.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS.
|
#pragma warning(disable:4996) // _CRT_SECURE_NO_WARNINGS.
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TLOG_VS
|
#ifdef TLOG_VS
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifndef TLOG_NO_COLOURS
|
#ifndef TLOG_NO_COLOURS
|
||||||
#define TLOG_NO_COLOURS
|
#define TLOG_NO_COLOURS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define VC_EXTRALEAN
|
#define VC_EXTRALEAN
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#else
|
#else
|
||||||
#error "can't use TLOG_VS if not on windows"
|
#error "can't use TLOG_VS if not on windows"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TLOG_NO_COLOURS
|
#ifdef TLOG_NO_COLOURS
|
||||||
#define BLACK ""
|
#define BLACK ""
|
||||||
#define RED ""
|
#define RED ""
|
||||||
#define GREEN ""
|
#define GREEN ""
|
||||||
#define YELLOW ""
|
#define YELLOW ""
|
||||||
#define BLUE ""
|
#define BLUE ""
|
||||||
#define MAGENTA ""
|
#define MAGENTA ""
|
||||||
#define CYAN ""
|
#define CYAN ""
|
||||||
#define WHITE ""
|
#define WHITE ""
|
||||||
#define RESET ""
|
#define RESET ""
|
||||||
#define BOLD ""
|
#define BOLD ""
|
||||||
#else
|
#else
|
||||||
#define BLACK "\033[30m"
|
#define BLACK "\033[30m"
|
||||||
#define RED "\033[31m"
|
#define RED "\033[31m"
|
||||||
#define GREEN "\033[32m"
|
#define GREEN "\033[32m"
|
||||||
#define YELLOW "\033[33m"
|
#define YELLOW "\033[33m"
|
||||||
#define BLUE "\033[22;34m"
|
#define BLUE "\033[22;34m"
|
||||||
#define MAGENTA "\033[35m"
|
#define MAGENTA "\033[35m"
|
||||||
#define CYAN "\033[36m"
|
#define CYAN "\033[36m"
|
||||||
#define WHITE "\033[37m"
|
#define WHITE "\033[37m"
|
||||||
#define RESET "\033[0m"
|
#define RESET "\033[0m"
|
||||||
#define BOLD "\033[1m"
|
#define BOLD "\033[1m"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_TRACELOG_MSG_LENGTH 1024
|
#define MAX_TRACELOG_MSG_LENGTH 1024
|
||||||
|
|
||||||
bool use_newline = true;
|
bool use_newline = true;
|
||||||
|
|
||||||
void traceLog(LogLevel level, const char *fmt, ...) {
|
void traceLog(LogLevel level, const char *fmt, ...) {
|
||||||
char buffer[MAX_TRACELOG_MSG_LENGTH];
|
char buffer[MAX_TRACELOG_MSG_LENGTH];
|
||||||
memset(buffer, 0, sizeof(buffer));
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
const char *beg;
|
const char *beg;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case LogTrace: beg = BOLD WHITE "[TRACE]: " RESET; break;
|
case LogTrace: beg = BOLD WHITE "[TRACE]: " RESET; break;
|
||||||
case LogDebug: beg = BOLD BLUE "[DEBUG]: " RESET; break;
|
case LogDebug: beg = BOLD BLUE "[DEBUG]: " RESET; break;
|
||||||
case LogInfo: beg = BOLD GREEN "[INFO]: " RESET; break;
|
case LogInfo: beg = BOLD GREEN "[INFO]: " RESET; break;
|
||||||
case LogWarning: beg = BOLD YELLOW "[WARNING]: " RESET; break;
|
case LogWarning: beg = BOLD YELLOW "[WARNING]: " RESET; break;
|
||||||
case LogError: beg = BOLD RED "[ERROR]: " RESET; break;
|
case LogError: beg = BOLD RED "[ERROR]: " RESET; break;
|
||||||
case LogFatal: beg = BOLD RED "[FATAL]: " RESET; break;
|
case LogFatal: beg = BOLD RED "[FATAL]: " RESET; break;
|
||||||
default: break;
|
default: beg = ""; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t offset = strlen(beg);
|
size_t offset = strlen(beg);
|
||||||
strncpy(buffer, beg, sizeof(buffer));
|
strncpy(buffer, beg, sizeof(buffer));
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vsnprintf(buffer + offset, sizeof(buffer) - offset, fmt, args);
|
vsnprintf(buffer + offset, sizeof(buffer) - offset, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
#ifdef TLOG_VS
|
#ifdef TLOG_VS
|
||||||
OutputDebugStringA(buffer);
|
OutputDebugStringA(buffer);
|
||||||
if(use_newline) OutputDebugStringA("\n");
|
if(use_newline) OutputDebugStringA("\n");
|
||||||
#else
|
#else
|
||||||
printf("%s", buffer);
|
printf("%s", buffer);
|
||||||
if(use_newline) puts("");
|
if(use_newline) puts("");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef TLOG_DONT_EXIT_ON_FATAL
|
#ifndef TLOG_DONT_EXIT_ON_FATAL
|
||||||
if (level == LogFatal) exit(1);
|
if (level == LogFatal) exit(1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void traceUseNewline(bool newline) {
|
void traceUseNewline(bool newline) {
|
||||||
use_newline = newline;
|
use_newline = newline;
|
||||||
}
|
}
|
||||||
61
tracelog.h
61
tracelog.h
|
|
@ -1,31 +1,32 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Define any of this to turn on the option
|
/* Define any of this to turn on the option
|
||||||
* -> TLOG_NO_COLOURS: print without using colours
|
* -> TLOG_NO_COLOURS: print without using colours
|
||||||
* -> TLOG_VS: print to visual studio console, also turns on TLOG_NO_COLOURS
|
* -> TLOG_VS: print to visual studio console, also turns on TLOG_NO_COLOURS
|
||||||
* -> TLOG_DONT_EXIT_ON_FATAL: don't call 'exit(1)' when using LogFatal
|
* -> TLOG_DONT_EXIT_ON_FATAL: don't call 'exit(1)' when using LogFatal
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
LogAll, LogTrace, LogDebug, LogInfo, LogWarning, LogError, LogFatal
|
LogAll, LogTrace, LogDebug, LogInfo, LogWarning, LogError, LogFatal
|
||||||
} LogLevel;
|
} LogLevel;
|
||||||
|
|
||||||
void traceLog(LogLevel level, const char *fmt, ...);
|
void traceLog(LogLevel level, const char *fmt, ...);
|
||||||
void traceUseNewline(bool use_newline);
|
void traceUseNewline(bool use_newline);
|
||||||
|
|
||||||
#define trace(...) traceLog(LogTrace, __VA_ARGS__)
|
#define tall(...) traceLog(LogAll, __VA_ARGS__)
|
||||||
#define debug(...) traceLog(LogDebug, __VA_ARGS__)
|
#define trace(...) traceLog(LogTrace, __VA_ARGS__)
|
||||||
#define info(...) traceLog(LogInfo, __VA_ARGS__)
|
#define debug(...) traceLog(LogDebug, __VA_ARGS__)
|
||||||
#define warn(...) traceLog(LogWarning, __VA_ARGS__)
|
#define info(...) traceLog(LogInfo, __VA_ARGS__)
|
||||||
#define err(...) traceLog(LogError, __VA_ARGS__)
|
#define warn(...) traceLog(LogWarning, __VA_ARGS__)
|
||||||
#define fatal(...) traceLog(LogFatal, __VA_ARGS__)
|
#define err(...) traceLog(LogError, __VA_ARGS__)
|
||||||
|
#define fatal(...) traceLog(LogFatal, __VA_ARGS__)
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
603
vec.h
Normal file
603
vec.h
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue