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