win32-mmap 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
3
+ cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
4
+ MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
5
+ ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
6
+ bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
7
+ Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
8
+ S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
9
+ gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
10
+ FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
11
+ zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
12
+ DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
13
+ nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
14
+ bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
15
+ ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
16
+ tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
17
+ /sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
18
+ wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
19
+ EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
20
+ tGSHgAmcLlkdGgan182qsE/4kKM=
21
+ -----END CERTIFICATE-----
@@ -1,20 +1,20 @@
1
- #######################################################################
2
- # example_mmap_client.rb
3
- #
4
- # This program demonstrates a simple mmap client. You should run this
5
- # program *after* you have run the example server program in a
6
- # separate terminal.
7
- #
8
- # You can run this program via the 'rake example_client' task.
9
- #
10
- # Modify this program as you see fit.
11
- #######################################################################
12
- require 'win32/mmap'
13
- include Win32
14
-
15
- mmap = MMap.open('alpha')
16
-
17
- p mmap.foo
18
- p mmap.bar
19
-
1
+ #######################################################################
2
+ # example_mmap_client.rb
3
+ #
4
+ # This program demonstrates a simple mmap client. You should run this
5
+ # program *after* you have run the example server program in a
6
+ # separate terminal.
7
+ #
8
+ # You can run this program via the 'rake example_client' task.
9
+ #
10
+ # Modify this program as you see fit.
11
+ #######################################################################
12
+ require 'win32/mmap'
13
+ include Win32
14
+
15
+ mmap = MMap.open('alpha')
16
+
17
+ p mmap.foo
18
+ p mmap.bar
19
+
20
20
  mmap.close
@@ -1,25 +1,25 @@
1
- #######################################################################
2
- # example_mmap_file.rb
3
- #
4
- # This sample script demonstrates creating a memory mapped file, then
5
- # later reading from it. You can run this example via the
6
- # 'rake example_file' task.
7
- #
8
- # Modify this program as you see fit.
9
- #######################################################################
10
- require 'win32/mmap'
11
- include Win32
12
-
13
- map1 = MMap.new(:file => "C:\\mmap.test", :size => 1024)
14
-
15
- map1.foo = 'hello'
16
- map1.bar = 77
17
-
18
- map1.close
19
-
20
- map2 = MMap.new(:file => "C:\\mmap.test")
21
-
22
- p map2.foo
23
- p map2.bar
24
-
1
+ #######################################################################
2
+ # example_mmap_file.rb
3
+ #
4
+ # This sample script demonstrates creating a memory mapped file, then
5
+ # later reading from it. You can run this example via the
6
+ # 'rake example_file' task.
7
+ #
8
+ # Modify this program as you see fit.
9
+ #######################################################################
10
+ require 'win32/mmap'
11
+ include Win32
12
+
13
+ map1 = MMap.new(:file => "C:\\mmap.test", :size => 1024)
14
+
15
+ map1.foo = 'hello'
16
+ map1.bar = 77
17
+
18
+ map1.close
19
+
20
+ map2 = MMap.new(:file => "C:\\mmap.test")
21
+
22
+ p map2.foo
23
+ p map2.bar
24
+
25
25
  map2.close
@@ -1,20 +1,20 @@
1
- #######################################################################
2
- # example_mmap_server.rb
3
- #
4
- # A test script for general futzing. Run this in its own terminal
5
- # then run the example_mmap_client.rb program in a separate terminal.
6
- # You can run this program via the 'rake example_server' task.
7
- #
8
- # Modify as you see fit.
9
- #######################################################################
10
- require 'win32/mmap'
11
- include Win32
12
-
13
- mmap = MMap.new(:name => 'alpha', :size => 2000)
14
-
15
- mmap.foo = 'hello'
16
- mmap.bar = 27
17
-
18
- sleep 100
19
-
1
+ #######################################################################
2
+ # example_mmap_server.rb
3
+ #
4
+ # A test script for general futzing. Run this in its own terminal
5
+ # then run the example_mmap_client.rb program in a separate terminal.
6
+ # You can run this program via the 'rake example_server' task.
7
+ #
8
+ # Modify as you see fit.
9
+ #######################################################################
10
+ require 'win32/mmap'
11
+ include Win32
12
+
13
+ mmap = MMap.new(:name => 'alpha', :size => 2000)
14
+
15
+ mmap.foo = 'hello'
16
+ mmap.bar = 27
17
+
18
+ sleep 100
19
+
20
20
  mmap.close
@@ -0,0 +1 @@
1
+ require_relative 'win32/mmap'
@@ -1,406 +1,406 @@
1
- require File.join(File.dirname(__FILE__), 'windows', 'constants')
2
- require File.join(File.dirname(__FILE__), 'windows', 'structs')
3
- require File.join(File.dirname(__FILE__), 'windows', 'functions')
4
-
5
- # The Win32 module serves as a namespace only.
6
- #
7
- module Win32
8
- # The MMap class encapsulates functions for memory mapped files.
9
- #
10
- class MMap
11
- # The version of the win32-mmap library.
12
- VERSION = '0.4.1'
13
-
14
- include Windows::Constants
15
- include Windows::Functions
16
- include Windows::Structs
17
-
18
- # The name of the file from which to create a mapping object. This
19
- # value may be nil.
20
- #
21
- attr_accessor :file
22
-
23
- # The protection for the file view. This may be any of the following
24
- # values:
25
- #
26
- # * PAGE_READONLY
27
- # * PAGE_READWRITE
28
- # * PAGE_WRITECOPY
29
- # * PAGE_WRITECOPY
30
- # * PAGE_EXECUTE_READ
31
- # * PAGE_EXECUTE_READWRITE
32
- #
33
- # You can OR the protection value with any of these section attributes:
34
- #
35
- # * SEC_COMMIT
36
- # * SEC_IMAGE
37
- # * SEC_LARGE_PAGES
38
- # * SEC_NOCACHE
39
- # * SEC_RESERVE
40
- #
41
- # The default protection is PAGE_READWRITE.
42
- #
43
- attr_accessor :protection
44
-
45
- # A string that determines the name of the mapping object.
46
- # By default this value is nil, i.e. anonymous. If you specify a +name+
47
- # that already exists then an attempt is made to access that object.
48
- #
49
- attr_accessor :name
50
-
51
- # The maximum size for the file mapping object. If a +file+ is
52
- # specified, this value defaults to the size of +file+. Otherwise
53
- # it defaults to zero (though you should set it manually).
54
- #
55
- attr_accessor :size
56
-
57
- # Access desired to the file mapping object. This may be
58
- # any of the following values:
59
- #
60
- # * FILE_MAP_WRITE
61
- # * FILE_MAP_READ
62
- # * FILE_MAP_COPY
63
- # * FILE_MAP_ALL_ACCESS
64
- #
65
- # The default access is FILE_MAP_WRITE.
66
- #
67
- attr_accessor :access
68
-
69
- # The address of the file view mapping.
70
- #
71
- attr_reader :address
72
-
73
- # Suggested starting address of mapped view. If this value is not
74
- # specified, the system will choose the base mapping address. If you do
75
- # specify a value, it must be a multiple of the system's allocation
76
- # granularity.
77
- #
78
- # Note: The MSDN documentation recommends that, in most case cases, you
79
- # should not set this value.
80
- #--
81
- # A system's allocation granularity can be found via GetSystemInfo()
82
- # and the dwAllocationGranularity member of the SYSTEM_INFO struct.
83
- #
84
- attr_accessor :base_address
85
-
86
- # Sets whether or not a semaphore lock is automatically placed on the
87
- # mapped view for read and write operations. This is true by default.
88
- #
89
- attr_writer :autolock
90
-
91
- # The value, in milliseconds, used to wait on the semaphore lock. Only
92
- # used if the +autolock+ option is true. The default is 10 milliseconds.
93
- #
94
- attr_accessor :timeout
95
-
96
- # :call-seq:
97
- # MMap.new(opts = {})
98
- # MMap.new(opts = {}){ |address| block }
99
- #
100
- # Creates and returns a new Win32::MMap object. If +file+ is set, then
101
- # that file is used to create the mapping. If a block is provided the
102
- # address is yielded and the mapping object is automatically closed at the
103
- # end of the block.
104
- #
105
- # Accepts any one of the following hash attributes:
106
- #
107
- # * protection
108
- # * size
109
- # * access
110
- # * inherit
111
- # * name
112
- # * base_address
113
- # * autolock
114
- # * timeout
115
- # * file
116
- #
117
- # Please see the documentation on the individual attributes for
118
- # further details. Note that, although these are accessors, they
119
- # *must* be set in the constructor (if set at all). Setting them
120
- # after the call to MMap.new will not have any effect.
121
- #
122
- # == Example
123
- # require 'win32/mmap'
124
- # require 'windows/msvcrt/string'
125
- # include Windows::MSVCRT::String
126
- # include Win32
127
- #
128
- # # Reverse the contents of a file.
129
- # mmap = MMap.new(:file => 'test.txt') do |addr|
130
- # strrev(addr)
131
- # end
132
- #
133
- def initialize(opts = {})
134
- valid = %w[
135
- file name protection access inherit size
136
- base_address open autolock timeout
137
- ]
138
-
139
- @open = nil
140
- @file = nil
141
- @autolock = nil
142
-
143
- # Validate the keys, handle inherit specially.
144
- opts.each{ |key, value|
145
- key = key.to_s.downcase
146
-
147
- unless valid.include?(key)
148
- raise ArgumentError, "Invalid key '#{key}'"
149
- end
150
-
151
- if key == 'inherit'
152
- self.inherit = value # To force inherit= method call
153
- else
154
- instance_variable_set("@#{key}", value)
155
- end
156
- }
157
-
158
- @protection ||= PAGE_READWRITE
159
- @access ||= FILE_MAP_WRITE
160
- @size ||= 0
161
- @inherit ||= nil
162
- @base_address ||= 0
163
- @timeout ||= 10 # Milliseconds
164
-
165
- self.inherit = false unless @inherit
166
-
167
- @hash = {}
168
-
169
- # Anything except an explicit false means the autolock is on.
170
- @autolock = true unless @autolock == false
171
-
172
- @lock_flag = 0 # Internal use only
173
-
174
- if @file
175
- if File.exists?(@file)
176
- fsize = File.size(@file)
177
- raise ArgumentError, 'cannot open 0 byte file' if fsize.zero?
178
- @size = fsize if @size < fsize
179
- end
180
-
181
- rights = GENERIC_READ|GENERIC_WRITE
182
-
183
- @fh = CreateFile(@file, rights, 0, nil, OPEN_ALWAYS, 0, 0)
184
-
185
- if @fh == INVALID_HANDLE_VALUE
186
- raise SystemCallError.new('CreateFile', FFI.errno)
187
- end
188
- else
189
- @fh = INVALID_HANDLE_VALUE
190
- end
191
-
192
- if @open
193
- @mh = OpenFileMapping(@access, @inherit[:bInheritHandle], @name)
194
- raise SystemCallError.new('OpenFileMapping', FFI.errno) if @mh == 0
195
- else
196
- @mh = CreateFileMapping(@fh, @inherit, @protection, 0, @size, @name)
197
- raise SystemCallError.new('CreateFileMapping', FFI.errno) if @mh == 0
198
- end
199
-
200
- if @open
201
- @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
202
- @size = get_view_size()
203
- else
204
- @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
205
- end
206
-
207
- if @address == 0
208
- raise SystemCallError.new('MapviewOfFileEx', FFI.errno)
209
- end
210
-
211
- if @autolock
212
- @semaphore = CreateSemaphore(nil, 1, 1, "#{@name}.ruby_lock")
213
- if @semaphore == 0
214
- raise Error, get_last_error
215
- end
216
- end
217
-
218
- if block_given?
219
- begin
220
- yield @address
221
- ensure
222
- close
223
- end
224
- end
225
-
226
- @address
227
- end
228
-
229
- # Opens an existing file mapping using +name+. You may pass a hash
230
- # of +opts+ as you would to MMap.new. If you don't specify a size
231
- # as part of the +opts+, it will be dynamically determined for you
232
- # (in blocks equal to your system's page size, typically 4k).
233
- #
234
- # This method is otherwise identical to MMap.new.
235
- #--
236
- # This forces MMap.new to use OpenFileMapping() behind the scenes.
237
- #
238
- def self.open(name, opts={}, &block)
239
- opts[:name] = name
240
- opts[:open] = true
241
- self.new(opts, &block)
242
- end
243
-
244
- # Sets whether or not the mapping handle can be inherited
245
- # by child processes.
246
- #--
247
- # If true, we have to create a SECURITY_ATTRIBUTES struct and set
248
- # its nLength member to 12 and its bInheritHandle member to TRUE.
249
- #
250
- def inherit=(bool)
251
- @inherit = SECURITY_ATTRIBUTES.new
252
- @inherit[:nLength] = SECURITY_ATTRIBUTES.size
253
-
254
- if bool
255
- @inherit[:bInheritHandle] = true
256
- else
257
- @inherit[:bInheritHandle] = false
258
- end
259
- end
260
-
261
- # Returns whether or not the mapping handle can be
262
- # inherited by child processes. The default is false.
263
- #
264
- def inherit?
265
- @inherit and @inherit[:bInheritHandle]
266
- end
267
-
268
- # Writes +num_bytes+ to the disk within a mapped view of a file, or to
269
- # the end of the mapping if +num_bytes+ is not specified.
270
- #
271
- def flush(num_bytes = 0)
272
- unless FlushViewOfFile(@address, num_bytes)
273
- SystemCallError.new('FlushViewOfFile', FFI.errno)
274
- end
275
- end
276
-
277
- # Unmaps the file view and closes all open file handles. You should
278
- # always call this when you are finished with the object (when using
279
- # the non-block form).
280
- #
281
- def close
282
- UnmapViewOfFile(@address) if @address
283
- CloseHandle(@fh) if @fh
284
- CloseHandle(@mh) if @mh
285
- ReleaseSemaphore(@semaphore, 1, nil) if @semaphore
286
- end
287
-
288
- # Returns whether or not a semaphore lock is automatically placed on the
289
- # mapped view for read and write operations. This is true by default.
290
- #
291
- def autolock?
292
- @autolock
293
- end
294
-
295
- # Writes a string directly to the underlying file
296
- def write_string(content)
297
- lock_pattern do
298
- ptr = FFI::Pointer.new(:char, @address)
299
- ptr.write_string(content,content.length)
300
- end
301
- end
302
-
303
- # Reads a string of a given length from the beginning of the file
304
- # if no length is given, reads the file with the @size attribute
305
- # of this instance
306
- def read_string(length = @size)
307
- lock_pattern do
308
- FFI::MemoryPointer.new(:char, length)
309
- ptr = FFI::Pointer.new(:char, @address)
310
- ptr.read_string(length)
311
- end
312
- end
313
-
314
- private
315
-
316
- # :stopdoc:
317
-
318
- # This is used to allow dynamic getters and setters between memory
319
- # mapped objects.
320
- #--
321
- # This replaces the getvar/setvar API from 0.1.0.
322
- #
323
- def method_missing(method_id, *args)
324
- method = method_id.id2name
325
- args = args.first if args.length == 1
326
-
327
- if method[-1,1] == '=' # Setter
328
- method.chop!
329
- @hash["#{method}"] = args
330
-
331
- lock_pattern do
332
- instance_variable_set("@#{method}", args)
333
- marshal = Marshal.dump(@hash)
334
- ptr = FFI::Pointer.new(:char, @address)
335
- ptr.write_string(marshal,marshal.length)
336
- end
337
-
338
- else # Getter
339
-
340
- lock_pattern do
341
- buf = FFI::MemoryPointer.new(:char, @size)
342
- ptr = FFI::Pointer.new(:char, @address)
343
- buf = ptr.read_string(@size)
344
- hash = Marshal.load(buf)
345
- val = hash["#{method}"]
346
- instance_variable_set("@#{method}", val)
347
- end
348
-
349
- return instance_variable_get("@#{method}")
350
- end
351
- end
352
-
353
- def lock_pattern
354
- if @autolock
355
- if mmap_lock
356
- output = yield
357
- mmap_unlock
358
- output
359
- end
360
- else
361
- output = yield
362
- output
363
- end
364
- end
365
-
366
- # Adds a semaphore lock the mapping. Only used if +autolock+ is set
367
- # to true in the constructor.
368
- #
369
- def mmap_lock
370
- bool = false
371
-
372
- if(@lock_flag == 0)
373
- if WaitForSingleObject(@semaphore, @timeout) == WAIT_OBJECT_0
374
- bool = true
375
- end
376
- end
377
-
378
- @lock_flag += 1
379
- bool
380
- end
381
-
382
- # Releases a semaphore lock on the view. Only used if +autolock+ is
383
- # set to true in the constructor.
384
- #
385
- def mmap_unlock
386
- @lock_flag -= 1
387
-
388
- return false if @lock_flag != 0
389
-
390
- if ReleaseSemaphore(@semaphore, 1, nil) == 0
391
- raise SystemCallError.new('ReleaseSemaphore', FFI.errno)
392
- end
393
-
394
- true
395
- end
396
-
397
- # Gets the size of an existing mapping based on the address. This
398
- # is used by the MMap.open method when a size isn't specified.
399
- #
400
- def get_view_size
401
- mbi = MEMORY_BASIC_INFORMATION.new
402
- VirtualQuery(@address, mbi, mbi.size)
403
- mbi[:RegionSize]
404
- end
405
- end # MMap
406
- end # Win32
1
+ require_relative 'windows/constants'
2
+ require_relative 'windows/structs'
3
+ require_relative 'windows/functions'
4
+
5
+ # The Win32 module serves as a namespace only.
6
+ #
7
+ module Win32
8
+ # The MMap class encapsulates functions for memory mapped files.
9
+ #
10
+ class MMap
11
+ # The version of the win32-mmap library.
12
+ VERSION = '0.4.2'
13
+
14
+ include Windows::Constants
15
+ include Windows::Functions
16
+ include Windows::Structs
17
+
18
+ # The name of the file from which to create a mapping object. This
19
+ # value may be nil.
20
+ #
21
+ attr_accessor :file
22
+
23
+ # The protection for the file view. This may be any of the following
24
+ # values:
25
+ #
26
+ # * PAGE_READONLY
27
+ # * PAGE_READWRITE
28
+ # * PAGE_WRITECOPY
29
+ # * PAGE_WRITECOPY
30
+ # * PAGE_EXECUTE_READ
31
+ # * PAGE_EXECUTE_READWRITE
32
+ #
33
+ # You can OR the protection value with any of these section attributes:
34
+ #
35
+ # * SEC_COMMIT
36
+ # * SEC_IMAGE
37
+ # * SEC_LARGE_PAGES
38
+ # * SEC_NOCACHE
39
+ # * SEC_RESERVE
40
+ #
41
+ # The default protection is PAGE_READWRITE.
42
+ #
43
+ attr_accessor :protection
44
+
45
+ # A string that determines the name of the mapping object.
46
+ # By default this value is nil, i.e. anonymous. If you specify a +name+
47
+ # that already exists then an attempt is made to access that object.
48
+ #
49
+ attr_accessor :name
50
+
51
+ # The maximum size for the file mapping object. If a +file+ is
52
+ # specified, this value defaults to the size of +file+. Otherwise
53
+ # it defaults to zero (though you should set it manually).
54
+ #
55
+ attr_accessor :size
56
+
57
+ # Access desired to the file mapping object. This may be
58
+ # any of the following values:
59
+ #
60
+ # * FILE_MAP_WRITE
61
+ # * FILE_MAP_READ
62
+ # * FILE_MAP_COPY
63
+ # * FILE_MAP_ALL_ACCESS
64
+ #
65
+ # The default access is FILE_MAP_WRITE.
66
+ #
67
+ attr_accessor :access
68
+
69
+ # The address of the file view mapping.
70
+ #
71
+ attr_reader :address
72
+
73
+ # Suggested starting address of mapped view. If this value is not
74
+ # specified, the system will choose the base mapping address. If you do
75
+ # specify a value, it must be a multiple of the system's allocation
76
+ # granularity.
77
+ #
78
+ # Note: The MSDN documentation recommends that, in most case cases, you
79
+ # should not set this value.
80
+ #--
81
+ # A system's allocation granularity can be found via GetSystemInfo()
82
+ # and the dwAllocationGranularity member of the SYSTEM_INFO struct.
83
+ #
84
+ attr_accessor :base_address
85
+
86
+ # Sets whether or not a semaphore lock is automatically placed on the
87
+ # mapped view for read and write operations. This is true by default.
88
+ #
89
+ attr_writer :autolock
90
+
91
+ # The value, in milliseconds, used to wait on the semaphore lock. Only
92
+ # used if the +autolock+ option is true. The default is 10 milliseconds.
93
+ #
94
+ attr_accessor :timeout
95
+
96
+ # :call-seq:
97
+ # MMap.new(opts = {})
98
+ # MMap.new(opts = {}){ |address| block }
99
+ #
100
+ # Creates and returns a new Win32::MMap object. If +file+ is set, then
101
+ # that file is used to create the mapping. If a block is provided the
102
+ # address is yielded and the mapping object is automatically closed at the
103
+ # end of the block.
104
+ #
105
+ # Accepts any one of the following hash attributes:
106
+ #
107
+ # * protection
108
+ # * size
109
+ # * access
110
+ # * inherit
111
+ # * name
112
+ # * base_address
113
+ # * autolock
114
+ # * timeout
115
+ # * file
116
+ #
117
+ # Please see the documentation on the individual attributes for
118
+ # further details. Note that, although these are accessors, they
119
+ # *must* be set in the constructor (if set at all). Setting them
120
+ # after the call to MMap.new will not have any effect.
121
+ #
122
+ # == Example
123
+ # require 'win32/mmap'
124
+ # require 'windows/msvcrt/string'
125
+ # include Windows::MSVCRT::String
126
+ # include Win32
127
+ #
128
+ # # Reverse the contents of a file.
129
+ # mmap = MMap.new(:file => 'test.txt') do |addr|
130
+ # strrev(addr)
131
+ # end
132
+ #
133
+ def initialize(opts = {})
134
+ valid = %w[
135
+ file name protection access inherit size
136
+ base_address open autolock timeout
137
+ ]
138
+
139
+ @open = nil
140
+ @file = nil
141
+ @autolock = nil
142
+
143
+ # Validate the keys, handle inherit specially.
144
+ opts.each{ |key, value|
145
+ key = key.to_s.downcase
146
+
147
+ unless valid.include?(key)
148
+ raise ArgumentError, "Invalid key '#{key}'"
149
+ end
150
+
151
+ if key == 'inherit'
152
+ self.inherit = value # To force inherit= method call
153
+ else
154
+ instance_variable_set("@#{key}", value)
155
+ end
156
+ }
157
+
158
+ @protection ||= PAGE_READWRITE
159
+ @access ||= FILE_MAP_WRITE
160
+ @size ||= 0
161
+ @inherit ||= nil
162
+ @base_address ||= 0
163
+ @timeout ||= 10 # Milliseconds
164
+
165
+ self.inherit = false unless @inherit
166
+
167
+ @hash = {}
168
+
169
+ # Anything except an explicit false means the autolock is on.
170
+ @autolock = true unless @autolock == false
171
+
172
+ @lock_flag = 0 # Internal use only
173
+
174
+ if @file
175
+ if File.exists?(@file)
176
+ fsize = File.size(@file)
177
+ raise ArgumentError, 'cannot open 0 byte file' if fsize.zero?
178
+ @size = fsize if @size < fsize
179
+ end
180
+
181
+ rights = GENERIC_READ|GENERIC_WRITE
182
+
183
+ @fh = CreateFile(@file, rights, 0, nil, OPEN_ALWAYS, 0, 0)
184
+
185
+ if @fh == INVALID_HANDLE_VALUE
186
+ raise SystemCallError.new('CreateFile', FFI.errno)
187
+ end
188
+ else
189
+ @fh = INVALID_HANDLE_VALUE
190
+ end
191
+
192
+ if @open
193
+ @mh = OpenFileMapping(@access, @inherit[:bInheritHandle], @name)
194
+ raise SystemCallError.new('OpenFileMapping', FFI.errno) if @mh == 0
195
+ else
196
+ @mh = CreateFileMapping(@fh, @inherit, @protection, 0, @size, @name)
197
+ raise SystemCallError.new('CreateFileMapping', FFI.errno) if @mh == 0
198
+ end
199
+
200
+ if @open
201
+ @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
202
+ @size = get_view_size()
203
+ else
204
+ @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
205
+ end
206
+
207
+ if @address == 0
208
+ raise SystemCallError.new('MapviewOfFileEx', FFI.errno)
209
+ end
210
+
211
+ if @autolock
212
+ @semaphore = CreateSemaphore(nil, 1, 1, "#{@name}.ruby_lock")
213
+ if @semaphore == 0
214
+ raise Error, get_last_error
215
+ end
216
+ end
217
+
218
+ if block_given?
219
+ begin
220
+ yield @address
221
+ ensure
222
+ close
223
+ end
224
+ end
225
+
226
+ @address
227
+ end
228
+
229
+ # Opens an existing file mapping using +name+. You may pass a hash
230
+ # of +opts+ as you would to MMap.new. If you don't specify a size
231
+ # as part of the +opts+, it will be dynamically determined for you
232
+ # (in blocks equal to your system's page size, typically 4k).
233
+ #
234
+ # This method is otherwise identical to MMap.new.
235
+ #--
236
+ # This forces MMap.new to use OpenFileMapping() behind the scenes.
237
+ #
238
+ def self.open(name, opts={}, &block)
239
+ opts[:name] = name
240
+ opts[:open] = true
241
+ self.new(opts, &block)
242
+ end
243
+
244
+ # Sets whether or not the mapping handle can be inherited
245
+ # by child processes.
246
+ #--
247
+ # If true, we have to create a SECURITY_ATTRIBUTES struct and set
248
+ # its nLength member to 12 and its bInheritHandle member to TRUE.
249
+ #
250
+ def inherit=(bool)
251
+ @inherit = SECURITY_ATTRIBUTES.new
252
+ @inherit[:nLength] = SECURITY_ATTRIBUTES.size
253
+
254
+ if bool
255
+ @inherit[:bInheritHandle] = true
256
+ else
257
+ @inherit[:bInheritHandle] = false
258
+ end
259
+ end
260
+
261
+ # Returns whether or not the mapping handle can be
262
+ # inherited by child processes. The default is false.
263
+ #
264
+ def inherit?
265
+ @inherit and @inherit[:bInheritHandle]
266
+ end
267
+
268
+ # Writes +num_bytes+ to the disk within a mapped view of a file, or to
269
+ # the end of the mapping if +num_bytes+ is not specified.
270
+ #
271
+ def flush(num_bytes = 0)
272
+ unless FlushViewOfFile(@address, num_bytes)
273
+ SystemCallError.new('FlushViewOfFile', FFI.errno)
274
+ end
275
+ end
276
+
277
+ # Unmaps the file view and closes all open file handles. You should
278
+ # always call this when you are finished with the object (when using
279
+ # the non-block form).
280
+ #
281
+ def close
282
+ UnmapViewOfFile(@address) if @address
283
+ CloseHandle(@fh) if @fh
284
+ CloseHandle(@mh) if @mh
285
+ ReleaseSemaphore(@semaphore, 1, nil) if @semaphore
286
+ end
287
+
288
+ # Returns whether or not a semaphore lock is automatically placed on the
289
+ # mapped view for read and write operations. This is true by default.
290
+ #
291
+ def autolock?
292
+ @autolock
293
+ end
294
+
295
+ # Writes a string directly to the underlying file
296
+ def write_string(content)
297
+ lock_pattern do
298
+ ptr = FFI::Pointer.new(:char, @address)
299
+ ptr.write_string(content,content.length)
300
+ end
301
+ end
302
+
303
+ # Reads a string of a given length from the beginning of the file
304
+ # if no length is given, reads the file with the @size attribute
305
+ # of this instance
306
+ def read_string(length = @size)
307
+ lock_pattern do
308
+ FFI::MemoryPointer.new(:char, length)
309
+ ptr = FFI::Pointer.new(:char, @address)
310
+ ptr.read_string(length)
311
+ end
312
+ end
313
+
314
+ private
315
+
316
+ # :stopdoc:
317
+
318
+ # This is used to allow dynamic getters and setters between memory
319
+ # mapped objects.
320
+ #--
321
+ # This replaces the getvar/setvar API from 0.1.0.
322
+ #
323
+ def method_missing(method_id, *args)
324
+ method = method_id.id2name
325
+ args = args.first if args.length == 1
326
+
327
+ if method[-1,1] == '=' # Setter
328
+ method.chop!
329
+ @hash["#{method}"] = args
330
+
331
+ lock_pattern do
332
+ instance_variable_set("@#{method}", args)
333
+ marshal = Marshal.dump(@hash)
334
+ ptr = FFI::Pointer.new(:char, @address)
335
+ ptr.write_string(marshal,marshal.length)
336
+ end
337
+
338
+ else # Getter
339
+
340
+ lock_pattern do
341
+ buf = FFI::MemoryPointer.new(:char, @size)
342
+ ptr = FFI::Pointer.new(:char, @address)
343
+ buf = ptr.read_string(@size)
344
+ hash = Marshal.load(buf)
345
+ val = hash["#{method}"]
346
+ instance_variable_set("@#{method}", val)
347
+ end
348
+
349
+ return instance_variable_get("@#{method}")
350
+ end
351
+ end
352
+
353
+ def lock_pattern
354
+ if @autolock
355
+ if mmap_lock
356
+ output = yield
357
+ mmap_unlock
358
+ output
359
+ end
360
+ else
361
+ output = yield
362
+ output
363
+ end
364
+ end
365
+
366
+ # Adds a semaphore lock the mapping. Only used if +autolock+ is set
367
+ # to true in the constructor.
368
+ #
369
+ def mmap_lock
370
+ bool = false
371
+
372
+ if(@lock_flag == 0)
373
+ if WaitForSingleObject(@semaphore, @timeout) == WAIT_OBJECT_0
374
+ bool = true
375
+ end
376
+ end
377
+
378
+ @lock_flag += 1
379
+ bool
380
+ end
381
+
382
+ # Releases a semaphore lock on the view. Only used if +autolock+ is
383
+ # set to true in the constructor.
384
+ #
385
+ def mmap_unlock
386
+ @lock_flag -= 1
387
+
388
+ return false if @lock_flag != 0
389
+
390
+ if ReleaseSemaphore(@semaphore, 1, nil) == 0
391
+ raise SystemCallError.new('ReleaseSemaphore', FFI.errno)
392
+ end
393
+
394
+ true
395
+ end
396
+
397
+ # Gets the size of an existing mapping based on the address. This
398
+ # is used by the MMap.open method when a size isn't specified.
399
+ #
400
+ def get_view_size
401
+ mbi = MEMORY_BASIC_INFORMATION.new
402
+ VirtualQuery(@address, mbi, mbi.size)
403
+ mbi[:RegionSize]
404
+ end
405
+ end # MMap
406
+ end # Win32