diff --git a/include/eventloop.h b/include/eventloop.h new file mode 100644 index 0000000..220f27b --- /dev/null +++ b/include/eventloop.h @@ -0,0 +1,6 @@ +#pragma once + +struct eventloop; + +struct eventloop *eventloop_new(void); +void eventloop_free(struct eventloop *el); diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..901e2ff --- /dev/null +++ b/include/io.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "scoped.h" + +static inline void _close_if_nonnegative(int fd) { + if (fd >= 0) + close(fd); +} + +DEF_SCOPED_TYPE(fd, int, -1, _close_if_nonnegative) + +#define FD_SCOPED(VAL) SCOPED(fd, int, VAL) +#define FD_CLOSE(FD) _scoped_fd_cleanup(FD) +#define FD_RELEASE(FD) _scoped_fd_release(FD) + +static inline int setsockopt_int(int fd, int level, int option, int val) { + return setsockopt(fd, level, option, &val, sizeof(val)); +} diff --git a/include/mem.h b/include/mem.h new file mode 100644 index 0000000..3d70c45 --- /dev/null +++ b/include/mem.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include "scoped.h" + +DEF_SCOPED_TYPE(mem, void *, NULL, free) + +#define MEM_SCOPED(TYPE, VAL) SCOPED(mem, TYPE *, VAL) +#define MEM_FREE(TYPE, MEM) ({ CHECK_TYPE(TYPE **, MEM), (TYPE *)_scoped_mem_cleanup((void **)(MEM)); }) +#define MEM_RELEASE(TYPE, MEM) ({ CHECK_TYPE(TYPE **, MEM), (TYPE *)_scoped_mem_release((void **)(MEM)); }) diff --git a/include/scoped.h b/include/scoped.h new file mode 100644 index 0000000..f6812c1 --- /dev/null +++ b/include/scoped.h @@ -0,0 +1,29 @@ +#pragma once + +#define CHECK_TYPE(TYPE, VAL) ({ \ + typedef void (*_check_type_dummy_fn_t)(TYPE); \ + _check_type_dummy_fn_t _check_type_dummy_fn = (_check_type_dummy_fn_t)NULL; \ + if (0) \ + _check_type_dummy_fn(VAL); \ + 1; \ +}) + +#define DEF_SCOPED_TYPE(NAME, TYPE, UNOCCUPIED, CLEANUP_FN) \ + static inline TYPE _scoped_##NAME##_unoccupied(void) { \ + return (UNOCCUPIED); \ + } \ + static inline void _scoped_##NAME##_cleanup(TYPE *_val) { \ + CLEANUP_FN(*_val); \ + *_val = (UNOCCUPIED); \ + } \ + static inline void _scoped_##NAME##_cleanup_raw(void *_val) { \ + _scoped_##NAME##_cleanup((TYPE *)_val); \ + } \ + static inline TYPE _scoped_##NAME##_release(TYPE *_val) { \ + TYPE _released = *_val; \ + *_val = (UNOCCUPIED); \ + return _released; \ + } + +#define SCOPED(NAME, TYPE, VAL) \ + TYPE VAL __attribute__((cleanup(_scoped_##NAME##_cleanup_raw))) = _scoped_##NAME##_unoccupied() diff --git a/include/str.h b/include/str.h index 2306caf..9a9b258 100644 --- a/include/str.h +++ b/include/str.h @@ -1,22 +1,60 @@ #pragma once +#include #include #include -#define SV_LIT(S) (S "LITERAL_CHECK", (str_view_t) { S, sizeof(S) - 1 }) +#define SV_LIT(S) ((void)(S "LITERAL_CHECK"), (str_view_t) { S, sizeof(S) - 1 }) #define SV_NULL ((str_view_t) { NULL, 0 }) +#define SB_NULL ((str_buf_t) { NULL, 0 }) +#define SB_SCOPED(NAME) str_buf_t NAME __attribute__((cleanup(sb_free))) = SB_NULL +#define SV_PRI "%.*s" +#define SV_PRI_ARGS(S) (int)((S).len), (S).ptr + +// String which is: +// - Not owned +// - Not necessarily heap-allocated +// - Not necessarily nul-terminated typedef struct { const char *ptr; size_t len; } str_view_t; +// String which is: +// - Owned +// - Heap-allocated +// - Nul-terminated +// The length does not include the nul terminator. +typedef struct { + char *ptr; + size_t len; +} str_buf_t; + +static inline bool sv_is_null(str_view_t s) { + return !s.ptr; +} + +static inline bool sv_is_empty(str_view_t s) { + return !s.len; +} + +static inline bool sb_is_null(str_buf_t buf) { + return !buf.ptr; +} + +static inline bool sb_is_empty(str_buf_t buf) { + return !buf.len; +} + static inline str_view_t sv_from_parts(const char *ptr, size_t len) { return (str_view_t) { ptr, len }; } static inline str_view_t sv_from_cstr(const char *ptr, size_t maxlen) { - size_t len = strnlen(ptr, maxlen); + size_t len; + + len = strnlen(ptr, maxlen); return sv_from_parts(ptr, len); } @@ -24,3 +62,27 @@ static inline str_view_t sv_from_cstr_unbounded(const char *ptr) { size_t len = strlen(ptr); return sv_from_parts(ptr, len); } + +static inline bool sv_eq(str_view_t s1, str_view_t s2) { + return (s1.len == s2.len) && ((!s1.ptr || !s2.ptr) + ? (s1.ptr == s2.ptr) + : !memcmp(s1.ptr, s2.ptr, s1.len)); +} + +static inline str_view_t sv_substr(str_view_t s, size_t start, size_t end) { + if ((start > end) || (start > s.len) || (end > s.len)) + return SV_NULL; + return sv_from_parts(s.ptr + start, end - start); +} + +static inline str_view_t sb_view(str_buf_t buf) { + return sv_from_parts(buf.ptr, buf.len); +} + +str_buf_t sb_from_sv(str_view_t s); + +static inline void sb_free(str_buf_t *buf) { + free(buf->ptr); + buf->ptr = NULL; + buf->len = 0; +} diff --git a/meson.build b/meson.build index 84d4678..fdee116 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,8 @@ sources = [ 'src/main.c', 'src/server.c', 'src/client.c', + 'src/str.c', + 'src/eventloop.c', ] includes = include_directories('include') diff --git a/src/eventloop.c b/src/eventloop.c new file mode 100644 index 0000000..621dd1e --- /dev/null +++ b/src/eventloop.c @@ -0,0 +1,28 @@ +#include +#include +#include + +#include "eventloop.h" +#include "mem.h" + +struct eventloop { + int kqueue_fd; +}; + +struct eventloop *eventloop_new(void) { + MEM_SCOPED(struct eventloop, el); + + if (!(el = malloc(sizeof(struct eventloop)))) { + errno = ENOMEM; + return NULL; + } + + return MEM_RELEASE(struct eventloop, &el); +} + +void eventloop_free(struct eventloop *el) { + if (!el) + return; + close(el->kqueue_fd); + free(el); +} diff --git a/src/str.c b/src/str.c new file mode 100644 index 0000000..8339b6a --- /dev/null +++ b/src/str.c @@ -0,0 +1,18 @@ +#include +#include + +#include "str.h" + +str_buf_t sb_from_sv(str_view_t s) { + char *buf; + + if (sv_is_null(s) || sv_is_empty(s)) + return SB_NULL; + + if (!(buf = malloc(s.len + 1))) + return SB_NULL; + + memcpy(buf, s.ptr, s.len); + buf[s.len] = 0; + return (str_buf_t) { buf, s.len }; +}