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.
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
+ }