polyphony 0.82 → 0.84.1

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.
@@ -3,6 +3,7 @@
3
3
  require 'rubygems'
4
4
  require 'mkmf'
5
5
 
6
+ dir_config 'polyphony_ext'
6
7
 
7
8
  KERNEL_INFO_RE = /Linux (\d)\.(\d+)\.(?:\d+)\-(?:\d+\-)?(\w+)/
8
9
  def get_config
@@ -25,6 +26,24 @@ end
25
26
  config = get_config
26
27
  puts "Building Polyphony... (#{config.inspect})"
27
28
 
29
+ require_relative 'zlib_conf'
30
+
31
+ if config[:io_uring]
32
+ liburing_path = File.expand_path('../../vendor/liburing', __dir__)
33
+ FileUtils.cd liburing_path do
34
+ system('./configure', exception: true)
35
+ FileUtils.cd File.join(liburing_path, 'src') do
36
+ system('make', 'liburing.a', exception: true)
37
+ end
38
+ end
39
+
40
+ if !find_header 'liburing.h', File.expand_path('../../vendor/liburing/src/include', __dir__)
41
+ raise "Couldn't find liburing.h"
42
+ end
43
+
44
+ $LDFLAGS << " -L#{File.expand_path('../../vendor/liburing/src', __dir__)} -l uring"
45
+ end
46
+
28
47
  $defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
29
48
  if config[:io_uring]
30
49
  $defs << "-DPOLYPHONY_BACKEND_LIBURING"
@@ -53,5 +72,5 @@ CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
53
72
 
54
73
  have_func('rb_fiber_transfer', 'ruby.h')
55
74
 
56
- dir_config 'polyphony_ext'
75
+
57
76
  create_makefile 'polyphony_ext'
@@ -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
+ }
@@ -94,6 +94,10 @@ VALUE Polyphony_backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE c
94
94
  return Backend_splice_to_eof(BACKEND(), src, dest, chunksize);
95
95
  }
96
96
 
97
+ VALUE Polyphony_backend_tee(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
98
+ return Backend_tee(BACKEND(), src, dest, chunksize);
99
+ }
100
+
97
101
  VALUE Polyphony_backend_timeout(int argc,VALUE *argv, VALUE self) {
98
102
  return Backend_timeout(argc, argv, BACKEND());
99
103
  }
@@ -186,6 +190,7 @@ void Init_Polyphony() {
186
190
  rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
187
191
  rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
188
192
  rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
193
+ rb_define_singleton_method(mPolyphony, "backend_tee", Polyphony_backend_tee, 3);
189
194
  rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
190
195
  rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
191
196
  rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
@@ -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
@@ -108,11 +112,13 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
108
112
  VALUE Backend_sleep(VALUE self, VALUE duration);
109
113
  VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
110
114
  VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
115
+ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
111
116
  VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
112
117
  VALUE Backend_timer_loop(VALUE self, VALUE interval);
113
118
  VALUE Backend_wait_event(VALUE self, VALUE raise);
114
119
  VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
115
120
  VALUE Backend_waitpid(VALUE self, VALUE pid);
121
+ VALUE Backend_write(VALUE self, VALUE io, VALUE str);
116
122
  VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
117
123
  // VALUE Backend_close(VALUE self, VALUE io);
118
124
 
@@ -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