# coding: utf-8 # # This file is part of ruby-ffi. # For licensing, see LICENSE.SPECS # require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) describe "MemoryPointer" do it "makes a pointer from a string" do m = FFI::MemoryPointer.from_string("FFI is Awesome") expect(m.total).to eq(15) expect(m.type_size).to eq(1) end it "does not make a pointer from non-strings" do expect { FFI::MemoryPointer.from_string(nil) }.to raise_error(TypeError) end it "makes a pointer from a string with multibyte characters" do m = FFI::MemoryPointer.from_string("ぱんだ") expect(m.total).to eq(10) expect(m.type_size).to eq(1) end it "reads back a string" do m = FFI::MemoryPointer.from_string("FFI is Awesome") expect(m.read_string).to eq("FFI is Awesome") end it "reads back an empty string" do expect(FFI::Pointer::NULL.read_string(0)).to eq('') expect(FFI::Pointer::NULL.read_string(0).encoding).to eq(Encoding::BINARY) end it "makes a pointer for a certain number of bytes" do m = FFI::MemoryPointer.new(8) m.write_array_of_int([1,2]) expect(m.read_array_of_int(2)).to eq([1,2]) end it "allows access to an element of the pointer (as an array)" do m = FFI::MemoryPointer.new(:int, 2) m.write_array_of_int([1,2]) expect(m[0].read_int).to eq(1) expect(m[1].read_int).to eq(2) end it "allows writing as an int" do m = FFI::MemoryPointer.new(:int) m.write_int(1) expect(m.read_int).to eq(1) expect(m.read :int).to eq(1) expect(m.read FFI::Type::INT).to eq(1) end it "allows writing as a sized int" do m = FFI::MemoryPointer.new(:uint32) m.write_uint32(1) expect(m.read_uint32).to eq(1) expect(m.read :uint32).to eq(1) expect(m.read FFI::Type::UINT32).to eq(1) m = FFI::MemoryPointer.new(:uint32) m.write :uint32, 1 expect(m.read :uint32).to eq(1) m = FFI::MemoryPointer.new(:int64) m.write_int64(1) expect(m.read_int64).to eq(1) expect(m.read :int64).to eq(1) expect(m.read FFI::Type::INT64).to eq(1) m = FFI::MemoryPointer.new(:int64) m.write :int64, 1 expect(m.read :int64).to eq(1) end it "allows writing as a long" do m = FFI::MemoryPointer.new(:long) m.write_long(10) expect(m.read_long).to eq(10) expect(m.read :long).to eq(10) expect(m.read FFI::Type::LONG).to eq(10) m.write :long, 10 expect(m.read :long).to eq(10) end it "allows writing as a size_t" do m = FFI::MemoryPointer.new(:size_t) m.write(:size_t, 10) expect(m.read :size_t).to eq(10) end it "allows writing as a bool" do m = FFI::MemoryPointer.new(:bool) m.write(:bool, true) expect(m.read :bool).to eq(true) expect(m.read FFI::Type::BOOL).to eq(true) m.write(:bool, false) expect(m.read :bool).to eq(false) expect(m.read FFI::Type::BOOL).to eq(false) end it "allows writing a custom typedef" do FFI.typedef :uint, :fubar_t FFI.typedef :size_t, :fubar2_t m = FFI::MemoryPointer.new(:fubar_t) m.write(:fubar_t, 10) expect(m.read :fubar_t).to eq(10) m = FFI::MemoryPointer.new(:fubar2_t) m.write(:fubar2_t, 10) expect(m.read :fubar2_t).to eq(10) end it "raises an error if you try to read an undefined type" do m = FFI::MemoryPointer.new(:long) expect { m.read(:undefined_type) }.to raise_error(ArgumentError) end it "raises an error if you try putting a long into a pointer of size 1" do m = FFI::MemoryPointer.new(1) expect { m.write_long(10) }.to raise_error(IndexError) end it "raises an error if you try putting an int into a pointer of size 1" do m = FFI::MemoryPointer.new(1) expect { m.write_int(10) }.to raise_error(IndexError) end # it "does not raise IndexError for opaque pointers" do # m = FFI::MemoryPointer.new(8) # p2 = FFI::MemoryPointer.new(1024) # m.write_long(p2.address) # p = m.read_pointer # lambda { p.write_int(10) }.should_not raise_error # end it "makes a pointer for a certain type" do m = FFI::MemoryPointer.new(:int) m.write_int(10) expect(m.read_int).to eq(10) end it "makes a memory pointer for a number of a certain type" do m = FFI::MemoryPointer.new(:int, 2) m.write_array_of_int([1,2]) expect(m.read_array_of_int(2)).to eq([1,2]) end it "makes a pointer for an object responding to #size" do m = FFI::MemoryPointer.new(Struct.new(:size).new(8)) m.write_array_of_int([1,2]) expect(m.read_array_of_int(2)).to eq([1,2]) end it "makes a pointer for a number of an object responding to #size" do m = FFI::MemoryPointer.new(Struct.new(:size).new(4), 2) m.write_array_of_int([1,2]) expect(m.read_array_of_int(2)).to eq([1,2]) end it "MemoryPointer#address returns correct value" do m = FFI::MemoryPointer.new(:long_long) magic = 0x12345678 m.write_long(magic) expect(m.read_pointer.address).to eq(magic) end it "MemoryPointer#null? returns true for zero value" do m = FFI::MemoryPointer.new(:long_long) m.write_long(0) expect(m.read_pointer.null?).to be true end it "MemoryPointer#null? returns false for non-zero value" do m = FFI::MemoryPointer.new(:long_long) m.write_long(0x12345678) expect(m.read_pointer.null?).to be false end it "initialize with block should execute block" do block_executed = false FFI::MemoryPointer.new(:pointer) do |ptr| block_executed = true end expect(block_executed).to be true end it "has a memsize function", skip: RUBY_ENGINE != "ruby" do base_size = ObjectSpace.memsize_of(Object.new) pointer = FFI::MemoryPointer.from_string("FFI is Awesome") size = ObjectSpace.memsize_of(pointer) expect(size).to be > base_size end end