Class: IO
- Inherits:
-
Object
- Object
- IO
- Defined in:
- lib/io-extra.rb
Overview
Reopen the core IO class to define pread and pwrite singleton methods for backwards compatibility.
Constant Summary collapse
- EXTRA_VERSION =
'1.5.0'.freeze
- DIRECTIO_OFF =
Applications get the default system behavior when accessing file data.
0- DIRECTIO_ON =
File data is not cached in the system’s memory pages.
1- IOV_MAX =
LONG2NUM(IOV_MAX)
Class Method Summary collapse
-
.closefrom(lowfd) ⇒ Object
Close all open file descriptors (associated with the current process) that are greater than or equal to
lowfd. -
.fdwalk(lowfd) {|fh| ... } ⇒ Object
Iterates over each open file descriptor and yields back a File object.
-
.pread(fd, maxlen, offset) ⇒ Object
Singleton version of the IO#pread method.
-
.pwrite(fd, string, offset) ⇒ Object
Singleton version of the IO#pwrite method.
-
.writev(fd, ary) ⇒ Object
IO.writev(fd, %w(hello world)).
Instance Method Summary collapse
-
#directio=(advice) ⇒ Object
Sets the advice for the current file descriptor using directio().
-
#directio? ⇒ Boolean
Returns true or false, based on whether directio has been set for the current handle.
-
#ttyname ⇒ Object
io.ttyname.
Class Method Details
.closefrom(lowfd) ⇒ Object
Close all open file descriptors (associated with the current process) that are greater than or equal to lowfd.
This method uses your system’s builtin closefrom() function, if supported. Otherwise, it uses a manual, and (probably) less efficient approach.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'ext/io/extra.c', line 88
static VALUE io_closefrom(VALUE klass, VALUE v_low_fd){
int i, lowfd;
int maxfd;
lowfd = NUM2INT(v_low_fd);
if(lowfd < 0)
rb_raise(rb_eArgError, "lowfd must be non-negative");
maxfd = open_max();
if(maxfd < 0)
rb_raise(rb_eRuntimeError, "failed to determine maximum file descriptor");
for(i = lowfd; i < maxfd; i++) {
if(!RB_RESERVED_FD_P(i))
close(i);
}
return klass;
}
|
.fdwalk(lowfd) {|fh| ... } ⇒ Object
Iterates over each open file descriptor and yields back a File object.
Not supported on all platforms.
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'ext/io/extra.c', line 210
static VALUE io_fdwalk(int argc, VALUE* argv, VALUE klass){
VALUE v_low_fd, v_block;
struct fdwalk_data data;
rb_scan_args(argc, argv, "1&", &v_low_fd, &v_block);
data.lowfd = NUM2INT(v_low_fd);
if(data.lowfd < 0)
rb_raise(rb_eArgError, "lowfd must be non-negative");
#ifdef PROC_SELF_FD_DIR
data.dir = opendir(PROC_SELF_FD_DIR);
#endif
rb_ensure(fdwalk_body, (VALUE)&data, fdwalk_ensure, (VALUE)&data);
return klass;
}
|
.pread(fd, maxlen, offset) ⇒ Object
Singleton version of the IO#pread method.
16 17 18 |
# File 'lib/io-extra.rb', line 16 def self.pread(fd, maxlen, offset) fd.pread(maxlen, offset) end |
.pwrite(fd, string, offset) ⇒ Object
Singleton version of the IO#pwrite method.
10 11 12 |
# File 'lib/io-extra.rb', line 10 def self.pwrite(fd, string, offset) fd.pwrite(string, offset) end |
.writev(fd, ary) ⇒ Object
IO.writev(fd, %w(hello world))
This method writes the contents of an array of strings to the given fd. It can be useful to avoid generating a temporary string via Array#join when writing out large arrays of strings.
The given array should have fewer elements than the IO::IOV_MAX constant.
Returns the number of bytes written.
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 |
# File 'ext/io/extra.c', line 394
static VALUE s_io_writev(VALUE klass, VALUE fd, VALUE ary) {
ssize_t result = 0;
ssize_t left;
struct writev_args args;
struct iovec_buffer iov_buf;
// Allow a fileno or filehandle
if(rb_respond_to(fd, rb_intern("fileno")))
fd = rb_funcall(fd, rb_intern("fileno"), 0, 0);
args.fd = NUM2INT(fd);
ary2iovec(&iov_buf, ary);
args.iov = iov_buf.iov;
args.iovcnt = iov_buf.iovcnt;
left = iov_buf.expected;
for(;;) {
ssize_t w = (ssize_t)rb_thread_call_without_gvl(
(void*)nogvl_writev, &args, RUBY_UBF_IO, 0
);
if(w == -1){
if(rb_io_wait_writable(args.fd)){
continue;
}
else{
if(result > 0){
/* unlikely to hit this case, return the already written bytes,
* we'll let the next write (or close) fail instead */
break;
}
free_iovec_buffer(&iov_buf);
rb_sys_fail("writev");
}
}
result += w;
if(w == left){
break;
}
else{
// Partial write, this can get tricky
int i;
struct iovec *new_iov = args.iov;
left -= w;
// Skip over iovecs we've already written completely
for(i = 0; i < args.iovcnt; i++){
if (w == 0)
break;
// Bounds check before pointer arithmetic
if(new_iov == NULL){
free_iovec_buffer(&iov_buf);
rb_raise(rb_eRuntimeError, "writev: iovec bounds check failed");
}
// Partially written iov, modify and retry with current iovec in front
if(new_iov->iov_len > (size_t)w){
char* base = (char*)new_iov->iov_base;
// Validate base pointer before arithmetic
if(base == NULL){
free_iovec_buffer(&iov_buf);
rb_raise(rb_eRuntimeError, "writev: null iov_base");
}
new_iov->iov_len -= w;
new_iov->iov_base = (void *)(base + w);
break;
}
w -= new_iov->iov_len;
new_iov++;
}
// Validate we haven't exceeded bounds before modifying args
if(i > args.iovcnt){
free_iovec_buffer(&iov_buf);
rb_raise(rb_eRuntimeError, "writev: exceeded iovec array bounds");
}
// Retry without the already-written iovecs
args.iovcnt -= i;
args.iov = new_iov;
}
}
free_iovec_buffer(&iov_buf);
return LONG2NUM(result);
}
|
Instance Method Details
#directio=(advice) ⇒ Object
Sets the advice for the current file descriptor using directio(). Valid values are IO::DIRECTIO_ON and IO::DIRECTIO_OFF. See the directio(3c) man page for more information.
All file descriptors start at DIRECTIO_OFF, unless your filesystem has been mounted using ‘forcedirectio’ (and supports that option).
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'ext/io/extra.c', line 268
static VALUE io_set_directio(VALUE self, VALUE v_advice){
int fd;
int advice = NUM2INT(v_advice);
/* Only two possible valid values */
if( (advice != DIRECTIO_OFF) && (advice != DIRECTIO_ON) )
rb_raise(rb_eStandardError, "Invalid value passed to IO#directio=");
/* Retrieve the current file descriptor in order to pass it to directio() */
fd = NUM2INT(rb_funcall(self, rb_intern("fileno"), 0, 0));
#if defined(HAVE_DIRECTIO)
if(directio(fd, advice) < 0)
rb_raise(rb_eStandardError, "The directio() call failed");
if(advice == DIRECTIO_ON)
rb_iv_set(self, "@directio", Qtrue);
else
rb_iv_set(self, "@directio", Qfalse);
#else
{
#if defined(O_DIRECT)
int flags = fcntl(fd, F_GETFL);
if(flags < 0)
rb_sys_fail("fcntl");
if(advice == DIRECTIO_OFF){
if(flags & O_DIRECT){
if(fcntl(fd, F_SETFL, flags & ~O_DIRECT) < 0)
rb_sys_fail("fcntl");
}
} else { /* DIRECTIO_ON */
if(!(flags & O_DIRECT)){
if(fcntl(fd, F_SETFL, flags | O_DIRECT) < 0)
rb_sys_fail("fcntl");
}
}
#elif defined(F_NOCACHE)
if(advice == DIRECTIO_OFF){
if(fcntl(fd, F_NOCACHE, 0) < 0)
rb_sys_fail("fcntl");
rb_iv_set(self, "@directio", Qfalse);
} else { /* DIRECTIO_ON*/
if(fcntl(fd, F_NOCACHE, 1) < 0)
rb_sys_fail("fcntl");
rb_iv_set(self, "@directio", Qtrue);
}
#endif
}
#endif
return self;
}
|
#directio? ⇒ Boolean
Returns true or false, based on whether directio has been set for the current handle. The default is false.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'ext/io/extra.c', line 238
static VALUE io_get_directio(VALUE self){
#if defined(HAVE_DIRECTIO) || defined(F_NOCACHE)
VALUE v_advice = rb_iv_get(self, "@directio");
if(NIL_P(v_advice))
v_advice = Qfalse;
return v_advice;
#elif defined(O_DIRECT)
int fd = NUM2INT(rb_funcall(self, rb_intern("fileno"), 0, 0));
int flags = fcntl(fd, F_GETFL);
if(flags < 0)
rb_sys_fail("fcntl");
return (flags & O_DIRECT) ? Qtrue : Qfalse;
#endif /* O_DIRECT */
}
|
#ttyname ⇒ Object
io.ttyname
Returns the ttyname associated with the IO object, or nil if the IO object isn’t associated with a tty.
Example:
STDOUT.ttyname # => '/dev/ttyp1'
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
# File 'ext/io/extra.c', line 500
static VALUE io_get_ttyname(VALUE self){
VALUE v_return = Qnil;
int fd = NUM2INT(rb_funcall(self, rb_intern("fileno"), 0, 0));
if(fd < 0)
rb_raise(rb_eArgError, "invalid file descriptor");
errno = 0;
if(isatty(fd)){
char *name = ttyname(fd);
if(name != NULL)
v_return = rb_str_new2(name);
}
return v_return;
}
|