summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2024-12-10 12:24:07 +0100
committerAnton Kling <anton@kling.gg>2024-12-10 12:24:07 +0100
commit916aa42260290e9e864304bc7d9395b6aa693c27 (patch)
tree784cdcbe26e828e18413bf9d31d6a84ed74dd1ba
parentbcca3d183930eeaf3d024476f39d1d8fccf2ebab (diff)
kernel: Add string view and string builder
This makes write/read calls that use strings to communicate much simpler and less error prone.
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/audio.c37
-rw-r--r--kernel/includes/ctype.h1
-rw-r--r--kernel/lib/sb.c124
-rw-r--r--kernel/lib/sb.h30
-rw-r--r--kernel/lib/sv.c247
-rw-r--r--kernel/lib/sv.h43
-rw-r--r--kernel/libc/ctype/isspace.c5
-rw-r--r--kernel/libc/include/string.h1
-rw-r--r--kernel/libc/string/memmove.c14
-rw-r--r--kernel/timer.c22
11 files changed, 482 insertions, 44 deletions
diff --git a/kernel/Makefile b/kernel/Makefile
index a27b108..34b9b30 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -1,6 +1,6 @@
CC="i686-sb-gcc"
AS="i686-sb-as"
-OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o kubsan.o drivers/serial.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o elf.o sched/scheduler.o libc/string/copy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o drivers/pci.o drivers/rtl8139.o network/ethernet.o network/arp.o network/bytes.o network/ipv4.o network/udp.o signal.o network/tcp.o drivers/ahci.o crypto/xoshiro256plusplus/xoshiro256plusplus.o arch/i386/interrupts.o cpu/isr.o lib/stack.o lib/buffered_write.o lib/list.o cpu/arch_inst.o cpu/int_syscall.o lib/ringbuffer.o lib/relist.o arch/i386/tsc.o arch/i386/asm_tsc.o drivers/cmos.o timer.o queue.o fonts.o drivers/ac97.o audio.o libc/ctype/isdigit.o libc/stdlib/parsenum.o libc/ctype/tolower.o
+OBJ = arch/i386/boot.o init/kernel.o cpu/gdt.o cpu/reload_gdt.o cpu/idt.o cpu/io.o libc/stdio/print.o drivers/keyboard.o log.o drivers/pit.o libc/string/memcpy.o libc/string/strlen.o libc/string/memcmp.o drivers/ata.o libc/string/memset.o cpu/syscall.o read_eip.o libc/exit/assert.o process.o libc/string/strcpy.o arch/i386/mmu.o kmalloc.o fs/ext2.o fs/vfs.o fs/devfs.o cpu/spinlock.o random.o libc/string/strcmp.o crypto/ChaCha20/chacha20.o crypto/SHA1/sha1.o fs/tmpfs.o libc/string/isequal.o drivers/pst.o kubsan.o drivers/serial.o socket.o poll.o fs/fifo.o hashmap/hashmap.o fs/shm.o elf.o sched/scheduler.o libc/string/copy.o drivers/mouse.o libc/string/strlcpy.o libc/string/strcat.o drivers/vbe.o drivers/pci.o drivers/rtl8139.o network/ethernet.o network/arp.o network/bytes.o network/ipv4.o network/udp.o signal.o network/tcp.o drivers/ahci.o crypto/xoshiro256plusplus/xoshiro256plusplus.o arch/i386/interrupts.o cpu/isr.o lib/stack.o lib/buffered_write.o lib/list.o cpu/arch_inst.o cpu/int_syscall.o lib/ringbuffer.o lib/relist.o arch/i386/tsc.o arch/i386/asm_tsc.o drivers/cmos.o timer.o queue.o fonts.o drivers/ac97.o audio.o libc/ctype/isdigit.o libc/stdlib/parsenum.o libc/ctype/tolower.o lib/sv.o lib/sb.o libc/string/memmove.o libc/ctype/isspace.o
CFLAGS = -std=c99 -O0 -fsanitize=vla-bound,shift-exponent,pointer-overflow,shift,signed-integer-overflow,bounds -ggdb -ffreestanding -Wall -Wextra -Wno-int-conversion -Wno-unused-parameter -Werror -mgeneral-regs-only -Wimplicit-fallthrough -I./libc/include/ -I. -Wno-pointer-sign -DKERNEL
LDFLAGS=
#CFLAGS = -std=c99 -O3 -flto -ggdb -ffreestanding -Wall -Wextra -Wno-int-conversion -Wno-unused-parameter -Werror -mgeneral-regs-only -Wimplicit-fallthrough -I./libc/include/ -I. -Wno-pointer-sign -DKERNEL
diff --git a/kernel/audio.c b/kernel/audio.c
index d8fcd3c..fe78938 100644
--- a/kernel/audio.c
+++ b/kernel/audio.c
@@ -5,6 +5,7 @@
#include <drivers/ac97.h>
#include <errno.h>
#include <fs/devfs.h>
+#include <lib/sv.h>
#include <math.h>
int audio_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
@@ -25,24 +26,18 @@ int audio_can_write(vfs_inode_t *inode) {
int volume_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
(void)offset;
(void)fd;
- int volume = 0;
-
- size_t i = 0;
- for (; i < len; i++) {
- u8 c = buffer[i];
- if (!isdigit(c)) {
- break;
- }
- volume *= 10;
- volume += c - '0';
- if (volume > 100) {
- volume = 100;
- break;
- }
- }
+ struct sv string_view = sv_init(buffer, len);
+ struct sv rest;
+ u64 volume = sv_parse_unsigned_number(string_view, &rest);
+ int i = sv_length(string_view) - sv_length(rest);
if (0 == i) {
return 0;
}
+
+ if (volume > 100) {
+ volume = 0;
+ }
+
ac97_set_volume(volume);
return i;
}
@@ -52,18 +47,8 @@ int volume_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
return 0;
}
(void)fd;
- if (len < 3) {
- return 0;
- }
int volume = ac97_get_volume();
- assert(volume <= 100);
- if (volume == 100) {
- memcpy(buffer, "100", 3);
- return 3;
- }
- buffer[1] = (volume % 10) + '0';
- buffer[0] = ((volume - (volume % 10)) / 10) + '0';
- return 2;
+ return min(len, (u64)kbnprintf(buffer, len, "%d", volume));
}
static int add_files(void) {
diff --git a/kernel/includes/ctype.h b/kernel/includes/ctype.h
index f6be4f2..e8e0bae 100644
--- a/kernel/includes/ctype.h
+++ b/kernel/includes/ctype.h
@@ -1,2 +1,3 @@
int isdigit(int c);
int tolower(int c);
+int isspace(int c);
diff --git a/kernel/lib/sb.c b/kernel/lib/sb.c
new file mode 100644
index 0000000..3f99d95
--- /dev/null
+++ b/kernel/lib/sb.c
@@ -0,0 +1,124 @@
+#include <kmalloc.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <tb/sb.h>
+
+#define DEFAULT_CAPACITY 256
+
+void sb_init(struct sb *ctx) {
+ (void)sb_init_capacity(ctx, DEFAULT_CAPACITY);
+}
+
+int sb_init_capacity(struct sb *ctx, size_t starting_capacity) {
+ ctx->length = 0;
+ ctx->prebuffer = 0;
+ ctx->string = kmalloc(starting_capacity);
+ if (NULL == ctx->string) {
+ ctx->capacity = 0;
+ return 0;
+ }
+ ctx->capacity = starting_capacity;
+ return 1;
+}
+
+void sb_init_buffer(struct sb *ctx, char *buffer, size_t size) {
+ ctx->string = buffer;
+ ctx->capacity = size;
+ ctx->length = 0;
+ ctx->prebuffer = 1;
+}
+
+void sb_free(struct sb *ctx) {
+ if (ctx->prebuffer) {
+ ctx->length = 0;
+ return;
+ }
+ ctx->length = 0;
+ ctx->capacity = 0;
+ kfree(ctx->string);
+ ctx->string = NULL;
+}
+
+void sb_reset(struct sb *ctx) {
+ ctx->length = 0;
+}
+
+int sb_isempty(const struct sb *ctx) {
+ return (0 == ctx->length);
+}
+
+int sb_increase_buffer(struct sb *ctx, size_t min) {
+ if (ctx->prebuffer) {
+ return 0;
+ }
+ size_t new_capacity = ctx->capacity + max(32, min);
+ char *new_allocation = krealloc(ctx->string, new_capacity);
+ if (!new_allocation) {
+ return 0;
+ }
+
+ ctx->string = new_allocation;
+ ctx->capacity = new_capacity;
+ return 1;
+}
+
+int sb_append_char(struct sb *ctx, char c) {
+ if (1 > ctx->capacity - ctx->length) {
+ if (!sb_increase_buffer(ctx, 1)) {
+ return 0;
+ }
+ }
+ memcpy(ctx->string + ctx->length, &c, 1);
+ ctx->length++;
+ return 1;
+}
+
+int sb_delete_right(struct sb *ctx, size_t n) {
+ n = min(n, ctx->length);
+ ctx->length -= n;
+ return n;
+}
+
+int sb_append(struct sb *ctx, const char *s) {
+ size_t l = strlen(s);
+ if (l > ctx->capacity - ctx->length) {
+ if (!sb_increase_buffer(ctx, l)) {
+ return 0;
+ }
+ }
+ memcpy(ctx->string + ctx->length, s, l);
+ ctx->length += l;
+ return 1;
+}
+
+int sb_prepend_sv(struct sb *ctx, struct sv sv) {
+ return sb_prepend_buffer(ctx, sv.s, sv.length);
+}
+
+int sb_prepend_buffer(struct sb *ctx, const char *buffer, size_t length) {
+ if (length > ctx->capacity - ctx->length) {
+ if (!sb_increase_buffer(ctx, length)) {
+ return 0;
+ }
+ }
+ memmove(ctx->string + length, ctx->string, ctx->length);
+ memcpy(ctx->string, buffer, length);
+ ctx->length += length;
+ return 1;
+}
+
+int sb_append_buffer(struct sb *ctx, const char *buffer, size_t length) {
+ if (length > ctx->capacity - ctx->length) {
+ if (!sb_increase_buffer(ctx, length)) {
+ return 0;
+ }
+ }
+ memcpy(ctx->string + ctx->length, buffer, length);
+ ctx->length += length;
+ return 1;
+}
+
+int sb_append_sv(struct sb *ctx, struct sv sv) {
+ return sb_append_buffer(ctx, sv.s, sv.length);
+}
diff --git a/kernel/lib/sb.h b/kernel/lib/sb.h
new file mode 100644
index 0000000..8493997
--- /dev/null
+++ b/kernel/lib/sb.h
@@ -0,0 +1,30 @@
+#ifndef SB_H
+#define SB_H
+#include "sv.h"
+#include <stddef.h>
+#include <stdint.h>
+
+struct sb {
+ char *string;
+ size_t length;
+ size_t capacity;
+ uint8_t prebuffer;
+};
+
+struct sv;
+
+void sb_init(struct sb *ctx);
+int sb_init_capacity(struct sb *ctx, size_t starting_capacity);
+void sb_init_buffer(struct sb *ctx, char *buffer, size_t size);
+void sb_free(struct sb *ctx);
+void sb_reset(struct sb *ctx);
+int sb_isempty(const struct sb *ctx);
+int sb_append_char(struct sb *ctx, char c);
+int sb_delete_right(struct sb *ctx, size_t n);
+int sb_append(struct sb *ctx, const char *s);
+int sb_append_buffer(struct sb *ctx, const char *buffer,
+ size_t length);
+int sb_append_sv(struct sb *ctx, struct sv sv);
+int sb_prepend_sv(struct sb *ctx, struct sv sv);
+int sb_prepend_buffer(struct sb *ctx, const char *buffer, size_t length);
+#endif
diff --git a/kernel/lib/sv.c b/kernel/lib/sv.c
new file mode 100644
index 0000000..580e63a
--- /dev/null
+++ b/kernel/lib/sv.c
@@ -0,0 +1,247 @@
+#include <ctype.h>
+#include <kmalloc.h>
+#include <lib/sv.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sv sv_init(const char *s, size_t length) {
+ return (struct sv){.s = s, .length = length};
+}
+
+char *SV_TO_C(struct sv s) {
+ char *c_string = kmalloc(s.length + 1);
+ memcpy(c_string, s.s, s.length);
+ c_string[s.length] = '\0';
+ return c_string;
+}
+
+size_t sv_to_cstring_buffer(struct sv s, char *buffer, size_t length) {
+ if (0 == length || length - 1 < s.length) {
+ return s.length;
+ }
+ memcpy(buffer, s.s, s.length);
+ buffer[s.length] = '\0';
+ return s.length;
+}
+
+struct sv sv_next(struct sv s) {
+ if (0 == s.length) {
+ return s;
+ }
+ s.length--;
+ s.s++;
+ return s;
+}
+
+struct sv sv_skip_chars(const struct sv input, const char *chars) {
+ struct sv r = input;
+ for (; r.length > 0;) {
+ int found = 0;
+ const char *p = chars;
+ for (; *p; p++) {
+ if (*p == r.s[0]) {
+ r.s++;
+ r.length--;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ break;
+ }
+ }
+ return r;
+}
+
+uint64_t sv_parse_unsigned_number(struct sv input, struct sv *rest) {
+ uint64_t r = 0;
+ size_t i = 0;
+ for (; i < input.length; i++) {
+ if (!isdigit(input.s[i])) {
+ break;
+ }
+ r *= 10;
+ r += input.s[i] - '0';
+ }
+ input.length -= i;
+ input.s += i;
+ if (rest) {
+ *rest = input;
+ }
+ return r;
+}
+
+struct sv sv_split_function(const struct sv input, struct sv *rest,
+ int (*function)(int)) {
+ struct sv r = {
+ .s = input.s,
+ };
+ for (size_t i = 0; i < input.length; i++) {
+ if (function(input.s[i])) {
+ r.length = i;
+ if (rest) {
+ rest->s += i;
+ rest->length -= i;
+ }
+ return r;
+ }
+ }
+
+ if (rest) {
+ rest->s = NULL;
+ rest->length = 0;
+ }
+ return input;
+}
+
+struct sv sv_split_space(const struct sv input, struct sv *rest) {
+ return sv_split_function(input, rest, isspace);
+}
+
+struct sv sv_end_split_delim(const struct sv input, struct sv *rest,
+ char delim) {
+ for (size_t i = input.length - 1; i > 0; i--) {
+ if (delim == input.s[i]) {
+ struct sv r = {
+ .s = (input.s + i),
+ .length = input.length - i,
+ };
+ if (rest) {
+ rest->s = input.s;
+ rest->length = i;
+ }
+ return r;
+ }
+ }
+
+ if (rest) {
+ rest->s = NULL;
+ rest->length = 0;
+ }
+ return input;
+}
+
+struct sv sv_split_delim(const struct sv input, struct sv *rest, char delim) {
+ struct sv r = {
+ .s = input.s,
+ };
+ for (size_t i = 0; i < input.length; i++) {
+ if (delim == input.s[i]) {
+ r.length = i;
+ if (rest) {
+ rest->s += i + 1;
+ rest->length -= (i + 1);
+ }
+ return r;
+ }
+ }
+
+ if (rest) {
+ rest->s = NULL;
+ rest->length = 0;
+ }
+ return input;
+}
+
+int sv_isempty(struct sv s) {
+ return (0 == s.length);
+}
+
+char sv_peek(struct sv s) {
+ if (0 == s.length) {
+ return '\0';
+ }
+ return s.s[0];
+}
+
+int sv_partial_eq(struct sv a, struct sv b) {
+ if (a.length < b.length) {
+ return 0;
+ }
+ for (size_t i = 0; i < b.length; i++) {
+ if (a.s[i] != b.s[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int sv_eq(struct sv a, struct sv b) {
+ if (a.length != b.length) {
+ return 0;
+ }
+ for (size_t i = 0; i < a.length; i++) {
+ if (a.s[i] != b.s[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+struct sv sv_take(struct sv s, struct sv *rest, size_t n) {
+ if (s.length < n) {
+ if (rest) {
+ rest->length = 0;
+ }
+ return s;
+ }
+ s.length = n;
+ if (rest) {
+ rest->length -= n;
+ rest->s += n;
+ }
+ return s;
+}
+
+struct sv sv_take_end(struct sv s, struct sv *rest, size_t n) {
+ if (s.length < n) {
+ if (rest) {
+ rest->length = 0;
+ }
+ return s;
+ }
+ if (rest) {
+ rest->length = s.length - n;
+ }
+ s.s += (s.length - n);
+ s.length = n;
+ return s;
+}
+
+struct sv sv_trim_left(struct sv s, size_t n) {
+ if (s.length < n) {
+ s.s += s.length;
+ s.length = 0;
+ return s;
+ }
+ s.s += n;
+ s.length -= n;
+ return s;
+}
+
+struct sv sv_clone(struct sv s) {
+ struct sv new_sv;
+ new_sv.length = s.length;
+ char *new_string = kmalloc(s.length);
+ memcpy(new_string, s.s, s.length);
+ new_sv.s = new_string;
+ return new_sv;
+}
+
+char *sv_copy_to_c(struct sv s, char *out, size_t buffer_length) {
+ int copy_len = min(s.length + 1, buffer_length);
+ if (0 == copy_len) {
+ return NULL;
+ }
+ if (!out) {
+ out = kmalloc(copy_len);
+ }
+ memcpy(out, s.s, copy_len - 1);
+ out[copy_len - 1] = '\0';
+ return out;
+}
+
+struct sv sv_clone_from_c(const char *s) {
+ return sv_clone(C_TO_SV(s));
+}
diff --git a/kernel/lib/sv.h b/kernel/lib/sv.h
new file mode 100644
index 0000000..b5cb0c9
--- /dev/null
+++ b/kernel/lib/sv.h
@@ -0,0 +1,43 @@
+#ifndef SV_H
+#define SV_H
+#include "sb.h"
+#include <stddef.h>
+
+#define SB_TO_SV(_sb) \
+ (struct sv) { \
+ .s = (_sb).string, .length = (_sb).length \
+ }
+
+#define C_TO_SV(_c_string) \
+ ((struct sv){.length = strlen(_c_string), .s = (_c_string)})
+
+#define sv_length(a) ((a).length)
+
+struct sv {
+ const char *s;
+ size_t length;
+};
+
+struct sv sv_init(const char *s, size_t length);
+char *SV_TO_C(struct sv s);
+size_t sv_to_cstring_buffer(struct sv s, char *buffer, size_t length);
+struct sv sv_split_delim(const struct sv input, struct sv *rest, char delim);
+struct sv sv_end_split_delim(const struct sv input, struct sv *rest,
+ char delim);
+struct sv sv_split_space(const struct sv input, struct sv *rest);
+struct sv sv_skip_chars(const struct sv input, const char *chars);
+struct sv sv_split_function(const struct sv input, struct sv *rest,
+ int (*function)(int));
+struct sv sv_take(struct sv s, struct sv *rest, size_t n);
+struct sv sv_take_end(struct sv s, struct sv *rest, size_t n);
+struct sv sv_next(struct sv s);
+int sv_isempty(struct sv s);
+char sv_peek(struct sv s);
+int sv_eq(struct sv a, struct sv b);
+int sv_partial_eq(struct sv a, struct sv b);
+struct sv sv_trim_left(struct sv s, size_t n);
+struct sv sv_clone(struct sv s);
+struct sv sv_clone_from_c(const char *s);
+char *sv_copy_to_c(struct sv s, char *out, size_t buffer_length);
+uint64_t sv_parse_unsigned_number(struct sv input, struct sv *rest);
+#endif
diff --git a/kernel/libc/ctype/isspace.c b/kernel/libc/ctype/isspace.c
new file mode 100644
index 0000000..a922d91
--- /dev/null
+++ b/kernel/libc/ctype/isspace.c
@@ -0,0 +1,5 @@
+#include <ctype.h>
+
+int isspace(int c) {
+ return c == ' ' || (unsigned)c - '\t' < 5;
+}
diff --git a/kernel/libc/include/string.h b/kernel/libc/include/string.h
index 89ea5ab..4425085 100644
--- a/kernel/libc/include/string.h
+++ b/kernel/libc/include/string.h
@@ -15,4 +15,5 @@ char *copy_and_allocate_string(const char *s);
char *copy_and_allocate_user_string(const char *s);
size_t strlcpy(char *dst, const char *src, size_t dsize);
char *strcat(char *s1, const char *s2);
+void *memmove(void *s1, const void *s2, size_t n);
#endif
diff --git a/kernel/libc/string/memmove.c b/kernel/libc/string/memmove.c
new file mode 100644
index 0000000..5fc49f7
--- /dev/null
+++ b/kernel/libc/string/memmove.c
@@ -0,0 +1,14 @@
+#include <string.h>
+
+// copy bytes in memory with overlapping areas
+// https://pubs.opengroup.org/onlinepubs/9699919799/functions/memmove.html
+void *memmove(void *s1, const void *s2, size_t n) {
+ // Copying takes place as if the n bytes from the object pointed to by s2 are
+ // first copied into a temporary array of n bytes that does not overlap the
+ // objects pointed to by s1 and s2, and then the n bytes from the temporary
+ // array are copied into the object pointed to by s1.
+ unsigned char tmp[n];
+ memcpy(tmp, s2, n);
+ memcpy(s1, tmp, n);
+ return s1;
+}
diff --git a/kernel/timer.c b/kernel/timer.c
index dd92d3d..b84c16a 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -2,6 +2,7 @@
#include <drivers/cmos.h>
#include <fs/devfs.h>
#include <interrupts.h>
+#include <lib/sv.h>
#include <math.h>
#include <random.h>
#include <time.h>
@@ -49,31 +50,18 @@ int clock_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
if (0 != offset) {
return 0;
}
- // Currently don't have snprintf without NULL termination...
- char tmp[4096];
u64 r = timer_get_ms();
-
- u64 l = ksnprintf(tmp, 4096, "%llu", r);
- l = min(len, l);
- memcpy(buffer, tmp, l);
- return l;
+ return min(len, (u64)kbnprintf(buffer, len, "%llu", r));
}
int clock_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
if (0 != offset) {
return 0;
}
- // TODO: Move sv to kernel or something to just avoid this. I hate null
- // termination
- char tmp[4096];
- memcpy(tmp, buffer, len);
- tmp[len] = '\0';
- int err;
- u64 new_value_ms = parse_u64(tmp, NULL, 10, &err);
- if (err) {
- return 0;
- }
+ struct sv string_view = sv_init(buffer, len);
+
+ u64 new_value_ms = sv_parse_unsigned_number(string_view, &string_view);
i64 new_value_seconds = new_value_ms / 1000;