| /* ---------------------------------------------------------------------------- |
| Copyright (c) 2018-2023, Microsoft Research, Daan Leijen |
| This is free software; you can redistribute it and/or modify it under the |
| terms of the MIT license. A copy of the license can be found in the file |
| "LICENSE" at the root of this distribution. |
| -----------------------------------------------------------------------------*/ |
| |
| // -------------------------------------------------------- |
| // This module defines various std libc functions to reduce |
| // the dependency on libc, and also prevent errors caused |
| // by some libc implementations when called before `main` |
| // executes (due to malloc redirection) |
| // -------------------------------------------------------- |
| |
| #include "mimalloc.h" |
| #include "mimalloc/internal.h" |
| #include "mimalloc/prim.h" // mi_prim_getenv |
| |
| char _mi_toupper(char c) { |
| if (c >= 'a' && c <= 'z') return (c - 'a' + 'A'); |
| else return c; |
| } |
| |
| int _mi_strnicmp(const char* s, const char* t, size_t n) { |
| if (n == 0) return 0; |
| for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) { |
| if (_mi_toupper(*s) != _mi_toupper(*t)) break; |
| } |
| return (n == 0 ? 0 : *s - *t); |
| } |
| |
| void _mi_strlcpy(char* dest, const char* src, size_t dest_size) { |
| if (dest==NULL || src==NULL || dest_size == 0) return; |
| // copy until end of src, or when dest is (almost) full |
| while (*src != 0 && dest_size > 1) { |
| *dest++ = *src++; |
| dest_size--; |
| } |
| // always zero terminate |
| *dest = 0; |
| } |
| |
| void _mi_strlcat(char* dest, const char* src, size_t dest_size) { |
| if (dest==NULL || src==NULL || dest_size == 0) return; |
| // find end of string in the dest buffer |
| while (*dest != 0 && dest_size > 1) { |
| dest++; |
| dest_size--; |
| } |
| // and catenate |
| _mi_strlcpy(dest, src, dest_size); |
| } |
| |
| size_t _mi_strlen(const char* s) { |
| if (s==NULL) return 0; |
| size_t len = 0; |
| while(s[len] != 0) { len++; } |
| return len; |
| } |
| |
| size_t _mi_strnlen(const char* s, size_t max_len) { |
| if (s==NULL) return 0; |
| size_t len = 0; |
| while(s[len] != 0 && len < max_len) { len++; } |
| return len; |
| } |
| |
| #ifdef MI_NO_GETENV |
| bool _mi_getenv(const char* name, char* result, size_t result_size) { |
| MI_UNUSED(name); |
| MI_UNUSED(result); |
| MI_UNUSED(result_size); |
| return false; |
| } |
| #else |
| bool _mi_getenv(const char* name, char* result, size_t result_size) { |
| if (name==NULL || result == NULL || result_size < 64) return false; |
| return _mi_prim_getenv(name,result,result_size); |
| } |
| #endif |
| |
| // -------------------------------------------------------- |
| // Define our own limited `_mi_vsnprintf` and `_mi_snprintf` |
| // This is mostly to avoid calling these when libc is not yet |
| // initialized (and to reduce dependencies) |
| // |
| // format: d i, p x u, s |
| // prec: z l ll L |
| // width: 10 |
| // align-left: - |
| // fill: 0 |
| // plus: + |
| // -------------------------------------------------------- |
| |
| static void mi_outc(char c, char** out, char* end) { |
| char* p = *out; |
| if (p >= end) return; |
| *p = c; |
| *out = p + 1; |
| } |
| |
| static void mi_outs(const char* s, char** out, char* end) { |
| if (s == NULL) return; |
| char* p = *out; |
| while (*s != 0 && p < end) { |
| *p++ = *s++; |
| } |
| *out = p; |
| } |
| |
| static void mi_out_fill(char fill, size_t len, char** out, char* end) { |
| char* p = *out; |
| for (size_t i = 0; i < len && p < end; i++) { |
| *p++ = fill; |
| } |
| *out = p; |
| } |
| |
| static void mi_out_alignright(char fill, char* start, size_t len, size_t extra, char* end) { |
| if (len == 0 || extra == 0) return; |
| if (start + len + extra >= end) return; |
| // move `len` characters to the right (in reverse since it can overlap) |
| for (size_t i = 1; i <= len; i++) { |
| start[len + extra - i] = start[len - i]; |
| } |
| // and fill the start |
| for (size_t i = 0; i < extra; i++) { |
| start[i] = fill; |
| } |
| } |
| |
| |
| static void mi_out_num(uintptr_t x, size_t base, char prefix, char** out, char* end) |
| { |
| if (x == 0 || base == 0 || base > 16) { |
| if (prefix != 0) { mi_outc(prefix, out, end); } |
| mi_outc('0',out,end); |
| } |
| else { |
| // output digits in reverse |
| char* start = *out; |
| while (x > 0) { |
| char digit = (char)(x % base); |
| mi_outc((digit <= 9 ? '0' + digit : 'A' + digit - 10),out,end); |
| x = x / base; |
| } |
| if (prefix != 0) { |
| mi_outc(prefix, out, end); |
| } |
| size_t len = *out - start; |
| // and reverse in-place |
| for (size_t i = 0; i < (len / 2); i++) { |
| char c = start[len - i - 1]; |
| start[len - i - 1] = start[i]; |
| start[i] = c; |
| } |
| } |
| } |
| |
| |
| #define MI_NEXTC() c = *in; if (c==0) break; in++; |
| |
| void _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) { |
| if (buf == NULL || bufsize == 0 || fmt == NULL) return; |
| buf[bufsize - 1] = 0; |
| char* const end = buf + (bufsize - 1); |
| const char* in = fmt; |
| char* out = buf; |
| while (true) { |
| if (out >= end) break; |
| char c; |
| MI_NEXTC(); |
| if (c != '%') { |
| if ((c >= ' ' && c <= '~') || c=='\n' || c=='\r' || c=='\t') { // output visible ascii or standard control only |
| mi_outc(c, &out, end); |
| } |
| } |
| else { |
| MI_NEXTC(); |
| char fill = ' '; |
| size_t width = 0; |
| char numtype = 'd'; |
| char numplus = 0; |
| bool alignright = true; |
| if (c == '+' || c == ' ') { numplus = c; MI_NEXTC(); } |
| if (c == '-') { alignright = false; MI_NEXTC(); } |
| if (c == '0') { fill = '0'; MI_NEXTC(); } |
| if (c >= '1' && c <= '9') { |
| width = (c - '0'); MI_NEXTC(); |
| while (c >= '0' && c <= '9') { |
| width = (10 * width) + (c - '0'); MI_NEXTC(); |
| } |
| if (c == 0) break; // extra check due to while |
| } |
| if (c == 'z' || c == 't' || c == 'L') { numtype = c; MI_NEXTC(); } |
| else if (c == 'l') { |
| numtype = c; MI_NEXTC(); |
| if (c == 'l') { numtype = 'L'; MI_NEXTC(); } |
| } |
| |
| char* start = out; |
| if (c == 's') { |
| // string |
| const char* s = va_arg(args, const char*); |
| mi_outs(s, &out, end); |
| } |
| else if (c == 'p' || c == 'x' || c == 'u') { |
| // unsigned |
| uintptr_t x = 0; |
| if (c == 'x' || c == 'u') { |
| if (numtype == 'z') x = va_arg(args, size_t); |
| else if (numtype == 't') x = va_arg(args, uintptr_t); // unsigned ptrdiff_t |
| else if (numtype == 'L') x = (uintptr_t)va_arg(args, unsigned long long); |
| else x = va_arg(args, unsigned long); |
| } |
| else if (c == 'p') { |
| x = va_arg(args, uintptr_t); |
| mi_outs("0x", &out, end); |
| start = out; |
| width = (width >= 2 ? width - 2 : 0); |
| } |
| if (width == 0 && (c == 'x' || c == 'p')) { |
| if (c == 'p') { width = 2 * (x <= UINT32_MAX ? 4 : ((x >> 16) <= UINT32_MAX ? 6 : sizeof(void*))); } |
| if (width == 0) { width = 2; } |
| fill = '0'; |
| } |
| mi_out_num(x, (c == 'x' || c == 'p' ? 16 : 10), numplus, &out, end); |
| } |
| else if (c == 'i' || c == 'd') { |
| // signed |
| intptr_t x = 0; |
| if (numtype == 'z') x = va_arg(args, intptr_t ); |
| else if (numtype == 't') x = va_arg(args, ptrdiff_t); |
| else if (numtype == 'L') x = (intptr_t)va_arg(args, long long); |
| else x = va_arg(args, long); |
| char pre = 0; |
| if (x < 0) { |
| pre = '-'; |
| if (x > INTPTR_MIN) { x = -x; } |
| } |
| else if (numplus != 0) { |
| pre = numplus; |
| } |
| mi_out_num((uintptr_t)x, 10, pre, &out, end); |
| } |
| else if (c >= ' ' && c <= '~') { |
| // unknown format |
| mi_outc('%', &out, end); |
| mi_outc(c, &out, end); |
| } |
| |
| // fill & align |
| mi_assert_internal(out <= end); |
| mi_assert_internal(out >= start); |
| const size_t len = out - start; |
| if (len < width) { |
| mi_out_fill(fill, width - len, &out, end); |
| if (alignright && out <= end) { |
| mi_out_alignright(fill, start, len, width - len, end); |
| } |
| } |
| } |
| } |
| mi_assert_internal(out <= end); |
| *out = 0; |
| } |
| |
| void _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| _mi_vsnprintf(buf, buflen, fmt, args); |
| va_end(args); |
| } |