require "spec" private lib LibPointerSpec type A = Void type B = Void end private def reset(p1, p2) p1.value = 10 p2.value = 20 end describe "Pointer" do it "does malloc with value" do p1 = Pointer.malloc(4, 1) 4.times do |i| p1[i].should eq(1) end end it "does malloc with value from block" do p1 = Pointer.malloc(4) { |i| i } 4.times do |i| p1[i].should eq(i) end end it "does index with count" do p1 = Pointer.malloc(4) { |i| i ** 2 } p1.to_slice(4).index(4).should eq(2) p1.to_slice(4).index(5).should be_nil end describe "copy_from" do it "performs" do p1 = Pointer.malloc(4) { |i| i } p2 = Pointer.malloc(4) { 0 } p2.copy_from(p1, 4) 4.times do |i| p2[i].should eq(p1[i]) end end it "raises on negative count" do p1 = Pointer.malloc(4, 0) expect_raises(ArgumentError, "Negative count") do p1.copy_from(p1, -1) end end it "copies from union of pointers" do p1 = Pointer.malloc(4, 1) p2 = Pointer.malloc(4, 1.5) p3 = Pointer.malloc(4, 0 || 0.0) p3.copy_from(p1 || p2, 4) 4.times { |i| p3[i].should eq(p1[i]) } end end describe "realloc" do it "raises on negative count" do p1 = Pointer(Int32).new(123) expect_raises(ArgumentError) do p1.realloc(-1) end end end describe "copy_to" do it "performs" do p1 = Pointer.malloc(4) { |i| i } p2 = Pointer.malloc(4) { 0 } p1.copy_to(p2, 4) 4.times do |i| p2[i].should eq(p1[i]) end end it "raises on negative count" do p1 = Pointer.malloc(4, 0) expect_raises(ArgumentError, "Negative count") do p1.copy_to(p1, -1) end end it "copies to union of pointers" do p1 = Pointer.malloc(4, 1) p2 = Pointer.malloc(4, 0 || 1.5) p3 = Pointer.malloc(4, 0 || 'a') p1.copy_to(p2 || p3, 4) 4.times { |i| p2[i].should eq(p1[i]) } end it "doesn't raise OverflowError on unsigned size and different target type" do p1 = Pointer.malloc(4, 1) p2 = Pointer.malloc(4, 0 || nil) p1.copy_to(p2, 4_u32) 4.times { |i| p2[i].should eq(p1[i]) } end end describe "move_from" do it "performs with overlap right to left" do p1 = Pointer.malloc(4) { |i| i } (p1 + 1).move_from(p1 + 2, 2) p1[0].should eq(0) p1[1].should eq(2) p1[2].should eq(3) p1[3].should eq(3) end it "performs with overlap left to right" do p1 = Pointer.malloc(4) { |i| i } (p1 + 2).move_from(p1 + 1, 2) p1[0].should eq(0) p1[1].should eq(1) p1[2].should eq(1) p1[3].should eq(2) end it "raises on negative count" do p1 = Pointer.malloc(4, 0) expect_raises(ArgumentError, "Negative count") do p1.move_from(p1, -1) end end it "moves from union of pointers" do p1 = Pointer.malloc(4, 1) p2 = Pointer.malloc(4, 1.5) p3 = Pointer.malloc(4, 0 || 0.0) p3.move_from(p1 || p2, 4) 4.times { |i| p3[i].should eq(p1[i]) } end end describe "move_to" do it "performs with overlap right to left" do p1 = Pointer.malloc(4) { |i| i } (p1 + 2).move_to(p1 + 1, 2) p1[0].should eq(0) p1[1].should eq(2) p1[2].should eq(3) p1[3].should eq(3) end it "performs with overlap left to right" do p1 = Pointer.malloc(4) { |i| i } (p1 + 1).move_to(p1 + 2, 2) p1[0].should eq(0) p1[1].should eq(1) p1[2].should eq(1) p1[3].should eq(2) end it "raises on negative count" do p1 = Pointer.malloc(4, 0) expect_raises(ArgumentError, "Negative count") do p1.move_to(p1, -1) end end it "moves to union of pointers" do p1 = Pointer.malloc(4, 1) p2 = Pointer.malloc(4, 0 || 1.5) p3 = Pointer.malloc(4, 0 || 'a') p1.move_to(p2 || p3, 4) 4.times { |i| p2[i].should eq(p1[i]) } end end describe "memcmp" do it do p1 = Pointer.malloc(4) { |i| i } p2 = Pointer.malloc(4) { |i| i } p3 = Pointer.malloc(4) { |i| i + 1 } p1.memcmp(p2, 4).should eq(0) p1.memcmp(p3, 4).should be < 0 p3.memcmp(p1, 4).should be > 0 end end it "compares two pointers by address" do p1 = Pointer(Int32).malloc(1) p2 = Pointer(Int32).malloc(1) p1.should eq(p1) p1.should_not eq(p2) p1.should_not eq(1) end it "does to_s" do Pointer(Int32).null.to_s.should eq("Pointer(Int32).null") Pointer(Int32).new(1234_u64).to_s.should eq("Pointer(Int32)@0x4d2") end it "doesn't confuse lib typedefs (#16686)" do Pointer(LibPointerSpec::A).null.inspect.should eq "Pointer(LibPointerSpec::A).null" Pointer(LibPointerSpec::B).null.inspect.should eq "Pointer(LibPointerSpec::B).null" end it "creates from int" do Pointer(Int32).new(1234).address.should eq(1234) end it "performs arithmetic with u64" do p = Pointer(Int8).new(1234) d = 4_u64 (p + d).address.should eq(1238) (p - d).address.should eq(1230) p = Pointer(Int8).new(UInt64::MAX) d = UInt64::MAX - 1 (p - d).address.should eq(1) end it "performs arithmetic with u32" do p = Pointer(Int8).new(1234) d = 4_u32 (p + d).address.should eq(1238) (p - d).address.should eq(1230) end it "shuffles!" do a = Pointer(Int32).malloc(3) { |i| i + 1 } a.shuffle!(3) (a[0] + a[1] + a[2]).should eq(6) 3.times do |i| a.to_slice(3).should contain(i + 1) end end it "maps!" do a = Pointer(Int32).malloc(3) { |i| i + 1 } a.map!(3) { |i| i + 1 } a[0].should eq(2) a[1].should eq(3) a[2].should eq(4) end it "maps_with_index!" do a = Pointer(Int32).malloc(3) { |i| i + 1 } a.map_with_index!(3) { |e, i| e + i } a[0].should eq(1) a[1].should eq(3) a[2].should eq(5) end it "maps_with_index!, with offset" do a = Pointer(Int32).malloc(3) { |i| i + 1 } a.map_with_index!(3, offset: 10) { |e, i| e + i } a[0].should eq(11) a[1].should eq(13) a[2].should eq(15) end describe "#fill" do it "int" do slice = Slice[0, 1, 2, 3, 4] ptr = slice.to_unsafe + 1 ptr.fill(3, 7) slice.should eq Slice[0, 7, 7, 7, 4] end it "string" do slice = Slice["a", "b", "c", "d", "e"] ptr = slice.to_unsafe + 1 ptr.fill(3, " ") slice.should eq Slice["a", " ", " ", " ", "e"] end it "pointer" do slice = Slice[Pointer(Void).new(0x1_u64), Pointer(Void).new(0x2_u64), Pointer(Void).new(0x3_u64), Pointer(Void).new(0x4_u64), Pointer(Void).new(0x5_u64)] ptr = slice.to_unsafe + 1 ptr.fill(3, Pointer(Void).new(0x10_u64)) slice.should eq Slice[Pointer(Void).new(0x1_u64), Pointer(Void).new(0x10_u64), Pointer(Void).new(0x10_u64), Pointer(Void).new(0x10_u64), Pointer(Void).new(0x5_u64)] end describe "yielding" do it "int" do slice = Slice[1, 1, 1, 1, 1] ptr = slice.to_unsafe + 1 ptr.fill(3) { |i| i * i } slice.should eq Slice[1, 0, 1, 4, 1] ptr.fill(3, offset: 3) { |i| i * i } slice.should eq Slice[1, 9, 16, 25, 1] end end end it "raises if mallocs negative size" do expect_raises(ArgumentError) { Pointer.malloc(-1, 0) } end it "copies/move with different types" do p1 = Pointer(Int32).malloc(1) p2 = Pointer(Int32 | String).malloc(1) reset p1, p2 p1.copy_from(p1, 1) p1.value.should eq(10) # p1.copy_from(p2, 10) # invalid reset p1, p2 p2.copy_from(p1, 1) p2.value.should eq(10) reset p1, p2 p2.copy_from(p2, 1) p2.value.should eq(20) reset p1, p2 p1.move_from(p1, 1) p1.value.should eq(10) # p1.move_from(p2, 10) # invalid reset p1, p2 p2.move_from(p1, 1) p2.value.should eq(10) reset p1, p2 p2.move_from(p2, 1) p2.value.should eq(20) # --- reset p1, p2 p1.copy_to(p1, 1) p1.value.should eq(10) reset p1, p2 p1.copy_to(p2, 1) p2.value.should eq(10) # p2.copy_to(p1, 10) # invalid reset p1, p2 p2.copy_to(p2, 1) p2.value.should eq(20) reset p1, p2 p1.move_to(p1, 1) p1.value.should eq(10) reset p1, p2 p1.move_to(p2, 1) p2.value.should eq(10) # p2.move_to(p1, 10) # invalid reset p1, p2 p2.move_to(p2, 1) p2.value.should eq(20) end describe "clear" do it "clears one" do ptr = Pointer(Int32).malloc(2) ptr[0] = 10 ptr[1] = 20 ptr.clear ptr[0].should eq(0) ptr[1].should eq(20) end it "clears many" do ptr = Pointer(Int32).malloc(4) ptr[0] = 10 ptr[1] = 20 ptr[2] = 30 ptr[3] = 40 ptr.clear(2) ptr[0].should eq(0) ptr[1].should eq(0) ptr[2].should eq(30) ptr[3].should eq(40) end it "clears with union" do ptr = Pointer(Int32 | Nil).malloc(4) ptr[0] = 10 ptr[1] = 20 ptr[2] = 30 ptr[3] = 0 ptr.clear(2) ptr[0].should be_nil ptr[1].should be_nil ptr[2].should eq(30) ptr[3].should eq(0) ptr[3].should_not be_nil end end it "does !" do (!Pointer(Int32).null).should be_true (!Pointer(Int32).new(123)).should be_false end it "clones" do ptr = Pointer(Int32).new(123) ptr.clone.should eq(ptr) end it "aligns small pointers using #align_down and #align_up" do ptr = Pointer(Void).new(0x30_u64) ptr.align_down(16).should eq(Pointer(Void).new(0x30_u64)) ptr.align_down(32).should eq(Pointer(Void).new(0x20_u64)) ptr = Pointer(Void).new(0x30_u64) ptr.align_up(16).should eq(Pointer(Void).new(0x30_u64)) ptr.align_up(32).should eq(Pointer(Void).new(0x40_u64)) ptr = Pointer(Void).new(1_u64) ptr.align_up(1024).should eq(Pointer(Void).new(1024_u64)) ptr.align_down(1024).should eq(Pointer(Void).new(0_u64)) ptr = Pointer(Void).new(0xDEADC0DE_u64) ptr.align_up(1u64 << 29).address.should eq(0xE0000000_u64) ptr.align_down(1u64 << 29).address.should eq(0xC0000000_u64) ptr = Pointer(Void).new(0xBADCAB1E_u64) ptr.align_up(1u64 << 32).address.should eq({% if flag?(:bits32) %} 0u64 {% else %} 1u64 << 32 {% end %}) ptr.align_down(1u64 << 32).address.should eq(0_u64) ptr.align_up(1u64 << 40).address.should eq({% if flag?(:bits32) %} 0u64 {% else %} 1u64 << 40 {% end %}) ptr.align_down(1u64 << 40).address.should eq(0_u64) end it "only aligns pointers on byte boundaries using #align_down and #align_up" do ptr = Pointer(UInt128).new(10_u64) ptr.align_up(4).should eq(Pointer(UInt128).new(12_u64)) ptr.align_down(4).should eq(Pointer(UInt128).new(8_u64)) ptr.align_up(16).should eq(Pointer(UInt128).new(16_u64)) ptr.align_down(16).should eq(Pointer(UInt128).new(0_u64)) ptr = Pointer(UInt32).new(3_u64) ptr.align_down(2).should eq(Pointer(UInt32).new(2_u64)) ptr = Pointer(UInt32).new(1_u64) ptr.align_up(2).should eq(Pointer(UInt32).new(2_u64)) end it "correctly wraps around zero using #align_up" do # On 32 bit platforms, this results in a Pointer with address UInt32::MAX (truncated) ptr = Pointer(Void*).new(&-1_u64) ptr.align_up(1).address.should eq({% if flag?(:bits32) %} (&-1u32).to_u64 {% else %} &-1u64 {% end %}) ptr.align_up(2).address.should eq(0_u64) ptr.align_up(1u64 << 16).address.should eq(0_u64) end {% if flag?(:bits64) %} it "correctly aligns pointers using operands exceeding 32-bit range using #align_down and #align_up" do ptr = Pointer(Void).new(0xDECAFFEDC0FFEEEE_u64) # align to 48 bits (281_474_976_710_656 byte boundary) ptr.align_down(1u64 << 48).address.should eq(0xDECA000000000000_u64) ptr.align_up(1u64 << 48).address.should eq(0xDECB000000000000_u64) # align to 63 bits ptr = Pointer(Void).new((1u64 << 63) + 1) ptr.align_down(1u64 << 63).address.should eq(1u64 << 63) ptr.align_up(1u64 << 63).address.should eq(0_u64) ptr = Pointer(Void).new((1u64 << 63) - 1) ptr.align_down(1u64 << 63).address.should eq(0_u64) ptr.align_up(1u64 << 63).address.should eq(1u64 << 63) end {% end %} {% if flag?(:bits32) %} it "raises on copy_from with size bigger than UInt32::MAX" do ptr = Pointer(Int32).new(123) expect_raises(ArgumentError) do ptr.copy_from(ptr, UInt32::MAX.to_u64 + 1) end end it "raises on move_from with size bigger than UInt32::MAX" do ptr = Pointer(Int32).new(123) expect_raises(ArgumentError) do ptr.move_from(ptr, UInt32::MAX.to_u64 + 1) end end it "raises on clear with size bigger than UInt32::MAX" do ptr = Pointer(Int32).new(123) expect_raises(ArgumentError) do ptr.clear(UInt32::MAX.to_u64 + 1) end end {% end %} end