Ruby 3.5.0dev (2025-07-12 revision 3ec46aad37bb5f42656d81210838ac4c5c4013a0)
util.c (3ec46aad37bb5f42656d81210838ac4c5c4013a0)
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
39const char ruby_hexdigits[] = "0123456789abcdef0123456789ABCDEF";
40#define hexdigit ruby_hexdigits
41
42unsigned long
43ruby_scan_oct(const char *start, size_t len, size_t *retlen)
44{
45 int overflow;
46 unsigned long val = ruby_scan_digits(start, (ssize_t)len, 8, retlen, &overflow);
47 (void)overflow;
48 return val;
49}
50
51unsigned long
52ruby_scan_hex(const char *start, size_t len, size_t *retlen)
53{
54 int overflow;
55 unsigned long val = ruby_scan_digits(start, (ssize_t)len, 16, retlen, &overflow);
56 (void)overflow;
57 return val;
58}
59
60const 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
80NO_SANITIZE("unsigned-integer-overflow", extern unsigned long ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow));
81unsigned long
82ruby_scan_digits(const char *str, ssize_t len, int base, size_t *retlen, int *overflow)
83{
84 RBIMPL_ASSERT_OR_ASSUME(base >= 2);
85 RBIMPL_ASSERT_OR_ASSUME(base <= 36);
86
87 const char *start = str;
88 unsigned long ret = 0, x;
89 unsigned long mul_overflow = (~(unsigned long)0) / base;
90
91 *overflow = 0;
92
93 if (!len) {
94 *retlen = 0;
95 return 0;
96 }
97
98 do {
99 int d = ruby_digit36_to_number_table[(unsigned char)*str++];
100 if (d == -1 || base <= d) {
101 --str;
102 break;
103 }
104 if (mul_overflow < ret)
105 *overflow = 1;
106 ret *= base;
107 x = ret;
108 ret += d;
109 if (ret < x)
110 *overflow = 1;
111 } while (len < 0 || --len);
112 *retlen = str - start;
113 return ret;
114}
115
116unsigned long
117ruby_strtoul(const char *str, char **endptr, int base)
118{
119 int c, b, overflow;
120 int sign = 0;
121 size_t len;
122 unsigned long ret;
123 const char *subject_found = str;
124
125 if (base < 0) {
126 errno = EINVAL;
127 return 0;
128 }
129
130 if (base == 1 || 36 < base) {
131 errno = EINVAL;
132 return 0;
133 }
134
135 while ((c = *str) && ISSPACE(c))
136 str++;
137
138 if (c == '+') {
139 sign = 1;
140 str++;
141 }
142 else if (c == '-') {
143 sign = -1;
144 str++;
145 }
146
147 if (str[0] == '0') {
148 subject_found = str+1;
149 if (base == 0 || base == 16) {
150 if (str[1] == 'x' || str[1] == 'X') {
151 b = 16;
152 str += 2;
153 }
154 else {
155 b = base == 0 ? 8 : 16;
156 str++;
157 }
158 }
159 else {
160 b = base;
161 str++;
162 }
163 }
164 else {
165 b = base == 0 ? 10 : base;
166 }
167
168 ret = ruby_scan_digits(str, -1, b, &len, &overflow);
169
170 if (0 < len)
171 subject_found = str+len;
172
173 if (endptr)
174 *endptr = (char*)subject_found;
175
176 if (overflow) {
177 errno = ERANGE;
178 return ULONG_MAX;
179 }
180
181 if (sign < 0) {
182 ret = (unsigned long)(-(long)ret);
183 return ret;
184 }
185 else {
186 return ret;
187 }
188}
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
197typedef 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
209struct bsd_qsort_r_args {
210 cmpfunc_t *cmp;
211 void *arg;
212};
213
214static int
215cmp_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
221void
222ruby_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. */
232void
233ruby_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
267static 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 */
306static 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
352typedef 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
360void
361ruby_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
514char *
515ruby_strdup(const char *str)
516{
517 char *tmp;
518 size_t len = strlen(str) + 1;
519
520 tmp = xmalloc(len);
521 memcpy(tmp, str, len);
522
523 return tmp;
524}
525
526#if defined HAVE_GETCWD
527# if defined NO_GETCWD_MALLOC
528
529char *
530ruby_getcwd(void)
531{
532 VALUE guard = rb_imemo_tmpbuf_auto_free_pointer();
533 int size = 200;
534 char *buf = xmalloc(size);
535
536 while (!getcwd(buf, size)) {
537 int e = errno;
538 if (e != ERANGE) {
539 rb_free_tmp_buffer(&guard);
540 rb_syserr_fail(e, "getcwd");
541 }
542 size *= 2;
543 rb_imemo_tmpbuf_set_ptr(guard, buf);
544 buf = xrealloc(buf, size);
545 }
546 rb_imemo_tmpbuf_set_ptr(guard, NULL);
547 return buf;
548}
549
550# else
551
552static const rb_data_type_t getcwd_buffer_guard_type = {
553 .wrap_struct_name = "ruby_getcwd_guard",
554 .function = {
555 .dfree = free // not xfree.
556 },
557 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
558};
559
560char *
561ruby_getcwd(void)
562{
563 VALUE guard = TypedData_Wrap_Struct((VALUE)0, &getcwd_buffer_guard_type, NULL);
564 char *buf, *cwd = getcwd(NULL, 0);
565 RTYPEDDATA_DATA(guard) = cwd;
566 if (!cwd) rb_sys_fail("getcwd");
567 buf = ruby_strdup(cwd); /* allocate by xmalloc */
568 free(cwd);
569 RTYPEDDATA_DATA(RB_GC_GUARD(guard)) = NULL;
570 return buf;
571}
572
573# endif
574#else
575
576# ifndef PATH_MAX
577# define PATH_MAX 8192
578# endif
579
580char *
582{
583 char *buf = xmalloc(PATH_MAX+1);
584
585 if (!getwd(buf)) {
586 int e = errno;
587 xfree(buf);
588 rb_syserr_fail(e, "getwd");
589 }
590 return buf;
591}
592
593#endif
594
595void
596ruby_each_words(const char *str, void (*func)(const char*, int, void*), void *arg)
597{
598 const char *end;
599 int len;
600
601 if (!str) return;
602 for (; *str; str = end) {
603 while (ISSPACE(*str) || *str == ',') str++;
604 if (!*str) break;
605 end = str;
606 while (*end && !ISSPACE(*end) && *end != ',') end++;
607 len = (int)(end - str); /* assume no string exceeds INT_MAX */
608 (*func)(str, len, arg);
609 }
610}
611
612#undef strtod
613#define strtod ruby_strtod
614#undef dtoa
615#define dtoa ruby_dtoa
616#undef hdtoa
617#define hdtoa ruby_hdtoa
618#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:117
#define ISSPACE
Old name of rb_isspace.
Definition ctype.h:88
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define xrealloc
Old name of ruby_xrealloc.
Definition xmalloc.h:56
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3905
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:60
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:515
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:581
const char ruby_hexdigits[]
Characters that Ruby accepts as hexadecimal digits.
Definition util.c:39
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
Definition rtypeddata.h:102
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:450
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:203
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:210
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40