polyphony 0.86 → 0.90
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test_io_uring.yml +8 -4
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/examples/io/http1_splice_chunked.rb +8 -0
- data/examples/io/static_web_server.rb +46 -0
- data/ext/polyphony/backend_common.c +1 -3
- data/ext/polyphony/backend_io_uring.c +46 -31
- data/ext/polyphony/backend_libev.c +5 -2
- data/ext/polyphony/extconf.rb +4 -2
- data/ext/polyphony/io_extensions.c +162 -54
- data/ext/polyphony/polyphony.c +7 -8
- data/ext/polyphony/polyphony.h +5 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/test_io.rb +204 -9
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9f125403a124d0374ebef23adfd4fe7f553767797003d63513ef780a248370b
|
4
|
+
data.tar.gz: 682134740af1614ac5e31d49c7d6a8ac8b881bbc26e5911e2f12a81f5ea52b27
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6292c9f2ddd6faf3d8df9565f8e6dce3be9ecd64fff95201bd2907cfacdb46eac5ef87a9281a3e88324471951493b6ebaa150dad40afb6a19958dd1e5992c0c
|
7
|
+
data.tar.gz: 10f8e607a0bd403664318e03abd4bbb4ab16704a69d1c0858ce5c9269c5c883e081dd95c51488dce7732ae471bf04cd139c0cbfb9623d6751a857551304e60f5
|
@@ -8,15 +8,19 @@ jobs:
|
|
8
8
|
fail-fast: false
|
9
9
|
matrix:
|
10
10
|
os: [ubuntu-latest]
|
11
|
-
ruby: [2.
|
11
|
+
ruby: ['2.7', '3.0', '3.1', 'head']
|
12
12
|
|
13
13
|
name: >-
|
14
14
|
${{matrix.os}}, ${{matrix.ruby}}
|
15
15
|
|
16
16
|
runs-on: ${{matrix.os}}
|
17
17
|
steps:
|
18
|
-
-
|
19
|
-
|
18
|
+
- name: Checkout repository and submodules
|
19
|
+
uses: actions/checkout@v2
|
20
|
+
with:
|
21
|
+
submodules: recursive
|
22
|
+
- name: Setup Ruby
|
23
|
+
uses: ruby/setup-ruby@v1
|
20
24
|
with:
|
21
25
|
ruby-version: ${{matrix.ruby}}
|
22
26
|
bundler-cache: true # 'bundle install' and cache
|
@@ -29,4 +33,4 @@ jobs:
|
|
29
33
|
- name: Compile C-extension
|
30
34
|
run: bundle exec rake compile
|
31
35
|
- name: Run tests
|
32
|
-
run: bundle exec
|
36
|
+
run: bundle exec ruby test/run.rb --verbose --name test_sleep
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## 0.90 2022-03-21
|
2
|
+
|
3
|
+
- Fix possible compilation error on Ruby 2.7.5 (#79)
|
4
|
+
|
5
|
+
## 0.89 2022-03-21
|
6
|
+
|
7
|
+
- Implement compression/decompression to/from strings (#86)
|
8
|
+
|
9
|
+
## 0.88 2022-03-18
|
10
|
+
|
11
|
+
- Improve IO stream compression utilities (release GVL, cleanup on exception)
|
12
|
+
|
13
|
+
## 0.87 2022-03-17
|
14
|
+
|
15
|
+
- Fix compilation on non-Linux OSes
|
16
|
+
|
1
17
|
## 0.86 2022-03-14
|
2
18
|
|
3
19
|
- Fix gemspec
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'polyphony'
|
6
|
+
require 'h1p'
|
7
|
+
|
8
|
+
server = Polyphony::Net.tcp_listen('localhost', 1234,
|
9
|
+
reuse_addr: true, reuse_port: true, dont_linger: true
|
10
|
+
)
|
11
|
+
puts 'Serving HTTP on port 1234'
|
12
|
+
|
13
|
+
def respond_default(conn)
|
14
|
+
conn << "HTTP/1.1 204\r\n\r\n"
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond_splice(conn, path)
|
18
|
+
f = File.open(path, 'r') do |f|
|
19
|
+
conn << "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\n\r\n"
|
20
|
+
IO.http1_splice_chunked(f, conn, 16384)
|
21
|
+
end
|
22
|
+
rescue => e
|
23
|
+
p e
|
24
|
+
# conn << "HTTP/1.1 500\r\nContent-Length: 0\r\n\r\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_client(conn)
|
28
|
+
parser = H1P::Parser.new(conn, :server)
|
29
|
+
while true
|
30
|
+
headers = parser.parse_headers
|
31
|
+
break unless headers
|
32
|
+
|
33
|
+
case headers[':path']
|
34
|
+
when /^\/splice\/(.+)$/
|
35
|
+
respond_splice(conn, $1)
|
36
|
+
else
|
37
|
+
respond_default(conn)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
rescue Errno::ECONNRESET
|
41
|
+
# ignore
|
42
|
+
end
|
43
|
+
|
44
|
+
server.accept_loop do |conn|
|
45
|
+
handle_client(conn)
|
46
|
+
end
|
@@ -363,8 +363,6 @@ inline void set_fd_blocking_mode(int fd, int blocking) {
|
|
363
363
|
}
|
364
364
|
|
365
365
|
inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
366
|
-
int flags;
|
367
|
-
int is_nonblocking;
|
368
366
|
VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
|
369
367
|
if (blocking == blocking_mode) return;
|
370
368
|
|
@@ -481,7 +479,7 @@ struct io_buffer get_io_buffer(VALUE in) {
|
|
481
479
|
struct raw_buffer *raw = FIX2PTR(in);
|
482
480
|
return (struct io_buffer){ raw->ptr, raw->len, 1 };
|
483
481
|
}
|
484
|
-
return (struct io_buffer){ RSTRING_PTR(in), RSTRING_LEN(in), 0 };
|
482
|
+
return (struct io_buffer){ (unsigned char *)RSTRING_PTR(in), RSTRING_LEN(in), 0 };
|
485
483
|
}
|
486
484
|
|
487
485
|
VALUE coerce_io_string_or_buffer(VALUE buf) {
|
@@ -262,6 +262,21 @@ inline struct backend_stats backend_get_stats(VALUE self) {
|
|
262
262
|
return backend_base_stats(&backend->base);
|
263
263
|
}
|
264
264
|
|
265
|
+
static inline struct io_uring_sqe *io_uring_backend_get_sqe(Backend_t *backend) {
|
266
|
+
struct io_uring_sqe *sqe;
|
267
|
+
sqe = io_uring_get_sqe(&backend->ring);
|
268
|
+
if (sqe) goto done;
|
269
|
+
|
270
|
+
if (backend->pending_sqes)
|
271
|
+
io_uring_backend_immediate_submit(backend);
|
272
|
+
else {
|
273
|
+
VALUE resume_value = backend_snooze(&backend->base);
|
274
|
+
RAISE_IF_EXCEPTION(resume_value);
|
275
|
+
}
|
276
|
+
done:
|
277
|
+
return sqe;
|
278
|
+
}
|
279
|
+
|
265
280
|
VALUE Backend_wakeup(VALUE self) {
|
266
281
|
Backend_t *backend;
|
267
282
|
GetBackend(self, backend);
|
@@ -269,7 +284,7 @@ VALUE Backend_wakeup(VALUE self) {
|
|
269
284
|
if (backend->base.currently_polling) {
|
270
285
|
// Since we're currently blocking while waiting for a completion, we add a
|
271
286
|
// NOP which would cause the io_uring_enter syscall to return
|
272
|
-
struct io_uring_sqe *sqe =
|
287
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
273
288
|
io_uring_prep_nop(sqe);
|
274
289
|
io_uring_backend_immediate_submit(backend);
|
275
290
|
|
@@ -302,7 +317,7 @@ int io_uring_backend_defer_submit_and_await(
|
|
302
317
|
|
303
318
|
// op was not completed (an exception was raised), so we need to cancel it
|
304
319
|
ctx->result = -ECANCELED;
|
305
|
-
sqe =
|
320
|
+
sqe = io_uring_backend_get_sqe(backend);
|
306
321
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
307
322
|
io_uring_backend_immediate_submit(backend);
|
308
323
|
}
|
@@ -317,7 +332,7 @@ VALUE io_uring_backend_wait_fd(Backend_t *backend, int fd, int write) {
|
|
317
332
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_POLL);
|
318
333
|
VALUE resumed_value = Qnil;
|
319
334
|
|
320
|
-
struct io_uring_sqe *sqe =
|
335
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
321
336
|
io_uring_prep_poll_add(sqe, fd, write ? POLLOUT : POLLIN);
|
322
337
|
|
323
338
|
io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resumed_value);
|
@@ -368,7 +383,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
368
383
|
|
369
384
|
if (string_cap < expected_read_length + buf_pos) {
|
370
385
|
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
371
|
-
buffer.ptr = RSTRING_PTR(str) + buf_pos;
|
386
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
372
387
|
buffer.len = expected_read_length;
|
373
388
|
}
|
374
389
|
else {
|
@@ -385,7 +400,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
385
400
|
while (1) {
|
386
401
|
VALUE resume_value = Qnil;
|
387
402
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
388
|
-
struct io_uring_sqe *sqe =
|
403
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
389
404
|
int result;
|
390
405
|
int completed;
|
391
406
|
|
@@ -415,7 +430,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
415
430
|
rb_str_resize(str, total + buf_pos);
|
416
431
|
rb_str_modify_expand(str, rb_str_capacity(str));
|
417
432
|
shrinkable_string = 0;
|
418
|
-
buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
|
433
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
|
419
434
|
buffer.len = rb_str_capacity(str) - total - buf_pos;
|
420
435
|
}
|
421
436
|
else {
|
@@ -453,7 +468,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
453
468
|
while (1) {
|
454
469
|
VALUE resume_value = Qnil;
|
455
470
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
456
|
-
struct io_uring_sqe *sqe =
|
471
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
457
472
|
ssize_t result;
|
458
473
|
int completed;
|
459
474
|
|
@@ -502,7 +517,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
502
517
|
while (1) {
|
503
518
|
VALUE resume_value = Qnil;
|
504
519
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_READ);
|
505
|
-
struct io_uring_sqe *sqe =
|
520
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
506
521
|
ssize_t result;
|
507
522
|
int completed;
|
508
523
|
|
@@ -546,7 +561,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
546
561
|
while (left > 0) {
|
547
562
|
VALUE resume_value = Qnil;
|
548
563
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITE);
|
549
|
-
struct io_uring_sqe *sqe =
|
564
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
550
565
|
int result;
|
551
566
|
int completed;
|
552
567
|
|
@@ -597,7 +612,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
597
612
|
while (1) {
|
598
613
|
VALUE resume_value = Qnil;
|
599
614
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_WRITEV);
|
600
|
-
struct io_uring_sqe *sqe =
|
615
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
601
616
|
int result;
|
602
617
|
int completed;
|
603
618
|
|
@@ -672,7 +687,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
|
672
687
|
|
673
688
|
if (string_cap < expected_read_length + buf_pos) {
|
674
689
|
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
675
|
-
buffer.ptr = RSTRING_PTR(str) + buf_pos;
|
690
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
676
691
|
buffer.len = expected_read_length;
|
677
692
|
}
|
678
693
|
else {
|
@@ -689,7 +704,7 @@ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos) {
|
|
689
704
|
while (1) {
|
690
705
|
VALUE resume_value = Qnil;
|
691
706
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
692
|
-
struct io_uring_sqe *sqe =
|
707
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
693
708
|
int result;
|
694
709
|
int completed;
|
695
710
|
|
@@ -739,7 +754,7 @@ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
739
754
|
while (1) {
|
740
755
|
VALUE resume_value = Qnil;
|
741
756
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
742
|
-
struct io_uring_sqe *sqe =
|
757
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
743
758
|
int result;
|
744
759
|
int completed;
|
745
760
|
|
@@ -787,7 +802,7 @@ VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method)
|
|
787
802
|
while (1) {
|
788
803
|
VALUE resume_value = Qnil;
|
789
804
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_RECV);
|
790
|
-
struct io_uring_sqe *sqe =
|
805
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
791
806
|
int result;
|
792
807
|
int completed;
|
793
808
|
|
@@ -831,7 +846,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
831
846
|
while (left > 0) {
|
832
847
|
VALUE resume_value = Qnil;
|
833
848
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_SEND);
|
834
|
-
struct io_uring_sqe *sqe =
|
849
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
835
850
|
int result;
|
836
851
|
int completed;
|
837
852
|
|
@@ -869,7 +884,7 @@ VALUE io_uring_backend_accept(Backend_t *backend, VALUE server_socket, VALUE soc
|
|
869
884
|
while (1) {
|
870
885
|
VALUE resume_value = Qnil;
|
871
886
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_ACCEPT);
|
872
|
-
struct io_uring_sqe *sqe =
|
887
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
873
888
|
int fd;
|
874
889
|
int completed;
|
875
890
|
|
@@ -935,7 +950,7 @@ VALUE io_uring_backend_splice(Backend_t *backend, VALUE src, VALUE dest, VALUE m
|
|
935
950
|
|
936
951
|
while (1) {
|
937
952
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
|
938
|
-
struct io_uring_sqe *sqe =
|
953
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
939
954
|
int result;
|
940
955
|
int completed;
|
941
956
|
|
@@ -984,7 +999,7 @@ VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
984
999
|
|
985
1000
|
while (1) {
|
986
1001
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_SPLICE);
|
987
|
-
struct io_uring_sqe *sqe =
|
1002
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
988
1003
|
int result;
|
989
1004
|
int completed;
|
990
1005
|
|
@@ -1021,7 +1036,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
1021
1036
|
GetBackend(self, backend);
|
1022
1037
|
fd = fd_from_io(sock, &fptr, 1, 0);
|
1023
1038
|
ctx = context_store_acquire(&backend->store, OP_CONNECT);
|
1024
|
-
sqe =
|
1039
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1025
1040
|
io_uring_prep_connect(sqe, fd, ai_addr, ai_addrlen);
|
1026
1041
|
result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
1027
1042
|
completed = context_store_release(&backend->store, ctx);
|
@@ -1063,7 +1078,7 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
1063
1078
|
// io_unset_nonblock(fptr, io);
|
1064
1079
|
|
1065
1080
|
// ctx = context_store_acquire(&backend->store, OP_CLOSE);
|
1066
|
-
// sqe =
|
1081
|
+
// sqe = io_uring_backend_get_sqe(backend);
|
1067
1082
|
// io_uring_prep_close(sqe, fd);
|
1068
1083
|
// result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
|
1069
1084
|
// completed = context_store_release(&backend->store, ctx);
|
@@ -1094,7 +1109,7 @@ inline struct __kernel_timespec duration_to_timespec(VALUE duration) {
|
|
1094
1109
|
// returns true if completed, 0 otherwise
|
1095
1110
|
int io_uring_backend_submit_timeout_and_await(Backend_t *backend, double duration, VALUE *resume_value) {
|
1096
1111
|
struct __kernel_timespec ts = double_to_timespec(duration);
|
1097
|
-
struct io_uring_sqe *sqe =
|
1112
|
+
struct io_uring_sqe *sqe = io_uring_backend_get_sqe(backend);
|
1098
1113
|
op_context_t *ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
1099
1114
|
|
1100
1115
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
@@ -1183,7 +1198,7 @@ VALUE Backend_timeout(int argc, VALUE *argv, VALUE self) {
|
|
1183
1198
|
GetBackend(self, backend);
|
1184
1199
|
timeout = rb_funcall(cTimeoutException, ID_new, 0);
|
1185
1200
|
|
1186
|
-
sqe =
|
1201
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1187
1202
|
ctx = context_store_acquire(&backend->store, OP_TIMEOUT);
|
1188
1203
|
ctx->resume_value = timeout;
|
1189
1204
|
io_uring_prep_timeout(sqe, &ts, 0, 0);
|
@@ -1259,7 +1274,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
1259
1274
|
struct io_uring_sqe *sqe;
|
1260
1275
|
|
1261
1276
|
backend->event_fd_ctx = context_store_acquire(&backend->store, OP_POLL);
|
1262
|
-
sqe =
|
1277
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1263
1278
|
io_uring_prep_poll_add(sqe, backend->event_fd, POLLIN);
|
1264
1279
|
backend->base.op_count++;
|
1265
1280
|
io_uring_sqe_set_data(sqe, backend->event_fd_ctx);
|
@@ -1275,7 +1290,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
1275
1290
|
|
1276
1291
|
// last fiber to use the eventfd, so we cancel the ongoing poll
|
1277
1292
|
struct io_uring_sqe *sqe;
|
1278
|
-
sqe =
|
1293
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1279
1294
|
io_uring_prep_cancel(sqe, (__u64)backend->event_fd_ctx, 0);
|
1280
1295
|
io_uring_backend_immediate_submit(backend);
|
1281
1296
|
backend->event_fd_ctx = NULL;
|
@@ -1296,7 +1311,7 @@ struct io_uring_sqe *Backend_chain_prepare_write(Backend_t *backend, VALUE io, V
|
|
1296
1311
|
struct io_uring_sqe *sqe;
|
1297
1312
|
|
1298
1313
|
fd = fd_from_io(io, &fptr, 1, 0);
|
1299
|
-
sqe =
|
1314
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1300
1315
|
io_uring_prep_write(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), 0);
|
1301
1316
|
return sqe;
|
1302
1317
|
}
|
@@ -1308,7 +1323,7 @@ struct io_uring_sqe *Backend_chain_prepare_send(Backend_t *backend, VALUE io, VA
|
|
1308
1323
|
|
1309
1324
|
fd = fd_from_io(io, &fptr, 1, 0);
|
1310
1325
|
|
1311
|
-
sqe =
|
1326
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1312
1327
|
io_uring_prep_send(sqe, fd, StringValuePtr(str), RSTRING_LEN(str), NUM2INT(flags));
|
1313
1328
|
return sqe;
|
1314
1329
|
}
|
@@ -1322,7 +1337,7 @@ struct io_uring_sqe *Backend_chain_prepare_splice(Backend_t *backend, VALUE src,
|
|
1322
1337
|
|
1323
1338
|
src_fd = fd_from_io(src, &src_fptr, 0, 0);
|
1324
1339
|
dest_fd = fd_from_io(dest, &dest_fptr, 1, 0);
|
1325
|
-
sqe =
|
1340
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1326
1341
|
io_uring_prep_splice(sqe, src_fd, -1, dest_fd, -1, NUM2INT(maxlen), 0);
|
1327
1342
|
return sqe;
|
1328
1343
|
}
|
@@ -1381,7 +1396,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1381
1396
|
|
1382
1397
|
ctx->ref_count = sqe_count;
|
1383
1398
|
ctx->result = -ECANCELED;
|
1384
|
-
sqe =
|
1399
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1385
1400
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
1386
1401
|
io_uring_backend_immediate_submit(backend);
|
1387
1402
|
}
|
@@ -1411,7 +1426,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1411
1426
|
|
1412
1427
|
// op was not completed (an exception was raised), so we need to cancel it
|
1413
1428
|
ctx->result = -ECANCELED;
|
1414
|
-
sqe =
|
1429
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1415
1430
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
1416
1431
|
io_uring_backend_immediate_submit(backend);
|
1417
1432
|
RAISE_IF_EXCEPTION(resume_value);
|
@@ -1470,14 +1485,14 @@ static inline void splice_chunks_get_sqe(
|
|
1470
1485
|
}
|
1471
1486
|
else
|
1472
1487
|
*ctx = context_store_acquire(&backend->store, type);
|
1473
|
-
(*sqe) =
|
1488
|
+
(*sqe) = io_uring_backend_get_sqe(backend);
|
1474
1489
|
}
|
1475
1490
|
|
1476
1491
|
static inline void splice_chunks_cancel(Backend_t *backend, op_context_t *ctx) {
|
1477
1492
|
struct io_uring_sqe *sqe;
|
1478
1493
|
|
1479
1494
|
ctx->result = -ECANCELED;
|
1480
|
-
sqe =
|
1495
|
+
sqe = io_uring_backend_get_sqe(backend);
|
1481
1496
|
io_uring_prep_cancel(sqe, (__u64)ctx, 0);
|
1482
1497
|
io_uring_backend_immediate_submit(backend);
|
1483
1498
|
}
|
@@ -311,7 +311,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
311
311
|
|
312
312
|
if (string_cap < expected_read_length + buf_pos) {
|
313
313
|
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
314
|
-
buffer.ptr = RSTRING_PTR(str) + buf_pos;
|
314
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
315
315
|
buffer.len = expected_read_length;
|
316
316
|
}
|
317
317
|
else {
|
@@ -353,7 +353,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
353
353
|
rb_str_resize(str, total + buf_pos);
|
354
354
|
rb_str_modify_expand(str, rb_str_capacity(str));
|
355
355
|
shrinkable_string = 0;
|
356
|
-
buffer.ptr = RSTRING_PTR(str) + total + buf_pos;
|
356
|
+
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
|
357
357
|
buffer.len = rb_str_capacity(str) - total - buf_pos;
|
358
358
|
}
|
359
359
|
else {
|
@@ -1645,6 +1645,9 @@ void Init_Backend() {
|
|
1645
1645
|
|
1646
1646
|
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1647
1647
|
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1648
|
+
#ifdef POLYPHONY_LINUX
|
1649
|
+
rb_define_method(cBackend, "tee", Backend_tee, 3);
|
1650
|
+
#endif
|
1648
1651
|
|
1649
1652
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1650
1653
|
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -47,6 +47,7 @@ end
|
|
47
47
|
$defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
|
48
48
|
if config[:io_uring]
|
49
49
|
$defs << "-DPOLYPHONY_BACKEND_LIBURING"
|
50
|
+
$defs << "-DPOLYPHONY_LINUX"
|
50
51
|
$defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
|
51
52
|
$CFLAGS << " -Wno-pointer-arith"
|
52
53
|
else
|
@@ -70,7 +71,8 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
|
|
70
71
|
|
71
72
|
CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
|
72
73
|
|
73
|
-
|
74
|
-
|
74
|
+
if RUBY_VERSION >= '3.1'
|
75
|
+
have_func('rb_fiber_transfer', 'ruby.h')
|
76
|
+
end
|
75
77
|
|
76
78
|
create_makefile 'polyphony_ext'
|
@@ -10,6 +10,7 @@
|
|
10
10
|
#include "polyphony.h"
|
11
11
|
#include "zlib.h"
|
12
12
|
#include "assert.h"
|
13
|
+
#include "ruby/thread.h"
|
13
14
|
|
14
15
|
ID ID_at;
|
15
16
|
ID ID_read_method;
|
@@ -29,6 +30,7 @@ VALUE SYM_orig_name;
|
|
29
30
|
VALUE SYM_readpartial;
|
30
31
|
|
31
32
|
enum read_method {
|
33
|
+
RM_STRING,
|
32
34
|
RM_BACKEND_READ,
|
33
35
|
RM_BACKEND_RECV,
|
34
36
|
RM_READPARTIAL,
|
@@ -36,13 +38,15 @@ enum read_method {
|
|
36
38
|
};
|
37
39
|
|
38
40
|
enum write_method {
|
41
|
+
WM_STRING,
|
39
42
|
WM_BACKEND_WRITE,
|
40
43
|
WM_BACKEND_SEND,
|
41
44
|
WM_WRITE,
|
42
45
|
WM_CALL
|
43
46
|
};
|
44
47
|
|
45
|
-
static enum read_method detect_read_method(VALUE io) {
|
48
|
+
static inline enum read_method detect_read_method(VALUE io) {
|
49
|
+
if (TYPE(io) == T_STRING) return RM_STRING;
|
46
50
|
if (rb_respond_to(io, ID_read_method)) {
|
47
51
|
VALUE method = rb_funcall(io, ID_read_method, 0);
|
48
52
|
if (method == SYM_readpartial) return RM_READPARTIAL;
|
@@ -58,7 +62,8 @@ static enum read_method detect_read_method(VALUE io) {
|
|
58
62
|
rb_raise(rb_eRuntimeError, "Given io instance should be a callable or respond to #__read_method__");
|
59
63
|
}
|
60
64
|
|
61
|
-
static enum write_method detect_write_method(VALUE io) {
|
65
|
+
static inline enum write_method detect_write_method(VALUE io) {
|
66
|
+
if (TYPE(io) == T_STRING) return WM_STRING;
|
62
67
|
if (rb_respond_to(io, ID_write_method)) {
|
63
68
|
VALUE method = rb_funcall(io, ID_write_method, 0);
|
64
69
|
if (method == SYM_readpartial) return WM_WRITE;
|
@@ -136,11 +141,18 @@ static inline int read_to_raw_buffer(VALUE backend, VALUE io, enum read_method m
|
|
136
141
|
RB_GC_GUARD(str);
|
137
142
|
return len;
|
138
143
|
}
|
144
|
+
default: {
|
145
|
+
rb_raise(rb_eRuntimeError, "Invalid read method");
|
146
|
+
}
|
139
147
|
}
|
140
148
|
}
|
141
149
|
|
142
150
|
static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_method method, struct raw_buffer *buffer) {
|
143
151
|
switch (method) {
|
152
|
+
case WM_STRING: {
|
153
|
+
rb_str_buf_cat(io, (char *)buffer->ptr, buffer->len);
|
154
|
+
return buffer->len;
|
155
|
+
}
|
144
156
|
case WM_BACKEND_WRITE: {
|
145
157
|
VALUE len = Backend_write(backend, io, PTR2FIX(buffer));
|
146
158
|
return FIX2INT(len);
|
@@ -165,6 +177,9 @@ static inline int write_from_raw_buffer(VALUE backend, VALUE io, enum write_meth
|
|
165
177
|
RB_GC_GUARD(str);
|
166
178
|
return buffer->len;
|
167
179
|
}
|
180
|
+
default: {
|
181
|
+
rb_raise(rb_eRuntimeError, "Invalid write method");
|
182
|
+
}
|
168
183
|
}
|
169
184
|
}
|
170
185
|
|
@@ -228,7 +243,7 @@ static inline time_t time_from_object(VALUE o) {
|
|
228
243
|
return FIX2INT(rb_funcall(o, rb_intern("to_i"), 0));
|
229
244
|
}
|
230
245
|
|
231
|
-
int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
|
246
|
+
int gzip_prepare_header(struct gzip_header_ctx *ctx, unsigned char *buffer, int maxlen) {
|
232
247
|
int len = 0;
|
233
248
|
unsigned char flags = 0, extraflags = 0;
|
234
249
|
|
@@ -259,7 +274,7 @@ int gzip_prepare_header(struct gzip_header_ctx *ctx, char *buffer, int maxlen) {
|
|
259
274
|
return len;
|
260
275
|
}
|
261
276
|
|
262
|
-
int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, char *buffer, int maxlen) {
|
277
|
+
static inline int gzip_prepare_footer(unsigned long crc32, unsigned long total_in, unsigned char *buffer, int maxlen) {
|
263
278
|
assert(maxlen >= GZIP_FOOTER_LEN);
|
264
279
|
|
265
280
|
gzfile_set32(crc32, buffer);
|
@@ -287,8 +302,8 @@ struct z_stream_ctx {
|
|
287
302
|
|
288
303
|
unsigned char in[CHUNK];
|
289
304
|
unsigned char out[CHUNK];
|
290
|
-
int in_pos;
|
291
|
-
int out_pos;
|
305
|
+
unsigned int in_pos;
|
306
|
+
unsigned int out_pos;
|
292
307
|
unsigned long in_total;
|
293
308
|
unsigned long out_total;
|
294
309
|
|
@@ -297,8 +312,8 @@ struct z_stream_ctx {
|
|
297
312
|
|
298
313
|
typedef int (*zlib_func)(z_streamp, int);
|
299
314
|
|
300
|
-
void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, unsigned long *total_read) {
|
301
|
-
|
315
|
+
void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, unsigned int *in_pos, unsigned long *total_read) {
|
316
|
+
unsigned long null_pos;
|
302
317
|
// find null terminator
|
303
318
|
for (null_pos = *in_pos; null_pos < *total_read; null_pos++) {
|
304
319
|
if (!buffer->ptr[null_pos]) break;
|
@@ -306,28 +321,37 @@ void read_gzip_header_str(struct raw_buffer *buffer, VALUE *str, int *in_pos, un
|
|
306
321
|
if (null_pos == *total_read)
|
307
322
|
rb_raise(rb_eRuntimeError, "Invalid gzip header");
|
308
323
|
|
309
|
-
*str = rb_str_new_cstr(buffer->ptr + *in_pos);
|
324
|
+
*str = rb_str_new_cstr((char *)buffer->ptr + *in_pos);
|
310
325
|
*in_pos = null_pos + 1;
|
311
326
|
}
|
312
327
|
|
313
328
|
void gzip_read_header(struct z_stream_ctx *ctx, struct gzip_header_ctx *header_ctx) {
|
314
|
-
struct raw_buffer in_buffer
|
329
|
+
struct raw_buffer in_buffer;
|
315
330
|
int flags;
|
316
331
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
ctx->in_total
|
332
|
+
if (ctx->src_read_method == RM_STRING) {
|
333
|
+
in_buffer.ptr = (unsigned char *)RSTRING_PTR(ctx->src);
|
334
|
+
in_buffer.len = RSTRING_LEN(ctx->src);
|
335
|
+
ctx->in_total = in_buffer.len;
|
321
336
|
}
|
337
|
+
else {
|
338
|
+
in_buffer.ptr = ctx->in;
|
339
|
+
in_buffer.len = CHUNK;
|
340
|
+
while (ctx->in_total < 10) {
|
341
|
+
int read = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
|
342
|
+
if (read == 0) goto error;
|
343
|
+
ctx->in_total += read;
|
344
|
+
}
|
345
|
+
}
|
346
|
+
|
322
347
|
// PRINT_BUFFER("read gzip header", ctx->in, ctx->in_total);
|
323
|
-
if (
|
324
|
-
if (
|
325
|
-
if (
|
326
|
-
flags =
|
348
|
+
if (in_buffer.ptr[0] != GZ_MAGIC1) goto error;
|
349
|
+
if (in_buffer.ptr[1] != GZ_MAGIC2) goto error;
|
350
|
+
if (in_buffer.ptr[2] != GZ_METHOD_DEFLATE) goto error;
|
351
|
+
flags = in_buffer.ptr[3];
|
327
352
|
|
328
|
-
unsigned long mtime = gzfile_get32(
|
353
|
+
unsigned long mtime = gzfile_get32(in_buffer.ptr + 4);
|
329
354
|
header_ctx->mtime = INT2FIX(mtime);
|
330
|
-
|
331
355
|
ctx->in_pos = 10;
|
332
356
|
|
333
357
|
if (flags & GZ_FLAG_ORIG_NAME)
|
@@ -344,6 +368,26 @@ error:
|
|
344
368
|
rb_raise(rb_eRuntimeError, "Invalid gzip header");
|
345
369
|
}
|
346
370
|
|
371
|
+
struct process_z_stream_ctx {
|
372
|
+
z_stream *strm;
|
373
|
+
int flags;
|
374
|
+
zlib_func fun;
|
375
|
+
int ret;
|
376
|
+
};
|
377
|
+
|
378
|
+
void *do_process_z_stream_without_gvl(void *ptr) {
|
379
|
+
struct process_z_stream_ctx *ctx = (struct process_z_stream_ctx *)ptr;
|
380
|
+
|
381
|
+
ctx->ret = (ctx->fun)(ctx->strm, ctx->flags);
|
382
|
+
return NULL;
|
383
|
+
}
|
384
|
+
|
385
|
+
static inline int process_without_gvl(zlib_func fun, z_stream *strm, int flags) {
|
386
|
+
struct process_z_stream_ctx ctx = { strm, flags, fun, 0 };
|
387
|
+
rb_thread_call_without_gvl2(do_process_z_stream_without_gvl, (void *)&ctx, RUBY_UBF_IO, 0);
|
388
|
+
return ctx.ret;
|
389
|
+
}
|
390
|
+
|
347
391
|
static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, int eof) {
|
348
392
|
int ret;
|
349
393
|
int written;
|
@@ -351,7 +395,7 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
|
|
351
395
|
|
352
396
|
int avail_out_pre = ctx->strm.avail_out = CHUNK - ctx->out_pos;
|
353
397
|
ctx->strm.next_out = ctx->out + ctx->out_pos;
|
354
|
-
ret = fun
|
398
|
+
ret = process_without_gvl(fun, &ctx->strm, eof ? Z_FINISH : Z_NO_FLUSH);
|
355
399
|
assert(ret != Z_STREAM_ERROR);
|
356
400
|
written = avail_out_pre - ctx->strm.avail_out;
|
357
401
|
out_buffer.ptr = ctx->out;
|
@@ -372,12 +416,13 @@ static inline int z_stream_write_out(struct z_stream_ctx *ctx, zlib_func fun, in
|
|
372
416
|
return ctx->strm.avail_out;
|
373
417
|
}
|
374
418
|
|
375
|
-
|
419
|
+
VALUE z_stream_io_loop(struct z_stream_ctx *ctx) {
|
376
420
|
zlib_func fun = (ctx->mode == SM_DEFLATE) ? deflate : inflate;
|
377
421
|
|
378
|
-
if (ctx->in_total > ctx->in_pos) {
|
422
|
+
if ((ctx->src_read_method != RM_STRING) && (ctx->in_total > ctx->in_pos)) {
|
379
423
|
// In bytes already read for parsing gzip header, so we need to process the
|
380
424
|
// rest.
|
425
|
+
|
381
426
|
ctx->strm.next_in = ctx->in + ctx->in_pos;
|
382
427
|
ctx->strm.avail_in = ctx->in_total -= ctx->in_pos;
|
383
428
|
|
@@ -389,14 +434,27 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
|
|
389
434
|
}
|
390
435
|
|
391
436
|
while (1) {
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
ctx->
|
437
|
+
int eof;
|
438
|
+
int read_len;
|
439
|
+
if (ctx->src_read_method == RM_STRING) {
|
440
|
+
struct raw_buffer in_buffer = {
|
441
|
+
(unsigned char *)RSTRING_PTR(ctx->src) + ctx->in_pos,
|
442
|
+
RSTRING_LEN(ctx->src) - ctx->in_pos
|
443
|
+
};
|
444
|
+
ctx->strm.next_in = in_buffer.ptr;
|
445
|
+
read_len = ctx->strm.avail_in = in_buffer.len;
|
446
|
+
eof = 1;
|
447
|
+
if (ctx->mode == SM_DEFLATE) ctx->crc32 = crc32(ctx->crc32, in_buffer.ptr, read_len);
|
448
|
+
}
|
449
|
+
else {
|
450
|
+
struct raw_buffer in_buffer = {ctx->in, CHUNK};
|
451
|
+
ctx->strm.next_in = ctx->in;
|
452
|
+
read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
|
453
|
+
if (!read_len) break;
|
454
|
+
eof = read_len < CHUNK;
|
455
|
+
if (ctx->mode == SM_DEFLATE) ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
|
456
|
+
}
|
457
|
+
|
400
458
|
ctx->in_total += read_len;
|
401
459
|
|
402
460
|
// PRINT_BUFFER("read stream", ctx->in, read_len);
|
@@ -407,13 +465,15 @@ void z_stream_io_loop(struct z_stream_ctx *ctx) {
|
|
407
465
|
if (z_stream_write_out(ctx, fun, eof)) break;
|
408
466
|
}
|
409
467
|
|
410
|
-
if (eof)
|
468
|
+
if (eof) goto done;
|
411
469
|
}
|
412
470
|
|
413
471
|
//flush
|
414
472
|
ctx->strm.avail_in = 0;
|
415
473
|
ctx->strm.next_in = ctx->in;
|
416
474
|
z_stream_write_out(ctx, fun, 1);
|
475
|
+
done:
|
476
|
+
return Qnil;
|
417
477
|
}
|
418
478
|
|
419
479
|
static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VALUE src, VALUE dest) {
|
@@ -434,6 +494,18 @@ static inline void setup_ctx(struct z_stream_ctx *ctx, enum stream_mode mode, VA
|
|
434
494
|
ctx->crc32 = 0;
|
435
495
|
}
|
436
496
|
|
497
|
+
static inline VALUE z_stream_cleanup(struct z_stream_ctx *ctx) {
|
498
|
+
if (ctx->mode == SM_DEFLATE)
|
499
|
+
deflateEnd(&ctx->strm);
|
500
|
+
else
|
501
|
+
inflateEnd(&ctx->strm);
|
502
|
+
return Qnil;
|
503
|
+
}
|
504
|
+
|
505
|
+
#define SAFE(f) (VALUE (*)(VALUE))(f)
|
506
|
+
#define Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx) \
|
507
|
+
rb_ensure(SAFE(z_stream_io_loop), (VALUE)&ctx, SAFE(z_stream_cleanup), (VALUE)&ctx)
|
508
|
+
|
437
509
|
VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
|
438
510
|
VALUE src;
|
439
511
|
VALUE dest;
|
@@ -450,18 +522,16 @@ VALUE IO_gzip(int argc, VALUE *argv, VALUE self) {
|
|
450
522
|
};
|
451
523
|
|
452
524
|
struct z_stream_ctx ctx;
|
453
|
-
int level = DEFAULT_LEVEL;
|
454
525
|
int ret;
|
455
526
|
|
456
527
|
setup_ctx(&ctx, SM_DEFLATE, src, dest);
|
457
528
|
ctx.f_gzip_footer = 1; // write gzip footer
|
458
|
-
ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
|
529
|
+
ctx.out_total = ctx.out_pos = gzip_prepare_header(&header_ctx, ctx.out, sizeof(ctx.out));
|
459
530
|
|
460
|
-
ret = deflateInit2(&ctx.strm,
|
461
|
-
if (ret != Z_OK)
|
462
|
-
|
463
|
-
|
464
|
-
|
531
|
+
ret = deflateInit2(&ctx.strm, DEFAULT_LEVEL, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
532
|
+
if (ret != Z_OK)
|
533
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
534
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
465
535
|
return INT2FIX(ctx.out_total);
|
466
536
|
}
|
467
537
|
|
@@ -482,21 +552,25 @@ VALUE IO_gunzip(int argc, VALUE *argv, VALUE self) {
|
|
482
552
|
setup_ctx(&ctx, SM_INFLATE, src, dest);
|
483
553
|
gzip_read_header(&ctx, &header_ctx);
|
484
554
|
|
555
|
+
ret = inflateInit2(&ctx.strm, -MAX_WBITS);
|
556
|
+
if (ret != Z_OK)
|
557
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
558
|
+
|
559
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
560
|
+
|
561
|
+
// gzip_read_footer(&ctx, &footer_ctx);
|
562
|
+
// TODO: verify crc32
|
563
|
+
// TODO: verify total length
|
564
|
+
|
485
565
|
if (info != Qnil) {
|
486
566
|
rb_hash_aset(info, SYM_mtime, FIX2TIME(header_ctx.mtime));
|
487
567
|
rb_hash_aset(info, SYM_orig_name, header_ctx.orig_name);
|
488
568
|
rb_hash_aset(info, SYM_comment, header_ctx.comment);
|
489
569
|
}
|
570
|
+
RB_GC_GUARD(header_ctx.orig_name);
|
571
|
+
RB_GC_GUARD(header_ctx.comment);
|
490
572
|
|
491
|
-
|
492
|
-
if (ret != Z_OK) return INT2FIX(ret);
|
493
|
-
z_stream_io_loop(&ctx);
|
494
|
-
inflateEnd(&ctx.strm);
|
495
|
-
|
496
|
-
// gzip_read_footer(&ctx, &footer_ctx);
|
497
|
-
// TODO: verify crc32
|
498
|
-
// TODO: verify total length
|
499
|
-
return self;
|
573
|
+
return INT2FIX(ctx.out_total);
|
500
574
|
}
|
501
575
|
|
502
576
|
VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
|
@@ -506,9 +580,10 @@ VALUE IO_deflate(VALUE self, VALUE src, VALUE dest) {
|
|
506
580
|
|
507
581
|
setup_ctx(&ctx, SM_DEFLATE, src, dest);
|
508
582
|
ret = deflateInit(&ctx.strm, level);
|
509
|
-
if (ret != Z_OK)
|
510
|
-
|
511
|
-
|
583
|
+
if (ret != Z_OK)
|
584
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
585
|
+
|
586
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
512
587
|
|
513
588
|
return INT2FIX(ctx.out_total);
|
514
589
|
}
|
@@ -519,19 +594,52 @@ VALUE IO_inflate(VALUE self, VALUE src, VALUE dest) {
|
|
519
594
|
|
520
595
|
setup_ctx(&ctx, SM_INFLATE, src, dest);
|
521
596
|
ret = inflateInit(&ctx.strm);
|
522
|
-
if (ret != Z_OK)
|
523
|
-
|
524
|
-
|
597
|
+
if (ret != Z_OK)
|
598
|
+
rb_raise(rb_eRuntimeError, "zlib error: %s\n", ctx.strm.msg);
|
599
|
+
|
600
|
+
Z_STREAM_SAFE_IO_LOOP_WITH_CLEANUP(ctx);
|
525
601
|
|
526
602
|
return INT2FIX(ctx.out_total);
|
527
603
|
}
|
528
604
|
|
605
|
+
VALUE IO_http1_splice_chunked(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
606
|
+
enum write_method method = detect_write_method(dest);
|
607
|
+
VALUE backend = BACKEND();
|
608
|
+
VALUE pipe = rb_funcall(cPipe, ID_new, 0);
|
609
|
+
unsigned char out[128];
|
610
|
+
struct raw_buffer buffer = { out, 0 };
|
611
|
+
|
612
|
+
while (1) {
|
613
|
+
int len = FIX2INT(Backend_splice(backend, src, pipe, maxlen));
|
614
|
+
if (!len) break;
|
615
|
+
|
616
|
+
// write chunk header
|
617
|
+
buffer.len += sprintf((char *)buffer.ptr + buffer.len, "%x\r\n", len);
|
618
|
+
write_from_raw_buffer(backend, dest, method, &buffer);
|
619
|
+
buffer.len = 0;
|
620
|
+
while (len) {
|
621
|
+
int spliced = FIX2INT(Backend_splice(backend, pipe, dest, INT2FIX(len)));
|
622
|
+
len -= spliced;
|
623
|
+
}
|
624
|
+
buffer.len += sprintf((char *)buffer.ptr + buffer.len, "\r\n");
|
625
|
+
}
|
626
|
+
buffer.len += sprintf((char *)buffer.ptr + buffer.len, "0\r\n\r\n");
|
627
|
+
write_from_raw_buffer(backend, dest, method, &buffer);
|
628
|
+
|
629
|
+
Pipe_close(pipe);
|
630
|
+
RB_GC_GUARD(pipe);
|
631
|
+
|
632
|
+
return self;
|
633
|
+
}
|
634
|
+
|
529
635
|
void Init_IOExtensions() {
|
530
636
|
rb_define_singleton_method(rb_cIO, "gzip", IO_gzip, -1);
|
531
637
|
rb_define_singleton_method(rb_cIO, "gunzip", IO_gunzip, -1);
|
532
638
|
rb_define_singleton_method(rb_cIO, "deflate", IO_deflate, 2);
|
533
639
|
rb_define_singleton_method(rb_cIO, "inflate", IO_inflate, 2);
|
534
640
|
|
641
|
+
rb_define_singleton_method(rb_cIO, "http1_splice_chunked", IO_http1_splice_chunked, 3);
|
642
|
+
|
535
643
|
ID_at = rb_intern("at");
|
536
644
|
ID_read_method = rb_intern("__read_method__");
|
537
645
|
ID_readpartial = rb_intern("readpartial");
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -94,9 +94,11 @@ 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
|
+
#ifdef POLYPHONY_LINUX
|
97
98
|
VALUE Polyphony_backend_tee(VALUE self, VALUE src, VALUE dest, VALUE chunksize) {
|
98
99
|
return Backend_tee(BACKEND(), src, dest, chunksize);
|
99
100
|
}
|
101
|
+
#endif
|
100
102
|
|
101
103
|
VALUE Polyphony_backend_timeout(int argc,VALUE *argv, VALUE self) {
|
102
104
|
return Backend_timeout(argc, argv, BACKEND());
|
@@ -143,7 +145,7 @@ VALUE Polyphony_raw_buffer_get(int argc, VALUE *argv, VALUE self) {
|
|
143
145
|
int length = (len == Qnil) ? buffer->len : FIX2INT(len);
|
144
146
|
|
145
147
|
if (length > buffer->len) length = buffer->len;
|
146
|
-
return rb_utf8_str_new(buffer->ptr, length);
|
148
|
+
return rb_utf8_str_new((char *)buffer->ptr, length);
|
147
149
|
}
|
148
150
|
|
149
151
|
VALUE Polyphony_raw_buffer_set(VALUE self, VALUE buf, VALUE str) {
|
@@ -162,12 +164,6 @@ VALUE Polyphony_raw_buffer_size(VALUE self, VALUE buf) {
|
|
162
164
|
return INT2FIX(buffer->len);
|
163
165
|
}
|
164
166
|
|
165
|
-
VALUE Polyphony_backend_test(VALUE self, VALUE io, VALUE str) {
|
166
|
-
struct raw_buffer buffer = { RSTRING_PTR(str), RSTRING_LEN(str) };
|
167
|
-
VALUE args[2] = { io, PTR2FIX(&buffer) };
|
168
|
-
return Polyphony_backend_write(2, args, self);
|
169
|
-
}
|
170
|
-
|
171
167
|
// VALUE Polyphony_backend_close(VALUE self, VALUE io) {
|
172
168
|
// return Backend_close(BACKEND(), io);
|
173
169
|
// }
|
@@ -190,7 +186,11 @@ void Init_Polyphony() {
|
|
190
186
|
rb_define_singleton_method(mPolyphony, "backend_sleep", Polyphony_backend_sleep, 1);
|
191
187
|
rb_define_singleton_method(mPolyphony, "backend_splice", Polyphony_backend_splice, 3);
|
192
188
|
rb_define_singleton_method(mPolyphony, "backend_splice_to_eof", Polyphony_backend_splice_to_eof, 3);
|
189
|
+
|
190
|
+
#ifdef POLYPHONY_LINUX
|
193
191
|
rb_define_singleton_method(mPolyphony, "backend_tee", Polyphony_backend_tee, 3);
|
192
|
+
#endif
|
193
|
+
|
194
194
|
rb_define_singleton_method(mPolyphony, "backend_timeout", Polyphony_backend_timeout, -1);
|
195
195
|
rb_define_singleton_method(mPolyphony, "backend_timer_loop", Polyphony_backend_timer_loop, 1);
|
196
196
|
rb_define_singleton_method(mPolyphony, "backend_wait_event", Polyphony_backend_wait_event, 1);
|
@@ -204,7 +204,6 @@ void Init_Polyphony() {
|
|
204
204
|
rb_define_singleton_method(mPolyphony, "__raw_buffer_get__", Polyphony_raw_buffer_get, -1);
|
205
205
|
rb_define_singleton_method(mPolyphony, "__raw_buffer_set__", Polyphony_raw_buffer_set, 2);
|
206
206
|
rb_define_singleton_method(mPolyphony, "__raw_buffer_size__", Polyphony_raw_buffer_size, 1);
|
207
|
-
rb_define_singleton_method(mPolyphony, "backend_test", Polyphony_backend_test, 2);
|
208
207
|
|
209
208
|
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
210
209
|
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -90,6 +90,7 @@ int Runqueue_should_poll_nonblocking(VALUE self);
|
|
90
90
|
|
91
91
|
void Pipe_verify_blocking_mode(VALUE self, VALUE blocking);
|
92
92
|
int Pipe_get_fd(VALUE self, int write_mode);
|
93
|
+
VALUE Pipe_close(VALUE self);
|
93
94
|
|
94
95
|
#ifdef POLYPHONY_BACKEND_LIBEV
|
95
96
|
#define Backend_recv_loop Backend_read_loop
|
@@ -112,7 +113,11 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
|
112
113
|
VALUE Backend_sleep(VALUE self, VALUE duration);
|
113
114
|
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
|
114
115
|
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE chunksize);
|
116
|
+
|
117
|
+
#ifdef POLYPHONY_LINUX
|
115
118
|
VALUE Backend_tee(VALUE self, VALUE src, VALUE dest, VALUE maxlen);
|
119
|
+
#endif
|
120
|
+
|
116
121
|
VALUE Backend_timeout(int argc,VALUE *argv, VALUE self);
|
117
122
|
VALUE Backend_timer_loop(VALUE self, VALUE interval);
|
118
123
|
VALUE Backend_wait_event(VALUE self, VALUE raise);
|
data/lib/polyphony/version.rb
CHANGED
data/test/test_io.rb
CHANGED
@@ -656,19 +656,75 @@ class IOExtensionsTest < MiniTest::Test
|
|
656
656
|
i, o = IO.pipe
|
657
657
|
r, w = IO.pipe
|
658
658
|
|
659
|
-
|
660
|
-
|
659
|
+
ret = nil
|
660
|
+
f = spin {
|
661
|
+
ret = IO.deflate(i, w)
|
661
662
|
w.close
|
662
663
|
}
|
663
664
|
|
664
665
|
o << 'foobar' * 20
|
665
666
|
o.close
|
666
667
|
|
668
|
+
f.await
|
669
|
+
assert_equal 17, ret
|
670
|
+
|
667
671
|
data = r.read
|
668
672
|
msg = Zlib::Inflate.inflate(data)
|
669
673
|
assert_equal 'foobar' * 20, msg
|
670
674
|
end
|
671
675
|
|
676
|
+
def test_deflate_to_string
|
677
|
+
i, o = IO.pipe
|
678
|
+
r, w = IO.pipe
|
679
|
+
str = +''
|
680
|
+
|
681
|
+
ret = nil
|
682
|
+
f = spin {
|
683
|
+
ret = IO.deflate(i, str)
|
684
|
+
w << str
|
685
|
+
w.close
|
686
|
+
}
|
687
|
+
|
688
|
+
o << 'foobar' * 20
|
689
|
+
o.close
|
690
|
+
|
691
|
+
f.await
|
692
|
+
assert_equal 17, ret
|
693
|
+
|
694
|
+
data = r.read
|
695
|
+
msg = Zlib::Inflate.inflate(data)
|
696
|
+
assert_equal 'foobar' * 20, msg
|
697
|
+
end
|
698
|
+
|
699
|
+
def test_deflate_to_frozen_string
|
700
|
+
i, o = IO.pipe
|
701
|
+
str = '' # frozen
|
702
|
+
|
703
|
+
f = spin {
|
704
|
+
o << 'foobar' * 20
|
705
|
+
o.close
|
706
|
+
}
|
707
|
+
|
708
|
+
assert_raises(FrozenError) { IO.deflate(i, str) }
|
709
|
+
end
|
710
|
+
|
711
|
+
def test_deflate_from_string
|
712
|
+
r, w = IO.pipe
|
713
|
+
str = 'foobar' * 10000
|
714
|
+
ret = nil
|
715
|
+
|
716
|
+
f = spin {
|
717
|
+
ret = IO.deflate(str, w)
|
718
|
+
w.close
|
719
|
+
}
|
720
|
+
f.await
|
721
|
+
assert_equal 118, ret
|
722
|
+
|
723
|
+
data = r.read
|
724
|
+
msg = Zlib::Inflate.inflate(data)
|
725
|
+
assert_equal str, msg
|
726
|
+
end
|
727
|
+
|
672
728
|
def test_inflate
|
673
729
|
i, o = IO.pipe
|
674
730
|
r, w = IO.pipe
|
@@ -679,7 +735,35 @@ class IOExtensionsTest < MiniTest::Test
|
|
679
735
|
o.close
|
680
736
|
}
|
681
737
|
|
682
|
-
IO.inflate(i, w)
|
738
|
+
ret = IO.inflate(i, w)
|
739
|
+
assert_equal 6, ret
|
740
|
+
w.close
|
741
|
+
msg = r.read
|
742
|
+
assert_equal 'foobar', msg
|
743
|
+
end
|
744
|
+
|
745
|
+
def test_inflate_to_string
|
746
|
+
i, o = IO.pipe
|
747
|
+
str = +''
|
748
|
+
|
749
|
+
spin {
|
750
|
+
data = Zlib::Deflate.deflate('foobar', 9)
|
751
|
+
o << data
|
752
|
+
o.close
|
753
|
+
}
|
754
|
+
|
755
|
+
ret = IO.inflate(i, str)
|
756
|
+
assert_equal 6, ret
|
757
|
+
assert_equal 6, str.bytesize
|
758
|
+
assert_equal 'foobar', str
|
759
|
+
end
|
760
|
+
|
761
|
+
def test_inflate_from_string
|
762
|
+
r, w = IO.pipe
|
763
|
+
str = Zlib::Deflate.deflate('foobar', 9)
|
764
|
+
|
765
|
+
ret = IO.inflate(str, w)
|
766
|
+
assert_equal 6, ret
|
683
767
|
w.close
|
684
768
|
msg = r.read
|
685
769
|
assert_equal 'foobar', msg
|
@@ -690,7 +774,7 @@ class IOExtensionsTest < MiniTest::Test
|
|
690
774
|
dest = Polyphony.pipe
|
691
775
|
now = nil
|
692
776
|
|
693
|
-
spin {
|
777
|
+
f = spin {
|
694
778
|
now = Time.now
|
695
779
|
IO.gzip(src, dest)
|
696
780
|
dest.close
|
@@ -698,6 +782,32 @@ class IOExtensionsTest < MiniTest::Test
|
|
698
782
|
|
699
783
|
src << IO.read(__FILE__)
|
700
784
|
src.close
|
785
|
+
f.await
|
786
|
+
|
787
|
+
gz = Zlib::GzipReader.new(dest)
|
788
|
+
data = gz.read
|
789
|
+
assert_equal IO.read(__FILE__), data
|
790
|
+
assert_in_range (now-2)..(now+1), gz.mtime
|
791
|
+
assert_nil gz.orig_name
|
792
|
+
assert_nil gz.comment
|
793
|
+
end
|
794
|
+
|
795
|
+
def test_gzip_to_string
|
796
|
+
src = Polyphony.pipe
|
797
|
+
dest = Polyphony.pipe
|
798
|
+
str = +''
|
799
|
+
now = nil
|
800
|
+
|
801
|
+
f = spin {
|
802
|
+
now = Time.now
|
803
|
+
IO.gzip(src, str)
|
804
|
+
dest << str
|
805
|
+
dest.close
|
806
|
+
}
|
807
|
+
|
808
|
+
src << IO.read(__FILE__)
|
809
|
+
src.close
|
810
|
+
f.await
|
701
811
|
|
702
812
|
gz = Zlib::GzipReader.new(dest)
|
703
813
|
data = gz.read
|
@@ -707,6 +817,39 @@ class IOExtensionsTest < MiniTest::Test
|
|
707
817
|
assert_nil gz.comment
|
708
818
|
end
|
709
819
|
|
820
|
+
def test_gzip_from_string
|
821
|
+
str = IO.read(__FILE__)
|
822
|
+
dest = Polyphony.pipe
|
823
|
+
now = nil
|
824
|
+
|
825
|
+
IO.gzip(str, dest)
|
826
|
+
dest.close
|
827
|
+
|
828
|
+
gz = Zlib::GzipReader.new(dest)
|
829
|
+
data = gz.read
|
830
|
+
assert_equal IO.read(__FILE__), data
|
831
|
+
end
|
832
|
+
|
833
|
+
def test_gzip_return_value
|
834
|
+
src = Polyphony.pipe
|
835
|
+
dest = Polyphony.pipe
|
836
|
+
now = nil
|
837
|
+
ret = nil
|
838
|
+
|
839
|
+
f = spin {
|
840
|
+
now = Time.now
|
841
|
+
ret = IO.gzip(src, dest)
|
842
|
+
dest.close
|
843
|
+
}
|
844
|
+
|
845
|
+
src << IO.read(__FILE__)
|
846
|
+
src.close
|
847
|
+
f.await
|
848
|
+
|
849
|
+
gzipped = dest.read
|
850
|
+
assert_equal gzipped.bytesize, ret
|
851
|
+
end
|
852
|
+
|
710
853
|
def test_gzip_with_mtime_int
|
711
854
|
src = Polyphony.pipe
|
712
855
|
dest = Polyphony.pipe
|
@@ -801,19 +944,51 @@ class IOExtensionsTest < MiniTest::Test
|
|
801
944
|
def test_gunzip
|
802
945
|
src = Polyphony.pipe
|
803
946
|
dest = Polyphony.pipe
|
947
|
+
ret = nil
|
804
948
|
|
805
|
-
spin {
|
806
|
-
IO.gunzip(src, dest)
|
949
|
+
f = spin {
|
950
|
+
ret = IO.gunzip(src, dest)
|
807
951
|
dest.close
|
808
952
|
}
|
809
953
|
|
810
954
|
gz = Zlib::GzipWriter.new(src, 9)
|
811
|
-
gz <<
|
955
|
+
gz << IO.read(__FILE__)
|
812
956
|
gz.close
|
957
|
+
f.await
|
813
958
|
|
814
959
|
data = dest.read
|
815
|
-
|
816
|
-
assert_equal
|
960
|
+
assert_equal IO.read(__FILE__).bytesize, ret
|
961
|
+
assert_equal IO.read(__FILE__), data
|
962
|
+
end
|
963
|
+
|
964
|
+
def test_gunzip_to_string
|
965
|
+
src = Polyphony.pipe
|
966
|
+
str = +''
|
967
|
+
ret = nil
|
968
|
+
|
969
|
+
f = spin {
|
970
|
+
ret = IO.gunzip(src, str)
|
971
|
+
}
|
972
|
+
|
973
|
+
gz = Zlib::GzipWriter.new(src, 9)
|
974
|
+
gz << IO.read(__FILE__)
|
975
|
+
gz.close
|
976
|
+
f.await
|
977
|
+
|
978
|
+
assert_equal IO.read(__FILE__).bytesize, ret
|
979
|
+
assert_equal IO.read(__FILE__), str
|
980
|
+
end
|
981
|
+
|
982
|
+
def test_gunzip_from_string
|
983
|
+
src_data = 'foobar' * 1000
|
984
|
+
str = Zlib.gzip(src_data, level: 9)
|
985
|
+
dest = Polyphony.pipe
|
986
|
+
ret = IO.gunzip(str, dest)
|
987
|
+
dest.close
|
988
|
+
|
989
|
+
dest_data = dest.read
|
990
|
+
assert_equal src_data.bytesize, ret
|
991
|
+
assert_equal src_data, dest_data
|
817
992
|
end
|
818
993
|
|
819
994
|
def test_gunzip_multi
|
@@ -893,4 +1068,24 @@ class IOExtensionsTest < MiniTest::Test
|
|
893
1068
|
assert_equal 'foo.bar', dest_info[:orig_name]
|
894
1069
|
assert_equal 'hello!', dest_info[:comment]
|
895
1070
|
end
|
1071
|
+
|
1072
|
+
def test_deflate_inflate_strings
|
1073
|
+
src_data = IO.read(__FILE__)
|
1074
|
+
deflated = +''
|
1075
|
+
IO.deflate(src_data, deflated)
|
1076
|
+
inflated = +''
|
1077
|
+
IO.inflate(deflated, inflated)
|
1078
|
+
|
1079
|
+
assert_equal src_data, inflated
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
def test_gzip_gunzip_strings
|
1083
|
+
src_data = IO.read(__FILE__)
|
1084
|
+
gzipped = +''
|
1085
|
+
IO.gzip(src_data, gzipped)
|
1086
|
+
gunzipped = +''
|
1087
|
+
IO.gunzip(gzipped, gunzipped)
|
1088
|
+
|
1089
|
+
assert_equal src_data, gunzipped
|
1090
|
+
end
|
896
1091
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.90'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-03-
|
11
|
+
date: 2022-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -262,6 +262,7 @@ files:
|
|
262
262
|
- examples/io/echo_stdin.rb
|
263
263
|
- examples/io/gzip.rb
|
264
264
|
- examples/io/happy-eyeballs.rb
|
265
|
+
- examples/io/http1_splice_chunked.rb
|
265
266
|
- examples/io/httparty.rb
|
266
267
|
- examples/io/https_server.rb
|
267
268
|
- examples/io/irb.rb
|
@@ -274,6 +275,7 @@ files:
|
|
274
275
|
- examples/io/reline.rb
|
275
276
|
- examples/io/splice_chunks.rb
|
276
277
|
- examples/io/splice_echo_server.rb
|
278
|
+
- examples/io/static_web_server.rb
|
277
279
|
- examples/io/stdio.rb
|
278
280
|
- examples/io/system.rb
|
279
281
|
- examples/io/tcp_proxy.rb
|