Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ext/strscan/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
have_func("onig_region_memsize(NULL)")
have_func("rb_reg_onig_match", "ruby/re.h")
have_func("rb_deprecate_constant")
have_func("rb_gc_location", "ruby.h") # RUBY_VERSION >= 2.7
have_const("RUBY_TYPED_EMBEDDABLE", "ruby.h") # RUBY_VERSION >= 3.3
create_makefile 'strscan'
else
File.write('Makefile', dummy_makefile("").join)
Expand Down
52 changes: 44 additions & 8 deletions ext/strscan/strscan.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,37 +182,73 @@ extract_beg_len(struct strscanner *p, long beg_i, long len)
Constructor
======================================================================= */

#ifdef RUBY_TYPED_EMBEDDABLE
# define HAVE_RUBY_TYPED_EMBEDDABLE 1
#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
Comment on lines +187 to +193
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
#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

Copilot uses AI. Check for mistakes.
#endif

#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);
}
Comment on lines +196 to 214
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.

static void
strscan_free(void *ptr)
{
struct strscanner *p = ptr;
onig_region_free(&(p->regs), 0);
#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
ruby_xfree(p);
#endif
}

static size_t
strscan_memsize(const void *ptr)
{
const struct strscanner *p = ptr;
size_t size = sizeof(*p) - sizeof(p->regs);
size_t size = 0;
#ifndef HAVE_RUBY_TYPED_EMBEDDABLE
size += sizeof(struct strscanner);
#endif

#ifdef HAVE_ONIG_REGION_MEMSIZE
size += onig_region_memsize(&p->regs);
const struct strscanner *p = ptr;
size += onig_region_memsize(&p->regs) - sizeof(p->regs);
#endif
return size;
}

static const rb_data_type_t strscanner_type = {
"StringScanner",
{strscan_mark, strscan_free, strscan_memsize},
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
.wrap_struct_name = "StringScanner",
.function = {
.dmark = strscan_mark,
.dfree = strscan_free,
.dsize = strscan_memsize,
#ifdef HAVE_RB_GC_LOCATION
.dcompact = strscan_compact,
#endif
},
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
};

static VALUE
Expand Down
Loading