From 529bd606f278dd504fef1e724377e2811331d854 Mon Sep 17 00:00:00 2001 From: The Major Date: Sun, 10 Nov 2024 23:09:58 +0000 Subject: [PATCH] Implement rb_hash_bulk_insert --- CHANGELOG.md | 1 + .../truffleruby/truffleruby-abi-version.h | 2 +- spec/ruby/optional/capi/ext/hash_spec.c | 15 +++++ spec/ruby/optional/capi/hash_spec.rb | 55 +++++++++++++++++++ src/main/c/cext/hash.c | 8 +++ 5 files changed, 80 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 197a39f11ef6..41701a11e5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Compatibility: * Implement `rb_str_strlen()` (#3697, @Th3-M4jor). * Support `Time.new` with String argument and error when invalid (#3693, @rwstauner). * Implement `rb_enc_interned_str()` (#3703, @Th3-M4jor). +* Implement `rb_hash_bulk_insert()` (#3705, @Th3-M4jor). Performance: diff --git a/lib/cext/include/truffleruby/truffleruby-abi-version.h b/lib/cext/include/truffleruby/truffleruby-abi-version.h index 6bdaae61d315..aff4f1ca93f3 100644 --- a/lib/cext/include/truffleruby/truffleruby-abi-version.h +++ b/lib/cext/include/truffleruby/truffleruby-abi-version.h @@ -20,6 +20,6 @@ // $RUBY_VERSION must be the same as TruffleRuby.LANGUAGE_VERSION. // $ABI_NUMBER starts at 1 and is incremented for every ABI-incompatible change. -#define TRUFFLERUBY_ABI_VERSION "3.2.4.6" +#define TRUFFLERUBY_ABI_VERSION "3.2.4.7" #endif diff --git a/spec/ruby/optional/capi/ext/hash_spec.c b/spec/ruby/optional/capi/ext/hash_spec.c index 69ef02d5da6e..e7fdbb10f474 100644 --- a/spec/ruby/optional/capi/ext/hash_spec.c +++ b/spec/ruby/optional/capi/ext/hash_spec.c @@ -134,6 +134,20 @@ VALUE hash_spec_compute_a_hash_code(VALUE self, VALUE seed) { return ULONG2NUM(h); } +VALUE hash_spec_rb_hash_bulk_insert(VALUE self, VALUE array_len, VALUE array, VALUE hash) { + VALUE* ptr; + + if (array == Qnil) { + ptr = NULL; + } else { + ptr = RARRAY_PTR(array); + } + + long len = FIX2LONG(array_len); + rb_hash_bulk_insert(len, ptr, hash); + return Qnil; +} + void Init_hash_spec(void) { VALUE cls = rb_define_class("CApiHashSpecs", rb_cObject); rb_define_method(cls, "rb_hash", hash_spec_rb_hash, 1); @@ -162,6 +176,7 @@ void Init_hash_spec(void) { rb_define_method(cls, "rb_hash_size", hash_spec_rb_hash_size, 1); rb_define_method(cls, "rb_hash_set_ifnone", hash_spec_rb_hash_set_ifnone, 2); rb_define_method(cls, "compute_a_hash_code", hash_spec_compute_a_hash_code, 1); + rb_define_method(cls, "rb_hash_bulk_insert", hash_spec_rb_hash_bulk_insert, 3); } #ifdef __cplusplus diff --git a/spec/ruby/optional/capi/hash_spec.rb b/spec/ruby/optional/capi/hash_spec.rb index a0e49ffc4cc0..08e03fcb26e4 100644 --- a/spec/ruby/optional/capi/hash_spec.rb +++ b/spec/ruby/optional/capi/hash_spec.rb @@ -194,6 +194,61 @@ end end + describe "rb_hash_bulk_insert" do + it 'inserts key-value pairs into the hash' do + arr = [:a, 1, :b, 2, :c, 3] + hash = {} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 1, b: 2, c: 3} + end + + it 'overwrites existing keys' do + arr = [:a, 4, :b, 5, :c, 6] + hash = {a: 1, b: 2} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 4, b: 5, c: 6} + end + + it 'uses the last key in the array if it appears multiple times' do + arr = [:a, 1, :b, 2, :a, 3] + hash = {} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 3, b: 2} + end + + it 'allows the array to be NULL if the length is zero' do + hash = {} + + @s.rb_hash_bulk_insert(0, nil, hash) + + hash.should == {} + end + + it 'does not include any keys after the given length' do + arr = [:a, 1, :b, 2, :c, 3, :d, 4] + hash = {} + + @s.rb_hash_bulk_insert(arr.length - 2, arr, hash) + + hash.should == {a: 1, b: 2, c: 3} + end + + it 'does not modify the hash if the length is zero' do + arr = [] + hash = {a: 1, b: 2} + + @s.rb_hash_bulk_insert(arr.length, arr, hash) + + hash.should == {a: 1, b: 2} + end + end + describe "rb_hash_size" do it "returns the size of the hash" do hsh = {fast: 'car', good: 'music'} diff --git a/src/main/c/cext/hash.c b/src/main/c/cext/hash.c index 147754c845ed..7e5283109d3a 100644 --- a/src/main/c/cext/hash.c +++ b/src/main/c/cext/hash.c @@ -100,6 +100,14 @@ void rb_hash_foreach(VALUE hash, int (*func)(VALUE key, VALUE val, VALUE arg), V polyglot_invoke(RUBY_CEXT, "rb_hash_foreach", rb_tr_unwrap(hash), func, (void*)arg); } +void rb_hash_bulk_insert(long n, const VALUE *values, VALUE hash) { + void* unwrapped_hash = rb_tr_unwrap(hash); + + for (long i = 0; i < n; i += 2) { + polyglot_invoke(unwrapped_hash, "[]=", rb_tr_unwrap(values[i]), rb_tr_unwrap(values[i + 1])); + } +} + VALUE rb_hash_size(VALUE hash) { return RUBY_INVOKE(hash, "size"); }