diff --git a/spec/manual/hash_large_spec.cr b/spec/manual/hash_large_spec.cr new file mode 100644 index 000000000000..d4ca4af96a8f --- /dev/null +++ b/spec/manual/hash_large_spec.cr @@ -0,0 +1,8 @@ +require "spec" + +it "creates Hash at maximum capacity" do + # we don't try to go as high as Int32::MAX because it would allocate 18GB of + # memory in total. This already tests for Int32 overflows while 'only' needing + # 4.5GB of memory. + Hash(Int32, Int32).new(initial_capacity: (Int32::MAX // 4) + 1) +end diff --git a/src/hash.cr b/src/hash.cr index 9b2936ddd618..1be6543d730c 100644 --- a/src/hash.cr +++ b/src/hash.cr @@ -236,12 +236,14 @@ class Hash(K, V) # Translate initial capacity to the nearest power of 2, but keep it a minimum of 8. if initial_capacity < 8 initial_entries_size = 8 + elsif initial_capacity > 2**30 + initial_entries_size = Int32::MAX else initial_entries_size = Math.pw2ceil(initial_capacity) end # Because we always keep indice_size >= entries_size * 2 - initial_indices_size = initial_entries_size * 2 + initial_indices_size = initial_entries_size.to_u64 * 2 @entries = malloc_entries(initial_entries_size) @@ -830,7 +832,7 @@ class Hash(K, V) # The actual number of bytes needed to allocate `@indices`. private def indices_malloc_size(size) - size * @indices_bytesize + size.to_u64 * @indices_bytesize end # Reallocates `size` number of indices for `@indices`.