win32-nio 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +7 -0
- data/README +51 -38
- data/Rakefile +40 -9
- data/ext/extconf.rb +2 -0
- data/ext/win32/nio.c +347 -0
- data/test/test_win32_nio_read.rb +3 -3
- data/test/test_win32_nio_readlines.rb +12 -3
- data/win32-nio.gemspec +9 -9
- metadata +17 -33
- data/lib/win32/nio.rb +0 -200
- data/lib/win32/windows/constants.rb +0 -77
- data/lib/win32/windows/functions.rb +0 -27
- data/lib/win32/windows/macros.rb +0 -7
- data/lib/win32/windows/structs.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31fe6e337f6c93527d80360c231b9fb5766e2399
|
4
|
+
data.tar.gz: bac2e6d821bc01dd2814f8a09b749d476da7d74e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2985ccd28d539b4433ebf2a163fe98567b596b965ef0259eccba7df26cdf670e6cf5a7cb102b58d7a4dfd3f3d04a52973f527248aba7d16687cd70efb7fe34af
|
7
|
+
data.tar.gz: eeae941454d811133a0d6cf7e84093a855fbfb992e633ce4e0a895140a6780ca75780f940dfda54cc5b82d0de2d7cc16a836a22bb1a029b359fe681811bedb33
|
data/CHANGES
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
== 0.2.0 - 30-Mar-2015
|
2
|
+
* Converted to C code for performance.
|
3
|
+
* The NIO.readlines method now accepts an optional event object as
|
4
|
+
an argument.
|
5
|
+
* A negative offset value will now raise an ArgumentError.
|
6
|
+
* Some documentation updates.
|
7
|
+
|
1
8
|
== 0.1.3 - 21-Oct-2013
|
2
9
|
* Fixed INVALID_HANDLE_VALUE for 64-bit versions of Ruby.
|
3
10
|
* Added Rake as a development dependency.
|
data/README
CHANGED
@@ -9,58 +9,71 @@
|
|
9
9
|
require 'win32/nio'
|
10
10
|
include Win32
|
11
11
|
|
12
|
+
# Similar to Ruby
|
12
13
|
p NIO.read('some_file.text')
|
13
|
-
|
14
|
+
p NIO.read('some_file.text', 50)
|
15
|
+
p NIO.read('some_file.text', 50, 5)
|
16
|
+
|
14
17
|
p NIO.readlines('some_file.txt')
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
18
|
+
p NIO.readlines('some_file.txt', '')
|
19
|
+
|
20
|
+
# With events
|
21
|
+
event = Win32::Event.new
|
22
|
+
|
23
|
+
NIO.read('some_big_file.txt', nil, nil, event)
|
24
|
+
p event.signaled? # => true
|
25
|
+
|
26
|
+
# With blocks
|
27
|
+
NIO.read('some_big_file.txt'){ puts "Finished" }
|
28
|
+
|
29
|
+
= Proof of Concept
|
30
|
+
This code was originally written to see if using pure Windows functions
|
31
|
+
to implement MRI methods would offer any practical advantage. The answer
|
32
|
+
is a definite maybe.
|
27
33
|
|
34
|
+
Functionally, these library does offer something the MRI methods do not,
|
35
|
+
which is the ability to provide a block or event object that is called
|
36
|
+
when a read is finished.
|
37
|
+
|
38
|
+
In terms of speed, I've found the NIO.read method to be almost twice as
|
39
|
+
fast as Ruby 2.2, while the NIO.readlines method was about the same speed.
|
40
|
+
In most cases the NIO.readlines method offers no practical advantage, but
|
41
|
+
see the documentation for details.
|
42
|
+
|
28
43
|
= Benchmarks
|
29
44
|
|
30
|
-
Using my
|
31
|
-
|
45
|
+
Using my current laptop running Windows 7 I saw these results, which were
|
46
|
+
typical in repeated runs:
|
47
|
+
|
48
|
+
IO.read(small) 0.000000 0.016000 0.016000 ( 0.020342)
|
49
|
+
NIO.read(small) 0.000000 0.000000 0.000000 ( 0.008140)
|
50
|
+
IO.read(medium) 0.187000 0.047000 0.234000 ( 0.230579)
|
51
|
+
NIO.read(medium) 0.015000 0.093000 0.108000 ( 0.122542)
|
52
|
+
IO.read(large) 1.654000 0.593000 2.247000 ( 2.355478)
|
53
|
+
NIO.read(large) 0.343000 0.765000 1.108000 ( 1.222567)
|
54
|
+
IO.readlines(small) 0.125000 0.000000 0.125000 ( 0.119982)
|
55
|
+
NIO.readlines(small) 0.094000 0.015000 0.109000 ( 0.132461)
|
56
|
+
IO.readlines(medium) 1.419000 0.234000 1.653000 ( 1.764216)
|
57
|
+
NIO.readlines(medium) 1.092000 0.109000 1.201000 ( 1.537593)
|
58
|
+
IO.readlines(large) 12.714000 0.874000 13.588000 ( 13.798494)
|
59
|
+
NIO.readlines(large) 9.719000 0.468000 10.187000 ( 13.437217)
|
60
|
+
|
61
|
+
Your results may vary.
|
62
|
+
|
63
|
+
= JRuby
|
64
|
+
As of version 0.2.0 this code was written as a C extension, and
|
65
|
+
does not support JRuby. However, JRuby users can continue to use
|
66
|
+
the 0.1.x branch.
|
32
67
|
|
33
|
-
user system total real
|
34
|
-
IO.read(small) 0.016000 0.047000 0.063000 ( 0.063000)
|
35
|
-
NIO.read(small) 0.109000 0.000000 0.109000 ( 0.109000)
|
36
|
-
IO.read(medium) 0.422000 0.094000 0.516000 ( 0.532000)
|
37
|
-
NIO.read(medium) 0.937000 0.062000 0.999000 ( 1.047000)
|
38
|
-
IO.read(large) 3.282000 1.063000 4.345000 ( 4.468000)
|
39
|
-
NIO.read(large) 8.187000 1.062000 9.249000 ( 9.454000)
|
40
|
-
IO.readlines(small) 0.156000 0.063000 0.219000 ( 0.234000)
|
41
|
-
NIO.readlines(small) 0.141000 0.000000 0.141000 ( 0.797000)
|
42
|
-
IO.readlines(medium) 2.984000 0.250000 3.234000 ( 3.625000)
|
43
|
-
NIO.readlines(medium) 1.172000 0.062000 1.234000 ( 3.406000)
|
44
|
-
IO.readlines(large) 18.625000 2.281000 20.906000 ( 21.532000)
|
45
|
-
NIO.readlines(large) 12.406000 0.563000 12.969000 (122.643000)
|
46
|
-
|
47
|
-
Note that, in most cases, the user and system time has decreased, but
|
48
|
-
the real time has increased.
|
49
|
-
|
50
68
|
= Known Bugs
|
51
69
|
None that I know of. Please log any other bug reports on the RubyForge
|
52
70
|
project page at https://github.com/djberg96/win32-nio.
|
53
71
|
|
54
|
-
= Future Plans
|
55
|
-
The pure Ruby code is really only meant for prototyping. The eventual
|
56
|
-
plan is to convert the Ruby code to equivalent C code in order to improve
|
57
|
-
performance.
|
58
|
-
|
59
72
|
= License
|
60
73
|
Artistic 2.0
|
61
74
|
|
62
75
|
= Copyright
|
63
|
-
(C) 2008-
|
76
|
+
(C) 2008-2015 Daniel J. Berger, All Rights Reserved
|
64
77
|
|
65
78
|
= Warranty
|
66
79
|
This package is provided "as is" and without any express or
|
data/Rakefile
CHANGED
@@ -1,19 +1,44 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rake/testtask'
|
4
|
+
include RbConfig
|
4
5
|
|
5
|
-
CLEAN.include(
|
6
|
+
CLEAN.include(
|
7
|
+
'**/*.gem', # Gem files
|
8
|
+
'**/*.rbc', # Rubinius
|
9
|
+
'**/*.o', # C object file
|
10
|
+
'**/*.log', # Ruby extension build log
|
11
|
+
'**/Makefile', # C Makefile
|
12
|
+
'**/*.def', # Definition files
|
13
|
+
'**/*.exp',
|
14
|
+
'**/*.lib',
|
15
|
+
'**/*.pdb',
|
16
|
+
'**/*.obj',
|
17
|
+
'**/*.stackdump', # Junk that can happen on Windows
|
18
|
+
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
19
|
+
)
|
20
|
+
|
21
|
+
desc "Build the win32-nio library"
|
22
|
+
task :build => [:clean] do
|
23
|
+
if RbConfig::CONFIG['host_os'] =~ /mingw|cygwn/i
|
24
|
+
require 'devkit'
|
25
|
+
make_cmd = "make"
|
26
|
+
else
|
27
|
+
make_cmd = "nmake"
|
28
|
+
end
|
29
|
+
Dir.chdir('ext') do
|
30
|
+
ruby "extconf.rb"
|
31
|
+
sh make_cmd
|
32
|
+
cp 'nio.so', 'win32' # For testing
|
33
|
+
end
|
34
|
+
end
|
6
35
|
|
7
36
|
namespace 'gem' do
|
8
37
|
desc 'Create the win32-nio gem'
|
9
38
|
task :create => [:clean] do
|
39
|
+
require 'rubygems/package'
|
10
40
|
spec = eval(IO.read('win32-nio.gemspec'))
|
11
|
-
|
12
|
-
Gem::Builder.new(spec).build
|
13
|
-
else
|
14
|
-
require 'rubygems/package'
|
15
|
-
Gem::Package.build(spec)
|
16
|
-
end
|
41
|
+
Gem::Package.build(spec)
|
17
42
|
end
|
18
43
|
|
19
44
|
desc 'Install the win32-nio gem'
|
@@ -24,24 +49,30 @@ namespace 'gem' do
|
|
24
49
|
end
|
25
50
|
|
26
51
|
desc 'Run the benchmark suite'
|
27
|
-
task :bench do
|
28
|
-
sh "ruby -
|
52
|
+
task :bench => [:build] do
|
53
|
+
sh "ruby -Iext benchmarks/win32_nio_benchmarks.rb"
|
29
54
|
end
|
30
55
|
|
31
56
|
namespace :test do
|
32
57
|
Rake::TestTask.new(:read) do |t|
|
58
|
+
task :read => [:build]
|
59
|
+
t.libs << 'ext'
|
33
60
|
t.verbose = true
|
34
61
|
t.warning = true
|
35
62
|
t.test_files = FileList['test/test_win32_nio_read.rb']
|
36
63
|
end
|
37
64
|
|
38
65
|
Rake::TestTask.new(:readlines) do |t|
|
66
|
+
task :readlines => [:build]
|
67
|
+
t.libs << 'ext'
|
39
68
|
t.verbose = true
|
40
69
|
t.warning = true
|
41
70
|
t.test_files = FileList['test/test_win32_nio_readlines.rb']
|
42
71
|
end
|
43
72
|
|
44
73
|
Rake::TestTask.new(:all) do |t|
|
74
|
+
task :all => [:build]
|
75
|
+
t.libs << 'ext'
|
45
76
|
t.verbose = true
|
46
77
|
t.warning = true
|
47
78
|
t.test_files = FileList['test/test*.rb']
|
data/ext/extconf.rb
ADDED
data/ext/win32/nio.c
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/encoding.h>
|
3
|
+
#include <windows.h>
|
4
|
+
#include <math.h>
|
5
|
+
|
6
|
+
// Callback used when a block is provided to read method. It simply calls the block.
|
7
|
+
void CALLBACK read_complete(DWORD dwErrorCode, DWORD dwBytes, LPOVERLAPPED olap){
|
8
|
+
VALUE p = rb_block_proc();
|
9
|
+
rb_funcall(p, rb_intern("call"), 0);
|
10
|
+
}
|
11
|
+
|
12
|
+
// Helper function to raise system errors the way I would like
|
13
|
+
void rb_raise_syserr(const char* msg, int errnum){
|
14
|
+
VALUE v_sys = rb_funcall(rb_eSystemCallError, rb_intern("new"), 2, rb_str_new2(msg), INT2FIX(errnum));
|
15
|
+
rb_funcall(rb_mKernel, rb_intern("raise"), 1, v_sys);
|
16
|
+
}
|
17
|
+
|
18
|
+
/*
|
19
|
+
* This method is similar to Ruby's IO.read method except that it uses
|
20
|
+
* native function calls.
|
21
|
+
*
|
22
|
+
* Examples:
|
23
|
+
*
|
24
|
+
* # Read everything
|
25
|
+
* Win32::NIO.read(file)
|
26
|
+
*
|
27
|
+
* # Read the first 100 bytes
|
28
|
+
* Win32::NIO.read(file, 100)
|
29
|
+
*
|
30
|
+
* # Read 50 bytes starting at offset 10
|
31
|
+
* Win32::NIO.read(file, 50, 10)
|
32
|
+
*
|
33
|
+
* Note that the + options + that may be passed to this method are limited
|
34
|
+
* to :encoding, : mode and : event because we're no longer using the open
|
35
|
+
* function internally.In the case of:mode the only thing that is checked
|
36
|
+
* for is the presence of the 'b' (binary)mode.
|
37
|
+
*
|
38
|
+
* The :event option, if present, must be a Win32::Event object.
|
39
|
+
*/
|
40
|
+
static VALUE rb_nio_read(int argc, VALUE* argv, VALUE self){
|
41
|
+
OVERLAPPED olap;
|
42
|
+
HANDLE h;
|
43
|
+
DWORD bytes_read;
|
44
|
+
LARGE_INTEGER lsize;
|
45
|
+
BOOL b;
|
46
|
+
VALUE v_file, v_length, v_offset, v_options;
|
47
|
+
VALUE v_event, v_mode, v_encoding, v_result;
|
48
|
+
size_t length;
|
49
|
+
int flags, error, size;
|
50
|
+
wchar_t* file = NULL;
|
51
|
+
char* buffer = NULL;
|
52
|
+
|
53
|
+
memset(&olap, 0, sizeof(olap));
|
54
|
+
|
55
|
+
// Paranoid initialization
|
56
|
+
v_length = Qnil;
|
57
|
+
v_offset = Qnil;
|
58
|
+
v_options = Qnil;
|
59
|
+
|
60
|
+
rb_scan_args(argc, argv, "13", &v_file, &v_length, &v_offset, &v_options);
|
61
|
+
|
62
|
+
// Allow path-y objects.
|
63
|
+
if (rb_respond_to(v_file, rb_intern("to_path")))
|
64
|
+
v_file = rb_funcall(v_file, rb_intern("to_path"), 0, NULL);
|
65
|
+
|
66
|
+
SafeStringValue(v_file);
|
67
|
+
|
68
|
+
// If a length is provided it cannot be negative.
|
69
|
+
if (!NIL_P(v_length)){
|
70
|
+
length = NUM2SIZET(v_length);
|
71
|
+
if ((int)length < 0)
|
72
|
+
rb_raise(rb_eArgError, "negative length %i given", (int)length);
|
73
|
+
}
|
74
|
+
else{
|
75
|
+
length = 0; // Mostly to make gcc stfu. We'll set this later.
|
76
|
+
}
|
77
|
+
|
78
|
+
// Unlike MRI, don't wait for an internal C function to fail on an invalid argument.
|
79
|
+
if (!NIL_P(v_offset)){
|
80
|
+
olap.Offset = NUM2ULONG(v_offset);
|
81
|
+
if ((int)olap.Offset < 0)
|
82
|
+
rb_raise(rb_eArgError, "negative offset %i given", (int)olap.Offset);
|
83
|
+
}
|
84
|
+
|
85
|
+
// Gotta do this for wide character function support.
|
86
|
+
size = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, NULL, 0);
|
87
|
+
file = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
88
|
+
|
89
|
+
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, file, size)){
|
90
|
+
ruby_xfree(file);
|
91
|
+
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
92
|
+
}
|
93
|
+
|
94
|
+
flags = FILE_FLAG_SEQUENTIAL_SCAN;
|
95
|
+
|
96
|
+
// Possible options are :event, :mode and :encoding
|
97
|
+
if (!NIL_P(v_options)){
|
98
|
+
Check_Type(v_options, T_HASH);
|
99
|
+
|
100
|
+
v_event = rb_hash_aref(v_options, ID2SYM(rb_intern("event")));
|
101
|
+
v_encoding = rb_hash_aref(v_options, ID2SYM(rb_intern("encoding")));
|
102
|
+
v_mode = rb_hash_aref(v_options, ID2SYM(rb_intern("mode")));
|
103
|
+
|
104
|
+
if (!NIL_P(v_event)){
|
105
|
+
flags |= FILE_FLAG_OVERLAPPED;
|
106
|
+
olap.hEvent = (HANDLE)NUM2OFFT(rb_funcall(v_event, rb_intern("handle"), 0, 0));
|
107
|
+
}
|
108
|
+
}
|
109
|
+
else{
|
110
|
+
v_event = Qnil;
|
111
|
+
v_encoding = Qnil;
|
112
|
+
v_mode = Qnil;
|
113
|
+
}
|
114
|
+
|
115
|
+
h = CreateFileW(
|
116
|
+
file,
|
117
|
+
GENERIC_READ,
|
118
|
+
FILE_SHARE_READ,
|
119
|
+
NULL,
|
120
|
+
OPEN_EXISTING,
|
121
|
+
flags,
|
122
|
+
NULL
|
123
|
+
);
|
124
|
+
|
125
|
+
if (h == INVALID_HANDLE_VALUE)
|
126
|
+
rb_raise_syserr("CreateFile", GetLastError());
|
127
|
+
|
128
|
+
// Get the file size. We may use this later to limit read length.
|
129
|
+
if (!GetFileSizeEx(h, &lsize)){
|
130
|
+
error = GetLastError();
|
131
|
+
CloseHandle(h);
|
132
|
+
rb_raise_syserr("GetFileSizeEx", error);
|
133
|
+
}
|
134
|
+
|
135
|
+
// If no length is specified, read the entire file
|
136
|
+
if (NIL_P(v_length))
|
137
|
+
length = (size_t)lsize.QuadPart;
|
138
|
+
|
139
|
+
// Don't read past the end of the file
|
140
|
+
if (olap.Offset + length > (size_t)lsize.QuadPart)
|
141
|
+
length = (size_t)lsize.QuadPart - olap.Offset;
|
142
|
+
|
143
|
+
buffer = (char*)ruby_xmalloc(length * sizeof(char));
|
144
|
+
|
145
|
+
// If a block is given then treat it as a callback
|
146
|
+
if (rb_block_given_p()){
|
147
|
+
flags |= FILE_FLAG_OVERLAPPED;
|
148
|
+
b = ReadFileEx(h, buffer, length, &olap, read_complete);
|
149
|
+
}
|
150
|
+
else{
|
151
|
+
b = ReadFile(h, buffer, length, &bytes_read, &olap);
|
152
|
+
}
|
153
|
+
|
154
|
+
error = GetLastError();
|
155
|
+
|
156
|
+
// Put in alertable wait state if overlapped IO
|
157
|
+
if (flags & FILE_FLAG_OVERLAPPED)
|
158
|
+
SleepEx(1, TRUE);
|
159
|
+
|
160
|
+
if (!b){
|
161
|
+
if(error == ERROR_IO_PENDING){
|
162
|
+
DWORD bytes;
|
163
|
+
if (!GetOverlappedResult(h, &olap, &bytes, TRUE)){
|
164
|
+
ruby_xfree(buffer);
|
165
|
+
CloseHandle(h);
|
166
|
+
rb_raise_syserr("GetOverlappedResult", error);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
else{
|
170
|
+
ruby_xfree(buffer);
|
171
|
+
CloseHandle(h);
|
172
|
+
rb_raise_syserr("ReadFile", error);
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
CloseHandle(h);
|
177
|
+
|
178
|
+
v_result = rb_str_new(buffer, length);
|
179
|
+
ruby_xfree(buffer);
|
180
|
+
|
181
|
+
// Convert CRLF to LF if text mode
|
182
|
+
if (!NIL_P(v_mode) && strstr(RSTRING_PTR(v_mode), "t"))
|
183
|
+
rb_funcall(v_result, rb_intern("gsub!"), 2, rb_str_new2("\r\n"), rb_gv_get("$/"));
|
184
|
+
|
185
|
+
if (!NIL_P(v_encoding))
|
186
|
+
rb_funcall(v_result, rb_intern("encode!"), 1, v_encoding);
|
187
|
+
|
188
|
+
return v_result;
|
189
|
+
}
|
190
|
+
|
191
|
+
/*
|
192
|
+
* Reads the entire file specified by portname as individual lines, and
|
193
|
+
* returns those lines in an array. Lines are separated by +sep+.
|
194
|
+
*
|
195
|
+
* Examples:
|
196
|
+
*
|
197
|
+
* # Standard call
|
198
|
+
* Win32::NIO.readlines('file.txt') # => ['line 1', 'line 2', 'line 3', 'line 4']
|
199
|
+
*
|
200
|
+
* # Paragraph mode
|
201
|
+
* Win32::NIO.readlines('file.txt', '') # => ['line 1\r\nline 2', 'line 3\r\nline 4']
|
202
|
+
*
|
203
|
+
* # With event
|
204
|
+
* event = Win32::Event.new
|
205
|
+
* Win32::NIO.readlines('file.txt', nil, event)
|
206
|
+
* p event.signaled? # => true
|
207
|
+
*
|
208
|
+
* Superficially this method acts the same as the Ruby IO.readlines call, except that
|
209
|
+
* it does not transform line endings and accepts an optional event object. However,
|
210
|
+
* internally this method is using a scattered read to accomplish its goal. In practice
|
211
|
+
* this is only relevant in specific situations. Using it outside of those situations
|
212
|
+
* is unlikely to provide any practical benefit, and may even result in slower performance.
|
213
|
+
*
|
214
|
+
* See information on vectored IO for more details.
|
215
|
+
*/
|
216
|
+
static VALUE rb_nio_readlines(int argc, VALUE* argv, VALUE self){
|
217
|
+
HANDLE h;
|
218
|
+
SYSTEM_INFO info;
|
219
|
+
LARGE_INTEGER file_size;
|
220
|
+
size_t length, page_size;
|
221
|
+
double size;
|
222
|
+
void* base_address;
|
223
|
+
int error, page_num;
|
224
|
+
wchar_t* file;
|
225
|
+
VALUE v_file, v_sep, v_event, v_result;
|
226
|
+
|
227
|
+
rb_scan_args(argc, argv, "12", &v_file, &v_sep, &v_event);
|
228
|
+
|
229
|
+
SafeStringValue(v_file);
|
230
|
+
|
231
|
+
if (NIL_P(v_sep))
|
232
|
+
v_sep = rb_str_new2("\r\n");
|
233
|
+
else
|
234
|
+
SafeStringValue(v_sep);
|
235
|
+
|
236
|
+
v_result = Qnil;
|
237
|
+
|
238
|
+
length = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, NULL, 0);
|
239
|
+
file = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
240
|
+
|
241
|
+
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, file, length)){
|
242
|
+
ruby_xfree(file);
|
243
|
+
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
244
|
+
}
|
245
|
+
|
246
|
+
h = CreateFileW(
|
247
|
+
file,
|
248
|
+
GENERIC_READ,
|
249
|
+
FILE_SHARE_READ,
|
250
|
+
NULL,
|
251
|
+
OPEN_EXISTING,
|
252
|
+
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
253
|
+
NULL
|
254
|
+
);
|
255
|
+
|
256
|
+
if (h == INVALID_HANDLE_VALUE)
|
257
|
+
rb_raise_syserr("CreateFile", GetLastError());
|
258
|
+
|
259
|
+
if (!GetFileSizeEx(h, &file_size)){
|
260
|
+
error = GetLastError();
|
261
|
+
CloseHandle(h);
|
262
|
+
rb_raise_syserr("GetFileSizeEx", error);
|
263
|
+
}
|
264
|
+
|
265
|
+
length = (size_t)file_size.QuadPart;
|
266
|
+
|
267
|
+
GetSystemInfo(&info);
|
268
|
+
page_size = info.dwPageSize;
|
269
|
+
|
270
|
+
page_num = (int)ceil((double)length / page_size);
|
271
|
+
|
272
|
+
size = page_num * page_size;
|
273
|
+
|
274
|
+
base_address = VirtualAlloc(NULL, (size_t)size, MEM_COMMIT, PAGE_READWRITE);
|
275
|
+
|
276
|
+
if (!base_address){
|
277
|
+
error = GetLastError();
|
278
|
+
CloseHandle(h);
|
279
|
+
rb_raise_syserr("VirtualAlloc", error);
|
280
|
+
}
|
281
|
+
else{
|
282
|
+
int i;
|
283
|
+
OVERLAPPED olap;
|
284
|
+
BOOL rv;
|
285
|
+
FILE_SEGMENT_ELEMENT* fse;
|
286
|
+
|
287
|
+
olap.Offset = 0;
|
288
|
+
olap.OffsetHigh = 0;
|
289
|
+
|
290
|
+
if (NIL_P(v_event))
|
291
|
+
olap.hEvent = NULL;
|
292
|
+
else
|
293
|
+
olap.hEvent = (HANDLE)NUM2OFFT(rb_funcall(v_event, rb_intern("handle"), 0, 0));
|
294
|
+
|
295
|
+
fse = (FILE_SEGMENT_ELEMENT*)malloc(sizeof(FILE_SEGMENT_ELEMENT) * (page_num + 1));
|
296
|
+
memset(fse, 0, sizeof(FILE_SEGMENT_ELEMENT) * (page_num + 1));
|
297
|
+
v_result = Qnil;
|
298
|
+
|
299
|
+
for (i = 0; i < page_num; i++)
|
300
|
+
fse[i].Alignment = (ULONGLONG)base_address + (page_size * i);
|
301
|
+
|
302
|
+
rv = ReadFileScatter(h, fse, (DWORD)size, NULL, &olap);
|
303
|
+
|
304
|
+
if (!rv){
|
305
|
+
error = GetLastError();
|
306
|
+
|
307
|
+
if (error == ERROR_IO_PENDING){
|
308
|
+
while (!HasOverlappedIoCompleted(&olap))
|
309
|
+
SleepEx(1, TRUE);
|
310
|
+
}
|
311
|
+
else{
|
312
|
+
VirtualFree(base_address, 0, MEM_RELEASE);
|
313
|
+
CloseHandle(h);
|
314
|
+
rb_raise_syserr("ReadFileScatter", error);
|
315
|
+
}
|
316
|
+
}
|
317
|
+
|
318
|
+
// Explicitly handle paragraph mode
|
319
|
+
if (rb_equal(v_sep, rb_str_new2(""))){
|
320
|
+
VALUE v_args[1];
|
321
|
+
v_args[0] = rb_str_new2("(\r\n){2,}");
|
322
|
+
v_sep = rb_class_new_instance(1, v_args, rb_cRegexp);
|
323
|
+
v_result = rb_funcall(rb_str_new2(fse[0].Buffer), rb_intern("split"), 1, v_sep);
|
324
|
+
rb_funcall(v_result, rb_intern("delete"), 1, rb_str_new2("\r\n"));
|
325
|
+
}
|
326
|
+
else{
|
327
|
+
v_result = rb_funcall(rb_str_new2(fse[0].Buffer), rb_intern("split"), 1, v_sep);
|
328
|
+
}
|
329
|
+
|
330
|
+
VirtualFree(base_address, 0, MEM_RELEASE);
|
331
|
+
}
|
332
|
+
|
333
|
+
CloseHandle(h);
|
334
|
+
|
335
|
+
return v_result;
|
336
|
+
}
|
337
|
+
|
338
|
+
void Init_nio(){
|
339
|
+
VALUE mWin32 = rb_define_module("Win32");
|
340
|
+
VALUE cNio = rb_define_class_under(mWin32, "NIO", rb_cObject);
|
341
|
+
|
342
|
+
rb_define_singleton_method(cNio, "read", rb_nio_read, -1);
|
343
|
+
rb_define_singleton_method(cNio, "readlines", rb_nio_readlines, -1);
|
344
|
+
|
345
|
+
/* 0.2.0: The version of the win32-nio library */
|
346
|
+
rb_define_const(cNio, "VERSION", rb_str_new2("0.2.0"));
|
347
|
+
}
|
data/test/test_win32_nio_read.rb
CHANGED
@@ -24,7 +24,7 @@ class TC_Win32_NIO_Read < Test::Unit::TestCase
|
|
24
24
|
end
|
25
25
|
|
26
26
|
test "version number is set to expected value" do
|
27
|
-
assert_equal('0.
|
27
|
+
assert_equal('0.2.0', Win32::NIO::VERSION)
|
28
28
|
end
|
29
29
|
|
30
30
|
test "read method basic functionality" do
|
@@ -61,7 +61,7 @@ class TC_Win32_NIO_Read < Test::Unit::TestCase
|
|
61
61
|
end
|
62
62
|
|
63
63
|
test "offset parameter must be a positive number" do
|
64
|
-
assert_raise(
|
64
|
+
assert_raise(ArgumentError){ NIO.read(@@file, 1, -1) }
|
65
65
|
assert_raise(TypeError){ NIO.read(@@file, 1, 'foo') }
|
66
66
|
end
|
67
67
|
|
@@ -74,7 +74,7 @@ class TC_Win32_NIO_Read < Test::Unit::TestCase
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def self.shutdown
|
77
|
-
File.delete(@@file) if File.
|
77
|
+
File.delete(@@file) if File.exist?(@@file)
|
78
78
|
@@file = nil
|
79
79
|
@@text = nil
|
80
80
|
end
|
@@ -5,6 +5,7 @@
|
|
5
5
|
#######################################################################
|
6
6
|
require 'test-unit'
|
7
7
|
require 'win32/nio'
|
8
|
+
require 'win32/event'
|
8
9
|
include Win32
|
9
10
|
|
10
11
|
class TC_Win32_NIO_Readlines < Test::Unit::TestCase
|
@@ -23,6 +24,7 @@ class TC_Win32_NIO_Readlines < Test::Unit::TestCase
|
|
23
24
|
|
24
25
|
def setup
|
25
26
|
@array = nil
|
27
|
+
@event = Win32::Event.new
|
26
28
|
end
|
27
29
|
|
28
30
|
test "readlines method basic functionality" do
|
@@ -45,20 +47,27 @@ class TC_Win32_NIO_Readlines < Test::Unit::TestCase
|
|
45
47
|
assert_equal(4, NIO.readlines(@@file, '').size)
|
46
48
|
end
|
47
49
|
|
50
|
+
test "readlines accepts an event object" do
|
51
|
+
assert_false(@event.signaled?)
|
52
|
+
assert_nothing_raised{ NIO.readlines(@@file, nil, @event) }
|
53
|
+
assert_true(@event.signaled?)
|
54
|
+
end
|
55
|
+
|
48
56
|
test "readlines expects at least one argument" do
|
49
57
|
assert_raise(ArgumentError){ NIO.readlines }
|
50
58
|
end
|
51
59
|
|
52
|
-
test "readlines accepts a maximum of
|
53
|
-
assert_raise(ArgumentError){ NIO.readlines(@@file, '', true) }
|
60
|
+
test "readlines accepts a maximum of three arguments" do
|
61
|
+
assert_raise(ArgumentError){ NIO.readlines(@@file, '', @event, true) }
|
54
62
|
end
|
55
63
|
|
56
64
|
def teardown
|
57
65
|
@array = nil
|
66
|
+
@event = nil
|
58
67
|
end
|
59
68
|
|
60
69
|
def self.shutdown
|
61
|
-
File.delete(@@file) if File.
|
70
|
+
File.delete(@@file) if File.exist?(@@file)
|
62
71
|
@@file = nil
|
63
72
|
@@size = nil
|
64
73
|
@@line = nil
|
data/win32-nio.gemspec
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
|
-
spec.name
|
5
|
-
spec.version
|
6
|
-
spec.author
|
7
|
-
spec.license
|
8
|
-
spec.email
|
9
|
-
spec.homepage
|
10
|
-
spec.summary
|
11
|
-
spec.files
|
4
|
+
spec.name = 'win32-nio'
|
5
|
+
spec.version = '0.2.0'
|
6
|
+
spec.author = 'Daniel J. Berger'
|
7
|
+
spec.license = 'Artistic 2.0'
|
8
|
+
spec.email = 'djberg96@gmail.com'
|
9
|
+
spec.homepage = 'https://github.com/djberg96/win32-nio'
|
10
|
+
spec.summary = 'Native IO for MS Windows'
|
11
|
+
spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
12
|
+
spec.extensions = ['ext/extconf.rb']
|
12
13
|
|
13
14
|
spec.rubyforge_project = 'Win32Utils'
|
14
15
|
spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
15
16
|
spec.required_ruby_version = '> 1.9.0'
|
16
17
|
|
17
|
-
spec.add_dependency('ffi')
|
18
18
|
spec.add_dependency('win32-event', '>= 0.6.0')
|
19
19
|
|
20
20
|
spec.add_development_dependency('rake')
|
metadata
CHANGED
@@ -1,69 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: win32-nio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel J. Berger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-03-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: ffi
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - '>='
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - '>='
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: win32-event
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
30
16
|
requirements:
|
31
|
-
- -
|
17
|
+
- - ">="
|
32
18
|
- !ruby/object:Gem::Version
|
33
19
|
version: 0.6.0
|
34
20
|
type: :runtime
|
35
21
|
prerelease: false
|
36
22
|
version_requirements: !ruby/object:Gem::Requirement
|
37
23
|
requirements:
|
38
|
-
- -
|
24
|
+
- - ">="
|
39
25
|
- !ruby/object:Gem::Version
|
40
26
|
version: 0.6.0
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: rake
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
44
30
|
requirements:
|
45
|
-
- -
|
31
|
+
- - ">="
|
46
32
|
- !ruby/object:Gem::Version
|
47
33
|
version: '0'
|
48
34
|
type: :development
|
49
35
|
prerelease: false
|
50
36
|
version_requirements: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
|
-
- -
|
38
|
+
- - ">="
|
53
39
|
- !ruby/object:Gem::Version
|
54
40
|
version: '0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: test-unit
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
|
-
- -
|
45
|
+
- - ">="
|
60
46
|
- !ruby/object:Gem::Version
|
61
47
|
version: '0'
|
62
48
|
type: :development
|
63
49
|
prerelease: false
|
64
50
|
version_requirements: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
|
-
- -
|
52
|
+
- - ">="
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '0'
|
69
55
|
description: |2
|
@@ -73,22 +59,20 @@ description: |2
|
|
73
59
|
additional event handling capability.
|
74
60
|
email: djberg96@gmail.com
|
75
61
|
executables: []
|
76
|
-
extensions:
|
62
|
+
extensions:
|
63
|
+
- ext/extconf.rb
|
77
64
|
extra_rdoc_files:
|
78
65
|
- README
|
79
66
|
- CHANGES
|
80
67
|
- MANIFEST
|
81
68
|
files:
|
82
|
-
- benchmarks/win32_nio_benchmarks.rb
|
83
69
|
- CHANGES
|
84
|
-
- lib/win32/nio.rb
|
85
|
-
- lib/win32/windows/constants.rb
|
86
|
-
- lib/win32/windows/functions.rb
|
87
|
-
- lib/win32/windows/macros.rb
|
88
|
-
- lib/win32/windows/structs.rb
|
89
70
|
- MANIFEST
|
90
|
-
- Rakefile
|
91
71
|
- README
|
72
|
+
- Rakefile
|
73
|
+
- benchmarks/win32_nio_benchmarks.rb
|
74
|
+
- ext/extconf.rb
|
75
|
+
- ext/win32/nio.c
|
92
76
|
- test/test_win32_nio_read.rb
|
93
77
|
- test/test_win32_nio_readlines.rb
|
94
78
|
- win32-nio.gemspec
|
@@ -102,17 +86,17 @@ require_paths:
|
|
102
86
|
- lib
|
103
87
|
required_ruby_version: !ruby/object:Gem::Requirement
|
104
88
|
requirements:
|
105
|
-
- -
|
89
|
+
- - ">"
|
106
90
|
- !ruby/object:Gem::Version
|
107
91
|
version: 1.9.0
|
108
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
93
|
requirements:
|
110
|
-
- -
|
94
|
+
- - ">="
|
111
95
|
- !ruby/object:Gem::Version
|
112
96
|
version: '0'
|
113
97
|
requirements: []
|
114
98
|
rubyforge_project: Win32Utils
|
115
|
-
rubygems_version: 2.
|
99
|
+
rubygems_version: 2.4.5
|
116
100
|
signing_key:
|
117
101
|
specification_version: 4
|
118
102
|
summary: Native IO for MS Windows
|
data/lib/win32/nio.rb
DELETED
@@ -1,200 +0,0 @@
|
|
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.3'
|
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,77 +0,0 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
module Windows
|
3
|
-
module Constants
|
4
|
-
include FFI::Library
|
5
|
-
|
6
|
-
private
|
7
|
-
|
8
|
-
INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
|
9
|
-
|
10
|
-
CREATE_NEW = 1
|
11
|
-
CREATE_ALWAYS = 2
|
12
|
-
OPEN_EXISTING = 3
|
13
|
-
OPEN_ALWAYS = 4
|
14
|
-
TRUNCATE_EXISTING = 5
|
15
|
-
|
16
|
-
GENERIC_READ = 0x80000000
|
17
|
-
GENERIC_WRITE = 0x40000000
|
18
|
-
GENERIC_EXECUTE = 0x20000000
|
19
|
-
GENERIC_ALL = 0x10000000
|
20
|
-
|
21
|
-
FILE_SHARE_READ = 0x00000001
|
22
|
-
|
23
|
-
FILE_FLAG_WRITE_THROUGH = 0x80000000
|
24
|
-
FILE_FLAG_OVERLAPPED = 0x40000000
|
25
|
-
FILE_FLAG_NO_BUFFERING = 0x20000000
|
26
|
-
FILE_FLAG_RANDOM_ACCESS = 0x10000000
|
27
|
-
FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
|
28
|
-
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
|
29
|
-
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
|
30
|
-
FILE_FLAG_POSIX_SEMANTICS = 0x01000000
|
31
|
-
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
|
32
|
-
FILE_FLAG_OPEN_NO_RECALL = 0x00100000
|
33
|
-
FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
|
34
|
-
|
35
|
-
MEM_COMMIT = 0x1000
|
36
|
-
MEM_RESERVE = 0x2000
|
37
|
-
MEM_DECOMMIT = 0x4000
|
38
|
-
MEM_RELEASE = 0x8000
|
39
|
-
MEM_FREE = 0x10000
|
40
|
-
MEM_PRIVATE = 0x20000
|
41
|
-
MEM_MAPPED = 0x40000
|
42
|
-
MEM_RESET = 0x80000
|
43
|
-
MEM_TOP_DOWN = 0x100000
|
44
|
-
MEM_WRITE_WATCH = 0x200000
|
45
|
-
MEM_PHYSICAL = 0x400000
|
46
|
-
MEM_LARGE_PAGES = 0x20000000
|
47
|
-
MEM_4MB_PAGES = 0x80000000
|
48
|
-
|
49
|
-
PAGE_NOACCESS = 0x01
|
50
|
-
PAGE_READONLY = 0x02
|
51
|
-
PAGE_READWRITE = 0x04
|
52
|
-
PAGE_WRITECOPY = 0x08
|
53
|
-
PAGE_EXECUTE = 0x10
|
54
|
-
PAGE_EXECUTE_READ = 0x20
|
55
|
-
PAGE_EXECUTE_READWRITE = 0x40
|
56
|
-
PAGE_EXECUTE_WRITECOPY = 0x80
|
57
|
-
PAGE_GUARD = 0x100
|
58
|
-
PAGE_NOCACHE = 0x200
|
59
|
-
PAGE_WRITECOMBINE = 0x400
|
60
|
-
|
61
|
-
INFINITE = 0xFFFFFFFF
|
62
|
-
WAIT_OBJECT_0 = 0
|
63
|
-
WAIT_TIMEOUT = 0x102
|
64
|
-
WAIT_ABANDONED = 128
|
65
|
-
WAIT_ABANDONED_0 = WAIT_ABANDONED
|
66
|
-
WAIT_FAILED = 0xFFFFFFFF
|
67
|
-
|
68
|
-
STATUS_WAIT_0 = 0
|
69
|
-
STATUS_ABANDONED_WAIT_0 = 128
|
70
|
-
STATUS_USER_APC = 192
|
71
|
-
STATUS_TIMEOUT = 258
|
72
|
-
STATUS_PENDING = 259
|
73
|
-
|
74
|
-
ERROR_IO_INCOMPLETE = 996
|
75
|
-
ERROR_IO_PENDING = 997
|
76
|
-
end
|
77
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
|
3
|
-
module Windows
|
4
|
-
module Functions
|
5
|
-
extend FFI::Library
|
6
|
-
typedef :ulong, :dword
|
7
|
-
typedef :uintptr_t, :handle
|
8
|
-
typedef :pointer, :ptr
|
9
|
-
|
10
|
-
ffi_lib :kernel32
|
11
|
-
ffi_convention :stdcall
|
12
|
-
|
13
|
-
attach_function :CloseHandle, [:handle], :bool
|
14
|
-
attach_function :CreateFileA, [:string, :dword, :dword, :ptr, :dword, :dword, :handle], :handle
|
15
|
-
attach_function :CreateFileW, [:buffer_in, :dword, :dword, :ptr, :dword, :dword, :handle], :handle
|
16
|
-
attach_function :GetOverlappedResult, [:handle, :ptr, :ptr, :bool], :bool
|
17
|
-
attach_function :GetSystemInfo, [:ptr], :void
|
18
|
-
attach_function :ReadFile, [:handle, :buffer_out, :dword, :ptr, :ptr], :bool
|
19
|
-
attach_function :ReadFileScatter, [:handle, :ptr, :dword, :ptr, :ptr], :bool
|
20
|
-
attach_function :SleepEx, [:dword, :bool], :dword
|
21
|
-
attach_function :VirtualAlloc, [:ptr, :size_t, :dword, :dword], :dword
|
22
|
-
attach_function :VirtualFree, [:dword, :size_t, :dword], :bool
|
23
|
-
|
24
|
-
callback :completion_function, [:dword, :dword, :ptr], :void
|
25
|
-
attach_function :ReadFileEx, [:handle, :buffer_out, :dword, :ptr, :completion_function], :bool
|
26
|
-
end
|
27
|
-
end
|
data/lib/win32/windows/macros.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
|
3
|
-
module Windows
|
4
|
-
module Structs
|
5
|
-
extend FFI::Library
|
6
|
-
|
7
|
-
# I'm assuming the anonymous struct for the internal union here.
|
8
|
-
class Overlapped < FFI::Struct
|
9
|
-
layout(
|
10
|
-
:Internal, :uintptr_t,
|
11
|
-
:InternalHigh, :uintptr_t,
|
12
|
-
:Offset, :ulong,
|
13
|
-
:OffsetHigh, :ulong,
|
14
|
-
:hEvent, :uintptr_t
|
15
|
-
)
|
16
|
-
end
|
17
|
-
|
18
|
-
# dwOemId is deprecated. Just assume the nested struct.
|
19
|
-
class SystemInfo < FFI::Struct
|
20
|
-
layout(
|
21
|
-
:wProcessorArchitecture, :ushort,
|
22
|
-
:wReserved, :ushort,
|
23
|
-
:dwPageSize, :ulong,
|
24
|
-
:lpMinimumApplicationAddress, :pointer,
|
25
|
-
:lpMaximumApplicationAddress, :pointer,
|
26
|
-
:dwActiveProcessorMask, :pointer,
|
27
|
-
:dwNumberOfProcessors, :ulong,
|
28
|
-
:dwProcessorType, :ulong,
|
29
|
-
:dwAllocationGranularity, :ulong,
|
30
|
-
:wProcessorLevel, :ushort,
|
31
|
-
:wProcessorRevision, :ushort
|
32
|
-
)
|
33
|
-
end
|
34
|
-
|
35
|
-
class FileSegmentElement < FFI::Union
|
36
|
-
layout(:Buffer, :pointer, :Alignment, :uint64)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|