diff --git a/spec/std/array_spec.cr b/spec/std/array_spec.cr index d922d4ef8afc..a5dd2e60f1fd 100644 --- a/spec/std/array_spec.cr +++ b/spec/std/array_spec.cr @@ -960,11 +960,51 @@ describe "Array" do end end - it "does replace" do - a = [1, 2, 3] - b = [1] - b.replace a - b.should eq(a) + describe "#replace" do + it "replaces all elements" do + a = [1, 2, 3] + b = [4, 5, 6] + a.replace(b).should be(a) + a.should eq(b) + end + + it "reuses the buffer if possible" do + a = [1, 2, 3, 4, 5] + a.shift + b = [6, 7, 8, 9, 10] + a.replace(b).should be(a) + a.should eq(b) + a.@capacity.should eq(5) + a.@offset_to_buffer.should eq(0) + + a = [1, 2, 3, 4, 5] + a.shift(2) + b = [6, 7, 8, 9] + a.replace(b).should be(a) + a.should eq(b) + a.@capacity.should eq(5) + a.@offset_to_buffer.should eq(1) + end + + it "resizes the buffer if capacity is not enough" do + a = [1, 2, 3, 4, 5] + b = [6, 7, 8, 9, 10, 11] + a.replace(b).should be(a) + a.should eq(b) + a.@capacity.should eq(10) + a.@offset_to_buffer.should eq(0) + end + + it "clears unused elements if new size is smaller" do + a = [1, 2, 3, 4, 5] + b = [6, 7, 8] + a.replace(b).should be(a) + a.should eq(b) + a.@capacity.should eq(5) + a.@offset_to_buffer.should eq(0) + a.unsafe_fetch(3).should eq(0) + a.unsafe_fetch(4).should eq(0) + end end it "does reverse with an odd number of elements" do diff --git a/src/array.cr b/src/array.cr index 7b283aaf13f2..cd8341fd63f3 100644 --- a/src/array.cr +++ b/src/array.cr @@ -1392,9 +1392,17 @@ class Array(T) # a2 # => [1, 2, 3] # ``` def replace(other : Array) : self - @size = other.size - resize_to_capacity(Math.pw2ceil(@size)) if @size > @capacity + if other.size > @capacity + reset_buffer_to_root_buffer + resize_to_capacity(calculate_new_capacity(other.size)) + elsif other.size > remaining_capacity + shift_buffer_by(remaining_capacity - other.size) + elsif other.size < @size + (@buffer + other.size).clear(@size - other.size) + end + @buffer.copy_from(other.to_unsafe, other.size) + @size = other.size self end @@ -2051,6 +2059,7 @@ class Array(T) @capacity - @offset_to_buffer end + # behaves like `calculate_new_capacity(@capacity + 1)` private def calculate_new_capacity return INITIAL_CAPACITY if @capacity == 0 @@ -2061,6 +2070,18 @@ class Array(T) end end + private def calculate_new_capacity(new_size) + new_capacity = @capacity == 0 ? INITIAL_CAPACITY : @capacity + while new_capacity < new_size + if new_capacity < CAPACITY_THRESHOLD + new_capacity *= 2 + else + new_capacity += (new_capacity + 3 * CAPACITY_THRESHOLD) // 4 + end + end + new_capacity + end + private def increase_capacity resize_to_capacity(calculate_new_capacity) end