FastLED 3.9.15
Loading...
Searching...
No Matches
cstdlib.cpp.hpp
Go to the documentation of this file.
1#include "fl/stl/cstdlib.h"
2#include "fl/stl/cstring.h"
3#include "platforms/is_platform.h" // IWYU pragma: keep
4
5// IWYU pragma: begin_keep
6#include <stdlib.h> // ok include
7#if defined(FL_IS_WIN)
8#include <malloc.h> // ok include — _aligned_malloc / _aligned_free
9#endif
10#ifdef FL_IS_STUB
11 #include <cstdlib> // ok include
12#endif
13// IWYU pragma: end_keep
14
15namespace fl {
16
17// ============================================================================
18// fl::aligned_alloc / fl::aligned_free
19// ============================================================================
20
21void *aligned_alloc(fl::size_t alignment, fl::size_t size) {
22#if defined(FL_IS_AVR) || defined(FL_IS_ESP8266) || defined(FL_IS_ARM) || defined(FL_IS_APOLLO3)
23 // Many bare-metal toolchains (newlib-nano on STM32, nRF52, SAMD, RP2040,
24 // Teensy, etc.) ship an aligned_alloc that internally calls
25 // posix_memalign, which doesn't exist on bare-metal and causes an
26 // "undefined reference to `posix_memalign'" link error.
27 // Fall back to plain malloc — sufficient for the alignments FastLED
28 // actually requests on these constrained targets.
29 (void)alignment;
30 return ::malloc(size);
31#elif defined(FL_IS_WIN)
32 return ::_aligned_malloc(size, alignment);
33#elif defined(FL_IS_ESP32) && !ESP_IDF_VERSION_4_OR_HIGHER
34 // ESP-IDF 3.x toolchain (GCC 5.2 / newlib) does not provide
35 // ::aligned_alloc. Fall back to plain malloc.
36 (void)alignment;
37 return ::malloc(size);
38#else
39 fl::size_t aligned_size = (size + alignment - 1) & ~(alignment - 1);
40 return ::aligned_alloc(alignment, aligned_size);
41#endif
42}
43
44void aligned_free(void *ptr) {
45#if defined(FL_IS_AVR) || defined(FL_IS_ESP8266) || defined(FL_IS_ARM) || defined(FL_IS_APOLLO3)
46 ::free(ptr);
47#elif defined(FL_IS_WIN)
48 ::_aligned_free(ptr);
49#else
50 ::free(ptr);
51#endif
52}
53
54// Helper function to check if a character is a digit in the given base
55static bool isDigitInBase(char c, int base) {
56 if (base <= 10) {
57 return c >= '0' && c < ('0' + base);
58 }
59 if (c >= '0' && c <= '9') {
60 return true;
61 }
62 if (c >= 'a' && c < ('a' + base - 10)) {
63 return true;
64 }
65 if (c >= 'A' && c < ('A' + base - 10)) {
66 return true;
67 }
68 return false;
69}
70
71// Helper function to convert character to digit value
72static int charToDigit(char c) {
73 if (c >= '0' && c <= '9') {
74 return c - '0';
75 }
76 if (c >= 'a' && c <= 'z') {
77 return c - 'a' + 10;
78 }
79 if (c >= 'A' && c <= 'Z') {
80 return c - 'A' + 10;
81 }
82 return 0;
83}
84
85long strtol(const char* str, char** endptr, int base) {
86 if (!str) {
87 if (endptr) {
88 *endptr = const_cast<char*>(str);
89 }
90 return 0;
91 }
92
93 const char* p = str;
94 long result = 0;
95 bool negative = false;
96
97 // Skip leading whitespace
98 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v') {
99 p++;
100 }
101
102 // Handle sign
103 if (*p == '-') {
104 negative = true;
105 p++;
106 } else if (*p == '+') {
107 p++;
108 }
109
110 // Auto-detect base if base is 0
111 if (base == 0) {
112 if (*p == '0') {
113 if (*(p + 1) == 'x' || *(p + 1) == 'X') {
114 base = 16;
115 p += 2;
116 } else {
117 base = 8;
118 p++;
119 }
120 } else {
121 base = 10;
122 }
123 } else if (base == 16) {
124 // Skip optional 0x/0X prefix for base 16
125 if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) {
126 p += 2;
127 }
128 }
129
130 // Validate base
131 if (base < 2 || base > 36) {
132 if (endptr) {
133 *endptr = const_cast<char*>(str);
134 }
135 return 0;
136 }
137
138 // Parse digits
139 const char* start = p;
140 while (*p && isDigitInBase(*p, base)) {
141 result = result * base + charToDigit(*p);
142 p++;
143 }
144
145 // Set endptr to point to first invalid character
146 if (endptr) {
147 if (p == start) {
148 // No digits parsed
149 *endptr = const_cast<char*>(str);
150 } else {
151 *endptr = const_cast<char*>(p);
152 }
153 }
154
155 return negative ? -result : result;
156}
157
158unsigned long strtoul(const char* str, char** endptr, int base) {
159 if (!str) {
160 if (endptr) {
161 *endptr = const_cast<char*>(str);
162 }
163 return 0;
164 }
165
166 const char* p = str;
167 unsigned long result = 0;
168
169 // Skip leading whitespace
170 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v') {
171 p++;
172 }
173
174 // Handle optional + sign (ignore - for unsigned)
175 if (*p == '+') {
176 p++;
177 } else if (*p == '-') {
178 // Negative values wrap around for unsigned
179 p++;
180 }
181
182 // Auto-detect base if base is 0
183 if (base == 0) {
184 if (*p == '0') {
185 if (*(p + 1) == 'x' || *(p + 1) == 'X') {
186 base = 16;
187 p += 2;
188 } else {
189 base = 8;
190 p++;
191 }
192 } else {
193 base = 10;
194 }
195 } else if (base == 16) {
196 // Skip optional 0x/0X prefix for base 16
197 if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) {
198 p += 2;
199 }
200 }
201
202 // Validate base
203 if (base < 2 || base > 36) {
204 if (endptr) {
205 *endptr = const_cast<char*>(str);
206 }
207 return 0;
208 }
209
210 // Parse digits
211 const char* start = p;
212 while (*p && isDigitInBase(*p, base)) {
213 result = result * base + charToDigit(*p);
214 p++;
215 }
216
217 // Set endptr to point to first invalid character
218 if (endptr) {
219 if (p == start) {
220 // No digits parsed
221 *endptr = const_cast<char*>(str);
222 } else {
223 *endptr = const_cast<char*>(p);
224 }
225 }
226
227 return result;
228}
229
230int atoi(const char* str) {
231 return static_cast<int>(strtol(str, nullptr, 10));
232}
233
234long atol(const char* str) {
235 return strtol(str, nullptr, 10);
236}
237
238double strtod(const char* str, char** endptr) {
239 if (!str) {
240 if (endptr) {
241 *endptr = const_cast<char*>(str);
242 }
243 return 0.0;
244 }
245
246 const char* p = str;
247 double result = 0.0;
248 bool negative = false;
249
250 // Skip leading whitespace
251 while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v') {
252 p++;
253 }
254
255 // Handle sign
256 if (*p == '-') {
257 negative = true;
258 p++;
259 } else if (*p == '+') {
260 p++;
261 }
262
263 const char* start = p;
264
265 // Parse integer part
266 while (*p >= '0' && *p <= '9') {
267 result = result * 10.0 + (*p - '0');
268 p++;
269 }
270
271 // Parse fractional part
272 if (*p == '.') {
273 p++;
274 double fraction = 0.1;
275 while (*p >= '0' && *p <= '9') {
276 result += (*p - '0') * fraction;
277 fraction *= 0.1;
278 p++;
279 }
280 }
281
282 // Parse exponent
283 if (*p == 'e' || *p == 'E') {
284 p++;
285 bool expNegative = false;
286 if (*p == '-') {
287 expNegative = true;
288 p++;
289 } else if (*p == '+') {
290 p++;
291 }
292
293 int exp = 0;
294 while (*p >= '0' && *p <= '9') {
295 exp = exp * 10 + (*p - '0');
296 p++;
297 }
298
299 double multiplier = 1.0;
300 for (int i = 0; i < exp; i++) {
301 multiplier *= 10.0;
302 }
303
304 if (expNegative) {
305 result /= multiplier;
306 } else {
307 result *= multiplier;
308 }
309 }
310
311 // Set endptr
312 if (endptr) {
313 if (p == start) {
314 *endptr = const_cast<char*>(str);
315 } else {
316 *endptr = const_cast<char*>(p);
317 }
318 }
319
320 return negative ? -result : result;
321}
322
323// Forward declarations for qsort helpers
324namespace detail {
325 void qsort_swap(char* a, char* b, size_t size);
326 void qsort_impl(char* base, size_t nmemb, size_t size, qsort_compare_fn compar);
327}
328
329// Swap two elements of given size
330void detail::qsort_swap(char* a, char* b, size_t size) {
331 if (a == b) return;
332 constexpr size_t STACK_BUF_SIZE = 64;
333 if (size <= STACK_BUF_SIZE) {
334 char tmp[STACK_BUF_SIZE];
335 memcpy(tmp, a, size);
336 memcpy(a, b, size);
337 memcpy(b, tmp, size);
338 } else {
339 for (size_t i = 0; i < size; ++i) {
340 char t = a[i];
341 a[i] = b[i];
342 b[i] = t;
343 }
344 }
345}
346
347// Insertion sort for small arrays (stable and efficient for small n)
348static void qsort_insertion_sort(char* base, size_t nmemb, size_t size, qsort_compare_fn compar) {
349 for (size_t i = 1; i < nmemb; ++i) {
350 // Save current element in temp buffer
351 char temp[256]; // Stack buffer for elements up to 256 bytes
352 char* elem_i = base + i * size;
353
354 if (size <= sizeof(temp)) {
355 memcpy(temp, elem_i, size);
356
357 size_t j = i;
358 while (j > 0 && compar(temp, base + (j - 1) * size) < 0) {
359 memcpy(base + j * size, base + (j - 1) * size, size);
360 --j;
361 }
362
363 memcpy(base + j * size, temp, size);
364 } else {
365 // For large elements, use swaps instead of shifts
366 size_t j = i;
367 while (j > 0 && compar(elem_i, base + (j - 1) * size) < 0) {
368 detail::qsort_swap(base + j * size, base + (j - 1) * size, size);
369 --j;
370 }
371 }
372 }
373}
374
375// Partition function for quicksort
376static size_t qsort_partition(char* base, size_t nmemb, size_t size, qsort_compare_fn compar) {
377 // Use median-of-three for pivot selection
378 size_t mid = nmemb / 2;
379 size_t last = nmemb - 1;
380
381 char* first_elem = base;
382 char* mid_elem = base + mid * size;
383 char* last_elem = base + last * size;
384
385 // Sort first, middle, last to find median
386 if (compar(mid_elem, first_elem) < 0) {
387 detail::qsort_swap(first_elem, mid_elem, size);
388 }
389 if (compar(last_elem, first_elem) < 0) {
390 detail::qsort_swap(first_elem, last_elem, size);
391 }
392 if (compar(last_elem, mid_elem) < 0) {
393 detail::qsort_swap(mid_elem, last_elem, size);
394 }
395
396 // Now mid_elem contains the median, swap it to end-1 position
397 detail::qsort_swap(mid_elem, base + (last - 1) * size, size);
398 char* pivot = base + (last - 1) * size;
399
400 // Partition
401 size_t i = 0;
402 for (size_t j = 0; j < last - 1; ++j) {
403 if (compar(base + j * size, pivot) < 0) {
404 if (i != j) {
405 detail::qsort_swap(base + i * size, base + j * size, size);
406 }
407 ++i;
408 }
409 }
410
411 // Move pivot to its final position
412 detail::qsort_swap(base + i * size, pivot, size);
413 return i;
414}
415
416// Recursive quicksort implementation
417void detail::qsort_impl(char* base, size_t nmemb, size_t size, qsort_compare_fn compar) {
418 if (nmemb <= 1) {
419 return;
420 }
421
422 // Use insertion sort for small arrays (threshold of 16)
423 if (nmemb <= 16) {
424 qsort_insertion_sort(base, nmemb, size, compar);
425 return;
426 }
427
428 size_t pivot_idx = qsort_partition(base, nmemb, size, compar);
429
430 // Sort left partition
431 qsort_impl(base, pivot_idx, size, compar);
432
433 // Sort right partition
434 if (pivot_idx + 1 < nmemb) {
435 qsort_impl(base + (pivot_idx + 1) * size, nmemb - pivot_idx - 1, size, compar);
436 }
437}
438
439// qsort - Direct implementation without using fl::sort to avoid proxy iterator issues
440void qsort(void* base, size_t nmemb, size_t size, qsort_compare_fn compar) {
441 if (!base || nmemb <= 1 || size == 0 || !compar) {
442 return;
443 }
444
445 char* arr = static_cast<char*>(base);
446 detail::qsort_impl(arr, nmemb, size, compar);
447}
448
449u32 rand() {
450 return static_cast<u32>(::rand());
451}
452
453// Get the value of an environment variable
454// Only functional on FL_IS_STUB (stub platform), returns nullptr otherwise
455const char* getenv(const char* name) {
456#ifdef FL_IS_STUB
457 return ::getenv(name);
458#else
459 (void)name; // Suppress unused parameter warning
460 return nullptr;
461#endif
462}
463
464} // namespace fl
void qsort_impl(char *base, size_t nmemb, size_t size, qsort_compare_fn compar)
void qsort_swap(char *a, char *b, size_t size)
__SIZE_TYPE__ size_t
Definition s16x16x4.h:16
void * memcpy(void *dest, const void *src, size_t n) FL_NOEXCEPT
static int charToDigit(char c)
static size_t qsort_partition(char *base, size_t nmemb, size_t size, qsort_compare_fn compar)
void aligned_free(void *ptr)
long atol(const char *str)
const char * getenv(const char *name)
int(* qsort_compare_fn)(const void *, const void *)
Definition cstdlib.h:47
void * aligned_alloc(fl::size_t alignment, fl::size_t size)
void qsort(void *base, size_t nmemb, size_t size, qsort_compare_fn compar)
static bool isDigitInBase(char c, int base)
static void qsort_insertion_sort(char *base, size_t nmemb, size_t size, qsort_compare_fn compar)
void free(void *ptr)
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
int atoi(const char *str)
u32 rand()
double strtod(const char *str, char **endptr)
unsigned long strtoul(const char *str, char **endptr, int base)
enable_if< is_fixed_point< T >::value, T >::type exp(T x) FL_NOEXCEPT
long strtol(const char *str, char **endptr, int base)
Base definition for an LED controller.
Definition crgb.hpp:179