nio4r 1.2.1 → 2.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/workflow.yml +43 -0
  3. data/.gitignore +1 -0
  4. data/.rspec +0 -1
  5. data/.rubocop.yml +70 -31
  6. data/CHANGES.md +190 -42
  7. data/Gemfile +8 -4
  8. data/Guardfile +10 -0
  9. data/README.md +102 -147
  10. data/Rakefile +3 -4
  11. data/examples/echo_server.rb +3 -2
  12. data/ext/libev/Changes +44 -13
  13. data/ext/libev/README +2 -1
  14. data/ext/libev/ev.c +314 -225
  15. data/ext/libev/ev.h +90 -88
  16. data/ext/libev/ev_epoll.c +30 -16
  17. data/ext/libev/ev_kqueue.c +19 -9
  18. data/ext/libev/ev_linuxaio.c +642 -0
  19. data/ext/libev/ev_poll.c +19 -11
  20. data/ext/libev/ev_port.c +13 -6
  21. data/ext/libev/ev_select.c +4 -2
  22. data/ext/libev/ev_vars.h +14 -3
  23. data/ext/libev/ev_wrap.h +16 -0
  24. data/ext/nio4r/bytebuffer.c +429 -0
  25. data/ext/nio4r/extconf.rb +17 -30
  26. data/ext/nio4r/monitor.c +113 -49
  27. data/ext/nio4r/nio4r.h +11 -13
  28. data/ext/nio4r/org/nio4r/ByteBuffer.java +293 -0
  29. data/ext/nio4r/org/nio4r/Monitor.java +175 -0
  30. data/ext/nio4r/org/nio4r/Nio4r.java +22 -391
  31. data/ext/nio4r/org/nio4r/Selector.java +299 -0
  32. data/ext/nio4r/selector.c +155 -68
  33. data/lib/nio.rb +4 -4
  34. data/lib/nio/bytebuffer.rb +229 -0
  35. data/lib/nio/monitor.rb +73 -11
  36. data/lib/nio/selector.rb +64 -21
  37. data/lib/nio/version.rb +1 -1
  38. data/nio4r.gemspec +34 -20
  39. data/{tasks → rakelib}/extension.rake +4 -0
  40. data/{tasks → rakelib}/rspec.rake +2 -0
  41. data/{tasks → rakelib}/rubocop.rake +2 -0
  42. data/spec/nio/acceptables_spec.rb +5 -5
  43. data/spec/nio/bytebuffer_spec.rb +354 -0
  44. data/spec/nio/monitor_spec.rb +128 -79
  45. data/spec/nio/selectables/pipe_spec.rb +12 -3
  46. data/spec/nio/selectables/ssl_socket_spec.rb +61 -29
  47. data/spec/nio/selectables/tcp_socket_spec.rb +47 -34
  48. data/spec/nio/selectables/udp_socket_spec.rb +24 -7
  49. data/spec/nio/selector_spec.rb +65 -16
  50. data/spec/spec_helper.rb +12 -3
  51. data/spec/support/selectable_examples.rb +45 -18
  52. metadata +33 -23
  53. data/.rubocop_todo.yml +0 -35
  54. data/.travis.yml +0 -27
  55. data/LICENSE.txt +0 -20
  56. data/ext/libev/README.embed +0 -3
  57. data/ext/libev/test_libev_win32.c +0 -123
@@ -1,41 +1,28 @@
1
- require "mkmf"
2
-
3
- have_header("unistd.h")
1
+ # frozen_string_literal: true
4
2
 
5
- if have_func("rb_thread_blocking_region")
6
- $defs << "-DHAVE_RB_THREAD_BLOCKING_REGION"
7
- end
3
+ require "rubygems"
8
4
 
9
- if have_func("rb_thread_call_without_gvl")
10
- $defs << "-DHAVE_RB_THEREAD_CALL_WITHOUT_GVL"
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
- $defs << "-DEV_USE_SELECT" if have_header("sys/select.h")
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
- $defs << "-DEV_USE_PORT" if have_header("port.h")
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
- $defs << "-DHAVE_RUBYSIG_H" if RUBY_VERSION.to_f < 1.9
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
@@ -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
- #if HAVE_RB_IO_T
32
- rb_io_t *fptr;
33
- #else
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, 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
- ev_io_start(selector->ev_loop, &monitor->ev_io);
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
- ev_io_stop(monitor->selector->ev_loop, &monitor->ev_io);
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 != 0) {
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
+ }
@@ -7,11 +7,7 @@
7
7
  #define NIO4R_H
8
8
 
9
9
  #include "ruby.h"
10
- #if HAVE_RUBY_IO_H
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
+ }