Commit Graph

5757 Commits

Author SHA1 Message Date
dearblue a8c841433f Rename the members of the mrb_combination_state structure
Since these are expressed as "nPk" or "nCk" in mathematics, rename `n` to `k` and `array_size` to `n`.
Additionally, rename the parameters `#__repeated_combination` and `#__combination_init` from `n` to `k`.

However, the parameter `n` in `#repeated_permutation` and `#repeated_combination` remains unchanged to align with CRuby.
2026-05-05 22:25:26 +09:00
Yukihiro "Matz" Matsumoto 3f321f09bc mruby-regexp: fix leak and UAF on compile error paths
compile_error is the chokepoint for all regex-compile errors;
mrb_raisef longjmps out of re_compile, abandoning the stack-local
re_compiler struct. Three connected bugs:

1. Memory leak: c->code and c->classes (grown by emit/add_class
   via mrb_realloc) were never freed before raising, leaking on
   any compile error like /[/. c->stripped was already cleaned up
   here for the same reason; the other two buffers were missed.

2. Use-after-free: c->src aliases c->stripped when RE_FLAG_EXTENDED
   is set, but the original code freed c->stripped before passing
   c->src to mrb_raisef's "%s" formatter. Format the message into
   an mrb_value first (mruby's GC-managed string survives the
   longjmp), then free, then raise.

3. Heap-buffer-overflow: strip_extended returns a non-NUL-terminated
   buffer of size len. Even with format-before-free, "%s" called
   strlen and read past the buffer end. Use mruby's %l directive
   which takes an explicit (char*, size_t) and avoids strlen.

Reported by OSS-Fuzz (clusterfuzz testcase 5394267353972736).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-05 09:31:38 +09:00
Yukihiro "Matz" Matsumoto 479af5c1bd mruby-regexp: bounds-check non-ASCII RE_CHAR in first_set_walk
The first-byte bitmap (bm[16]) is intentionally ASCII-only
(include/re_internal.h:75 documents it as 128 bits / ASCII), and
the matcher at re_exec.c:39 short-circuits for bytes >= 128. But
first_set_walk's RE_CHAR case wrote bm[a >> 3] without checking
a, overflowing the 16-byte stack buffer for any pattern
containing a byte >= 128.

When a >= 128, return FALSE so compute_first_set marks the filter
unusable, matching the bail-out pattern already used for RE_NCLASS
and RE_ANY. The pattern still compiles and matches; only the
first-byte optimization is skipped.

Reported by OSS-Fuzz (clusterfuzz testcase 4909069193510912).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-03 22:43:22 +09:00
Yukihiro "Matz" Matsumoto c24d01c6fa Merge pull request #6776 from dearblue/array-combination.3
Return nil if a number less than 1 is passed to `Array#__combination_init`
2026-05-02 23:41:04 +09:00
dearblue 525ab7a800 Return nil if a number less than 1 is passed to Array#__combination_init
This simplifies the subsequent processing.
2026-05-02 22:16:56 +09:00
dearblue a98c6b62ea Don't use #puts in bintest for "mruby-bin-mrb"
Since `Kernel#puts` is undefined in unit tests, using `Kernel#print` is required.
Previously, for example, running `rake test` with the following build configuration caused the tests to fail.

```ruby
MRuby::Build.new do
  toolchain
  enable_debug
  enable_test
  enable_bintest
  gem core: "mruby-bin-mrb"
end
```
2026-05-02 20:52:00 +09:00
Yukihiro "Matz" Matsumoto 7dfd560df8 mruby-io: cap puts recursion depth to prevent C stack overflow
io_puts_ary recursed unconditionally on nested arrays. For cyclic
arrays (a = []; a << a; puts a) or pathologically deep arrays,
this caused a C stack overflow.

Add a depth cap (IO_PUTS_MAX_DEPTH = 16); on overflow, write
"[...]\n" and return, matching CRuby's behavior on cycles. The
pattern mirrors mruby-set's MAX_NESTED_DEPTH for the same problem
shape (pure C recursion not dispatched as a Ruby method).

Reported by OSS-Fuzz (clusterfuzz testcase 6233530857488384).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-02 11:27:01 +09:00
Yukihiro "Matz" Matsumoto e4cd7e6daf mruby-compiler: place newline before else
The mruby C style places `else` on its own line. Reformat the
remaining `} else if (...)` occurrence in the C action block of
NODE_SYMBOLS dump.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-28 12:56:25 +09:00
Yukihiro "Matz" Matsumoto 16f1f4418a mruby-bigint: place newline before else
The mruby C style places `else` on its own line. Reformat the
remaining `} else {` occurrences.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-28 12:56:25 +09:00
Yukihiro "Matz" Matsumoto 1702b89c25 Merge pull request #6787 from dearblue/sprintf 2026-04-28 12:56:07 +09:00
Yukihiro "Matz" Matsumoto 06d236d535 mruby-bin-config: register installer in products instead of bins
`build.bins` is for compiled binaries with bare names; entries are
fed through `exefile()` in `tasks/bin.rake`, which appends
`exts.executable` (e.g. `.exe`) when no extension is present.

When `ENV['OS']` is not `Windows_NT` but the toolchain has
`exts.executable=".exe"` (e.g. cross builds, or environments where
`OS` is unset under visualcpp), `mruby-bin-config` ended up with a
file task at `.../mruby-config` while rake asked for
`.../mruby-config.exe`, aborting with "Don't know how to build task".

Skip the `bins` path and call `build.define_installer` directly with
`mruby_config_path`, mirroring the existing `iscross` branch. The
file task path now always matches the script content's extension
(`.bat` on Windows, no extension elsewhere).

close #6807, reported by UENO, M. (@eunos-1128).

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-25 19:57:44 +09:00
Yukihiro "Matz" Matsumoto 550be1445f Revert "mruby-socket: add temporary getaddrinfo diagnostic for Windows CI"
This reverts commit a69d12aa54.

The diagnostic served its purpose: it isolated the failure to
Winsock's getaddrinfo for the "localhost" hostname. The permanent
fix (skip guard) is now in place, so remove the temporary probe.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 08:19:04 +09:00
Yukihiro "Matz" Matsumoto 57ca531f76 mruby-socket: skip localhost-dependent addrinfo tests on Windows
Addrinfo.getaddrinfo("localhost", ...) and Addrinfo.foreach("localhost",
...) crash on GitHub Actions Windows runners (Windows Server 2022 and
2025, both mingw-gcc and MSVC). Diagnostic showed 127.0.0.1 as a
numeric literal resolves fine via Winsock getaddrinfo, but "localhost"
as a hostname returns WSAHOST_NOT_FOUND under every address family
(AF_INET, AF_INET6, AF_UNSPEC, nil). PowerShell Resolve-DnsName on
the same runner succeeds, so the failure is Winsock-specific rather
than OS-level. Earlier workarounds (Dnscache service start, hosts
file append) had no effect.

Skip both tests on Windows with the existing SocketTest.win? guard,
matching the pattern already used for Addrinfo.unix and
Addrinfo#afamily. Linux and macOS coverage is unaffected.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 08:18:41 +09:00
Yukihiro "Matz" Matsumoto a69d12aa54 mruby-socket: add temporary getaddrinfo diagnostic for Windows CI
Addrinfo.getaddrinfo("localhost", 53, AF_INET, SOCK_STREAM) crashes
on GitHub Actions Windows runners with WSAHOST_NOT_FOUND, while the
same call works on Linux and macOS. Earlier hypotheses (winsock link
missing; hosts file not populated) have been ruled out on CI.

Probe five variants in one run to isolate the failing condition:
numeric literal 127.0.0.1, localhost with AF_UNSPEC, localhost with
nil family, localhost with AF_INET6, and the original AF_INET path.
Results go to stdout via puts so they are visible in CI logs. This
commit is temporary and will be reverted once the real fix lands.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-24 01:20:15 +09:00
Yukihiro "Matz" Matsumoto 2ff6563484 mruby-task: restore Windows link library dropped during HAL migration
Commit be6413f0d86a ("mruby-task: migrate HAL to ports/ directories")
moved the Windows HAL source into mruby-task/ports/win/ but dropped
the linker.libraries declaration that previously lived in
hal-win-task/mrbgem.rake. Both mingw and MSVC builds now fail to link
task_hal.o/obj with undefined references to timeBeginPeriod,
timeEndPeriod, timeSetEvent, and timeKillEvent. Re-declare winmm
under a for_windows? guard.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:16:21 +09:00
Yukihiro "Matz" Matsumoto 3f27eac308 mruby-io: restore Windows link library dropped during HAL migration
Commit 9965f11cfe1c ("mruby-io: migrate HAL to ports/ directories")
moved the Windows HAL source into mruby-io/ports/win/ but dropped the
linker.libraries declaration that previously lived in
hal-win-io/mrbgem.rake. Both mingw and MSVC builds now fail to link
with undefined references to closesocket, WSAStartup, WSACleanup,
select, and WSAGetLastError referenced from io.c and io_hal.c.
Re-declare ws2_32 under a for_windows? guard.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:16:11 +09:00
Yukihiro "Matz" Matsumoto 22df8d61be mruby-socket: restore Windows link libraries dropped during HAL migration
Commit d9e107c801 ("mruby-socket: migrate HAL to ports/ directories")
moved the Windows HAL source into mruby-socket/ports/win/ but dropped
the linker.libraries declarations that previously lived in
hal-win-socket/mrbgem.rake. Both mingw and MSVC builds now fail to
link with undefined references to WSAStartup, socket, send, and other
Winsock APIs. Re-declare wsock32 and ws2_32 under a for_windows? guard.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 23:16:02 +09:00
Yukihiro "Matz" Matsumoto ad8fc7d918 mruby-bigint: add multi-precision gcd tests
Cover zero operands, power-of-2 fast path, negative operands,
balanced multi-limb pairs with a shared Fibonacci factor, highly
unbalanced pairs (to exercise the Euclidean fallback), and
Fibonacci neighbors (always coprime). Declare a test dependency
on mruby-numeric-ext since Integer#gcd is defined there.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 22:56:44 +09:00
Yukihiro "Matz" Matsumoto 8e91554c6d mruby-bigint: rewrite mpz_gcd main loop as binary Stein algorithm
Replace the classical Euclidean main loop (mpz_mod per iteration)
with Stein's binary GCD: subtract + factor out trailing 2s on
odd-maintained operands. Keep an mpz_mod fallback for heavily
unbalanced pairs (the smaller operand has at least two fewer limbs)
where one long division replaces many Stein subtracts.

The old loop allocated a temporary mpz_t every iteration to hold the
mod result; the Stein loop is allocation-free thanks to the in-place
paths in mpz_sub and mpz_div_2exp. This matches the "memory first"
priority and also happens to be faster on typical inputs because
mpz_mod's setup cost dominates when the quotient is small (the
classical Fibonacci-neighbor worst case).

Also add a small mpz_swap helper used by the new loop.

Benchmark (bin/mruby benchmark/bm_bigint_gcd.rb, median of 3):

  case                       before    after    ratio
  single-limb                 42 ms    50 ms    1.19  (fast-path
                                                      unchanged;
                                                      noise)
  fib(200) vs fib(201)        82 ms    23 ms    0.28
  balanced ~700-bit shared    46 ms    32 ms    0.70
  unbalanced big vs small     35 ms    28 ms    0.80
  power-of-2 path             38 ms    42 ms    1.11  (fast-path
                                                      unchanged;
                                                      noise)
  balanced ~2800-bit shared   19 ms    15 ms    0.79

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 22:52:05 +09:00
Yukihiro "Matz" Matsumoto ec55e0d0f1 mruby-bigint: correct mpz_gcd comment to describe hybrid behavior
The preceding comment claimed "Binary GCD (Stein's algorithm)",
but the multi-limb main loop is classical Euclidean using mpz_mod.
Only the prelude (factoring out common 2s, single-limb and
power-of-2 fast paths) is Stein-flavored. Describe what the code
actually does so readers are not misled.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 22:36:18 +09:00
Yukihiro "Matz" Matsumoto 81bcd4e931 mruby-bigint: simplify MPZ_CTX_INIT with positional initializer
Per gemini-code-assist review on #6791: replace member-by-member
assignment with C89/C++98-style positional aggregate initialization,
keeping the macro a single declaration and matching the existing
pool_storage initializer style in the same macro.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto 449040400a mruby-regexp: keep MatchData source/regexp GC-reachable
The mrb_match_data struct stores `source` and `regexp` as plain
mrb_value members of a C-allocated struct, which the GC does not
scan.  Under MRB_GC_STRESS the source string could be collected
while the MatchData was still alive, causing md[0] to read freed
memory (observed as "\xff\xff\xff").

Also stash source and regexp as instance variables on the MatchData
object so they remain reachable via the object's iv_tbl during GC.
The C struct members continue to provide fast direct access, and
no other call sites need to change.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:39 +09:00
Yukihiro "Matz" Matsumoto 909f5a070f hw-adc: add ADC peripheral gem for embedded platforms
Add hw-adc gem with analog-to-digital conversion support:
- Common Ruby API with read, read_raw, and read_voltage
- ports/esp32/ using ESP-IDF ADC oneshot driver
- ports/rp2040/ using Pico SDK ADC hardware

Based on picoruby-adc by HASUMI Hitoshi.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:38 +09:00
Yukihiro "Matz" Matsumoto ce4bc77aaf hw-pwm: add PWM peripheral gem for embedded platforms
Add hw-pwm gem with pulse width modulation support:
- Common Ruby API with frequency, duty, period_us, pulse_width_us
- ports/esp32/ using LEDC peripheral
- ports/rp2040/ using Pico SDK PWM hardware

Based on picoruby-pwm by HASUMI Hitoshi.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:38 +09:00
Yukihiro "Matz" Matsumoto 5931a96a17 hw-spi: add SPI peripheral gem for embedded platforms
Add hw-spi gem with SPI communication support:
- Common Ruby API with write, read, and full-duplex transfer
- ports/esp32/ using ESP-IDF SPI master driver
- ports/rp2040/ using Pico SDK

Based on picoruby-spi by HASUMI Hitoshi.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:38 +09:00
Yukihiro "Matz" Matsumoto be6413f0d8 mruby-task: migrate HAL to ports/ directories
Move hal-posix-task and hal-win-task into
mruby-task/ports/posix/ and mruby-task/ports/win/.
Remove HAL auto-detection logic from mrbgem.rake. Update
cosmopolitan.rb to use conf.ports :posix instead of explicit
hal-* gem references.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:37 +09:00
Yukihiro "Matz" Matsumoto d9e107c801 mruby-socket: migrate HAL to ports/ directories
Move hal-posix-socket and hal-win-socket into
mruby-socket/ports/posix/ and mruby-socket/ports/win/.
Remove HAL auto-detection logic from mrbgem.rake.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:37 +09:00
Yukihiro "Matz" Matsumoto 886bf270f0 mruby-dir: migrate HAL to ports/ directories
Move hal-posix-dir and hal-win-dir into mruby-dir/ports/posix/
and mruby-dir/ports/win/. Remove HAL auto-detection logic from
mrbgem.rake.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:37 +09:00
Yukihiro "Matz" Matsumoto 9965f11cfe mruby-io: migrate HAL to ports/ directories
Move hal-posix-io and hal-win-io into mruby-io/ports/posix/ and
mruby-io/ports/win/. Platform sources are now compiled
automatically based on conf.ports setting. Remove HAL
auto-detection logic from mrbgem.rake.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:37 +09:00
Yukihiro "Matz" Matsumoto a8c82b506f hw-uart: consolidate platform gems into ports/ directories
Move hw-esp32-uart and hw-rp2040-uart into hw-uart/ports/esp32/
and hw-uart/ports/rp2040/ using the new ports build system.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:37 +09:00
Yukihiro "Matz" Matsumoto 53a43e9d36 hw-gpio: consolidate platform gems into ports/ directories
Move hw-esp32-gpio and hw-rp2040-gpio into hw-gpio/ports/esp32/
and hw-gpio/ports/rp2040/ using the new ports build system.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:37 +09:00
Yukihiro "Matz" Matsumoto b8eccc7a38 hw-i2c: consolidate platform gems into ports/ directories
Move hw-esp32-i2c and hw-rp2040-i2c into hw-i2c/ports/esp32/
and hw-i2c/ports/rp2040/ using the new ports build system.
Platform sources are now compiled automatically based on
conf.ports setting. Separate platform gems are no longer needed.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:36 +09:00
Yukihiro "Matz" Matsumoto 2b5a389de3 hw-uart: add UART peripheral gems for embedded platforms
Add three new gems for UART serial communication:
- hw-uart: common Ruby API, C bindings, ring buffer, and HAL header
- hw-esp32-uart: ESP32 HAL using ESP-IDF UART driver with FreeRTOS
  RX task
- hw-rp2040-uart: RP2040 HAL using Pico SDK with IRQ-driven RX

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:36 +09:00
Yukihiro "Matz" Matsumoto bdc3a65601 mruby-io: call mrb_hal_io_init/final from gem_init/gem_final
The HAL init/final functions were defined in hal-posix-io and
hal-win-io but never called. On Windows, hal-win-io performs
WSAStartup/WSACleanup in these functions, which was not being
invoked by mruby-io unlike other HAL gems (dir, socket, task).

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:36 +09:00
Yukihiro "Matz" Matsumoto 0ff41e4e28 hw-gpio: add GPIO peripheral gems for embedded platforms
Add three new gems for GPIO pin control:
- hw-gpio: common Ruby API, C bindings, and HAL header
- hw-esp32-gpio: ESP32 HAL using ESP-IDF GPIO driver
- hw-rp2040-gpio: RP2040 HAL using Pico SDK

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:36 +09:00
Yukihiro "Matz" Matsumoto 1ed52461e8 hw-i2c: add I2C peripheral gems for embedded platforms
Add three new gems for I2C communication:
- hw-i2c: common Ruby API, C bindings, and HAL header
- hw-esp32-i2c: ESP32 HAL using ESP-IDF I2C master driver
- hw-rp2040-i2c: RP2040 HAL using Pico SDK

The HAL API provides init, read, write, and atomic write_read
(repeated START) operations. Platform gems depend on hw-i2c
and are only compiled when explicitly included in build config.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:35 +09:00
Yukihiro "Matz" Matsumoto 416793db3d mruby-sprintf: use mrb_uint cast instead of uint64_t
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:35 +09:00
Yukihiro "Matz" Matsumoto 6f202d6c2b mruby-io: use mrb_int consistently in IO HAL interface
Replace all int64_t, uint64_t, uint32_t, and int32_t with mrb_int
in the HAL struct, timeval, and function signatures. All values
originate from or end up as mrb_int at the Ruby layer. Also removes
dead overflow checks in callers and the stdint.h dependency from
io_hal.h.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:35 +09:00
Yukihiro "Matz" Matsumoto ad1fd9285c mirb.c: add fileno() to _fileno() mapping for MSVC
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:35 +09:00
Yukihiro "Matz" Matsumoto 29f84030ca bigint.c: fix signed/unsigned comparison warning in base conversion
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:35 +09:00
Yukihiro "Matz" Matsumoto b70c393039 mruby-io: use mrb_int for mrb_hal_io_readlink() return type
int64_t was unnecessary; readlink(2) returns ssize_t (bounded by
PATH_MAX), and the Windows HAL just raises NotImplementedError.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:35 +09:00
Yukihiro "Matz" Matsumoto 726d8febf3 mruby-regexp: cache Pike VM state in pattern to avoid per-exec malloc
Pre-allocate visited array and thread lists at compile time and
reuse them across re_exec calls. A cache_in_use flag detects
re-entrancy and falls back to malloc when needed.

Eliminates 3 malloc + 3 free per NFA execution for the common
non-re-entrant case. Combined with the literal fast path, brings
literal match? from 4.7x to 2.5x vs CRuby.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:34 +09:00
Yukihiro "Matz" Matsumoto 034a64346c mruby-regexp: add literal pattern fast path bypassing NFA
Pure literal patterns (/hello/, /abc/) are detected at compile
time and matched using memchr+memcmp directly, completely
bypassing Pike VM setup (no malloc, no visited array, no thread
lists). Engine-only time drops from ~400ns to ~80ns.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:34 +09:00
Yukihiro "Matz" Matsumoto 8624764bca mruby-regexp: implement gsub/sub/scan core in C
Move the match loop for non-block gsub, sub, and scan from Ruby
to C. Key improvements:
- re_exec called directly in a loop without MatchData per match
- MatchData created only once (for $~ of the last match)
- Replacement \-escapes processed in C (apply_replacement)
- No intermediate string objects for pre_match/post_match
- Block variants remain in Ruby to avoid VM callbacks

Performance improvement (vs CRuby ratio):
  gsub simple: 5.0x -> 2.9x
  scan words:  5.8x -> 1.9x

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:34 +09:00
Yukihiro "Matz" Matsumoto 337b5906a2 mruby-regexp: add first-byte bitmap for fast position skipping
Compute a 128-bit bitmap of bytes that could start a match.
For patterns like /cat|dog|fox/ the bitmap contains only {b,c,d,f},
skipping positions where no alternative can match. For /\d+/ only
{'0'-'9'} are set.

Used when no literal prefix is available (alternation, character
class patterns). Falls back gracefully when too many bytes match.

Key improvements vs CRuby ratio:
  alternation miss: 9.5x -> 3.6x
  \d+ medium string: 3.0x -> 1.4x

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:33 +09:00
Yukihiro "Matz" Matsumoto 466e4ad84a mruby-regexp: defer pool_copy until character actually matches
Move pool_copy inside each match condition so captures are only
copied for threads that advance to the next step, skipping the
copy for non-matching threads.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:33 +09:00
Yukihiro "Matz" Matsumoto 1641c8c06f mruby-regexp: add literal prefix skip for fast string search
Extract the leading literal bytes from compiled bytecode and use
memchr + memcmp to skip positions where the prefix cannot match.
Both Pike VM and backtracking engine benefit.

For /needle/ in a 2006-char string: 29x faster (1.97s -> 0.07s),
now on par with CRuby/Oniguruma.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:33 +09:00
Yukihiro "Matz" Matsumoto 355c68f487 mruby-regexp: rename has_nongreedy to needs_backtrack
The flag is set not only for non-greedy quantifiers but also for
lookahead, lookbehind, and backreferences. The new name accurately
reflects its purpose: indicating that the backtracking engine is
required instead of the Pike VM.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:33 +09:00
Yukihiro "Matz" Matsumoto 13e63657a4 mruby-regexp: use array join in __sub_replace to avoid O(n^2)
Same pattern as the earlier gsub fix: collect parts in an array
and join at the end instead of repeated string concatenation.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:33 +09:00
Yukihiro "Matz" Matsumoto 6edef4e7e6 mruby-regexp: use dynamic captures allocation in exec_match()
Replace fixed RE_MAX_CAPTURES*2 (256 bytes) stack array with
malloc sized to actual pat->num_captures*2. Consistent with
the Pike VM's dynamic ncap-sized pool.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:33 +09:00