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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Both methods had identical loop bodies, differing only in the
starting group index (1 vs 0). Extracted matchdata_to_ary()
with a from parameter.
Co-authored-by: Claude <noreply@anthropic.com>
Regexp#match and Regexp#=~ shared most of their logic (get pattern,
execute, create MatchData, set globals). Extracted into exec_match()
internal function. Regexp#=~ now calls exec_match() and reads the
match position from the returned MatchData.
Co-authored-by: Claude <noreply@anthropic.com>
The pattern of reading @flags IV and converting to uint32_t was
repeated in 6 methods. Consolidated into a single helper function.
Co-authored-by: Claude <noreply@anthropic.com>
Add (?<=...) positive and (?<!...) negative lookbehind support.
The sub-pattern must have a fixed byte length (no quantifiers or
alternation), computed at compile time and stored in the instruction.
At execution time, the engine backs up by that many bytes and runs
the sub-pattern forward. Maximum lookbehind length is 255 bytes.
Co-authored-by: Claude <noreply@anthropic.com>
Collect replacement parts in an array and join at the end instead
of repeated string += which creates intermediate string objects.
Co-authored-by: Claude <noreply@anthropic.com>
When match? calls re_exec with captures=NULL, the Pike VM now
skips all capture pool operations: no pool_copy, no RE_SAVE
writes, no pool compaction. Only a single dummy pool slot is
allocated. This significantly reduces work for boolean matching.
Co-authored-by: Claude <noreply@anthropic.com>
Pre-intern the $1-$9 symbols on first use instead of calling
mrb_intern_cstr (which computes strlen + hash) on every match.
Co-authored-by: Claude <noreply@anthropic.com>
Major changes to the NFA execution engine:
- Thread captures stored in a flat pool sized to actual ncap
(e.g. 4 ints for 1 capture group vs 64 fixed), dramatically
reducing per-thread copy cost
- Generation counter for visited[] eliminates per-step memset
of the entire bytecode-length array
- Pool compaction between steps reclaims dead thread slots
- Backtracking engine also uses dynamic ncap-sized captures
Co-authored-by: Claude <noreply@anthropic.com>
Internal flags (MULTILINE=2, DOTALL=4, EXTENDED=8) differ from
Ruby constants (EXTENDED=2, MULTILINE=4). Convert in C instead
of returning raw internal flags. Also add Regexp#casefold?.
Co-authored-by: Claude <noreply@anthropic.com>