433 lines
7.9 KiB
C
433 lines
7.9 KiB
C
#include "axl_string.h"
|
|
|
|
u32 axl_strlen(const i8* s)
|
|
{
|
|
if(s == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
u32 len = 0;
|
|
|
|
while(s[len] != '\0')
|
|
{
|
|
len++;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
i8* axl_strcpy(i8* dst, const i8* src)
|
|
{
|
|
if(dst == NULL || src == NULL)
|
|
{
|
|
return dst;
|
|
}
|
|
|
|
i8* start = dst;
|
|
|
|
while((*(dst++) = *(src++)) != '\0');
|
|
|
|
return start;
|
|
}
|
|
|
|
i8* axl_strncpy(i8* dst, const i8* src, u32 n)
|
|
{
|
|
if(dst == NULL || src == NULL)
|
|
{
|
|
return dst;
|
|
}
|
|
|
|
u32 i = 0;
|
|
|
|
for(; i < n && src[i] != '\0'; i++)
|
|
{
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
for(; i < n; i++)
|
|
{
|
|
dst[i] = '\0';
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
i8* axl_strcat(i8* dst, const i8* src)
|
|
{
|
|
if(dst == NULL || src == NULL)
|
|
{
|
|
return dst;
|
|
}
|
|
|
|
axl_strcpy(dst + axl_strlen(dst), src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
i8* axl_strncat(i8* dst, const i8* src, u32 n) //n actually means "not more than"
|
|
{
|
|
if(!dst || !src)
|
|
{
|
|
return dst;
|
|
}
|
|
|
|
i8* p = dst + axl_strlen(dst);
|
|
while (n-- && (*p++ = *src++)); //quick note: '\0' == 0
|
|
|
|
*p = '\0'; //in case we copied exactly n without '\0'
|
|
|
|
return dst;
|
|
}
|
|
|
|
i32 axl_strcmp(const i8* s1, const i8* s2)
|
|
{
|
|
if(!s1 || !s2)
|
|
{
|
|
return (s1 == s2) ? 0 : (!s1 ? -1 : 1);
|
|
}
|
|
|
|
for(;; s1++, s2++)
|
|
{
|
|
u8 c1 = *(const u8*)s1;
|
|
u8 c2 = *(const u8*)s2;
|
|
|
|
if (c1 != c2 || c1 == '\0' || c2 == '\0')
|
|
{
|
|
return c1 - c2;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
i32 axl_strncmp(const i8* s1, const i8* s2, u32 n)
|
|
{
|
|
if(!s1 || !s2)
|
|
{
|
|
return (s1 == s2) ? 0 : (!s1 ? -1 : 1);
|
|
}
|
|
|
|
for(u32 i = 0; i < n; s1++, s2++, i++)
|
|
{
|
|
u8 c1 = *(const u8*)s1;
|
|
u8 c2 = *(const u8*)s2;
|
|
|
|
if(c1 != c2 || c1 == '\0' || c2 == '\0')
|
|
{
|
|
return c1 - c2;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
const i8* axl_strchr(const i8* str, i8 c)
|
|
{
|
|
if(!str)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for(; *str != '\0'; ++str)
|
|
{
|
|
if(*str == c)
|
|
{
|
|
return str;
|
|
}
|
|
}
|
|
|
|
return (c == '\0') ? str : NULL;
|
|
}
|
|
|
|
i8* axl_strstr(const i8* str, const i8* substr)
|
|
{
|
|
if(!str || !substr)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if(*substr == '\0')
|
|
{
|
|
return (i8*)str;
|
|
}
|
|
|
|
u32 n = axl_strlen(substr);
|
|
|
|
while(*str)
|
|
{
|
|
if(!axl_strncmp(str++, substr, n))
|
|
{
|
|
return (i8*)(str - 1);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
i8* axl_strrev(i8* str)
|
|
{
|
|
if(!str)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
i8* start = str;
|
|
i8* end = str + axl_strlen(str) - 1;
|
|
|
|
while(start < end)
|
|
{
|
|
i8 tmp = *start;
|
|
*start = *end;
|
|
*end = tmp;
|
|
start++;
|
|
end--;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
/*static const char digits_lower[] = "0123456789abcdefghijklmnopqrstuvwxyz";*/
|
|
static const char digits_upper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
i8* axl_utoa(u64 val, i8* str, u8 base)
|
|
{
|
|
if(!str)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (base < 2 || base > 36)
|
|
{
|
|
*str = '\0';
|
|
return str;
|
|
}
|
|
|
|
i8* head = str;
|
|
const i8* digits = digits_upper;
|
|
|
|
|
|
if (val == 0)
|
|
{
|
|
*head++ = '0';
|
|
*head++ = '\0';
|
|
return str;
|
|
}
|
|
|
|
while (val != 0)
|
|
{
|
|
*head++ = digits[val % base];
|
|
val /= base;
|
|
}
|
|
|
|
*head = '\0';
|
|
|
|
return axl_strrev(str);
|
|
}
|
|
|
|
i8* axl_itoa(i64 val, i8* str, u8 base)
|
|
{
|
|
if(!str)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if (base < 2 || base > 36)
|
|
{
|
|
*str = '\0';
|
|
return str;
|
|
}
|
|
|
|
i8* head = str;
|
|
u64 uval = val < 0 ? (u64)(-(val)) : (u64)val;
|
|
|
|
if (uval == 0)
|
|
{
|
|
*head++ = '0';
|
|
*head++ = '\0';
|
|
return str;
|
|
}
|
|
|
|
while (uval != 0)
|
|
{
|
|
*head++ = digits_upper[uval % base];
|
|
uval /= base;
|
|
}
|
|
|
|
if (val < 0)
|
|
{
|
|
*head++ = '-';
|
|
}
|
|
|
|
*head = '\0';
|
|
|
|
return axl_strrev(str);
|
|
}
|
|
|
|
#if !__STDC_HOSTED__
|
|
int _fltused;
|
|
#endif // !__STDC_HOSTED__
|
|
|
|
/*
|
|
* @brief Converts float to string with specified precision
|
|
*
|
|
* @param val: Float value to convert
|
|
* @param str: Destination buffer (must be large enough)
|
|
* @param precision: Digits after decimal point (0-AXL_F32_MAX_PRECISION)
|
|
* @return Pointer to start of string
|
|
*
|
|
* @note Handles nan, inf, -inf, zero
|
|
* @note Uses scientific notation for very large/small numbers
|
|
* @note Removes trailing zeros from fractional part
|
|
*/
|
|
i8* axl_ftoa(f32 val, i8* str, u32 precision)
|
|
{
|
|
if (!str) return NULL;
|
|
|
|
// Clamp precision to meaningful range
|
|
if (precision > AXL_F32_MAX_PRECISION)
|
|
{
|
|
precision = AXL_F32_MAX_PRECISION;
|
|
}
|
|
|
|
// Extract float components
|
|
union { f32 f; u32 u; } bits = { .f = val };
|
|
u32 sign = bits.u >> 31;
|
|
u32 exponent = (bits.u >> 23) & 0xFF;
|
|
u32 mantissa = bits.u & 0x7FFFFF;
|
|
|
|
// Handle NaN
|
|
if (exponent == 0xFF && mantissa != 0)
|
|
{
|
|
axl_strcpy(str, "nan");
|
|
return str;
|
|
}
|
|
|
|
// Handle Infinity
|
|
if (exponent == 0xFF && mantissa == 0)
|
|
{
|
|
if (sign) axl_strcpy(str, "-inf");
|
|
else axl_strcpy(str, "inf");
|
|
return str;
|
|
}
|
|
|
|
// Handle zero and denormalized numbers (treat as zero)
|
|
if (exponent == 0)
|
|
{
|
|
i8* p = str;
|
|
if (sign) *p++ = '-';
|
|
*p++ = '0';
|
|
if (precision > 0)
|
|
{
|
|
*p++ = '.';
|
|
for (u32 i = 0; i < precision; i++)
|
|
{
|
|
*p++ = '0';
|
|
}
|
|
}
|
|
*p = '\0';
|
|
return str;
|
|
}
|
|
|
|
i8* start = str;
|
|
f32 abs_val = val;
|
|
|
|
// Apply sign
|
|
if (sign)
|
|
{
|
|
*str++ = '-';
|
|
abs_val = -abs_val;
|
|
}
|
|
|
|
// Calculate magnitude for format selection
|
|
i32 magnitude = 0;
|
|
f32 temp = abs_val;
|
|
while (temp >= 10.0f)
|
|
{ temp /= 10.0f; magnitude++; }
|
|
while (temp > 0.0f && temp < 1.0f)
|
|
{ temp *= 10.0f; magnitude--; }
|
|
|
|
// Use scientific notation for extreme ranges
|
|
if (magnitude > 15 || magnitude < -15)
|
|
{
|
|
// Normalize mantissa to [1, 10)
|
|
f32 mant = abs_val;
|
|
while (mant >= 10.0f) mant /= 10.0f;
|
|
while (mant > 0.0f && mant < 1.0f) mant *= 10.0f;
|
|
|
|
// Integer part (single digit)
|
|
u64 int_part = (u64)mant;
|
|
axl_utoa(int_part, str, 10);
|
|
str += axl_strlen(str);
|
|
|
|
// Fractional part
|
|
if (precision > 0)
|
|
{
|
|
*str++ = '.';
|
|
f32 frac = mant - (f32)int_part;
|
|
for (u32 i = 0; i < precision; i++)
|
|
{
|
|
frac *= 10.0f;
|
|
u32 digit = (u32)frac;
|
|
*str++ = '0' + digit;
|
|
frac -= (f32)digit;
|
|
}
|
|
// Trim trailing zeros
|
|
while (*(str-1) == '0' && *(str-2) != '.') str--;
|
|
}
|
|
|
|
// Add exponent
|
|
*str++ = 'e';
|
|
if (magnitude >= 0) *str++ = '+';
|
|
axl_itoa(magnitude, str, 10);
|
|
return start;
|
|
}
|
|
|
|
// Fixed-point representation for normal range
|
|
|
|
// Split into integer and fractional parts
|
|
u64 int_part = (u64)abs_val;
|
|
f32 frac_part = abs_val - (f32)int_part;
|
|
|
|
// Write integer part
|
|
if (int_part == 0)
|
|
{
|
|
*str++ = '0';
|
|
}
|
|
else
|
|
{
|
|
i8 buf[32];
|
|
axl_utoa(int_part, buf, 10);
|
|
axl_strcpy(str, buf);
|
|
str += axl_strlen(buf);
|
|
}
|
|
|
|
// Write fractional part
|
|
if (precision > 0)
|
|
{
|
|
*str++ = '.';
|
|
f32 f = frac_part;
|
|
u32 i;
|
|
for (i = 0; i < precision; i++)
|
|
{
|
|
f *= 10.0f;
|
|
u32 digit = (u32)f;
|
|
*str++ = '0' + digit;
|
|
f -= (f32)digit;
|
|
}
|
|
|
|
// Remove trailing zeros
|
|
while (str > start + 2 && *(str-1) == '0')
|
|
{
|
|
str--;
|
|
}
|
|
// Remove decimal point if no fractional digits remain
|
|
if (*(str-1) == '.')
|
|
{
|
|
str--;
|
|
}
|
|
}
|
|
|
|
*str = '\0';
|
|
return start;
|
|
}
|