Skip to content

Commit ab4a68e

Browse files
author
Ary Borenszweig
committed
Pointer: make copy_from/copy_to/move_from/move_to work well with unions of pointers. Fixes #3775
1 parent 4cc34b6 commit ab4a68e

File tree

3 files changed

+79
-28
lines changed

3 files changed

+79
-28
lines changed

spec/std/array_spec.cr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ describe "Array" do
396396
a.concat(1..4)
397397
a.@capacity.should eq(6)
398398
end
399+
400+
it "concats a union of arrays" do
401+
a = [1, '2']
402+
a.concat([3] || ['4'])
403+
a.should eq([1, '2', 3])
404+
end
399405
end
400406

401407
describe "delete" do

spec/std/pointer_spec.cr

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe "Pointer" do
3232
p2 = Pointer.malloc(4) { 0 }
3333
p2.copy_from(p1, 4)
3434
4.times do |i|
35-
p2[0].should eq(p1[0])
35+
p2[i].should eq(p1[i])
3636
end
3737
end
3838

@@ -42,6 +42,14 @@ describe "Pointer" do
4242
p1.copy_from(p1, -1)
4343
end
4444
end
45+
46+
it "copies from union of pointers" do
47+
p1 = Pointer.malloc(4, 1)
48+
p2 = Pointer.malloc(4, 1.5)
49+
p3 = Pointer.malloc(4, 0 || 0.0)
50+
p3.copy_from(p1 || p2, 4)
51+
4.times { |i| p3[i].should eq(p1[i]) }
52+
end
4553
end
4654

4755
describe "copy_to" do
@@ -50,7 +58,7 @@ describe "Pointer" do
5058
p2 = Pointer.malloc(4) { 0 }
5159
p1.copy_to(p2, 4)
5260
4.times do |i|
53-
p2[0].should eq(p1[0])
61+
p2[i].should eq(p1[i])
5462
end
5563
end
5664

@@ -60,6 +68,14 @@ describe "Pointer" do
6068
p1.copy_to(p1, -1)
6169
end
6270
end
71+
72+
it "copies to union of pointers" do
73+
p1 = Pointer.malloc(4, 1)
74+
p2 = Pointer.malloc(4, 0 || 1.5)
75+
p3 = Pointer.malloc(4, 0 || 'a')
76+
p1.copy_to(p2 || p3, 4)
77+
4.times { |i| p2[i].should eq(p1[i]) }
78+
end
6379
end
6480

6581
describe "move_from" do
@@ -87,6 +103,14 @@ describe "Pointer" do
87103
p1.move_from(p1, -1)
88104
end
89105
end
106+
107+
it "moves from union of pointers" do
108+
p1 = Pointer.malloc(4, 1)
109+
p2 = Pointer.malloc(4, 1.5)
110+
p3 = Pointer.malloc(4, 0 || 0.0)
111+
p3.move_from(p1 || p2, 4)
112+
4.times { |i| p3[i].should eq(p1[i]) }
113+
end
90114
end
91115

92116
describe "move_to" do
@@ -114,6 +138,14 @@ describe "Pointer" do
114138
p1.move_to(p1, -1)
115139
end
116140
end
141+
142+
it "moves to union of pointers" do
143+
p1 = Pointer.malloc(4, 1)
144+
p2 = Pointer.malloc(4, 0 || 1.5)
145+
p3 = Pointer.malloc(4, 0 || 'a')
146+
p1.move_to(p2 || p3, 4)
147+
4.times { |i| p2[i].should eq(p1[i]) }
148+
end
117149
end
118150

119151
describe "memcmp" do

src/pointer.cr

Lines changed: 39 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -146,16 +146,7 @@ struct Pointer(T)
146146
# ptr1[3] # => 4
147147
# ```
148148
def copy_from(source : Pointer(T), count : Int)
149-
raise ArgumentError.new("negative count") if count < 0
150-
151-
if self.class == source.class
152-
Intrinsics.memcpy(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false)
153-
else
154-
while (count -= 1) >= 0
155-
self[count] = source[count]
156-
end
157-
end
158-
self
149+
source.copy_to(self, count)
159150
end
160151

161152
# :nodoc:
@@ -186,7 +177,7 @@ struct Pointer(T)
186177
# ptr2[3] # => 14
187178
# ```
188179
def copy_to(target : Pointer, count : Int)
189-
target.copy_from(self, count)
180+
target.copy_from_impl(self, count)
190181
end
191182

192183
# Copies *count* elements from *source* into *self*.
@@ -207,20 +198,7 @@ struct Pointer(T)
207198
# ptr1[3] # => 3
208199
# ```
209200
def move_from(source : Pointer(T), count : Int)
210-
raise ArgumentError.new("negative count") if count < 0
211-
212-
if self.class == source.class
213-
Intrinsics.memmove(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false)
214-
else
215-
if source.address < address
216-
copy_from source, count
217-
else
218-
count.times do |i|
219-
self[i] = source[i]
220-
end
221-
end
222-
end
223-
self
201+
source.move_to(self, count)
224202
end
225203

226204
# :nodoc:
@@ -250,7 +228,42 @@ struct Pointer(T)
250228
# ptr1[3] # => 3
251229
# ```
252230
def move_to(target : Pointer, count : Int)
253-
target.move_from(self, count)
231+
target.move_from_impl(self, count)
232+
end
233+
234+
# We use separate method in which we make sure that `source`
235+
# is never a union of pointers. This is guaranteed because both
236+
# copy_from/move_from/copy_to/move_to reverse self and caller,
237+
# and so if either self or the arguments are unions a dispatch
238+
# will happen and unions will disappear.
239+
protected def copy_from_impl(source : Pointer(T), count : Int)
240+
raise ArgumentError.new("negative count") if count < 0
241+
242+
if self.class == source.class
243+
Intrinsics.memcpy(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false)
244+
else
245+
while (count -= 1) >= 0
246+
self[count] = source[count]
247+
end
248+
end
249+
self
250+
end
251+
252+
protected def move_from_impl(source : Pointer(T), count : Int)
253+
raise ArgumentError.new("negative count") if count < 0
254+
255+
if self.class == source.class
256+
Intrinsics.memmove(self.as(Void*), source.as(Void*), (count * sizeof(T)).to_u32, 0_u32, false)
257+
else
258+
if source.address < address
259+
copy_from source, count
260+
else
261+
count.times do |i|
262+
self[i] = source[i]
263+
end
264+
end
265+
end
266+
self
254267
end
255268

256269
# Compares *count* elements from this pointer and *other*, byte by byte.

0 commit comments

Comments
 (0)