Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
f01bf18
Refactor core_hash_merge_ptr and core_hash_merge_kw
byroot Aug 15, 2026
ee1da20
[ruby/rubygems] Memoize cooldown settings lookup during resolve
hsbt Aug 24, 2026
298a40c
[ruby/rubygems] Warn when a configured cooldown value is invalid
hsbt Aug 24, 2026
e6a0e37
[ruby/rubygems] Read offset-less created_at timestamps as UTC
hsbt Aug 24, 2026
08735ae
[ruby/rubygems] Resolve globbed entries literally in Gem::Util.glob_f…
hsbt Aug 24, 2026
ae04a9d
[ruby/rubygems] Avoid interpolating install paths into glob patterns
hsbt Aug 24, 2026
4bdea88
[ruby/rubygems] Do not rely on the host RubyGems for literal glob joi…
hsbt Aug 24, 2026
b8f4f8a
[ruby/rubygems] Show the newest out-of-cooldown version in bundle out…
hsbt Aug 24, 2026
ee8f5c6
[ruby/rubygems] Say "out of cooldown" in the outdated table too
hsbt Aug 24, 2026
9a37be8
[ruby/rubygems] Document the newest out-of-cooldown tokens in bundle-…
hsbt Aug 24, 2026
f474b2d
[ruby/rubygems] Memoize matching_specs in bundle outdated
hsbt Aug 24, 2026
ecea262
[ruby/rubygems] Unnest the interpolation in the outdated cooldown column
hsbt Aug 24, 2026
6b10376
[ruby/rubygems] Add a strict-mode regression test for outdated with c…
hsbt Aug 24, 2026
37ba8a6
[ruby/rubygems] Pin the singular form of the cooldown remaining-days …
hsbt Aug 24, 2026
8f3d809
[ruby/rubygems] Include the source in the matching_specs memo key
hsbt Aug 24, 2026
5c26e1f
[ruby/rubygems] Evaluate cooldown remaining days against a single tim…
hsbt Aug 24, 2026
4e7d55b
[ruby/rubygems] Document that strict mode carries no cooldown annotat…
hsbt Aug 24, 2026
37dc070
[ruby/rubygems] Note that the patch level options also imply strict
hsbt Aug 24, 2026
09fdc3b
Avoid unsafe zero-length `IO::Buffer` operations. (#18465)
samuel-williams-shopify Aug 24, 2026
042e2bf
Process.warmup: recompute allocatable_bytes
byroot Aug 22, 2026
cc706f3
Clarify `IO::Buffer` slice validity documentation. (#18469)
samuel-williams-shopify Aug 24, 2026
281c2d4
Document `IO::Buffer#free` empty state. (#18471)
samuel-williams-shopify Aug 24, 2026
ddb7df3
Front-load slower compilation shards
nobu Aug 24, 2026
d3bf452
Validate `IO::Buffer` constructor flags. (#18470)
samuel-williams-shopify Aug 24, 2026
5ebbf33
Respect frozen `IO::Buffer` lifecycle state. (#18473)
samuel-williams-shopify Aug 24, 2026
2449a13
Hash::[] start from a right-sized hash
byroot Aug 24, 2026
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
170 changes: 85 additions & 85 deletions .github/workflows/compilers.yml

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -5406,7 +5406,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
* - It contains key-value pairs. So we need to take every two elements.
* We can assume that the length is always even.
*
* - Merging is done by a method call (id_core_hash_merge_ptr).
* - Merging is done by a method call (id_core_hash_merge_bang_ptr).
* Sometimes we need to insert the receiver, so "anchor" is needed.
* In addition, a method call is much slower than concatarray.
* So it pays only when the subsequence is really long.
Expand Down Expand Up @@ -5436,7 +5436,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth
ADD_INSN1(ret, line_node, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE)); \
ADD_INSN(ret, line_node, swap); \
APPEND_LIST(ret, anchor); \
ADD_SEND(ret, line_node, id_core_hash_merge_ptr, INT2FIX(stack_len + 1)); \
ADD_SEND(ret, line_node, id_core_hash_merge_bang_ptr, INT2FIX(stack_len + 1)); \
} \
INIT_ANCHOR(anchor); \
first_chunk = stack_len = 0; \
Expand Down Expand Up @@ -5482,7 +5482,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth

ADD_INSN1(ret, line_node, putobject, hash);

ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
ADD_SEND(ret, line_node, id_core_hash_merge_bang_kwd, INT2FIX(2));
}
RB_OBJ_WRITTEN(iseq, Qundef, hash);
}
Expand Down Expand Up @@ -5556,7 +5556,7 @@ compile_hash(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int meth

NO_CHECK(COMPILE(ret, "keyword splat", kw));

ADD_SEND(ret, line_node, id_core_hash_merge_kwd, INT2FIX(2));
ADD_SEND(ret, line_node, id_core_hash_merge_bang_kwd, INT2FIX(2));
}
}

Expand Down Expand Up @@ -10217,7 +10217,7 @@ compile_super(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, i
ADD_INSN1(args, node, putobject, ID2SYM(id));
ADD_GETLOCAL(args, node, idx, lvar_level);
}
ADD_SEND(args, node, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
ADD_SEND(args, node, id_core_hash_merge_bang_ptr, INT2FIX(i * 2 + 1));
flag |= VM_CALL_KW_SPLAT| VM_CALL_KW_SPLAT_MUT;
}
else if (local_body->param.flags.has_kwrest) {
Expand Down
3 changes: 3 additions & 0 deletions defs/id.def
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ firstline, predefined = __LINE__+1, %[\
core#define_singleton_method
core#set_postexe
core#hash_merge_ptr
core#hash_merge_bang_ptr
core#hash_merge_kwd
core#hash_merge_bang_kwd
core#hash_coerce
core#raise
core#sprintf

Expand Down
15 changes: 8 additions & 7 deletions gc/default/default.c
Original file line number Diff line number Diff line change
Expand Up @@ -9518,9 +9518,6 @@ rb_gc_impl_prepare_heap(void *objspace_ptr)
{
rb_objspace_t *objspace = objspace_ptr;

size_t orig_total_slots = objspace_available_slots(objspace);
size_t orig_allocatable_bytes = objspace->heap_pages.allocatable_bytes;

rb_gc_impl_each_objects(objspace, gc_set_candidate_object_i, objspace_ptr);

double orig_max_free_slots = gc_params.heap_free_slots_max_ratio;
Expand All @@ -9534,11 +9531,15 @@ rb_gc_impl_prepare_heap(void *objspace_ptr)
heap_pages_free_unused_pages(objspace_ptr);
GC_ASSERT(heap_pages_freeable_pages == 0);
GC_ASSERT(objspace->empty_pages_count == 0);
objspace->heap_pages.allocatable_bytes = orig_allocatable_bytes;

size_t total_slots = objspace_available_slots(objspace);
if (orig_total_slots > total_slots) {
objspace->heap_pages.allocatable_bytes += (orig_total_slots - total_slots) * heaps[0].slot_size;
// Process.warmup is meant to be called at the end of the boot sequence, which is commonly allocation
// heavy and result in GC limits raising significantly, but it's not indicative of the limits needed
// for runtime.
// Recompute the allocatable_bytes limit based on `gc_params.heap_init_bytes`.
GC_ASSERT(objspace->heap_pages.allocatable_bytes == 0);
for (int i = 0; i < HEAP_COUNT; i++) {
rb_heap_t *heap = &heaps[i];
heap_allocatable_bytes_expand(objspace, heap, heap->empty_slots, heap->total_slots, heap->slot_size);
}

#if defined(HAVE_MALLOC_TRIM) && !defined(RUBY_ALTERNATIVE_MALLOC_HEADER)
Expand Down
90 changes: 79 additions & 11 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1565,10 +1565,19 @@ copy_compare_by_id(VALUE hash, VALUE basis)
return hash;
}

static VALUE
hash_new_capa(VALUE klass, size_t capa)
{
return hash_init_capa(hash_alloc_capa(klass, 0, Qnil, capa, false), capa);
}

VALUE
rb_hash_new_capa(long capa)
{
return hash_init_capa(hash_alloc_capa(rb_cHash, 0, Qnil, capa, false), capa);
if (capa < 0) {
rb_raise(rb_eArgError, "negative hash size (or size too big)");
}
return hash_new_capa(rb_cHash, capa);
}

VALUE
Expand Down Expand Up @@ -1641,6 +1650,17 @@ hash_dup(VALUE hash, VALUE klass, VALUE flags)
return hash_copy(dup, hash);
}

static VALUE
hash_dup_capa(VALUE hash, size_t capa)
{
VALUE ret = hash_alloc_capa(rb_cHash, 0, Qnil, capa, false);
if (capa > RHASH_AR_TABLE_MAX_SIZE) {
RHASH_SET_ST_FLAG(ret);
}
hash_copy(ret, hash);
return ret;
}

VALUE
rb_hash_dup(VALUE hash)
{
Expand Down Expand Up @@ -1855,6 +1875,7 @@ rb_hash_init(rb_execution_context_t *ec, VALUE hash, VALUE capa_value, VALUE ifn
}

static VALUE rb_hash_to_a(VALUE hash);
static VALUE hash_new_with_bulk_insert(VALUE klass, long argc, const VALUE *argv);

/*
* call-seq:
Expand Down Expand Up @@ -1901,27 +1922,31 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
if (argc == 1) {
tmp = rb_hash_s_try_convert(Qnil, argv[0]);
if (!NIL_P(tmp)) {
if (!RHASH_EMPTY_P(tmp) && rb_hash_compare_by_id_p(tmp)) {
if (RHASH_EMPTY_P(tmp)) {
return hash_new_capa(klass, 0);
}

if (rb_hash_compare_by_id_p(tmp)) {
/* hash_copy for non-empty hash will copy compare_by_identity
flag, but we don't want it copied. Work around by
converting hash to flattened array and using that. */
tmp = rb_hash_to_a(tmp);
}
else {
hash = hash_alloc(klass);
if (!RHASH_EMPTY_P(tmp))
hash_copy(hash, tmp);
return hash;
hash = hash_alloc_capa(klass, 0, Qnil, RHASH_SIZE(tmp), false);
return hash_copy(hash, tmp);
}
}
else {
tmp = rb_check_array_type(argv[0]);
}

if (!NIL_P(tmp)) {
long i;
if (RARRAY_LEN(tmp) == 0) {
return hash_new_capa(klass, 0);
}

hash = hash_alloc(klass);
long i;
for (i = 0; i < RARRAY_LEN(tmp); ++i) {
VALUE e = RARRAY_AREF(tmp, i);
VALUE v = rb_check_array_type(e);
Expand All @@ -1931,6 +1956,18 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)",
rb_builtin_class_name(e), i);
}

if (i == 0) {
switch (RARRAY_LEN(v)) {
case 2:
hash = hash_new_capa(klass, RARRAY_LEN(tmp));
break;
case 1:
hash = hash_new_capa(klass, RARRAY_LEN(tmp) / 1);
break;
}
}

switch (RARRAY_LEN(v)) {
default:
rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)",
Expand All @@ -1949,8 +1986,7 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
rb_raise(rb_eArgError, "odd number of arguments for Hash");
}

hash = hash_alloc(klass);
rb_hash_bulk_insert(argc, argv, hash);
hash = hash_new_with_bulk_insert(klass, argc, argv);
hash_verify(hash);
return hash;
}
Expand Down Expand Up @@ -5213,14 +5249,46 @@ rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
}
}

static VALUE
hash_new_with_bulk_insert(VALUE klass, long argc, const VALUE *argv)
{
VALUE val = hash_new_capa(klass, argc / 2);
rb_hash_bulk_insert(argc, argv, val);
return val;
}

VALUE
rb_hash_new_with_bulk_insert(long argc, const VALUE *argv)
{
VALUE val = rb_hash_new_capa(argc / 2);
return hash_new_with_bulk_insert(rb_cHash, argc, argv);
}

VALUE
rb_hash_merge2_bulk(VALUE hash, long argc, const VALUE *argv, bool dup)
{
VALUE val = hash;
if (dup) {
// This is used to build literal hashes and keyword arguments,
// we can assume duplicate keys are very rare.
val = hash_dup_capa(val, RHASH_SIZE(val) + argc / 2);
}
rb_hash_bulk_insert(argc, argv, val);
return val;
}

VALUE
rb_hash_merge2(VALUE h1, VALUE h2, bool dup)
{
VALUE val = h1;
if (dup) {
// This is used to build literal hashes and keyword arguments,
// we can assume duplicate keys are very rare.
val = hash_dup_capa(val, RHASH_SIZE(val) + RHASH_SIZE(h2));
}
rb_hash_foreach(h2, rb_hash_update_i, val);
return val;
}

#undef USE_ORIGENVIRON
#if !defined(_WIN32) && !(defined(HAVE_SETENV) && defined(HAVE_UNSETENV))
# define USE_ORIGENVIRON 1
Expand Down
5 changes: 3 additions & 2 deletions include/ruby/io/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ VALUE rb_io_buffer_free(VALUE self);
// not exactly one.
VALUE rb_io_buffer_free_locked(VALUE self);

// Access the internal buffer and flags. Validates the pointers.
// The points may not remain valid if the source buffer is manipulated.
// Access the internal buffer and flags. Validates the pointers. If the returned
// base is NULL, the returned size is always zero.
// The pointers may not remain valid if the source buffer is manipulated.
// Consider using rb_io_buffer_lock if needed.
enum rb_io_buffer_flags rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size);
void rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size);
Expand Down
2 changes: 2 additions & 0 deletions internal/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ int rb_hash_stlike_foreach_with_replace(VALUE hash, st_foreach_check_callback_fu
RUBY_SYMBOL_EXPORT_END

VALUE rb_hash_new_with_bulk_insert(long argc, const VALUE *argv);
VALUE rb_hash_merge2(VALUE h1, VALUE h2, bool dup);
VALUE rb_hash_merge2_bulk(VALUE hash, long argc, const VALUE *argv, bool dup);
VALUE rb_hash_resurrect(VALUE hash);
int rb_hash_stlike_lookup(VALUE hash, st_data_t key, st_data_t *pval);
VALUE rb_hash_keys(VALUE hash);
Expand Down
Loading