nio4r 1.2.1 → 2.5.3
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 +5 -5
- data/.github/workflows/workflow.yml +43 -0
- data/.gitignore +1 -0
- data/.rspec +0 -1
- data/.rubocop.yml +70 -31
- data/CHANGES.md +190 -42
- data/Gemfile +8 -4
- data/Guardfile +10 -0
- data/README.md +102 -147
- data/Rakefile +3 -4
- data/examples/echo_server.rb +3 -2
- data/ext/libev/Changes +44 -13
- data/ext/libev/README +2 -1
- data/ext/libev/ev.c +314 -225
- data/ext/libev/ev.h +90 -88
- data/ext/libev/ev_epoll.c +30 -16
- data/ext/libev/ev_kqueue.c +19 -9
- data/ext/libev/ev_linuxaio.c +642 -0
- data/ext/libev/ev_poll.c +19 -11
- data/ext/libev/ev_port.c +13 -6
- data/ext/libev/ev_select.c +4 -2
- data/ext/libev/ev_vars.h +14 -3
- data/ext/libev/ev_wrap.h +16 -0
- data/ext/nio4r/bytebuffer.c +429 -0
- data/ext/nio4r/extconf.rb +17 -30
- data/ext/nio4r/monitor.c +113 -49
- data/ext/nio4r/nio4r.h +11 -13
- data/ext/nio4r/org/nio4r/ByteBuffer.java +293 -0
- data/ext/nio4r/org/nio4r/Monitor.java +175 -0
- data/ext/nio4r/org/nio4r/Nio4r.java +22 -391
- data/ext/nio4r/org/nio4r/Selector.java +299 -0
- data/ext/nio4r/selector.c +155 -68
- data/lib/nio.rb +4 -4
- data/lib/nio/bytebuffer.rb +229 -0
- data/lib/nio/monitor.rb +73 -11
- data/lib/nio/selector.rb +64 -21
- data/lib/nio/version.rb +1 -1
- data/nio4r.gemspec +34 -20
- data/{tasks → rakelib}/extension.rake +4 -0
- data/{tasks → rakelib}/rspec.rake +2 -0
- data/{tasks → rakelib}/rubocop.rake +2 -0
- data/spec/nio/acceptables_spec.rb +5 -5
- data/spec/nio/bytebuffer_spec.rb +354 -0
- data/spec/nio/monitor_spec.rb +128 -79
- data/spec/nio/selectables/pipe_spec.rb +12 -3
- data/spec/nio/selectables/ssl_socket_spec.rb +61 -29
- data/spec/nio/selectables/tcp_socket_spec.rb +47 -34
- data/spec/nio/selectables/udp_socket_spec.rb +24 -7
- data/spec/nio/selector_spec.rb +65 -16
- data/spec/spec_helper.rb +12 -3
- data/spec/support/selectable_examples.rb +45 -18
- metadata +33 -23
- data/.rubocop_todo.yml +0 -35
- data/.travis.yml +0 -27
- data/LICENSE.txt +0 -20
- data/ext/libev/README.embed +0 -3
- data/ext/libev/test_libev_win32.c +0 -123
data/ext/nio4r/extconf.rb
CHANGED
@@ -1,41 +1,28 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
have_header("unistd.h")
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
$defs << "-DHAVE_RB_THREAD_BLOCKING_REGION"
|
7
|
-
end
|
3
|
+
require "rubygems"
|
8
4
|
|
9
|
-
|
10
|
-
|
5
|
+
# Write a dummy Makefile on Windows because we use the pure Ruby implementation there
|
6
|
+
if Gem.win_platform?
|
7
|
+
require "devkit" if RUBY_PLATFORM.include?("mingw")
|
8
|
+
File.write("Makefile", "all install::\n")
|
9
|
+
File.write("nio4r_ext.so", "")
|
10
|
+
exit
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
$defs << "-DEV_USE_POLL" if have_header("poll.h")
|
16
|
-
|
17
|
-
$defs << "-DEV_USE_EPOLL" if have_header("sys/epoll.h")
|
18
|
-
|
19
|
-
if have_header("sys/event.h") && have_header("sys/queue.h")
|
20
|
-
$defs << "-DEV_USE_KQUEUE"
|
21
|
-
end
|
13
|
+
require "mkmf"
|
22
14
|
|
23
|
-
|
15
|
+
have_header("unistd.h")
|
24
16
|
|
17
|
+
$defs << "-DEV_USE_LINUXAIO" if have_header("linux/aio_abi.h")
|
18
|
+
$defs << "-DEV_USE_SELECT" if have_header("sys/select.h")
|
19
|
+
$defs << "-DEV_USE_POLL" if have_type("port_event_t", "poll.h")
|
20
|
+
$defs << "-DEV_USE_EPOLL" if have_header("sys/epoll.h")
|
21
|
+
$defs << "-DEV_USE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h")
|
22
|
+
$defs << "-DEV_USE_PORT" if have_type("port_event_t", "port.h")
|
25
23
|
$defs << "-DHAVE_SYS_RESOURCE_H" if have_header("sys/resource.h")
|
26
24
|
|
27
|
-
|
25
|
+
CONFIG["optflags"] << " -fno-strict-aliasing" unless RUBY_PLATFORM =~ /mswin/
|
28
26
|
|
29
27
|
dir_config "nio4r_ext"
|
30
28
|
create_makefile "nio4r_ext"
|
31
|
-
|
32
|
-
# win32 needs to link in "just the right order" for some reason or
|
33
|
-
# ioctlsocket will be mapped to an [inverted] ruby specific version.
|
34
|
-
if RUBY_PLATFORM =~ /mingw|win32/
|
35
|
-
makefile_contents = File.read "Makefile"
|
36
|
-
|
37
|
-
makefile_contents.gsub! "DLDFLAGS = ", "DLDFLAGS = -export-all "
|
38
|
-
|
39
|
-
makefile_contents.gsub! "LIBS = $(LIBRUBYARG_SHARED)", "LIBS = -lws2_32 $(LIBRUBYARG_SHARED)"
|
40
|
-
File.open("Makefile", "w") { |f| f.write makefile_contents }
|
41
|
-
end
|
data/ext/nio4r/monitor.c
CHANGED
@@ -15,12 +15,13 @@ static void NIO_Monitor_free(struct NIO_Monitor *monitor);
|
|
15
15
|
|
16
16
|
/* Methods */
|
17
17
|
static VALUE NIO_Monitor_initialize(VALUE self, VALUE selector, VALUE io, VALUE interests);
|
18
|
-
static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests);
|
19
|
-
|
20
18
|
static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self);
|
21
19
|
static VALUE NIO_Monitor_is_closed(VALUE self);
|
22
20
|
static VALUE NIO_Monitor_io(VALUE self);
|
23
21
|
static VALUE NIO_Monitor_interests(VALUE self);
|
22
|
+
static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests);
|
23
|
+
static VALUE NIO_Monitor_add_interest(VALUE self, VALUE interest);
|
24
|
+
static VALUE NIO_Monitor_remove_interest(VALUE self, VALUE interest);
|
24
25
|
static VALUE NIO_Monitor_selector(VALUE self);
|
25
26
|
static VALUE NIO_Monitor_is_readable(VALUE self);
|
26
27
|
static VALUE NIO_Monitor_is_writable(VALUE self);
|
@@ -28,11 +29,9 @@ static VALUE NIO_Monitor_value(VALUE self);
|
|
28
29
|
static VALUE NIO_Monitor_set_value(VALUE self, VALUE obj);
|
29
30
|
static VALUE NIO_Monitor_readiness(VALUE self);
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
OpenFile *fptr;
|
35
|
-
#endif
|
32
|
+
/* Internal C functions */
|
33
|
+
static int NIO_Monitor_symbol2interest(VALUE interests);
|
34
|
+
static void NIO_Monitor_update_interests(VALUE self, int interests);
|
36
35
|
|
37
36
|
/* Monitor control how a channel is being waited for by a monitor */
|
38
37
|
void Init_NIO_Monitor()
|
@@ -42,11 +41,13 @@ void Init_NIO_Monitor()
|
|
42
41
|
rb_define_alloc_func(cNIO_Monitor, NIO_Monitor_allocate);
|
43
42
|
|
44
43
|
rb_define_method(cNIO_Monitor, "initialize", NIO_Monitor_initialize, 3);
|
45
|
-
rb_define_method(cNIO_Monitor, "interests=", NIO_Monitor_set_interests, 1);
|
46
44
|
rb_define_method(cNIO_Monitor, "close", NIO_Monitor_close, -1);
|
47
45
|
rb_define_method(cNIO_Monitor, "closed?", NIO_Monitor_is_closed, 0);
|
48
46
|
rb_define_method(cNIO_Monitor, "io", NIO_Monitor_io, 0);
|
49
47
|
rb_define_method(cNIO_Monitor, "interests", NIO_Monitor_interests, 0);
|
48
|
+
rb_define_method(cNIO_Monitor, "interests=", NIO_Monitor_set_interests, 1);
|
49
|
+
rb_define_method(cNIO_Monitor, "add_interest", NIO_Monitor_add_interest, 1);
|
50
|
+
rb_define_method(cNIO_Monitor, "remove_interest", NIO_Monitor_remove_interest, 1);
|
50
51
|
rb_define_method(cNIO_Monitor, "selector", NIO_Monitor_selector, 0);
|
51
52
|
rb_define_method(cNIO_Monitor, "value", NIO_Monitor_value, 0);
|
52
53
|
rb_define_method(cNIO_Monitor, "value=", NIO_Monitor_set_value, 1);
|
@@ -65,6 +66,7 @@ static VALUE NIO_Monitor_allocate(VALUE klass)
|
|
65
66
|
|
66
67
|
static void NIO_Monitor_mark(struct NIO_Monitor *monitor)
|
67
68
|
{
|
69
|
+
return rb_gc_mark(monitor->self);
|
68
70
|
}
|
69
71
|
|
70
72
|
static void NIO_Monitor_free(struct NIO_Monitor *monitor)
|
@@ -77,12 +79,7 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
|
|
77
79
|
struct NIO_Monitor *monitor;
|
78
80
|
struct NIO_Selector *selector;
|
79
81
|
ID interests_id;
|
80
|
-
|
81
|
-
#if HAVE_RB_IO_T
|
82
|
-
rb_io_t *fptr;
|
83
|
-
#else
|
84
|
-
OpenFile *fptr;
|
85
|
-
#endif
|
82
|
+
rb_io_t *fptr;
|
86
83
|
|
87
84
|
interests_id = SYM2ID(interests);
|
88
85
|
|
@@ -96,7 +93,7 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
|
|
96
93
|
monitor->interests = EV_READ | EV_WRITE;
|
97
94
|
} else {
|
98
95
|
rb_raise(rb_eArgError, "invalid event type %s (must be :r, :w, or :rw)",
|
99
|
-
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0
|
96
|
+
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0)));
|
100
97
|
}
|
101
98
|
|
102
99
|
GetOpenFile(rb_convert_type(io, T_FILE, "IO", "to_io"), fptr);
|
@@ -115,41 +112,11 @@ static VALUE NIO_Monitor_initialize(VALUE self, VALUE io, VALUE interests, VALUE
|
|
115
112
|
object where it originally came from */
|
116
113
|
monitor->selector = selector;
|
117
114
|
|
118
|
-
|
119
|
-
|
120
|
-
return Qnil;
|
121
|
-
}
|
122
|
-
|
123
|
-
static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests)
|
124
|
-
{
|
125
|
-
struct NIO_Monitor *monitor;
|
126
|
-
ID interests_id;
|
127
|
-
|
128
|
-
if(NIO_Monitor_is_closed(self) == Qtrue) {
|
129
|
-
rb_raise(rb_eTypeError, "monitor is already closed");
|
130
|
-
}
|
131
|
-
|
132
|
-
interests_id = SYM2ID(interests);
|
133
|
-
Data_Get_Struct(self, struct NIO_Monitor, monitor);
|
134
|
-
|
135
|
-
if(interests_id == rb_intern("r")) {
|
136
|
-
monitor->interests = EV_READ;
|
137
|
-
} else if(interests_id == rb_intern("w")) {
|
138
|
-
monitor->interests = EV_WRITE;
|
139
|
-
} else if(interests_id == rb_intern("rw")) {
|
140
|
-
monitor->interests = EV_READ | EV_WRITE;
|
141
|
-
} else {
|
142
|
-
rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)",
|
143
|
-
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0, 0)));
|
115
|
+
if (monitor->interests) {
|
116
|
+
ev_io_start(selector->ev_loop, &monitor->ev_io);
|
144
117
|
}
|
145
118
|
|
146
|
-
|
147
|
-
ev_io_set(&monitor->ev_io, monitor->ev_io.fd, monitor->interests);
|
148
|
-
ev_io_start(monitor->selector->ev_loop, &monitor->ev_io);
|
149
|
-
|
150
|
-
rb_ivar_set(self, rb_intern("interests"), interests);
|
151
|
-
|
152
|
-
return interests;
|
119
|
+
return Qnil;
|
153
120
|
}
|
154
121
|
|
155
122
|
static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
|
@@ -163,7 +130,7 @@ static VALUE NIO_Monitor_close(int argc, VALUE *argv, VALUE self)
|
|
163
130
|
|
164
131
|
if(selector != Qnil) {
|
165
132
|
/* if ev_loop is 0, it means that the loop has been stopped already (see NIO_Selector_shutdown) */
|
166
|
-
if(monitor->selector->ev_loop
|
133
|
+
if(monitor->interests && monitor->selector->ev_loop) {
|
167
134
|
ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
|
168
135
|
}
|
169
136
|
|
@@ -197,6 +164,37 @@ static VALUE NIO_Monitor_interests(VALUE self)
|
|
197
164
|
return rb_ivar_get(self, rb_intern("interests"));
|
198
165
|
}
|
199
166
|
|
167
|
+
static VALUE NIO_Monitor_set_interests(VALUE self, VALUE interests)
|
168
|
+
{
|
169
|
+
if(NIL_P(interests)) {
|
170
|
+
NIO_Monitor_update_interests(self, 0);
|
171
|
+
} else {
|
172
|
+
NIO_Monitor_update_interests(self, NIO_Monitor_symbol2interest(interests));
|
173
|
+
}
|
174
|
+
|
175
|
+
return rb_ivar_get(self, rb_intern("interests"));
|
176
|
+
}
|
177
|
+
|
178
|
+
static VALUE NIO_Monitor_add_interest(VALUE self, VALUE interest) {
|
179
|
+
struct NIO_Monitor *monitor;
|
180
|
+
Data_Get_Struct(self, struct NIO_Monitor, monitor);
|
181
|
+
|
182
|
+
interest = monitor->interests | NIO_Monitor_symbol2interest(interest);
|
183
|
+
NIO_Monitor_update_interests(self, interest);
|
184
|
+
|
185
|
+
return rb_ivar_get(self, rb_intern("interests"));
|
186
|
+
}
|
187
|
+
|
188
|
+
static VALUE NIO_Monitor_remove_interest(VALUE self, VALUE interest) {
|
189
|
+
struct NIO_Monitor *monitor;
|
190
|
+
Data_Get_Struct(self, struct NIO_Monitor, monitor);
|
191
|
+
|
192
|
+
interest = monitor->interests & ~NIO_Monitor_symbol2interest(interest);
|
193
|
+
NIO_Monitor_update_interests(self, interest);
|
194
|
+
|
195
|
+
return rb_ivar_get(self, rb_intern("interests"));
|
196
|
+
}
|
197
|
+
|
200
198
|
static VALUE NIO_Monitor_selector(VALUE self)
|
201
199
|
{
|
202
200
|
return rb_ivar_get(self, rb_intern("selector"));
|
@@ -251,3 +249,69 @@ static VALUE NIO_Monitor_is_writable(VALUE self)
|
|
251
249
|
return Qfalse;
|
252
250
|
}
|
253
251
|
}
|
252
|
+
|
253
|
+
/* Internal C functions */
|
254
|
+
|
255
|
+
static int NIO_Monitor_symbol2interest(VALUE interests)
|
256
|
+
{
|
257
|
+
ID interests_id;
|
258
|
+
interests_id = SYM2ID(interests);
|
259
|
+
|
260
|
+
if(interests_id == rb_intern("r")) {
|
261
|
+
return EV_READ;
|
262
|
+
} else if(interests_id == rb_intern("w")) {
|
263
|
+
return EV_WRITE;
|
264
|
+
} else if(interests_id == rb_intern("rw")) {
|
265
|
+
return EV_READ | EV_WRITE;
|
266
|
+
} else {
|
267
|
+
rb_raise(rb_eArgError, "invalid interest type %s (must be :r, :w, or :rw)",
|
268
|
+
RSTRING_PTR(rb_funcall(interests, rb_intern("inspect"), 0)));
|
269
|
+
}
|
270
|
+
}
|
271
|
+
|
272
|
+
static void NIO_Monitor_update_interests(VALUE self, int interests)
|
273
|
+
{
|
274
|
+
ID interests_id;
|
275
|
+
struct NIO_Monitor *monitor;
|
276
|
+
Data_Get_Struct(self, struct NIO_Monitor, monitor);
|
277
|
+
|
278
|
+
if(NIO_Monitor_is_closed(self) == Qtrue) {
|
279
|
+
rb_raise(rb_eEOFError, "monitor is closed");
|
280
|
+
}
|
281
|
+
|
282
|
+
if(interests) {
|
283
|
+
switch(interests) {
|
284
|
+
case EV_READ:
|
285
|
+
interests_id = rb_intern("r");
|
286
|
+
break;
|
287
|
+
case EV_WRITE:
|
288
|
+
interests_id = rb_intern("w");
|
289
|
+
break;
|
290
|
+
case EV_READ | EV_WRITE:
|
291
|
+
interests_id = rb_intern("rw");
|
292
|
+
break;
|
293
|
+
default:
|
294
|
+
rb_raise(rb_eRuntimeError, "bogus NIO_Monitor_update_interests! (%d)", interests);
|
295
|
+
}
|
296
|
+
|
297
|
+
rb_ivar_set(self, rb_intern("interests"), ID2SYM(interests_id));
|
298
|
+
} else {
|
299
|
+
rb_ivar_set(self, rb_intern("interests"), Qnil);
|
300
|
+
}
|
301
|
+
|
302
|
+
if(monitor->interests != interests) {
|
303
|
+
// If the monitor currently has interests, we should stop it.
|
304
|
+
if(monitor->interests) {
|
305
|
+
ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
|
306
|
+
}
|
307
|
+
|
308
|
+
// Assign the interests we are now monitoring for:
|
309
|
+
monitor->interests = interests;
|
310
|
+
ev_io_set(&monitor->ev_io, monitor->ev_io.fd, monitor->interests);
|
311
|
+
|
312
|
+
// If we are interested in events, schedule the monitor back into the event loop:
|
313
|
+
if(monitor->interests) {
|
314
|
+
ev_io_start(monitor->selector->ev_loop, &monitor->ev_io);
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
data/ext/nio4r/nio4r.h
CHANGED
@@ -7,11 +7,7 @@
|
|
7
7
|
#define NIO4R_H
|
8
8
|
|
9
9
|
#include "ruby.h"
|
10
|
-
#
|
11
|
-
# include "ruby/io.h"
|
12
|
-
#else
|
13
|
-
# include "rubyio.h"
|
14
|
-
#endif
|
10
|
+
#include "ruby/io.h"
|
15
11
|
#include "libev.h"
|
16
12
|
|
17
13
|
struct NIO_Selector
|
@@ -20,9 +16,10 @@ struct NIO_Selector
|
|
20
16
|
struct ev_timer timer; /* for timeouts */
|
21
17
|
struct ev_io wakeup;
|
22
18
|
|
23
|
-
int wakeup_reader, wakeup_writer;
|
24
|
-
int closed, selecting;
|
25
19
|
int ready_count;
|
20
|
+
int closed, selecting;
|
21
|
+
int wakeup_reader, wakeup_writer;
|
22
|
+
volatile int wakeup_fired;
|
26
23
|
|
27
24
|
VALUE ready_array;
|
28
25
|
};
|
@@ -41,16 +38,17 @@ struct NIO_Monitor
|
|
41
38
|
struct NIO_Selector *selector;
|
42
39
|
};
|
43
40
|
|
41
|
+
struct NIO_ByteBuffer
|
42
|
+
{
|
43
|
+
char *buffer;
|
44
|
+
int position, limit, capacity, mark;
|
45
|
+
};
|
46
|
+
|
47
|
+
|
44
48
|
#ifdef GetReadFile
|
45
49
|
# define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
|
46
50
|
#else
|
47
|
-
|
48
|
-
#if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
|
49
|
-
# define FPTR_TO_FD(fptr) fileno(fptr->f)
|
50
|
-
#else
|
51
51
|
# define FPTR_TO_FD(fptr) fptr->fd
|
52
|
-
#endif /* !HAVE_RB_IO_T */
|
53
|
-
|
54
52
|
#endif /* GetReadFile */
|
55
53
|
|
56
54
|
/* Thunk between libev callbacks in NIO::Monitors and NIO::Selectors */
|
@@ -0,0 +1,293 @@
|
|
1
|
+
package org.nio4r;
|
2
|
+
|
3
|
+
import java.io.IOException;
|
4
|
+
import java.nio.channels.Channel;
|
5
|
+
import java.nio.channels.SelectableChannel;
|
6
|
+
import java.nio.channels.ReadableByteChannel;
|
7
|
+
import java.nio.channels.WritableByteChannel;
|
8
|
+
import java.nio.BufferOverflowException;
|
9
|
+
import java.nio.BufferUnderflowException;
|
10
|
+
import java.nio.InvalidMarkException;
|
11
|
+
|
12
|
+
import org.jruby.Ruby;
|
13
|
+
import org.jruby.RubyClass;
|
14
|
+
import org.jruby.RubyIO;
|
15
|
+
import org.jruby.RubyNumeric;
|
16
|
+
import org.jruby.RubyObject;
|
17
|
+
import org.jruby.RubyString;
|
18
|
+
import org.jruby.anno.JRubyMethod;
|
19
|
+
import org.jruby.exceptions.RaiseException;
|
20
|
+
import org.jruby.runtime.ThreadContext;
|
21
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
22
|
+
import org.jruby.runtime.Block;
|
23
|
+
|
24
|
+
/*
|
25
|
+
created by Upekshej
|
26
|
+
*/
|
27
|
+
public class ByteBuffer extends RubyObject {
|
28
|
+
private java.nio.ByteBuffer byteBuffer;
|
29
|
+
|
30
|
+
public static RaiseException newOverflowError(ThreadContext context, String message) {
|
31
|
+
RubyClass klass = context.runtime.getModule("NIO").getClass("ByteBuffer").getClass("OverflowError");
|
32
|
+
return context.runtime.newRaiseException(klass, message);
|
33
|
+
}
|
34
|
+
|
35
|
+
public static RaiseException newUnderflowError(ThreadContext context, String message) {
|
36
|
+
RubyClass klass = context.runtime.getModule("NIO").getClass("ByteBuffer").getClass("UnderflowError");
|
37
|
+
return context.runtime.newRaiseException(klass, message);
|
38
|
+
}
|
39
|
+
|
40
|
+
public static RaiseException newMarkUnsetError(ThreadContext context, String message) {
|
41
|
+
RubyClass klass = context.runtime.getModule("NIO").getClass("ByteBuffer").getClass("MarkUnsetError");
|
42
|
+
return context.runtime.newRaiseException(klass, message);
|
43
|
+
}
|
44
|
+
|
45
|
+
public ByteBuffer(final Ruby ruby, RubyClass rubyClass) {
|
46
|
+
super(ruby, rubyClass);
|
47
|
+
}
|
48
|
+
|
49
|
+
@JRubyMethod
|
50
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject capacity) {
|
51
|
+
this.byteBuffer = java.nio.ByteBuffer.allocate(RubyNumeric.num2int(capacity));
|
52
|
+
return this;
|
53
|
+
}
|
54
|
+
|
55
|
+
@JRubyMethod
|
56
|
+
public IRubyObject clear(ThreadContext context) {
|
57
|
+
this.byteBuffer.clear();
|
58
|
+
return this;
|
59
|
+
}
|
60
|
+
|
61
|
+
@JRubyMethod(name = "position")
|
62
|
+
public IRubyObject getPosition(ThreadContext context) {
|
63
|
+
return context.getRuntime().newFixnum(this.byteBuffer.position());
|
64
|
+
}
|
65
|
+
|
66
|
+
@JRubyMethod(name = "position=")
|
67
|
+
public IRubyObject setPosition(ThreadContext context, IRubyObject newPosition) {
|
68
|
+
int pos = RubyNumeric.num2int(newPosition);
|
69
|
+
|
70
|
+
if(pos < 0) {
|
71
|
+
throw context.runtime.newArgumentError("negative position given");
|
72
|
+
}
|
73
|
+
|
74
|
+
if(pos > this.byteBuffer.limit()) {
|
75
|
+
throw context.runtime.newArgumentError("specified position exceeds limit");
|
76
|
+
}
|
77
|
+
|
78
|
+
try {
|
79
|
+
this.byteBuffer.position(pos);
|
80
|
+
return newPosition;
|
81
|
+
} catch(IllegalArgumentException e) {
|
82
|
+
throw context.runtime.newArgumentError(e.getLocalizedMessage());
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
@JRubyMethod(name = "limit")
|
87
|
+
public IRubyObject getLimit(ThreadContext context) {
|
88
|
+
return context.getRuntime().newFixnum(this.byteBuffer.limit());
|
89
|
+
}
|
90
|
+
|
91
|
+
@JRubyMethod(name = "limit=")
|
92
|
+
public IRubyObject setLimit(ThreadContext context, IRubyObject newLimit) {
|
93
|
+
int lim = RubyNumeric.num2int(newLimit);
|
94
|
+
|
95
|
+
if(lim < 0) {
|
96
|
+
throw context.runtime.newArgumentError("negative limit given");
|
97
|
+
}
|
98
|
+
|
99
|
+
if(lim > this.byteBuffer.capacity()) {
|
100
|
+
throw context.runtime.newArgumentError("specified limit exceeds capacity");
|
101
|
+
}
|
102
|
+
|
103
|
+
try {
|
104
|
+
this.byteBuffer.limit(lim);
|
105
|
+
return newLimit;
|
106
|
+
} catch(IllegalArgumentException e) {
|
107
|
+
throw context.runtime.newArgumentError(e.getLocalizedMessage());
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
@JRubyMethod(name = {"capacity", "size"})
|
112
|
+
public IRubyObject capacity(ThreadContext context) {
|
113
|
+
return context.getRuntime().newFixnum(this.byteBuffer.capacity());
|
114
|
+
}
|
115
|
+
|
116
|
+
@JRubyMethod
|
117
|
+
public IRubyObject remaining(ThreadContext context) {
|
118
|
+
return context.getRuntime().newFixnum(this.byteBuffer.remaining());
|
119
|
+
}
|
120
|
+
|
121
|
+
@JRubyMethod(name = "full?")
|
122
|
+
public IRubyObject isFull(ThreadContext context) {
|
123
|
+
if (this.byteBuffer.hasRemaining()) {
|
124
|
+
return context.getRuntime().getFalse();
|
125
|
+
} else {
|
126
|
+
return context.getRuntime().getTrue();
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
130
|
+
@JRubyMethod
|
131
|
+
public IRubyObject get(ThreadContext context) {
|
132
|
+
return this.get(context, context.getRuntime().newFixnum(this.byteBuffer.remaining()));
|
133
|
+
}
|
134
|
+
|
135
|
+
@JRubyMethod
|
136
|
+
public IRubyObject get(ThreadContext context, IRubyObject length) {
|
137
|
+
int len = RubyNumeric.num2int(length);
|
138
|
+
byte[] bytes = new byte[len];
|
139
|
+
|
140
|
+
try {
|
141
|
+
this.byteBuffer.get(bytes);
|
142
|
+
} catch(BufferUnderflowException e) {
|
143
|
+
throw ByteBuffer.newUnderflowError(context, "not enough data in buffer");
|
144
|
+
}
|
145
|
+
|
146
|
+
return RubyString.newString(context.getRuntime(), bytes);
|
147
|
+
}
|
148
|
+
|
149
|
+
@JRubyMethod(name = "[]")
|
150
|
+
public IRubyObject fetch(ThreadContext context, IRubyObject index) {
|
151
|
+
int i = RubyNumeric.num2int(index);
|
152
|
+
|
153
|
+
if(i < 0) {
|
154
|
+
throw context.runtime.newArgumentError("negative index given");
|
155
|
+
}
|
156
|
+
|
157
|
+
if(i >= this.byteBuffer.limit()) {
|
158
|
+
throw context.runtime.newArgumentError("index exceeds limit");
|
159
|
+
}
|
160
|
+
|
161
|
+
return context.getRuntime().newFixnum(this.byteBuffer.get(i));
|
162
|
+
}
|
163
|
+
|
164
|
+
@JRubyMethod(name = "<<")
|
165
|
+
public IRubyObject put(ThreadContext context, IRubyObject str) {
|
166
|
+
try {
|
167
|
+
this.byteBuffer.put(str.convertToString().getByteList().bytes());
|
168
|
+
} catch(BufferOverflowException e) {
|
169
|
+
throw ByteBuffer.newOverflowError(context, "buffer is full");
|
170
|
+
}
|
171
|
+
|
172
|
+
return this;
|
173
|
+
}
|
174
|
+
|
175
|
+
@JRubyMethod(name = "read_from")
|
176
|
+
public IRubyObject readFrom(ThreadContext context, IRubyObject io) {
|
177
|
+
Ruby runtime = context.runtime;
|
178
|
+
Channel channel = RubyIO.convertToIO(context, io).getChannel();
|
179
|
+
|
180
|
+
if(!this.byteBuffer.hasRemaining()) {
|
181
|
+
throw ByteBuffer.newOverflowError(context, "buffer is full");
|
182
|
+
}
|
183
|
+
|
184
|
+
if(!(channel instanceof ReadableByteChannel) || !(channel instanceof SelectableChannel)) {
|
185
|
+
throw runtime.newArgumentError("unsupported IO object: " + io.getType().toString());
|
186
|
+
}
|
187
|
+
|
188
|
+
try {
|
189
|
+
((SelectableChannel)channel).configureBlocking(false);
|
190
|
+
} catch(IOException ie) {
|
191
|
+
throw runtime.newIOError(ie.getLocalizedMessage());
|
192
|
+
}
|
193
|
+
|
194
|
+
try {
|
195
|
+
int bytesRead = ((ReadableByteChannel)channel).read(this.byteBuffer);
|
196
|
+
|
197
|
+
if(bytesRead >= 0) {
|
198
|
+
return runtime.newFixnum(bytesRead);
|
199
|
+
} else {
|
200
|
+
throw runtime.newEOFError();
|
201
|
+
}
|
202
|
+
} catch(IOException ie) {
|
203
|
+
throw runtime.newIOError(ie.getLocalizedMessage());
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
@JRubyMethod(name = "write_to")
|
208
|
+
public IRubyObject writeTo(ThreadContext context, IRubyObject io) {
|
209
|
+
Ruby runtime = context.runtime;
|
210
|
+
Channel channel = RubyIO.convertToIO(context, io).getChannel();
|
211
|
+
|
212
|
+
if(!this.byteBuffer.hasRemaining()) {
|
213
|
+
throw ByteBuffer.newUnderflowError(context, "not enough data in buffer");
|
214
|
+
}
|
215
|
+
|
216
|
+
if(!(channel instanceof WritableByteChannel) || !(channel instanceof SelectableChannel)) {
|
217
|
+
throw runtime.newArgumentError("unsupported IO object: " + io.getType().toString());
|
218
|
+
}
|
219
|
+
|
220
|
+
try {
|
221
|
+
((SelectableChannel)channel).configureBlocking(false);
|
222
|
+
} catch(IOException ie) {
|
223
|
+
throw runtime.newIOError(ie.getLocalizedMessage());
|
224
|
+
}
|
225
|
+
|
226
|
+
try {
|
227
|
+
int bytesWritten = ((WritableByteChannel)channel).write(this.byteBuffer);
|
228
|
+
|
229
|
+
if(bytesWritten >= 0) {
|
230
|
+
return runtime.newFixnum(bytesWritten);
|
231
|
+
} else {
|
232
|
+
throw runtime.newEOFError();
|
233
|
+
}
|
234
|
+
} catch(IOException ie) {
|
235
|
+
throw runtime.newIOError(ie.getLocalizedMessage());
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
@JRubyMethod
|
240
|
+
public IRubyObject flip(ThreadContext context) {
|
241
|
+
this.byteBuffer.flip();
|
242
|
+
return this;
|
243
|
+
}
|
244
|
+
|
245
|
+
@JRubyMethod
|
246
|
+
public IRubyObject rewind(ThreadContext context) {
|
247
|
+
this.byteBuffer.rewind();
|
248
|
+
return this;
|
249
|
+
}
|
250
|
+
|
251
|
+
@JRubyMethod
|
252
|
+
public IRubyObject mark(ThreadContext context) {
|
253
|
+
this.byteBuffer.mark();
|
254
|
+
return this;
|
255
|
+
}
|
256
|
+
|
257
|
+
@JRubyMethod
|
258
|
+
public IRubyObject reset(ThreadContext context) {
|
259
|
+
try {
|
260
|
+
this.byteBuffer.reset();
|
261
|
+
return this;
|
262
|
+
} catch(InvalidMarkException ie) {
|
263
|
+
throw ByteBuffer.newMarkUnsetError(context, "mark has not been set");
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
@JRubyMethod
|
268
|
+
public IRubyObject compact(ThreadContext context) {
|
269
|
+
this.byteBuffer.compact();
|
270
|
+
return this;
|
271
|
+
}
|
272
|
+
|
273
|
+
@JRubyMethod
|
274
|
+
public IRubyObject each(ThreadContext context, Block block) {
|
275
|
+
for(int i = 0; i < this.byteBuffer.limit(); i++) {
|
276
|
+
block.call(context, context.getRuntime().newFixnum(this.byteBuffer.get(i)));
|
277
|
+
}
|
278
|
+
|
279
|
+
return this;
|
280
|
+
}
|
281
|
+
|
282
|
+
@JRubyMethod
|
283
|
+
public IRubyObject inspect(ThreadContext context) {
|
284
|
+
return context.runtime.newString(String.format(
|
285
|
+
"#<%s:0x%x @position=%d @limit=%d @capacity=%d>",
|
286
|
+
this.getType().toString(),
|
287
|
+
System.identityHashCode(this),
|
288
|
+
this.byteBuffer.position(),
|
289
|
+
this.byteBuffer.limit(),
|
290
|
+
this.byteBuffer.capacity()
|
291
|
+
));
|
292
|
+
}
|
293
|
+
}
|