Implement compaction and embedded structs#201
Conversation
GC compaction was introduced in Ruby 2.7. Embedded Structs was introduced in Ruby 3.3. When enabled, the `struct strscanner` is stored direclty inside the object slot, meaning reading the struct member doesn't require loading another memory region.
There was a problem hiding this comment.
Pull request overview
This PR updates the strscan C extension to better support Ruby’s moving GC (compaction, introduced in Ruby 2.7) and to take advantage of Ruby 3.3’s embedded typed data for StringScanner’s internal struct strscanner.
Changes:
- Add GC compaction support via movable marking and a
dcompactcallback that relocates internalVALUEreferences. - Enable embedded typed data for
struct strscanner(when supported) and adjust free/memsize behavior accordingly. - Extend
extconf.rbfeature detection forrb_gc_locationandRUBY_TYPED_EMBEDDABLE.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| ext/strscan/strscan.c | Adds movable marking + compaction relocation, enables embeddable typed data flag, and adjusts free/memsize. |
| ext/strscan/extconf.rb | Detects rb_gc_location and RUBY_TYPED_EMBEDDABLE support at build time. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #else | ||
| # ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE | ||
| # define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE | ||
| # define HAVE_RUBY_TYPED_EMBEDDABLE 1 | ||
| # else | ||
| # define RUBY_TYPED_EMBEDDABLE 0 | ||
| # endif |
There was a problem hiding this comment.
The HAVE_CONST_RUBY_TYPED_EMBEDDABLE branch defines RUBY_TYPED_EMBEDDABLE as itself (#define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE), which is effectively a no-op and can be confusing (and may trigger self-referential macro warnings on some toolchains). Since have_const("RUBY_TYPED_EMBEDDABLE") already tells you whether the identifier exists, consider simplifying this detection to only define HAVE_RUBY_TYPED_EMBEDDABLE (and otherwise #define RUBY_TYPED_EMBEDDABLE 0) without redefining the constant name.
| #else | |
| # ifdef HAVE_CONST_RUBY_TYPED_EMBEDDABLE | |
| # define RUBY_TYPED_EMBEDDABLE RUBY_TYPED_EMBEDDABLE | |
| # define HAVE_RUBY_TYPED_EMBEDDABLE 1 | |
| # else | |
| # define RUBY_TYPED_EMBEDDABLE 0 | |
| # endif | |
| #elif defined(HAVE_CONST_RUBY_TYPED_EMBEDDABLE) | |
| # define HAVE_RUBY_TYPED_EMBEDDABLE 1 | |
| #else | |
| # define RUBY_TYPED_EMBEDDABLE 0 |
| #ifdef HAVE_RB_GC_LOCATION | ||
| static void | ||
| strscan_compact(void *ptr) | ||
| { | ||
| struct strscanner *p = ptr; | ||
| p->str = rb_gc_location(p->str); | ||
| p->regex = rb_gc_location(p->regex); | ||
| } | ||
| #else | ||
| #define rb_gc_mark_movable rb_gc_mark | ||
| #endif | ||
|
|
||
| static void | ||
| strscan_mark(void *ptr) | ||
| { | ||
| struct strscanner *p = ptr; | ||
| rb_gc_mark(p->str); | ||
| rb_gc_mark(p->regex); | ||
| rb_gc_mark_movable(p->str); | ||
| rb_gc_mark_movable(p->regex); | ||
| } |
There was a problem hiding this comment.
The new dcompact callback and rb_gc_mark_movable usage changes object marking semantics under compaction. Please add a regression test that exercises StringScanner across GC.compact (e.g., create a scanner, perform a scan to set internal state, call GC.compact, then assert subsequent scans and match data still behave correctly) so failures in dcompact/movable marking are caught in CI.
(ruby/strscan#201) GC compaction was introduced in Ruby 2.7. Embedded Structs was introduced in Ruby 3.3. When enabled, the `struct strscanner` is stored directly inside the object slot, meaning reading the struct member doesn't require loading another memory region. ruby/strscan@a018cbe40e
GC compaction was introduced in Ruby 2.7.
Embedded Structs was introduced in Ruby 3.3. When enabled, the
struct strscanneris stored directly inside the object slot, meaning reading the struct member doesn't require loading another memory region.