Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/mimalloc/libc.c
Line
Count
Source
1
/* ----------------------------------------------------------------------------
2
Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
3
This is free software; you can redistribute it and/or modify it under the
4
terms of the MIT license. A copy of the license can be found in the file
5
"LICENSE" at the root of this distribution.
6
-----------------------------------------------------------------------------*/
7
8
// --------------------------------------------------------
9
// This module defines various std libc functions to reduce
10
// the dependency on libc, and also prevent errors caused
11
// by some libc implementations when called before `main`
12
// executes (due to malloc redirection)
13
// --------------------------------------------------------
14
15
#include "mimalloc.h"
16
#include "mimalloc/internal.h"
17
#include "mimalloc/prim.h"      // mi_prim_getenv
18
19
113k
char _mi_toupper(char c) {
20
113k
  if (c >= 'a' && c <= 'z') return (c - 'a' + 'A');
21
56.5k
                       else return c;
22
113k
}
23
24
54.9k
int _mi_strnicmp(const char* s, const char* t, size_t n) {
25
54.9k
  mi_assert_internal(s!=NULL && t!=NULL);
26
54.9k
  if (n == 0) return 0;
27
56.5k
  for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) {
28
56.5k
    if (_mi_toupper(*s) != _mi_toupper(*t)) break;
29
56.5k
  }
30
54.9k
  return (n == 0 ? 0 : *s - *t);
31
54.9k
}
32
33
0
bool _mi_streq(const char* s, const char* t) {
34
0
  if (s==NULL && t==NULL) return true;
35
0
  if (s==NULL || t==NULL) return false;
36
0
  for (; *s != 0 && *t != 0; s++, t++) {
37
0
    if (*s != *t) break;
38
0
  }
39
0
  return (*s == *t);
40
0
}
41
42
3.33k
bool _mi_strlcpy(char* dest, const char* src, size_t dest_size) {
43
3.33k
  if (dest==NULL || src==NULL || dest_size == 0) return (src==NULL || *src==0);
44
  // copy until end of src, or when dest is (almost) full
45
45.1k
  while (*src != 0 && dest_size > 1) {
46
41.7k
    *dest++ = *src++;
47
41.7k
    dest_size--;
48
41.7k
  }
49
  // always zero terminate
50
3.33k
  *dest = 0;
51
3.33k
  return (*src == 0);
52
3.33k
}
53
54
1.66k
bool _mi_strlcat(char* dest, const char* src, size_t dest_size) {
55
1.66k
  if (dest==NULL || src==NULL || dest_size == 0) return (src==NULL || *src==0);
56
  // find end of string in the dest buffer
57
16.6k
  while (*dest != 0 && dest_size > 1) {
58
14.9k
    dest++;
59
14.9k
    dest_size--;
60
14.9k
  }
61
  // and catenate
62
1.66k
  return _mi_strlcpy(dest, src, dest_size);
63
1.66k
}
64
65
1.66k
size_t _mi_strnlen(const char* s, size_t max_len) {
66
1.66k
  if (s==NULL) return 0;
67
1.66k
  size_t len = 0;
68
43.4k
  while(len < max_len && s[len] != 0) { len++; }
69
1.66k
  return len;
70
1.66k
}
71
72
1.66k
size_t _mi_strlen(const char* s) {
73
1.66k
  return _mi_strnlen(s,PTRDIFF_MAX);
74
1.66k
}
75
76
#ifdef MI_NO_GETENV
77
int _mi_getenv(const char* name, char* result, size_t result_size) {
78
  MI_UNUSED(name);
79
  MI_UNUSED(result);
80
  MI_UNUSED(result_size);
81
  return ENOENT;
82
}
83
#else
84
1.66k
int _mi_getenv(const char* name, char* result, size_t result_size) {
85
1.66k
  if (name==NULL || result == NULL || result_size < 64) return ENOENT;
86
  // change the result of _mi_prim_getenv to an errno result
87
1.66k
  const int res = _mi_prim_getenv(name,result,result_size);
88
1.66k
  return (res > 0 ? 0 : (res == 0 ? ENOENT : EAGAIN));
89
1.66k
}
90
#endif
91
92
93
// --------------------------------------------------------
94
// Define our own primitives for doing an action once
95
// --------------------------------------------------------
96
97
// Returns `true` only on the first invocation, signifying we can execute an action once.
98
// If it returns `true`, the caller should call `_mi_atomic_once_release` after performing the action.
99
// Other threads (than the initial thread that entered) will block until `_mi_atomic_once_release` has been called.
100
185
bool _mi_atomic_once_enter(mi_atomic_once_t* once) {
101
185
  const uintptr_t once_tid = mi_atomic_load_acquire(&once->tid);
102
185
  if mi_likely(once_tid == 1) {
103
37
    return false; // already executed
104
37
  }
105
148
  const mi_threadid_t current_tid = _mi_thread_id();
106
148
  if (once_tid == current_tid) {
107
74
    return false; // recursive invocation; we need this for process_init for example
108
74
  }
109
110
74
  mi_lock_acquire(&once->lock);
111
74
  uintptr_t expected = 0;
112
74
  if (mi_atomic_cas_strong_acq_rel(&once->tid, &expected, current_tid)) {  // could use atomic_load/store as well
113
74
    return true;  // should execute and release
114
74
  }
115
0
  else {
116
0
    mi_lock_release(&once->lock);
117
0
    return false; // already another thread entered and released
118
0
  }
119
74
}
120
121
74
void _mi_atomic_once_release(mi_atomic_once_t* once) {
122
74
  if (mi_atomic_load_acquire(&once->tid)>1) {  // paranoia
123
74
    mi_atomic_store_release(&once->tid,1);     // done executing
124
74
    mi_lock_release(&once->lock);
125
74
  }
126
74
}
127
128
129
// --------------------------------------------------------
130
// Define our own limited `_mi_vsnprintf` and `_mi_snprintf`
131
// This is mostly to avoid calling these when libc is not yet
132
// initialized (and to reduce dependencies)
133
//
134
// format:      d i, p x u, s
135
// prec:        z l ll L
136
// width:       10
137
// align-left:  -
138
// fill:        0
139
// plus:        +
140
// --------------------------------------------------------
141
142
0
static void mi_outc(char c, char** out, char* end) {
143
0
  char* p = *out;
144
0
  if (p >= end) return;
145
0
  *p = c;
146
0
  *out = p + 1;
147
0
}
148
149
0
static void mi_outs(const char* s, char** out, char* end) {
150
0
  if (s == NULL) return;
151
0
  char* p = *out;
152
0
  while (*s != 0 && p < end) {
153
0
    *p++ = *s++;
154
0
  }
155
0
  *out = p;
156
0
}
157
158
0
static void mi_out_fill(char fill, size_t len, char** out, char* end) {
159
0
  char* p = *out;
160
0
  for (size_t i = 0; i < len && p < end; i++) {
161
0
    *p++ = fill;
162
0
  }
163
0
  *out = p;
164
0
}
165
166
0
static void mi_out_alignright(char fill, char* start, size_t len, size_t extra, char* end) {
167
0
  if (len == 0 || extra == 0) return;
168
0
  if (start + len + extra >= end) return;
169
  // move `len` characters to the right (in reverse since it can overlap)
170
0
  for (size_t i = 1; i <= len; i++) {
171
0
    start[len + extra - i] = start[len - i];
172
0
  }
173
  // and fill the start
174
0
  for (size_t i = 0; i < extra; i++) {
175
0
    start[i] = fill;
176
0
  }
177
0
}
178
179
180
static void mi_out_num(uintmax_t x, size_t base, char prefix, char** out, char* end)
181
0
{
182
0
  if (x == 0 || base == 0 || base > 16) {
183
0
    if (prefix != 0) { mi_outc(prefix, out, end); }
184
0
    mi_outc('0',out,end);
185
0
  }
186
0
  else {
187
0
    #define MI_MAX_OUT_DIGITS (160)     /* a 512 bit number has 155 digits */
188
0
    char num[MI_MAX_OUT_DIGITS];
189
0
    int dcount = 0;
190
0
    while(x>0 && dcount < MI_MAX_OUT_DIGITS) {
191
0
      char digit = (char)(x % base);
192
0
      num[dcount++] = (digit <= 9 ? '0' + digit : 'A' + digit - 10);
193
0
      x = x / base;
194
0
    }
195
0
    if (dcount>=MI_MAX_OUT_DIGITS) return;  // don't output anything?
196
0
    if (prefix != 0) {
197
0
      mi_outc(prefix, out, end);
198
0
    }
199
0
    while(dcount-- > 0) {
200
0
      mi_outc(num[dcount], out, end);
201
0
    }
202
0
  }
203
0
}
204
205
206
0
#define MI_NEXTC()  c = *in; if (c==0) break; in++;
207
208
0
int _mi_vsnprintf(char* buf, size_t bufsize, const char* fmt, va_list args) {
209
0
  if (buf == NULL || bufsize == 0 || fmt == NULL) return 0;
210
0
  buf[bufsize - 1] = 0;
211
0
  char* const end = buf + (bufsize - 1);
212
0
  const char* in = fmt;
213
0
  char* out = buf;
214
0
  while (true) {
215
0
    if (out >= end) break;
216
0
    char c;
217
0
    MI_NEXTC();
218
0
    if (c != '%') {
219
0
      if ((c >= ' ' && c <= '~') || c=='\n' || c=='\r' || c=='\t') { // output visible ascii or standard control only
220
0
        mi_outc(c, &out, end);
221
0
      }
222
0
    }
223
0
    else {
224
0
      MI_NEXTC();
225
0
      char   fill = ' ';
226
0
      size_t width = 0;
227
0
      char   numtype = 'd';
228
0
      char   numplus = 0;
229
0
      bool   alignright = true;
230
0
      if (c == '+' || c == ' ') { numplus = c; MI_NEXTC(); }
231
0
      if (c == '-') { alignright = false; MI_NEXTC(); }
232
0
      if (c == '0') { fill = '0'; MI_NEXTC(); }
233
0
      if (c >= '1' && c <= '9') {
234
0
        width = (c - '0'); MI_NEXTC();
235
0
        while (c >= '0' && c <= '9') {
236
0
          if (width < SIZE_MAX/1024) {  // no overflow
237
0
            width = (10 * width) + (c - '0');
238
0
          }
239
0
          MI_NEXTC();
240
0
        }
241
0
        if (c == 0) break;  // extra check due to while
242
0
      }
243
0
      if (c == 'z' || c == 't' || c == 'L') { numtype = c; MI_NEXTC(); }
244
0
      else if (c == 'l') {
245
0
        numtype = c; MI_NEXTC();
246
0
        if (c == 'l') { numtype = 'L'; MI_NEXTC(); }
247
0
      }
248
249
0
      char* start = out;
250
0
      if (c == 's') {
251
        // string
252
0
        const char* s = va_arg(args, const char*);
253
0
        mi_outs(s, &out, end);
254
0
      }
255
0
      else if (c == 'p' || c == 'x' || c == 'u') {
256
        // unsigned
257
0
        uintmax_t x = 0;
258
0
        if (c == 'x' || c == 'u') {
259
0
          if (numtype == 'z')       x = va_arg(args, size_t);
260
0
          else if (numtype == 't')  x = va_arg(args, uintptr_t); // unsigned ptrdiff_t
261
0
          else if (numtype == 'L')  x = va_arg(args, unsigned long long);
262
0
          else if (numtype == 'l')  x = va_arg(args, unsigned long);
263
0
                               else x = va_arg(args, unsigned int);
264
0
        }
265
0
        else if (c == 'p') {
266
0
          void* const p = va_arg(args, void*);
267
0
          x = (uintptr_t)p;
268
0
          mi_outs("0x", &out, end);
269
0
          start = out;
270
0
          width = (width >= 2 ? width - 2 : 0);
271
0
        }
272
0
        if (width == 0 && (c == 'x' || c == 'p')) {
273
0
          if (c == 'p')   { width = 2 * (x <= UINT32_MAX ? 4 : ((x >> 16) <= UINT32_MAX ? 6 : sizeof(void*))); }
274
0
          if (width == 0) { width = 2; }
275
0
          if (alignright) { fill = '0'; }
276
0
        }
277
0
        mi_out_num(x, (c == 'x' || c == 'p' ? 16 : 10), numplus, &out, end);
278
0
      }
279
0
      else if (c == 'i' || c == 'd') {
280
        // signed
281
0
        intmax_t x = 0;
282
0
        if (numtype == 'z')       x = va_arg(args, intptr_t );
283
0
        else if (numtype == 't')  x = va_arg(args, ptrdiff_t);
284
0
        else if (numtype == 'L')  x = va_arg(args, long long);
285
0
        else if (numtype == 'l')  x = va_arg(args, long);
286
0
                             else x = va_arg(args, int);
287
0
        char pre = 0;
288
0
        if (x < 0) {
289
0
          pre = '-';
290
0
          if (x > INTMAX_MIN) { x = -x; }
291
0
        }
292
0
        else if (numplus != 0) {
293
0
          pre = numplus;
294
0
        }
295
0
        mi_out_num((uintmax_t)x, 10, pre, &out, end);
296
0
      }
297
0
      else if (c >= ' ' && c <= '~') {
298
        // unknown format
299
0
        mi_outc('%', &out, end);
300
0
        mi_outc(c, &out, end);
301
0
      }
302
303
      // fill & align
304
0
      mi_assert_internal(out <= end);
305
0
      mi_assert_internal(out >= start);
306
0
      const size_t len = out - start;
307
0
      if (len < width) {
308
0
        mi_out_fill(fill, width - len, &out, end);
309
0
        if (alignright && out <= end) {
310
0
          mi_out_alignright(fill, start, len, width - len, end);
311
0
        }
312
0
      }
313
0
    }
314
0
  }
315
0
  mi_assert_internal(out <= end);
316
0
  *out = 0;
317
0
  return (int)(out - buf);
318
0
}
319
320
0
int _mi_snprintf(char* buf, size_t buflen, const char* fmt, ...) {
321
0
  va_list args;
322
0
  va_start(args, fmt);
323
0
  const int written = _mi_vsnprintf(buf, buflen, fmt, args);
324
0
  va_end(args);
325
0
  return written;
326
0
}
327
328
329
#if MI_SIZE_SIZE == 4
330
#define mi_mask_even_bits32      (0x55555555)
331
#define mi_mask_even_pairs32     (0x33333333)
332
#define mi_mask_even_nibbles32   (0x0F0F0F0F)
333
334
// sum of all the bytes in `x` if it is guaranteed that the sum < 256!
335
static size_t mi_byte_sum32(uint32_t x) {
336
  // perform `x * 0x01010101`: the highest byte contains the sum of all bytes.
337
  x += (x << 8);
338
  x += (x << 16);
339
  return (size_t)(x >> 24);
340
}
341
342
static size_t mi_popcount_generic32(uint32_t x) {
343
  // first count each 2-bit group `a`, where: a==0b00 -> 00, a==0b01 -> 01, a==0b10 -> 01, a==0b11 -> 10
344
  // in other words, `a - (a>>1)`; to do this in parallel, we need to mask to prevent spilling a bit pair
345
  // into the lower bit-pair:
346
  x = x - ((x >> 1) & mi_mask_even_bits32);
347
  // add the 2-bit pair results
348
  x = (x & mi_mask_even_pairs32) + ((x >> 2) & mi_mask_even_pairs32);
349
  // add the 4-bit nibble results
350
  x = (x + (x >> 4)) & mi_mask_even_nibbles32;
351
  // each byte now has a count of its bits, we can sum them now:
352
  return mi_byte_sum32(x);
353
}
354
355
mi_decl_noinline size_t _mi_popcount_generic(size_t x) {
356
  return mi_popcount_generic32(x);
357
}
358
359
#else
360
0
#define mi_mask_even_bits64      (0x5555555555555555)
361
0
#define mi_mask_even_pairs64     (0x3333333333333333)
362
0
#define mi_mask_even_nibbles64   (0x0F0F0F0F0F0F0F0F)
363
364
// sum of all the bytes in `x` if it is guaranteed that the sum < 256!
365
0
static size_t mi_byte_sum64(uint64_t x) {
366
0
  x += (x << 8);
367
0
  x += (x << 16);
368
0
  x += (x << 32);
369
0
  return (size_t)(x >> 56);
370
0
}
371
372
0
static size_t mi_popcount_generic64(uint64_t x) {
373
0
  x = x - ((x >> 1) & mi_mask_even_bits64);
374
0
  x = (x & mi_mask_even_pairs64) + ((x >> 2) & mi_mask_even_pairs64);
375
0
  x = (x + (x >> 4)) & mi_mask_even_nibbles64;
376
0
  return mi_byte_sum64(x);
377
0
}
378
379
0
mi_decl_noinline size_t _mi_popcount_generic(size_t x) {
380
0
  return mi_popcount_generic64(x);
381
0
}
382
#endif
383