Commit 3186141

kravantokh <kravantokh@noreply.codeberg.org>
2026-08-16 14:54:32
Add multiplication to safemath.
1 parent 5dcb1af
testing/tests/001_safemath.c
@@ -124,6 +124,121 @@ void entry(void) {
 	assert( S_FAILED( s16_add( s16_MIN, -1, (s16*)&res )));
 	assert( S_FAILED(  s8_add(  s8_MIN, -1, (s8 *)&res )));
 
+	/* Basic multiplication */
+	assert( S_OK( u64_mul( 3, 2,       &res ))); assert(      res == 6 );
+	assert( S_OK( u32_mul( 3, 2, (u32*)&res ))); assert( (u32)res == 6 );
+	assert( S_OK( u16_mul( 3, 2, (u16*)&res ))); assert( (u16)res == 6 );
+	assert( S_OK(  u8_mul( 3, 2, (u8 *)&res ))); assert( ( u8)res == 6 );
+
+	assert( S_OK( s64_mul( 3, 2, (s64*)&res ))); assert( (s64)res == 6 );
+	assert( S_OK( s32_mul( 3, 2, (s32*)&res ))); assert( (s32)res == 6 );
+	assert( S_OK( s16_mul( 3, 2, (s16*)&res ))); assert( (s16)res == 6 );
+	assert( S_OK(  s8_mul( 3, 2, (s8 *)&res ))); assert( ( s8)res == 6 );
+
+	assert( S_OK( s64_mul( -3, 2, (s64*)&res ))); assert( (s64)res == -6 );
+	assert( S_OK( s32_mul( -3, 2, (s32*)&res ))); assert( (s32)res == -6 );
+	assert( S_OK( s16_mul( -3, 2, (s16*)&res ))); assert( (s16)res == -6 );
+	assert( S_OK(  s8_mul( -3, 2, (s8 *)&res ))); assert( ( s8)res == -6 );
+
+	/* Zero */
+	assert( S_OK( u64_mul( u64_MAX, 0,       &res ))); assert( res == 0 );
+	assert( S_OK( u32_mul( u32_MAX, 0, (u32*)&res ))); assert( res == 0 );
+	assert( S_OK( u16_mul( u16_MAX, 0, (u16*)&res ))); assert( res == 0 );
+	assert( S_OK(  u8_mul(  u8_MAX, 0, (u8 *)&res ))); assert( res == 0 );
+
+	assert( S_OK( s64_mul( s64_MAX, 0, (s64*)&res ))); assert( res == 0 );
+	assert( S_OK( s32_mul( s32_MAX, 0, (s32*)&res ))); assert( res == 0 );
+	assert( S_OK( s16_mul( s16_MAX, 0, (s16*)&res ))); assert( res == 0 );
+	assert( S_OK(  s8_mul(  s8_MAX, 0, (s8 *)&res ))); assert( res == 0 );
+
+	assert( S_OK( u64_mul( 0, u64_MAX,       &res ))); assert(      res == 0 );
+	assert( S_OK( u32_mul( 0, u32_MAX, (u32*)&res ))); assert( (u32)res == 0 );
+	assert( S_OK( u16_mul( 0, u16_MAX, (u16*)&res ))); assert( (u16)res == 0 );
+	assert( S_OK(  u8_mul( 0,  u8_MAX, (u8 *)&res ))); assert( ( u8)res == 0 );
+
+	assert( S_OK( s64_mul( 0, s64_MIN, (s64*)&res ))); assert( (s64)res == 0 );
+	assert( S_OK( s32_mul( 0, s32_MIN, (s32*)&res ))); assert( (s32)res == 0 );
+	assert( S_OK( s16_mul( 0, s16_MIN, (s16*)&res ))); assert( (s16)res == 0 );
+	assert( S_OK(  s8_mul( 0,  s8_MIN, (s8 *)&res ))); assert( ( s8)res == 0 );
+
+	/* Overflow */
+	assert( S_FAILED( u64_mul( u64_MAX, 2,       &res )));
+	assert( S_FAILED( u32_mul( u32_MAX, 2, (u32*)&res )));
+	assert( S_FAILED( u16_mul( u16_MAX, 2, (u16*)&res )));
+	assert( S_FAILED(  u8_mul(  u8_MAX, 2, (u8 *)&res )));
+
+	assert( S_FAILED( s64_mul( s64_MIN, 2, (s64*)&res )));
+	assert( S_FAILED( s32_mul( s32_MIN, 2, (s32*)&res )));
+	assert( S_FAILED( s16_mul( s16_MIN, 2, (s16*)&res )));
+	assert( S_FAILED(  s8_mul(  s8_MIN, 2, (s8 *)&res )));
+
+	assert( S_FAILED( s64_mul( s64_MAX, 2, (s64*)&res )));
+	assert( S_FAILED( s32_mul( s32_MAX, 2, (s32*)&res )));
+	assert( S_FAILED( s16_mul( s16_MAX, 2, (s16*)&res )));
+	assert( S_FAILED(  s8_mul(  s8_MAX, 2, (s8 *)&res )));
+
+	assert( S_FAILED( s64_mul( s64_MIN, -1, (s64*)&res )));
+	assert( S_FAILED( s32_mul( s32_MIN, -1, (s32*)&res )));
+	assert( S_FAILED( s16_mul( s16_MIN, -1, (s16*)&res )));
+	assert( S_FAILED(  s8_mul(  s8_MIN, -1, (s8 *)&res )));
+
+	assert( S_FAILED( s64_mul( s64_MIN/2, -2, (s64*)&res )));
+	assert( S_FAILED( s32_mul( s32_MIN/2, -2, (s32*)&res )));
+	assert( S_FAILED( s16_mul( s16_MIN/2, -2, (s16*)&res )));
+	assert( S_FAILED(  s8_mul(  s8_MIN/2, -2, (s8 *)&res )));
+
+	/* Signed-specific */
+	assert( S_OK( s64_mul( s64_MAX, -1, (s64*)&res )));
+	assert( S_OK( s32_mul( s32_MAX, -1, (s32*)&res )));
+	assert( S_OK( s16_mul( s16_MAX, -1, (s16*)&res )));
+	assert( S_OK(  s8_mul(  s8_MAX, -1, (s8 *)&res )));
+
+	assert( S_OK( s64_mul( s64_MIN + 1, -1, (s64*)&res ))); assert( (s64)res == s64_MAX );
+	assert( S_OK( s32_mul( s32_MIN + 1, -1, (s32*)&res ))); assert( (s32)res == s32_MAX );
+	assert( S_OK( s16_mul( s16_MIN + 1, -1, (s16*)&res ))); assert( (s16)res == s16_MAX );
+	assert( S_OK(  s8_mul(  s8_MIN + 1, -1, (s8 *)&res ))); assert( ( s8)res ==  s8_MAX );
+
+	assert( S_OK( s64_mul( s64_MIN / -2, -2, (s64*)&res ))); assert( (s64)res == s64_MIN );
+	assert( S_OK( s32_mul( s32_MIN / -2, -2, (s32*)&res ))); assert( (s32)res == s32_MIN );
+	assert( S_OK( s16_mul( s16_MIN / -2, -2, (s16*)&res ))); assert( (s16)res == s16_MIN );
+	assert( S_OK(  s8_mul(  s8_MIN / -2, -2, (s8 *)&res ))); assert( ( s8)res ==  s8_MIN );
+
+	assert( S_OK( s64_mul( s64_MIN / 2 + 1, -2, (s64*)&res ))); assert( (s64)res == s64_MAX - 1 );
+	assert( S_OK( s32_mul( s32_MIN / 2 + 1, -2, (s32*)&res ))); assert( (s32)res == s32_MAX - 1 );
+	assert( S_OK( s16_mul( s16_MIN / 2 + 1, -2, (s16*)&res ))); assert( (s16)res == s16_MAX - 1 );
+	assert( S_OK(  s8_mul(  s8_MIN / 2 + 1, -2, (s8 *)&res ))); assert( ( s8)res ==  s8_MAX - 1 );
+
+	
+	/* Catch potential off-by-one errors */
+	assert( S_OK( u64_mul( u64_MAX/2, 2,       &res ))); assert(      res == u64_MAX - 1 );
+	assert( S_OK( u32_mul( u32_MAX/2, 2, (u32*)&res ))); assert( (u32)res == u32_MAX - 1 );
+	assert( S_OK( u16_mul( u16_MAX/2, 2, (u16*)&res ))); assert( (u16)res == u16_MAX - 1);
+	assert( S_OK(  u8_mul(  u8_MAX/2, 2, (u8 *)&res ))); assert( ( u8)res ==  u8_MAX - 1);
+
+	assert( S_FAILED( u64_mul( u64_MAX / 2 + 1, 2,       &res )));
+	assert( S_FAILED( u32_mul( u32_MAX / 2 + 1, 2, (u32*)&res )));
+	assert( S_FAILED( u16_mul( u16_MAX / 2 + 1, 2, (u16*)&res )));
+	assert( S_FAILED(  u8_mul(  u8_MAX / 2 + 1, 2, (u8 *)&res )));
+
+	assert( S_OK( s64_mul( s64_MAX/2, 2, (s64*)&res ))); assert( (s64)res == s64_MAX - 1 );
+	assert( S_OK( s32_mul( s32_MAX/2, 2, (s32*)&res ))); assert( (s32)res == s32_MAX - 1 );
+	assert( S_OK( s16_mul( s16_MAX/2, 2, (s16*)&res ))); assert( (s16)res == s16_MAX - 1 );
+	assert( S_OK(  s8_mul(  s8_MAX/2, 2, (s8 *)&res ))); assert( ( s8)res ==  s8_MAX - 1 );
+
+	assert( S_OK( s64_mul( s64_MIN/2, 2, (s64*)&res ))); assert( (s64)res == s64_MIN );
+	assert( S_OK( s32_mul( s32_MIN/2, 2, (s32*)&res ))); assert( (s32)res == s32_MIN );
+	assert( S_OK( s16_mul( s16_MIN/2, 2, (s16*)&res ))); assert( (s16)res == s16_MIN );
+	assert( S_OK(  s8_mul(  s8_MIN/2, 2, (s8 *)&res ))); assert( ( s8)res ==  s8_MIN );
+
+	assert( S_FAILED( s64_mul( s64_MAX/2 + 1,  2, (s64*)&res )));
+	assert( S_FAILED( s32_mul( s32_MAX/2 + 1,  2, (s32*)&res )));
+	assert( S_FAILED( s16_mul( s16_MAX/2 + 1,  2, (s16*)&res )));
+	assert( S_FAILED(  s8_mul(  s8_MAX/2 + 1,  2, (s8 *)&res )));
+
+	assert( S_FAILED( s64_mul( s64_MIN/2 - 1,  2, (s64*)&res )));
+	assert( S_FAILED( s32_mul( s32_MIN/2 - 1,  2, (s32*)&res )));
+	assert( S_FAILED( s16_mul( s16_MIN/2 - 1,  2, (s16*)&res )));
+	assert( S_FAILED(  s8_mul(  s8_MIN/2 - 1,  2, (s8 *)&res )));
 
 	terminate(0);
 }
util/mathops/max.h
@@ -1,5 +1,5 @@
-#ifndef NOTLIBC_HEADER_UTIL_MATHOPS_MIN_INCLUDED
-#define NOTLIBC_HEADER_UTIL_MATHOPS_MIN_INCLUDED
+#ifndef NOTLIBC_HEADER_UTIL_MATHOPS_MAX_INCLUDED
+#define NOTLIBC_HEADER_UTIL_MATHOPS_MAX_INCLUDED
 
 #include "platform/common/types.h"
 
util/safemath.h
@@ -7,20 +7,34 @@
 
 
 /* Ensure limit sanity - at least for unsigned */
-ASSERT(u64_MAX == 0xffffffffffffffff );
-ASSERT(u32_MAX == 0xffffffff );
-ASSERT(u16_MAX == 0xffff );
-ASSERT(u8_MAX == 0xff );
+ASSERT( u64_MAX == 0xffffffffffffffff );
+ASSERT( u32_MAX == 0xffffffff );
+ASSERT( u16_MAX == 0xffff );
+ASSERT( u8_MAX == 0xff );
 
 static inline Status  u8_add(u8 n1, u8 n2, u8 *out);
 static inline Status u16_add(u16 n1, u16 n2, u16 *out);
 static inline Status u32_add(u32 n1, u32 n2, u32 *out);
 static inline Status u64_add(u64 n1, u64 n2, u64 *out);
-static inline Status usize_add(u64 n1, u64 n2, u64 *out) {
+
+static inline Status  u8_mul(u8 n1, u8 n2, u8 *out);
+static inline Status u16_mul(u16 n1, u16 n2, u16 *out);
+static inline Status u32_mul(u32 n1, u32 n2, u32 *out);
+static inline Status u64_mul(u64 n1, u64 n2, u64 *out);
+
+static inline Status usize_add(usize n1, usize n2, usize *out) {
+	#if ARCH == ARCH_X86_64
+		return u64_add( n1, n2, out );
+	#elif ARCH == ARCH_I386
+		return u32_add( n1, n2, out );
+	#endif
+}
+
+static inline Status usize_mul(usize n1, usize n2, usize *out) {
 	#if ARCH == ARCH_X86_64
-	return u64_add( n1, n2, out );
+		return u64_mul( n1, n2, out );
 	#elif ARCH == ARCH_I386
-	return u32_add( n1, n2, out );
+		return u32_mul( n1, n2, out );
 	#endif
 }
 
@@ -31,7 +45,7 @@ static inline Status s64_add(s64 n1, s64 n2, s64 *out);
 
 
 #ifdef NOTLIBC_IMPLEMENT
-	#if COMPILER == COMPILER_GCC || COPMILER == COMPILER_CLANG
+	#if COMPILER == COMPILER_GCC || COMPILER == COMPILER_CLANG
 		#include "./safemath_gcclike.h"
 	#else
 		#include "./safemath_generic.h"
util/safemath_gcclike.h
@@ -1,7 +1,7 @@
 #ifndef NOTLIBC_HEADER_UTIL_SAFEMATH_GCCLIKE_INCLUDED
 #define NOTLIBC_HEADER_UTIL_SAFEMATH_GCCLIKE_INCLUDED
 
-#include "datatypes/common.h"
+#include "datatypes/error.h"
 
 /* Unsigned addition */
 
@@ -125,4 +125,62 @@ static inline Status s8_sub( s8 n1, s8 n2, s8 *out ) {
 	return S_SUCCESS;
 }
 
+/* Unsigned multiplication */
+
+static inline Status u64_mul( u64 n1, u64 n2, u64 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status u32_mul( u32 n1, u32 n2, u32 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status u16_mul( u16 n1, u16 n2, u16 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status u8_mul( u8 n1, u8 n2, u8 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status s64_mul( s64 n1, s64 n2, s64 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status s32_mul( s32 n1, s32 n2, s32 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status s16_mul( s16 n1, s16 n2, s16 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
+static inline Status s8_mul( s8 n1, s8 n2, s8 *out ) {
+	if( __builtin_mul_overflow(n1, n2, out) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	return S_SUCCESS;
+}
+
 #endif
util/safemath_generic.h
@@ -141,4 +141,90 @@ static inline Status s8_sub( s8 n1, s8 n2, s8 *out ) {
 	return S_SUCCESS;
 }
 
+/* Unsigned multiplication */
+static inline Status u64_mul( u64 n1, u64 n2, u64 *out ) {
+	if( n1 && n2 > (u64_MAX/n1) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	*out = n1*n2;
+	return S_SUCCESS;
+}
+
+static inline Status u32_mul( u32 n1, u32 n2, u32 *out ) {
+	if( n1 && n2 > (u32_MAX/n1) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	*out = n1*n2;
+	return S_SUCCESS;
+}
+
+static inline Status u16_mul( u16 n1, u16 n2, u16 *out ) {
+	if( n1 && n2 > (u16_MAX/n1) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	*out = n1*n2;
+	return S_SUCCESS;
+}
+
+static inline Status u8_mul( u8 n1, u8 n2, u8 *out ) {
+	if( n1 && n2 > (u8_MAX/n1) ) {
+		return S_FAILURE | ERR_OVERFLOW;
+	}
+	*out = n1*n2;
+	return S_SUCCESS;
+}
+
+/* Signed multiplication */
+static inline Status s64_mul( s64 n1, s64 n2, s64 *out ) {
+	if(
+		( n2 > 0 && n1 >= s64_MIN/n2 && n1 <= s64_MAX/n2 ) ||
+		( n2 == 0 ) ||
+		( n2 == -1 && n1 >= -s64_MAX ) ||
+		( n2 < -1 && n1 >= s64_MAX/n2 && n1 <= s64_MIN/n2 )
+	) {
+		*out = n1*n2;
+		return S_SUCCESS;
+	}
+	return S_FAILURE | ERR_OVERFLOW;
+}
+
+static inline Status s32_mul( s32 n1, s32 n2, s32 *out ) {
+	if(
+		( n2 > 0 && n1 >= s32_MIN/n2 && n1 <= s32_MAX/n2 ) ||
+		( n2 == 0 ) ||
+		( n2 == -1 && n1 >= -s32_MAX ) ||
+		( n2 < -1 && n1 >= s32_MAX/n2 && n1 <= s32_MIN/n2 )
+	) {
+		*out = n1*n2;
+		return S_SUCCESS;
+	}
+	return S_FAILURE | ERR_OVERFLOW;
+}
+
+static inline Status s16_mul( s16 n1, s16 n2, s16 *out ) {
+	if(
+		( n2 > 0 && n1 >= s16_MIN/n2 && n1 <= s16_MAX/n2 ) ||
+		( n2 == 0 ) ||
+		( n2 == -1 && n1 >= -s16_MAX ) ||
+		( n2 < -1 && n1 >= s16_MAX/n2 && n1 <= s16_MIN/n2 )
+	) {
+		*out = n1*n2;
+		return S_SUCCESS;
+	}
+	return S_FAILURE | ERR_OVERFLOW;
+}
+
+static inline Status s8_mul( s8 n1, s8 n2, s8 *out ) {
+	if(
+		( n2 > 0 && n1 >= s8_MIN/n2 && n1 <= s8_MAX/n2 ) ||
+		( n2 == 0 ) ||
+		( n2 == -1 && n1 >= -s8_MAX ) ||
+		( n2 < -1 && n1 >= s8_MAX/n2 && n1 <= s8_MIN/n2 )
+	) {
+		*out = n1*n2;
+		return S_SUCCESS;
+	}
+	return S_FAILURE | ERR_OVERFLOW;
+}
+
 #endif