From 8d21f666b8098545a366c46f1990edf2a9f4ffcb Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Wed, 16 Apr 2025 16:50:37 +0900 Subject: [PATCH] Introduce `enum rb_io_mode`. (#7894) --- include/ruby/io.h | 277 +++++++++++++++++++++++++--------------------- internal/io.h | 2 +- io.c | 89 ++++++++------- 3 files changed, 198 insertions(+), 170 deletions(-) diff --git a/include/ruby/io.h b/include/ruby/io.h index d2fd3ed317..11d5ce5bfe 100644 --- a/include/ruby/io.h +++ b/include/ruby/io.h @@ -137,129 +137,6 @@ struct rb_io_encoding { VALUE ecopts; }; -#ifndef HAVE_RB_IO_T -#define HAVE_RB_IO_T 1 -/** Ruby's IO, metadata and buffers. */ -struct rb_io { - /** The IO's Ruby level counterpart. */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - VALUE self; - - /** stdio ptr for read/write, if available. */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - FILE *stdio_file; - - /** file descriptor. */ - RBIMPL_ATTR_DEPRECATED(("rb_io_descriptor")) - int fd; - - /** mode flags: FMODE_XXXs */ - RBIMPL_ATTR_DEPRECATED(("rb_io_mode")) - int mode; - - /** child's pid (for pipes) */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - rb_pid_t pid; - - /** number of lines read */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - int lineno; - - /** pathname for file */ - RBIMPL_ATTR_DEPRECATED(("rb_io_path")) - VALUE pathv; - - /** finalize proc */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - void (*finalize)(struct rb_io*,int); - - /** Write buffer. */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - rb_io_buffer_t wbuf; - - /** - * (Byte) read buffer. Note also that there is a field called - * ::rb_io_t::cbuf, which also concerns read IO. - */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - rb_io_buffer_t rbuf; - - /** - * Duplex IO object, if set. - * - * @see rb_io_set_write_io() - */ - RBIMPL_ATTR_DEPRECATED(("rb_io_get_write_io")) - VALUE tied_io_for_writing; - - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - struct rb_io_encoding encs; /**< Decomposed encoding flags. */ - - /** Encoding converter used when reading from this IO. */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - rb_econv_t *readconv; - - /** - * rb_io_ungetc() destination. This buffer is read before checking - * ::rb_io_t::rbuf - */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - rb_io_buffer_t cbuf; - - /** Encoding converter used when writing to this IO. */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - rb_econv_t *writeconv; - - /** - * This is, when set, an instance of ::rb_cString which holds the "common" - * encoding. Write conversion can convert strings twice... In case - * conversion from encoding X to encoding Y does not exist, Ruby finds an - * encoding Z that bridges the two, so that X to Z to Y conversion happens. - */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - VALUE writeconv_asciicompat; - - /** Whether ::rb_io_t::writeconv is already set up. */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - int writeconv_initialized; - - /** - * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before - * initialising ::rb_io_t::writeconv. - */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - int writeconv_pre_ecflags; - - /** - * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising - * ::rb_io_t::writeconv. - */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - VALUE writeconv_pre_ecopts; - - /** - * This is a Ruby level mutex. It avoids multiple threads to write to an - * IO at once; helps for instance rb_io_puts() to ensure newlines right - * next to its arguments. - * - * This of course doesn't help inter-process IO interleaves, though. - */ - RBIMPL_ATTR_DEPRECATED(("with no replacement")) - VALUE write_lock; - - /** - * The timeout associated with this IO when performing blocking operations. - */ - RBIMPL_ATTR_DEPRECATED(("rb_io_timeout/rb_io_set_timeout")) - VALUE timeout; -}; -#endif - -typedef struct rb_io rb_io_t; - -/** @alias{rb_io_enc_t} */ -typedef struct rb_io_encoding rb_io_enc_t; - /** * @name Possible flags for ::rb_io_t::mode * @@ -372,6 +249,154 @@ typedef struct rb_io_encoding rb_io_enc_t; /** @} */ +enum rb_io_mode { + RUBY_IO_MODE_EXTERNAL = FMODE_EXTERNAL, + + RUBY_IO_MODE_READABLE = FMODE_READABLE, + RUBY_IO_MODE_WRITABLE = FMODE_WRITABLE, + RUBY_IO_MODE_READABLE_WRITABLE = (RUBY_IO_MODE_READABLE|RUBY_IO_MODE_WRITABLE), + + RUBY_IO_MODE_BINARY = FMODE_BINMODE, + RUBY_IO_MODE_TEXT = FMODE_TEXTMODE, + RUBY_IO_MODE_TEXT_SET_ENCODING_FROM_BOM = FMODE_SETENC_BY_BOM, + + RUBY_IO_MODE_SYNCHRONISED = FMODE_SYNC, + + RUBY_IO_MODE_TTY = FMODE_TTY, + + RUBY_IO_MODE_DUPLEX = FMODE_DUPLEX, + + RUBY_IO_MODE_APPEND = FMODE_APPEND, + RUBY_IO_MODE_CREATE = FMODE_CREATE, + RUBY_IO_MODE_EXCLUSIVE = FMODE_EXCL, + RUBY_IO_MODE_TRUNCATE = FMODE_TRUNC, +}; + +typedef enum rb_io_mode rb_io_mode_t; + +#ifndef HAVE_RB_IO_T +#define HAVE_RB_IO_T 1 +/** Ruby's IO, metadata and buffers. */ +struct rb_io { + /** The IO's Ruby level counterpart. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE self; + + /** stdio ptr for read/write, if available. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + FILE *stdio_file; + + /** file descriptor. */ + RBIMPL_ATTR_DEPRECATED(("rb_io_descriptor")) + int fd; + + /** mode flags: FMODE_XXXs */ + RBIMPL_ATTR_DEPRECATED(("rb_io_mode")) + enum rb_io_mode mode; + + /** child's pid (for pipes) */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_pid_t pid; + + /** number of lines read */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + int lineno; + + /** pathname for file */ + RBIMPL_ATTR_DEPRECATED(("rb_io_path")) + VALUE pathv; + + /** finalize proc */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + void (*finalize)(struct rb_io*,int); + + /** Write buffer. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_io_buffer_t wbuf; + + /** + * (Byte) read buffer. Note also that there is a field called + * ::rb_io_t::cbuf, which also concerns read IO. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_io_buffer_t rbuf; + + /** + * Duplex IO object, if set. + * + * @see rb_io_set_write_io() + */ + RBIMPL_ATTR_DEPRECATED(("rb_io_get_write_io")) + VALUE tied_io_for_writing; + + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + struct rb_io_encoding encs; /**< Decomposed encoding flags. */ + + /** Encoding converter used when reading from this IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_econv_t *readconv; + + /** + * rb_io_ungetc() destination. This buffer is read before checking + * ::rb_io_t::rbuf + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_io_buffer_t cbuf; + + /** Encoding converter used when writing to this IO. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + rb_econv_t *writeconv; + + /** + * This is, when set, an instance of ::rb_cString which holds the "common" + * encoding. Write conversion can convert strings twice... In case + * conversion from encoding X to encoding Y does not exist, Ruby finds an + * encoding Z that bridges the two, so that X to Z to Y conversion happens. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE writeconv_asciicompat; + + /** Whether ::rb_io_t::writeconv is already set up. */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + int writeconv_initialized; + + /** + * Value of ::rb_io_t::rb_io_enc_t::ecflags stored right before + * initialising ::rb_io_t::writeconv. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + int writeconv_pre_ecflags; + + /** + * Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising + * ::rb_io_t::writeconv. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE writeconv_pre_ecopts; + + /** + * This is a Ruby level mutex. It avoids multiple threads to write to an + * IO at once; helps for instance rb_io_puts() to ensure newlines right + * next to its arguments. + * + * This of course doesn't help inter-process IO interleaves, though. + */ + RBIMPL_ATTR_DEPRECATED(("with no replacement")) + VALUE write_lock; + + /** + * The timeout associated with this IO when performing blocking operations. + */ + RBIMPL_ATTR_DEPRECATED(("rb_io_timeout/rb_io_set_timeout")) + VALUE timeout; +}; +#endif + +typedef struct rb_io rb_io_t; + +/** @alias{rb_io_enc_t} */ +typedef struct rb_io_encoding rb_io_enc_t; + /** * Allocate a new IO object, with the given file descriptor. */ @@ -525,7 +550,7 @@ FILE *rb_fdopen(int fd, const char *modestr); * * rb_io_modestr_fmode() is not a pure function because it raises. */ -int rb_io_modestr_fmode(const char *modestr); +enum rb_io_mode rb_io_modestr_fmode(const char *modestr); /** * Identical to rb_io_modestr_fmode(), except it returns a mixture of `O_` @@ -780,7 +805,7 @@ int rb_io_mode(VALUE io); * @post `enc2_p` is the specified external encoding. * @post `fmode_p` is the specified set of `FMODE_` modes. */ -int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p); +int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, enum rb_io_mode *fmode_p); /** * This function can be seen as an extended version of @@ -849,7 +874,7 @@ int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding ** * ) -> void * ``` */ -void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, int *fmode_p, rb_io_enc_t *convconfig_p); +void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, int *oflags_p, enum rb_io_mode *fmode_p, rb_io_enc_t *convconfig_p); /* :TODO: can this function be __attribute__((warn_unused_result)) or not? */ /** diff --git a/internal/io.h b/internal/io.h index b4efa174a1..70efdf0327 100644 --- a/internal/io.h +++ b/internal/io.h @@ -31,7 +31,7 @@ struct rb_io { int fd; /** mode flags: FMODE_XXXs */ - int mode; + enum rb_io_mode mode; /** child's pid (for pipes) */ rb_pid_t pid; diff --git a/io.c b/io.c index 6663a865ee..857c204b40 100644 --- a/io.c +++ b/io.c @@ -220,7 +220,7 @@ static VALUE sym_DATA; static VALUE sym_HOLE; #endif -static VALUE prep_io(int fd, int fmode, VALUE klass, const char *path); +static VALUE prep_io(int fd, enum rb_io_mode fmode, VALUE klass, const char *path); VALUE rb_io_blocking_region_wait(struct rb_io *io, rb_blocking_function_t *function, void *argument, enum rb_io_event events) @@ -6432,7 +6432,7 @@ rb_io_binmode_p(VALUE io) } static const char* -rb_io_fmode_modestr(int fmode) +rb_io_fmode_modestr(enum rb_io_mode fmode) { if (fmode & FMODE_APPEND) { if ((fmode & FMODE_READWRITE) == FMODE_READWRITE) { @@ -6466,10 +6466,10 @@ io_encname_bom_p(const char *name, long len) return len > bom_prefix_len && STRNCASECMP(name, bom_prefix, bom_prefix_len) == 0; } -int +enum rb_io_mode rb_io_modestr_fmode(const char *modestr) { - int fmode = 0; + enum rb_io_mode fmode = 0; const char *m = modestr, *p = NULL; switch (*m++) { @@ -6526,7 +6526,7 @@ rb_io_modestr_fmode(const char *modestr) int rb_io_oflags_fmode(int oflags) { - int fmode = 0; + enum rb_io_mode fmode = 0; switch (oflags & O_ACCMODE) { case O_RDONLY: @@ -6562,7 +6562,7 @@ rb_io_oflags_fmode(int oflags) } static int -rb_io_fmode_oflags(int fmode) +rb_io_fmode_oflags(enum rb_io_mode fmode) { int oflags = 0; @@ -6647,7 +6647,7 @@ rb_io_oflags_modestr(int oflags) * Qnil => no encoding specified (internal only) */ static void -rb_io_ext_int_to_encs(rb_encoding *ext, rb_encoding *intern, rb_encoding **enc, rb_encoding **enc2, int fmode) +rb_io_ext_int_to_encs(rb_encoding *ext, rb_encoding *intern, rb_encoding **enc, rb_encoding **enc2, enum rb_io_mode fmode) { int default_ext = 0; @@ -6682,12 +6682,12 @@ unsupported_encoding(const char *name, rb_encoding *enc) static void parse_mode_enc(const char *estr, rb_encoding *estr_enc, - rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p) + rb_encoding **enc_p, rb_encoding **enc2_p, enum rb_io_mode *fmode_p) { const char *p; char encname[ENCODING_MAXNAMELEN+1]; int idx, idx2; - int fmode = fmode_p ? *fmode_p : 0; + enum rb_io_mode fmode = fmode_p ? *fmode_p : 0; rb_encoding *ext_enc, *int_enc; long len; @@ -6749,7 +6749,7 @@ parse_mode_enc(const char *estr, rb_encoding *estr_enc, } int -rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p) +rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, enum rb_io_mode *fmode_p) { VALUE encoding=Qnil, extenc=Qundef, intenc=Qundef, tmp; int extracted = 0; @@ -6818,9 +6818,9 @@ rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2 } static void -validate_enc_binmode(int *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *enc2) +validate_enc_binmode(enum rb_io_mode *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *enc2) { - int fmode = *fmode_p; + enum rb_io_mode fmode = *fmode_p; if ((fmode & FMODE_READABLE) && !enc2 && @@ -6845,7 +6845,7 @@ validate_enc_binmode(int *fmode_p, int ecflags, rb_encoding *enc, rb_encoding *e } static void -extract_binmode(VALUE opthash, int *fmode) +extract_binmode(VALUE opthash, enum rb_io_mode *fmode) { if (!NIL_P(opthash)) { VALUE v; @@ -6875,10 +6875,11 @@ extract_binmode(VALUE opthash, int *fmode) void rb_io_extract_modeenc(VALUE *vmode_p, VALUE *vperm_p, VALUE opthash, - int *oflags_p, int *fmode_p, struct rb_io_encoding *convconfig_p) + int *oflags_p, enum rb_io_mode *fmode_p, struct rb_io_encoding *convconfig_p) { VALUE vmode; - int oflags, fmode; + int oflags; + enum rb_io_mode fmode; rb_encoding *enc, *enc2; int ecflags; VALUE ecopts; @@ -7193,7 +7194,7 @@ io_set_encoding_by_bom(VALUE io) } static VALUE -rb_file_open_generic(VALUE io, VALUE filename, int oflags, int fmode, +rb_file_open_generic(VALUE io, VALUE filename, int oflags, enum rb_io_mode fmode, const struct rb_io_encoding *convconfig, mode_t perm) { VALUE pathv; @@ -7230,7 +7231,7 @@ rb_file_open_generic(VALUE io, VALUE filename, int oflags, int fmode, static VALUE rb_file_open_internal(VALUE io, VALUE filename, const char *modestr) { - int fmode = rb_io_modestr_fmode(modestr); + enum rb_io_mode fmode = rb_io_modestr_fmode(modestr); const char *p = strchr(modestr, ':'); struct rb_io_encoding convconfig; @@ -7546,7 +7547,7 @@ char *rb_execarg_commandline(const struct rb_execarg *eargp, VALUE *prog); #ifndef __EMSCRIPTEN__ static VALUE -pipe_open(VALUE execarg_obj, const char *modestr, int fmode, +pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode, const struct rb_io_encoding *convconfig) { struct rb_execarg *eargp = NIL_P(execarg_obj) ? NULL : rb_execarg_get(execarg_obj); @@ -7775,7 +7776,7 @@ pipe_open(VALUE execarg_obj, const char *modestr, int fmode, } #else static VALUE -pipe_open(VALUE execarg_obj, const char *modestr, int fmode, +pipe_open(VALUE execarg_obj, const char *modestr, enum rb_io_mode fmode, const struct rb_io_encoding *convconfig) { rb_raise(rb_eNotImpError, "popen() is not available"); @@ -7797,7 +7798,7 @@ is_popen_fork(VALUE prog) } static VALUE -pipe_open_s(VALUE prog, const char *modestr, int fmode, +pipe_open_s(VALUE prog, const char *modestr, enum rb_io_mode fmode, const struct rb_io_encoding *convconfig) { int argc = 1; @@ -8006,7 +8007,8 @@ rb_io_popen(VALUE pname, VALUE pmode, VALUE env, VALUE opt) { const char *modestr; VALUE tmp, execarg_obj = Qnil; - int oflags, fmode; + int oflags; + enum rb_io_mode fmode; struct rb_io_encoding convconfig; tmp = rb_check_array_type(pname); @@ -8115,8 +8117,9 @@ ruby_popen_writer(char *const *argv, rb_pid_t *pid) static VALUE rb_open_file(VALUE io, VALUE fname, VALUE vmode, VALUE vperm, VALUE opt) { + int oflags; + enum rb_io_mode fmode; struct rb_io_encoding convconfig; - int oflags, fmode; mode_t perm; FilePathValue(fname); @@ -8299,22 +8302,8 @@ rb_f_open(int argc, VALUE *argv, VALUE _) return rb_io_s_open(argc, argv, rb_cFile); } -static VALUE rb_io_open_generic(VALUE, VALUE, int, int, const struct rb_io_encoding *, mode_t); - static VALUE -rb_io_open(VALUE io, VALUE filename, VALUE vmode, VALUE vperm, VALUE opt) -{ - int oflags, fmode; - struct rb_io_encoding convconfig; - mode_t perm; - - rb_io_extract_modeenc(&vmode, &vperm, opt, &oflags, &fmode, &convconfig); - perm = NIL_P(vperm) ? 0666 : NUM2MODET(vperm); - return rb_io_open_generic(io, filename, oflags, fmode, &convconfig, perm); -} - -static VALUE -rb_io_open_generic(VALUE klass, VALUE filename, int oflags, int fmode, +rb_io_open_generic(VALUE klass, VALUE filename, int oflags, enum rb_io_mode fmode, const struct rb_io_encoding *convconfig, mode_t perm) { VALUE cmd; @@ -8329,6 +8318,19 @@ rb_io_open_generic(VALUE klass, VALUE filename, int oflags, int fmode, } } +static VALUE +rb_io_open(VALUE io, VALUE filename, VALUE vmode, VALUE vperm, VALUE opt) +{ + int oflags; + enum rb_io_mode fmode; + struct rb_io_encoding convconfig; + mode_t perm; + + rb_io_extract_modeenc(&vmode, &vperm, opt, &oflags, &fmode, &convconfig); + perm = NIL_P(vperm) ? 0666 : NUM2MODET(vperm); + return rb_io_open_generic(io, filename, oflags, fmode, &convconfig, perm); +} + static VALUE io_reopen(VALUE io, VALUE nfile) { @@ -8482,7 +8484,7 @@ rb_io_reopen(int argc, VALUE *argv, VALUE file) } if (!NIL_P(nmode) || !NIL_P(opt)) { - int fmode; + enum rb_io_mode fmode; struct rb_io_encoding convconfig; rb_io_extract_modeenc(&nmode, 0, opt, &oflags, &fmode, &convconfig); @@ -9317,7 +9319,7 @@ rb_io_open_descriptor(VALUE klass, int descriptor, int mode, VALUE path, VALUE t } static VALUE -prep_io(int fd, int fmode, VALUE klass, const char *path) +prep_io(int fd, enum rb_io_mode fmode, VALUE klass, const char *path) { VALUE path_value = Qnil; rb_encoding *e; @@ -9363,7 +9365,7 @@ rb_io_fdopen(int fd, int oflags, const char *path) } static VALUE -prep_stdio(FILE *f, int fmode, VALUE klass, const char *path) +prep_stdio(FILE *f, enum rb_io_mode fmode, VALUE klass, const char *path) { rb_io_t *fptr; VALUE io = prep_io(fileno(f), fmode|FMODE_EXTERNAL|DEFAULT_TEXTMODE, klass, path); @@ -9523,7 +9525,8 @@ static VALUE io_initialize(VALUE io, VALUE fnum, VALUE vmode, VALUE opt) { rb_io_t *fp; - int fd, fmode, oflags = O_RDONLY; + int fd, oflags = O_RDONLY; + enum rb_io_mode fmode; struct rb_io_encoding convconfig; #if defined(HAVE_FCNTL) && defined(F_GETFL) int ofmode; @@ -10135,7 +10138,7 @@ argf_next_argv(VALUE argf) char *fn; rb_io_t *fptr; int stdout_binmode = 0; - int fmode; + enum rb_io_mode fmode; VALUE r_stdout = rb_ractor_stdout(); @@ -11917,7 +11920,7 @@ rb_io_s_pipe(int argc, VALUE *argv, VALUE klass) VALUE opt; rb_io_t *fptr, *fptr2; struct io_encoding_set_args ies_args; - int fmode = 0; + enum rb_io_mode fmode = 0; VALUE ret; argc = rb_scan_args(argc, argv, "02:", &v1, &v2, &opt);