polyphony 0.82 → 0.83

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,440 @@
1
+ /*
2
+ Relevant resources:
3
+
4
+ zlib manual: https://zlib.net/manual.html
5
+ gzip format: https://www.ietf.org/rfc/rfc1952.txt
6
+ ruby zlib src: https://github.com/ruby/zlib/blob/master/ext/zlib/zlib.c
7
+ */
8
+
9
+ #include <time.h>
10
+ #include "polyphony.h"
11
+ #include "zlib.h"
12
+ #include "assert.h"
13
+
14
+ ID ID_at;
15
+ ID ID_read_method;
16
+ ID ID_to_i;
17
+ ID ID_write_method;
18
+
19
+ VALUE SYM_mtime;
20
+ VALUE SYM_orig_name;
21
+ VALUE SYM_comment;
22
+
23
+ enum read_method {
24
+ RM_BACKEND_READ,
25
+ RM_BACKEND_RECV
26
+ };
27
+
28
+ enum write_method {
29
+ WM_BACKEND_WRITE,
30
+ WM_BACKEND_SEND
31
+ };
32
+
33
+ #define print_buffer(prefix, ptr, len) { \
34
+ printf("%s buffer (%d): ", prefix, (int)len); \
35
+ for (int i = 0; i < len; i++) printf("%02X ", ptr[i]); \
36
+ printf("\n"); \
37
+ }
38
+
39
+ #define CHUNK 16384
40
+ #define MAX_WRITE_STR_LEN 16384
41
+ #define DEFAULT_LEVEL 9
42
+ #define DEFAULT_MEM_LEVEL 8
43
+
44
+ /* from zutil.h */
45
+ #define OS_MSDOS 0x00
46
+ #define OS_AMIGA 0x01
47
+ #define OS_VMS 0x02
48
+ #define OS_UNIX 0x03
49
+ #define OS_ATARI 0x05
50
+ #define OS_OS2 0x06
51
+ #define OS_MACOS 0x07
52
+ #define OS_TOPS20 0x0a
53
+ #define OS_WIN32 0x0b
54
+
55
+ #define OS_VMCMS 0x04
56
+ #define OS_ZSYSTEM 0x08
57
+ #define OS_CPM 0x09
58
+ #define OS_QDOS 0x0c
59
+ #define OS_RISCOS 0x0d
60
+ #define OS_UNKNOWN 0xff
61
+
62
+ #ifndef OS_CODE
63
+ #define OS_CODE OS_UNIX
64
+ #endif
65
+
66
+ static inline int read_to_raw_buffer(VALUE backend, VALUE io, enum read_method method, struct raw_buffer *buffer) {
67
+ VALUE len = Backend_read(backend, io, PTR2FIX(buffer), Qnil, Qfalse, INT2FIX(0));
68
+ return (len == Qnil) ? 0 : FIX2INT(len);
69
+ }
70
+
71
+ static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_method method, struct raw_buffer *buffer) {
72
+ VALUE len = Backend_write(backend, io, PTR2FIX(buffer));
73
+ return FIX2INT(len);
74
+ }
75
+
76
+ static inline int write_c_string_from_str(VALUE str, struct raw_buffer *buffer) {
77
+ int strlen = RSTRING_LEN(str);
78
+ if (strlen >= buffer->len)
79
+ rb_raise(rb_eRuntimeError, "string too long to fit in gzip header buffer");
80
+
81
+ memcpy(buffer->ptr, RSTRING_PTR(str), strlen);
82
+ buffer->ptr[strlen] = 0;
83
+ int written = strlen + 1;
84
+ buffer->ptr += written;
85
+ buffer->len -= written;
86
+ return written;
87
+ }
88
+
89
+ struct gzip_header_ctx {
90
+ VALUE mtime;
91
+ VALUE orig_name;
92
+ VALUE comment;
93
+ };
94
+
95
+ struct gzip_footer_ctx {
96
+ int crc32;
97
+ int isize;
98
+ };
99
+
100
+ #define GZ_MAGIC1 0x1f
101
+ #define GZ_MAGIC2 0x8b
102
+ #define GZ_METHOD_DEFLATE 8
103
+ #define GZ_FLAG_MULTIPART 0x2
104
+ #define GZ_FLAG_EXTRA 0x4
105
+ #define GZ_FLAG_ORIG_NAME 0x8
106
+ #define GZ_FLAG_COMMENT 0x10
107
+ #define GZ_FLAG_ENCRYPT 0x20
108
+ #define GZ_FLAG_UNKNOWN_MASK 0xc0
109
+
110
+ #define GZ_EXTRAFLAG_FAST 0x4
111
+ #define GZ_EXTRAFLAG_SLOW 0x2
112
+
113
+ static inline void gzfile_set32(unsigned long n, unsigned char *dst) {
114
+ *(dst++) = n & 0xff;
115
+ *(dst++) = (n >> 8) & 0xff;
116
+ *(dst++) = (n >> 16) & 0xff;
117
+ *dst = (n >> 24) & 0xff;
118
+ }
119
+
120
+ static inline unsigned long gzfile_get32(unsigned char *src) {
121
+ unsigned long n;
122
+ n = *(src++) & 0xff;
123
+ n |= (*(src++) & 0xff) << 8;
124
+ n |= (*(src++) & 0xff) << 16;
125
+ n |= (*(src++) & 0xffU) << 24;
126
+ return n;
127
+ }
128
+
129
+ static inline time_t time_from_object(VALUE o) {
130
+ if (o == Qfalse) return 0;
131
+ if (o == Qnil) return time(0); // now
132
+ if (FIXNUM_P(o)) return FIX2INT(o);
133
+ return FIX2INT(rb_funcall(o, rb_intern("to_i"), 0));
134
+ }
135
+
136
+ int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
137
+ int len = 0;
138
+ unsigned char flags = 0, extraflags = 0;
139
+
140
+ assert(maxlen >= 10);
141
+
142
+ if (!NIL_P(ctx->orig_name)) flags |= GZ_FLAG_ORIG_NAME;
143
+ if (!NIL_P(ctx->comment)) flags |= GZ_FLAG_COMMENT;
144
+
145
+ if (ctx->mtime)
146
+ ctx->mtime = time_from_object(ctx->mtime);
147
+
148
+ buffer[0] = GZ_MAGIC1;
149
+ buffer[1] = GZ_MAGIC2;
150
+ buffer[2] = GZ_METHOD_DEFLATE;
151
+ buffer[3] = flags;
152
+ gzfile_set32((unsigned long)ctx->mtime, &buffer[4]);
153
+ buffer[8] = extraflags;
154
+ buffer[9] = OS_CODE;
155
+
156
+ len = 10;
157
+
158
+ struct raw_buffer buffer_spec = {buffer + len, maxlen - len};
159
+ if (!NIL_P(ctx->orig_name))
160
+ len += write_c_string_from_str(ctx->orig_name, &buffer_spec);
161
+ if (!NIL_P(ctx->comment))
162
+ len += write_c_string_from_str(ctx->comment, &buffer_spec);
163
+
164
+ return len;
165
+ }
166
+
167
+ int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, char *buffer, int maxlen) {
168
+ assert(maxlen >= 8);
169
+
170
+ gzfile_set32(crc32, buffer);
171
+ gzfile_set32(total_in, buffer + 4);
172
+
173
+ return 8;
174
+ }
175
+
176
+ enum stream_mode {
177
+ SM_DEFLATE,
178
+ SM_INFLATE
179
+ };
180
+
181
+ struct z_stream_ctx {
182
+ VALUE backend;
183
+ VALUE src;
184
+ VALUE dest;
185
+
186
+ enum stream_mode mode;
187
+ z_stream strm;
188
+
189
+ unsigned char in[CHUNK];
190
+ unsigned char out[CHUNK];
191
+ int in_pos;
192
+ int out_pos;
193
+ unsigned long in_total;
194
+ unsigned long out_total;
195
+
196
+ unsigned long crc32;
197
+ };
198
+
199
+ typedef int (*zlib_func)(z_streamp, int);
200
+
201
+ void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, unsigned long *total_read) {
202
+ int null_pos;
203
+ // find null terminator
204
+ for (null_pos = *in_pos; null_pos < *total_read; null_pos++) {
205
+ if (!buffer->ptr[null_pos]) break;
206
+ }
207
+ if (null_pos == *total_read)
208
+ rb_raise(rb_eRuntimeError, "Invalid gzip header");
209
+
210
+ *str = rb_str_new_cstr(buffer->ptr + *in_pos);
211
+ *in_pos = null_pos + 1;
212
+ }
213
+
214
+ void gzip_read_header(struct z_stream_ctx *ctx, struct gzip_header_ctx *header_ctx) {
215
+ struct raw_buffer in_buffer = { ctx->in, CHUNK };
216
+ int flags;
217
+
218
+ while (ctx->in_total < 10) {
219
+ int read = read_to_raw_buffer(ctx->backend, ctx->src, RM_BACKEND_READ, &in_buffer);
220
+ if (read == 0) goto error;
221
+ ctx->in_total += read;
222
+ }
223
+ // print_buffer("read gzip header", ctx->in, ctx->in_total);
224
+ if (ctx->in[0] != GZ_MAGIC1) goto error;
225
+ if (ctx->in[1] != GZ_MAGIC2) goto error;
226
+ if (ctx->in[2] != GZ_METHOD_DEFLATE) goto error;
227
+ flags = ctx->in[3];
228
+
229
+ unsigned long mtime = gzfile_get32(ctx->in + 4);
230
+ header_ctx->mtime = INT2FIX(mtime);
231
+
232
+ ctx->in_pos = 10;
233
+
234
+ if (flags & GZ_FLAG_ORIG_NAME)
235
+ read_gzip_header_str(&in_buffer, &header_ctx->orig_name, &ctx->in_pos, &ctx->in_total);
236
+ else
237
+ header_ctx->orig_name = Qnil;
238
+ if (flags & GZ_FLAG_COMMENT)
239
+ read_gzip_header_str(&in_buffer, &header_ctx->comment, &ctx->in_pos, &ctx->in_total);
240
+ else
241
+ header_ctx->comment = Qnil;
242
+ return;
243
+
244
+ error:
245
+ rb_raise(rb_eRuntimeError, "Invalid gzip header");
246
+ }
247
+
248
+ // void gzip_read_footer(struct z_stream_ctx *ctx, struct gzip_footer_ctx *footer_ctx) {
249
+ // }
250
+
251
+ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, int eof) {
252
+ int ret;
253
+ int written;
254
+ struct raw_buffer out_buffer;
255
+
256
+ int avail_out_pre = ctx->strm.avail_out = CHUNK - ctx->out_pos;
257
+ ctx->strm.next_out = ctx->out + ctx->out_pos;
258
+ ret = fun(&ctx->strm, eof ? Z_FINISH : Z_NO_FLUSH);
259
+ assert(ret != Z_STREAM_ERROR);
260
+ written = avail_out_pre - ctx->strm.avail_out;
261
+ out_buffer.ptr = ctx->out;
262
+ out_buffer.len = ctx->out_pos + written;
263
+ if (out_buffer.len) {
264
+ ret = write_from_raw_buffer(ctx->backend, ctx->dest, WM_BACKEND_WRITE, &out_buffer);
265
+ if (ctx->mode == SM_INFLATE)
266
+ ctx->crc32 = crc32(ctx->crc32, out_buffer.ptr + ctx->out_pos, written);
267
+ ctx->out_total += ret - ctx->out_pos;
268
+ }
269
+ ctx->out_pos = 0;
270
+ return ctx->strm.avail_out;
271
+ }
272
+
273
+ void z_stream_io_loop(struct z_stream_ctx *ctx) {
274
+ zlib_func fun = (ctx->mode == SM_DEFLATE) ? deflate : inflate;
275
+
276
+ if (ctx->in_total > ctx->in_pos) {
277
+ // In bytes already read for parsing gzip header, so we need to process the
278
+ // rest.
279
+ ctx->strm.next_in = ctx->in + ctx->in_pos;
280
+ ctx->strm.avail_in = ctx->in_total -= ctx->in_pos;
281
+
282
+ while (1) {
283
+ // z_stream_write_out returns strm.avail_out. If there's still room in the
284
+ // out buffer that means the input buffer has been exhausted.
285
+ if (z_stream_write_out(ctx, fun, 0)) break;
286
+ }
287
+ }
288
+
289
+ while (1) {
290
+ struct raw_buffer in_buffer = {ctx->in, CHUNK};
291
+ ctx->strm.next_in = ctx->in;
292
+ int read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, RM_BACKEND_READ, &in_buffer);
293
+ if (!read_len) break;
294
+ int eof = read_len < CHUNK;
295
+
296
+ if (ctx->mode == SM_DEFLATE)
297
+ ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
298
+ ctx->in_total += read_len;
299
+
300
+ // print_buffer("read stream", ctx->in, read_len);
301
+
302
+ while (1) {
303
+ // z_stream_write_out returns strm.avail_out. If there's still room in the
304
+ // out buffer that means the input buffer has been exhausted.
305
+ if (z_stream_write_out(ctx, fun, eof)) break;
306
+ }
307
+
308
+ if (eof) return;
309
+ }
310
+
311
+ //flush
312
+ ctx->strm.avail_in = 0;
313
+ ctx->strm.next_in = ctx->in;
314
+ z_stream_write_out(ctx, fun, 1);
315
+ }
316
+
317
+ static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VALUE src, VALUE dest) {
318
+ ctx->backend = BACKEND();
319
+ ctx->src = src;
320
+ ctx->dest = dest;
321
+ ctx->mode = mode;
322
+ ctx->strm.zalloc = Z_NULL;
323
+ ctx->strm.zfree = Z_NULL;
324
+ ctx->strm.opaque = Z_NULL;
325
+ ctx->in_pos = 0;
326
+ ctx->out_pos = 0;
327
+ ctx->in_total = 0;
328
+ ctx->out_total = 0;
329
+ ctx->crc32 = 0;
330
+ }
331
+
332
+ VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
333
+ VALUE src;
334
+ VALUE dest;
335
+ VALUE opts = Qnil;
336
+ int opts_present;
337
+
338
+ rb_scan_args(argc, argv, "21", &src, &dest, &opts);
339
+ opts_present = opts != Qnil;
340
+
341
+ struct gzip_header_ctx header_ctx = {
342
+ opts_present ? rb_hash_aref(opts, SYM_mtime) : Qnil,
343
+ opts_present ? rb_hash_aref(opts, SYM_orig_name) : Qnil,
344
+ opts_present ? rb_hash_aref(opts, SYM_comment) : Qnil
345
+ };
346
+
347
+ struct z_stream_ctx ctx;
348
+ int level = DEFAULT_LEVEL;
349
+ int ret;
350
+
351
+ setup_ctx(&ctx, SM_DEFLATE, src, dest);
352
+ ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
353
+
354
+ ret = deflateInit2(&ctx.strm, level, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
355
+ if (ret != Z_OK) return INT2FIX(ret);
356
+ z_stream_io_loop(&ctx);
357
+ int footer_len = gzip_prepare_footer(ctx.crc32, ctx.in_total, ctx.out, sizeof(ctx.out));
358
+ struct raw_buffer footer_buffer = {ctx.out, footer_len};
359
+ write_from_raw_buffer(ctx.backend, dest, WM_BACKEND_WRITE, &footer_buffer);
360
+ deflateEnd(&ctx.strm);
361
+
362
+ return INT2FIX(ctx.out_total);
363
+ }
364
+
365
+ # define FIX2TIME(v) (rb_funcall(rb_cTime, ID_at, 1, v))
366
+
367
+ VALUE IO_gunzip(int argc, VALUE *argv, VALUE self) {
368
+ VALUE src;
369
+ VALUE dest;
370
+ VALUE info = Qnil;
371
+
372
+ rb_scan_args(argc, argv, "21", &src, &dest, &info);
373
+
374
+ struct gzip_header_ctx header_ctx;
375
+ // struct gzip_footer_ctx footer_ctx;
376
+ struct z_stream_ctx ctx;
377
+ int ret;
378
+
379
+ setup_ctx(&ctx, SM_INFLATE, src, dest);
380
+ gzip_read_header(&ctx, &header_ctx);
381
+
382
+ if (info != Qnil) {
383
+ rb_hash_aset(info, SYM_mtime, FIX2TIME(header_ctx.mtime));
384
+ rb_hash_aset(info, SYM_orig_name, header_ctx.orig_name);
385
+ rb_hash_aset(info, SYM_comment, header_ctx.comment);
386
+ }
387
+
388
+ ret = inflateInit2(&ctx.strm, -MAX_WBITS);
389
+ if (ret != Z_OK) return INT2FIX(ret);
390
+ z_stream_io_loop(&ctx);
391
+ inflateEnd(&ctx.strm);
392
+
393
+ // gzip_read_footer(&ctx, &footer_ctx);
394
+ // TODO: verify crc32
395
+ // TODO: verify total length
396
+ return self;
397
+ }
398
+
399
+ VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
400
+ struct z_stream_ctx ctx;
401
+ int level = DEFAULT_LEVEL;
402
+ int ret;
403
+
404
+ setup_ctx(&ctx, SM_DEFLATE, src, dest);
405
+ ret = deflateInit(&ctx.strm, level);
406
+ if (ret != Z_OK) return INT2FIX(ret);
407
+ z_stream_io_loop(&ctx);
408
+ deflateEnd(&ctx.strm);
409
+
410
+ return INT2FIX(ctx.out_total);
411
+ }
412
+
413
+ VALUE IO_inflate(VALUE self, VALUE src, VALUE dest) {
414
+ struct z_stream_ctx ctx;
415
+ int ret;
416
+
417
+ setup_ctx(&ctx, SM_INFLATE, src, dest);
418
+ ret = inflateInit(&ctx.strm);
419
+ if (ret != Z_OK) return INT2FIX(ret);
420
+ z_stream_io_loop(&ctx);
421
+ inflateEnd(&ctx.strm);
422
+
423
+ return INT2FIX(ctx.out_total);
424
+ }
425
+
426
+ void Init_IOExtensions() {
427
+ rb_define_singleton_method(rb_cIO, "gzip", IO_gzip, -1);
428
+ rb_define_singleton_method(rb_cIO, "gunzip", IO_gunzip, -1);
429
+ rb_define_singleton_method(rb_cIO, "deflate", IO_deflate, 2);
430
+ rb_define_singleton_method(rb_cIO, "inflate", IO_inflate, 2);
431
+
432
+ ID_at = rb_intern("at");
433
+ ID_read_method = rb_intern("__read_method__");
434
+ ID_to_i = rb_intern("to_i");
435
+ ID_write_method = rb_intern("__write_method__");
436
+
437
+ SYM_mtime = ID2SYM(rb_intern("mtime"));
438
+ SYM_orig_name = ID2SYM(rb_intern("orig_name"));
439
+ SYM_comment = ID2SYM(rb_intern("comment"));
440
+ }
@@ -0,0 +1,109 @@
1
+ #include <unistd.h>
2
+ #include "polyphony.h"
3
+
4
+ typedef struct pipe {
5
+ int fds[2];
6
+ unsigned int w_closed;
7
+ } Pipe_t;
8
+
9
+ VALUE cPipe = Qnil;
10
+ VALUE cClosedPipeError = Qnil;
11
+
12
+ static void Pipe_free(void *ptr) {
13
+ Pipe_t *pipe = ptr;
14
+ close(pipe->fds[0]);
15
+ if (!pipe->w_closed) close(pipe->fds[1]);
16
+ xfree(ptr);
17
+ }
18
+
19
+ static size_t Pipe_size(const void *ptr) {
20
+ return sizeof(Pipe_t);
21
+ }
22
+
23
+ static const rb_data_type_t Pipe_type = {
24
+ "Pipe",
25
+ {NULL, Pipe_free, Pipe_size,},
26
+ 0, 0, 0
27
+ };
28
+
29
+ static VALUE Pipe_allocate(VALUE klass) {
30
+ Pipe_t *pipe;
31
+
32
+ pipe = ALLOC(Pipe_t);
33
+ return TypedData_Wrap_Struct(klass, &Pipe_type, pipe);
34
+ }
35
+
36
+ #define GetPipe(obj, pipe) \
37
+ TypedData_Get_Struct((obj), Pipe_t, &Pipe_type, (pipe))
38
+
39
+ static VALUE Pipe_initialize(int argc, VALUE *argv, VALUE self) {
40
+ Pipe_t *pipe_struct;
41
+ GetPipe(self, pipe_struct);
42
+
43
+ int ret = pipe(pipe_struct->fds);
44
+ if (ret) {
45
+ int e = errno;
46
+ rb_syserr_fail(e, strerror(e));
47
+ }
48
+ pipe_struct->w_closed = 0;
49
+
50
+ return self;
51
+ }
52
+
53
+ void Pipe_verify_blocking_mode(VALUE self, VALUE blocking) {
54
+ Pipe_t *pipe_struct;
55
+ VALUE blocking_mode = rb_ivar_get(self, ID_ivar_blocking_mode);
56
+ if (blocking == blocking_mode) return;
57
+
58
+ rb_ivar_set(self, ID_ivar_blocking_mode, blocking);
59
+ GetPipe(self, pipe_struct);
60
+
61
+ set_fd_blocking_mode(pipe_struct->fds[0], blocking == Qtrue);
62
+ set_fd_blocking_mode(pipe_struct->fds[1], blocking == Qtrue);
63
+ }
64
+
65
+ int Pipe_get_fd(VALUE self, int write_mode) {
66
+ Pipe_t *pipe;
67
+ GetPipe(self, pipe);
68
+
69
+ if (write_mode && pipe->w_closed)
70
+ rb_raise(cClosedPipeError, "Pipe is closed for writing");
71
+
72
+ return pipe->fds[write_mode ? 1 : 0];
73
+ }
74
+
75
+ VALUE Pipe_closed_p(VALUE self) {
76
+ Pipe_t *pipe;
77
+ GetPipe(self, pipe);
78
+ return pipe->w_closed ? Qtrue : Qfalse;
79
+ }
80
+
81
+ VALUE Pipe_close(VALUE self) {
82
+ Pipe_t *pipe;
83
+ GetPipe(self, pipe);
84
+ if (pipe->w_closed)
85
+ rb_raise(rb_eRuntimeError, "Pipe is already closed for writing");
86
+
87
+ pipe->w_closed = 1;
88
+ close(pipe->fds[1]);
89
+ return self;
90
+ }
91
+
92
+ VALUE Pipe_fds(VALUE self) {
93
+ Pipe_t *pipe;
94
+ GetPipe(self, pipe);
95
+
96
+ return rb_ary_new_from_args(2, INT2FIX(pipe->fds[0]), INT2FIX(pipe->fds[1]));
97
+ }
98
+
99
+ void Init_Pipe() {
100
+ cPipe = rb_define_class_under(mPolyphony, "Pipe", rb_cObject);
101
+ cClosedPipeError = rb_define_class_under(cPipe, "ClosedPipeError", rb_eRuntimeError);
102
+
103
+ rb_define_alloc_func(cPipe, Pipe_allocate);
104
+
105
+ rb_define_method(cPipe, "initialize", Pipe_initialize, -1);
106
+ rb_define_method(cPipe, "closed?", Pipe_closed_p, 0);
107
+ rb_define_method(cPipe, "close", Pipe_close, 0);
108
+ rb_define_method(cPipe, "fds", Pipe_fds, 0);
109
+ }
@@ -36,6 +36,7 @@
36
36
  #define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
37
37
 
38
38
  extern VALUE mPolyphony;
39
+ extern VALUE cPipe;
39
40
  extern VALUE cQueue;
40
41
  extern VALUE cEvent;
41
42
  extern VALUE cTimeoutException;
@@ -87,6 +88,9 @@ long Runqueue_len(VALUE self);
87
88
  int Runqueue_empty_p(VALUE self);
88
89
  int Runqueue_should_poll_nonblocking(VALUE self);
89
90
 
91
+ void Pipe_verify_blocking_mode(VALUE self, VALUE blocking);
92
+ int Pipe_get_fd(VALUE self, int write_mode);
93
+
90
94
  #ifdef POLYPHONY_BACKEND_LIBEV
91
95
  #define Backend_recv_loop Backend_read_loop
92
96
  #define Backend_recv_feed_loop Backend_feed_loop
@@ -113,6 +117,7 @@ VALUE Backend_timer_loop(VALUE self, VALUE interval);
113
117
  VALUE Backend_wait_event(VALUE self, VALUE raise);
114
118
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
115
119
  VALUE Backend_waitpid(VALUE self, VALUE pid);
120
+ VALUE Backend_write(VALUE self, VALUE io, VALUE str);
116
121
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
117
122
  // VALUE Backend_close(VALUE self, VALUE io);
118
123
 
@@ -1,13 +1,16 @@
1
1
  #include "polyphony.h"
2
2
 
3
- void Init_Fiber();
4
3
  void Init_Polyphony();
5
4
  void Init_Backend();
5
+ void Init_Pipe();
6
6
  void Init_Queue();
7
7
  void Init_Event();
8
- void Init_SocketExtensions();
8
+ void Init_Fiber();
9
9
  void Init_Thread();
10
10
 
11
+ void Init_IOExtensions();
12
+ void Init_SocketExtensions();
13
+
11
14
  #ifdef POLYPHONY_PLAYGROUND
12
15
  extern void playground();
13
16
  #endif
@@ -17,10 +20,12 @@ void Init_polyphony_ext() {
17
20
 
18
21
  Init_Backend();
19
22
  Init_Queue();
23
+ Init_Pipe();
20
24
  Init_Event();
21
25
  Init_Fiber();
22
26
  Init_Thread();
23
27
 
28
+ Init_IOExtensions();
24
29
  Init_SocketExtensions();
25
30
 
26
31
  #ifdef POLYPHONY_PLAYGROUND