win32-nio 0.1.1 → 0.1.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.
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