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 <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-03-24 11:12:58 +09:00
parent 966aa9512e
commit 7ae73ac7e4
+26 -14
View File
@@ -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++;
}