summaryrefslogtreecommitdiff
path: root/spec/ffi/rbx/memory_pointer_spec.rb
blob: 2db89a94c3b31484ab73a4eaa7165f56dfb5a37e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# 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