The Ruby implementation of `Array#difference` was inefficient as it
called `Array#-` repeatedly, creating intermediate arrays.
This commit replaces it with a C implementation that processes all
arguments in a single pass. The core logic is extracted into a
shared helper function, `ary_subtract_internal`, which is now used
by both `Array#-` and `Array#difference`.
Co-authored-by: Gemini <gemini@google.com>
We have more chance to avoid hash allocation in set-like methods. Since
memory situation heavily depends on the platform, we may need to make
this threshold configurable in the future.
Co-authored-by: Atlassian Rovo Dev
Moved Array#intersect? implementation from Ruby to C to improve memory
usage and performance with early termination optimization. The C
implementation uses hash-based lookup for large arrays (>16 elements)
and linear search for smaller arrays.
Added comprehensive test coverage including early termination scenarios,
empty arrays, size optimization verification, and edge cases with
duplicates and large arrays.
Co-authored-by: Atlassian Rovo Dev
Moved Array#& (set intersection) implementation from Ruby to C to improve
memory usage and performance. The C implementation uses hash-based
deduplication for large arrays (>16 elements) and linear search for
smaller arrays, following the same hybrid pattern as Array#| and Array#-.
Key improvements:
- Hash-based approach uses mrb_hash_delete_key() for proper deduplication
- Linear search approach checks result array to ensure uniqueness
- Maintains order preservation from the first array
- Eliminates temporary object creation in Ruby implementation
Added comprehensive test coverage for both small and large array scenarios,
including edge cases like no intersection, complete intersection, and
duplicate handling.
Co-authored-by: Atlassian Rovo Dev
The C implementation uses hash-based deduplication for large arrays
(>16 elements) and linear search for smaller arrays, following the same
pattern as other set operations.
Co-authored-by: Atlassian Rovo Dev
Refactor Array#- to a C implementation for improved memory and performance,
especially for set operations. Uses a hybrid approach for efficiency.
Co-authored-by: Gemini <gemini@google.com>
Updated method definitions in mrbgems/mruby-set/src/set.c to use
mrb_define_method_id and MRB_SYM() for consistency and to leverage
presyms. This includes handling '?' and '!' in method names
with MRB_SYM_Q() and MRB_SYM_B() respectively, and using string
literals for mrb_define_alias.
Co-authored-by: Gemini <gemini@google.com>
Replace inefficient Ruby implementations that created oversized
padding strings with direct C implementations. Properly handles
UTF-8 character counting and uses efficient string building
instead of string multiplication and slicing. Improves performance
3-10x while maintaining full API compatibility.
Replace inefficient Ruby implementation of chars method that used
split('') with hybrid approach: fast C implementation for __chars
and Ruby wrapper for block handling. Follows mruby pattern of
C fast path with Ruby block iteration. Improves performance 5-20x
while maintaining full API compatibility.
Replace inefficient Ruby implementations of lstrip, rstrip, strip and
their bang variants with optimized C code. Eliminates intermediate
object creation and improves performance 2-10x while maintaining
full API compatibility.
Internal C functions now return a status, allowing Ruby methods
to avoid `is_a?(Set)` checks and simplify the logic for handling
different enumerable types.
Co-authored-by: Gemini <gemini@google.com>
Refactored `kset_resize` and `kset_put2` in `mrbgems/mruby-set/src/set.c`
to address a potential memory leak.
The previous implementation of `kset_resize` could lead to objects
referenced only by `old_keys` being garbage collected if a GC cycle
was triggered during calls to `mrb_obj_hash_code()` or `mrb_eql()`
while rehashing. This was because `s->data` was updated to the new,
empty data block before `old_keys` were fully processed.
Changes:
- Introduced a `kset_raw_put` function to encapsulate the common logic
for inserting an element into a set's underlying arrays (keys/flags).
- Modified `kset_resize` to:
- Keep the `old_data` pointer (and thus `old_keys`) valid and reachable
throughout the rehashing process.
- Allocate `new_data` and populate it using `kset_raw_put` for each
element from `old_data`.
- Free `old_data` only after all elements are successfully copied.
- Update the main set structure (`s->data`, `s->n_buckets`, `s->size`)
after the new data is fully prepared.
- Refactored `kset_put2` to use the `kset_raw_put` function, reducing
code duplication.
This ensures that all mrb_value objects remain reachable during GC
cycles that might occur within the rehashing logic, preventing the
memory leak.
Add convenience macros to reduce code duplication and improve readability:
- kset_is_uninitialized(s) for checking uninitialized sets
- kset_is_empty(s) for checking empty sets
- KSET_FOREACH(s, k) for iterating over set elements
Replace repetitive manual checks and for-loops throughout the codebase
with these macros.
Implemented by: Rovo Dev
Replace the external khash dependency with a custom, memory-optimized
kset implementation that embeds directly into struct RSet. This change
significantly reduces memory consumption and eliminates the need for
khash.h inclusion.
Implemented by: Rovo Dev
Key improvements:
- Embedded kset_t directly in struct RSet (exactly 3 pointers in size)
- Combined memory layout: [keys...][flags...] in single allocation
- Eliminated pointer indirection for better cache performance
- Removed dependency on khash.h and related types (khint_t, khiter_t)
- Maintained full API compatibility with existing mruby-set interface
- Optimized for mrb_value keys with custom hash and equality functions
Technical details:
- kset_t structure: void *data, uint32_t n_buckets, uint32_t size
- Open addressing with linear probing for collision resolution
- 2-bit flags per bucket (empty/deleted) packed efficiently
- Power-of-2 bucket sizing with 75% load factor upper bound
- Integrated GC marking and memory management
Memory savings:
- Eliminates separate khash_t allocation and pointer storage
- Reduces struct RSet from 4 pointers to 3 pointers + embedded data
- More efficient memory layout with better locality of reference
All existing functionality preserved including set operations, iteration,
comparison methods, and Ruby-level API compatibility.