win32-nio 0.2.1 → 0.2.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +2 -0
- data/CHANGES +46 -41
- data/MANIFEST +12 -10
- data/README +86 -86
- data/Rakefile +84 -83
- data/appveyor.yml +45 -0
- data/benchmarks/win32_nio_benchmarks.rb +113 -113
- data/certs/djberg96_pub.pem +21 -0
- data/ext/extconf.rb +2 -2
- data/ext/win32/nio.c +355 -355
- data/test/test_win32_nio_read.rb +79 -79
- data/test/test_win32_nio_readlines.rb +75 -75
- data/win32-nio.gemspec +29 -28
- metadata +37 -8
- metadata.gz.sig +3 -0
data/appveyor.yml
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
version: '{build}'
|
2
|
+
branches:
|
3
|
+
only:
|
4
|
+
- C
|
5
|
+
skip_tags: true
|
6
|
+
clone_depth: 10
|
7
|
+
environment:
|
8
|
+
matrix:
|
9
|
+
- ruby_version: 200
|
10
|
+
ruby_dir: 2.0.0
|
11
|
+
- ruby_version: 200-x64
|
12
|
+
ruby_dir: 2.0.0
|
13
|
+
- ruby_version: 21
|
14
|
+
ruby_dir: 2.1.0
|
15
|
+
- ruby_version: 21-x64
|
16
|
+
ruby_dir: 2.1.0
|
17
|
+
- ruby_version: 22
|
18
|
+
ruby_dir: 2.2.0
|
19
|
+
- ruby_version: 22-x64
|
20
|
+
ruby_dir: 2.2.0
|
21
|
+
install:
|
22
|
+
- ps: >-
|
23
|
+
$env:path = "C:\Ruby" + $env:ruby_version + "\bin;" + $env:path
|
24
|
+
|
25
|
+
$tpath = "C:\Ruby" + $env:ruby_version + "\lib\ruby\" + $env:ruby_dir + "\test"
|
26
|
+
|
27
|
+
if ((test-path $tpath) -eq $True){ rm -recurse -force $tpath }
|
28
|
+
|
29
|
+
gem update --system > $null
|
30
|
+
|
31
|
+
if ((gem query -i win32-event) -eq $False){ gem install win32-event --no-document }
|
32
|
+
|
33
|
+
if ((gem query -i test-unit -v ">= 3.0") -eq $False){ gem install test-unit --no-document }
|
34
|
+
|
35
|
+
C:\Ruby21\DevKit\devkitvars.bat
|
36
|
+
cache:
|
37
|
+
- C:\Ruby200\lib\ruby\gems\2.0.0
|
38
|
+
- C:\Ruby200-x64\lib\ruby\gems\2.0.0
|
39
|
+
- C:\Ruby21\lib\ruby\gems\2.1.0
|
40
|
+
- C:\Ruby21-x64\lib\ruby\gems\2.1.0
|
41
|
+
- C:\Ruby22\lib\ruby\gems\2.2.0
|
42
|
+
- C:\Ruby22-x64\lib\ruby\gems\2.2.0
|
43
|
+
build: off
|
44
|
+
test_script:
|
45
|
+
- cmd: rake
|
@@ -1,113 +1,113 @@
|
|
1
|
-
########################################################################
|
2
|
-
# win32_nio_benchmarks.rb
|
3
|
-
#
|
4
|
-
# Run this via the 'rake bench' task to compare win32-nio with Ruby's
|
5
|
-
# own IO methods. This benchmark will take a bit of time, since it
|
6
|
-
# generates some test files on the fly.
|
7
|
-
########################################################################
|
8
|
-
require 'benchmark'
|
9
|
-
require 'win32/nio'
|
10
|
-
include Win32
|
11
|
-
|
12
|
-
MAX = (ARGV[0] || '10').chomp.to_i # Default to 10 iterations
|
13
|
-
PHRASE = "The quick brown fox jumped over the lazy dog's back"
|
14
|
-
|
15
|
-
SMALL_FILE = "small_nio_test.txt" # 588k
|
16
|
-
MEDIUM_FILE = "medium_nio_test.txt" # 6mb
|
17
|
-
LARGE_FILE = "large_nio_test.txt" # 60mb
|
18
|
-
HUGE_FILE = "huge_nio_test.txt" # 618mb
|
19
|
-
|
20
|
-
unless File.exists?(SMALL_FILE)
|
21
|
-
File.open(SMALL_FILE, 'w'){ |fh|
|
22
|
-
10000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
23
|
-
}
|
24
|
-
|
25
|
-
puts "Small file created"
|
26
|
-
end
|
27
|
-
|
28
|
-
unless File.exists?(MEDIUM_FILE)
|
29
|
-
File.open(MEDIUM_FILE, 'w'){ |fh|
|
30
|
-
110000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
31
|
-
}
|
32
|
-
|
33
|
-
puts "Medium file created"
|
34
|
-
end
|
35
|
-
|
36
|
-
unless File.exists?(LARGE_FILE)
|
37
|
-
File.open(LARGE_FILE, 'w'){ |fh|
|
38
|
-
1000000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
39
|
-
}
|
40
|
-
|
41
|
-
puts "Large file created"
|
42
|
-
end
|
43
|
-
|
44
|
-
unless File.exists?(HUGE_FILE)
|
45
|
-
#File.open(HUGE_FILE, 'w'){ |fh|
|
46
|
-
# 10000000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
47
|
-
#}
|
48
|
-
|
49
|
-
#puts "Huge file created"
|
50
|
-
end
|
51
|
-
|
52
|
-
Benchmark.bm(20) do |x|
|
53
|
-
x.report('IO.read(small)'){
|
54
|
-
MAX.times{ IO.read(SMALL_FILE) }
|
55
|
-
}
|
56
|
-
|
57
|
-
x.report('NIO.read(small)'){
|
58
|
-
MAX.times{ NIO.read(SMALL_FILE) }
|
59
|
-
}
|
60
|
-
|
61
|
-
x.report('IO.read(medium)'){
|
62
|
-
MAX.times{ IO.read(MEDIUM_FILE) }
|
63
|
-
}
|
64
|
-
|
65
|
-
x.report('NIO.read(medium)'){
|
66
|
-
MAX.times{ NIO.read(MEDIUM_FILE) }
|
67
|
-
}
|
68
|
-
|
69
|
-
x.report('IO.read(large)'){
|
70
|
-
MAX.times{ IO.read(LARGE_FILE) }
|
71
|
-
}
|
72
|
-
|
73
|
-
x.report('NIO.read(large)'){
|
74
|
-
MAX.times{ NIO.read(LARGE_FILE) }
|
75
|
-
}
|
76
|
-
|
77
|
-
#x.report('IO.read(huge)'){
|
78
|
-
# MAX.times{ IO.read(HUGE_FILE) }
|
79
|
-
#}
|
80
|
-
|
81
|
-
#x.report('NIO.read(huge)'){
|
82
|
-
# MAX.times{ NIO.read(HUGE_FILE) }
|
83
|
-
#}
|
84
|
-
|
85
|
-
x.report('IO.readlines(small)'){
|
86
|
-
MAX.times{ IO.readlines(SMALL_FILE) }
|
87
|
-
}
|
88
|
-
|
89
|
-
x.report('NIO.readlines(small)'){
|
90
|
-
MAX.times{ NIO.readlines(SMALL_FILE) }
|
91
|
-
}
|
92
|
-
|
93
|
-
x.report('IO.readlines(medium)'){
|
94
|
-
MAX.times{ IO.readlines(MEDIUM_FILE) }
|
95
|
-
}
|
96
|
-
|
97
|
-
x.report('NIO.readlines(medium)'){
|
98
|
-
MAX.times{ NIO.readlines(MEDIUM_FILE) }
|
99
|
-
}
|
100
|
-
|
101
|
-
x.report('IO.readlines(large)'){
|
102
|
-
MAX.times{ IO.readlines(LARGE_FILE) }
|
103
|
-
}
|
104
|
-
|
105
|
-
x.report('NIO.readlines(large)'){
|
106
|
-
MAX.times{ NIO.readlines(LARGE_FILE) }
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
|
-
File.delete(SMALL_FILE) if File.exists?(SMALL_FILE)
|
111
|
-
File.delete(MEDIUM_FILE) if File.exists?(MEDIUM_FILE)
|
112
|
-
File.delete(LARGE_FILE) if File.exists?(LARGE_FILE)
|
113
|
-
File.delete(HUGE_FILE) if File.exists?(HUGE_FILE)
|
1
|
+
########################################################################
|
2
|
+
# win32_nio_benchmarks.rb
|
3
|
+
#
|
4
|
+
# Run this via the 'rake bench' task to compare win32-nio with Ruby's
|
5
|
+
# own IO methods. This benchmark will take a bit of time, since it
|
6
|
+
# generates some test files on the fly.
|
7
|
+
########################################################################
|
8
|
+
require 'benchmark'
|
9
|
+
require 'win32/nio'
|
10
|
+
include Win32
|
11
|
+
|
12
|
+
MAX = (ARGV[0] || '10').chomp.to_i # Default to 10 iterations
|
13
|
+
PHRASE = "The quick brown fox jumped over the lazy dog's back"
|
14
|
+
|
15
|
+
SMALL_FILE = "small_nio_test.txt" # 588k
|
16
|
+
MEDIUM_FILE = "medium_nio_test.txt" # 6mb
|
17
|
+
LARGE_FILE = "large_nio_test.txt" # 60mb
|
18
|
+
HUGE_FILE = "huge_nio_test.txt" # 618mb
|
19
|
+
|
20
|
+
unless File.exists?(SMALL_FILE)
|
21
|
+
File.open(SMALL_FILE, 'w'){ |fh|
|
22
|
+
10000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
23
|
+
}
|
24
|
+
|
25
|
+
puts "Small file created"
|
26
|
+
end
|
27
|
+
|
28
|
+
unless File.exists?(MEDIUM_FILE)
|
29
|
+
File.open(MEDIUM_FILE, 'w'){ |fh|
|
30
|
+
110000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
31
|
+
}
|
32
|
+
|
33
|
+
puts "Medium file created"
|
34
|
+
end
|
35
|
+
|
36
|
+
unless File.exists?(LARGE_FILE)
|
37
|
+
File.open(LARGE_FILE, 'w'){ |fh|
|
38
|
+
1000000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
39
|
+
}
|
40
|
+
|
41
|
+
puts "Large file created"
|
42
|
+
end
|
43
|
+
|
44
|
+
unless File.exists?(HUGE_FILE)
|
45
|
+
#File.open(HUGE_FILE, 'w'){ |fh|
|
46
|
+
# 10000000.times{ |n| fh.puts PHRASE + ": #{n}" }
|
47
|
+
#}
|
48
|
+
|
49
|
+
#puts "Huge file created"
|
50
|
+
end
|
51
|
+
|
52
|
+
Benchmark.bm(20) do |x|
|
53
|
+
x.report('IO.read(small)'){
|
54
|
+
MAX.times{ IO.read(SMALL_FILE) }
|
55
|
+
}
|
56
|
+
|
57
|
+
x.report('NIO.read(small)'){
|
58
|
+
MAX.times{ NIO.read(SMALL_FILE) }
|
59
|
+
}
|
60
|
+
|
61
|
+
x.report('IO.read(medium)'){
|
62
|
+
MAX.times{ IO.read(MEDIUM_FILE) }
|
63
|
+
}
|
64
|
+
|
65
|
+
x.report('NIO.read(medium)'){
|
66
|
+
MAX.times{ NIO.read(MEDIUM_FILE) }
|
67
|
+
}
|
68
|
+
|
69
|
+
x.report('IO.read(large)'){
|
70
|
+
MAX.times{ IO.read(LARGE_FILE) }
|
71
|
+
}
|
72
|
+
|
73
|
+
x.report('NIO.read(large)'){
|
74
|
+
MAX.times{ NIO.read(LARGE_FILE) }
|
75
|
+
}
|
76
|
+
|
77
|
+
#x.report('IO.read(huge)'){
|
78
|
+
# MAX.times{ IO.read(HUGE_FILE) }
|
79
|
+
#}
|
80
|
+
|
81
|
+
#x.report('NIO.read(huge)'){
|
82
|
+
# MAX.times{ NIO.read(HUGE_FILE) }
|
83
|
+
#}
|
84
|
+
|
85
|
+
x.report('IO.readlines(small)'){
|
86
|
+
MAX.times{ IO.readlines(SMALL_FILE) }
|
87
|
+
}
|
88
|
+
|
89
|
+
x.report('NIO.readlines(small)'){
|
90
|
+
MAX.times{ NIO.readlines(SMALL_FILE) }
|
91
|
+
}
|
92
|
+
|
93
|
+
x.report('IO.readlines(medium)'){
|
94
|
+
MAX.times{ IO.readlines(MEDIUM_FILE) }
|
95
|
+
}
|
96
|
+
|
97
|
+
x.report('NIO.readlines(medium)'){
|
98
|
+
MAX.times{ NIO.readlines(MEDIUM_FILE) }
|
99
|
+
}
|
100
|
+
|
101
|
+
x.report('IO.readlines(large)'){
|
102
|
+
MAX.times{ IO.readlines(LARGE_FILE) }
|
103
|
+
}
|
104
|
+
|
105
|
+
x.report('NIO.readlines(large)'){
|
106
|
+
MAX.times{ NIO.readlines(LARGE_FILE) }
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
File.delete(SMALL_FILE) if File.exists?(SMALL_FILE)
|
111
|
+
File.delete(MEDIUM_FILE) if File.exists?(MEDIUM_FILE)
|
112
|
+
File.delete(LARGE_FILE) if File.exists?(LARGE_FILE)
|
113
|
+
File.delete(HUGE_FILE) if File.exists?(HUGE_FILE)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MREwDwYDVQQDDAhkamJl
|
3
|
+
cmc5NjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYDY29t
|
4
|
+
MB4XDTE1MDkwMjIwNDkxOFoXDTE2MDkwMTIwNDkxOFowPzERMA8GA1UEAwwIZGpi
|
5
|
+
ZXJnOTYxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv
|
6
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMyTkvXqRp6hLs9eoJOS
|
7
|
+
Hmi8kRYbq9Vkf15/hMxJpotYMgJVHHWrmDcC5Dye2PbnXjTkKf266Zw0PtT9h+lI
|
8
|
+
S3ts9HO+vaCFSMwFFZmnWJSpQ3CNw2RcHxjWkk9yF7imEM8Kz9ojhiDXzBetdV6M
|
9
|
+
gr0lV/alUr7TNVBDngbXEfTWscyXh1qd7xZ4EcOdsDktCe5G45N/o3662tPQvJsi
|
10
|
+
FOF0CM/KuBsa/HL1/eoEmF4B3EKIRfTHrQ3hu20Kv3RJ88QM4ec2+0dd97uX693O
|
11
|
+
zv6981fyEg+aXLkxrkViM/tz2qR2ZE0jPhHTREPYeMEgptRkTmWSKAuLVWrJEfgl
|
12
|
+
DtkCAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFEwe
|
13
|
+
nn6bfJADmuIDiMSOzedOrL+xMB0GA1UdEQQWMBSBEmRqYmVyZzk2QGdtYWlsLmNv
|
14
|
+
bTAdBgNVHRIEFjAUgRJkamJlcmc5NkBnbWFpbC5jb20wDQYJKoZIhvcNAQEFBQAD
|
15
|
+
ggEBAHmNOCWoDVD75zHFueY0viwGDVP1BNGFC+yXcb7u2GlK+nEMCORqzURbYPf7
|
16
|
+
tL+/hzmePIRz7i30UM//64GI1NLv9jl7nIwjhPpXpf7/lu2I9hOTsvwSumb5UiKC
|
17
|
+
/sqBxI3sfj9pr79Wpv4MuikX1XPik7Ncb7NPsJPw06Lvyc3Hkg5X2XpPtLtS+Gr2
|
18
|
+
wKJnmzb5rIPS1cmsqv0M9LPWflzfwoZ/SpnmhagP+g05p8bRNKjZSA2iImM/GyYZ
|
19
|
+
EJYzxdPOrx2n6NYR3Hk+vHP0U7UBSveI6+qx+ndQYaeyCn+GRX2PKS9h66YF/Q1V
|
20
|
+
tGSHgAmcLlkdGgan182qsE/4kKM=
|
21
|
+
-----END CERTIFICATE-----
|
data/ext/extconf.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
require 'mkmf'
|
2
|
-
create_makefile('win32/nio', 'win32')
|
1
|
+
require 'mkmf'
|
2
|
+
create_makefile('win32/nio', 'win32')
|
data/ext/win32/nio.c
CHANGED
@@ -1,355 +1,355 @@
|
|
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
|
-
* NIO.read(file, length=nil, offset=0, options=nil){ # optional block }
|
20
|
-
*
|
21
|
-
* This method is similar to Ruby's IO.read method except that it uses
|
22
|
-
* native function calls. It also optionally accepts a Win32::Event object,
|
23
|
-
* signalling it upon completion. If provided it calls a block upon completion
|
24
|
-
* of the read.
|
25
|
-
*
|
26
|
-
* Examples:
|
27
|
-
*
|
28
|
-
* # Read everything
|
29
|
-
* Win32::NIO.read(file)
|
30
|
-
*
|
31
|
-
* # Read the first 100 bytes
|
32
|
-
* Win32::NIO.read(file, 100)
|
33
|
-
*
|
34
|
-
* # Read 50 bytes starting at offset 10
|
35
|
-
* Win32::NIO.read(file, 50, 10)
|
36
|
-
*
|
37
|
-
* Note that the +options+ that may be passed to this method are limited
|
38
|
-
* to :encoding, :mode and :event because we're no longer using the open
|
39
|
-
* function internally. In the case of :mode the only thing that is checked
|
40
|
-
* for is the presence of the 'b' (binary) mode.
|
41
|
-
*
|
42
|
-
* The :event option, if present, must be a Win32::Event object.
|
43
|
-
*/
|
44
|
-
static VALUE rb_nio_read(int argc, VALUE* argv, VALUE self){
|
45
|
-
OVERLAPPED olap;
|
46
|
-
HANDLE h;
|
47
|
-
DWORD bytes_read;
|
48
|
-
LARGE_INTEGER lsize;
|
49
|
-
BOOL b;
|
50
|
-
VALUE v_file, v_length, v_offset, v_options;
|
51
|
-
VALUE v_event, v_mode, v_encoding, v_result;
|
52
|
-
size_t length;
|
53
|
-
int flags, error, size;
|
54
|
-
wchar_t* file = NULL;
|
55
|
-
char* buffer = NULL;
|
56
|
-
|
57
|
-
memset(&olap, 0, sizeof(olap));
|
58
|
-
|
59
|
-
// Paranoid initialization
|
60
|
-
v_length = Qnil;
|
61
|
-
v_offset = Qnil;
|
62
|
-
v_options = Qnil;
|
63
|
-
|
64
|
-
rb_scan_args(argc, argv, "13", &v_file, &v_length, &v_offset, &v_options);
|
65
|
-
|
66
|
-
// Allow path-y objects.
|
67
|
-
if (rb_respond_to(v_file, rb_intern("to_path")))
|
68
|
-
v_file = rb_funcall(v_file, rb_intern("to_path"), 0, NULL);
|
69
|
-
|
70
|
-
SafeStringValue(v_file);
|
71
|
-
|
72
|
-
// If a length is provided it cannot be negative.
|
73
|
-
if (!NIL_P(v_length)){
|
74
|
-
length = NUM2SIZET(v_length);
|
75
|
-
if ((int)length < 0)
|
76
|
-
rb_raise(rb_eArgError, "negative length %i given", (int)length);
|
77
|
-
}
|
78
|
-
else{
|
79
|
-
length = 0; // Mostly to make gcc stfu. We'll set this later.
|
80
|
-
}
|
81
|
-
|
82
|
-
// Unlike MRI, don't wait for an internal C function to fail on an invalid argument.
|
83
|
-
if (!NIL_P(v_offset)){
|
84
|
-
olap.Offset = NUM2ULONG(v_offset);
|
85
|
-
if ((int)olap.Offset < 0)
|
86
|
-
rb_raise(rb_eArgError, "negative offset %i given", (int)olap.Offset);
|
87
|
-
}
|
88
|
-
|
89
|
-
// Gotta do this for wide character function support.
|
90
|
-
size = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, NULL, 0);
|
91
|
-
file = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
92
|
-
|
93
|
-
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, file, size)){
|
94
|
-
ruby_xfree(file);
|
95
|
-
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
96
|
-
}
|
97
|
-
|
98
|
-
flags = FILE_FLAG_SEQUENTIAL_SCAN;
|
99
|
-
|
100
|
-
// Possible options are :event, :mode and :encoding
|
101
|
-
if (!NIL_P(v_options)){
|
102
|
-
Check_Type(v_options, T_HASH);
|
103
|
-
|
104
|
-
v_event = rb_hash_aref(v_options, ID2SYM(rb_intern("event")));
|
105
|
-
v_encoding = rb_hash_aref(v_options, ID2SYM(rb_intern("encoding")));
|
106
|
-
v_mode = rb_hash_aref(v_options, ID2SYM(rb_intern("mode")));
|
107
|
-
|
108
|
-
if (!NIL_P(v_event)){
|
109
|
-
flags |= FILE_FLAG_OVERLAPPED;
|
110
|
-
olap.hEvent = (HANDLE)(uintptr_t)NUM2OFFT(rb_funcall(v_event, rb_intern("handle"), 0, 0));
|
111
|
-
}
|
112
|
-
}
|
113
|
-
else{
|
114
|
-
v_event = Qnil;
|
115
|
-
v_encoding = Qnil;
|
116
|
-
v_mode = Qnil;
|
117
|
-
}
|
118
|
-
|
119
|
-
h = CreateFileW(
|
120
|
-
file,
|
121
|
-
GENERIC_READ,
|
122
|
-
FILE_SHARE_READ,
|
123
|
-
NULL,
|
124
|
-
OPEN_EXISTING,
|
125
|
-
flags,
|
126
|
-
NULL
|
127
|
-
);
|
128
|
-
|
129
|
-
if (h == INVALID_HANDLE_VALUE)
|
130
|
-
rb_raise_syserr("CreateFile", GetLastError());
|
131
|
-
|
132
|
-
// Get the file size. We may use this later to limit read length.
|
133
|
-
if (!GetFileSizeEx(h, &lsize)){
|
134
|
-
error = GetLastError();
|
135
|
-
CloseHandle(h);
|
136
|
-
rb_raise_syserr("GetFileSizeEx", error);
|
137
|
-
}
|
138
|
-
|
139
|
-
// If no length is specified, read the entire file
|
140
|
-
if (NIL_P(v_length))
|
141
|
-
length = (size_t)lsize.QuadPart;
|
142
|
-
|
143
|
-
// Don't read past the end of the file
|
144
|
-
if (olap.Offset + length > (size_t)lsize.QuadPart)
|
145
|
-
length = (size_t)lsize.QuadPart - olap.Offset;
|
146
|
-
|
147
|
-
buffer = (char*)ruby_xmalloc(length * sizeof(char));
|
148
|
-
|
149
|
-
// If a block is given then treat it as a callback
|
150
|
-
if (rb_block_given_p()){
|
151
|
-
flags |= FILE_FLAG_OVERLAPPED;
|
152
|
-
b = ReadFileEx(h, buffer, length, &olap, read_complete);
|
153
|
-
}
|
154
|
-
else{
|
155
|
-
b = ReadFile(h, buffer, length, &bytes_read, &olap);
|
156
|
-
}
|
157
|
-
|
158
|
-
error = GetLastError();
|
159
|
-
|
160
|
-
// Put in alertable wait state if overlapped IO
|
161
|
-
if (flags & FILE_FLAG_OVERLAPPED)
|
162
|
-
SleepEx(1, TRUE);
|
163
|
-
|
164
|
-
if (!b){
|
165
|
-
if(error == ERROR_IO_PENDING){
|
166
|
-
DWORD bytes;
|
167
|
-
if (!GetOverlappedResult(h, &olap, &bytes, TRUE)){
|
168
|
-
ruby_xfree(buffer);
|
169
|
-
CloseHandle(h);
|
170
|
-
rb_raise_syserr("GetOverlappedResult", error);
|
171
|
-
}
|
172
|
-
}
|
173
|
-
else{
|
174
|
-
ruby_xfree(buffer);
|
175
|
-
CloseHandle(h);
|
176
|
-
rb_raise_syserr("ReadFile", error);
|
177
|
-
}
|
178
|
-
}
|
179
|
-
|
180
|
-
CloseHandle(h);
|
181
|
-
|
182
|
-
v_result = rb_str_new(buffer, length);
|
183
|
-
ruby_xfree(buffer);
|
184
|
-
|
185
|
-
// Convert CRLF to LF if text mode
|
186
|
-
if (!NIL_P(v_mode) && strstr(RSTRING_PTR(v_mode), "t"))
|
187
|
-
rb_funcall(v_result, rb_intern("gsub!"), 2, rb_str_new2("\r\n"), rb_gv_get("$/"));
|
188
|
-
|
189
|
-
if (!NIL_P(v_encoding))
|
190
|
-
rb_funcall(v_result, rb_intern("encode!"), 1, v_encoding);
|
191
|
-
|
192
|
-
return v_result;
|
193
|
-
}
|
194
|
-
|
195
|
-
/*
|
196
|
-
* NIO.readlines(file, sep="\r\n", event=nil)
|
197
|
-
*
|
198
|
-
* Reads the entire file specified by portname as individual lines, and
|
199
|
-
* returns those lines in an array. Lines are separated by +sep+. The +event+
|
200
|
-
* argument must be a Win32::Event if present, and it is signalled upon
|
201
|
-
* completion.
|
202
|
-
*
|
203
|
-
* Examples:
|
204
|
-
*
|
205
|
-
* # Standard call
|
206
|
-
* Win32::NIO.readlines('file.txt') # => ['line 1', 'line 2', 'line 3', 'line 4']
|
207
|
-
*
|
208
|
-
* # Paragraph mode
|
209
|
-
* Win32::NIO.readlines('file.txt', '') # => ['line 1\r\nline 2', 'line 3\r\nline 4']
|
210
|
-
*
|
211
|
-
* # With event
|
212
|
-
* event = Win32::Event.new
|
213
|
-
* Win32::NIO.readlines('file.txt', nil, event)
|
214
|
-
* p event.signaled? # => true
|
215
|
-
*
|
216
|
-
* Superficially this method acts the same as the Ruby IO.readlines call, except that
|
217
|
-
* it does not transform line endings and accepts an optional event object. However,
|
218
|
-
* internally this method is using a scattered read to accomplish its goal. In practice
|
219
|
-
* this is only relevant in specific situations. Using it outside of those situations
|
220
|
-
* is unlikely to provide any practical benefit, and may even result in slower performance.
|
221
|
-
*
|
222
|
-
* See information on vectored IO for more details.
|
223
|
-
*/
|
224
|
-
static VALUE rb_nio_readlines(int argc, VALUE* argv, VALUE self){
|
225
|
-
HANDLE h;
|
226
|
-
SYSTEM_INFO info;
|
227
|
-
LARGE_INTEGER file_size;
|
228
|
-
size_t length, page_size;
|
229
|
-
double size;
|
230
|
-
void* base_address;
|
231
|
-
int error, page_num;
|
232
|
-
wchar_t* file;
|
233
|
-
VALUE v_file, v_sep, v_event, v_result;
|
234
|
-
|
235
|
-
rb_scan_args(argc, argv, "12", &v_file, &v_sep, &v_event);
|
236
|
-
|
237
|
-
SafeStringValue(v_file);
|
238
|
-
|
239
|
-
if (NIL_P(v_sep))
|
240
|
-
v_sep = rb_str_new2("\r\n");
|
241
|
-
else
|
242
|
-
SafeStringValue(v_sep);
|
243
|
-
|
244
|
-
v_result = Qnil;
|
245
|
-
|
246
|
-
length = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, NULL, 0);
|
247
|
-
file = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
248
|
-
|
249
|
-
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, file, length)){
|
250
|
-
ruby_xfree(file);
|
251
|
-
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
252
|
-
}
|
253
|
-
|
254
|
-
h = CreateFileW(
|
255
|
-
file,
|
256
|
-
GENERIC_READ,
|
257
|
-
FILE_SHARE_READ,
|
258
|
-
NULL,
|
259
|
-
OPEN_EXISTING,
|
260
|
-
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
261
|
-
NULL
|
262
|
-
);
|
263
|
-
|
264
|
-
if (h == INVALID_HANDLE_VALUE)
|
265
|
-
rb_raise_syserr("CreateFile", GetLastError());
|
266
|
-
|
267
|
-
if (!GetFileSizeEx(h, &file_size)){
|
268
|
-
error = GetLastError();
|
269
|
-
CloseHandle(h);
|
270
|
-
rb_raise_syserr("GetFileSizeEx", error);
|
271
|
-
}
|
272
|
-
|
273
|
-
length = (size_t)file_size.QuadPart;
|
274
|
-
|
275
|
-
GetSystemInfo(&info);
|
276
|
-
page_size = info.dwPageSize;
|
277
|
-
|
278
|
-
page_num = (int)ceil((double)length / page_size);
|
279
|
-
|
280
|
-
size = page_num * page_size;
|
281
|
-
|
282
|
-
base_address = VirtualAlloc(NULL, (size_t)size, MEM_COMMIT, PAGE_READWRITE);
|
283
|
-
|
284
|
-
if (!base_address){
|
285
|
-
error = GetLastError();
|
286
|
-
CloseHandle(h);
|
287
|
-
rb_raise_syserr("VirtualAlloc", error);
|
288
|
-
}
|
289
|
-
else{
|
290
|
-
int i;
|
291
|
-
OVERLAPPED olap;
|
292
|
-
BOOL rv;
|
293
|
-
FILE_SEGMENT_ELEMENT* fse;
|
294
|
-
|
295
|
-
olap.Offset = 0;
|
296
|
-
olap.OffsetHigh = 0;
|
297
|
-
|
298
|
-
if (NIL_P(v_event))
|
299
|
-
olap.hEvent = NULL;
|
300
|
-
else
|
301
|
-
olap.hEvent = (HANDLE)(uintptr_t)NUM2OFFT(rb_funcall(v_event, rb_intern("handle"), 0, 0));
|
302
|
-
|
303
|
-
fse = (FILE_SEGMENT_ELEMENT*)malloc(sizeof(FILE_SEGMENT_ELEMENT) * (page_num + 1));
|
304
|
-
memset(fse, 0, sizeof(FILE_SEGMENT_ELEMENT) * (page_num + 1));
|
305
|
-
v_result = Qnil;
|
306
|
-
|
307
|
-
for (i = 0; i < page_num; i++)
|
308
|
-
fse[i].Alignment = (ULONGLONG)(uintptr_t)base_address + (page_size * i);
|
309
|
-
|
310
|
-
rv = ReadFileScatter(h, fse, (DWORD)size, NULL, &olap);
|
311
|
-
|
312
|
-
if (!rv){
|
313
|
-
error = GetLastError();
|
314
|
-
|
315
|
-
if (error == ERROR_IO_PENDING){
|
316
|
-
while (!HasOverlappedIoCompleted(&olap))
|
317
|
-
SleepEx(1, TRUE);
|
318
|
-
}
|
319
|
-
else{
|
320
|
-
VirtualFree(base_address, 0, MEM_RELEASE);
|
321
|
-
CloseHandle(h);
|
322
|
-
rb_raise_syserr("ReadFileScatter", error);
|
323
|
-
}
|
324
|
-
}
|
325
|
-
|
326
|
-
// Explicitly handle paragraph mode
|
327
|
-
if (rb_equal(v_sep, rb_str_new2(""))){
|
328
|
-
VALUE v_args[1];
|
329
|
-
v_args[0] = rb_str_new2("(\r\n){2,}");
|
330
|
-
v_sep = rb_class_new_instance(1, v_args, rb_cRegexp);
|
331
|
-
v_result = rb_funcall(rb_str_new2(fse[0].Buffer), rb_intern("split"), 1, v_sep);
|
332
|
-
rb_funcall(v_result, rb_intern("delete"), 1, rb_str_new2("\r\n"));
|
333
|
-
}
|
334
|
-
else{
|
335
|
-
v_result = rb_funcall(rb_str_new2(fse[0].Buffer), rb_intern("split"), 1, v_sep);
|
336
|
-
}
|
337
|
-
|
338
|
-
VirtualFree(base_address, 0, MEM_RELEASE);
|
339
|
-
}
|
340
|
-
|
341
|
-
CloseHandle(h);
|
342
|
-
|
343
|
-
return v_result;
|
344
|
-
}
|
345
|
-
|
346
|
-
void Init_nio(){
|
347
|
-
VALUE mWin32 = rb_define_module("Win32");
|
348
|
-
VALUE cNio = rb_define_class_under(mWin32, "NIO", rb_cObject);
|
349
|
-
|
350
|
-
rb_define_singleton_method(cNio, "read", rb_nio_read, -1);
|
351
|
-
rb_define_singleton_method(cNio, "readlines", rb_nio_readlines, -1);
|
352
|
-
|
353
|
-
/* 0.2.
|
354
|
-
rb_define_const(cNio, "VERSION", rb_str_new2("0.2.
|
355
|
-
}
|
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
|
+
* NIO.read(file, length=nil, offset=0, options=nil){ # optional block }
|
20
|
+
*
|
21
|
+
* This method is similar to Ruby's IO.read method except that it uses
|
22
|
+
* native function calls. It also optionally accepts a Win32::Event object,
|
23
|
+
* signalling it upon completion. If provided it calls a block upon completion
|
24
|
+
* of the read.
|
25
|
+
*
|
26
|
+
* Examples:
|
27
|
+
*
|
28
|
+
* # Read everything
|
29
|
+
* Win32::NIO.read(file)
|
30
|
+
*
|
31
|
+
* # Read the first 100 bytes
|
32
|
+
* Win32::NIO.read(file, 100)
|
33
|
+
*
|
34
|
+
* # Read 50 bytes starting at offset 10
|
35
|
+
* Win32::NIO.read(file, 50, 10)
|
36
|
+
*
|
37
|
+
* Note that the +options+ that may be passed to this method are limited
|
38
|
+
* to :encoding, :mode and :event because we're no longer using the open
|
39
|
+
* function internally. In the case of :mode the only thing that is checked
|
40
|
+
* for is the presence of the 'b' (binary) mode.
|
41
|
+
*
|
42
|
+
* The :event option, if present, must be a Win32::Event object.
|
43
|
+
*/
|
44
|
+
static VALUE rb_nio_read(int argc, VALUE* argv, VALUE self){
|
45
|
+
OVERLAPPED olap;
|
46
|
+
HANDLE h;
|
47
|
+
DWORD bytes_read;
|
48
|
+
LARGE_INTEGER lsize;
|
49
|
+
BOOL b;
|
50
|
+
VALUE v_file, v_length, v_offset, v_options;
|
51
|
+
VALUE v_event, v_mode, v_encoding, v_result;
|
52
|
+
size_t length;
|
53
|
+
int flags, error, size;
|
54
|
+
wchar_t* file = NULL;
|
55
|
+
char* buffer = NULL;
|
56
|
+
|
57
|
+
memset(&olap, 0, sizeof(olap));
|
58
|
+
|
59
|
+
// Paranoid initialization
|
60
|
+
v_length = Qnil;
|
61
|
+
v_offset = Qnil;
|
62
|
+
v_options = Qnil;
|
63
|
+
|
64
|
+
rb_scan_args(argc, argv, "13", &v_file, &v_length, &v_offset, &v_options);
|
65
|
+
|
66
|
+
// Allow path-y objects.
|
67
|
+
if (rb_respond_to(v_file, rb_intern("to_path")))
|
68
|
+
v_file = rb_funcall(v_file, rb_intern("to_path"), 0, NULL);
|
69
|
+
|
70
|
+
SafeStringValue(v_file);
|
71
|
+
|
72
|
+
// If a length is provided it cannot be negative.
|
73
|
+
if (!NIL_P(v_length)){
|
74
|
+
length = NUM2SIZET(v_length);
|
75
|
+
if ((int)length < 0)
|
76
|
+
rb_raise(rb_eArgError, "negative length %i given", (int)length);
|
77
|
+
}
|
78
|
+
else{
|
79
|
+
length = 0; // Mostly to make gcc stfu. We'll set this later.
|
80
|
+
}
|
81
|
+
|
82
|
+
// Unlike MRI, don't wait for an internal C function to fail on an invalid argument.
|
83
|
+
if (!NIL_P(v_offset)){
|
84
|
+
olap.Offset = NUM2ULONG(v_offset);
|
85
|
+
if ((int)olap.Offset < 0)
|
86
|
+
rb_raise(rb_eArgError, "negative offset %i given", (int)olap.Offset);
|
87
|
+
}
|
88
|
+
|
89
|
+
// Gotta do this for wide character function support.
|
90
|
+
size = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, NULL, 0);
|
91
|
+
file = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
92
|
+
|
93
|
+
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, file, size)){
|
94
|
+
ruby_xfree(file);
|
95
|
+
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
96
|
+
}
|
97
|
+
|
98
|
+
flags = FILE_FLAG_SEQUENTIAL_SCAN;
|
99
|
+
|
100
|
+
// Possible options are :event, :mode and :encoding
|
101
|
+
if (!NIL_P(v_options)){
|
102
|
+
Check_Type(v_options, T_HASH);
|
103
|
+
|
104
|
+
v_event = rb_hash_aref(v_options, ID2SYM(rb_intern("event")));
|
105
|
+
v_encoding = rb_hash_aref(v_options, ID2SYM(rb_intern("encoding")));
|
106
|
+
v_mode = rb_hash_aref(v_options, ID2SYM(rb_intern("mode")));
|
107
|
+
|
108
|
+
if (!NIL_P(v_event)){
|
109
|
+
flags |= FILE_FLAG_OVERLAPPED;
|
110
|
+
olap.hEvent = (HANDLE)(uintptr_t)NUM2OFFT(rb_funcall(v_event, rb_intern("handle"), 0, 0));
|
111
|
+
}
|
112
|
+
}
|
113
|
+
else{
|
114
|
+
v_event = Qnil;
|
115
|
+
v_encoding = Qnil;
|
116
|
+
v_mode = Qnil;
|
117
|
+
}
|
118
|
+
|
119
|
+
h = CreateFileW(
|
120
|
+
file,
|
121
|
+
GENERIC_READ,
|
122
|
+
FILE_SHARE_READ,
|
123
|
+
NULL,
|
124
|
+
OPEN_EXISTING,
|
125
|
+
flags,
|
126
|
+
NULL
|
127
|
+
);
|
128
|
+
|
129
|
+
if (h == INVALID_HANDLE_VALUE)
|
130
|
+
rb_raise_syserr("CreateFile", GetLastError());
|
131
|
+
|
132
|
+
// Get the file size. We may use this later to limit read length.
|
133
|
+
if (!GetFileSizeEx(h, &lsize)){
|
134
|
+
error = GetLastError();
|
135
|
+
CloseHandle(h);
|
136
|
+
rb_raise_syserr("GetFileSizeEx", error);
|
137
|
+
}
|
138
|
+
|
139
|
+
// If no length is specified, read the entire file
|
140
|
+
if (NIL_P(v_length))
|
141
|
+
length = (size_t)lsize.QuadPart;
|
142
|
+
|
143
|
+
// Don't read past the end of the file
|
144
|
+
if (olap.Offset + length > (size_t)lsize.QuadPart)
|
145
|
+
length = (size_t)lsize.QuadPart - olap.Offset;
|
146
|
+
|
147
|
+
buffer = (char*)ruby_xmalloc(length * sizeof(char));
|
148
|
+
|
149
|
+
// If a block is given then treat it as a callback
|
150
|
+
if (rb_block_given_p()){
|
151
|
+
flags |= FILE_FLAG_OVERLAPPED;
|
152
|
+
b = ReadFileEx(h, buffer, length, &olap, read_complete);
|
153
|
+
}
|
154
|
+
else{
|
155
|
+
b = ReadFile(h, buffer, length, &bytes_read, &olap);
|
156
|
+
}
|
157
|
+
|
158
|
+
error = GetLastError();
|
159
|
+
|
160
|
+
// Put in alertable wait state if overlapped IO
|
161
|
+
if (flags & FILE_FLAG_OVERLAPPED)
|
162
|
+
SleepEx(1, TRUE);
|
163
|
+
|
164
|
+
if (!b){
|
165
|
+
if(error == ERROR_IO_PENDING){
|
166
|
+
DWORD bytes;
|
167
|
+
if (!GetOverlappedResult(h, &olap, &bytes, TRUE)){
|
168
|
+
ruby_xfree(buffer);
|
169
|
+
CloseHandle(h);
|
170
|
+
rb_raise_syserr("GetOverlappedResult", error);
|
171
|
+
}
|
172
|
+
}
|
173
|
+
else{
|
174
|
+
ruby_xfree(buffer);
|
175
|
+
CloseHandle(h);
|
176
|
+
rb_raise_syserr("ReadFile", error);
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
CloseHandle(h);
|
181
|
+
|
182
|
+
v_result = rb_str_new(buffer, length);
|
183
|
+
ruby_xfree(buffer);
|
184
|
+
|
185
|
+
// Convert CRLF to LF if text mode
|
186
|
+
if (!NIL_P(v_mode) && strstr(RSTRING_PTR(v_mode), "t"))
|
187
|
+
rb_funcall(v_result, rb_intern("gsub!"), 2, rb_str_new2("\r\n"), rb_gv_get("$/"));
|
188
|
+
|
189
|
+
if (!NIL_P(v_encoding))
|
190
|
+
rb_funcall(v_result, rb_intern("encode!"), 1, v_encoding);
|
191
|
+
|
192
|
+
return v_result;
|
193
|
+
}
|
194
|
+
|
195
|
+
/*
|
196
|
+
* NIO.readlines(file, sep="\r\n", event=nil)
|
197
|
+
*
|
198
|
+
* Reads the entire file specified by portname as individual lines, and
|
199
|
+
* returns those lines in an array. Lines are separated by +sep+. The +event+
|
200
|
+
* argument must be a Win32::Event if present, and it is signalled upon
|
201
|
+
* completion.
|
202
|
+
*
|
203
|
+
* Examples:
|
204
|
+
*
|
205
|
+
* # Standard call
|
206
|
+
* Win32::NIO.readlines('file.txt') # => ['line 1', 'line 2', 'line 3', 'line 4']
|
207
|
+
*
|
208
|
+
* # Paragraph mode
|
209
|
+
* Win32::NIO.readlines('file.txt', '') # => ['line 1\r\nline 2', 'line 3\r\nline 4']
|
210
|
+
*
|
211
|
+
* # With event
|
212
|
+
* event = Win32::Event.new
|
213
|
+
* Win32::NIO.readlines('file.txt', nil, event)
|
214
|
+
* p event.signaled? # => true
|
215
|
+
*
|
216
|
+
* Superficially this method acts the same as the Ruby IO.readlines call, except that
|
217
|
+
* it does not transform line endings and accepts an optional event object. However,
|
218
|
+
* internally this method is using a scattered read to accomplish its goal. In practice
|
219
|
+
* this is only relevant in specific situations. Using it outside of those situations
|
220
|
+
* is unlikely to provide any practical benefit, and may even result in slower performance.
|
221
|
+
*
|
222
|
+
* See information on vectored IO for more details.
|
223
|
+
*/
|
224
|
+
static VALUE rb_nio_readlines(int argc, VALUE* argv, VALUE self){
|
225
|
+
HANDLE h;
|
226
|
+
SYSTEM_INFO info;
|
227
|
+
LARGE_INTEGER file_size;
|
228
|
+
size_t length, page_size;
|
229
|
+
double size;
|
230
|
+
void* base_address;
|
231
|
+
int error, page_num;
|
232
|
+
wchar_t* file;
|
233
|
+
VALUE v_file, v_sep, v_event, v_result;
|
234
|
+
|
235
|
+
rb_scan_args(argc, argv, "12", &v_file, &v_sep, &v_event);
|
236
|
+
|
237
|
+
SafeStringValue(v_file);
|
238
|
+
|
239
|
+
if (NIL_P(v_sep))
|
240
|
+
v_sep = rb_str_new2("\r\n");
|
241
|
+
else
|
242
|
+
SafeStringValue(v_sep);
|
243
|
+
|
244
|
+
v_result = Qnil;
|
245
|
+
|
246
|
+
length = MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, NULL, 0);
|
247
|
+
file = (wchar_t*)ruby_xmalloc(MAX_PATH * sizeof(wchar_t));
|
248
|
+
|
249
|
+
if (!MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(v_file), -1, file, length)){
|
250
|
+
ruby_xfree(file);
|
251
|
+
rb_raise_syserr("MultibyteToWideChar", GetLastError());
|
252
|
+
}
|
253
|
+
|
254
|
+
h = CreateFileW(
|
255
|
+
file,
|
256
|
+
GENERIC_READ,
|
257
|
+
FILE_SHARE_READ,
|
258
|
+
NULL,
|
259
|
+
OPEN_EXISTING,
|
260
|
+
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,
|
261
|
+
NULL
|
262
|
+
);
|
263
|
+
|
264
|
+
if (h == INVALID_HANDLE_VALUE)
|
265
|
+
rb_raise_syserr("CreateFile", GetLastError());
|
266
|
+
|
267
|
+
if (!GetFileSizeEx(h, &file_size)){
|
268
|
+
error = GetLastError();
|
269
|
+
CloseHandle(h);
|
270
|
+
rb_raise_syserr("GetFileSizeEx", error);
|
271
|
+
}
|
272
|
+
|
273
|
+
length = (size_t)file_size.QuadPart;
|
274
|
+
|
275
|
+
GetSystemInfo(&info);
|
276
|
+
page_size = info.dwPageSize;
|
277
|
+
|
278
|
+
page_num = (int)ceil((double)length / page_size);
|
279
|
+
|
280
|
+
size = page_num * page_size;
|
281
|
+
|
282
|
+
base_address = VirtualAlloc(NULL, (size_t)size, MEM_COMMIT, PAGE_READWRITE);
|
283
|
+
|
284
|
+
if (!base_address){
|
285
|
+
error = GetLastError();
|
286
|
+
CloseHandle(h);
|
287
|
+
rb_raise_syserr("VirtualAlloc", error);
|
288
|
+
}
|
289
|
+
else{
|
290
|
+
int i;
|
291
|
+
OVERLAPPED olap;
|
292
|
+
BOOL rv;
|
293
|
+
FILE_SEGMENT_ELEMENT* fse;
|
294
|
+
|
295
|
+
olap.Offset = 0;
|
296
|
+
olap.OffsetHigh = 0;
|
297
|
+
|
298
|
+
if (NIL_P(v_event))
|
299
|
+
olap.hEvent = NULL;
|
300
|
+
else
|
301
|
+
olap.hEvent = (HANDLE)(uintptr_t)NUM2OFFT(rb_funcall(v_event, rb_intern("handle"), 0, 0));
|
302
|
+
|
303
|
+
fse = (FILE_SEGMENT_ELEMENT*)malloc(sizeof(FILE_SEGMENT_ELEMENT) * (page_num + 1));
|
304
|
+
memset(fse, 0, sizeof(FILE_SEGMENT_ELEMENT) * (page_num + 1));
|
305
|
+
v_result = Qnil;
|
306
|
+
|
307
|
+
for (i = 0; i < page_num; i++)
|
308
|
+
fse[i].Alignment = (ULONGLONG)(uintptr_t)base_address + (page_size * i);
|
309
|
+
|
310
|
+
rv = ReadFileScatter(h, fse, (DWORD)size, NULL, &olap);
|
311
|
+
|
312
|
+
if (!rv){
|
313
|
+
error = GetLastError();
|
314
|
+
|
315
|
+
if (error == ERROR_IO_PENDING){
|
316
|
+
while (!HasOverlappedIoCompleted(&olap))
|
317
|
+
SleepEx(1, TRUE);
|
318
|
+
}
|
319
|
+
else{
|
320
|
+
VirtualFree(base_address, 0, MEM_RELEASE);
|
321
|
+
CloseHandle(h);
|
322
|
+
rb_raise_syserr("ReadFileScatter", error);
|
323
|
+
}
|
324
|
+
}
|
325
|
+
|
326
|
+
// Explicitly handle paragraph mode
|
327
|
+
if (rb_equal(v_sep, rb_str_new2(""))){
|
328
|
+
VALUE v_args[1];
|
329
|
+
v_args[0] = rb_str_new2("(\r\n){2,}");
|
330
|
+
v_sep = rb_class_new_instance(1, v_args, rb_cRegexp);
|
331
|
+
v_result = rb_funcall(rb_str_new2(fse[0].Buffer), rb_intern("split"), 1, v_sep);
|
332
|
+
rb_funcall(v_result, rb_intern("delete"), 1, rb_str_new2("\r\n"));
|
333
|
+
}
|
334
|
+
else{
|
335
|
+
v_result = rb_funcall(rb_str_new2(fse[0].Buffer), rb_intern("split"), 1, v_sep);
|
336
|
+
}
|
337
|
+
|
338
|
+
VirtualFree(base_address, 0, MEM_RELEASE);
|
339
|
+
}
|
340
|
+
|
341
|
+
CloseHandle(h);
|
342
|
+
|
343
|
+
return v_result;
|
344
|
+
}
|
345
|
+
|
346
|
+
void Init_nio(){
|
347
|
+
VALUE mWin32 = rb_define_module("Win32");
|
348
|
+
VALUE cNio = rb_define_class_under(mWin32, "NIO", rb_cObject);
|
349
|
+
|
350
|
+
rb_define_singleton_method(cNio, "read", rb_nio_read, -1);
|
351
|
+
rb_define_singleton_method(cNio, "readlines", rb_nio_readlines, -1);
|
352
|
+
|
353
|
+
/* 0.2.2: The version of the win32-nio library */
|
354
|
+
rb_define_const(cNio, "VERSION", rb_str_new2("0.2.2"));
|
355
|
+
}
|