win32-mmap 0.2.0

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.
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: