Commit 3bd866f

kravantokh <kravantokh@noreply.codeberg.org>
2026-03-13 16:40:23
Add stream interface and basic file I/O.
1 parent a643ba6
io/file.h
@@ -0,0 +1,147 @@
+#ifndef KC_HEADER_IO_FILE_INCLUDED
+#define KC_HEADER_IO_FILE_INCLUDED
+
+#include "./stream.h"
+#include "../types/string.h"
+#include "../memory/arenas.h"
+
+typedef struct _File File;
+
+void File_to_stream_source( File *f, SourceGeneric *stream );
+void File_to_stream_sink( File *f, SinkGeneric *stream );
+
+enum FileOption {
+	FILEOPTION_READ = (1<<0),
+	FILEOPTION_WRITE = (1<<1),
+	FILEOPTION_READWRITE = FILEOPTION_READ | FILEOPTION_WRITE,
+
+	FILEOPTION_DISALLOW_CREATION = (1<<2),
+	FILEOPTION_NO_FOLLOW_LINKS = (1<<3),
+
+};
+
+status File_open( File* fout, const string *path, u64 file_options, MemArena scratch );
+status File_close( File* f );
+
+status File_read( File *f, u64 *amount, u8 *out);
+status File_write( File *f, u64 *amount, const u8 *in);
+
+#ifdef KC_IMPLEMENT
+
+	void File_to_stream_source( File *f, SourceGeneric *stream ) {
+		stream->self = f;
+		stream->close = (StreamCloseFunction)&File_close;
+		stream->consume = (StreamInteractFunction)&File_read;
+	}
+
+	void File_to_stream_sink( File *f, SinkGeneric *stream ) {
+		stream->self = f;
+		stream->close = (StreamCloseFunction)&File_close;
+		stream->produce = (StreamInteractFunction)&File_write;
+	}
+
+	#if defined(KC_PLATFORM_LINUX)
+	#ifdef KC_USE_PLATFORM_LIBC
+		#include <fcntl.h>
+		#include <unistd.h>
+
+		struct _File {
+			i32 fd;
+		};
+
+		status File_open( File *fout, const string *path, u64 file_options, MemArena scratch ) {
+			u8* path_c = 0;
+			PROPAGATE_ERROR( string_to_cstring( path, &scratch, &path_c ) );
+
+			mode_t mode = 0;
+
+			if( file_options & FILEOPTION_READ ) {
+				if( file_options & FILEOPTION_WRITE ) {
+					mode = O_RDWR;
+				} else {
+					mode = O_RDONLY;
+				}
+			} else if( file_options & FILEOPTION_WRITE ) {
+				mode = O_RDWR;
+			} else {
+				return ERROR_INVALID_ARGUMENTS;
+			}
+
+			if( !file_options ) {
+				return ERROR_INVALID_ARGUMENTS;
+			}
+
+			if( !(file_options & FILEOPTION_DISALLOW_CREATION) ) {
+				mode |= O_CREAT;
+			}
+
+			if( file_options & FILEOPTION_NO_FOLLOW_LINKS ) {
+				mode |= O_NOFOLLOW;
+			}
+
+			fout->fd = open( (char*)path_c, mode );
+
+			if( fout->fd < 0 ) {
+				return ERROR_PLATFORM_FILE_OPEN_FAILED;
+			}
+
+			return STATUS_SUCCESS;
+		}
+
+		status File_close(File *f) {
+			assert( f->fd != -1 );
+
+			if( close(f->fd) != 0 ) {
+				return ERROR_PLATFORM_FILE_CLOSE_FAILED;
+			}
+
+			f->fd = -1;
+			return STATUS_SUCCESS;
+		}
+
+		status File_read(File *f, u64 *amount, u8 *out) {
+			
+			size_t res = read( f->fd, out, *amount );
+
+			if( res == 0 || res < *amount ) {
+				*amount = res;
+				return STATUS_STREAM_ENDED;
+			}
+
+			if( res == (size_t)-1 ) {
+				return ERROR_PLATFORM_FILE_READ_FAILED;
+			}
+
+			return STATUS_SUCCESS;
+		}
+
+		status File_write(File *f, u64 *amount, const u8 *in) {
+
+			size_t res = write( f->fd, (char*)in, *amount );
+
+			if( res != *amount ) {
+				*amount = res;
+				return STATUS_PARTIAL_WRITE;
+			}
+
+			if( res == (size_t)-1 ) {
+				return ERROR_PLATFORM_FILE_WRITE_FAILED;
+			}
+
+			return STATUS_SUCCESS;
+		}
+
+
+	#else
+		#error I/O is not supported without libc for now (due to syscalls missing - after adding syscall wrappers this will be possible)
+	#endif
+
+	#elif defined(KC_PLATFORM_WINDOWS)
+		#error Unsupported platform for I/O
+	#else
+		#error Unsupported platform for I/O
+	#endif
+
+#endif
+
+#endif
io/stream.h
@@ -0,0 +1,52 @@
+#ifndef KC_HEADER_IO_STREAM_INCLUDED
+#define KC_HEADER_IO_STREAM_INCLUDED
+
+#include "../types/types.h"
+#include "../memory/arenas.h"
+
+typedef status (*StreamInteractFunction)(void*, u64*, u8*);
+typedef status (*StreamCloseFunction)(void*);
+
+typedef struct _SourceGeneric {
+	void* self;
+	status (*consume)(void *self, u64 *amount, u8* buf);
+	status (*close)(void *self);
+} SourceGeneric;
+
+typedef struct _SinkGeneric{
+	void* self;
+	status (*produce)(void *self, u64 *amount, u8* buf);
+	status (*close)(void *self);
+} SinkGeneric;
+
+status Stream_into_arena( SourceGeneric *s, MemArena *a, u64 blocksize, u64 *amount_out );
+
+
+#ifdef KC_IMPLEMENT
+
+	status Stream_into_arena( SourceGeneric *s, MemArena *a, u64 blocksize, u64 *amount_out ) {
+		*amount_out = 0;
+		u8* block = 0;
+		u64 read = blocksize;
+		status state;
+
+		PROPAGATE_ERROR( MemArena_take_amount( a, blocksize, &block ) );
+
+		read = blocksize;
+		while( (state = s->consume(s->self, &read, block)) == STATUS_SUCCESS ) {
+			read = blocksize;
+			PROPAGATE_ERROR( MemArena_take_amount( a, blocksize, &block ) );
+			*amount_out += read;
+		}
+
+		if( state == STATUS_STREAM_ENDED ) {
+			*amount_out += read;
+			return STATUS_SUCCESS;
+		} else {
+			return state;
+		}
+	}
+
+#endif
+
+#endif
memory/arenas.h
@@ -23,7 +23,7 @@ status MemArena_destroy( MemArena *arena );
 
 MemArena MemArena_from( MemArena *arena ); /* Get a sub arena, with a new arena "frame" */
 
-status MemArena_take_amount( MemArena *arena, u64 size, void** out );
+status MemArena_take_amount( MemArena *arena, u64 size, u8** out );
 #define MemArena_take( arena, type, count, out ) MemArena_take_amount( arena, (sizeof(type))*(count), out )
 status MemArena_give_amount( MemArena *arena, u64 size );
 #define MemArena_give( arena, type, count ) MemArena_give_amount( arena, (sizeof(type))*(count) )
@@ -73,7 +73,7 @@ status MemArena_uncommit_unused( MemArena *arena );
 		};
 	}
 
-	status MemArena_take_amount( MemArena *arena, u64 size, void **outptr ) {
+	status MemArena_take_amount( MemArena *arena, u64 size, u8 **outptr ) {
 		ASSERT_MEM_ARENA_IS_SANE(arena);
 		assert(size);
 
@@ -146,7 +146,7 @@ status MemArena_uncommit_unused( MemArena *arena );
 		#if defined(KC_ZERO_MEMORY_STRICT)
 			mem_set_range( new_commited, arena->last_commited, 0 );
 		#endif
-		PROPAGATE_ERROR( kc_platform_mem_va_uncommit( new_commited,  arena->last_commited - new_commited ) );
+		PROPAGATE_ERROR( kc_platform_mem_va_uncommit( new_commited,  arena->end - new_commited ) ); /* We have to get rid of it until the end, in case a subarena has also allocated more. */
 
 		arena->last_commited = new_commited;
 
memory/ops.h
@@ -2,10 +2,23 @@
 #define KC_HEADER_MEMORY_OPS_INCLUDED
 
 #include "../types/types.h"
+#include <stddef.h>
 
-status mem_copy( void *src, void *dst, u64 size );
+void mem_copy( const void *src, void *dst, u64 size );
 status mem_move( void *src, void *dst, u64 size );
 status mem_set( void *buf, u64 size, u8 value );
 status mem_set_range( void *start, void *end, u8 value );
 
+#ifdef KC_IMPLEMENT
+
+void mem_copy( const void *src, void *dst, u64 size ) {
+	const u8 *s = src;
+	u8 *d = dst;
+	for( u64 i = 0; i < size; ++i ) {
+		d[i] = s[i];
+	}
+}
+
+#endif
+
 #endif
tests/a.out
Binary file
tests/main.c
@@ -9,6 +9,7 @@
 #include "../platform/assert.h"
 
 #include "../include/testing.h"
+#include "../io/file.h"
 
 KC_TESTFUNC( test0, 
 	printf("Good riddance!\n");
@@ -17,21 +18,30 @@ KC_TESTFUNC( test0,
 
 int main(void) {
 	MemArena a;
+	MemArena scratch;
 
-	assert(OK( MemArena_create( &a, GiB(120) )) );
+	assert(OK( MemArena_create( &a, GiB(4) )) );
+	assert(OK( MemArena_create( &scratch, MiB(1) )) );
 
-	u8* data;
-	assert( OK(MemArena_take( &a, u8, GiB(1), (void**)&data ) ));
+	File f = {0};
 
-	for( u64 i = 0; i < GiB(1); ++i ) {
-		data[i] = 'K';
-	}
+	STATIC_STRING( testfile_name, "./testfile" );
 
-	assert( OK(MemArena_give_amount( &a, MiB(512) ) ));
-	assert( OK(MemArena_uncommit_unused( &a )));
+	assert(OK( File_open( &f, (string*)&testfile_name, FILEOPTION_READ, MemArena_from( &scratch ) ) ));
 
-	assert( OK(MemArena_give_amount( &a, MiB(512) ) ));
-	assert( OK(MemArena_uncommit_unused( &a )));
+	SourceGeneric s = {0};
+
+	File_to_stream_source( &f, &s );
+
+	u64 amount = 0;
+	Stream_into_arena( &s, &a, 64, &amount );
+
+	printf("%lu", amount );
+	putchar('\n');
+	write(1, a.start, amount);
+
+
+	assert(OK( File_close( &f ) ));
 
 	assert(OK(MemArena_destroy(&a)));
 }
types/string.h
@@ -0,0 +1,38 @@
+#ifndef KC_HEADER_TYPES_STRING_INCLUDED
+#define KC_HEADER_TYPES_STRING_INCLUDED
+
+#include "../platform/types.h"
+#include "../memory/arenas.h"
+
+typedef struct _string {
+	u64 len;
+	u8 data[];
+} string;
+
+
+#define STATIC_STRING( varname, str )                                          \
+	const struct {                                                            \
+		u64 len;                                                             \
+		u8  data[sizeof( str ) - 1] __attribute__( ( nonstring ) );          \
+	} varname = { .len = sizeof( str ) - 1, .data = str }
+
+status string_to_cstring( const string *str, MemArena *a, u8** out );
+
+#ifdef KC_IMPLEMENT
+
+status string_to_cstring( const string *str, MemArena *a, u8** out ) {
+	u8* c_str;
+	u64 l = str->len;
+
+	PROPAGATE_ERROR(MemArena_take( a, u8, l + 1, &c_str ));
+	mem_copy( &str->data[0], c_str, l );
+	c_str[l] = '\0';
+
+	*out = c_str;
+	return STATUS_SUCCESS;
+}
+
+#endif
+
+
+#endif
types/types.h
@@ -16,14 +16,24 @@ typedef i32 status;
 
 enum _Status {
 	STATUS_SUCCESS = 0,
-	ERROR_GENERIC = 1,
-	ERROR_WOULD_OVERRUN = 2,
-	ERROR_WOULD_UNDERRUN = 3,
-
-	ERROR_PLATFORM_GENERIC = 10000,
-	ERROR_PLATFORM_MAPPING_FAILED = 10001,
-	ERROR_PLATFORM_MEMORY_PERMISSION_CHANGE_FAILED = 10002,
-	ERROR_PLATFORM_MEMORY_HINT_CHANGE_FAILED = 10003,
+
+	STATUS_STREAM_ENDED = 100,
+	STATUS_PARTIAL_WRITE = 101,
+
+	ERROR_GENERIC = -1,
+	ERROR_WOULD_OVERRUN = -2,
+	ERROR_WOULD_UNDERRUN = -3,
+	ERROR_INVALID_ARGUMENTS = -4,
+
+
+	ERROR_PLATFORM_GENERIC = -10000,
+	ERROR_PLATFORM_MAPPING_FAILED = -10001,
+	ERROR_PLATFORM_MEMORY_PERMISSION_CHANGE_FAILED = -10002,
+	ERROR_PLATFORM_MEMORY_HINT_CHANGE_FAILED = -10003,
+	ERROR_PLATFORM_FILE_OPEN_FAILED = -10004,
+	ERROR_PLATFORM_FILE_CLOSE_FAILED = -10005,
+	ERROR_PLATFORM_FILE_READ_FAILED = -10006,
+	ERROR_PLATFORM_FILE_WRITE_FAILED = -10007,
 
 };