io.c (rb_io_syswrite): avoid leaving garbage after write
As with IO#write, IO#syswrite also generates garbage which can be harmful in hand-coded read-write loops. * io.c (swrite_arg, swrite_do, swrite_end): new (rb_io_syswrite): use new functions to cleanup garbage [ruby-core:78898] [Bug #13085] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@57472 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
5c988df0dd
commit
4b9a21cdd6
45
io.c
45
io.c
@ -4745,6 +4745,34 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io)
|
|||||||
return OFFT2NUM(pos);
|
return OFFT2NUM(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct swrite_arg {
|
||||||
|
VALUE orig;
|
||||||
|
VALUE tmp;
|
||||||
|
rb_io_t *fptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
swrite_do(VALUE arg)
|
||||||
|
{
|
||||||
|
struct swrite_arg *sa = (struct swrite_arg *)arg;
|
||||||
|
const char *ptr;
|
||||||
|
long len;
|
||||||
|
|
||||||
|
RSTRING_GETMEM(sa->tmp, ptr, len);
|
||||||
|
|
||||||
|
return (VALUE)rb_write_internal(sa->fptr->fd, ptr, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
swrite_end(VALUE arg)
|
||||||
|
{
|
||||||
|
struct swrite_arg *sa = (struct swrite_arg *)arg;
|
||||||
|
|
||||||
|
rb_str_tmp_frozen_release(sa->orig, sa->tmp);
|
||||||
|
|
||||||
|
return Qfalse;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
* ios.syswrite(string) -> integer
|
* ios.syswrite(string) -> integer
|
||||||
@ -4761,26 +4789,25 @@ rb_io_sysseek(int argc, VALUE *argv, VALUE io)
|
|||||||
static VALUE
|
static VALUE
|
||||||
rb_io_syswrite(VALUE io, VALUE str)
|
rb_io_syswrite(VALUE io, VALUE str)
|
||||||
{
|
{
|
||||||
rb_io_t *fptr;
|
struct swrite_arg sa;
|
||||||
long n;
|
long n;
|
||||||
|
|
||||||
if (!RB_TYPE_P(str, T_STRING))
|
if (!RB_TYPE_P(str, T_STRING))
|
||||||
str = rb_obj_as_string(str);
|
str = rb_obj_as_string(str);
|
||||||
|
|
||||||
io = GetWriteIO(io);
|
io = GetWriteIO(io);
|
||||||
GetOpenFile(io, fptr);
|
GetOpenFile(io, sa.fptr);
|
||||||
rb_io_check_writable(fptr);
|
rb_io_check_writable(sa.fptr);
|
||||||
|
|
||||||
str = rb_str_new_frozen(str);
|
if (sa.fptr->wbuf.len) {
|
||||||
|
|
||||||
if (fptr->wbuf.len) {
|
|
||||||
rb_warn("syswrite for buffered IO");
|
rb_warn("syswrite for buffered IO");
|
||||||
}
|
}
|
||||||
|
|
||||||
n = rb_write_internal(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str));
|
sa.orig = str;
|
||||||
RB_GC_GUARD(str);
|
sa.tmp = rb_str_tmp_frozen_acquire(str);
|
||||||
|
n = (long)rb_ensure(swrite_do, (VALUE)&sa, swrite_end, (VALUE)&sa);
|
||||||
|
|
||||||
if (n == -1) rb_sys_fail_path(fptr->pathv);
|
if (n == -1) rb_sys_fail_path(sa.fptr->pathv);
|
||||||
|
|
||||||
return LONG2FIX(n);
|
return LONG2FIX(n);
|
||||||
}
|
}
|
||||||
|
@ -3512,11 +3512,13 @@ __END__
|
|||||||
with_pipe do |r, w|
|
with_pipe do |r, w|
|
||||||
before = ObjectSpace.count_objects(res)[:T_STRING]
|
before = ObjectSpace.count_objects(res)[:T_STRING]
|
||||||
n = w.write(buf)
|
n = w.write(buf)
|
||||||
|
s = w.syswrite(buf)
|
||||||
after = ObjectSpace.count_objects(res)[:T_STRING]
|
after = ObjectSpace.count_objects(res)[:T_STRING]
|
||||||
assert_equal before, after,
|
assert_equal before, after,
|
||||||
'no strings left over after write [ruby-core:78898] [Bug #13085]'
|
'no strings left over after write [ruby-core:78898] [Bug #13085]'
|
||||||
assert_not_predicate buf, :frozen?, 'no inadvertant freeze'
|
assert_not_predicate buf, :frozen?, 'no inadvertant freeze'
|
||||||
assert_equal buf.bytesize, n, 'wrote expected size'
|
assert_equal buf.bytesize, n, 'write wrote expected size'
|
||||||
|
assert_equal s, n, 'syswrite wrote expected size'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user