From 7ae73ac7e4a52afcb85b00234e5317d3b5f47759 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 24 Mar 2026 11:12:58 +0900 Subject: [PATCH] class.c: extract mrb_get_args fast path validator to inline function Replace the inline switch with a separate fast_fmt_ok() function that maps each format character to a validity code (0=invalid, 1=arg spec, 2=separator). Modern compilers generally lower this to a jump table, so the per-character cost remains effectively O(1). Keeping this as a switch (instead of a C99 array-index designator lookup table) also lets the file compile cleanly as C++. Co-authored-by: Claude --- src/class.c | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/class.c b/src/class.c index b7dc1138f..07a045838 100644 --- a/src/class.c +++ b/src/class.c @@ -1305,6 +1305,28 @@ mrb_block_given_p(mrb_state *mrb) #define GET_ARG(_type) (ptr ? ((_type)(*ptr++)) : va_arg((*ap), _type)) +/* + * Per-character validation for the mrb_get_args fast path. + * Written as a switch rather than an array-designator lookup table so + * that the file can be compiled as C++ (array-index designators are a + * C99-only feature). Modern compilers typically lower this to a jump + * table, giving the same effective O(1) behavior as the original table. + * Returns 1 for a valid arg specifier, 2 for the separator, 0 otherwise. + */ +static inline uint8_t +fast_fmt_ok(char c) +{ + switch (c) { + case 'o': case 'S': case 'A': case 'H': case 'i': case 'b': + case 'f': case 'n': case 'z': case 'c': case 's': case 'a': + return 1; + case '|': + return 2; + default: + return 0; + } +} + /* * Fast path for simple format strings (no *, :, !, +, &, ?). * Handles the most common patterns directly in one pass, @@ -1324,24 +1346,14 @@ get_args_fast(mrb_state *mrb, const char *format, void** ptr, va_list *ap) if (argc >= 15 || ci->nk > 0) return -1; argv = ci->stack + 1; - /* scan format: validate all specifiers and count required/optional */ + /* validate format and count args in one scan (table lookup, no switch) */ const char *p = format; int req = 0, opt = 0; mrb_bool in_opt = FALSE; while (*p) { - char c = *p; - if (c == '|') { in_opt = TRUE; p++; continue; } - /* bail out on complex specifiers or unsupported types */ - switch (c) { - case 'o': case 'S': case 'A': case 'H': case 'i': case 'b': - case 'f': case 'n': case 'z': case 'c': - break; - case 's': case 'a': - /* these consume 2 GET_ARG slots */ - break; - default: - return -1; /* unknown/complex, bail before touching va_list */ - } + uint8_t v = fast_fmt_ok(*p); + if (v == 0) return -1; /* unsupported specifier */ + if (v == 2) { in_opt = TRUE; p++; continue; } if (in_opt) opt++; else req++; p++; }