From 642b5fb0007b64c77d186fcb018d571152ee1d47 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Mon, 14 Aug 2023 18:51:07 +0200 Subject: reorganization: first steps --- src/libk/fsutil.c | 51 +++++++ src/libk/include/assert.h | 7 + src/libk/include/camellia/errno.h | 28 ++++ src/libk/include/camellia/execbuf.h | 9 ++ src/libk/include/camellia/flags.h | 35 +++++ src/libk/include/camellia/fsutil.h | 12 ++ src/libk/include/camellia/path.h | 17 +++ src/libk/include/camellia/syscalls.h | 96 +++++++++++++ src/libk/include/camellia/types.h | 41 ++++++ src/libk/include/shared/mem.h | 18 +++ src/libk/include/shared/printf.h | 6 + src/libk/include/shared/ring.h | 23 +++ src/libk/mem.c | 82 +++++++++++ src/libk/path.c | 53 +++++++ src/libk/printf.c | 268 +++++++++++++++++++++++++++++++++++ src/libk/ring.c | 60 ++++++++ 16 files changed, 806 insertions(+) create mode 100644 src/libk/fsutil.c create mode 100644 src/libk/include/assert.h create mode 100644 src/libk/include/camellia/errno.h create mode 100644 src/libk/include/camellia/execbuf.h create mode 100644 src/libk/include/camellia/flags.h create mode 100644 src/libk/include/camellia/fsutil.h create mode 100644 src/libk/include/camellia/path.h create mode 100644 src/libk/include/camellia/syscalls.h create mode 100644 src/libk/include/camellia/types.h create mode 100644 src/libk/include/shared/mem.h create mode 100644 src/libk/include/shared/printf.h create mode 100644 src/libk/include/shared/ring.h create mode 100644 src/libk/mem.c create mode 100644 src/libk/path.c create mode 100644 src/libk/printf.c create mode 100644 src/libk/ring.c (limited to 'src/libk') diff --git a/src/libk/fsutil.c b/src/libk/fsutil.c new file mode 100644 index 0000000..05ca44a --- /dev/null +++ b/src/libk/fsutil.c @@ -0,0 +1,51 @@ +#include +#include +#include + +void fs_normslice(long *restrict offset, size_t *restrict length, size_t max, bool expand) +{ + assert(max <= (size_t)LONG_MAX); + + if (*offset < 0) { + /* Negative offsets are relative to EOF + 1. + * Thus: + * write(-1) writes right after the file ends; atomic append + * write(-n) writes, overriding the last (n-1) bytes + * read(-n) reads the last (n-1) bytes + */ + *offset += max + 1; + if (*offset < 0) { + /* cursor went before the file, EOF */ + *length = 0; + *offset = max; + goto end; + } + } + + if (expand) { + /* This is a write() to a file with a dynamic size. + * We don't care if it goes past the current size, because the + * driver can handle expanding it. */ + } else { + /* This operation can't extend the file, it's either: + * - any read() + * - a write() to a file with a static size (e.g. a framebuffer) + * *offset and *length describe a slice of a buffer with length max, + * so their sum must not overflow it. */ + if ((size_t)*offset <= max) { + size_t maxlen = max - (size_t)*offset; + if (*length > maxlen) + *length = maxlen; + } else { + /* regular EOF */ + *length = 0; + *offset = max; + goto end; + } + } + +end: + assert(0 <= *offset); + if (!expand) + assert(*offset + *length <= max); +} diff --git a/src/libk/include/assert.h b/src/libk/include/assert.h new file mode 100644 index 0000000..7520aa9 --- /dev/null +++ b/src/libk/include/assert.h @@ -0,0 +1,7 @@ +#ifdef NDEBUG +#define assert(stmt) do {} while (0) +#else +#define assert(stmt) do { if (!(stmt)) __badassert(__func__, __FILE__, __LINE__); } while (0) +#endif + +_Noreturn void __badassert(const char *func, const char *file, int line); diff --git a/src/libk/include/camellia/errno.h b/src/libk/include/camellia/errno.h new file mode 100644 index 0000000..1177f54 --- /dev/null +++ b/src/libk/include/camellia/errno.h @@ -0,0 +1,28 @@ +#pragma once +/* the comments are directly pasted into user visible strings. + * keep them short, don't include " */ + +#define EGENERIC 1 /* unknown error */ +#define EFAULT 2 +#define EBADF 3 /* bad file descriptor */ +#define EINVAL 4 +#define ENOSYS 5 /* unsupported */ +#define ERANGE 6 +#define ENOMEM 7 +#define ENOENT 8 +#define ENOTEMPTY 9 +#define EACCES 10 +#define EMFILE 11 /* all file descriptors taken */ +#define ECONNRESET 12 +#define EPIPE 13 +#define ECHILD 14 + +#define EISDIR 200 +#define ENAMETOOLONG 201 +#define ENOTDIR 202 +#define ELOOP 203 +#define ENOEXEC 204 +#define EINTR 205 +#define EWOULDBLOCK 206 +#define EEXIST 207 +#define EAGAIN 208 diff --git a/src/libk/include/camellia/execbuf.h b/src/libk/include/camellia/execbuf.h new file mode 100644 index 0000000..de7ae3b --- /dev/null +++ b/src/libk/include/camellia/execbuf.h @@ -0,0 +1,9 @@ +#pragma once +/* the instruction format is bound to change, atm it's extremely inefficient */ + +#define EXECBUF_MAX_LEN 4096 + +/* takes 6 arguments */ +#define EXECBUF_SYSCALL 0xF0000001 +/* takes 1 argument, changes %rip */ +#define EXECBUF_JMP 0xF0000002 diff --git a/src/libk/include/camellia/flags.h b/src/libk/include/camellia/flags.h new file mode 100644 index 0000000..f4c54fe --- /dev/null +++ b/src/libk/include/camellia/flags.h @@ -0,0 +1,35 @@ +#pragma once + +#define MEMFLAG_PRESENT 1 +#define MEMFLAG_FINDFREE 2 + +#define FORK_NOREAP 1 +#define FORK_NEWFS 2 +#define FORK_SHAREMEM 4 +#define FORK_SHAREHANDLE 8 + +#define WRITE_TRUNCATE 1 + +#define FSR_DELEGATE 1 + +#define DUP_SEARCH 1 + +#define OPEN_READ 1 +#define OPEN_WRITE 2 +#define OPEN_RW 3 +/* not setting OPEN_READ nor OPEN_WRITE works as if OPEN_READ was set, but it also checks the execute bit. + * same as in plan9. */ +#define OPEN_EXEC 0 + +#define OPEN_READABLE(flags) ((flags & 3) != OPEN_WRITE) +#define OPEN_WRITEABLE(flags) (flags & OPEN_WRITE) + +/* Requires OPEN_WRITE to be set, enforced by the kernel. + * The idea is that if all flags which allow modifying the filesystem state require + * OPEN_WRITE to be set, filesystem handlers could just check for the OPEN_WRITE flag. */ +#define OPEN_CREATE 4 + + +/* special handles */ +#define HANDLE_NULLFS -2 +#define HANDLE_PROCFS -3 diff --git a/src/libk/include/camellia/fsutil.h b/src/libk/include/camellia/fsutil.h new file mode 100644 index 0000000..8b8c4fc --- /dev/null +++ b/src/libk/include/camellia/fsutil.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +/** Normalizes the offset and length passed to a fs into safe values. + * + * @param expand Can this operation expand the target file? + * true if writing to a file with an adjustable size + * false if reading any sort of file + * or writing to a file with static size + */ +void fs_normslice(long *restrict offset, size_t *restrict length, size_t max, bool expand); diff --git a/src/libk/include/camellia/path.h b/src/libk/include/camellia/path.h new file mode 100644 index 0000000..b268595 --- /dev/null +++ b/src/libk/include/camellia/path.h @@ -0,0 +1,17 @@ +#pragma once +#include + +#define PATH_MAX 512 + +/** Reduce a path to its simplest form. + * Kinds of invalid paths: + * - relative - "" "a" "./a" + * - going behind the root directory - "/../" + * + * @return On success, length of the string in *out, <= len. 0 if the path was invalid. + * + * returns an unsigned type because: + * 1. valid paths always return at least 1, for the initial slash + * 2. it makes it easier to assign the result to an unsigned variable and check for error + */ +size_t path_simplify(const char *in, char *out, size_t len); diff --git a/src/libk/include/camellia/syscalls.h b/src/libk/include/camellia/syscalls.h new file mode 100644 index 0000000..9a8fa94 --- /dev/null +++ b/src/libk/include/camellia/syscalls.h @@ -0,0 +1,96 @@ +#pragma once + +#define _SYS_EXIT 0 +#define _SYS_AWAIT 1 +#define _SYS_FORK 2 +#define _SYS_OPEN 3 +#define _SYS_MOUNT 4 +#define _SYS_DUP 5 +#define _SYS_READ 6 +#define _SYS_WRITE 7 +#define _SYS_GETSIZE 8 +#define _SYS_REMOVE 9 +#define _SYS_CLOSE 10 +#define _SYS_FS_WAIT 11 +#define _SYS_FS_RESPOND 12 +#define _SYS_MEMFLAG 13 +#define _SYS_PIPE 14 +#define _SYS_SLEEP 15 +#define _SYS_FILICIDE 16 +#define _SYS_INTR 17 +#define _SYS_INTR_SET 18 +#define _SYS_GETPID 19 +#define _SYS_GETPPID 20 +#define _SYS_WAIT2 21 + +#define _SYS_EXECBUF 100 +#define _SYS_DEBUG_KLOG 101 + +#ifndef ASM_FILE +#include +#include + +long _syscall(long, long, long, long, long, long); + +/** Kills the current process. + */ +_Noreturn void _sys_exit(long ret); + +/** Waits for a child to exit. + * @return the value the child passed to exit() + */ +long _sys_await(void); + +/** Creates a copy of the current process, and executes it. + * All user memory pages get copied too. + * + * @param flags FORK_NOREAP, FORK_NEWFS + * @param fs_front requires FORK_NEWFS. the front handle to the new fs is put there + * + * @return 0 in the child, the CID in the parent. + */ +long _sys_fork(int flags, hid_t __user *fs_front); + +hid_t _sys_open(const char __user *path, long len, int flags); +long _sys_mount(hid_t h, const char __user *path, long len); +hid_t _sys_dup(hid_t from, hid_t to, int flags); + +long _sys_read(hid_t h, void __user *buf, size_t len, long offset); +long _sys_write(hid_t h, const void __user *buf, size_t len, long offset, int flags); +long _sys_getsize(hid_t h); +long _sys_remove(hid_t h); +long _sys_close(hid_t h); + +hid_t _sys_fs_wait(char __user *buf, long max_len, struct ufs_request __user *res); +long _sys_fs_respond(hid_t hid, const void __user *buf, long ret, int flags); + +/** Modifies the virtual address space. + * + * If the MEMFLAG_PRESENT flag is present - mark the memory region as allocated. + * Otherwise, free it. + * + * MEMFLAG_FINDFREE tries to find the first free region of length `len`. + * + * @return address of the first affected page (usually == addr) + */ +void __user *_sys_memflag(void __user *addr, size_t len, int flags); +long _sys_pipe(hid_t __user user_ends[2], int flags); + +void _sys_sleep(long ms); + +void _sys_filicide(void); +void _sys_intr(void); +void _sys_intr_set(void __user *ip); + +uint32_t _sys_getpid(void); +uint32_t _sys_getppid(void); + +// TODO deprecate await +int _sys_wait2(int pid, int flags, struct sys_wait2 __user *out); + +/* see shared/execbuf.h */ +long _sys_execbuf(void __user *buf, size_t len); + +void _sys_debug_klog(const void __user *buf, size_t len); + +#endif diff --git a/src/libk/include/camellia/types.h b/src/libk/include/camellia/types.h new file mode 100644 index 0000000..65200d7 --- /dev/null +++ b/src/libk/include/camellia/types.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include + +#ifdef __CHECKER__ +# define __user __attribute__((noderef, address_space(__user))) +# define __force __attribute__((force)) +#else +# define __user +# define __force +#endif + +typedef void __user * userptr_t; +typedef int hid_t; + +enum vfs_op { + VFSOP_OPEN, + VFSOP_READ, + VFSOP_WRITE, + VFSOP_GETSIZE, + VFSOP_REMOVE, + VFSOP_CLOSE, +}; + +struct ufs_request { + enum vfs_op op; + size_t len; // how much was put in *buf + size_t capacity; // how much output can be accepted by the caller + void __user *id; // file id (returned by the open handler, passed to other calls) + long offset; + int flags; +}; + +struct intr_data { + void __user *ip; + void __user *sp; /* last for pop %rsp */ +}; + +struct sys_wait2 { + long status; +}; diff --git a/src/libk/include/shared/mem.h b/src/libk/include/shared/mem.h new file mode 100644 index 0000000..3597edf --- /dev/null +++ b/src/libk/include/shared/mem.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +/* note: (partially) tested in the userland tests */ + +void *memchr(const void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); + +int strcmp(const char *s1, const char *s2); +size_t strlen(const char *s); + +int snprintf(char *restrict str, size_t len, const char *restrict fmt, ...); +int vsnprintf(char *restrict str, size_t len, const char *restrict fmt, va_list ap); diff --git a/src/libk/include/shared/printf.h b/src/libk/include/shared/printf.h new file mode 100644 index 0000000..45fd358 --- /dev/null +++ b/src/libk/include/shared/printf.h @@ -0,0 +1,6 @@ +#pragma once +#include +#include + +int __printf_internal(const char *fmt, va_list argp, + void (*back)(void*, const char*, size_t), void *back_arg); diff --git a/src/libk/include/shared/ring.h b/src/libk/include/shared/ring.h new file mode 100644 index 0000000..dbaf331 --- /dev/null +++ b/src/libk/include/shared/ring.h @@ -0,0 +1,23 @@ +#pragma once +#include +#include + +typedef struct { + char *buf; + size_t capacity; + size_t _head, _tail; +} ring_t; + +/** Returns amount of bytes stored in the buffer. */ +size_t ring_used(ring_t*); +/** Returns amount of space left in the buffer. */ +size_t ring_avail(ring_t*); + +void ring_put(ring_t*, const void*, size_t); +void ring_put1b(ring_t*, uint8_t); + +size_t ring_get(ring_t*, void*, size_t); + +/** Consumes up to `len` bytes, and returns a pointer to the buffer where it's stored. + * Not thread-safe. */ +void* ring_contig(ring_t*, size_t *len); diff --git a/src/libk/mem.c b/src/libk/mem.c new file mode 100644 index 0000000..14dd6bd --- /dev/null +++ b/src/libk/mem.c @@ -0,0 +1,82 @@ +#include +#include +#include + +union dualptr_const { const uintptr_t *w; const char *c; uintptr_t u; }; +union dualptr { uintptr_t *w; char *c; uintptr_t u; }; + +void *memchr(const void *s, int c, size_t n) { + const unsigned char *s2 = s; + for (size_t i = 0; i < n; i++) { + if (s2[i] == (unsigned char)c) + return (void*)&s2[i]; + } + return NULL; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const unsigned char *c1 = s1, *c2 = s2; + for (size_t i = 0; i < n; i++) { + if (c1[i] != c2[i]) { + if (c1[i] < c2[i]) return -1; + else return 1; + } + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t n) { + // TODO erms, rep movsb + union dualptr_const s = {.c = src}; + union dualptr d = {.c = dest}; + if (dest == src) return dest; + // assert(src >= dest || src + n < dest); + + for (; (d.u & 7) && n != 0; n--) { + *(d.c)++ = *(s.c)++; + } + while (n >= sizeof(uintptr_t)) { + *(d.w)++ = *(s.w)++; + n -= sizeof(uintptr_t); + } + while (n-- != 0) { + *(d.c)++ = *(s.c)++; + } + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) { + if (src >= dest || src + n < dest) + return memcpy(dest, src, n); + for (; n; n--) /* naive reverse copy */ + ((uint8_t*)dest)[n-1] = ((uint8_t*)src)[n-1]; + return dest; +} + +void *memset(void *s, int c, size_t n) { + union dualptr d = {.c = s}; + + uintptr_t fill = (c & 0xff) * 0x0101010101010101; + while (n >= sizeof(uintptr_t)) { + *(d.w)++ = fill; + n -= sizeof(uintptr_t); + } + while (n-- != 0) + *(d.c)++ = c; + return s; +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && *s1 == *s2) { + s1++; s2++; + } + if (*s1 == *s2) return 0; + if (*s1 < *s2) return -1; + else return 1; +} + +size_t strlen(const char *s) { + size_t c = 0; + while (*s++) c++; + return c; +} diff --git a/src/libk/path.c b/src/libk/path.c new file mode 100644 index 0000000..4e3077b --- /dev/null +++ b/src/libk/path.c @@ -0,0 +1,53 @@ +#include +#include +#include +#include + +size_t path_simplify(const char *in, char *out, size_t len) { + if (len == 0) return 0; /* empty paths are invalid */ + if (in[0] != '/') return 0; /* so are relative paths */ + + int seg_len; + int out_pos = 0; + bool directory = false; + + for (size_t i = 0; i < len; i += seg_len + 1) { + assert(in[i] == '/'); + + seg_len = 0; + directory = false; + for (size_t j = i + 1; j < len; j++) { + if (in[j] == '/') { + directory = true; + break; + } + seg_len++; + } + + /* |i=5 |next i = i + seg_len + 1 = 10 + * v v + * /some/path/asdf + * |--| + * seg_len = 4, segment starts at i+1 */ + + if (seg_len == 0 || (seg_len == 1 && in[i + 1] == '.')) { + /* // or /./ */ + directory = true; + } else if (seg_len == 2 && in[i + 1] == '.' && in[i + 2] == '.') { + /* /../ */ + directory = true; + /* try to backtrack to the last slash */ + while (--out_pos >= 0 && out[out_pos] != '/'); + if (out_pos < 0) return 0; + } else { + /* a normal segment, e.g. /asdf/ */ + out[out_pos] = '/'; + memcpy(&out[out_pos + 1], &in[i + 1], seg_len); + out_pos += seg_len + 1; + } + } + + if (directory) out[out_pos++] = '/'; + assert(0 < out_pos && (size_t)out_pos <= len); + return out_pos; +} diff --git a/src/libk/printf.c b/src/libk/printf.c new file mode 100644 index 0000000..fffd801 --- /dev/null +++ b/src/libk/printf.c @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include + +enum lenmod { + LM_int, + LM_long, + LM_longlong, + LM_size, +}; + +struct out_state { + void (*back)(void *, const char *, size_t); + void *backarg; + int written; + + char *cache; + size_t cpos, clen; +}; + +struct mods { + char fill_char; + size_t field_width; + size_t precision; +}; + + +static int flush(struct out_state *os) { + if (os->cpos) { + os->back(os->backarg, os->cache, os->cpos); + os->written += os->cpos; + os->cpos = 0; + } + return os->written; +} + +static void output(struct out_state *os, const char *buf, size_t len) { + if (os->cpos + len < os->clen) { + memcpy(os->cache + os->cpos, buf, len); + os->cpos += len; + return; + } + flush(os); + os->back(os->backarg, buf, len); + os->written += len; +} + +static void output_c(struct out_state *os, char c, int amt) { + for (int i = 0; i < amt; i++) + output(os, &c, 1); +} + + +static void pad(struct out_state *os, struct mods *m, size_t len) { + output_c(os, m->fill_char, m->field_width - len); +} + +static void padnum(struct out_state *os, struct mods *m, size_t len, char sign) { + if (len < m->precision) { + output_c(os, m->fill_char, m->field_width - m->precision - (sign ? 1 : 0)); + if (sign) output_c(os, sign, 1); + output_c(os, '0', m->precision - len); + } else { + output_c(os, m->fill_char, m->field_width - len - (sign ? 1 : 0)); + if (sign) output_c(os, sign, 1); + } +} + +static void output_uint(struct out_state *os, struct mods *m, unsigned long long n, char sign) { + char buf[sizeof(unsigned long long) * 3]; + size_t pos = sizeof(buf); + + if (!n) { + buf[--pos] = '0'; + } else while (n) { + unsigned long long q = n / 10, r = n % 10; + buf[--pos] = r + '0'; + n = q; + } + size_t len = sizeof(buf) - pos; + padnum(os, m, len, sign); + output(os, buf + pos, len); +} + +static void output_uint16(struct out_state *os, struct mods *m, unsigned long long n) { + size_t len = 1; + while (n >> (len * 4) && (len * 4) < (sizeof(n) * 8)) + len++; + padnum(os, m, len, '\0'); + while (len-- > 0) { + char h = '0' + ((n >> (len * 4)) & 0xf); + if (h > '9') h += 'a' - '9' - 1; + output_c(os, h, 1); + } +} + + +int __printf_internal(const char *fmt, va_list argp, + void (*back)(void*, const char*, size_t), void *backarg) +{ + const char *seg = fmt; /* beginning of the current non-modifier streak */ + char cache[64]; + struct out_state os = { + .back = back, + .backarg = backarg, + .cache = cache, + .cpos = 0, + .clen = sizeof(cache), + }; + + for (;;) { + char c = *fmt++; + if (c == '\0') { + output(&os, seg, fmt - seg - 1); + return flush(&os); + } + if (c != '%') continue; + output(&os, seg, fmt - seg - 1); + + struct mods m = { + .fill_char = ' ', + .field_width = 0, + .precision = 0, + }; + + for (bool modifier = true; modifier;) { + c = *fmt++; + switch (c) { + case '0': + m.fill_char = '0'; + break; + default: + modifier = false; + break; + } + } + + while ('0' <= c && c <= '9') { + m.field_width *= 10; + m.field_width += c - '0'; + c = *fmt++; + } + + if (c == '.') { + c = *fmt++; + if (c == '*') { + // TODO handle negative precision + m.precision = va_arg(argp, int); + c = *fmt++; + } else while ('0' <= c && c <= '9') { + m.precision *= 10; + m.precision += c - '0'; + c = *fmt++; + } + } + + enum lenmod lm; + switch (c) { + case 'l': + lm = LM_long; + c = *fmt++; + if (c == 'l') { + lm = LM_longlong; + c = *fmt++; + } + break; + case 'z': + lm = LM_size; + c = *fmt++; + break; + default: + lm = LM_int; + break; + } + + switch (c) { + unsigned long n, len; + long ns; + char sign; + + case 'c': + output_c(&os, va_arg(argp, int), 1); + break; + + case 's': + const char *s = va_arg(argp, char*); + if (s == NULL) s = "(null)"; + // TODO can segfault even if precision caps the string + len = strlen(s); + if (len > m.precision && m.precision != 0) + len = m.precision; + pad(&os, &m, len); + output(&os, s, len); + break; + + case 'p': + output(&os, "0x", 2); + output_uint16(&os, &m, (uintptr_t)va_arg(argp, void*)); + break; + + case 'x': + if (lm == LM_int) n = va_arg(argp, unsigned int); + else if (lm == LM_long) n = va_arg(argp, unsigned long); + else if (lm == LM_longlong) n = va_arg(argp, unsigned long long); + else if (lm == LM_size) n = va_arg(argp, size_t); + output_uint16(&os, &m, n); + break; + + case 'u': + if (lm == LM_int) n = va_arg(argp, unsigned int); + else if (lm == LM_long) n = va_arg(argp, unsigned long); + else if (lm == LM_longlong) n = va_arg(argp, unsigned long long); + else if (lm == LM_size) n = va_arg(argp, size_t); + output_uint(&os, &m, n, '\0'); + break; + + case 'd': + case 'i': + if (lm == LM_int) ns = va_arg(argp, int); + else if (lm == LM_long) ns = va_arg(argp, long); + else if (lm == LM_longlong) ns = va_arg(argp, long long); + else if (lm == LM_size) ns = va_arg(argp, size_t); + sign = '\0'; + if (ns < 0) { + ns = -ns; + sign = '-'; + } + output_uint(&os, &m, (long)ns, sign); + break; + + case '%': + output(&os, "%", 1); + break; + } + seg = fmt; + } +} + + +static void vsnprintf_backend(void *arg, const char *buf, size_t len) { + char **ptrs = arg; + size_t space = ptrs[1] - ptrs[0]; + if (!ptrs[0]) return; + if (len > space) len = space; + + memcpy(ptrs[0], buf, len); + ptrs[0] += len; + /* ptrs[1] is the last byte of the buffer, it must be 0. + * on overflow: + * ptrs[0] + (ptrs[1] - ptrs[0]) = ptrs[1] */ + *ptrs[0] = '\0'; +} + +int vsnprintf(char *restrict str, size_t len, const char *restrict fmt, va_list ap) { + char *ptrs[2] = {str, str + len - 1}; + return __printf_internal(fmt, ap, vsnprintf_backend, &ptrs); +} + +int snprintf(char *restrict str, size_t len, const char *restrict fmt, ...) { + int ret; + va_list argp; + va_start(argp, fmt); + ret = vsnprintf(str, len, fmt, argp); + va_end(argp); + return ret; +} diff --git a/src/libk/ring.c b/src/libk/ring.c new file mode 100644 index 0000000..44c73f6 --- /dev/null +++ b/src/libk/ring.c @@ -0,0 +1,60 @@ +#include +#include +#include + +static bool at_end(ring_t *r) { + return r->_head + 1 == r->_tail + || (r->_head + 1 == r->capacity && r->_tail == 0); +} + +size_t ring_used(ring_t *r) { + if (r->_head >= r->_tail) + return r->_head - r->_tail; + else + return r->_head + r->capacity - r->_tail; +} + +size_t ring_avail(ring_t *r) { + return r->capacity - ring_used(r); +} + +void ring_put(ring_t *r, const void *buf, size_t len) { + // TODO do something similar to ring_get + for (size_t i = 0; i < len; i++) + ring_put1b(r, ((uint8_t*)buf)[i]); +} + +void ring_put1b(ring_t *r, uint8_t byte) { + if (at_end(r)) return; + r->buf[r->_head++] = byte; + if (r->_head >= r->capacity) r->_head = 0; +} + +size_t ring_get(ring_t *r, void *buf, size_t len) { + size_t read = 0; + size_t plen; + void *pbuf; + for (size_t i = 0; i < 2; i++) { + plen = len - read; + pbuf = ring_contig(r, &plen); + if (buf) memcpy(buf + read, pbuf, plen); + read += plen; + } + return read; +} + +void *ring_contig(ring_t *r, size_t *len) { + void *ret = &r->buf[r->_tail]; + size_t avail; + if (r->_head >= r->_tail) + avail = r->_head - r->_tail; + else + avail = r->capacity - r->_tail; + + if (*len > avail) + *len = avail; + + r->_tail += *len; + if (r->_tail >= r->capacity) r->_tail = 0; + return ret; +} -- cgit v1.2.3