polyphony 0.93 → 0.94
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +8 -12
- data/examples/pipes/echo_server.rb +1 -1
- data/examples/pipes/http_server.rb +33 -0
- data/ext/polyphony/backend_common.c +50 -5
- data/ext/polyphony/backend_common.h +21 -13
- data/ext/polyphony/backend_io_uring.c +67 -131
- data/ext/polyphony/backend_libev.c +58 -89
- data/ext/polyphony/extconf.rb +2 -2
- data/ext/polyphony/io_extensions.c +67 -67
- data/ext/polyphony/polyphony.c +22 -22
- data/ext/polyphony/polyphony.h +0 -9
- data/lib/polyphony/version.rb +1 -1
- data/test/test_global_api.rb +1 -1
- metadata +8 -7
@@ -284,51 +284,25 @@ static inline int fd_from_io(VALUE io, rb_io_t **fptr, int write_mode, int recti
|
|
284
284
|
}
|
285
285
|
}
|
286
286
|
|
287
|
-
VALUE Backend_read(VALUE self, VALUE io, VALUE
|
287
|
+
VALUE Backend_read(VALUE self, VALUE io, VALUE buffer, VALUE length, VALUE to_eof, VALUE pos) {
|
288
288
|
Backend_t *backend;
|
289
289
|
struct libev_io watcher;
|
290
290
|
int fd;
|
291
291
|
rb_io_t *fptr;
|
292
292
|
|
293
|
-
struct
|
294
|
-
long buf_pos = FIX2INT(pos);
|
295
|
-
int shrinkable_string = 0;
|
296
|
-
int expandable_buffer = 0;
|
293
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 0);
|
297
294
|
long total = 0;
|
298
295
|
VALUE switchpoint_result = Qnil;
|
299
296
|
int read_to_eof = RTEST(to_eof);
|
300
297
|
|
301
|
-
if (buffer.raw) {
|
302
|
-
if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
|
303
|
-
buffer.ptr += buf_pos;
|
304
|
-
buffer.len -= buf_pos;
|
305
|
-
}
|
306
|
-
else {
|
307
|
-
expandable_buffer = length == Qnil;
|
308
|
-
long expected_read_length = expandable_buffer ? 4096 : FIX2INT(length);
|
309
|
-
long string_cap = rb_str_capacity(str);
|
310
|
-
if (buf_pos < 0 || buf_pos > buffer.len) buf_pos = buffer.len;
|
311
|
-
|
312
|
-
if (string_cap < expected_read_length + buf_pos) {
|
313
|
-
shrinkable_string = io_setstrbuf(&str, expected_read_length + buf_pos);
|
314
|
-
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + buf_pos;
|
315
|
-
buffer.len = expected_read_length;
|
316
|
-
}
|
317
|
-
else {
|
318
|
-
buffer.ptr += buf_pos;
|
319
|
-
buffer.len = string_cap - buf_pos;
|
320
|
-
if (buffer.len > expected_read_length)
|
321
|
-
buffer.len = expected_read_length;
|
322
|
-
}
|
323
|
-
}
|
324
|
-
|
325
298
|
GetBackend(self, backend);
|
299
|
+
backend_prepare_read_buffer(buffer, length, &buffer_spec, FIX2INT(pos));
|
326
300
|
fd = fd_from_io(io, &fptr, 0, 1);
|
327
301
|
watcher.fiber = Qnil;
|
328
302
|
|
329
303
|
while (1) {
|
330
304
|
backend->base.op_count++;
|
331
|
-
ssize_t result = read(fd,
|
305
|
+
ssize_t result = read(fd, buffer_spec.ptr, buffer_spec.len);
|
332
306
|
if (result < 0) {
|
333
307
|
int e = errno;
|
334
308
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
@@ -346,40 +320,34 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
346
320
|
total += result;
|
347
321
|
if (!read_to_eof) break;
|
348
322
|
|
349
|
-
if (result ==
|
350
|
-
if (
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
rb_str_modify_expand(str, rb_str_capacity(str));
|
355
|
-
shrinkable_string = 0;
|
356
|
-
buffer.ptr = (unsigned char *)RSTRING_PTR(str) + total + buf_pos;
|
357
|
-
buffer.len = rb_str_capacity(str) - total - buf_pos;
|
323
|
+
if (result == buffer_spec.len) {
|
324
|
+
if (buffer_spec.expandable)
|
325
|
+
backend_grow_string_buffer(buffer, &buffer_spec, total);
|
326
|
+
else
|
327
|
+
break;
|
358
328
|
}
|
359
329
|
else {
|
360
|
-
|
361
|
-
|
362
|
-
if (!
|
330
|
+
buffer_spec.ptr += result;
|
331
|
+
buffer_spec.len -= result;
|
332
|
+
if (!buffer_spec.len) break;
|
363
333
|
}
|
364
334
|
}
|
365
335
|
}
|
366
336
|
|
367
|
-
if (!buffer.raw) {
|
368
|
-
io_set_read_length(str, buf_pos + total, shrinkable_string);
|
369
|
-
if (fptr) io_enc_str(str, fptr);
|
370
|
-
}
|
371
337
|
if (!total) return Qnil;
|
338
|
+
|
339
|
+
if (!buffer_spec.raw) backend_finalize_string_buffer(buffer, &buffer_spec, total, fptr);
|
372
340
|
|
373
341
|
RB_GC_GUARD(watcher.fiber);
|
374
342
|
RB_GC_GUARD(switchpoint_result);
|
375
343
|
|
376
|
-
return
|
344
|
+
return buffer_spec.raw ? INT2FIX(total) : buffer;
|
377
345
|
error:
|
378
346
|
return RAISE_EXCEPTION(switchpoint_result);
|
379
347
|
}
|
380
348
|
|
381
|
-
VALUE Backend_recv(VALUE self, VALUE io, VALUE
|
382
|
-
return Backend_read(self, io,
|
349
|
+
VALUE Backend_recv(VALUE self, VALUE io, VALUE buffer, VALUE length, VALUE pos) {
|
350
|
+
return Backend_read(self, io, buffer, length, Qnil, pos);
|
383
351
|
}
|
384
352
|
|
385
353
|
VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
@@ -387,11 +355,11 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
387
355
|
struct libev_io watcher;
|
388
356
|
int fd;
|
389
357
|
rb_io_t *fptr;
|
390
|
-
VALUE
|
358
|
+
VALUE buffer;
|
391
359
|
long total;
|
360
|
+
char *ptr;
|
392
361
|
long len = FIX2INT(maxlen);
|
393
362
|
int shrinkable;
|
394
|
-
char *buf;
|
395
363
|
VALUE switchpoint_result = Qnil;
|
396
364
|
|
397
365
|
READ_LOOP_PREPARE_STR();
|
@@ -402,7 +370,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
402
370
|
|
403
371
|
while (1) {
|
404
372
|
backend->base.op_count++;
|
405
|
-
ssize_t n = read(fd,
|
373
|
+
ssize_t n = read(fd, ptr, len);
|
406
374
|
if (n < 0) {
|
407
375
|
int e = errno;
|
408
376
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
@@ -421,7 +389,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen) {
|
|
421
389
|
}
|
422
390
|
}
|
423
391
|
|
424
|
-
RB_GC_GUARD(
|
392
|
+
RB_GC_GUARD(buffer);
|
425
393
|
RB_GC_GUARD(watcher.fiber);
|
426
394
|
RB_GC_GUARD(switchpoint_result);
|
427
395
|
|
@@ -435,11 +403,11 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
435
403
|
struct libev_io watcher;
|
436
404
|
int fd;
|
437
405
|
rb_io_t *fptr;
|
438
|
-
VALUE
|
406
|
+
VALUE buffer;
|
439
407
|
long total;
|
408
|
+
char *ptr;
|
440
409
|
long len = 8192;
|
441
410
|
int shrinkable;
|
442
|
-
char *buf;
|
443
411
|
VALUE switchpoint_result = Qnil;
|
444
412
|
ID method_id = SYM2ID(method);
|
445
413
|
|
@@ -451,7 +419,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
451
419
|
|
452
420
|
while (1) {
|
453
421
|
backend->base.op_count++;
|
454
|
-
ssize_t n = read(fd,
|
422
|
+
ssize_t n = read(fd, ptr, len);
|
455
423
|
if (n < 0) {
|
456
424
|
int e = errno;
|
457
425
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
@@ -470,7 +438,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
470
438
|
}
|
471
439
|
}
|
472
440
|
|
473
|
-
RB_GC_GUARD(
|
441
|
+
RB_GC_GUARD(buffer);
|
474
442
|
RB_GC_GUARD(watcher.fiber);
|
475
443
|
RB_GC_GUARD(switchpoint_result);
|
476
444
|
|
@@ -479,15 +447,15 @@ error:
|
|
479
447
|
return RAISE_EXCEPTION(switchpoint_result);
|
480
448
|
}
|
481
449
|
|
482
|
-
VALUE Backend_write(VALUE self, VALUE io, VALUE
|
450
|
+
VALUE Backend_write(VALUE self, VALUE io, VALUE buffer) {
|
483
451
|
Backend_t *backend;
|
484
452
|
struct libev_io watcher;
|
485
453
|
int fd;
|
486
454
|
rb_io_t *fptr;
|
487
455
|
VALUE switchpoint_result = Qnil;
|
488
456
|
|
489
|
-
struct
|
490
|
-
long left =
|
457
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 1);
|
458
|
+
long left = buffer_spec.len;
|
491
459
|
|
492
460
|
GetBackend(self, backend);
|
493
461
|
fd = fd_from_io(io, &fptr, 1, 0);
|
@@ -495,7 +463,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
495
463
|
|
496
464
|
while (left > 0) {
|
497
465
|
backend->base.op_count++;
|
498
|
-
ssize_t result = write(fd,
|
466
|
+
ssize_t result = write(fd, buffer_spec.ptr, left);
|
499
467
|
if (result < 0) {
|
500
468
|
int e = errno;
|
501
469
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
@@ -505,7 +473,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
505
473
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
506
474
|
}
|
507
475
|
else {
|
508
|
-
|
476
|
+
buffer_spec.ptr += result;
|
509
477
|
left -= result;
|
510
478
|
}
|
511
479
|
}
|
@@ -519,7 +487,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
519
487
|
RB_GC_GUARD(watcher.fiber);
|
520
488
|
RB_GC_GUARD(switchpoint_result);
|
521
489
|
|
522
|
-
return INT2FIX(
|
490
|
+
return INT2FIX(buffer_spec.len);
|
523
491
|
error:
|
524
492
|
return RAISE_EXCEPTION(switchpoint_result);
|
525
493
|
}
|
@@ -542,9 +510,9 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
542
510
|
|
543
511
|
iov = malloc(iov_count * sizeof(struct iovec));
|
544
512
|
for (int i = 0; i < argc; i++) {
|
545
|
-
VALUE
|
546
|
-
iov[i].iov_base = StringValuePtr(
|
547
|
-
iov[i].iov_len = RSTRING_LEN(
|
513
|
+
VALUE buffer = argv[i];
|
514
|
+
iov[i].iov_base = StringValuePtr(buffer);
|
515
|
+
iov[i].iov_len = RSTRING_LEN(buffer);
|
548
516
|
total_length += iov[i].iov_len;
|
549
517
|
}
|
550
518
|
iov_ptr = iov;
|
@@ -754,15 +722,15 @@ error:
|
|
754
722
|
return RAISE_EXCEPTION(switchpoint_result);
|
755
723
|
}
|
756
724
|
|
757
|
-
VALUE Backend_send(VALUE self, VALUE io, VALUE
|
725
|
+
VALUE Backend_send(VALUE self, VALUE io, VALUE buffer, VALUE flags) {
|
758
726
|
Backend_t *backend;
|
759
727
|
struct libev_io watcher;
|
760
728
|
int fd;
|
761
729
|
rb_io_t *fptr;
|
762
730
|
VALUE switchpoint_result = Qnil;
|
763
731
|
|
764
|
-
struct
|
765
|
-
long left =
|
732
|
+
struct backend_buffer_spec buffer_spec = backend_get_buffer_spec(buffer, 1);
|
733
|
+
long left = buffer_spec.len;
|
766
734
|
int flags_int = FIX2INT(flags);
|
767
735
|
|
768
736
|
GetBackend(self, backend);
|
@@ -771,7 +739,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
771
739
|
|
772
740
|
while (left > 0) {
|
773
741
|
backend->base.op_count++;
|
774
|
-
ssize_t result = send(fd,
|
742
|
+
ssize_t result = send(fd, buffer_spec.ptr, left, flags_int);
|
775
743
|
if (result < 0) {
|
776
744
|
int e = errno;
|
777
745
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
@@ -781,7 +749,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
781
749
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
782
750
|
}
|
783
751
|
else {
|
784
|
-
|
752
|
+
buffer_spec.ptr += result;
|
785
753
|
left -= result;
|
786
754
|
}
|
787
755
|
}
|
@@ -795,7 +763,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
795
763
|
RB_GC_GUARD(watcher.fiber);
|
796
764
|
RB_GC_GUARD(switchpoint_result);
|
797
765
|
|
798
|
-
return INT2FIX(
|
766
|
+
return INT2FIX(buffer_spec.len);
|
799
767
|
error:
|
800
768
|
return RAISE_EXCEPTION(switchpoint_result);
|
801
769
|
}
|
@@ -952,11 +920,11 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
952
920
|
rb_io_t *dest_fptr;
|
953
921
|
int left = 0;
|
954
922
|
int total = 0;
|
923
|
+
char *ptr;
|
955
924
|
int maxlen_i = FIX2INT(maxlen);
|
956
925
|
int splice_to_eof = maxlen_i < 0;
|
957
926
|
if (splice_to_eof) maxlen_i = -maxlen_i;
|
958
|
-
VALUE
|
959
|
-
char *buf = RSTRING_PTR(str);
|
927
|
+
VALUE buffer = rb_str_new(0, maxlen_i);
|
960
928
|
|
961
929
|
GetBackend(self, backend);
|
962
930
|
src_fd = fd_from_io(src, &src_fptr, 0, 0);
|
@@ -967,7 +935,8 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
967
935
|
int done;
|
968
936
|
while (1) {
|
969
937
|
backend->base.op_count++;
|
970
|
-
|
938
|
+
ptr = RSTRING_PTR(buffer);
|
939
|
+
ssize_t n = read(src_fd, ptr, maxlen_i);
|
971
940
|
if (n < 0) {
|
972
941
|
int e = errno;
|
973
942
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
@@ -984,7 +953,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
984
953
|
|
985
954
|
while (left > 0) {
|
986
955
|
backend->base.op_count++;
|
987
|
-
ssize_t n = write(dest_fd,
|
956
|
+
ssize_t n = write(dest_fd, ptr, left);
|
988
957
|
if (n < 0) {
|
989
958
|
int e = errno;
|
990
959
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
@@ -994,7 +963,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
994
963
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
995
964
|
}
|
996
965
|
else {
|
997
|
-
|
966
|
+
ptr += n;
|
998
967
|
left -= n;
|
999
968
|
}
|
1000
969
|
}
|
@@ -1008,7 +977,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
1008
977
|
|
1009
978
|
RB_GC_GUARD(watcher.fiber);
|
1010
979
|
RB_GC_GUARD(switchpoint_result);
|
1011
|
-
RB_GC_GUARD(
|
980
|
+
RB_GC_GUARD(buffer);
|
1012
981
|
|
1013
982
|
return INT2FIX(total);
|
1014
983
|
error:
|
@@ -1290,13 +1259,13 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
|
|
1290
1259
|
return self;
|
1291
1260
|
}
|
1292
1261
|
|
1293
|
-
static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE
|
1294
|
-
char *
|
1295
|
-
int len = RSTRING_LEN(
|
1262
|
+
static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE buffer, struct libev_rw_io *watcher, VALUE *result) {
|
1263
|
+
char *ptr = RSTRING_PTR(buffer);
|
1264
|
+
int len = RSTRING_LEN(buffer);
|
1296
1265
|
int left = len;
|
1297
1266
|
while (left > 0) {
|
1298
1267
|
backend->base.op_count++;
|
1299
|
-
ssize_t n = write(fd,
|
1268
|
+
ssize_t n = write(fd, ptr, left);
|
1300
1269
|
if (n < 0) {
|
1301
1270
|
int err = errno;
|
1302
1271
|
if ((err != EWOULDBLOCK && err != EAGAIN)) return err;
|
@@ -1305,7 +1274,7 @@ static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, str
|
|
1305
1274
|
if (TEST_EXCEPTION(*result)) return -1;
|
1306
1275
|
}
|
1307
1276
|
else {
|
1308
|
-
|
1277
|
+
ptr += n;
|
1309
1278
|
left -= n;
|
1310
1279
|
}
|
1311
1280
|
}
|
@@ -1388,8 +1357,8 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1388
1357
|
struct libev_rw_io watcher;
|
1389
1358
|
watcher.ctx.fiber = Qnil;
|
1390
1359
|
int maxlen = FIX2INT(chunk_size);
|
1391
|
-
VALUE str = Qnil;
|
1392
1360
|
VALUE chunk_len_value = Qnil;
|
1361
|
+
VALUE buffer;
|
1393
1362
|
|
1394
1363
|
int pipefd[2] = { -1, -1 };
|
1395
1364
|
if (pipe(pipefd) == -1) {
|
@@ -1414,8 +1383,8 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1414
1383
|
chunk_len_value = INT2FIX(chunk_len);
|
1415
1384
|
|
1416
1385
|
if (chunk_prefix != Qnil) {
|
1417
|
-
|
1418
|
-
int err = splice_chunks_write(backend, dest_fd,
|
1386
|
+
buffer = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
|
1387
|
+
int err = splice_chunks_write(backend, dest_fd, buffer, &watcher, &result);
|
1419
1388
|
if (err == -1) goto error; else if (err) goto syscallerror;
|
1420
1389
|
}
|
1421
1390
|
|
@@ -1429,8 +1398,8 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1429
1398
|
}
|
1430
1399
|
|
1431
1400
|
if (chunk_postfix != Qnil) {
|
1432
|
-
|
1433
|
-
int err = splice_chunks_write(backend, dest_fd,
|
1401
|
+
buffer = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
|
1402
|
+
int err = splice_chunks_write(backend, dest_fd, buffer, &watcher, &result);
|
1434
1403
|
if (err == -1) goto error; else if (err) goto syscallerror;
|
1435
1404
|
}
|
1436
1405
|
}
|
@@ -1444,7 +1413,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1444
1413
|
result = backend_snooze(&backend->base);
|
1445
1414
|
if (TEST_EXCEPTION(result)) goto error;
|
1446
1415
|
}
|
1447
|
-
RB_GC_GUARD(
|
1416
|
+
RB_GC_GUARD(buffer);
|
1448
1417
|
RB_GC_GUARD(chunk_len_value);
|
1449
1418
|
RB_GC_GUARD(result);
|
1450
1419
|
if (pipefd[0] != -1) close(pipefd[0]);
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -5,7 +5,7 @@ require 'mkmf'
|
|
5
5
|
|
6
6
|
dir_config 'polyphony_ext'
|
7
7
|
|
8
|
-
KERNEL_INFO_RE = /Linux (\d)\.(\d+)
|
8
|
+
KERNEL_INFO_RE = /Linux (\d)\.(\d+)(?:\.)?((?:\d+\.?)*)(?:\-)?([\w\-]+)?/
|
9
9
|
def get_config
|
10
10
|
config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
|
11
11
|
return config if !config[:linux]
|
@@ -14,7 +14,7 @@ def get_config
|
|
14
14
|
m = kernel_info.match(KERNEL_INFO_RE)
|
15
15
|
raise "Could not parse Linux kernel information (#{kernel_info.inspect})" if !m
|
16
16
|
|
17
|
-
version, major_revision, distribution = m[1].to_i, m[2].to_i, m[
|
17
|
+
version, major_revision, distribution = m[1].to_i, m[2].to_i, m[4]
|
18
18
|
config[:pidfd_open] = (version == 5) && (major_revision >= 3)
|
19
19
|
|
20
20
|
force_libev = ENV['POLYPHONY_LIBEV'] != nil
|