polyphony 0.86 → 0.90

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b87dae4e2c17055e8206c820a4281774cdf3127195173948c97df4eb09a2bd13
4
- data.tar.gz: 2e7bd958ec7435569423398069223eda41e9c2d89f580627bc22e9054276dd1c
3
+ metadata.gz: d9f125403a124d0374ebef23adfd4fe7f553767797003d63513ef780a248370b
4
+ data.tar.gz: 682134740af1614ac5e31d49c7d6a8ac8b881bbc26e5911e2f12a81f5ea52b27
5
5
  SHA512:
6
- metadata.gz: 48f6489939da965463735c83acb4372263eb44b48b5a16da1e704a15e3af971a5d43abd7da5e44f2455b40d880413c6fa8159794e663176c83a11eddebdebc7d
7
- data.tar.gz: 3ada5f9041041e91b6b2a7f31ec609352752317e87c51df090517c9e2b556d9ef69ef8842babd15f25b2257c4226431520ff15df505748cb38bb4a1d5a19063c
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.6, 2.7, 3.0]
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
- - uses: actions/checkout@v1
19
- - uses: ruby/setup-ruby@v1
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 rake test
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.86)
4
+ polyphony (0.90)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ require 'polyphony'
7
+
8
+ IO.http1_splice_chunked(STDIN, STDOUT, 16384)
@@ -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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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) = io_uring_get_sqe(&backend->ring);
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 = io_uring_get_sqe(&backend->ring);
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);
@@ -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
- have_func('rb_fiber_transfer', 'ruby.h')
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
- int null_pos;
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 = { ctx->in, CHUNK };
329
+ struct raw_buffer in_buffer;
315
330
  int flags;
316
331
 
317
- while (ctx->in_total < 10) {
318
- int read = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
319
- if (read == 0) goto error;
320
- ctx->in_total += read;
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 (ctx->in[0] != GZ_MAGIC1) goto error;
324
- if (ctx->in[1] != GZ_MAGIC2) goto error;
325
- if (ctx->in[2] != GZ_METHOD_DEFLATE) goto error;
326
- flags = ctx->in[3];
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(ctx->in + 4);
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(&ctx->strm, eof ? Z_FINISH : Z_NO_FLUSH);
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
- void z_stream_io_loop(struct z_stream_ctx *ctx) {
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
- struct raw_buffer in_buffer = {ctx->in, CHUNK};
393
- ctx->strm.next_in = ctx->in;
394
- int read_len = ctx->strm.avail_in = read_to_raw_buffer(ctx->backend, ctx->src, ctx->src_read_method, &in_buffer);
395
- if (!read_len) break;
396
- int eof = read_len < CHUNK;
397
-
398
- if (ctx->mode == SM_DEFLATE)
399
- ctx->crc32 = crc32(ctx->crc32, ctx->in, read_len);
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) return;
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, level, Z_DEFLATED, -MAX_WBITS, DEFAULT_MEM_LEVEL, Z_DEFAULT_STRATEGY);
461
- if (ret != Z_OK) return INT2FIX(ret);
462
- z_stream_io_loop(&ctx);
463
- deflateEnd(&ctx.strm);
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
- ret = inflateInit2(&ctx.strm, -MAX_WBITS);
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) return INT2FIX(ret);
510
- z_stream_io_loop(&ctx);
511
- deflateEnd(&ctx.strm);
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) return INT2FIX(ret);
523
- z_stream_io_loop(&ctx);
524
- inflateEnd(&ctx.strm);
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");
@@ -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);
@@ -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);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.86'
4
+ VERSION = '0.90'
5
5
  end
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
- spin {
660
- IO.deflate(i, w)
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 << 'foobar'#IO.read(__FILE__)
955
+ gz << IO.read(__FILE__)
812
956
  gz.close
957
+ f.await
813
958
 
814
959
  data = dest.read
815
- # assert_equal IO.read(__FILE__), data
816
- assert_equal 'foobar', data
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.86'
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-14 00:00:00.000000000 Z
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