Commit 61b200b

kravantokh <kravantokh@noreply.codeberg.org>
2026-08-28 16:39:38
Implement basic string operations.
1 parent 9927069
testing/tests/016_strings_and_slices.c
@@ -0,0 +1,93 @@
+#include "notlibc.c"
+
+void entry(void) {
+	STRING( hackunana, "hackunana" );
+	STRING( hack, "hack" );
+	STRING( hack2, "hack" );
+	STRING( una, "una" );
+
+	Arena a = {0};
+	Heap h = {0};
+	assert(S_OK( Arena_create( &a, KiB(4) ) ));
+	assert(S_OK( Heap_create( &h, KiB(4) ) ));
+
+	StrSlc hack_slc = {0};
+	assert(S_OK( StrSlc_from_Str( &hack_slc, (const Str*)&hack, 0, hack.len ) ));
+	assert( StrSlc_equals( hack_slc, hack_slc ) == 1);
+	assert( StrSlc_equals_by_ref( &hack_slc, &hack_slc ) == 1);
+
+	assert(S_OK( StrSlc_equals_Str( hack_slc, (const Str*)&hack ) ));
+	assert(S_OK( StrSlc_equals_Str_by_ref( &hack_slc, (const Str*)&hack ) ));
+
+	StrSlc hack2_slc = {0};
+	assert(S_OK( StrSlc_from_Str( &hack2_slc, (const Str*)&hack2, 0, hack.len ) ));
+	assert( StrSlc_equals( hack_slc, hack2_slc ) == 1);
+	assert( StrSlc_equals_by_ref( &hack_slc, &hack2_slc ) == 1);
+
+	StrSlc na_slc = {0};
+	assert(S_OK( StrSlc_from_Str( &na_slc, (const Str*)&una, 1, una.len - 1 ) ));
+
+	StrAsm sa = {0};
+	assert( S_OK( StrAsm_create( &sa, KiB(4) ) ));
+
+	
+	assert( S_OK( StrAsm_cat_Str( &sa, (const Str*)&hack ) ));
+	assert( S_OK( StrAsm_cat_char( &sa, 'u' ) ));
+	assert( S_OK( StrAsm_cat_StrSlc( &sa, na_slc ) ) );
+	assert( S_OK( StrAsm_cat_StrSlc_by_ref( &sa, &na_slc ) ) );
+
+	Str *s_a = 0;
+	Str *s_h = 0;
+
+	assert( S_OK( StrAsm_store_into_arena( &sa, &a, &s_a ) ) );
+	assert( S_OK( StrAsm_store_onto_heap( &sa, &h, &s_h ) ) );
+
+	assert( Str_equals( s_a, s_h ) == 1 );
+	assert( Str_equals( s_a, (const Str*)&hackunana ) == 1);
+	assert( Str_equals( s_h, (const Str*)&hackunana ) == 1);
+
+	Heap_free( &h, (void**)&s_h );
+
+	assert( S_OK( StrAsm_reset( &sa ) ) );
+
+	STRING( numbers_hex, "0xfe" "0xffff" "0xffffffff" "0xffffffffffffffff" );
+	assert( S_OK( StrAsm_fmtx_u8( &sa, 0xfe ) ) );
+	assert( S_OK( StrAsm_fmtx_u16( &sa, 0xffff ) ) );
+	assert( S_OK( StrAsm_fmtx_u32( &sa, 0xffffffff) ) );
+	assert( S_OK( StrAsm_fmtx_u64( &sa, 0xffffffffffffffff ) ) );
+
+	assert( S_OK( StrAsm_store_onto_heap( &sa, &h, &s_h ) ) );
+	assert( Str_equals( s_h, (const Str*)&numbers_hex ) == 1 );
+	Heap_free( &h, (void**)&s_h );
+
+	assert( S_OK( StrAsm_reset( &sa ) ) );
+
+	STRING( numbers_dec, "255" "65535" "4294967295" "18446744073709551615" );
+	assert( S_OK( StrAsm_fmtd_u8( &sa, 255 ) ) );
+	assert( S_OK( StrAsm_fmtd_u16( &sa, 65535 ) ) );
+	assert( S_OK( StrAsm_fmtd_u32( &sa, 4294967295) ) );
+	assert( S_OK( StrAsm_fmtd_u64( &sa, 18446744073709551615u ) ) );
+
+	assert( S_OK( StrAsm_store_onto_heap( &sa, &h, &s_h ) ) );
+	assert( Str_equals( s_h, (const Str*)&numbers_dec ) == 1 );
+	Heap_free( &h, (void**)&s_h );
+
+	assert( S_OK( StrAsm_reset( &sa ) ) );
+
+	STRING( numbers_dec_s, "-128" "-32768" "-2147483648" "-9223372036854775808" );
+	assert( S_OK( StrAsm_fmtd_s8( &sa, -128 ) ) );
+	assert( S_OK( StrAsm_fmtd_s16( &sa, -32768 ) ) );
+	assert( S_OK( StrAsm_fmtd_s32( &sa, -2147483648 ) ) );
+	assert( S_OK( StrAsm_fmtd_s64( &sa, -9223372036854775807LL - 1 ) ) );
+
+	assert( S_OK( StrAsm_store_onto_heap( &sa, &h, &s_h ) ) );
+	assert( Str_equals( s_h, (const Str*)&numbers_dec_s ) == 1 );
+	Heap_free( &h, (void**)&s_h );
+
+	assert( S_OK( StrAsm_destroy( &sa ) ));
+	assert( S_OK( Arena_destroy( &a ) ));
+	assert( S_OK( Heap_destroy( &h ) ));
+
+
+	terminate(0);
+}
notlibc.c
@@ -576,6 +576,321 @@ Status AnyArr_dup( const AnyArr *arr, AnyArr **out_arr, Heap *h, usize elem_size
 	return S_SUCCESS;
 }
 
+/*================================= String ===================================*/
+
+u8 Str_equals( const Str *s1, const Str *s2 ) {
+	if( s1->len != s2->len ) return 0;
+
+	for( usize i = 0; i < s1->len; ++i ) {
+		if( s1->data[i] != s2->data[i] ) return 0;
+	}
+	return 1;
+}
+
+Status StrSlc_from_Str( StrSlc *out, const Str *s, usize start, usize len ) {
+	if( start > s->len ) {
+		DEBUG( assert(0) );
+		return S_FAILURE | ERR_OUT_OF_BOUND | ERRSRC_ARG_3;
+	}
+
+	if( s->len >= len && s->len - len < start ) {
+		DEBUG( assert(0) );
+		return S_FAILURE | ERR_OUT_OF_BOUND | ERRSRC_ARG_3;
+	}
+
+	out->data = &s->data[start];
+	out->len = len;
+
+	return S_SUCCESS;
+}
+
+u8 StrSlc_equals( const StrSlc sl1, const StrSlc sl2 ) {
+	if( sl1.len != sl2.len ) {
+		return 0;
+	}
+
+	if( sl1.data == sl2.data ) return 1; /* If the pointers are the same 
+	the data has got to be the same */
+
+	for( usize i = 0; i < sl1.len; ++i ) {
+		if( sl1.data[i] != sl2.data[i] ) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+u8 StrSlc_equals_by_ref( const StrSlc *sl1, const StrSlc *sl2 ) {
+	if( sl1->len != sl2->len ) {
+		return 0;
+	}
+
+	if( sl1->data == sl2->data ) return 1;
+
+	for( usize i = 0; i < sl1->len; ++i ) {
+		if( sl1->data[i] != sl2->data[i] ) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+
+u8 StrSlc_equals_Str( const StrSlc sl, const Str* s ) {
+	if( sl.len != s->len ) {
+		return 0;
+	}
+
+	for( usize i = 0; i < sl.len; ++i ) {
+		if( sl.data[i] != s->data[i] ) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+u8 StrSlc_equals_Str_by_ref( const StrSlc *sl, const Str *s ) {
+	if( sl->len != s->len ) {
+		return 0;
+	}
+
+	for( usize i = 0; i < sl->len; ++i ) {
+		if( sl->data[i] != s->data[i] ) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/* String Assembler */
+
+Status StrAsm_create( StrAsm *out, usize max_size ) {
+	return Arena_create( &out->a, max_size );
+}
+
+Status StrAsm_reset( StrAsm *out ) {
+	return Arena_give( &out->a, out->a.taken );
+}
+
+Status StrAsm_destroy( StrAsm *out ) {
+	return Arena_destroy( &out->a );
+}
+
+Status StrAsm_store_into_arena( StrAsm *sa, Arena *dst, Str **out ) {
+	usize len = sa->a.taken;
+
+	#if OS != OS_NONE
+		len++;
+	#endif
+
+	u8 *p = 0;
+	TRY( Arena_take( dst, len, &p ) );
+
+	#if OS != OS_NONE
+		p[len] = 0; /* We need the NUL in case this string is used in a syscall
+			- it has to be an actual string */
+	#endif
+
+	*out = (Str*)p;
+
+	#if OS != OS_NONE
+		(*out)->len = len - 1;
+	#else
+		(*out)->len = len;
+	#endif
+
+	mem_copy( sa->a.start, &(*out)->data, len );
+
+	return S_SUCCESS;
+}
+
+Status StrAsm_store_onto_heap( StrAsm *sa, Heap *dst, Str **out ) {
+	usize len = sa->a.taken;
+
+	#if OS != OS_NONE
+		len++;
+	#endif
+
+	void *p = 0;
+	TRY( Heap_alloc( dst, len, &p ) );
+
+	#if OS != OS_NONE
+		((u8*)p)[len] = 0;
+	#endif
+
+	*out = (Str*)p;
+
+	#if OS != OS_NONE
+		(*out)->len = len - 1;
+	#else
+		(*out)->len = len;
+	#endif
+
+	mem_copy( sa->a.start, &(*out)->data, len );
+
+	return S_SUCCESS;
+}
+
+Status StrAsm_cat_char ( StrAsm *sa, u8 value ) {
+	u8 *data = 0;
+	TRY( Arena_take( &sa->a, sizeof(value), &data ) );
+	*data = value;
+	return S_SUCCESS;
+}
+
+Status StrAsm_cat_Str( StrAsm *sa, const Str *s ) {
+	u8 *data = 0;
+	TRY( Arena_take( &sa->a, s->len, &data ) );
+	mem_copy( &s->data, data, s->len );
+	return S_SUCCESS;
+}
+
+Status StrAsm_cat_StrSlc( StrAsm *sa, StrSlc sl ) {
+	u8 *data = 0;
+	TRY( Arena_take( &sa->a, sl.len, &data ) );
+	mem_copy( sl.data, data, sl.len );
+	return S_SUCCESS;
+}
+
+Status StrAsm_cat_StrSlc_by_ref( StrAsm *sa, const StrSlc *sl ) {
+	u8 *data = 0;
+	TRY( Arena_take( &sa->a, sl->len, &data ) );
+	mem_copy( sl->data, data, sl->len );
+	return S_SUCCESS;
+}
+
+
+u8 _halfbyte_to_hex_ascii[] = {
+	[0x0] = '0',
+	[0x1] = '1',
+	[0x2] = '2',
+	[0x3] = '3',
+	[0x4] = '4',
+	[0x5] = '5',
+	[0x6] = '6',
+	[0x7] = '7',
+	[0x8] = '8',
+	[0x9] = '9',
+	[0xa] = 'a',
+	[0xb] = 'b',
+	[0xc] = 'c',
+	[0xd] = 'd',
+	[0xe] = 'e',
+	[0xf] = 'f',
+};
+
+static inline u8* _write_hex_prefix( u8* start ) {
+	start[0] = '0';
+	start[1] = 'x';
+	return start + 2;
+}
+
+static inline Status _write_bin_as_hex_into_arena( u8* data, Arena *a, usize len ) {
+	u8 *out = 0;
+	TRY( Arena_take( a, 2*len + 2, &out ) );
+	out = _write_hex_prefix( out );
+	_bin_to_hex_ascii( data, out, len );
+
+	return S_SUCCESS;
+}
+
+Status StrAsm_fmtx_u8 ( StrAsm *sa,  u8 value) {
+	return _write_bin_as_hex_into_arena( &value, &sa->a, sizeof(value) );
+}
+
+Status StrAsm_fmtx_u16( StrAsm *sa, u16 value) {
+	return _write_bin_as_hex_into_arena( (u8*)&value, &sa->a, sizeof(value) );
+}
+
+Status StrAsm_fmtx_u32( StrAsm *sa, u32 value) {
+	return _write_bin_as_hex_into_arena( (u8*)&value, &sa->a, sizeof(value) );
+}
+
+Status StrAsm_fmtx_u64( StrAsm *sa, u64 value) {
+	return _write_bin_as_hex_into_arena( (u8*)&value, &sa->a, sizeof(value) );
+}
+
+static inline Status _write_u64_as_dec_into_arena( u64 value, Arena *a ) {
+	u8 digits[21] = {0};
+
+	u8 *p = &digits[21];
+
+	do {
+		*--p = '0' + value%10;
+		value /= 10;
+	} while(value);
+
+
+	u8 *out = 0;
+	usize n = &digits[21] - p;
+	TRY( Arena_take( a, n, &out ) );
+	mem_copy( p, out, n );
+
+	return S_SUCCESS;
+}
+
+Status StrAsm_fmtd_u8 ( StrAsm *sa,  u8 value) {
+	return _write_u64_as_dec_into_arena( value, &sa->a );
+}
+
+Status StrAsm_fmtd_u16( StrAsm *sa, u16 value) {
+	return _write_u64_as_dec_into_arena( value, &sa->a );
+}
+
+Status StrAsm_fmtd_u32( StrAsm *sa, u32 value) {
+	return _write_u64_as_dec_into_arena( value, &sa->a );
+}
+
+Status StrAsm_fmtd_u64( StrAsm *sa, u64 value) {
+	return _write_u64_as_dec_into_arena( value, &sa->a );
+}
+
+static inline Status _write_s64_as_dec_into_arena( s64 value, Arena *a ) {
+	u8 negative = (value < 0);
+	u8 digits[21] = {0};
+
+	u8 *p = &digits[21];
+
+	do {
+		s8 digit = value%10;
+		*--p = '0' + ((digit < 0)? -digit : digit);
+		value /= 10;
+	} while(value);
+
+
+	u8 *out = 0;
+	usize n = &digits[21] - p;
+	if( negative ) {
+		n++;
+		*--p = '-';
+	}
+	TRY( Arena_take( a, n, &out ) );
+	mem_copy( p, out, n );
+
+	return S_SUCCESS;
+}
+
+Status StrAsm_fmtd_s8 ( StrAsm *sa,  s8 value) {
+	return _write_s64_as_dec_into_arena( value, &sa->a );
+}
+
+Status StrAsm_fmtd_s16( StrAsm *sa, s16 value) {
+	return _write_s64_as_dec_into_arena( value, &sa->a );
+}
+
+Status StrAsm_fmtd_s32( StrAsm *sa, s32 value) {
+	return _write_s64_as_dec_into_arena( value, &sa->a );
+}
+
+Status StrAsm_fmtd_s64( StrAsm *sa, s64 value) {
+	return _write_s64_as_dec_into_arena( value, &sa->a );
+}
+
+
 
 /******************************************************************************/
 /*                            LINUX-SPECIFIC CODE                             */
@@ -584,14 +899,14 @@ Status AnyArr_dup( const AnyArr *arr, AnyArr **out_arr, Heap *h, usize elem_size
 #if OS == OS_LINUX
 /*============================= Entrypoint ===================================*/
 #if ARCH == ARCH_X86_64
-	u8* linux_startup_sp = 0;
+	u8* _linux_startup_sp = 0;
 
 	__asm__ (
 		".section .text\n"
 		".global _start\n"
 		".type _start, @function\n"
 		"_start:\n"
-			"leaq linux_startup_sp(%rip), %rax\n"
+			"leaq _linux_startup_sp(%rip), %rax\n"
 			"movq %rsp, (%rax)\n" /* save original stack pointer */
 			"xor %rax, %rax\n"
 			"call entry\n"
notlibc.h
@@ -15,6 +15,7 @@
 #define ARCH_I386 1
 #define ARCH_X86_64 2
 
+#define OS_NONE 0
 #define OS_LINUX 1
 #define OS_WINDOWS 2
 
@@ -1273,7 +1274,7 @@ STRUCT( Str,
 
 STRUCT( StrSlc,
 	usize len;
-	u8 *data;
+	const u8 *data;
 );
 
 /* On typical platforms that already have/need nul-termination we keep the
@@ -1308,27 +1309,28 @@ STRUCT( StrSlc,
 
 #endif
 
-
-/* String assembler */
-STRUCT( StrAsm,
-	Arena *a;
-);
+u8 Str_equals( const Str *s1, const Str *s2 );
 
 Status StrSlc_from_Str( StrSlc *out, const Str *s, usize start, usize len );
 
 u8 StrSlc_equals( const StrSlc sl1, const StrSlc sl2 );
 u8 StrSlc_equals_by_ref( const StrSlc *sl1, const StrSlc *sl2 );
 
-u8 StrSlc_equals_Str( const StrSlc sl, const Str* s2 );
-u8 StrSlc_equals_Str_by_ref( const StrSlc *s1, const StrSlc s2 );
+
+u8 StrSlc_equals_Str( const StrSlc sl, const Str* s );
+u8 StrSlc_equals_Str_by_ref( const StrSlc *sl, const Str *s );
 
 /* String assembler */
+STRUCT( StrAsm,
+	Arena a;
+);
 
 Status StrAsm_create( StrAsm *out, usize max_size );
+Status StrAsm_reset( StrAsm *out );
 Status StrAsm_destroy( StrAsm *out );
 
-Status StrAsm_store_into_arena( StrAsm *sa, Arena *dst, void **out);
-Status StrAsm_store_onto_heap( StrAsm *sa, Heap *dst, void **out );
+Status StrAsm_store_into_arena( StrAsm *sa, Arena *dst, Str **out );
+Status StrAsm_store_onto_heap( StrAsm *sa, Heap *dst, Str **out );
 
 /* String, character */
 Status StrAsm_cat_char ( StrAsm *sa, u8 value );
@@ -1336,29 +1338,65 @@ Status StrAsm_cat_Str( StrAsm *sa, const Str *s );
 Status StrAsm_cat_StrSlc( StrAsm *sa, StrSlc sl );
 Status StrAsm_cat_StrSlc_by_ref( StrAsm *sa, const StrSlc *sl );
 
+extern u8 _halfbyte_to_hex_ascii[16];
+
+static inline void _bin_to_hex_ascii( u8 *data, u8 *out, usize count ) {
+	#if ARCH == ARCH_X86_64 || ARCH == ARCH_I386
+		for( u64 i = 0; i < count; ++i ) {
+			u8 byte = data[count - i - 1];
+
+			out[2 * i] = _halfbyte_to_hex_ascii[( byte >> 4 ) & 0x0f];
+			out[2 * i + 1] = _halfbyte_to_hex_ascii[byte & 0x0f];
+		}
+	#else
+		#error Unimpleneted for big endian.
+	#endif
+}
+
 /* Hex */
 Status StrAsm_fmtx_u8 ( StrAsm *sa,  u8 value);
 Status StrAsm_fmtx_u16( StrAsm *sa, u16 value);
 Status StrAsm_fmtx_u32( StrAsm *sa, u32 value);
 Status StrAsm_fmtx_u64( StrAsm *sa, u64 value);
-Status StrAsm_fmtx_usize( StrAsm *sa, usize value);
-Status StrAsm_fmtx_s8 ( StrAsm *sa,  s8 value);
-Status StrAsm_fmtx_s16( StrAsm *sa, s16 value);
-Status StrAsm_fmtx_s32( StrAsm *sa, s32 value);
-Status StrAsm_fmtx_s64( StrAsm *sa, s64 value);
-Status StrAsm_fmtx_ssize( StrAsm *sa, ssize value);
+static inline Status StrAsm_fmtx_usize( StrAsm *sa, usize value) {
+	#if ARCH == ARCH_X86_64
+		return StrAsm_fmtx_u64( sa, value );
+	#elif ARCH == ARCH_I386
+		return StrAsm_fmtx_u32( sa, value );
+	#else
+		#error Unimplemented for current arch.
+	#endif
+}
 
 /* Decimal */
 Status StrAsm_fmtd_u8 ( StrAsm *sa,  u8 value);
 Status StrAsm_fmtd_u16( StrAsm *sa, u16 value);
 Status StrAsm_fmtd_u32( StrAsm *sa, u32 value);
 Status StrAsm_fmtd_u64( StrAsm *sa, u64 value);
-Status StrAsm_fmtd_usize( StrAsm *sa, usize value);
+Status StrAsm_fmtd_usize( StrAsm *sa, usize value) {
+	#if ARCH == ARCH_X86_64
+		return StrAsm_fmtd_u64( sa, value );
+	#elif ARCH == ARCH_I386
+		return StrAsm_fmtd_u32( sa, value );
+	#else
+		#error Unimplemented for current arch.
+	#endif
+}
+
 Status StrAsm_fmtd_s8 ( StrAsm *sa,  s8 value);
 Status StrAsm_fmtd_s16( StrAsm *sa, s16 value);
 Status StrAsm_fmtd_s32( StrAsm *sa, s32 value);
 Status StrAsm_fmtd_s64( StrAsm *sa, s64 value);
-Status StrAsm_fmtd_ssize( StrAsm *sa, ssize value);
+Status StrAsm_fmtd_ssize( StrAsm *sa, ssize value) {
+	#if ARCH == ARCH_X86_64
+		return StrAsm_fmtd_s64( sa, value );
+	#elif ARCH == ARCH_I386
+		return StrAsm_fmtd_s32( sa, value );
+	#else
+		#error Unimplemented for current arch.
+	#endif
+}
+
 
 /*                                   File                                     */