#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; }