polyphony 0.81.1 → 0.84
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 +4 -4
- data/.github/workflows/test.yml +1 -1
- data/.gitmodules +3 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +14 -0
- data/bin/test +1 -1
- data/ext/polyphony/backend_common.c +33 -10
- data/ext/polyphony/backend_common.h +20 -2
- data/ext/polyphony/backend_io_uring.c +182 -220
- data/ext/polyphony/backend_libev.c +212 -197
- data/ext/polyphony/extconf.rb +21 -2
- data/ext/polyphony/io_extensions.c +440 -0
- data/ext/polyphony/pipe.c +109 -0
- data/ext/polyphony/polyphony.c +50 -1
- data/ext/polyphony/polyphony.h +6 -11
- data/ext/polyphony/polyphony_ext.c +7 -2
- data/ext/polyphony/zlib_conf.rb +119 -0
- data/lib/polyphony/extensions/io.rb +20 -2
- data/lib/polyphony/extensions/pipe.rb +171 -0
- data/lib/polyphony/extensions.rb +1 -0
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +4 -0
- data/test/helper.rb +4 -0
- data/test/test_backend.rb +3 -48
- data/test/test_global_api.rb +23 -23
- data/test/test_io.rb +426 -0
- data/test/test_pipe.rb +41 -0
- data/test/test_process_supervision.rb +1 -1
- data/test/test_raw_buffer.rb +37 -0
- data/test/test_socket.rb +50 -0
- metadata +8 -13
- data/ext/liburing/liburing/README.md +0 -4
- data/ext/liburing/liburing/barrier.h +0 -73
- data/ext/liburing/liburing/compat.h +0 -15
- data/ext/liburing/liburing/io_uring.h +0 -343
- data/ext/liburing/liburing.h +0 -585
- data/ext/liburing/queue.c +0 -333
- data/ext/liburing/register.c +0 -187
- data/ext/liburing/setup.c +0 -210
- data/ext/liburing/syscall.c +0 -54
- data/ext/liburing/syscall.h +0 -18
- data/ext/polyphony/liburing.c +0 -8
data/ext/polyphony/extconf.rb
CHANGED
@@ -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
|
@@ -16,7 +17,7 @@ def get_config
|
|
16
17
|
version, major_revision, distribution = m[1].to_i, m[2].to_i, m[3]
|
17
18
|
config[:pidfd_open] = (version == 5) && (major_revision >= 3)
|
18
19
|
|
19
|
-
force_libev = ENV['
|
20
|
+
force_libev = ENV['POLYPHONY_LIBEV'] != nil
|
20
21
|
config[:io_uring] = !force_libev &&
|
21
22
|
(version == 5) && (major_revision >= 6) && (distribution != 'linuxkit')
|
22
23
|
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
|
-
|
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
|
+
}
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -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
|
}
|
@@ -118,9 +122,49 @@ VALUE Polyphony_backend_write(int argc, VALUE *argv, VALUE self) {
|
|
118
122
|
return Backend_write_m(argc, argv, BACKEND());
|
119
123
|
}
|
120
124
|
|
125
|
+
VALUE Polyphony_with_raw_buffer(VALUE self, VALUE size) {
|
126
|
+
struct raw_buffer buffer;
|
127
|
+
buffer.len = NUM2INT(size);
|
128
|
+
buffer.ptr = malloc(buffer.len);
|
129
|
+
if (!buffer.ptr)
|
130
|
+
rb_raise(rb_eRuntimeError, "Failed to allocate buffer");
|
131
|
+
|
132
|
+
VALUE return_value = rb_yield(PTR2FIX(&buffer));
|
133
|
+
free(buffer.ptr);
|
134
|
+
return return_value;
|
135
|
+
}
|
136
|
+
|
137
|
+
VALUE Polyphony_raw_buffer_get(int argc, VALUE *argv, VALUE self) {
|
138
|
+
VALUE buf = Qnil;
|
139
|
+
VALUE len = Qnil;
|
140
|
+
rb_scan_args(argc, argv, "11", &buf, &len);
|
141
|
+
|
142
|
+
struct raw_buffer *buffer = FIX2PTR(buf);
|
143
|
+
int length = (len == Qnil) ? buffer->len : FIX2INT(len);
|
144
|
+
|
145
|
+
if (length > buffer->len) length = buffer->len;
|
146
|
+
return rb_utf8_str_new(buffer->ptr, length);
|
147
|
+
}
|
148
|
+
|
149
|
+
VALUE Polyphony_raw_buffer_set(VALUE self, VALUE buf, VALUE str) {
|
150
|
+
struct raw_buffer *buffer = FIX2PTR(buf);
|
151
|
+
int len = RSTRING_LEN(str);
|
152
|
+
if (len > buffer->len)
|
153
|
+
rb_raise(rb_eRuntimeError, "Given string does not fit in given buffer");
|
154
|
+
|
155
|
+
memcpy(buffer->ptr, RSTRING_PTR(str), len);
|
156
|
+
buffer->len = len;
|
157
|
+
return self;
|
158
|
+
}
|
159
|
+
|
160
|
+
VALUE Polyphony_raw_buffer_size(VALUE self, VALUE buf) {
|
161
|
+
struct raw_buffer *buffer = FIX2PTR(buf);
|
162
|
+
return INT2FIX(buffer->len);
|
163
|
+
}
|
164
|
+
|
121
165
|
VALUE Polyphony_backend_test(VALUE self, VALUE io, VALUE str) {
|
122
166
|
struct raw_buffer buffer = { RSTRING_PTR(str), RSTRING_LEN(str) };
|
123
|
-
VALUE args[2] = { io,
|
167
|
+
VALUE args[2] = { io, PTR2FIX(&buffer) };
|
124
168
|
return Polyphony_backend_write(2, args, self);
|
125
169
|
}
|
126
170
|
|
@@ -146,6 +190,7 @@ void Init_Polyphony() {
|
|
146
190
|
rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
|
147
191
|
rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
|
148
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);
|
149
194
|
rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
|
150
195
|
rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
|
151
196
|
rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
|
@@ -155,6 +200,10 @@ void Init_Polyphony() {
|
|
155
200
|
// rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
|
156
201
|
rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode", Backend_verify_blocking_mode, 2);
|
157
202
|
|
203
|
+
rb_define_singleton_method(mPolyphony, "__with_raw_buffer__", Polyphony_with_raw_buffer, 1);
|
204
|
+
rb_define_singleton_method(mPolyphony, "__raw_buffer_get__", Polyphony_raw_buffer_get, -1);
|
205
|
+
rb_define_singleton_method(mPolyphony, "__raw_buffer_set__", Polyphony_raw_buffer_set, 2);
|
206
|
+
rb_define_singleton_method(mPolyphony, "__raw_buffer_size__", Polyphony_raw_buffer_size, 1);
|
158
207
|
rb_define_singleton_method(mPolyphony, "backend_test", Polyphony_backend_test, 2);
|
159
208
|
|
160
209
|
rb_define_global_function("snooze", Polyphony_snooze, 0);
|