win32-mmap 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,23 @@
1
+ == 0.2.0 - 13-Oct-2006
2
+ * Completely scrapped the old interface and code. It is now pure Ruby and
3
+ has a much different API, and some internal changes.
4
+ * Added a gemspec and an install.rb file for manual installation.
5
+ * Now requires the windows-pr package, 0.5.5 or higher.
6
+ * Modified the example scripts.
7
+ * Updated the docs, and replaced the .txt and .rd files with a single .rdoc
8
+ file.
9
+
10
+ == 0.1.1 - 1-Mar-2005
11
+ * Moved the 'examples' directory to the toplevel directory.
12
+ * Made the CHANGES and README files rdoc friendly.
13
+
14
+ == 0.1.0 - 12-Aug-2004
15
+ * Modified to use the newer allocation framework. That means that, as of
16
+ this release, this package requires Ruby 1.8.0 or later.
17
+ * Moved sample programs under doc/examples.
18
+
19
+ == 0.0.2 - 14-Mar-2004
20
+ * Fixed sprintf() bug in new().
21
+
22
+ == 0.0.1 - 13-Mar-2004
23
+ * Initial (Beta) release
@@ -0,0 +1,15 @@
1
+ CHANGES
2
+ MANIFEST
3
+ README
4
+ install.rb
5
+ win32-mmap.gemspec
6
+
7
+ doc/mmap.rdoc
8
+
9
+ examples/mmap_test.rb
10
+ examples/test_client.rb
11
+ examples/test_server.rb
12
+
13
+ lib/win32/mmap.rb
14
+
15
+ test/tc_mmap.rb
data/README ADDED
@@ -0,0 +1,70 @@
1
+ = Description
2
+ This package provides a Ruby interface for memory mapped I/O on MS Windows.
3
+
4
+ = Prerequisites
5
+ Ruby 1.8.0 or later.
6
+ windows-pr, 0.5.5 or later.
7
+
8
+ = Installation
9
+ == Standard Installation
10
+ ruby install.rb
11
+
12
+ == Gem Installation
13
+ ruby win32-mmap.gemspec
14
+ gem install win32-mmap-x.y.z.gem
15
+
16
+ = Synopsis
17
+ require 'win32/mmap'
18
+ include Win32
19
+
20
+ map1 = MMap.new(:file => "C:\\test.map", :size => 1024)
21
+ map1.foo = 'hello'
22
+ map1.bar = 77
23
+ map1.close
24
+
25
+ map2 = MMap.new(:file => "C:\\test.map")
26
+ p map2.foo # 'hello'
27
+ p map2.bar # 77
28
+ map2.close
29
+
30
+ = About Memory Mapped Files under Windows
31
+ Under Windows, code and data are both repesented by pages of memory backed
32
+ by files on disk - code by executable image an data by system pagefile
33
+ (swapfile). These are called memory mapped files. Memory mapped files can be
34
+ used to provide a mechanism for shared memory between processes. Different
35
+ processes are able to share data backed by the same swapfile, whether it's
36
+ the system pagefile or a user-defined swapfile.
37
+
38
+ Windows has a tight security system that prevents processes from directly
39
+ sharing information among each other, but mapped memory files provide a
40
+ mechanism that works with the Windows security system - by using a name that
41
+ all processes use to open the swapfile.
42
+
43
+ A shared section of the swapfile is translated into pages of memory that are
44
+ addressable by more than one process, Windows uses a system resource called a
45
+ prototype page table entry (PPTE) to enable more than one process to address
46
+ the same physical page of memory, thus multiple process can share the same
47
+ data without violating the Windows system security.
48
+
49
+ In short, MMF's provide shared memory under Windows.
50
+
51
+ (This explanation was largely borrowed from Roger Lee's Win32::MMF Perl
52
+ module.)
53
+
54
+ = Future Plans
55
+ Suggestions welcome.
56
+
57
+ = License
58
+ Ruby's
59
+
60
+ = Copyright
61
+ (C) 2003-2006 Daniel J. Berger, All Rights Reserved
62
+
63
+ = Warranty
64
+ This package is provided "as is" and without any express or
65
+ implied warranties, including, without limitation, the implied
66
+ warranties of merchantability and fitness for a particular purpose.
67
+
68
+ = Authors
69
+ Daniel J. Berger
70
+ Park Heesob
@@ -0,0 +1,11 @@
1
+ # For those who don't like gems...
2
+ require 'rbconfig'
3
+ require 'ftools'
4
+ include Config
5
+
6
+ sitelibdir = CONFIG['sitelibdir']
7
+ installdir = sitelibdir + '/win32'
8
+ file = 'lib\win32\mmap.rb'
9
+
10
+ Dir.mkdir(installdir) unless File.exists?(installdir)
11
+ File.copy(file, installdir, true)
@@ -0,0 +1,392 @@
1
+ require 'windows/file'
2
+ require 'windows/file_mapping'
3
+ require 'windows/error'
4
+ require 'windows/handle'
5
+ require 'windows/msvcrt/buffer'
6
+ require 'windows/synchronize'
7
+ require 'windows/memory'
8
+
9
+ module Win32
10
+
11
+ # Memory mapped files for MS Windows.
12
+ #
13
+ class MMap
14
+ VERSION = '0.2.0'
15
+
16
+ # This is a slightly different version of memcpy than what's provided
17
+ # in the Windows::MSVCRT::Buffer module. The difference is that the
18
+ # first argument is an address, not a char buffer.
19
+ MEMCPY = Win32API.new('msvcrt', 'memcpy', 'LPL', 'P')
20
+
21
+ # All errors generated by Win32::MMap are of type MMap::Error.
22
+ #
23
+ class Error < StandardError; end
24
+
25
+ include Windows::Error
26
+ include Windows::File
27
+ include Windows::FileMapping
28
+ include Windows::Handle
29
+ include Windows::MSVCRT::Buffer
30
+ include Windows::Synchronize
31
+ include Windows::Memory
32
+
33
+ # The name of the file from which to create a mapping object. This
34
+ # value may be nil.
35
+ #
36
+ attr_accessor :file
37
+
38
+ # The protection for the file view. This may be any of the following
39
+ # values:
40
+ #
41
+ # * PAGE_READONLY
42
+ # * PAGE_READWRITE
43
+ # * PAGE_WRITECOPY
44
+ # * PAGE_WRITECOPY
45
+ # * PAGE_EXECUTE_READ
46
+ # * PAGE_EXECUTE_READWRITE
47
+ #
48
+ # You can OR the protection value with any of these section attributes:
49
+ #
50
+ # * SEC_COMMIT
51
+ # * SEC_IMAGE
52
+ # * SEC_LARGE_PAGES
53
+ # * SEC_NOCACHE
54
+ # * SEC_RESERVE
55
+ #
56
+ # The default protection is PAGE_READWRITE.
57
+ #
58
+ attr_accessor :protection
59
+
60
+ # A string that determines the name of the mapping object.
61
+ # By default this value is nil, i.e. anonymous. If you specify a +name+
62
+ # that already exists then an attempt is made to access that object.
63
+ #
64
+ attr_accessor :name
65
+
66
+ # The maximum size for the file mapping object. If a +file+ is
67
+ # specified, this value defaults to the size of +file+. Otherwise
68
+ # it defaults to zero (though you should set it manually).
69
+ #
70
+ attr_accessor :size
71
+
72
+ # Access desired to the file mapping object. This may be
73
+ # any of the following values:
74
+ #
75
+ # * FILE_MAP_WRITE
76
+ # * FILE_MAP_READ
77
+ # * FILE_MAP_COPY
78
+ # * FILE_MAP_ALL_ACCESS
79
+ #
80
+ # The default access is FILE_MAP_WRITE.
81
+ #
82
+ attr_accessor :access
83
+
84
+ # The address of the file view mapping.
85
+ #
86
+ attr_reader :address
87
+
88
+ # Suggested starting address of mapped view. If this value is not
89
+ # specified, the system will choose the base mapping address. If you do
90
+ # specify a value, it must be a multiple of the system's allocation
91
+ # granularity.
92
+ #
93
+ # Note: The MSDN documentation recommends that, in most case cases, you
94
+ # should not set this value.
95
+ #--
96
+ # A system's allocation granularity can be found via GetSystemInfo()
97
+ # and the dwAllocationGranularity member of the SYSTEM_INFO struct.
98
+ #
99
+ attr_accessor :base_address
100
+
101
+ # Sets whether or not a semaphore lock is automatically placed on the
102
+ # mapped view for read and write operations. This is true by default.
103
+ #
104
+ attr_writer :autolock
105
+
106
+ # The value, in milliseconds, used to wait on the semaphore lock. Only
107
+ # used if the +autolock+ option is true. The default is 10 milliseconds.
108
+ #
109
+ attr_accessor :timeout
110
+
111
+ # :call-seq:
112
+ # MMap.new(opts = {})
113
+ # MMap.new(opts = {}){ |address| block }
114
+ #
115
+ # Creates and returns a new Win32::MMap object. If +file+ is set, then
116
+ # that file is used to create the mapping. If a block is provided the
117
+ # address is yielded and the mapping object is automatically closed at the
118
+ # end of the block.
119
+ #
120
+ # Accepts any one of the following hash attributes:
121
+ #
122
+ # * protection
123
+ # * size
124
+ # * access
125
+ # * inherit
126
+ # * name
127
+ # * base_address
128
+ # * autolock
129
+ # * timeout
130
+ # * file
131
+ #
132
+ # Please see the documentation for the individual attributes for
133
+ # further details. Note that, although these are accessors, they
134
+ # *must* be set in the constructor (if set at all). Setting them
135
+ # after the call to MMap.new will not have any effect.
136
+ #
137
+ # == Example
138
+ # require 'win32/mmap'
139
+ # require 'windows/msvcrt/string'
140
+ # include Windows::MSVCRT::String
141
+ # include Win32
142
+ #
143
+ # # Reverse the contents of a file.
144
+ # mmap = MMap.new(:file => 'test.txt') do |addr|
145
+ # strrev(addr)
146
+ # end
147
+ #
148
+ def initialize(opts = {})
149
+ valid = %w/
150
+ file name protection access inherit size
151
+ base_address open autolock timeout
152
+ /
153
+
154
+ opts.each{ |key, value|
155
+ key = key.to_s.downcase
156
+ unless valid.include?(key)
157
+ raise ArgumentError, "Invalid key '#{key}'"
158
+ end
159
+ if key == 'inherit'
160
+ self.inherit = value # To force inherit= method call
161
+ else
162
+ instance_variable_set("@#{key}", value)
163
+ end
164
+ }
165
+
166
+ @protection ||= PAGE_READWRITE
167
+ @access ||= FILE_MAP_WRITE
168
+ @size ||= 0
169
+ @inherit ||= 0
170
+ @base_address ||= 0
171
+ @timeout ||= 10 # Milliseconds
172
+
173
+ @hash = {}
174
+
175
+ # Anything except an explicit false means the autolock is on.
176
+ @autolock = true unless @autolock == false
177
+
178
+ @lock_flag = 0 # Internal use only
179
+
180
+ if @file
181
+ if File.exists?(@file)
182
+ fsize = File.size(@file)
183
+ raise Error, 'cannot open 0 byte file' if fsize.zero?
184
+ @size = fsize if @size < fsize
185
+ end
186
+
187
+ rights = GENERIC_READ|GENERIC_WRITE
188
+
189
+ @fh = CreateFile(@file, rights, 0, 0, OPEN_ALWAYS, 0, 0)
190
+
191
+ if @fh == INVALID_HANDLE_VALUE
192
+ raise Error, get_last_error
193
+ end
194
+ else
195
+ @fh = INVALID_HANDLE_VALUE
196
+ end
197
+
198
+ if @open
199
+ @mh = OpenFileMapping.call(@access, @inherit, @name)
200
+ else
201
+ @mh = CreateFileMapping(@fh, @inherit, @protection, 0, @size, @name)
202
+ end
203
+
204
+ if @mh == 0
205
+ raise Error, get_last_error
206
+ end
207
+
208
+ if @open
209
+ @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
210
+ @size = get_view_size()
211
+ else
212
+ @address = MapViewOfFileEx(@mh, @access, 0, 0, 0, @base_address)
213
+ end
214
+
215
+ if @address == 0
216
+ raise Error, get_last_error
217
+ end
218
+
219
+ if @autolock
220
+ @semaphore = CreateSemaphore(nil, 1, 1, "#{@name}.ruby_lock")
221
+ if @semaphore == 0
222
+ raise Error, get_last_error
223
+ end
224
+ end
225
+
226
+ if block_given?
227
+ begin
228
+ yield @address
229
+ ensure
230
+ close
231
+ end
232
+ end
233
+
234
+ @address
235
+ end
236
+
237
+ # Opens an existing file mapping using +name+. You may pass a hash
238
+ # of +opts+ as you would to MMap.new. If you don't specify a size
239
+ # as part of the +opts+, it will be dynamically determined for you
240
+ # (in blocks equal to your system's page size, typically 4k).
241
+ #
242
+ # This method is otherwise identical to MMap.new.
243
+ #--
244
+ # This forces MMap.new to use OpenFileMapping() behind the scenes.
245
+ #
246
+ def self.open(name, opts={})
247
+ opts[:name] = name
248
+ opts[:open] = true
249
+ self.new(opts)
250
+ end
251
+
252
+ # Sets whether or not the mapping handle can be inherited
253
+ # by child processes.
254
+ #--
255
+ # If true, we have to create a SECURITY_ATTRIBUTES struct and set
256
+ # its nLength member to 12 and its bInheritHandle member to TRUE.
257
+ #
258
+ def inherit=(bool)
259
+ if bool
260
+ buf = 0.chr * 12 # sizeof(SECURITY_ATTRIBUTES)
261
+ buf[0,4] = [12].pack('L')
262
+ buf[8,4] = [1].pack('L') # 1 == TRUE
263
+ @inherit = buf
264
+ else
265
+ @inherit = 0
266
+ end
267
+ end
268
+
269
+ # Returns whether or not the mapping handle can be
270
+ # inherited by child processes. The default is false.
271
+ #
272
+ def inherit?
273
+ @inherit != 0
274
+ end
275
+
276
+ # Writes +num_bytes+ to the disk within a mapped view of a file, or to
277
+ # the end of the mapping if +num_bytes+ is not specified.
278
+ #
279
+ def flush(num_bytes = 0)
280
+ unless FlushViewOfFile(@address, num_bytes)
281
+ raise Error, get_last_error
282
+ end
283
+ end
284
+
285
+ # Unmaps the file view and closes all open file handles. You should
286
+ # always call this when you are finished with the object (when using
287
+ # the non-block form).
288
+ #
289
+ def close
290
+ UnmapViewOfFile(@address) if @address
291
+ CloseHandle(@fh) if @fh
292
+ CloseHandle(@mh) if @mh
293
+ ReleaseSemaphore(@semaphore, 1, nil) if @semaphore
294
+ end
295
+
296
+ # Returns whether or not a semaphore lock is automatically placed on the
297
+ # mapped view for read and write operations. This is true by default.
298
+ #
299
+ def autolock?
300
+ @autolock
301
+ end
302
+
303
+ private
304
+
305
+ # This is used to allow dynamic getters and setters between memory
306
+ # mapped objects.
307
+ #--
308
+ # This replaces the getvar/setvar API from 0.1.0.
309
+ #
310
+ def method_missing(method_id, *args)
311
+ method = method_id.id2name
312
+ args = args.first if args.length == 1
313
+
314
+ if method[-1,1] == '=' # Setter
315
+ method.chop!
316
+ @hash["#{method}"] = args
317
+
318
+ if @autolock
319
+ if mmap_lock
320
+ instance_variable_set("@#{method}", args)
321
+ marshal = Marshal.dump(@hash)
322
+ MEMCPY.call(@address, marshal, marshal.length)
323
+ mmap_unlock
324
+ end
325
+ else
326
+ instance_variable_set("@#{method}", args)
327
+ marshal = Marshal.dump(@hash)
328
+ MEMCPY.call(@address, marshal, marshal.length)
329
+ end
330
+ else # Getter
331
+ buf = 0.chr * @size
332
+ if @autolock
333
+ if mmap_lock
334
+ marshal = memcpy(buf, @address, @size)
335
+ hash = Marshal.load(marshal)
336
+ val = hash["#{method}"]
337
+ instance_variable_set("@#{method}", val)
338
+ mmap_unlock
339
+ end
340
+ else
341
+ marshal = memcpy(buf, @address, @size)
342
+ hash = Marshal.load(marshal)
343
+ val = hash["#{method}"]
344
+ instance_variable_set("@#{method}", val)
345
+ end
346
+ return instance_variable_get("@#{method}")
347
+ end
348
+ end
349
+
350
+ # Adds a semaphore lock the mapping. Only used if +autolock+ is set
351
+ # to true in the constructor.
352
+ #
353
+ def mmap_lock
354
+ bool = false
355
+
356
+ if(@lock_flag == 0)
357
+ if WaitForSingleObject(@semaphore, @timeout) == WAIT_OBJECT_0
358
+ bool = true
359
+ end
360
+ end
361
+
362
+ @lock_flag += 1
363
+ bool
364
+ end
365
+
366
+ # Releases a semaphore lock on the view. Only used if +autolock+ is
367
+ # set to true in the constructor.
368
+ #
369
+ def mmap_unlock
370
+ @lock_flag -= 1
371
+
372
+ if @lock_flag != 0
373
+ return false
374
+ end
375
+
376
+ if ReleaseSemaphore(@semaphore, 1, nil) == 0
377
+ raise Error, get_last_error
378
+ end
379
+
380
+ true
381
+ end
382
+
383
+ # Gets the size of an existing mapping based on the address. Used by
384
+ # the MMap.open method when a size isn't specified.
385
+ #
386
+ def get_view_size
387
+ mbi = [0,0,0,0,0,0,0].pack('LLLLLLL') # MEMORY_BASIC_INFORMATION
388
+ VirtualQuery(@address, mbi, mbi.size)
389
+ mbi[12,4].unpack('L').first # RegionSize
390
+ end
391
+ end
392
+ end
@@ -0,0 +1,88 @@
1
+ #################################################
2
+ # tc_mmap.rb
3
+ #
4
+ # Test suite for the win32-mmap package.
5
+ #################################################
6
+ Dir.chdir '..' if File.basename(Dir.pwd) == 'test'
7
+ $LOAD_PATH.unshift Dir.pwd
8
+ $LOAD_PATH.unshift Dir.pwd + '/lib'
9
+
10
+ require 'test/unit'
11
+ require 'win32/mmap'
12
+ include Win32
13
+
14
+ # The order of the tests matters (for now)
15
+ class TC_Mmap < Test::Unit::TestCase
16
+ def setup
17
+ @mmap = MMap.new(:name => 'test', :size => 100)
18
+ end
19
+
20
+ def test_version
21
+ assert_equal('0.2.0', MMap::VERSION)
22
+ end
23
+
24
+ def test_dynamic_value
25
+ assert_nothing_raised{ @mmap.foo = 'test' }
26
+ assert_equal('test', @mmap.foo)
27
+ end
28
+
29
+ def test_valid_options
30
+ assert_raises(ArgumentError){ MMap.new(:foo => 1) }
31
+ end
32
+
33
+ def test_address
34
+ assert_respond_to(@mmap, :address)
35
+ assert_kind_of(Fixnum, @mmap.address)
36
+ end
37
+
38
+ def test_base_address
39
+ assert_respond_to(@mmap, :base_address)
40
+ assert_respond_to(@mmap, :base_address=)
41
+ assert_kind_of(Fixnum, @mmap.base_address)
42
+ end
43
+
44
+ def test_name
45
+ assert_respond_to(@mmap, :name)
46
+ assert_respond_to(@mmap, :name=)
47
+ assert_equal('test', @mmap.name)
48
+ end
49
+
50
+ def test_inherit
51
+ assert_respond_to(@mmap, :inherit?)
52
+ assert_respond_to(@mmap, :inherit=)
53
+ assert_equal(false, @mmap.inherit?)
54
+ end
55
+
56
+ def test_size
57
+ assert_respond_to(@mmap, :size)
58
+ assert_respond_to(@mmap, :size=)
59
+ assert_equal(100, @mmap.size)
60
+ end
61
+
62
+ def test_file
63
+ assert_respond_to(@mmap, :file)
64
+ assert_respond_to(@mmap, :file=)
65
+ assert_nil(@mmap.file)
66
+ end
67
+
68
+ def test_access
69
+ assert_respond_to(@mmap, :access)
70
+ assert_respond_to(@mmap, :access=)
71
+ end
72
+
73
+ def test_autolock
74
+ assert_respond_to(@mmap, :autolock?)
75
+ assert_respond_to(@mmap, :autolock=)
76
+ assert_equal(true, @mmap.autolock?)
77
+ end
78
+
79
+ def test_protection
80
+ assert_respond_to(@mmap, :protection)
81
+ assert_respond_to(@mmap, :protection=)
82
+ assert_equal(4, @mmap.protection)
83
+ end
84
+
85
+ def teardown
86
+ @mmap.close
87
+ end
88
+ end
@@ -0,0 +1,24 @@
1
+ require "rubygems"
2
+
3
+ spec = Gem::Specification.new do |gem|
4
+ gem.name = "win32-mmap"
5
+ gem.version = "0.2.0"
6
+ gem.author = "Daniel J. Berger"
7
+ gem.email = "djberg96@gmail.com"
8
+ gem.homepage = "http://www.rubyforge.org/projects/win32utils"
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = "Memory mapped IO for Windows."
11
+ gem.description = "Memory mapped IO for Windows."
12
+ gem.test_file = "test/tc_mmap.rb"
13
+ gem.has_rdoc = true
14
+ gem.files = Dir["lib/win32/*.rb"] + Dir["test/*"] + Dir["[A-Z]*"]
15
+ gem.files.reject! { |fn| fn.include? "CVS" }
16
+ gem.require_path = "lib"
17
+ gem.extra_rdoc_files = ["README", "CHANGES"]
18
+ gem.add_dependency("windows-pr", ">= 0.5.5")
19
+ end
20
+
21
+ if $0 == __FILE__
22
+ Gem.manage_gems
23
+ Gem::Builder.new(spec).build
24
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.0
3
+ specification_version: 1
4
+ name: win32-mmap
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.2.0
7
+ date: 2006-10-13 00:00:00 -06:00
8
+ summary: Memory mapped IO for Windows.
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/win32utils
13
+ rubyforge_project:
14
+ description: Memory mapped IO for Windows.
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Daniel J. Berger
31
+ files:
32
+ - lib/win32/mmap.rb
33
+ - test/tc_mmap.rb
34
+ - CHANGES
35
+ - doc
36
+ - examples
37
+ - install.rb
38
+ - lib
39
+ - MANIFEST
40
+ - README
41
+ - test
42
+ - win32-mmap.gemspec
43
+ test_files:
44
+ - test/tc_mmap.rb
45
+ rdoc_options: []
46
+
47
+ extra_rdoc_files:
48
+ - README
49
+ - CHANGES
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ requirements: []
55
+
56
+ dependencies:
57
+ - !ruby/object:Gem::Dependency
58
+ name: windows-pr
59
+ version_requirement:
60
+ version_requirements: !ruby/object:Gem::Version::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 0.5.5
65
+ version: