win32-mmap 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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