solidc
Robust collection of general-purpose cross-platform C libraries and data structures designed for rapid and safe development in C
Loading...
Searching...
No Matches
str_to_num.c
1#include "../include/str_to_num.h"
2#include "../include/str.h"
3
4#include <errno.h> // for errno, ERANGE
5#include <float.h> // for FLT_EPSILON, DBL_EPSILON, FLT_MAX, DBL_MAX
6#include <inttypes.h> // for strtoimax, strtoumax, intmax_t, uintmax_t
7#include <limits.h> // for INT_MAX, UINT_MAX, etc.
8#include <math.h>
9#include <stdbool.h> // for bool, true, false
10#include <stdio.h> // for fprintf, stderr
11#include <stdlib.h> // for strtod, strtof
12#include <string.h> // for strlen
13
15static inline StoError validate_and_parse_signed(const char* str, int base, intmax_t* result) {
16 if (str == NULL || result == NULL) {
17 return STO_INVALID;
18 }
19
20 char* endptr = NULL;
21 errno = 0;
22 *result = strtoimax(str, &endptr, base);
23
24 // Check for standard ERANGE (overflow of intmax_t)
25 if (errno == ERANGE) {
26 return STO_OVERFLOW;
27 }
28
29 // Check for invalid input (no conversion or partial conversion)
30 if (endptr == str || *endptr != '\0') {
31 return STO_INVALID;
32 }
33
34 return STO_SUCCESS;
35}
36
38static inline StoError validate_and_parse_unsigned(const char* str, int base, uintmax_t* result) {
39 if (str == NULL || result == NULL) {
40 return STO_INVALID;
41 }
42
43 // Skip whitespace to correctly find the sign
44 const char* p = str;
45 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\v' || *p == '\f') {
46 p++;
47 }
48
49 // Check for negative input manually.
50 // Standard strtoumax wraps negative numbers (e.g. "-1" -> UINTMAX_MAX).
51 // For a strict "string to unsigned" conversion, negative input is an underflow.
52 if (*p == '-') {
53 return STO_UNDERFLOW;
54 }
55
56 char* endptr = NULL;
57 errno = 0;
58 *result = strtoumax(str, &endptr, base);
59
60 // Check for standard conversion errors (overflow of uintmax_t)
61 if (errno == ERANGE) {
62 return STO_OVERFLOW;
63 }
64
65 // Check for invalid input (no conversion or partial conversion)
66 if (endptr == str || *endptr != '\0') {
67 return STO_INVALID;
68 }
69
70 return STO_SUCCESS;
71}
72// NOLINTBEGIN(bugprone-macro-parentheses)
73
78#define IMPLEMENT_SIGNED_CONVERSION(func_name, type_name, type_max, type_min) \
79 StoError func_name(const char* str, type_name* result) { \
80 if (result == NULL) { \
81 return STO_INVALID; \
82 } \
83 \
84 intmax_t temp; \
85 StoError err = validate_and_parse_signed(str, 10, &temp); \
86 if (err != STO_SUCCESS) { \
87 return err; \
88 } \
89 \
90 /* Range check before casting */ \
91 if (temp > (intmax_t)(type_max) || temp < (intmax_t)(type_min)) { \
92 return STO_OVERFLOW; \
93 } \
94 \
95 *result = (type_name)temp; \
96 return STO_SUCCESS; \
97 }
98
103#define IMPLEMENT_UNSIGNED_CONVERSION(func_name, type_name, type_max) \
104 StoError func_name(const char* str, type_name* result) { \
105 if (result == NULL) { \
106 return STO_INVALID; \
107 } \
108 \
109 uintmax_t temp; \
110 StoError err = validate_and_parse_unsigned(str, 10, &temp); \
111 if (err != STO_SUCCESS) { \
112 return err; \
113 } \
114 \
115 /* Range check before casting */ \
116 if (temp > (uintmax_t)(type_max)) { \
117 return STO_OVERFLOW; \
118 } \
119 \
120 *result = (type_name)temp; \
121 return STO_SUCCESS; \
122 }
123
125#define IMPLEMENT_SIGNED_BASE_CONVERSION(func_name, type_name, type_max, type_min) \
126 StoError func_name(const char* str, int base, type_name* result) { \
127 if (result == NULL) { \
128 return STO_INVALID; \
129 } \
130 \
131 intmax_t temp; \
132 StoError err = validate_and_parse_signed(str, base, &temp); \
133 if (err != STO_SUCCESS) { \
134 return err; \
135 } \
136 \
137 /* Range check before casting */ \
138 if (temp > (intmax_t)(type_max) || temp < (intmax_t)(type_min)) { \
139 return STO_OVERFLOW; \
140 } \
141 \
142 *result = (type_name)temp; \
143 return STO_SUCCESS; \
144 }
145
147#define IMPLEMENT_UNSIGNED_BASE_CONVERSION(func_name, type_name, type_max) \
148 StoError func_name(const char* str, int base, type_name* result) { \
149 if (result == NULL) { \
150 return STO_INVALID; \
151 } \
152 \
153 uintmax_t temp; \
154 StoError err = validate_and_parse_unsigned(str, base, &temp); \
155 if (err != STO_SUCCESS) { \
156 return err; \
157 } \
158 \
159 /* Range check before casting */ \
160 if (temp > (uintmax_t)(type_max)) { \
161 return STO_OVERFLOW; \
162 } \
163 \
164 *result = (type_name)temp; \
165 return STO_SUCCESS; \
166 }
167
168// NOLINTEND(bugprone-macro-parentheses)
169
170// Generate all the integer conversion functions using macros
171IMPLEMENT_SIGNED_CONVERSION(str_to_i8, int8_t, INT8_MAX, INT8_MIN)
172IMPLEMENT_UNSIGNED_CONVERSION(str_to_u8, uint8_t, UINT8_MAX)
173
174IMPLEMENT_SIGNED_CONVERSION(str_to_i16, int16_t, INT16_MAX, INT16_MIN)
175IMPLEMENT_UNSIGNED_CONVERSION(str_to_u16, uint16_t, UINT16_MAX)
176
177IMPLEMENT_SIGNED_CONVERSION(str_to_i32, int32_t, INT32_MAX, INT32_MIN)
178IMPLEMENT_UNSIGNED_CONVERSION(str_to_u32, uint32_t, UINT32_MAX)
179
180IMPLEMENT_SIGNED_CONVERSION(str_to_i64, int64_t, INT64_MAX, INT64_MIN)
181IMPLEMENT_UNSIGNED_CONVERSION(str_to_u64, uint64_t, UINT64_MAX)
182
183IMPLEMENT_SIGNED_CONVERSION(str_to_int, int, INT_MAX, INT_MIN)
184IMPLEMENT_UNSIGNED_CONVERSION(str_to_uint, unsigned int, UINT_MAX)
185
186IMPLEMENT_SIGNED_CONVERSION(str_to_long, long, LONG_MAX, LONG_MIN)
187IMPLEMENT_UNSIGNED_CONVERSION(str_to_ulong, unsigned long, ULONG_MAX)
188
189// Base conversion variants
190IMPLEMENT_SIGNED_BASE_CONVERSION(str_to_int_base, int, INT_MAX, INT_MIN)
191IMPLEMENT_SIGNED_BASE_CONVERSION(str_to_long_base, long, LONG_MAX, LONG_MIN)
192IMPLEMENT_UNSIGNED_BASE_CONVERSION(str_to_ulong_base, unsigned long, ULONG_MAX)
193
194// Special case for uintptr_t (no upper bound check needed as it can hold any uintmax_t value)
195StoError str_to_uintptr(const char* str, uintptr_t* result) {
196 if (result == NULL) {
197 return STO_INVALID;
198 }
199
200 uintmax_t temp = 0;
201 StoError err = validate_and_parse_unsigned(str, 10, &temp);
202 if (err != STO_SUCCESS) {
203 return err;
204 }
205
206 // uintptr_t should be able to hold any valid pointer value
207 // On most platforms, uintptr_t == uintmax_t, but we cast safely
208 *result = (uintptr_t)temp;
209 return STO_SUCCESS;
210}
211
212StoError str_to_float(const char* str, float* result) {
213 if (str == NULL || result == NULL) {
214 return STO_INVALID;
215 }
216 char* endptr = NULL;
217 errno = 0;
218 *result = strtof(str, &endptr);
219 if (endptr == str || *endptr != '\0') return STO_INVALID;
220 if (errno == ERANGE) {
221 if (isinf(*result) || fabsf(*result) > FLT_MAX) return STO_OVERFLOW;
222 if (*result == 0.0f || fabsf(*result) < FLT_MIN) return STO_UNDERFLOW;
223 }
224
225 return STO_SUCCESS;
226}
227
228StoError str_to_double(const char* str, double* result) {
229 if (str == NULL || result == NULL) {
230 return STO_INVALID;
231 }
232 char* endptr = NULL;
233 errno = 0;
234 *result = strtod(str, &endptr);
235 if (endptr == str || *endptr != '\0') return STO_INVALID;
236 if (errno == ERANGE) {
237 if (isinf(*result) || fabs(*result) > DBL_MAX) return STO_OVERFLOW;
238 if (*result == 0.0 || fabs(*result) < DBL_MIN) return STO_UNDERFLOW;
239 }
240 return STO_SUCCESS;
241}
242
244typedef struct {
245 const char* str;
246 bool value;
247} bool_mapping_t;
248
249static const bool_mapping_t BOOL_MAPPINGS[] = {
250 {"true", true}, {"false", false}, {"yes", true}, {"no", false},
251 {"on", true}, {"off", false}, {"1", true}, {"0", false},
252};
253
254static const size_t BOOL_MAPPINGS_COUNT = sizeof(BOOL_MAPPINGS) / sizeof(BOOL_MAPPINGS[0]);
255
256StoError str_to_bool(const char* str, bool* result) {
257 if (str == NULL || result == NULL) {
258 return STO_INVALID;
259 }
260
261 // Use lookup table for efficient case-insensitive matching
262 for (size_t i = 0; i < BOOL_MAPPINGS_COUNT; i++) {
263 if (strcasecmp(str, BOOL_MAPPINGS[i].str) == 0) {
264 *result = BOOL_MAPPINGS[i].value;
265 return STO_SUCCESS;
266 }
267 }
268
269 return STO_INVALID;
270}
271
272const char* sto_error_string(StoError code) {
273 switch (code) {
274 case STO_SUCCESS:
275 return "Conversion successful";
276 case STO_INVALID:
277 return "Invalid input string or null pointer";
278 case STO_OVERFLOW:
279 return "Numeric overflow";
280 case STO_UNDERFLOW:
281 return "Numeric overflow";
282 default:
283 return "Unknown error code";
284 }
285}
StoError str_to_bool(const char *str, bool *result)
Converts a string to a boolean. Valid inputs are "true", "false", "yes", "no", "1",...
Definition str_to_num.c:256
StoError str_to_ulong_base(const char *str, int base, unsigned long *result)
Converts a string to an unsigned long with a specified base.
StoError str_to_int_base(const char *str, int base, int *result)
Converts a string to an int with a specified base.
StoError str_to_float(const char *str, float *result)
Converts a string to a float.
Definition str_to_num.c:212
StoError str_to_ulong(const char *str, unsigned long *result)
Converts a string to an unsigned long.
StoError str_to_u64(const char *str, uint64_t *result)
Converts a string to a uint64_t.
StoError str_to_i64(const char *str, int64_t *result)
Converts a string to an int64_t.
StoError str_to_u8(const char *str, uint8_t *result)
Converts a string to a uint8_t.
StoError str_to_uint(const char *str, unsigned int *result)
Converts a string to an unsigned int.
StoError str_to_u32(const char *str, uint32_t *result)
Converts a string to a uint32_t.
StoError str_to_double(const char *str, double *result)
Converts a string to a double.
Definition str_to_num.c:228
StoError str_to_long_base(const char *str, int base, long *result)
Converts a string to a long with a specified base.
StoError str_to_u16(const char *str, uint16_t *result)
Converts a string to a uint16_t.
const char * sto_error_string(StoError code)
Definition str_to_num.c:272
StoError str_to_i32(const char *str, int32_t *result)
Converts a string to an int32_t.
StoError str_to_i16(const char *str, int16_t *result)
Converts a string to an int16_t.
StoError str_to_i8(const char *str, int8_t *result)
Converts a string to an int8_t.
StoError str_to_long(const char *str, long *result)
Converts a string to a long.
StoError str_to_int(const char *str, int *result)
Converts a string to an int.