win32-nio 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/win32/nio.rb CHANGED
@@ -1,200 +1,200 @@
1
- require 'ffi'
2
- require 'win32/event'
3
-
4
- require File.join(File.dirname(__FILE__), 'windows/functions')
5
- require File.join(File.dirname(__FILE__), 'windows/constants')
6
- require File.join(File.dirname(__FILE__), 'windows/structs')
7
- require File.join(File.dirname(__FILE__), 'windows/macros')
8
-
9
- # The Win32 module serves as a namespace only.
10
- module Win32
11
-
12
- # The NIO class encapsulates the native IO methods for MS Windows.
13
- class NIO
14
- include Windows::Constants
15
- include Windows::Structs
16
- extend Windows::Functions
17
- extend Windows::Macros
18
-
19
- # The version of the win32-nio library
20
- VERSION = '0.1.1'
21
-
22
- # This method is similar to Ruby's IO.read method except that it uses
23
- # native function calls.
24
- #
25
- # Examples:
26
- #
27
- # # Read everything
28
- # Win32::NIO.read(file)
29
- #
30
- # # Read the first 100 bytes
31
- # Win32::NIO.read(file, 100)
32
- #
33
- # # Read 50 bytes starting at offset 10
34
- # Win32::NIO.read(file, 50, 10)
35
- #
36
- # Note that the +options+ that may be passed to this method are limited
37
- # to :encoding, :mode and :event because we're no longer using the open
38
- # function internally. In the case of :mode the only thing that is checked
39
- # for is the presence of the 'b' (binary) mode.
40
- #
41
- # The :event option, if present, must be a Win32::Event object.
42
- #--
43
- # In practice the fact that I ignore open_args: is irrelevant since you
44
- # would never want to open in anything other than GENERIC_READ. I suppose
45
- # I could change this to as a way to pass flags to CreateFile.
46
- #
47
- def self.read(name, length=nil, offset=0, options={})
48
- begin
49
- fname = name + "\0"
50
- fname.encode!('UTF-16LE')
51
-
52
- flags = FILE_FLAG_SEQUENTIAL_SCAN
53
- olap = Overlapped.new
54
- event = options[:event]
55
-
56
- if event
57
- raise TypeError unless event.is_a?(Win32::Event)
58
- end
59
-
60
- olap[:Offset] = offset
61
-
62
- if offset > 0 || event
63
- flags |= FILE_FLAG_OVERLAPPED
64
- olap[:hEvent] = event.handle if event
65
- end
66
-
67
- handle = CreateFileW(
68
- fname,
69
- GENERIC_READ,
70
- FILE_SHARE_READ,
71
- nil,
72
- OPEN_EXISTING,
73
- flags,
74
- 0
75
- )
76
-
77
- if handle == INVALID_HANDLE_VALUE
78
- raise SystemCallError.new("CreateFile", FFI.errno)
79
- end
80
-
81
- length ||= File.size(name)
82
- buf = 0.chr * length
83
-
84
- if block_given?
85
- callback = Proc.new{ |e,b,o| block.call }
86
- bool = ReadFileEx(handle, buf, buf.size, olap, callback)
87
- else
88
- bool = ReadFile(handle, buf, buf.size, nil, olap)
89
- end
90
-
91
- errno = FFI.errno
92
-
93
- SleepEx(1, true) # Must be in alertable wait state
94
-
95
- unless bool
96
- if errno == ERROR_IO_PENDING
97
- bytes = FFI::MemoryPointer.new(:ulong)
98
- unless GetOverlappedResult(handle, olap, bytes, true)
99
- raise SystemCallError.new("GetOverlappedResult", FFI.errno)
100
- end
101
- else
102
- raise SystemCallError.new("ReadFile", errno)
103
- end
104
- end
105
-
106
- result = buf.delete(0.chr)
107
-
108
- result.encode!(options[:encoding]) if options[:encoding]
109
-
110
- if options[:mode] && options[:mode].include?('t') && ($/ != "\r\n")
111
- result.gsub!(/\r\n/, $/)
112
- end
113
-
114
- result
115
- ensure
116
- CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
117
- end
118
- end # NIO.read
119
-
120
- # Reads the entire file specified by portname as individual lines, and
121
- # returns those lines in an array. Lines are separated by +sep+.
122
- #--
123
- # The semantics are the same as the MRI version but the implementation
124
- # is drastically different. We use a scattered IO read.
125
- #
126
- def self.readlines(file, sep = "\r\n")
127
- fname = file + "\0"
128
- fname.encode!('UTF-16LE')
129
-
130
- begin
131
- handle = CreateFileW(
132
- fname,
133
- GENERIC_READ,
134
- FILE_SHARE_READ,
135
- nil,
136
- OPEN_EXISTING,
137
- FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
138
- 0
139
- )
140
-
141
- if handle == INVALID_HANDLE_VALUE
142
- raise SystemCallError.new("CreateFileW", FFI.errno)
143
- end
144
-
145
- sysinfo = SystemInfo.new
146
- GetSystemInfo(sysinfo)
147
-
148
- file_size = File.size(file)
149
- page_size = sysinfo[:dwPageSize]
150
- page_num = (file_size.to_f / page_size).ceil
151
-
152
- begin
153
- size = page_size * page_num
154
- base_address = VirtualAlloc(nil, size, MEM_COMMIT, PAGE_READWRITE)
155
-
156
- if base_address == 0
157
- raise SystemCallError.new("VirtualAlloc", FFI.errno)
158
- end
159
-
160
- # Add 1 for null as per the docs
161
- array = FFI::MemoryPointer.new(FileSegmentElement, page_num + 1)
162
-
163
- for i in 0...page_num
164
- fse = FileSegmentElement.new(array[i])
165
- fse[:Alignment] = base_address + page_size * i
166
- end
167
-
168
- overlapped = Overlapped.new
169
-
170
- bool = ReadFileScatter(handle, array, size, nil, overlapped)
171
-
172
- unless bool
173
- error = FFI.errno
174
- if error == ERROR_IO_PENDING
175
- SleepEx(1, true) while !HasOverlappedIoCompleted(overlapped)
176
- else
177
- raise SystemCallError.new("ReadFileScatter", error)
178
- end
179
- end
180
-
181
- string = array[0].read_pointer.read_string
182
-
183
- if sep == ""
184
- array = string.split(/(\r\n){2,}/)
185
- array.delete("\r\n")
186
- else
187
- array = string.split(sep)
188
- end
189
-
190
- array
191
- ensure
192
- VirtualFree(base_address, 0, MEM_RELEASE)
193
- end
194
- ensure
195
- CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
196
- end
197
- end # NIO.readlines
198
-
199
- end # NIO
200
- end # Win32
1
+ require 'ffi'
2
+ require 'win32/event'
3
+
4
+ require File.join(File.dirname(__FILE__), 'windows/functions')
5
+ require File.join(File.dirname(__FILE__), 'windows/constants')
6
+ require File.join(File.dirname(__FILE__), 'windows/structs')
7
+ require File.join(File.dirname(__FILE__), 'windows/macros')
8
+
9
+ # The Win32 module serves as a namespace only.
10
+ module Win32
11
+
12
+ # The NIO class encapsulates the native IO methods for MS Windows.
13
+ class NIO
14
+ include Windows::Constants
15
+ include Windows::Structs
16
+ extend Windows::Functions
17
+ extend Windows::Macros
18
+
19
+ # The version of the win32-nio library
20
+ VERSION = '0.1.2'
21
+
22
+ # This method is similar to Ruby's IO.read method except that it uses
23
+ # native function calls.
24
+ #
25
+ # Examples:
26
+ #
27
+ # # Read everything
28
+ # Win32::NIO.read(file)
29
+ #
30
+ # # Read the first 100 bytes
31
+ # Win32::NIO.read(file, 100)
32
+ #
33
+ # # Read 50 bytes starting at offset 10
34
+ # Win32::NIO.read(file, 50, 10)
35
+ #
36
+ # Note that the +options+ that may be passed to this method are limited
37
+ # to :encoding, :mode and :event because we're no longer using the open
38
+ # function internally. In the case of :mode the only thing that is checked
39
+ # for is the presence of the 'b' (binary) mode.
40
+ #
41
+ # The :event option, if present, must be a Win32::Event object.
42
+ #--
43
+ # In practice the fact that I ignore open_args: is irrelevant since you
44
+ # would never want to open in anything other than GENERIC_READ. I suppose
45
+ # I could change this to as a way to pass flags to CreateFile.
46
+ #
47
+ def self.read(name, length=nil, offset=0, options={})
48
+ begin
49
+ fname = name + "\0"
50
+ fname.encode!('UTF-16LE')
51
+
52
+ flags = FILE_FLAG_SEQUENTIAL_SCAN
53
+ olap = Overlapped.new
54
+ event = options[:event]
55
+
56
+ if event
57
+ raise TypeError unless event.is_a?(Win32::Event)
58
+ end
59
+
60
+ olap[:Offset] = offset
61
+
62
+ if offset > 0 || event
63
+ flags |= FILE_FLAG_OVERLAPPED
64
+ olap[:hEvent] = event.handle if event
65
+ end
66
+
67
+ handle = CreateFileW(
68
+ fname,
69
+ GENERIC_READ,
70
+ FILE_SHARE_READ,
71
+ nil,
72
+ OPEN_EXISTING,
73
+ flags,
74
+ 0
75
+ )
76
+
77
+ if handle == INVALID_HANDLE_VALUE
78
+ raise SystemCallError.new("CreateFile", FFI.errno)
79
+ end
80
+
81
+ length ||= File.size(name)
82
+ buf = 0.chr * length
83
+
84
+ if block_given?
85
+ callback = Proc.new{ |e,b,o| block.call }
86
+ bool = ReadFileEx(handle, buf, buf.size, olap, callback)
87
+ else
88
+ bool = ReadFile(handle, buf, buf.size, nil, olap)
89
+ end
90
+
91
+ errno = FFI.errno
92
+
93
+ SleepEx(1, true) # Must be in alertable wait state
94
+
95
+ unless bool
96
+ if errno == ERROR_IO_PENDING
97
+ bytes = FFI::MemoryPointer.new(:ulong)
98
+ unless GetOverlappedResult(handle, olap, bytes, true)
99
+ raise SystemCallError.new("GetOverlappedResult", FFI.errno)
100
+ end
101
+ else
102
+ raise SystemCallError.new("ReadFile", errno)
103
+ end
104
+ end
105
+
106
+ result = buf.delete(0.chr)
107
+
108
+ result.encode!(options[:encoding]) if options[:encoding]
109
+
110
+ if options[:mode] && options[:mode].include?('t') && ($/ != "\r\n")
111
+ result.gsub!(/\r\n/, $/)
112
+ end
113
+
114
+ result
115
+ ensure
116
+ CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
117
+ end
118
+ end # NIO.read
119
+
120
+ # Reads the entire file specified by portname as individual lines, and
121
+ # returns those lines in an array. Lines are separated by +sep+.
122
+ #--
123
+ # The semantics are the same as the MRI version but the implementation
124
+ # is drastically different. We use a scattered IO read.
125
+ #
126
+ def self.readlines(file, sep = "\r\n")
127
+ fname = file + "\0"
128
+ fname.encode!('UTF-16LE')
129
+
130
+ begin
131
+ handle = CreateFileW(
132
+ fname,
133
+ GENERIC_READ,
134
+ FILE_SHARE_READ,
135
+ nil,
136
+ OPEN_EXISTING,
137
+ FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
138
+ 0
139
+ )
140
+
141
+ if handle == INVALID_HANDLE_VALUE
142
+ raise SystemCallError.new("CreateFileW", FFI.errno)
143
+ end
144
+
145
+ sysinfo = SystemInfo.new
146
+ GetSystemInfo(sysinfo)
147
+
148
+ file_size = File.size(file)
149
+ page_size = sysinfo[:dwPageSize]
150
+ page_num = (file_size.to_f / page_size).ceil
151
+
152
+ begin
153
+ size = page_size * page_num
154
+ base_address = VirtualAlloc(nil, size, MEM_COMMIT, PAGE_READWRITE)
155
+
156
+ if base_address == 0
157
+ raise SystemCallError.new("VirtualAlloc", FFI.errno)
158
+ end
159
+
160
+ # Add 1 for null as per the docs
161
+ array = FFI::MemoryPointer.new(FileSegmentElement, page_num + 1)
162
+
163
+ for i in 0...page_num
164
+ fse = FileSegmentElement.new(array[i])
165
+ fse[:Alignment] = base_address + page_size * i
166
+ end
167
+
168
+ overlapped = Overlapped.new
169
+
170
+ bool = ReadFileScatter(handle, array, size, nil, overlapped)
171
+
172
+ unless bool
173
+ error = FFI.errno
174
+ if error == ERROR_IO_PENDING
175
+ SleepEx(1, true) while !HasOverlappedIoCompleted(overlapped)
176
+ else
177
+ raise SystemCallError.new("ReadFileScatter", error)
178
+ end
179
+ end
180
+
181
+ string = array[0].read_pointer.read_string
182
+
183
+ if sep == ""
184
+ array = string.split(/(\r\n){2,}/)
185
+ array.delete("\r\n")
186
+ else
187
+ array = string.split(sep)
188
+ end
189
+
190
+ array
191
+ ensure
192
+ VirtualFree(base_address, 0, MEM_RELEASE)
193
+ end
194
+ ensure
195
+ CloseHandle(handle) if handle && handle != INVALID_HANDLE_VALUE
196
+ end
197
+ end # NIO.readlines
198
+
199
+ end # NIO
200
+ end # Win32
@@ -1,72 +1,72 @@
1
- module Windows
2
- module Constants
3
- INVALID_HANDLE_VALUE = 0xFFFFFFFF
4
-
5
- CREATE_NEW = 1
6
- CREATE_ALWAYS = 2
7
- OPEN_EXISTING = 3
8
- OPEN_ALWAYS = 4
9
- TRUNCATE_EXISTING = 5
10
-
11
- GENERIC_READ = 0x80000000
12
- GENERIC_WRITE = 0x40000000
13
- GENERIC_EXECUTE = 0x20000000
14
- GENERIC_ALL = 0x10000000
15
-
16
- FILE_SHARE_READ = 0x00000001
17
-
18
- FILE_FLAG_WRITE_THROUGH = 0x80000000
19
- FILE_FLAG_OVERLAPPED = 0x40000000
20
- FILE_FLAG_NO_BUFFERING = 0x20000000
21
- FILE_FLAG_RANDOM_ACCESS = 0x10000000
22
- FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
23
- FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
24
- FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
25
- FILE_FLAG_POSIX_SEMANTICS = 0x01000000
26
- FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
27
- FILE_FLAG_OPEN_NO_RECALL = 0x00100000
28
- FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
29
-
30
- MEM_COMMIT = 0x1000
31
- MEM_RESERVE = 0x2000
32
- MEM_DECOMMIT = 0x4000
33
- MEM_RELEASE = 0x8000
34
- MEM_FREE = 0x10000
35
- MEM_PRIVATE = 0x20000
36
- MEM_MAPPED = 0x40000
37
- MEM_RESET = 0x80000
38
- MEM_TOP_DOWN = 0x100000
39
- MEM_WRITE_WATCH = 0x200000
40
- MEM_PHYSICAL = 0x400000
41
- MEM_LARGE_PAGES = 0x20000000
42
- MEM_4MB_PAGES = 0x80000000
43
-
44
- PAGE_NOACCESS = 0x01
45
- PAGE_READONLY = 0x02
46
- PAGE_READWRITE = 0x04
47
- PAGE_WRITECOPY = 0x08
48
- PAGE_EXECUTE = 0x10
49
- PAGE_EXECUTE_READ = 0x20
50
- PAGE_EXECUTE_READWRITE = 0x40
51
- PAGE_EXECUTE_WRITECOPY = 0x80
52
- PAGE_GUARD = 0x100
53
- PAGE_NOCACHE = 0x200
54
- PAGE_WRITECOMBINE = 0x400
55
-
56
- INFINITE = 0xFFFFFFFF
57
- WAIT_OBJECT_0 = 0
58
- WAIT_TIMEOUT = 0x102
59
- WAIT_ABANDONED = 128
60
- WAIT_ABANDONED_0 = WAIT_ABANDONED
61
- WAIT_FAILED = 0xFFFFFFFF
62
-
63
- STATUS_WAIT_0 = 0
64
- STATUS_ABANDONED_WAIT_0 = 128
65
- STATUS_USER_APC = 192
66
- STATUS_TIMEOUT = 258
67
- STATUS_PENDING = 259
68
-
69
- ERROR_IO_INCOMPLETE = 996
70
- ERROR_IO_PENDING = 997
71
- end
72
- end
1
+ module Windows
2
+ module Constants
3
+ INVALID_HANDLE_VALUE = 0xFFFFFFFF
4
+
5
+ CREATE_NEW = 1
6
+ CREATE_ALWAYS = 2
7
+ OPEN_EXISTING = 3
8
+ OPEN_ALWAYS = 4
9
+ TRUNCATE_EXISTING = 5
10
+
11
+ GENERIC_READ = 0x80000000
12
+ GENERIC_WRITE = 0x40000000
13
+ GENERIC_EXECUTE = 0x20000000
14
+ GENERIC_ALL = 0x10000000
15
+
16
+ FILE_SHARE_READ = 0x00000001
17
+
18
+ FILE_FLAG_WRITE_THROUGH = 0x80000000
19
+ FILE_FLAG_OVERLAPPED = 0x40000000
20
+ FILE_FLAG_NO_BUFFERING = 0x20000000
21
+ FILE_FLAG_RANDOM_ACCESS = 0x10000000
22
+ FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
23
+ FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
24
+ FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
25
+ FILE_FLAG_POSIX_SEMANTICS = 0x01000000
26
+ FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
27
+ FILE_FLAG_OPEN_NO_RECALL = 0x00100000
28
+ FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
29
+
30
+ MEM_COMMIT = 0x1000
31
+ MEM_RESERVE = 0x2000
32
+ MEM_DECOMMIT = 0x4000
33
+ MEM_RELEASE = 0x8000
34
+ MEM_FREE = 0x10000
35
+ MEM_PRIVATE = 0x20000
36
+ MEM_MAPPED = 0x40000
37
+ MEM_RESET = 0x80000
38
+ MEM_TOP_DOWN = 0x100000
39
+ MEM_WRITE_WATCH = 0x200000
40
+ MEM_PHYSICAL = 0x400000
41
+ MEM_LARGE_PAGES = 0x20000000
42
+ MEM_4MB_PAGES = 0x80000000
43
+
44
+ PAGE_NOACCESS = 0x01
45
+ PAGE_READONLY = 0x02
46
+ PAGE_READWRITE = 0x04
47
+ PAGE_WRITECOPY = 0x08
48
+ PAGE_EXECUTE = 0x10
49
+ PAGE_EXECUTE_READ = 0x20
50
+ PAGE_EXECUTE_READWRITE = 0x40
51
+ PAGE_EXECUTE_WRITECOPY = 0x80
52
+ PAGE_GUARD = 0x100
53
+ PAGE_NOCACHE = 0x200
54
+ PAGE_WRITECOMBINE = 0x400
55
+
56
+ INFINITE = 0xFFFFFFFF
57
+ WAIT_OBJECT_0 = 0
58
+ WAIT_TIMEOUT = 0x102
59
+ WAIT_ABANDONED = 128
60
+ WAIT_ABANDONED_0 = WAIT_ABANDONED
61
+ WAIT_FAILED = 0xFFFFFFFF
62
+
63
+ STATUS_WAIT_0 = 0
64
+ STATUS_ABANDONED_WAIT_0 = 128
65
+ STATUS_USER_APC = 192
66
+ STATUS_TIMEOUT = 258
67
+ STATUS_PENDING = 259
68
+
69
+ ERROR_IO_INCOMPLETE = 996
70
+ ERROR_IO_PENDING = 997
71
+ end
72
+ end