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