Ruby 4.1.0dev (2026-06-15 revision 4def576d21a768c85db6d79d99ae339314e3de5c)
sprintf.c (4def576d21a768c85db6d79d99ae339314e3de5c)
1/**********************************************************************
2
3 sprintf.c -
4
5 $Author$
6 created at: Fri Oct 15 10:39:26 JST 1993
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10 Copyright (C) 2000 Information-technology Promotion Agency, Japan
11
12**********************************************************************/
13
14#include "ruby/internal/config.h"
15
16#include <math.h>
17#include <stdarg.h>
18
19#ifdef HAVE_IEEEFP_H
20# include <ieeefp.h>
21#endif
22
23#include "id.h"
24#include "internal.h"
25#include "internal/error.h"
26#include "internal/hash.h"
27#include "internal/numeric.h"
28#include "internal/object.h"
29#include "internal/sanitizers.h"
30#include "internal/symbol.h"
31#include "ruby/encoding.h"
32#include "ruby/re.h"
33#include "ruby/util.h"
34
35#define BIT_DIGITS(N) (((N)*146)/485 + 1) /* log2(10) =~ 146/485 */
36
37static char *fmt_setup(char*,size_t,int,int,int,int);
38static char *ruby_ultoa(unsigned long val, char *endp, int base, int octzero);
39
40static char
41sign_bits(int base, const char *p)
42{
43 char c = '.';
44
45 switch (base) {
46 case 16:
47 if (*p == 'X') c = 'F';
48 else c = 'f';
49 break;
50 case 8:
51 c = '7'; break;
52 case 2:
53 c = '1'; break;
54 }
55 return c;
56}
57
58#define FNONE 0
59#define FSHARP 1
60#define FMINUS 2
61#define FPLUS 4
62#define FZERO 8
63#define FSPACE 16
64#define FWIDTH 32
65#define FPREC 64
66#define FPREC0 128
67
68static long
69expand_result(VALUE result, long bsiz, long blen, long l)
70{
71 int cr = ENC_CODERANGE(result);
72 RUBY_ASSERT(bsiz >= blen);
73 while (l > bsiz - blen) {
74 bsiz *= 2;
75 if (bsiz < 0) rb_raise(rb_eArgError, "too big specifier");
76 }
77 rb_str_resize(result, bsiz);
78 ENC_CODERANGE_SET(result, cr);
79 return bsiz;
80}
81
82#define CHECK(l) do {\
83 bsiz = expand_result(result, bsiz, blen, l);\
84 buf = RSTRING_PTR(result);\
85} while (0)
86
87#define CHECK_WIDTH(l, w) do { \
88 if ((l) > INT_MAX - (w)) rb_raise(rb_eArgError, "width too big");\
89 CHECK((l)+(w));\
90} while (0)
91
92#define PUSH(s, l) do { \
93 CHECK(l);\
94 PUSH_(s, l);\
95} while (0)
96
97#define PUSH_(s, l) do { \
98 memcpy(&buf[blen], (s), (l));\
99 blen += (l);\
100} while (0)
101
102#define FILL(c, l) do { \
103 if ((l) <= 0) break;\
104 CHECK(l);\
105 FILL_(c, l);\
106} while (0)
107
108#define FILL_(c, l) do { \
109 memset(&buf[blen], (c), (l));\
110 blen += (l);\
111} while (0)
112
113#define GETARG() (!UNDEF_P(nextvalue) ? nextvalue : \
114 GETNEXTARG())
115
116#define GETNEXTARG() ( \
117 check_next_arg(posarg, nextarg), \
118 (posarg = nextarg++, GETNTHARG(posarg)))
119
120#define GETPOSARG(n) ( \
121 check_pos_arg(posarg, (n)), \
122 (posarg = -1, GETNTHARG(n)))
123
124#define GETNTHARG(nth) \
125 (((nth) >= argc) ? (rb_raise(rb_eArgError, "too few arguments"), 0) : argv[(nth)])
126
127#define CHECKNAMEARG(name, len, enc) ( \
128 check_name_arg(posarg, name, len, enc), \
129 posarg = -2)
130
131#define GETNUM(n, val) \
132 (!(p = get_num(p, end, enc, &(n))) ? \
133 rb_raise(rb_eArgError, #val " too big") : (void)0)
134
135#define GETASTER(val) do { \
136 t = p++; \
137 n = 0; \
138 GETNUM(n, val); \
139 if (*p == '$') { \
140 tmp = GETPOSARG(n); \
141 } \
142 else { \
143 tmp = GETNEXTARG(); \
144 p = t; \
145 } \
146 (val) = NUM2INT(tmp); \
147} while (0)
148
149static const char *
150get_num(const char *p, const char *end, rb_encoding *enc, int *valp)
151{
152 int next_n = *valp;
153 for (; p < end && rb_enc_isdigit(*p, enc); p++) {
154 if (MUL_OVERFLOW_INT_P(10, next_n))
155 return NULL;
156 next_n *= 10;
157 if (INT_MAX - (*p - '0') < next_n)
158 return NULL;
159 next_n += *p - '0';
160 }
161 if (p >= end) {
162 rb_raise(rb_eArgError, "malformed format string - %%*[0-9]");
163 }
164 *valp = next_n;
165 return p;
166}
167
168static void
169check_next_arg(int posarg, int nextarg)
170{
171 switch (posarg) {
172 case -1:
173 rb_raise(rb_eArgError, "unnumbered(%d) mixed with numbered", nextarg);
174 case -2:
175 rb_raise(rb_eArgError, "unnumbered(%d) mixed with named", nextarg);
176 }
177}
178
179static void
180check_pos_arg(int posarg, int n)
181{
182 if (posarg > 0) {
183 rb_raise(rb_eArgError, "numbered(%d) after unnumbered(%d)", n, posarg);
184 }
185 if (posarg == -2) {
186 rb_raise(rb_eArgError, "numbered(%d) after named", n);
187 }
188 if (n < 1) {
189 rb_raise(rb_eArgError, "invalid index - %d$", n);
190 }
191}
192
193static void
194check_name_arg(int posarg, const char *name, int len, rb_encoding *enc)
195{
196 if (posarg > 0) {
197 rb_enc_raise(enc, rb_eArgError, "named%.*s after unnumbered(%d)", len, name, posarg);
198 }
199 if (posarg == -1) {
200 rb_enc_raise(enc, rb_eArgError, "named%.*s after numbered", len, name);
201 }
202}
203
204static VALUE
205get_hash(volatile VALUE *hash, int argc, const VALUE *argv)
206{
207 VALUE tmp;
208
209 if (!UNDEF_P(*hash)) return *hash;
210 if (argc != 2) {
211 rb_raise(rb_eArgError, "one hash required");
212 }
213 tmp = rb_check_hash_type(argv[1]);
214 if (NIL_P(tmp)) {
215 rb_raise(rb_eArgError, "one hash required");
216 }
217 return (*hash = tmp);
218}
219
220VALUE
221rb_f_sprintf(int argc, const VALUE *argv)
222{
223 return rb_str_format(argc - 1, argv + 1, GETNTHARG(0));
224}
225
226VALUE
227rb_str_format(int argc, const VALUE *argv, VALUE fmt)
228{
229 enum {default_float_precision = 6};
230 rb_encoding *enc;
231 const char *p, *end;
232 char *buf;
233 long blen, bsiz;
234 VALUE result;
235
236 long scanned = 0;
238 int width, prec, flags = FNONE;
239 int nextarg = 1;
240 int posarg = 0;
241 VALUE nextvalue;
242 VALUE tmp;
243 VALUE orig;
244 VALUE str;
245 volatile VALUE hash = Qundef;
246
247#define CHECK_FOR_WIDTH(f) \
248 if ((f) & FWIDTH) { \
249 rb_raise(rb_eArgError, "width given twice"); \
250 } \
251 if ((f) & FPREC0) { \
252 rb_raise(rb_eArgError, "width after precision"); \
253 }
254#define CHECK_FOR_FLAGS(f) \
255 if ((f) & FWIDTH) { \
256 rb_raise(rb_eArgError, "flag after width"); \
257 } \
258 if ((f) & FPREC0) { \
259 rb_raise(rb_eArgError, "flag after precision"); \
260 }
261
262#define update_coderange(partial) do { \
263 if (coderange != ENC_CODERANGE_BROKEN && scanned < blen) { \
264 int cr = coderange; \
265 scanned += rb_str_coderange_scan_restartable(buf+scanned, buf+blen, enc, &cr); \
266 ENC_CODERANGE_SET(result, \
267 (partial && cr == ENC_CODERANGE_UNKNOWN ? \
268 ENC_CODERANGE_BROKEN : (coderange = cr))); \
269 } \
270 } while (0)
271 ++argc;
272 --argv;
273 StringValue(fmt);
274 enc = rb_enc_get(fmt);
276 orig = fmt;
277 fmt = rb_str_tmp_frozen_acquire(fmt);
278 p = RSTRING_PTR(fmt);
279 end = p + RSTRING_LEN(fmt);
280 blen = 0;
281 bsiz = 120;
282 result = rb_str_buf_new(bsiz);
283 rb_enc_associate(result, enc);
284 buf = RSTRING_PTR(result);
285 memset(buf, 0, bsiz);
286 ENC_CODERANGE_SET(result, coderange);
287
288 for (; p < end; p++) {
289 const char *t;
290 int n;
291 VALUE sym = Qnil;
292
293 for (t = p; t < end && *t != '%'; t++) ;
294 if (t + 1 == end) {
295 rb_raise(rb_eArgError, "incomplete format specifier; use %%%% (double %%) instead");
296 }
297 PUSH(p, t - p);
298 update_coderange(FALSE);
299 if (t >= end) {
300 /* end of fmt string */
301 goto sprint_exit;
302 }
303 p = t + 1; /* skip `%' */
304
305 width = prec = -1;
306 nextvalue = Qundef;
307 retry:
308 switch (*p) {
309 default:
310 if (rb_enc_isprint(*p, enc))
311 rb_raise(rb_eArgError, "malformed format string - %%%c", *p);
312 else
313 rb_raise(rb_eArgError, "malformed format string");
314 break;
315
316 case ' ':
317 CHECK_FOR_FLAGS(flags);
318 flags |= FSPACE;
319 p++;
320 goto retry;
321
322 case '#':
323 CHECK_FOR_FLAGS(flags);
324 flags |= FSHARP;
325 p++;
326 goto retry;
327
328 case '+':
329 CHECK_FOR_FLAGS(flags);
330 flags |= FPLUS;
331 p++;
332 goto retry;
333
334 case '-':
335 CHECK_FOR_FLAGS(flags);
336 flags |= FMINUS;
337 p++;
338 goto retry;
339
340 case '0':
341 CHECK_FOR_FLAGS(flags);
342 flags |= FZERO;
343 p++;
344 goto retry;
345
346 case '1': case '2': case '3': case '4':
347 case '5': case '6': case '7': case '8': case '9':
348 n = 0;
349 GETNUM(n, width);
350 if (*p == '$') {
351 if (!UNDEF_P(nextvalue)) {
352 rb_raise(rb_eArgError, "value given twice - %d$", n);
353 }
354 nextvalue = GETPOSARG(n);
355 p++;
356 goto retry;
357 }
358 CHECK_FOR_WIDTH(flags);
359 width = n;
360 flags |= FWIDTH;
361 goto retry;
362
363 case '<':
364 case '{':
365 {
366 const char *start = p;
367 char term = (*p == '<') ? '>' : '}';
368 int len;
369
370 for (; p < end && *p != term; ) {
371 p += rb_enc_mbclen(p, end, enc);
372 }
373 if (p >= end) {
374 rb_raise(rb_eArgError, "malformed name - unmatched parenthesis");
375 }
376#if SIZEOF_INT < SIZEOF_SIZE_T
377 if ((size_t)(p - start) >= INT_MAX) {
378 const int message_limit = 20;
379 len = (int)(rb_enc_right_char_head(start, start + message_limit, p, enc) - start);
380 rb_enc_raise(enc, rb_eArgError,
381 "too long name (%"PRIuSIZE" bytes) - %.*s...%c",
382 (size_t)(p - start - 2), len, start, term);
383 }
384#endif
385 len = (int)(p - start + 1); /* including parenthesis */
386 if (sym != Qnil) {
387 rb_enc_raise(enc, rb_eArgError, "named%.*s after <%"PRIsVALUE">",
388 len, start, rb_sym2str(sym));
389 }
390 CHECKNAMEARG(start, len, enc);
391 get_hash(&hash, argc, argv);
392 sym = rb_check_symbol_cstr(start + 1,
393 len - 2 /* without parenthesis */,
394 enc);
395 if (!NIL_P(sym)) nextvalue = rb_hash_lookup2(hash, sym, Qundef);
396 if (UNDEF_P(nextvalue)) {
397 if (NIL_P(sym)) {
398 sym = rb_sym_intern(start + 1,
399 len - 2 /* without parenthesis */,
400 enc);
401 }
402 nextvalue = rb_hash_default_value(hash, sym);
403 if (NIL_P(nextvalue)) {
404 rb_key_err_raise(rb_enc_sprintf(enc, "key%.*s not found", len, start), hash, sym);
405 }
406 }
407 if (term == '}') goto format_s;
408 p++;
409 goto retry;
410 }
411
412 case '*':
413 CHECK_FOR_WIDTH(flags);
414 flags |= FWIDTH;
415 GETASTER(width);
416 if (width < 0) {
417 flags |= FMINUS;
418 width = -width;
419 if (width < 0) rb_raise(rb_eArgError, "width too big");
420 }
421 p++;
422 goto retry;
423
424 case '.':
425 if (flags & FPREC0) {
426 rb_raise(rb_eArgError, "precision given twice");
427 }
428 flags |= FPREC|FPREC0;
429
430 prec = 0;
431 p++;
432 if (*p == '*') {
433 GETASTER(prec);
434 if (prec < 0) { /* ignore negative precision */
435 flags &= ~FPREC;
436 }
437 p++;
438 goto retry;
439 }
440
441 GETNUM(prec, precision);
442 goto retry;
443
444 case '%':
445 if (flags != FNONE) {
446 rb_raise(rb_eArgError, "invalid format character - %%");
447 }
448 PUSH("%", 1);
449 break;
450
451 case 'c':
452 {
453 VALUE val = GETARG();
454 VALUE tmp;
455 unsigned int c = 0;
456 int n, encidx = 0;
457
458 tmp = rb_check_string_type(val);
459 if (!NIL_P(tmp)) {
460 flags |= FPREC;
461 prec = 1;
462 str = tmp;
463 goto format_s1;
464 }
465 n = NUM2INT(val);
466 if (n >= 0) {
467 n = rb_enc_codelen((c = n), enc);
468 encidx = rb_ascii8bit_appendable_encoding_index(enc, c);
469 }
470 if (n <= 0) {
471 rb_raise(rb_eArgError, "invalid character");
472 }
473 if (encidx >= 0 && encidx != rb_enc_to_index(enc)) {
474 /* special case */
475 rb_enc_associate_index(result, encidx);
476 enc = rb_enc_from_index(encidx);
477 coderange = ENC_CODERANGE_VALID;
478 }
479 if (!(flags & FWIDTH)) {
480 CHECK(n);
481 rb_enc_mbcput(c, &buf[blen], enc);
482 blen += n;
483 }
484 else {
485 --width;
486 CHECK_WIDTH(n, (width > 0 ? width : 0));
487 if (!(flags & FMINUS) && (width > 0)) FILL_(' ', width);
488 rb_enc_mbcput(c, &buf[blen], enc);
489 blen += n;
490 if ((flags & FMINUS) && (width > 0)) FILL_(' ', width);
491 }
492 }
493 break;
494
495 case 's':
496 case 'p':
497 format_s:
498 {
499 VALUE arg = GETARG();
500 long len, slen;
501
502 if (*p == 'p') {
503 str = rb_inspect(arg);
504 }
505 else {
506 str = rb_obj_as_string(arg);
507 }
508 format_s1:
509 len = RSTRING_LEN(str);
510 rb_str_set_len(result, blen);
511 update_coderange(TRUE);
512 enc = rb_enc_check(result, str);
513 if (flags&(FPREC|FWIDTH)) {
514 slen = rb_enc_strlen(RSTRING_PTR(str),RSTRING_END(str),enc);
515 if (slen < 0) {
516 rb_raise(rb_eArgError, "invalid mbstring sequence");
517 }
518 if ((flags&FPREC) && (prec < slen)) {
519 char *p = rb_enc_nth(RSTRING_PTR(str), RSTRING_END(str),
520 prec, enc);
521 slen = prec;
522 len = p - RSTRING_PTR(str);
523 }
524 /* need to adjust multi-byte string pos */
525 if ((flags&FWIDTH) && (width > slen)) {
526 width -= (int)slen;
527 CHECK_WIDTH(len, width);
528 if (!(flags&FMINUS)) {
529 FILL_(' ', width);
530 width = 0;
531 }
532 memcpy(&buf[blen], RSTRING_PTR(str), len);
533 RB_GC_GUARD(str);
534 blen += len;
535 if (flags&FMINUS) {
536 FILL_(' ', width);
537 }
538 rb_enc_associate(result, enc);
539 break;
540 }
541 }
542 PUSH(RSTRING_PTR(str), len);
543 RB_GC_GUARD(str);
544 rb_enc_associate(result, enc);
545 }
546 break;
547
548 case 'd':
549 case 'i':
550 case 'o':
551 case 'x':
552 case 'X':
553 case 'b':
554 case 'B':
555 case 'u':
556 {
557 volatile VALUE val = GETARG();
558 int valsign;
559 char nbuf[BIT_DIGITS(SIZEOF_LONG*CHAR_BIT)+2], *s;
560 const char *prefix = 0;
561 int sign = 0, dots = 0;
562 char sc = 0;
563 long v = 0;
564 int base, bignum = 0;
565 int len;
566
567 switch (*p) {
568 case 'd':
569 case 'i':
570 case 'u':
571 sign = 1; break;
572 case 'o':
573 case 'x':
574 case 'X':
575 case 'b':
576 case 'B':
577 if (flags&(FPLUS|FSPACE)) sign = 1;
578 break;
579 }
580 if (flags & FSHARP) {
581 switch (*p) {
582 case 'o':
583 prefix = "0"; break;
584 case 'x':
585 prefix = "0x"; break;
586 case 'X':
587 prefix = "0X"; break;
588 case 'b':
589 prefix = "0b"; break;
590 case 'B':
591 prefix = "0B"; break;
592 }
593 }
594
595 bin_retry:
596 switch (TYPE(val)) {
597 case T_FLOAT:
598 if (FIXABLE(RFLOAT_VALUE(val))) {
599 val = LONG2FIX((long)RFLOAT_VALUE(val));
600 goto bin_retry;
601 }
602 val = rb_dbl2big(RFLOAT_VALUE(val));
603 if (FIXNUM_P(val)) goto bin_retry;
604 bignum = 1;
605 break;
606 case T_STRING:
607 val = rb_str_to_inum(val, 0, TRUE);
608 goto bin_retry;
609 case T_BIGNUM:
610 bignum = 1;
611 break;
612 case T_FIXNUM:
613 v = FIX2LONG(val);
614 break;
615 default:
616 val = rb_Integer(val);
617 goto bin_retry;
618 }
619
620 switch (*p) {
621 case 'o':
622 base = 8; break;
623 case 'x':
624 case 'X':
625 base = 16; break;
626 case 'b':
627 case 'B':
628 base = 2; break;
629 case 'u':
630 case 'd':
631 case 'i':
632 default:
633 base = 10; break;
634 }
635
636 if (base != 10) {
637 int numbits = ffs(base)-1;
638 size_t abs_nlz_bits;
639 size_t numdigits = rb_absint_numwords(val, numbits, &abs_nlz_bits);
640 long i;
641 if (INT_MAX-1 < numdigits) /* INT_MAX is used because rb_long2int is used later. */
642 rb_raise(rb_eArgError, "size too big");
643 if (sign) {
644 if (numdigits == 0)
645 numdigits = 1;
646 tmp = rb_str_new(NULL, numdigits);
647 valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
648 1, CHAR_BIT-numbits, INTEGER_PACK_BIG_ENDIAN);
649 for (i = 0; i < RSTRING_LEN(tmp); i++)
650 RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
651 s = RSTRING_PTR(tmp);
652 if (valsign < 0) {
653 sc = '-';
654 width--;
655 }
656 else if (flags & FPLUS) {
657 sc = '+';
658 width--;
659 }
660 else if (flags & FSPACE) {
661 sc = ' ';
662 width--;
663 }
664 }
665 else {
666 /* Following conditional "numdigits++" guarantees the
667 * most significant digit as
668 * - '1'(bin), '7'(oct) or 'f'(hex) for negative numbers
669 * - '0' for zero
670 * - not '0' for positive numbers.
671 *
672 * It also guarantees the most significant two
673 * digits will not be '11'(bin), '77'(oct), 'ff'(hex)
674 * or '00'. */
675 if (numdigits == 0 ||
676 ((abs_nlz_bits != (size_t)(numbits-1) ||
677 !rb_absint_singlebit_p(val)) &&
678 (!bignum ? v < 0 : BIGNUM_NEGATIVE_P(val))))
679 numdigits++;
680 tmp = rb_str_new(NULL, numdigits);
681 valsign = rb_integer_pack(val, RSTRING_PTR(tmp), RSTRING_LEN(tmp),
682 1, CHAR_BIT-numbits, INTEGER_PACK_2COMP | INTEGER_PACK_BIG_ENDIAN);
683 for (i = 0; i < RSTRING_LEN(tmp); i++)
684 RSTRING_PTR(tmp)[i] = ruby_digitmap[((unsigned char *)RSTRING_PTR(tmp))[i]];
685 s = RSTRING_PTR(tmp);
686 dots = valsign < 0;
687 }
688 len = rb_long2int(RSTRING_END(tmp) - s);
689 }
690 else if (!bignum) {
691 valsign = 1;
692 if (v < 0) {
693 v = -v;
694 sc = '-';
695 width--;
696 valsign = -1;
697 }
698 else if (flags & FPLUS) {
699 sc = '+';
700 width--;
701 }
702 else if (flags & FSPACE) {
703 sc = ' ';
704 width--;
705 }
706 s = ruby_ultoa((unsigned long)v, nbuf + sizeof(nbuf), 10, 0);
707 len = (int)(nbuf + sizeof(nbuf) - s);
708 }
709 else {
710 tmp = rb_big2str(val, 10);
711 s = RSTRING_PTR(tmp);
712 valsign = 1;
713 if (s[0] == '-') {
714 s++;
715 sc = '-';
716 width--;
717 valsign = -1;
718 }
719 else if (flags & FPLUS) {
720 sc = '+';
721 width--;
722 }
723 else if (flags & FSPACE) {
724 sc = ' ';
725 width--;
726 }
727 len = rb_long2int(RSTRING_END(tmp) - s);
728 }
729
730 if (dots) {
731 prec -= 2;
732 width -= 2;
733 }
734
735 if (*p == 'X') {
736 char *pp = s;
737 int c;
738 while ((c = (int)(unsigned char)*pp) != 0) {
739 *pp = rb_enc_toupper(c, enc);
740 pp++;
741 }
742 }
743 if (prefix && !prefix[1]) { /* octal */
744 if (dots) {
745 prefix = 0;
746 }
747 else if (len == 1 && *s == '0') {
748 len = 0;
749 if (flags & FPREC) prec--;
750 }
751 else if ((flags & FPREC) && (prec > len)) {
752 prefix = 0;
753 }
754 }
755 else if (len == 1 && *s == '0') {
756 prefix = 0;
757 }
758 if (prefix) {
759 width -= (int)strlen(prefix);
760 }
761 if ((flags & (FZERO|FMINUS|FPREC)) == FZERO) {
762 prec = width;
763 width = 0;
764 }
765 else {
766 if (prec < len) {
767 if (!prefix && prec == 0 && len == 1 && *s == '0') len = 0;
768 prec = len;
769 }
770 width -= prec;
771 }
772 if (!(flags&FMINUS)) {
773 FILL(' ', width);
774 width = 0;
775 }
776 if (sc) PUSH(&sc, 1);
777 if (prefix) {
778 int plen = (int)strlen(prefix);
779 PUSH(prefix, plen);
780 }
781 if (dots) PUSH("..", 2);
782 if (prec > len) {
783 CHECK(prec - len);
784 if (!sign && valsign < 0) {
785 char c = sign_bits(base, p);
786 FILL_(c, prec - len);
787 }
788 else if ((flags & (FMINUS|FPREC)) != FMINUS) {
789 FILL_('0', prec - len);
790 }
791 }
792 PUSH(s, len);
793 RB_GC_GUARD(tmp);
794 FILL(' ', width);
795 }
796 break;
797
798 case 'f':
799 {
800 VALUE val = GETARG(), num, den;
801 int sign = (flags&FPLUS) ? 1 : 0, zero = 0;
802 long len, fill;
803 if (RB_INTEGER_TYPE_P(val)) {
804 den = INT2FIX(1);
805 num = val;
806 }
807 else if (RB_TYPE_P(val, T_RATIONAL)) {
808 den = rb_rational_den(val);
809 num = rb_rational_num(val);
810 }
811 else {
812 nextvalue = val;
813 goto float_value;
814 }
815 if (!(flags&FPREC)) prec = default_float_precision;
816 if (FIXNUM_P(num)) {
817 if ((SIGNED_VALUE)num < 0) {
818 long n = -FIX2LONG(num);
819 num = LONG2NUM(n);
820 sign = -1;
821 }
822 }
823 else if (BIGNUM_NEGATIVE_P(num)) {
824 sign = -1;
825 num = rb_big_uminus(num);
826 }
827 if (den != INT2FIX(1)) {
828 num = rb_int_mul(num, rb_int_positive_pow(10, prec));
829 num = rb_int_plus(num, rb_int_idiv(den, INT2FIX(2)));
830 num = rb_int_idiv(num, den);
831 }
832 else if (prec >= 0) {
833 zero = prec;
834 }
835 val = rb_int2str(num, 10);
836 len = RSTRING_LEN(val) + zero;
837 if (prec >= len) len = prec + 1; /* integer part 0 */
838 if (sign || (flags&FSPACE)) ++len;
839 if (prec > 0) ++len; /* period */
840 fill = width > len ? width - len : 0;
841 CHECK(fill + len); /* max(width, len) */
842 if (fill && !(flags&(FMINUS|FZERO))) {
843 FILL_(' ', fill);
844 }
845 if (sign || (flags&FSPACE)) {
846 buf[blen++] = sign > 0 ? '+' : sign < 0 ? '-' : ' ';
847 }
848 if (fill && (flags&(FMINUS|FZERO)) == FZERO) {
849 FILL_('0', fill);
850 }
851 len = RSTRING_LEN(val) + zero;
852 t = RSTRING_PTR(val);
853 if (len > prec) {
854 PUSH_(t, len - prec);
855 }
856 else {
857 buf[blen++] = '0';
858 }
859 if (prec > 0) {
860 buf[blen++] = '.';
861 }
862 if (zero) {
863 FILL_('0', zero);
864 }
865 else if (prec > len) {
866 FILL_('0', prec - len);
867 PUSH_(t, len);
868 }
869 else if (prec > 0) {
870 PUSH_(t + len - prec, prec);
871 }
872 if (fill && (flags&FMINUS)) {
873 FILL_(' ', fill);
874 }
875 RB_GC_GUARD(val);
876 break;
877 }
878 case 'g':
879 case 'G':
880 case 'e':
881 case 'E':
882 /* TODO: rational support */
883 case 'a':
884 case 'A':
885 float_value:
886 {
887 VALUE val = GETARG();
888 double fval;
889
890 fval = RFLOAT_VALUE(rb_Float(val));
891 if (!isfinite(fval)) {
892 const char *expr;
893 int need;
894 int elen;
895 char sign = '\0';
896
897 if (isnan(fval)) {
898 expr = "NaN";
899 }
900 else {
901 expr = "Inf";
902 }
903 need = (int)strlen(expr);
904 elen = need;
905 if (!isnan(fval) && fval < 0.0)
906 sign = '-';
907 else if (flags & (FPLUS|FSPACE))
908 sign = (flags & FPLUS) ? '+' : ' ';
909 if (sign)
910 ++need;
911 if ((flags & FWIDTH) && need < width)
912 need = width;
913
914 FILL(' ', need);
915 if (flags & FMINUS) {
916 if (sign)
917 buf[blen - need--] = sign;
918 memcpy(&buf[blen - need], expr, elen);
919 }
920 else {
921 if (sign)
922 buf[blen - elen - 1] = sign;
923 memcpy(&buf[blen - elen], expr, elen);
924 }
925 break;
926 }
927 else {
928 int cr = ENC_CODERANGE(result);
929 char fbuf[2*BIT_DIGITS(SIZEOF_INT*CHAR_BIT)+10];
930 char *fmt = fmt_setup(fbuf, sizeof(fbuf), *p, flags, width, prec);
931 rb_str_set_len(result, blen);
932 rb_str_catf(result, fmt, fval);
933 ENC_CODERANGE_SET(result, cr);
934 bsiz = rb_str_capacity(result);
935 RSTRING_GETMEM(result, buf, blen);
936 }
937 }
938 break;
939 }
940 flags = FNONE;
941 }
942
943 update_coderange(FALSE);
944 sprint_exit:
945 rb_str_tmp_frozen_release(orig, fmt);
946 /* XXX - We cannot validate the number of arguments if (digit)$ style used.
947 */
948 if (posarg >= 0 && nextarg < argc && !(argc == 2 && RB_TYPE_P(argv[1], T_HASH))) {
949 const char *mesg = "too many arguments for format string";
950 if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg);
951 if (RTEST(ruby_verbose)) rb_warn("%s", mesg);
952 }
953 rb_str_resize(result, blen);
954
955 return result;
956}
957
958static char *
959fmt_setup(char *buf, size_t size, int c, int flags, int width, int prec)
960{
961 buf += size;
962 *--buf = '\0';
963 *--buf = c;
964
965 if (flags & FPREC) {
966 buf = ruby_ultoa(prec, buf, 10, 0);
967 *--buf = '.';
968 }
969
970 if (flags & FWIDTH) {
971 buf = ruby_ultoa(width, buf, 10, 0);
972 }
973
974 if (flags & FSPACE) *--buf = ' ';
975 if (flags & FZERO) *--buf = '0';
976 if (flags & FMINUS) *--buf = '-';
977 if (flags & FPLUS) *--buf = '+';
978 if (flags & FSHARP) *--buf = '#';
979 *--buf = '%';
980 return buf;
981}
982
983#undef FILE
984#define FILE rb_printf_buffer
985#define __sbuf rb_printf_sbuf
986#define __sFILE rb_printf_sfile
987#undef feof
988#undef ferror
989#undef clearerr
990#undef fileno
991#if SIZEOF_LONG < SIZEOF_LONG_LONG
992# if SIZEOF_LONG_LONG == SIZEOF_VOIDP
993/* actually this doesn't mean a pointer is strictly 64bit, but just
994 * quad_t size */
995# define _HAVE_LLP64_
996# endif
997# define _HAVE_SANE_QUAD_
998# define quad_t LONG_LONG
999# define u_quad_t unsigned LONG_LONG
1000#endif
1001#define FLOATING_POINT 1
1002#define BSD__dtoa ruby_dtoa
1003#define BSD__hdtoa ruby_hdtoa
1004#ifdef RUBY_PRI_VALUE_MARK
1005# define PRI_EXTRA_MARK RUBY_PRI_VALUE_MARK
1006#endif
1007#define lower_hexdigits (ruby_hexdigits+0)
1008#define upper_hexdigits (ruby_hexdigits+16)
1009#include "vsnprintf.c"
1010
1011static char *
1012ruby_ultoa(unsigned long val, char *endp, int base, int flags)
1013{
1014 const char *xdigs = lower_hexdigits;
1015 int octzero = flags & FSHARP;
1016 return BSD__ultoa(val, endp, base, octzero, xdigs);
1017}
1018
1019static int ruby_do_vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
1020
1021int
1022ruby_vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
1023{
1024 if (str && (ssize_t)n < 1)
1025 return (EOF);
1026 return ruby_do_vsnprintf(str, n, fmt, ap);
1027}
1028
1029static int
1030ruby_do_vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
1031{
1032 ssize_t ret;
1033 rb_printf_buffer f;
1034
1035 f._flags = __SWR | __SSTR;
1036 f._bf._base = f._p = (unsigned char *)str;
1037 f._bf._size = f._w = str ? (n - 1) : 0;
1038 f.vwrite = BSD__sfvwrite;
1039 f.vextra = 0;
1040 ret = BSD_vfprintf(&f, fmt, ap);
1041 if (str) *f._p = 0;
1042#if SIZEOF_SIZE_T > SIZEOF_INT
1043 if (n > INT_MAX) return INT_MAX;
1044#endif
1045 return (int)ret;
1046}
1047
1048int
1049ruby_snprintf(char *str, size_t n, char const *fmt, ...)
1050{
1051 int ret;
1052 va_list ap;
1053
1054 if (str && (ssize_t)n < 1)
1055 return (EOF);
1056
1057 va_start(ap, fmt);
1058 ret = ruby_do_vsnprintf(str, n, fmt, ap);
1059 va_end(ap);
1060 return ret;
1061}
1062
1063typedef struct {
1064 rb_printf_buffer base;
1065 volatile VALUE value;
1067
1068static int
1069ruby__sfvwrite(register rb_printf_buffer *fp, register struct __suio *uio)
1070{
1071 struct __siov *iov;
1072 VALUE result = (VALUE)fp->_bf._base;
1073 char *buf = (char*)fp->_p;
1074 long len, n;
1075 long blen = buf - RSTRING_PTR(result), bsiz = fp->_w;
1076
1077 if (RBASIC(result)->klass) {
1078 rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
1079 }
1080 if (uio->uio_resid == 0)
1081 return 0;
1082#if SIZE_MAX > LONG_MAX
1083 if (uio->uio_resid >= LONG_MAX)
1084 rb_raise(rb_eRuntimeError, "too big string");
1085#endif
1086 len = (long)uio->uio_resid;
1087 CHECK(len);
1088 buf += blen;
1089 fp->_w = bsiz;
1090 for (iov = uio->uio_iov; len > 0; ++iov) {
1091 MEMCPY(buf, iov->iov_base, char, n = iov->iov_len);
1092 buf += n;
1093 len -= n;
1094 }
1095 fp->_p = (unsigned char *)buf;
1096 rb_str_set_len(result, buf - RSTRING_PTR(result));
1097 return 0;
1098}
1099
1100static const char *
1101ruby__sfvextra(rb_printf_buffer *fp, size_t valsize, void *valp, long *sz, int sign)
1102{
1103 VALUE value, result = (VALUE)fp->_bf._base;
1104 rb_encoding *enc;
1105 char *cp;
1106
1107 if (valsize != sizeof(VALUE)) return 0;
1108 value = *(VALUE *)valp;
1109 if (RBASIC(result)->klass) {
1110 rb_raise(rb_eRuntimeError, "rb_vsprintf reentered");
1111 }
1112 if (sign == '+') {
1113# define LITERAL(str) (*sz = rb_strlen_lit(str), str)
1114 /* optimize special const cases */
1115 switch (value) {
1116# define LITERAL_CASE(x) case Q##x: return LITERAL(#x)
1117 LITERAL_CASE(nil);
1118 LITERAL_CASE(true);
1119 LITERAL_CASE(false);
1120# undef LITERAL_CASE
1121 }
1122# undef LITERAL
1123 value = rb_inspect(value);
1124 }
1125 else if (SYMBOL_P(value)) {
1126 value = rb_sym2str(value);
1127 if (sign == ' ' && !rb_str_symname_p(value)) {
1128 value = rb_str_escape(value);
1129 }
1130 }
1131 else {
1132 value = rb_obj_as_string(value);
1133 if (sign == ' ') value = QUOTE(value);
1134 }
1135 enc = rb_enc_compatible(result, value);
1136 if (enc && rb_enc_asciicompat(enc)) {
1137 rb_enc_associate(result, enc);
1138 }
1139 else {
1140 enc = rb_enc_get(result);
1141 value = rb_str_conv_enc_opts(value, rb_enc_get(value), enc,
1143 Qnil);
1144 *(volatile VALUE *)valp = value;
1145 }
1146 StringValueCStr(value);
1147 RSTRING_GETMEM(value, cp, *sz);
1148 ((rb_printf_buffer_extra *)fp)->value = value;
1149 return cp;
1150}
1151
1152static void
1153ruby_vsprintf0(VALUE result, char *p, const char *fmt, va_list ap)
1154{
1156#define f buffer.base
1157 VALUE klass = RBASIC(result)->klass;
1158 int coderange = ENC_CODERANGE(result);
1159 long scanned = 0;
1160
1161 if (coderange != ENC_CODERANGE_UNKNOWN) scanned = p - RSTRING_PTR(result);
1162
1163 f._flags = __SWR | __SSTR;
1164 f._bf._size = 0;
1165 f._w = rb_str_capacity(result);
1166 f._bf._base = (unsigned char *)result;
1167 f._p = (unsigned char *)p;
1168 RBASIC_CLEAR_CLASS(result);
1169 f.vwrite = ruby__sfvwrite;
1170 f.vextra = ruby__sfvextra;
1171 buffer.value = 0;
1172 BSD_vfprintf(&f, fmt, ap);
1173 RBASIC_SET_CLASS_RAW(result, klass);
1174 p = RSTRING_PTR(result);
1175 long blen = (char *)f._p - p;
1176
1177 coderange = ENC_CODERANGE(result);
1178 if (coderange != ENC_CODERANGE_UNKNOWN && scanned < blen) {
1179 rb_str_coderange_scan_restartable(p + scanned, p + blen, rb_enc_get(result), &coderange);
1180 ENC_CODERANGE_SET(result, coderange);
1181 }
1182 rb_str_resize(result, blen);
1183#undef f
1184}
1185
1186static rb_encoding *
1187enc_check(rb_encoding *enc)
1188{
1189 if (!rb_enc_asciicompat(enc)) {
1190 rb_raise(rb_eEncCompatError, "ASCII incompatible encoding: %s", rb_enc_name(enc));
1191 }
1192 return enc;
1193}
1194
1195VALUE
1196rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
1197{
1198 const int initial_len = 120;
1199 VALUE result;
1200
1201 result = rb_str_buf_new(initial_len);
1202 if (enc) {
1203 rb_enc_associate(result, enc_check(enc));
1204 }
1205 ruby_vsprintf0(result, RSTRING_PTR(result), fmt, ap);
1206 return result;
1207}
1208
1209VALUE
1210rb_enc_sprintf(rb_encoding *enc, const char *format, ...)
1211{
1212 VALUE result;
1213 va_list ap;
1214
1215 va_start(ap, format);
1216 result = rb_enc_vsprintf(enc, format, ap);
1217 va_end(ap);
1218
1219 return result;
1220}
1221
1222VALUE
1223rb_vsprintf(const char *fmt, va_list ap)
1224{
1225 return rb_enc_vsprintf(NULL, fmt, ap);
1226}
1227
1228VALUE
1229rb_sprintf(const char *format, ...)
1230{
1231 VALUE result;
1232 va_list ap;
1233
1234 va_start(ap, format);
1235 result = rb_vsprintf(format, ap);
1236 va_end(ap);
1237
1238 return result;
1239}
1240
1241VALUE
1242rb_str_vcatf(VALUE str, const char *fmt, va_list ap)
1243{
1244 StringValue(str);
1245 enc_check(rb_enc_get(str));
1246 rb_str_modify(str);
1247 ruby_vsprintf0(str, RSTRING_END(str), fmt, ap);
1248
1249 return str;
1250}
1251
1252VALUE
1253rb_str_catf(VALUE str, const char *format, ...)
1254{
1255 va_list ap;
1256
1257 va_start(ap, format);
1258 str = rb_str_vcatf(str, format, ap);
1259 va_end(ap);
1260
1261 return str;
1262}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
ruby_coderange_type
What rb_enc_str_coderange() returns.
Definition coderange.h:33
static bool rb_enc_isprint(OnigCodePoint c, rb_encoding *enc)
Identical to rb_isprint(), except it additionally takes an encoding.
Definition ctype.h:180
static bool rb_enc_isdigit(OnigCodePoint c, rb_encoding *enc)
Identical to rb_isdigit(), except it additionally takes an encoding.
Definition ctype.h:208
VALUE rb_enc_vsprintf(rb_encoding *enc, const char *fmt, va_list ap)
Identical to rb_enc_sprintf(), except it takes a va_list instead of variadic arguments.
Definition sprintf.c:1196
VALUE rb_enc_sprintf(rb_encoding *enc, const char *fmt,...)
Identical to rb_sprintf(), except it additionally takes an encoding.
Definition sprintf.c:1210
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:108
#define RB_INTEGER_TYPE_P
Old name of rb_integer_type_p.
Definition value_type.h:87
#define ENC_CODERANGE_7BIT
Old name of RUBY_ENC_CODERANGE_7BIT.
Definition coderange.h:180
#define ENC_CODERANGE_VALID
Old name of RUBY_ENC_CODERANGE_VALID.
Definition coderange.h:181
#define RFLOAT_VALUE
Old name of rb_float_value.
Definition double.h:28
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition value_type.h:64
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define ECONV_UNDEF_REPLACE
Old name of RUBY_ECONV_UNDEF_REPLACE.
Definition transcode.h:526
#define T_FIXNUM
Old name of RUBY_T_FIXNUM.
Definition value_type.h:63
#define ENC_CODERANGE(obj)
Old name of RB_ENC_CODERANGE.
Definition coderange.h:184
#define ENC_CODERANGE_UNKNOWN
Old name of RUBY_ENC_CODERANGE_UNKNOWN.
Definition coderange.h:179
#define FIXABLE
Old name of RB_FIXABLE.
Definition fixnum.h:25
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define ECONV_INVALID_REPLACE
Old name of RUBY_ECONV_INVALID_REPLACE.
Definition transcode.h:524
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define Qnil
Old name of RUBY_Qnil.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define NIL_P
Old name of RB_NIL_P.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define ENC_CODERANGE_SET(obj, cr)
Old name of RB_ENC_CODERANGE_SET.
Definition coderange.h:186
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
#define ruby_verbose
This variable controls whether the interpreter is in debug mode.
Definition error.h:476
VALUE rb_eEncCompatError
Encoding::CompatibilityError exception.
Definition error.c:1437
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:467
void rb_enc_raise(rb_encoding *enc, VALUE exc, const char *fmt,...)
Identical to rb_raise(), except it additionally takes an encoding.
Definition error.c:3838
VALUE rb_Float(VALUE val)
This is the logic behind Kernel#Float.
Definition object.c:3739
VALUE rb_Integer(VALUE val)
This is the logic behind Kernel#Integer.
Definition object.c:3394
VALUE rb_inspect(VALUE obj)
Generates a human-readable textual representation of the given object.
Definition object.c:657
Encoding relates APIs.
static char * rb_enc_right_char_head(const char *s, const char *p, const char *e, rb_encoding *enc)
Queries the right boundary of a character.
Definition encoding.h:704
char * rb_enc_nth(const char *head, const char *tail, long nth, rb_encoding *enc)
Queries the n-th character.
Definition string.c:3052
VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts)
Identical to rb_str_conv_enc(), except it additionally takes IO encoder options.
Definition string.c:1232
long rb_enc_strlen(const char *head, const char *tail, rb_encoding *enc)
Counts the number of characters of the passed string, according to the passed encoding.
Definition string.c:2334
long rb_str_coderange_scan_restartable(const char *str, const char *end, rb_encoding *enc, int *cr)
Scans the passed string until it finds something odd.
Definition string.c:830
VALUE rb_check_symbol_cstr(const char *ptr, long len, rb_encoding *enc)
Identical to rb_check_id_cstr(), except for the return type.
Definition symbol.c:1265
#define INTEGER_PACK_BIG_ENDIAN
Big endian combination.
Definition bignum.h:576
#define INTEGER_PACK_2COMP
Uses 2's complement representation.
Definition bignum.h:553
VALUE rb_int_positive_pow(long x, unsigned long y)
Raises the passed x to the power of y.
Definition numeric.c:4764
VALUE rb_rational_num(VALUE rat)
Queries the numerator of the passed Rational.
Definition rational.c:2021
VALUE rb_rational_den(VALUE rat)
Queries the denominator of the passed Rational.
Definition rational.c:2027
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
size_t rb_str_capacity(VALUE str)
Queries the capacity of the given string.
Definition string.c:1009
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3421
void rb_must_asciicompat(VALUE obj)
Asserts that the given string's encoding is (Ruby's definition of) ASCII compatible.
Definition string.c:2791
VALUE rb_check_string_type(VALUE obj)
Try converting an object to its stringised representation using its to_str method,...
Definition string.c:2976
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1724
VALUE rb_obj_as_string(VALUE obj)
Try converting an object to its stringised representation using its to_s method, if any.
Definition string.c:1856
VALUE rb_sym2str(VALUE symbol)
Obtain a frozen string representation of a symbol (not including the leading colon).
Definition symbol.c:1024
int len
Length of the buffer.
Definition io.h:8
VALUE rb_str_format(int argc, const VALUE *argv, VALUE fmt)
Formats a string.
Definition sprintf.c:227
VALUE rb_f_sprintf(int argc, const VALUE *argv)
Identical to rb_str_format(), except how the arguments are arranged.
Definition sprintf.c:221
VALUE rb_vsprintf(const char *fmt, va_list ap)
Identical to rb_sprintf(), except it takes a va_list.
Definition sprintf.c:1223
#define rb_long2int
Just another name of rb_long2int_inline.
Definition long.h:62
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
static char * RSTRING_END(VALUE str)
Queries the end of the contents pointer of the string.
Definition rstring.h:409
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
Definition rstring.h:450
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap)
Identical to ruby_snprintf(), except it takes a va_list.
Definition sprintf.c:1022
int ruby_snprintf(char *str, size_t n, char const *fmt,...)
Our own locale-insensitive version of snprintf(3).
Definition sprintf.c:1049
#define RTEST
This is an old name of RB_TEST.
Defines old _.
intptr_t SIGNED_VALUE
A signed integer type that has the same width with VALUE.
Definition value.h:63
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376