Coverage Report

Created: 2026-03-31 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ruby/util.c
Line
Count
Source
1
/**********************************************************************
2
3
  util.c -
4
5
  $Author$
6
  created at: Fri Mar 10 17:22:34 JST 1995
7
8
  Copyright (C) 1993-2008 Yukihiro Matsumoto
9
10
**********************************************************************/
11
12
#if defined __MINGW32__ || defined __MINGW64__
13
# define MINGW_HAS_SECURE_API 1
14
#endif
15
16
#ifndef __STDC_WANT_LIB_EXT1__
17
#define __STDC_WANT_LIB_EXT1__ 1 /* for qsort_s() */
18
#endif
19
20
#include "ruby/internal/config.h"
21
22
#include <ctype.h>
23
#include <errno.h>
24
#include <float.h>
25
#include <math.h>
26
#include <stdio.h>
27
28
#ifdef _WIN32
29
# include "missing/file.h"
30
#endif
31
32
#include "internal.h"
33
#include "internal/sanitizers.h"
34
#include "internal/imemo.h"
35
#include "internal/util.h"
36
#include "ruby/util.h"
37
#include "ruby_atomic.h"
38
39
const char ruby_hexdigits[] = "0123456789abcdef0123456789ABCDEF";
40
0
#define hexdigit ruby_hexdigits
41
42
unsigned long
43
ruby_scan_oct(const char *start, size_t len, size_t *retlen)
44
64.9k
{
45
64.9k
    int overflow;
46
64.9k
    unsigned long val = ruby_scan_digits(start, (ssize_t)len, 8, retlen, &overflow);
47
64.9k
    (void)overflow;
48
64.9k
    return val;
49
64.9k
}
50
51
unsigned long
52
ruby_scan_hex(const char *start, size_t len, size_t *retlen)
53
196k
{
54
196k
    int overflow;
55
196k
    unsigned long val = ruby_scan_digits(start, (ssize_t)len, 16, retlen, &overflow);
56
196k
    (void)overflow;
57
196k
    return val;
58
196k
}
59
60
const signed char ruby_digit36_to_number_table[] = {
61
    /*     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f */
62
    /*0*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
63
    /*1*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
64
    /*2*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
65
    /*3*/  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
66
    /*4*/ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
67
    /*5*/ 25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
68
    /*6*/ -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
69
    /*7*/ 25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
70
    /*8*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
71
    /*9*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
72
    /*a*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
73
    /*b*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
74
    /*c*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
75
    /*d*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
76
    /*e*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
77
    /*f*/ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
78
};
79
80
NO_SANITIZE("unsigned-integer-overflow", extern unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow));
81
unsigned long
82
ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow)
83
321k
{
84
321k
    RBIMPL_ASSERT_OR_ASSUME(base >= 2);
85
321k
    RBIMPL_ASSERT_OR_ASSUME(base <= 36);
86
87
321k
    const char *start = str;
88
321k
    unsigned long ret = 0, x;
89
321k
    unsigned long mul_overflow = (~(unsigned long)0) / base;
90
91
321k
    *overflow = 0;
92
93
321k
    if (!len) {
94
400
        *retlen = 0;
95
400
        return 0;
96
400
    }
97
98
246M
    do {
99
246M
        int d = ruby_digit36_to_number_table[(unsigned char)*str++];
100
246M
        if (d == -1 || base <= d) {
101
126k
            --str;
102
126k
            break;
103
126k
        }
104
246M
        if (mul_overflow < ret)
105
133M
            *overflow = 1;
106
246M
        ret *= base;
107
246M
        x = ret;
108
246M
        ret += d;
109
246M
        if (ret < x)
110
5.74k
            *overflow = 1;
111
246M
    } while (len < 0 || --len);
112
321k
    *retlen = str - start;
113
321k
    return ret;
114
321k
}
115
116
unsigned long
117
ruby_strtoul(const char *str, char **endptr, int base)
118
0
{
119
0
    int c, b, overflow;
120
0
    int sign = 0;
121
0
    size_t len;
122
0
    unsigned long ret;
123
0
    const char *subject_found = str;
124
125
0
    if (base < 0) {
126
0
        errno = EINVAL;
127
0
        return 0;
128
0
    }
129
130
0
    if (base == 1 || 36 < base) {
131
0
        errno = EINVAL;
132
0
        return 0;
133
0
    }
134
135
0
    while ((c = *str) && ISSPACE(c))
136
0
        str++;
137
138
0
    if (c == '+') {
139
0
        sign = 1;
140
0
        str++;
141
0
    }
142
0
    else if (c == '-') {
143
0
        sign = -1;
144
0
        str++;
145
0
    }
146
147
0
    if (str[0] == '0') {
148
0
        subject_found = str+1;
149
0
        if (base == 0 || base == 16) {
150
0
            if (str[1] == 'x' || str[1] == 'X') {
151
0
                b = 16;
152
0
                str += 2;
153
0
            }
154
0
            else {
155
0
                b = base == 0 ? 8 : 16;
156
0
                str++;
157
0
            }
158
0
        }
159
0
        else {
160
0
            b = base;
161
0
            str++;
162
0
        }
163
0
    }
164
0
    else {
165
0
        b = base == 0 ? 10 : base;
166
0
    }
167
168
0
    ret = ruby_scan_digits(str, -1, b, &len, &overflow);
169
170
0
    if (0 < len)
171
0
        subject_found = str+len;
172
173
0
    if (endptr)
174
0
        *endptr = (char*)subject_found;
175
176
0
    if (overflow) {
177
0
        errno = ERANGE;
178
0
        return ULONG_MAX;
179
0
    }
180
181
0
    if (sign < 0) {
182
0
        ret = (unsigned long)(-(long)ret);
183
0
        return ret;
184
0
    }
185
0
    else {
186
0
        return ret;
187
0
    }
188
0
}
189
190
#if !defined HAVE_GNU_QSORT_R
191
#include <sys/types.h>
192
#include <stdint.h>
193
#ifdef HAVE_UNISTD_H
194
#include <unistd.h>
195
#endif
196
197
typedef int (cmpfunc_t)(const void*, const void*, void*);
198
199
#if defined HAVE_QSORT_S && defined RUBY_MSVCRT_VERSION
200
/* In contrast to its name, Visual Studio qsort_s is incompatible with
201
 * C11 in the order of the comparison function's arguments, and same
202
 * as BSD qsort_r rather. */
203
# define qsort_r(base, nel, size, arg, cmp) qsort_s(base, nel, size, cmp, arg)
204
# define cmp_bsd_qsort cmp_ms_qsort
205
# define HAVE_BSD_QSORT_R 1
206
#endif
207
208
#if defined HAVE_BSD_QSORT_R
209
struct bsd_qsort_r_args {
210
    cmpfunc_t *cmp;
211
    void *arg;
212
};
213
214
static int
215
cmp_bsd_qsort(void *d, const void *a, const void *b)
216
{
217
    const struct bsd_qsort_r_args *args = d;
218
    return (*args->cmp)(a, b, args->arg);
219
}
220
221
void
222
ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void *d)
223
{
224
    struct bsd_qsort_r_args args;
225
    args.cmp = cmp;
226
    args.arg = d;
227
    qsort_r(base, nel, size, &args, cmp_bsd_qsort);
228
}
229
#elif defined HAVE_QSORT_S
230
/* C11 qsort_s has the same arguments as GNU's, but uses
231
 * runtime-constraints handler. */
232
void
233
ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void *d)
234
{
235
    if (!nel || !size) return;  /* nothing to sort */
236
237
    /* get rid of runtime-constraints handler for MT-safeness */
238
    if (!base || !cmp) return;
239
    if (nel > RSIZE_MAX || size > RSIZE_MAX) return;
240
241
    qsort_s(base, nel, size, cmp, d);
242
}
243
# define HAVE_GNU_QSORT_R 1
244
#else
245
/* mm.c */
246
247
#define mmtype long
248
#define mmcount (16 / SIZEOF_LONG)
249
#define A ((mmtype*)a)
250
#define B ((mmtype*)b)
251
#define C ((mmtype*)c)
252
#define D ((mmtype*)d)
253
254
#define mmstep (sizeof(mmtype) * mmcount)
255
#define mmprepare(base, size) do {\
256
 if (((VALUE)(base) % sizeof(mmtype)) == 0 && ((size) % sizeof(mmtype)) == 0) \
257
   if ((size) >= mmstep) mmkind = 1;\
258
   else              mmkind = 0;\
259
 else                mmkind = -1;\
260
 high = ((size) / mmstep) * mmstep;\
261
 low  = ((size) % mmstep);\
262
} while (0)\
263
264
#define mmarg mmkind, size, high, low
265
#define mmargdecl int mmkind, size_t size, size_t high, size_t low
266
267
static void mmswap_(register char *a, register char *b, mmargdecl)
268
{
269
 if (a == b) return;
270
 if (mmkind >= 0) {
271
   register mmtype s;
272
#if mmcount > 1
273
   if (mmkind > 0) {
274
     register char *t = a + high;
275
     do {
276
       s = A[0]; A[0] = B[0]; B[0] = s;
277
       s = A[1]; A[1] = B[1]; B[1] = s;
278
#if mmcount > 2
279
       s = A[2]; A[2] = B[2]; B[2] = s;
280
#if mmcount > 3
281
       s = A[3]; A[3] = B[3]; B[3] = s;
282
#endif
283
#endif
284
       a += mmstep; b += mmstep;
285
     } while (a < t);
286
   }
287
#endif
288
   if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = s;
289
#if mmcount > 2
290
     if (low >= 2 * sizeof(mmtype)) { s = A[1]; A[1] = B[1]; B[1] = s;
291
#if mmcount > 3
292
       if (low >= 3 * sizeof(mmtype)) {s = A[2]; A[2] = B[2]; B[2] = s;}
293
#endif
294
     }
295
#endif
296
   }
297
 }
298
 else {
299
   register char *t = a + size, s;
300
   do {s = *a; *a++ = *b; *b++ = s;} while (a < t);
301
 }
302
}
303
#define mmswap(a,b) mmswap_((a),(b),mmarg)
304
305
/* a, b, c = b, c, a */
306
static void mmrot3_(register char *a, register char *b, register char *c, mmargdecl)
307
{
308
 if (mmkind >= 0) {
309
   register mmtype s;
310
#if mmcount > 1
311
   if (mmkind > 0) {
312
     register char *t = a + high;
313
     do {
314
       s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s;
315
       s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s;
316
#if mmcount > 2
317
       s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;
318
#if mmcount > 3
319
       s = A[3]; A[3] = B[3]; B[3] = C[3]; C[3] = s;
320
#endif
321
#endif
322
       a += mmstep; b += mmstep; c += mmstep;
323
     } while (a < t);
324
   }
325
#endif
326
   if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s;
327
#if mmcount > 2
328
     if (low >= 2 * sizeof(mmtype)) { s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s;
329
#if mmcount > 3
330
       if (low == 3 * sizeof(mmtype)) {s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;}
331
#endif
332
     }
333
#endif
334
   }
335
 }
336
 else {
337
   register char *t = a + size, s;
338
   do {s = *a; *a++ = *b; *b++ = *c; *c++ = s;} while (a < t);
339
 }
340
}
341
#define mmrot3(a,b,c) mmrot3_((a),(b),(c),mmarg)
342
343
/* qs6.c */
344
/*****************************************************/
345
/*                                                   */
346
/*          qs6   (Quick sort function)              */
347
/*                                                   */
348
/* by  Tomoyuki Kawamura              1995.4.21      */
349
/* kawamura@tokuyama.ac.jp                           */
350
/*****************************************************/
351
352
typedef struct { char *LL, *RR; } stack_node; /* Stack structure for L,l,R,r */
353
#define PUSH(ll,rr) do { top->LL = (ll); top->RR = (rr); ++top; } while (0)  /* Push L,l,R,r */
354
#define POP(ll,rr)  do { --top; (ll) = top->LL; (rr) = top->RR; } while (0)      /* Pop L,l,R,r */
355
356
#define med3(a,b,c) ((*cmp)((a),(b),d)<0 ?                                   \
357
                       ((*cmp)((b),(c),d)<0 ? (b) : ((*cmp)((a),(c),d)<0 ? (c) : (a))) : \
358
                       ((*cmp)((b),(c),d)>0 ? (b) : ((*cmp)((a),(c),d)<0 ? (a) : (c))))
359
360
void
361
ruby_qsort(void* base, const size_t nel, const size_t size, cmpfunc_t *cmp, void *d)
362
{
363
  register char *l, *r, *m;           /* l,r:left,right group   m:median point */
364
  register int t, eq_l, eq_r;         /* eq_l: all items in left group are equal to S */
365
  char *L = base;                     /* left end of current region */
366
  char *R = (char*)base + size*(nel-1); /* right end of current region */
367
  size_t chklim = 63;                   /* threshold of ordering element check */
368
  enum {size_bits = sizeof(size) * CHAR_BIT};
369
  stack_node stack[size_bits];          /* enough for size_t size */
370
  stack_node *top = stack;
371
  int mmkind;
372
  size_t high, low, n;
373
374
  if (nel <= 1) return;        /* need not to sort */
375
  mmprepare(base, size);
376
  goto start;
377
378
  nxt:
379
  if (stack == top) return;    /* return if stack is empty */
380
  POP(L,R);
381
382
  for (;;) {
383
    start:
384
    if (L + size == R) {       /* 2 elements */
385
      if ((*cmp)(L,R,d) > 0) mmswap(L,R);
386
      goto nxt;
387
    }
388
389
    l = L; r = R;
390
    n = (r - l + size) / size;  /* number of elements */
391
    m = l + size * (n >> 1);    /* calculate median value */
392
393
    if (n >= 60) {
394
      register char *m1;
395
      register char *m3;
396
      if (n >= 200) {
397
        n = size*(n>>3); /* number of bytes in splitting 8 */
398
        {
399
          register char *p1 = l  + n;
400
          register char *p2 = p1 + n;
401
          register char *p3 = p2 + n;
402
          m1 = med3(p1, p2, p3);
403
          p1 = m  + n;
404
          p2 = p1 + n;
405
          p3 = p2 + n;
406
          m3 = med3(p1, p2, p3);
407
        }
408
      }
409
      else {
410
        n = size*(n>>2); /* number of bytes in splitting 4 */
411
        m1 = l + n;
412
        m3 = m + n;
413
      }
414
      m = med3(m1, m, m3);
415
    }
416
417
    if ((t = (*cmp)(l,m,d)) < 0) {                           /*3-5-?*/
418
      if ((t = (*cmp)(m,r,d)) < 0) {                         /*3-5-7*/
419
        if (chklim && nel >= chklim) {   /* check if already ascending order */
420
          char *p;
421
          chklim = 0;
422
          for (p=l; p<r; p+=size) if ((*cmp)(p,p+size,d) > 0) goto fail;
423
          goto nxt;
424
        }
425
        fail: goto loopA;                                    /*3-5-7*/
426
      }
427
      if (t > 0) {
428
        if ((*cmp)(l,r,d) <= 0) {mmswap(m,r); goto loopA;}     /*3-5-4*/
429
        mmrot3(r,m,l); goto loopA;                           /*3-5-2*/
430
      }
431
      goto loopB;                                            /*3-5-5*/
432
    }
433
434
    if (t > 0) {                                             /*7-5-?*/
435
      if ((t = (*cmp)(m,r,d)) > 0) {                         /*7-5-3*/
436
        if (chklim && nel >= chklim) {   /* check if already ascending order */
437
          char *p;
438
          chklim = 0;
439
          for (p=l; p<r; p+=size) if ((*cmp)(p,p+size,d) < 0) goto fail2;
440
          while (l<r) {mmswap(l,r); l+=size; r-=size;}  /* reverse region */
441
          goto nxt;
442
        }
443
        fail2: mmswap(l,r); goto loopA;                      /*7-5-3*/
444
      }
445
      if (t < 0) {
446
        if ((*cmp)(l,r,d) <= 0) {mmswap(l,m); goto loopB;}   /*7-5-8*/
447
        mmrot3(l,m,r); goto loopA;                           /*7-5-6*/
448
      }
449
      mmswap(l,r); goto loopA;                               /*7-5-5*/
450
    }
451
452
    if ((t = (*cmp)(m,r,d)) < 0)  {goto loopA;}              /*5-5-7*/
453
    if (t > 0) {mmswap(l,r); goto loopB;}                    /*5-5-3*/
454
455
    /* determining splitting type in case 5-5-5 */           /*5-5-5*/
456
    for (;;) {
457
      if ((l += size) == r)      goto nxt;                   /*5-5-5*/
458
      if (l == m) continue;
459
      if ((t = (*cmp)(l,m,d)) > 0) {mmswap(l,r); l = L; goto loopA;}/*575-5*/
460
      if (t < 0)                 {mmswap(L,l); l = L; goto loopB;}  /*535-5*/
461
    }
462
463
    loopA: eq_l = 1; eq_r = 1;  /* splitting type A */ /* left <= median < right */
464
    for (;;) {
465
      for (;;) {
466
        if ((l += size) == r)
467
          {l -= size; if (l != m) mmswap(m,l); l -= size; goto fin;}
468
        if (l == m) continue;
469
        if ((t = (*cmp)(l,m,d)) > 0) {eq_r = 0; break;}
470
        if (t < 0) eq_l = 0;
471
      }
472
      for (;;) {
473
        if (l == (r -= size))
474
          {l -= size; if (l != m) mmswap(m,l); l -= size; goto fin;}
475
        if (r == m) {m = l; break;}
476
        if ((t = (*cmp)(r,m,d)) < 0) {eq_l = 0; break;}
477
        if (t == 0) break;
478
      }
479
      mmswap(l,r);    /* swap left and right */
480
    }
481
482
    loopB: eq_l = 1; eq_r = 1;  /* splitting type B */ /* left < median <= right */
483
    for (;;) {
484
      for (;;) {
485
        if (l == (r -= size))
486
          {r += size; if (r != m) mmswap(r,m); r += size; goto fin;}
487
        if (r == m) continue;
488
        if ((t = (*cmp)(r,m,d)) < 0) {eq_l = 0; break;}
489
        if (t > 0) eq_r = 0;
490
      }
491
      for (;;) {
492
        if ((l += size) == r)
493
          {r += size; if (r != m) mmswap(r,m); r += size; goto fin;}
494
        if (l == m) {m = r; break;}
495
        if ((t = (*cmp)(l,m,d)) > 0) {eq_r = 0; break;}
496
        if (t == 0) break;
497
      }
498
      mmswap(l,r);    /* swap left and right */
499
    }
500
501
    fin:
502
    if (eq_l == 0)                         /* need to sort left side */
503
      if (eq_r == 0)                       /* need to sort right side */
504
        if (l-L < R-r) {PUSH(r,R); R = l;} /* sort left side first */
505
        else           {PUSH(L,l); L = r;} /* sort right side first */
506
      else R = l;                          /* need to sort left side only */
507
    else if (eq_r == 0) L = r;             /* need to sort right side only */
508
    else goto nxt;                         /* need not to sort both sides */
509
  }
510
}
511
#endif
512
#endif /* !HAVE_GNU_QSORT_R */
513
514
char *
515
ruby_strdup(const char *str)
516
304k
{
517
304k
    char *tmp;
518
304k
    size_t len = strlen(str) + 1;
519
520
304k
    tmp = xmalloc(len);
521
304k
    memcpy(tmp, str, len);
522
523
304k
    return tmp;
524
304k
}
525
526
#if defined HAVE_GETCWD
527
# if defined NO_GETCWD_MALLOC
528
529
char *
530
ruby_getcwd(void)
531
{
532
    int size = 200;
533
    char *buf = xmalloc(size);
534
535
    while (!getcwd(buf, size)) {
536
        int e = errno;
537
        if (e != ERANGE) {
538
            xfree(buf);
539
            rb_syserr_fail(e, "getcwd");
540
        }
541
        size *= 2;
542
        xfree(buf);
543
        buf = xmalloc(size);
544
    }
545
    return buf;
546
}
547
548
# else
549
550
static VALUE
551
getcwd_strdup(VALUE arg)
552
0
{
553
0
    return (VALUE)ruby_strdup((const char *)arg);
554
0
}
555
556
static VALUE
557
getcwd_free(VALUE arg)
558
0
{
559
0
    free((void *)arg);
560
0
    return Qnil;
561
0
}
562
563
char *
564
ruby_getcwd(void)
565
0
{
566
0
    char *cwd = getcwd(NULL, 0);
567
0
    if (!cwd) rb_sys_fail("getcwd");
568
0
    return (char *)rb_ensure(getcwd_strdup, (VALUE)cwd, getcwd_free, (VALUE)cwd);
569
0
}
570
571
# endif
572
#else
573
574
# ifndef PATH_MAX
575
#  define PATH_MAX 8192
576
# endif
577
578
char *
579
ruby_getcwd(void)
580
{
581
    char *buf = xmalloc(PATH_MAX+1);
582
583
    if (!getwd(buf)) {
584
        int e = errno;
585
        xfree(buf);
586
        rb_syserr_fail(e, "getwd");
587
    }
588
    return buf;
589
}
590
591
#endif
592
593
void
594
ruby_each_words(const char *str, void (*func)(const char*, int, void*), void *arg)
595
0
{
596
0
    const char *end;
597
0
    int len;
598
599
0
    if (!str) return;
600
0
    for (; *str; str = end) {
601
0
        while (ISSPACE(*str) || *str == ',') str++;
602
0
        if (!*str) break;
603
0
        end = str;
604
0
        while (*end && !ISSPACE(*end) && *end != ',') end++;
605
0
        len = (int)(end - str); /* assume no string exceeds INT_MAX */
606
0
        (*func)(str, len, arg);
607
0
    }
608
0
}
609
610
#undef strtod
611
#define strtod ruby_strtod
612
#undef dtoa
613
#define dtoa ruby_dtoa
614
#undef hdtoa
615
#define hdtoa ruby_hdtoa
616
#include "missing/dtoa.c"