Skip to content

Commit

Permalink
[GR-18163] Implement rb_hash_bulk_insert (#3715)
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4400
  • Loading branch information
andrykonchin committed Nov 12, 2024
2 parents 8494f9f + 529bd60 commit 56e0648
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion lib/cext/include/truffleruby/truffleruby-abi-version.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 15 additions & 0 deletions spec/ruby/optional/capi/ext/hash_spec.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
55 changes: 55 additions & 0 deletions spec/ruby/optional/capi/hash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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'}
Expand Down
8 changes: 8 additions & 0 deletions src/main/c/cext/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down

0 comments on commit 56e0648

Please sign in to comment.