diff --git a/spec/std/array_spec.cr b/spec/std/array_spec.cr index a5dd2e60f1fd..f465b49684ff 100644 --- a/spec/std/array_spec.cr +++ b/spec/std/array_spec.cr @@ -445,6 +445,24 @@ describe "Array" do a[3..] = [4, 5, 6] a.should eq([1, 2, 3, 4, 5, 6]) end + + it "reuses the buffer if possible" do + a = [1, 2, 3, 4, 5] + a.pop + a[4, 0] = [6] + a.should eq([1, 2, 3, 4, 6]) + a.@capacity.should eq(5) + a.@offset_to_buffer.should eq(0) + end + + it "resizes the buffer if capacity is not enough" do + a = [1, 2, 3, 4, 5] + a.shift + a[4, 0] = [6, 7, 8, 9] + a.should eq([2, 3, 4, 5, 6, 7, 8, 9]) + a.@capacity.should eq(10) + a.@offset_to_buffer.should eq(1) + end end describe "values_at" do diff --git a/src/array.cr b/src/array.cr index cd8341fd63f3..71dc841de1fe 100644 --- a/src/array.cr +++ b/src/array.cr @@ -531,7 +531,7 @@ class Array(T) @size -= diff else # Need to grow - resize_to_capacity(Math.pw2ceil(@size + diff)) + resize_if_cant_insert(diff) (@buffer + start + values.size).move_from(@buffer + start + count, size - start - count) (@buffer + start).copy_from(values.to_unsafe, values.size) @size += diff @@ -2071,6 +2071,10 @@ class Array(T) end private def calculate_new_capacity(new_size) + # Resizing is done via `Pointer#realloc` on the root buffer, so the space + # between the root and real buffers remains untouched + new_size += @offset_to_buffer + new_capacity = @capacity == 0 ? INITIAL_CAPACITY : @capacity while new_capacity < new_size if new_capacity < CAPACITY_THRESHOLD @@ -2118,13 +2122,11 @@ class Array(T) end private def resize_if_cant_insert(insert_size) - # Resize if we exceed the remaining capacity. - # `remaining_capacity - @size` is the actual number of slots we have - # to push new elements. - if insert_size > remaining_capacity - @size - # The new capacity that we need is what we already have occupied - # because of shift (`@offset_to_buffer`) plus my size plus the insert size. - resize_to_capacity(Math.pw2ceil(@offset_to_buffer + @size + insert_size)) + # Resize if we exceed the remaining capacity. This is less than `@capacity` + # if the array has been shifted and `@offset_to_buffer` is nonzero + new_size = @size + insert_size + if new_size > remaining_capacity + resize_to_capacity(calculate_new_capacity(new_size)) end end