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