From 161d3bff6aba8654c94def617df5d482bf5e2b1a Mon Sep 17 00:00:00 2001 From: Perfloop Agent Date: Mon, 2 Nov 2026 12:17:10 +0000 Subject: [PATCH 1/2] Close named standard descriptors and preserve stream identity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Perfloop Agent Signed-off-by: Tomás Senart --- docs/src/runtime/io.md | 43 +++-- libcc2rs/src/fd.rs | 66 ++++++-- libcc2rs/src/io.rs | 155 +++++++++++++------ rules/algorithm/tgt_refcount.rs | 11 +- rules/algorithm/tgt_unsafe.rs | 9 +- rules/fstream/tgt_refcount.rs | 40 ++++- rules/fstream/tgt_unsafe.rs | 52 ++++--- rules/iostream/tgt_refcount.rs | 8 +- rules/iostream/tgt_unsafe.rs | 14 +- rules/string/tgt_refcount.rs | 5 +- rules/string/tgt_unsafe.rs | 5 +- tests/unit/fd_close_standard.c | 15 ++ tests/unit/iostream_closed.cpp | 12 ++ tests/unit/out/refcount/cout_alias.rs | 4 +- tests/unit/out/refcount/fd_close_standard.rs | 76 +++++++++ tests/unit/out/refcount/iostream_closed.rs | 21 +++ tests/unit/out/refcount/stdcopy_ostream.rs | 89 ++++++++++- tests/unit/out/unsafe/cout_alias.rs | 12 +- tests/unit/out/unsafe/fd_close_standard.rs | 47 ++++++ tests/unit/out/unsafe/iostream_closed.rs | 23 +++ tests/unit/out/unsafe/stdcopy_ostream.rs | 77 ++++++++- tests/unit/stdcopy_ostream.cpp | 14 +- 22 files changed, 657 insertions(+), 141 deletions(-) create mode 100644 tests/unit/fd_close_standard.c create mode 100644 tests/unit/iostream_closed.cpp create mode 100644 tests/unit/out/refcount/fd_close_standard.rs create mode 100644 tests/unit/out/refcount/iostream_closed.rs create mode 100644 tests/unit/out/unsafe/fd_close_standard.rs create mode 100644 tests/unit/out/unsafe/iostream_closed.rs diff --git a/docs/src/runtime/io.md b/docs/src/runtime/io.md index 58767f27e3..e0e1a4c831 100644 --- a/docs/src/runtime/io.md +++ b/docs/src/runtime/io.md @@ -42,14 +42,28 @@ through the `CFile`; the unsafe variant forwards to `libc::fread`. ## C++ Streams In the refcount model `cin`, `cout`, and `cerr` are translated as -`Ptr` values over duplicates of the standard descriptors, -returned by the `cin`, `cout`, and `cerr` functions. `Ptr` implements -`write_fmt` and `write_all` whenever `T: Write`, forwarding to the pointee -through `with_mut`, so `cout << x` becomes `write!(cout(), "{}", x)` and a raw -byte range is written with `cout().write_all(..)`. In the unsafe model -`cin_unsafe`, `cout_unsafe`, and `cerr_unsafe` return raw pointers to -thread-local `std::fs::File` values. C++ streams do not map fully onto -`std::fs::File`, so this translation may change in the future. +`Ptr` values returned by the `cin`, `cout`, and `cerr` functions. +`CppStream` uses the original standard descriptor number without owning or +duplicating it. Its unbuffered reads and writes return an I/O error if the +descriptor is closed, even when the stream is first used after the close. File +streams use the same type, wrapping an owned `std::fs::File`. + +The stream object is distinct from its I/O handle. It is not cloneable: +references, stream buffers, and iterators refer to the same stream object rather +than duplicating its file handle. In the refcount model these are +`Ptr` values; in the unsafe model they are raw pointers. Reads and +writes borrow the stream mutably, leaving a place on the object for future +formatting state, such as floating-point precision and string padding, +independently of the handle. Iterators do not keep a file stream alive after its +owner is destroyed. + +`Ptr` implements `write_fmt` and `write_all` whenever `T: Write`, forwarding +to the pointee through `with_mut`, so `cout << x` becomes +`write!(cout(), "{}", x)` and a raw byte range is written with +`cout().write_all(..)`. In the unsafe model `cin_unsafe`, `cout_unsafe`, and +`cerr_unsafe` return raw pointers to thread-local `CppStream` values. The +translation does not yet model the full C++ iostream state, including sticky +error flags, and insertion expressions discard the returned I/O error. ## Formatting @@ -94,8 +108,11 @@ The registry follows the descriptor's life. When a rule opens a file, raw number. When a rule performs I/O on that number, `FdRegistry::with_fd` looks the entry up and lends it out as a `BorrowedFd` for the duration of the call. When the program calls `close`, `FdRegistry::close` removes the entry, which -closes the descriptor. The registry starts out holding the standard descriptors -0, 1, and 2. +closes the descriptor. The registry starts out borrowing the live standard +descriptors 0, 1, and 2 from Rust's standard stream handles, without duplicating +them. An explicit `close` releases the original descriptor, but dropping the +thread-local registry does not close these borrowed standard descriptors. A file +opened at one of these numbers is owned and closed normally. In the `fstat` rule, the descriptor argument goes through `with_fd`: @@ -127,4 +144,8 @@ FdRegistry::with_fds(&wanted, |borrowed: &[BorrowedFd<'_>]| { Using a descriptor that was never opened, or using it after it was closed, is a bug in the original program. The registry turns such a use into a panic (with a -message prefixed `ub:`) so the bug surfaces instead of going unnoticed. +message prefixed `ub:`) so the bug surfaces instead of going unnoticed. This +policy also applies when another thread has already closed a standard descriptor +before this thread initializes its registry. The table remains thread-local: it +does not track descriptor ownership or closure across threads, and does not +provide native C's `EBADF` behavior for unregistered numbers. diff --git a/libcc2rs/src/fd.rs b/libcc2rs/src/fd.rs index 119cc87bfc..a72e4a8a9b 100644 --- a/libcc2rs/src/fd.rs +++ b/libcc2rs/src/fd.rs @@ -4,17 +4,51 @@ use std::cell::RefCell; use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd}; +enum Fd { + Owned(OwnedFd), + Stdin(std::io::Stdin), + Stdout(std::io::Stdout), + Stderr(std::io::Stderr), +} + +impl AsRawFd for Fd { + fn as_raw_fd(&self) -> i32 { + match self { + Self::Owned(fd) => fd.as_raw_fd(), + Self::Stdin(fd) => fd.as_raw_fd(), + Self::Stdout(fd) => fd.as_raw_fd(), + Self::Stderr(fd) => fd.as_raw_fd(), + } + } +} + +impl AsFd for Fd { + fn as_fd(&self) -> BorrowedFd<'_> { + match self { + Self::Owned(fd) => fd.as_fd(), + Self::Stdin(fd) => fd.as_fd(), + Self::Stdout(fd) => fd.as_fd(), + Self::Stderr(fd) => fd.as_fd(), + } + } +} + pub struct FdRegistry { - fds: Vec>, + fds: Vec>, } thread_local! { static FD_REGISTRY: RefCell = RefCell::new(FdRegistry { - fds: vec![ - std::io::stdin().as_fd().try_clone_to_owned().ok(), - std::io::stdout().as_fd().try_clone_to_owned().ok(), - std::io::stderr().as_fd().try_clone_to_owned().ok(), - ], + // Borrow the original standard descriptors, without closing them when + // this thread exits. Only an explicit close releases these descriptors. + fds: [ + Fd::Stdin(std::io::stdin()), + Fd::Stdout(std::io::stdout()), + Fd::Stderr(std::io::stderr()), + ].into_iter().map(|fd| { + // Check the number without borrowing a potentially closed descriptor. + (unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_GETFD) } >= 0).then_some(fd) + }).collect(), }); } @@ -27,8 +61,13 @@ impl FdRegistry { if fds.len() <= idx { fds.resize_with(idx + 1, || None); } - assert!(fds[idx].is_none(), "ub: fd registry collision on fd {raw}"); - fds[idx] = Some(fd); + // The first open may reuse an initially closed standard descriptor + // before the registry is initialized. Replace its borrowed entry. + assert!( + !matches!(fds[idx], Some(Fd::Owned(_))), + "ub: fd registry collision on fd {raw}" + ); + fds[idx] = Some(Fd::Owned(fd)); }); raw } @@ -57,13 +96,22 @@ impl FdRegistry { } pub fn close(fd: i32) -> i32 { - FD_REGISTRY.with(|r| { + let entry = FD_REGISTRY.with(|r| { r.borrow_mut() .fds .get_mut(fd as usize) .and_then(|slot| slot.take()) .unwrap_or_else(|| panic!("ub: bad fd {fd}")) }); + match entry { + Fd::Owned(fd) => drop(fd), + fd => { + if let Err(e) = nix::unistd::close(fd.as_raw_fd()) { + crate::cpp2rust_errno().write(e as i32); + return -1; + } + } + } 0 } } diff --git a/libcc2rs/src/io.rs b/libcc2rs/src/io.rs index 70a98e28ed..d86fb69e41 100644 --- a/libcc2rs/src/io.rs +++ b/libcc2rs/src/io.rs @@ -3,49 +3,88 @@ use crate::{AnyPtr, AsPointer, CFile, Ptr, Value}; use std::cell::{RefCell, UnsafeCell}; -use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::io::{self, Read, Write}; use std::rc::Rc; +enum StreamHandle { + File(std::fs::File), + Standard(i32), +} + +// The stream object is separate from its I/O handle so formatting and error +// state can live here. References and iterators must refer to this same object, +// not clone its handle into another stream. +pub struct CppStream { + handle: StreamHandle, +} + +impl From for CppStream { + fn from(file: std::fs::File) -> Self { + Self { + handle: StreamHandle::File(file), + } + } +} + +impl CppStream { + fn standard(fd: i32) -> Self { + Self { + handle: StreamHandle::Standard(fd), + } + } +} + +impl Read for CppStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match &mut self.handle { + StreamHandle::File(file) => file.read(buf), + StreamHandle::Standard(fd) => { + // A standard descriptor may have been closed. Do not create an + // owning File or a BorrowedFd, which requires an open descriptor. + let n = unsafe { libc::read(*fd, buf.as_mut_ptr().cast(), buf.len()) }; + if n < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(n as usize) + } + } + } + } +} + +impl Write for CppStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + match &mut self.handle { + StreamHandle::File(file) => file.write(buf), + StreamHandle::Standard(fd) => { + // The slice is valid for the duration of this unbuffered write. + let n = unsafe { libc::write(*fd, buf.as_ptr().cast(), buf.len()) }; + if n < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(n as usize) + } + } + } + } + + fn flush(&mut self) -> io::Result<()> { + match &mut self.handle { + StreamHandle::File(file) => file.flush(), + StreamHandle::Standard(_) => Ok(()), + } + } +} + +impl crate::ByteRepr for CppStream {} + thread_local! { - static SAFE_STDIN: Value = Rc::new(RefCell::new(std::fs::File::from( - std::io::stdin().as_fd().try_clone_to_owned().unwrap(), - ))); - static SAFE_STDOUT: Value = Rc::new(RefCell::new(std::fs::File::from( - std::io::stdout().as_fd().try_clone_to_owned().unwrap(), - ))); - static SAFE_STDERR: Value = Rc::new(RefCell::new(std::fs::File::from( - std::io::stderr().as_fd().try_clone_to_owned().unwrap(), - ))); - static UNSAFE_STDIN: UnsafeCell = unsafe { - UnsafeCell::new( - std::fs::File::from_raw_fd( - std::io::stdin() - .as_fd() - .try_clone_to_owned() - .unwrap() - .into_raw_fd(), - )) - }; - static UNSAFE_STDOUT: UnsafeCell = unsafe { - UnsafeCell::new( - std::fs::File::from_raw_fd( - std::io::stdout() - .as_fd() - .try_clone_to_owned() - .unwrap() - .into_raw_fd(), - )) - }; - static UNSAFE_STDERR: UnsafeCell = unsafe { - UnsafeCell::new( - std::fs::File::from_raw_fd( - std::io::stderr() - .as_fd() - .try_clone_to_owned() - .unwrap() - .into_raw_fd(), - )) - }; + static SAFE_STDIN: Value = Rc::new(RefCell::new(CppStream::standard(0))); + static SAFE_STDOUT: Value = Rc::new(RefCell::new(CppStream::standard(1))); + static SAFE_STDERR: Value = Rc::new(RefCell::new(CppStream::standard(2))); + static UNSAFE_STDIN: UnsafeCell = UnsafeCell::new(CppStream::standard(0)); + static UNSAFE_STDOUT: UnsafeCell = UnsafeCell::new(CppStream::standard(1)); + static UNSAFE_STDERR: UnsafeCell = UnsafeCell::new(CppStream::standard(2)); } thread_local! { @@ -66,15 +105,15 @@ pub fn c_stderr() -> Ptr { C_STDERR.with(AsPointer::as_pointer) } -pub fn cin() -> Ptr { +pub fn cin() -> Ptr { SAFE_STDIN.with(AsPointer::as_pointer) } -pub fn cout() -> Ptr { +pub fn cout() -> Ptr { SAFE_STDOUT.with(AsPointer::as_pointer) } -pub fn cerr() -> Ptr { +pub fn cerr() -> Ptr { SAFE_STDERR.with(AsPointer::as_pointer) } @@ -82,7 +121,7 @@ pub fn cerr() -> Ptr { /// /// The caller must ensure that the returned pointer is not used after the /// thread finishes. -pub unsafe fn cin_unsafe() -> *mut std::fs::File { +pub unsafe fn cin_unsafe() -> *mut CppStream { UNSAFE_STDIN.with(UnsafeCell::get) } @@ -90,7 +129,7 @@ pub unsafe fn cin_unsafe() -> *mut std::fs::File { /// /// The caller must ensure that the returned pointer is not used after the /// thread finishes. -pub unsafe fn cout_unsafe() -> *mut std::fs::File { +pub unsafe fn cout_unsafe() -> *mut CppStream { UNSAFE_STDOUT.with(UnsafeCell::get) } @@ -98,7 +137,7 @@ pub unsafe fn cout_unsafe() -> *mut std::fs::File { /// /// The caller must ensure that the returned pointer is not used after the /// thread finishes. -pub unsafe fn cerr_unsafe() -> *mut std::fs::File { +pub unsafe fn cerr_unsafe() -> *mut CppStream { UNSAFE_STDERR.with(UnsafeCell::get) } @@ -189,3 +228,27 @@ pub unsafe fn fread_unsafe( ) -> usize { unsafe { libc::fread(a0, a1, a2, a3) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn file_stream_io() { + let (read, write) = nix::unistd::pipe().unwrap(); + let mut input = CppStream::from(std::fs::File::from(read)); + let output = Rc::new(RefCell::new(CppStream::from(std::fs::File::from(write)))); + let stream = output.as_pointer(); + let copy = stream.clone(); + stream.write_all(&[0, 127, 255]).unwrap(); + copy.with_mut(|s| s.flush()).unwrap(); + drop(output); + + let mut first = [9]; + input.read_exact(&mut first).unwrap(); + assert_eq!(first, [0]); + let mut rest = Vec::new(); + input.read_to_end(&mut rest).unwrap(); + assert_eq!(rest, [127, 255]); + } +} diff --git a/rules/algorithm/tgt_refcount.rs b/rules/algorithm/tgt_refcount.rs index b3f952d4ac..e49ac81935 100644 --- a/rules/algorithm/tgt_refcount.rs +++ b/rules/algorithm/tgt_refcount.rs @@ -107,16 +107,19 @@ fn f12, T2: Clone + ByteRepr>(a0: T1, a1: T1, a2: T2) { } } -fn f13>(a0: T1, a1: T1, a2: &mut ::std::fs::File) -> ::std::fs::File { +fn f13>( + a0: T1, + a1: T1, + a2: Ptr, +) -> Ptr { let mut __it = a0; - let __out = &mut *a2; let mut __bytes: Vec = Vec::new(); while __it != a1 { __bytes.push(__it.iter_deref().read() as u8); __it.iter_inc(); } - __out.write_all(&__bytes); - __out.try_clone().unwrap() + a2.write_all(&__bytes); + a2 } fn f14(a0: Ptr, a1: Ptr, a2: T2) diff --git a/rules/algorithm/tgt_unsafe.rs b/rules/algorithm/tgt_unsafe.rs index 3a4b31be1c..9dee276dc5 100644 --- a/rules/algorithm/tgt_unsafe.rs +++ b/rules/algorithm/tgt_unsafe.rs @@ -115,17 +115,16 @@ unsafe fn f12, T2: Clone>(a0: T1, a1: T1, a2: T2) unsafe fn f13>( a0: T1, a1: T1, - a2: &mut ::std::fs::File, -) -> ::std::fs::File { + a2: *mut libcc2rs::CppStream, +) -> *mut libcc2rs::CppStream { let mut __it = a0; - let __out = &mut *a2; let mut __bytes: Vec = Vec::new(); while __it != a1 { __bytes.push(*__it.iter_deref() as u8); __it.iter_inc(); } - __out.write_all(&__bytes); - __out.try_clone().unwrap() + (*a2).write_all(&__bytes); + a2 } unsafe fn f14(a0: *mut T1, a1: *mut T1, a2: T2) diff --git a/rules/fstream/tgt_refcount.rs b/rules/fstream/tgt_refcount.rs index 23815e0673..920e4f461c 100644 --- a/rules/fstream/tgt_refcount.rs +++ b/rules/fstream/tgt_refcount.rs @@ -5,10 +5,42 @@ use libcc2rs::*; use std::cell::RefCell; use std::rc::Rc; -fn f1(a0: Ptr) -> ::std::fs::File { - ::std::fs::File::create(a0.to_string()).expect("Failed to open file") +fn f1(a0: Ptr) -> libcc2rs::CppStream { + ::std::fs::File::create(a0.to_string()) + .map(libcc2rs::CppStream::from) + .expect("Failed to open file") } -fn f5(a0: Ptr) -> ::std::fs::File { - ::std::fs::File::open(a0.to_string()).expect("Failed to open file") +fn f5(a0: Ptr) -> libcc2rs::CppStream { + ::std::fs::File::open(a0.to_string()) + .map(libcc2rs::CppStream::from) + .expect("Failed to open file") +} + +fn t3() -> Ptr { + Ptr::null() +} + +fn f2(a0: Ptr) -> Ptr { + a0 +} + +fn f3(a0: Ptr) -> Ptr { + a0 +} + +fn f4(a0: Ptr) -> Ptr { + a0 +} + +fn f6(a0: Ptr) -> Ptr { + a0 +} + +fn f7(a0: Ptr) -> Ptr { + a0 +} + +fn f8(a0: Ptr) -> Ptr { + a0 } diff --git a/rules/fstream/tgt_unsafe.rs b/rules/fstream/tgt_unsafe.rs index b43ad125d0..84f0052db5 100644 --- a/rules/fstream/tgt_unsafe.rs +++ b/rules/fstream/tgt_unsafe.rs @@ -1,47 +1,57 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -fn t1() -> ::std::fs::File { - ::std::fs::File::open("").unwrap() +fn t1() -> libcc2rs::CppStream { + ::std::fs::File::open("") + .map(libcc2rs::CppStream::from) + .unwrap() } -fn t2() -> ::std::fs::File { - ::std::fs::File::open("").unwrap() +fn t2() -> libcc2rs::CppStream { + ::std::fs::File::open("") + .map(libcc2rs::CppStream::from) + .unwrap() } -fn t3() -> ::std::fs::File { - ::std::fs::File::open("").unwrap() +fn t3() -> *mut libcc2rs::CppStream { + std::ptr::null_mut() } -fn t4() -> ::std::fs::File { - ::std::fs::File::open("").unwrap() +fn t4() -> libcc2rs::CppStream { + ::std::fs::File::open("") + .map(libcc2rs::CppStream::from) + .unwrap() } -unsafe fn f1(a0: *const libc::c_char) -> ::std::fs::File { - ::std::fs::File::create(::std::ffi::CStr::from_ptr(a0).to_str().unwrap()).unwrap() +unsafe fn f1(a0: *const libc::c_char) -> libcc2rs::CppStream { + ::std::fs::File::create(::std::ffi::CStr::from_ptr(a0).to_str().unwrap()) + .map(libcc2rs::CppStream::from) + .unwrap() } -unsafe fn f2(a0: ::std::fs::File) -> ::std::fs::File { - a0.try_clone().unwrap() +unsafe fn f2(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { + a0 } -unsafe fn f3(a0: &mut ::std::fs::File) -> &mut ::std::fs::File { +unsafe fn f3(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { a0 } -unsafe fn f4(a0: ::std::fs::File) -> ::std::fs::File { +unsafe fn f4(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { a0 } -unsafe fn f5(a0: *const libc::c_char) -> ::std::fs::File { - ::std::fs::File::open(::std::ffi::CStr::from_ptr(a0).to_str().unwrap()).unwrap() +unsafe fn f5(a0: *const libc::c_char) -> libcc2rs::CppStream { + ::std::fs::File::open(::std::ffi::CStr::from_ptr(a0).to_str().unwrap()) + .map(libcc2rs::CppStream::from) + .unwrap() } -unsafe fn f6(a0: ::std::fs::File) -> ::std::fs::File { - a0.try_clone().unwrap() +unsafe fn f6(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { + a0 } -unsafe fn f7(a0: ::std::fs::File) -> ::std::fs::File { - a0.try_clone().unwrap() +unsafe fn f7(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { + a0 } -unsafe fn f8(a0: ::std::fs::File) -> ::std::fs::File { +unsafe fn f8(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { a0 } diff --git a/rules/iostream/tgt_refcount.rs b/rules/iostream/tgt_refcount.rs index 9dae4b7930..b60e7e2fcc 100644 --- a/rules/iostream/tgt_refcount.rs +++ b/rules/iostream/tgt_refcount.rs @@ -4,18 +4,18 @@ use libcc2rs::*; // TODO: t2 and t3 should be translated to Ptr -fn t2() -> Ptr { +fn t2() -> Ptr { Ptr::null() } -fn t3() -> Ptr { +fn t3() -> Ptr { Ptr::null() } -fn f1() -> Ptr<::std::fs::File> { +fn f1() -> Ptr { libcc2rs::cout() } -fn f2() -> Ptr<::std::fs::File> { +fn f2() -> Ptr { libcc2rs::cerr() } diff --git a/rules/iostream/tgt_unsafe.rs b/rules/iostream/tgt_unsafe.rs index 17002744a8..fbbf3055be 100644 --- a/rules/iostream/tgt_unsafe.rs +++ b/rules/iostream/tgt_unsafe.rs @@ -3,23 +3,25 @@ use libcc2rs::*; -fn t1() -> std::fs::File { - std::fs::File::open("").unwrap() +fn t1() -> libcc2rs::CppStream { + ::std::fs::File::open("") + .map(libcc2rs::CppStream::from) + .unwrap() } // TODO: t2 and t3 should be translated to *mut dyn Traits -unsafe fn t2() -> *mut std::fs::File { +unsafe fn t2() -> *mut libcc2rs::CppStream { libcc2rs::cout_unsafe() } -unsafe fn t3() -> *mut std::fs::File { +unsafe fn t3() -> *mut libcc2rs::CppStream { libcc2rs::cout_unsafe() } -unsafe fn f1() -> *mut ::std::fs::File { +unsafe fn f1() -> *mut libcc2rs::CppStream { libcc2rs::cout_unsafe() } -unsafe fn f2() -> *mut ::std::fs::File { +unsafe fn f2() -> *mut libcc2rs::CppStream { libcc2rs::cerr_unsafe() } diff --git a/rules/string/tgt_refcount.rs b/rules/string/tgt_refcount.rs index 5e7f50ff03..c6cb3f54b9 100644 --- a/rules/string/tgt_refcount.rs +++ b/rules/string/tgt_refcount.rs @@ -139,11 +139,10 @@ fn f2(a0: Vec) -> usize { (a0.len() - 1) } -fn f8(a0: std::fs::File, a1: std::fs::File) -> Vec { +fn f8(a0: Ptr, a1: Ptr) -> Vec { use std::io::Read; let mut __bytes: Vec = Vec::new(); - let mut __f = &a0; - __f.read_to_end(&mut __bytes) + a0.with_mut(|__f| __f.read_to_end(&mut __bytes)) .expect("couldn't read the file"); __bytes.push(0); CChar::from_byte_vec(__bytes) diff --git a/rules/string/tgt_unsafe.rs b/rules/string/tgt_unsafe.rs index fceb692dc2..3a8b6f3830 100644 --- a/rules/string/tgt_unsafe.rs +++ b/rules/string/tgt_unsafe.rs @@ -70,10 +70,9 @@ unsafe fn f7(a0: *const libc::c_char, a1: usize) -> Vec { // TODO: this does not care for a1 use std::io::Read; -unsafe fn f8(a0: std::fs::File, a1: std::fs::File) -> Vec { +unsafe fn f8(a0: *mut libcc2rs::CppStream, a1: *mut libcc2rs::CppStream) -> Vec { let mut __bytes: Vec = Vec::new(); - let mut __f = &a0; - __f.read_to_end(&mut __bytes) + (*a0).read_to_end(&mut __bytes) .expect("couldn't read the file"); let mut __buf: Vec = __bytes.iter().map(|&b| b as libc::c_char).collect(); __buf.push(0); diff --git a/tests/unit/fd_close_standard.c b/tests/unit/fd_close_standard.c new file mode 100644 index 0000000000..071065e559 --- /dev/null +++ b/tests/unit/fd_close_standard.c @@ -0,0 +1,15 @@ +#include +#include +#include + +int main(void) { + for (int fd = 0; fd < 3; ++fd) { + assert(close(fd) == 0); + int reopened = open("/dev/zero", O_RDONLY); + assert(reopened == fd); + unsigned char buf[3] = {1, 127, 255}; + assert(read(reopened, buf, sizeof(buf)) == sizeof(buf)); + assert(buf[0] == 0 && buf[1] == 0 && buf[2] == 0); + } + return 0; +} diff --git a/tests/unit/iostream_closed.cpp b/tests/unit/iostream_closed.cpp new file mode 100644 index 0000000000..025d103708 --- /dev/null +++ b/tests/unit/iostream_closed.cpp @@ -0,0 +1,12 @@ +#include +#include +#include + +int main() { + assert(close(1) == 0); + std::cout << "not written\n"; + std::cerr << "still open\n"; + assert(close(2) == 0); + std::cerr << "not written either\n"; + return 0; +} diff --git a/tests/unit/out/refcount/cout_alias.rs b/tests/unit/out/refcount/cout_alias.rs index 89bb455ccd..b7d486a39a 100644 --- a/tests/unit/out/refcount/cout_alias.rs +++ b/tests/unit/out/refcount/cout_alias.rs @@ -12,13 +12,13 @@ pub fn main() { } fn main_0() -> i32 { let mut cond: bool = true; - let mut os1: Ptr = if cond { + let mut os1: Ptr = if cond { (libcc2rs::cout()) } else { (libcc2rs::cerr()) }; write!(os1, "hello\n",); - let os2: Ptr = if cond { + let os2: Ptr = if cond { (libcc2rs::cout()) } else { (libcc2rs::cerr()) diff --git a/tests/unit/out/refcount/fd_close_standard.rs b/tests/unit/out/refcount/fd_close_standard.rs new file mode 100644 index 0000000000..a77e2bcfe4 --- /dev/null +++ b/tests/unit/out/refcount/fd_close_standard.rs @@ -0,0 +1,76 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let mut fd: i32 = 0; + 'loop_: while (((fd < 3) as i32) != 0) { + assert!((((FdRegistry::close(fd) == 0) as i32) != 0)); + let mut reopened: i32 = { + let __mode = match Box::<[VaArg]>::from([]).first() { + Some(__m) => { + nix::sys::stat::Mode::from_bits_truncate(i32::get(__m) as ::libc::mode_t) + } + None => nix::sys::stat::Mode::empty(), + }; + match nix::fcntl::open( + Ptr::::from_string_literal(b"/dev/zero") + .to_rust_string() + .as_str(), + nix::fcntl::OFlag::from_bits_retain(::libc::O_RDONLY), + __mode, + ) { + Ok(__ofd) => FdRegistry::register(__ofd), + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } + }; + assert!((((reopened == fd) as i32) != 0)); + let buf: Value> = Rc::new(RefCell::new(Box::new([1_u8, 127_u8, 255_u8]))); + assert!( + ((((match ({ ((buf.as_pointer() as Ptr::) as Ptr).to_any() },) { + (mut __tmp_0,) => { + match FdRegistry::with_fd(reopened, |__fd| { + __tmp_0 + .reinterpret_cast::() + .with_slice_mut(::std::mem::size_of::<[u8; 3]>(), |__buf| { + nix::unistd::read(__fd, __buf) + }) + }) { + Ok(__n) => __n as isize, + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } + } + } as usize) + == ::std::mem::size_of::<[u8; 3]>()) as i32) + != 0) + ); + assert!( + ({ + (((((((((((*buf.borrow())[(0) as usize] as i32) == 0) as i32) != 0) + && (((((*buf.borrow())[(1) as usize] as i32) == 0) as i32) != 0)) + as i32) + != 0) + && (((((*buf.borrow())[(2) as usize] as i32) == 0) as i32) != 0)) + as i32) + != 0) + }) + ); + fd.prefix_inc(); + } + return 0; +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/iostream_closed.rs b/tests/unit/out/refcount/iostream_closed.rs new file mode 100644 index 0000000000..3465c534c9 --- /dev/null +++ b/tests/unit/out/refcount/iostream_closed.rs @@ -0,0 +1,21 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!((FdRegistry::close(1) == 0)); + write!(libcc2rs::cout(), "not written\n",); + write!(libcc2rs::cerr(), "still open\n",); + assert!((FdRegistry::close(2) == 0)); + write!(libcc2rs::cerr(), "not written either\n",); + return 0; +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/stdcopy_ostream.rs b/tests/unit/out/refcount/stdcopy_ostream.rs index 1355b32930..06df527282 100644 --- a/tests/unit/out/refcount/stdcopy_ostream.rs +++ b/tests/unit/out/refcount/stdcopy_ostream.rs @@ -20,26 +20,99 @@ fn main_0() -> i32 { b"test_stdcopy_ostream.txt\0", ))); { - let ofs: Value<::std::fs::File> = Rc::new(RefCell::new( + let ofs: Value = Rc::new(RefCell::new( ::std::fs::File::create((file.as_pointer() as Ptr).to_string()) + .map(libcc2rs::CppStream::from) .expect("Failed to open file"), )); - match ({ RandomAccessIterator::Vec(str.decay()) }, { - RandomAccessIterator::Vec(str.decay().to_last()) - }) { - (mut __tmp_0, mut __tmp_1) => { + let mut out: Ptr = match ( + { RandomAccessIterator::Vec(str.decay()) }, + { RandomAccessIterator::Vec(str.decay().to_last()) }, + { ofs.as_pointer() }, + ) { + (mut __tmp_0, mut __tmp_1, mut __tmp_2) => { let mut __it = (__tmp_0).clone(); - let __out = &mut (*ofs.borrow_mut()); let mut __bytes: Vec = Vec::new(); while __it != __tmp_1 { __bytes.push(__it.iter_deref().read() as u8); __it.iter_inc(); } - __out.write_all(&__bytes); - __out.try_clone().unwrap() + __tmp_2.write_all(&__bytes); + (__tmp_2).clone() } }; + let mut copy: Ptr = (out).clone(); + match ( + { RandomAccessIterator::Vec(str.decay()) }, + { RandomAccessIterator::Vec(str.decay().to_last()) }, + { (copy).clone() }, + ) { + (mut __tmp_3, mut __tmp_4, mut __tmp_5) => { + let mut __it = (__tmp_3).clone(); + let mut __bytes: Vec = Vec::new(); + while __it != __tmp_4 { + __bytes.push(__it.iter_deref().read() as u8); + __it.iter_inc(); + } + __tmp_5.write_all(&__bytes); + (__tmp_5).clone() + } + }; + } + let mut fd: i32 = { + let __mode = match Box::<[VaArg]>::from([]).first() { + Some(__m) => nix::sys::stat::Mode::from_bits_truncate(i32::get(__m) as ::libc::mode_t), + None => nix::sys::stat::Mode::empty(), + }; + match nix::fcntl::open( + (file.as_pointer() as Ptr).to_rust_string().as_str(), + nix::fcntl::OFlag::from_bits_retain(::libc::O_RDONLY), + __mode, + ) { + Ok(__ofd) => FdRegistry::register(__ofd), + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } + }; + let buf: Value> = Rc::new(RefCell::new(Box::new([ + 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, 0_i8, + ]))); + let mut i: i32 = 0; + 'loop_: while (i < 2) { + assert!( + ({ + ((match ({ ((buf.as_pointer() as Ptr) as Ptr).to_any() }, { + ((*str.borrow()).len() - 1) + }) { + (mut __tmp_6, mut __tmp_7) => { + match FdRegistry::with_fd(fd, |__fd| { + __tmp_6 + .reinterpret_cast::() + .with_slice_mut(__tmp_7, |__buf| nix::unistd::read(__fd, __buf)) + }) { + Ok(__n) => __n as isize, + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } + } + } as usize) + == ((*str.borrow()).len() - 1)) + }) + ); + assert!( + ({ + (buf.as_pointer() as Ptr).with_c_str(|__s| { + (*str.borrow())[..(*str.borrow()).len().saturating_sub(1)] == *__s + }) + }) + ); + i.prefix_inc(); } + assert!((FdRegistry::close(fd) == 0)); match nix::unistd::unlink((file.as_pointer() as Ptr).to_rust_string().as_str()) { Ok(()) => 0, Err(__e) => { diff --git a/tests/unit/out/unsafe/cout_alias.rs b/tests/unit/out/unsafe/cout_alias.rs index 25c0ec7bc2..8052097b31 100644 --- a/tests/unit/out/unsafe/cout_alias.rs +++ b/tests/unit/out/unsafe/cout_alias.rs @@ -14,16 +14,16 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut cond: bool = true; - let mut os1: *mut std::fs::File = if cond { - (&raw mut (*libcc2rs::cout_unsafe()) as *mut std::fs::File) + let mut os1: *mut libcc2rs::CppStream = if cond { + (&raw mut (*libcc2rs::cout_unsafe()) as *mut libcc2rs::CppStream) } else { - (&raw mut (*libcc2rs::cerr_unsafe()) as *mut std::fs::File) + (&raw mut (*libcc2rs::cerr_unsafe()) as *mut libcc2rs::CppStream) }; write!((*os1), "hello\n",); - let os2: *mut std::fs::File = if cond { - (&raw mut (*libcc2rs::cout_unsafe()) as *mut std::fs::File) + let os2: *mut libcc2rs::CppStream = if cond { + (&raw mut (*libcc2rs::cout_unsafe()) as *mut libcc2rs::CppStream) } else { - (&raw mut (*libcc2rs::cerr_unsafe()) as *mut std::fs::File) + (&raw mut (*libcc2rs::cerr_unsafe()) as *mut libcc2rs::CppStream) }; write!((*os2), "hello\n",); return 0; diff --git a/tests/unit/out/unsafe/fd_close_standard.rs b/tests/unit/out/unsafe/fd_close_standard.rs new file mode 100644 index 0000000000..3f9b4612c3 --- /dev/null +++ b/tests/unit/out/unsafe/fd_close_standard.rs @@ -0,0 +1,47 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut fd: i32 = 0; + 'loop_: while ((((fd) < (3)) as i32) != 0) { + assert!(((((libc::close(fd)) == (0)) as i32) != 0)); + let mut reopened: i32 = (unsafe { + libc::open( + (c"/dev/zero".as_ptr().cast_mut()).cast_const() as *const i8, + ::libc::O_RDONLY as i32, + ) + }); + assert!(((((reopened) == (fd)) as i32) != 0)); + let mut buf: [u8; 3] = [1_u8, 127_u8, 255_u8]; + assert!( + ((((libc::read( + reopened, + (buf.as_mut_ptr() as *mut ::libc::c_void), + ::std::mem::size_of::<[u8; 3]>() + ) as usize) + == (::std::mem::size_of::<[u8; 3]>())) as i32) + != 0) + ); + assert!( + ((((((((((buf[(0) as usize] as i32) == (0)) as i32) != 0) + && ((((buf[(1) as usize] as i32) == (0)) as i32) != 0)) as i32) + != 0) + && ((((buf[(2) as usize] as i32) == (0)) as i32) != 0)) as i32) + != 0) + ); + fd.prefix_inc(); + } + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/iostream_closed.rs b/tests/unit/out/unsafe/iostream_closed.rs new file mode 100644 index 0000000000..607a121c1a --- /dev/null +++ b/tests/unit/out/unsafe/iostream_closed.rs @@ -0,0 +1,23 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!(((libc::close(1)) == (0))); + write!((*libcc2rs::cout_unsafe()), "not written\n",); + write!((*libcc2rs::cerr_unsafe()), "still open\n",); + assert!(((libc::close(2)) == (0))); + write!((*libcc2rs::cerr_unsafe()), "not written either\n",); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/stdcopy_ostream.rs b/tests/unit/out/unsafe/stdcopy_ostream.rs index 298056cb50..3a35cbcc34 100644 --- a/tests/unit/out/unsafe/stdcopy_ostream.rs +++ b/tests/unit/out/unsafe/stdcopy_ostream.rs @@ -23,25 +23,86 @@ unsafe fn main_0() -> i32 { .to_vec(); let file: [libc::c_char; 25] = std::mem::transmute(*b"test_stdcopy_ostream.txt\0"); { - let mut ofs: ::std::fs::File = + let mut ofs: libcc2rs::CppStream = ::std::fs::File::create(::std::ffi::CStr::from_ptr(file.as_ptr()).to_str().unwrap()) + .map(libcc2rs::CppStream::from) .unwrap(); - match ({ UnsafeRandomAccessIterator::Vec(str.as_mut_ptr()) }, { - UnsafeRandomAccessIterator::Vec(str.as_mut_ptr().add(str.len() - 1)) - }) { - (mut __tmp_0, mut __tmp_1) => { + let mut out: *mut libcc2rs::CppStream = match ( + { UnsafeRandomAccessIterator::Vec(str.as_mut_ptr()) }, + { UnsafeRandomAccessIterator::Vec(str.as_mut_ptr().add(str.len() - 1)) }, + { &mut ofs }, + ) { + (mut __tmp_0, mut __tmp_1, mut __tmp_2) => { let mut __it = (__tmp_0).clone(); - let __out = &mut ofs; let mut __bytes: Vec = Vec::new(); while __it != __tmp_1 { __bytes.push(*__it.iter_deref() as u8); __it.iter_inc(); } - __out.write_all(&__bytes); - __out.try_clone().unwrap() + (*__tmp_2).write_all(&__bytes); + __tmp_2 } }; + let mut copy: *mut libcc2rs::CppStream = out; + match ( + { UnsafeRandomAccessIterator::Vec(str.as_mut_ptr()) }, + { UnsafeRandomAccessIterator::Vec(str.as_mut_ptr().add(str.len() - 1)) }, + { copy }, + ) { + (mut __tmp_3, mut __tmp_4, mut __tmp_5) => { + let mut __it = (__tmp_3).clone(); + let mut __bytes: Vec = Vec::new(); + while __it != __tmp_4 { + __bytes.push(*__it.iter_deref() as u8); + __it.iter_inc(); + } + (*__tmp_5).write_all(&__bytes); + __tmp_5 + } + }; + } + let mut fd: i32 = (unsafe { libc::open(file.as_ptr() as *const i8, ::libc::O_RDONLY as i32) }); + let mut buf: [libc::c_char; 15] = [ + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + (0 as libc::c_char), + ]; + let mut i: i32 = 0; + 'loop_: while ((i) < (2)) { + assert!( + ((libc::read( + fd, + (buf.as_mut_ptr() as *mut ::libc::c_void), + (str.len() - 1) + ) as usize) + == (str.len() - 1)) + ); + assert!(match ({ (buf.as_mut_ptr()).cast_const() },) { + (mut __tmp_6,) => { + str == { + std::slice::from_raw_parts( + __tmp_6, + (0..).take_while(|&i| *__tmp_6.add(i) != 0).count() + 1, + ) + .to_vec() + } + } + }); + i.prefix_inc(); } + assert!(((libc::close(fd)) == (0))); libc::unlink(file.as_ptr()); return 0; } diff --git a/tests/unit/stdcopy_ostream.cpp b/tests/unit/stdcopy_ostream.cpp index 523188cfbf..bdd057383c 100644 --- a/tests/unit/stdcopy_ostream.cpp +++ b/tests/unit/stdcopy_ostream.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #include #include @@ -9,8 +11,18 @@ int main() { const char file[] = "test_stdcopy_ostream.txt"; { std::ofstream ofs(file, std::ios::binary); - std::copy(str.begin(), str.end(), std::ostream_iterator(ofs)); + auto out = + std::copy(str.begin(), str.end(), std::ostream_iterator(ofs)); + std::ostream_iterator copy(out); + std::copy(str.begin(), str.end(), copy); } + int fd = open(file, O_RDONLY); + char buf[sizeof "Hello, world!\n"] = {}; + for (int i = 0; i < 2; ++i) { + assert(read(fd, buf, str.size()) == str.size()); + assert(str == buf); + } + assert(close(fd) == 0); unlink(file); return 0; } From a5c069341cc212c5195cfbcc8adbf60756fb4b2c Mon Sep 17 00:00:00 2001 From: Perfloop Agent Date: Mon, 2 Nov 2026 12:39:16 +0000 Subject: [PATCH 2/2] Map stream-buffer iterators to shared stream references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Perfloop Agent Signed-off-by: Tomás Senart --- rules/fstream/src.cpp | 1 + rules/fstream/tgt_refcount.rs | 4 + rules/fstream/tgt_unsafe.rs | 4 + tests/unit/out/refcount/stdcopy_istream.rs | 122 +++++++++++++++++++++ tests/unit/out/unsafe/stdcopy_istream.rs | 88 +++++++++++++++ tests/unit/stdcopy_istream.cpp | 38 +++++++ 6 files changed, 257 insertions(+) create mode 100644 tests/unit/out/refcount/stdcopy_istream.rs create mode 100644 tests/unit/out/unsafe/stdcopy_istream.rs create mode 100644 tests/unit/stdcopy_istream.cpp diff --git a/rules/fstream/src.cpp b/rules/fstream/src.cpp index 05905f6fe8..7ce5be45fe 100644 --- a/rules/fstream/src.cpp +++ b/rules/fstream/src.cpp @@ -9,6 +9,7 @@ using t1 = std::ifstream; using t2 = std::ofstream; template using t3 = std::ostream_iterator; using t4 = std::ostream_iterator::ostream_type; +template using t5 = std::istreambuf_iterator; std::ofstream f1(const char *filename, std::ios_base::openmode mode) { return std::ofstream(filename, mode); diff --git a/rules/fstream/tgt_refcount.rs b/rules/fstream/tgt_refcount.rs index 920e4f461c..7654221f63 100644 --- a/rules/fstream/tgt_refcount.rs +++ b/rules/fstream/tgt_refcount.rs @@ -21,6 +21,10 @@ fn t3() -> Ptr { Ptr::null() } +fn t5() -> Ptr { + Ptr::null() +} + fn f2(a0: Ptr) -> Ptr { a0 } diff --git a/rules/fstream/tgt_unsafe.rs b/rules/fstream/tgt_unsafe.rs index 84f0052db5..ddc6c365b5 100644 --- a/rules/fstream/tgt_unsafe.rs +++ b/rules/fstream/tgt_unsafe.rs @@ -29,6 +29,10 @@ unsafe fn f1(a0: *const libc::c_char) -> libcc2rs::CppStream { .unwrap() } +fn t5() -> *mut libcc2rs::CppStream { + std::ptr::null_mut() +} + unsafe fn f2(a0: *mut libcc2rs::CppStream) -> *mut libcc2rs::CppStream { a0 } diff --git a/tests/unit/out/refcount/stdcopy_istream.rs b/tests/unit/out/refcount/stdcopy_istream.rs new file mode 100644 index 0000000000..87326f9c69 --- /dev/null +++ b/tests/unit/out/refcount/stdcopy_istream.rs @@ -0,0 +1,122 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +thread_local!( + pub static data_0: Value> = Rc::new(RefCell::new(Box::new([0_u8, 127_u8, 255_u8]))); +); +pub fn check_1(s: Ptr>) { + assert!(({ ({ ((*s.upgrade().deref()).len() - 1) } == { ::std::mem::size_of::<[u8; 3]>() }) })); + let mut i: u32 = 0_u32; + 'loop_: while ((i as usize) < ::std::mem::size_of::<[u8; 3]>()) { + assert!( + ({ (((index!(s, (i as usize)).read()) as u8) as i32) } == { + (({ + let __idx0 = (i) as usize; + data_0.with(|rc| rc.borrow()[__idx0]) + }) as i32) + }) + ); + i.prefix_inc(); + } +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let file: Value> = Rc::new(RefCell::new(i8::array_from_literal( + b"test_stdcopy_istream.txt\0", + ))); + let mut fd: i32 = { + let __mode = match Box::<[VaArg]>::from([(384).into()]).first() { + Some(__m) => nix::sys::stat::Mode::from_bits_truncate(i32::get(__m) as ::libc::mode_t), + None => nix::sys::stat::Mode::empty(), + }; + match nix::fcntl::open( + (file.as_pointer() as Ptr).to_rust_string().as_str(), + nix::fcntl::OFlag::from_bits_retain( + ((::libc::O_WRONLY | ::libc::O_CREAT) | ::libc::O_TRUNC), + ), + __mode, + ) { + Ok(__ofd) => FdRegistry::register(__ofd), + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } + }; + assert!((fd >= 0)); + assert!( + ((match FdRegistry::with_fd(fd, |__fd| { + ((data_0.with(|v| v.as_pointer()) as Ptr) as Ptr) + .to_any() + .reinterpret_cast::() + .with_slice(::std::mem::size_of::<[u8; 3]>(), |__buf| { + nix::unistd::write(__fd, __buf) + }) + }) { + Ok(__n) => __n as isize, + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + } as usize) + == ::std::mem::size_of::<[u8; 3]>()) + ); + assert!((FdRegistry::close(fd) == 0)); + { + let f: Value = Rc::new(RefCell::new( + ::std::fs::File::open((file.as_pointer() as Ptr).to_string()) + .map(libcc2rs::CppStream::from) + .expect("Failed to open file"), + )); + let s: Value> = Rc::new(RefCell::new({ + use std::io::Read; + let mut __bytes: Vec = Vec::new(); + (f.as_pointer()) + .with_mut(|__f| __f.read_to_end(&mut __bytes)) + .expect("couldn't read the file"); + __bytes.push(0); + CChar::from_byte_vec(__bytes) + })); + ({ check_1(s.as_pointer()) }); + } + { + let f: Value = Rc::new(RefCell::new( + ::std::fs::File::open((file.as_pointer() as Ptr).to_string()) + .map(libcc2rs::CppStream::from) + .expect("Failed to open file"), + )); + let mut begin: Ptr = f.as_pointer(); + let mut copy: Ptr = (begin).clone(); + let mut end: Ptr = Ptr::null(); + let s: Value> = Rc::new(RefCell::new({ + use std::io::Read; + let mut __bytes: Vec = Vec::new(); + (copy) + .clone() + .with_mut(|__f| __f.read_to_end(&mut __bytes)) + .expect("couldn't read the file"); + __bytes.push(0); + CChar::from_byte_vec(__bytes) + })); + ({ check_1(s.as_pointer()) }); + } + match nix::unistd::unlink((file.as_pointer() as Ptr).to_rust_string().as_str()) { + Ok(()) => 0, + Err(__e) => { + libcc2rs::cpp2rust_errno().write(__e as i32); + -1 + } + }; + return 0; +} +pub fn __cpp2rust_init_globals() { + let _ = data_0.with(|_| ()); +} diff --git a/tests/unit/out/unsafe/stdcopy_istream.rs b/tests/unit/out/unsafe/stdcopy_istream.rs new file mode 100644 index 0000000000..81398940b4 --- /dev/null +++ b/tests/unit/out/unsafe/stdcopy_istream.rs @@ -0,0 +1,88 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub static mut data_0: std::cell::LazyCell<[u8; 3]> = + std::cell::LazyCell::new(|| unsafe { [0_u8, 127_u8, 255_u8] }); +pub unsafe fn check_1(s: *const Vec) { + assert!((((*s).len() - 1) == (::std::mem::size_of::<[u8; 3]>()))); + let mut i: u32 = 0_u32; + 'loop_: while ((i as usize) < (::std::mem::size_of::<[u8; 3]>())) { + assert!( + ((((&(*s))[(i as usize)] as u8) as i32) + == ((*std::cell::LazyCell::force_mut(&mut *&raw mut data_0))[(i) as usize] as i32)) + ); + i.prefix_inc(); + } +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let file: [libc::c_char; 25] = std::mem::transmute(*b"test_stdcopy_istream.txt\0"); + let mut fd: i32 = (unsafe { + libc::open( + file.as_ptr() as *const i8, + (((::libc::O_WRONLY) | (::libc::O_CREAT)) | (::libc::O_TRUNC)) as i32, + (384), + ) + }); + assert!(((fd) >= (0))); + assert!( + ((libc::write( + fd, + ((*std::cell::LazyCell::force_mut(&mut *&raw mut data_0)).as_ptr() + as *const ::libc::c_void), + ::std::mem::size_of::<[u8; 3]>() + ) as usize) + == (::std::mem::size_of::<[u8; 3]>())) + ); + assert!(((libc::close(fd)) == (0))); + { + let mut f: libcc2rs::CppStream = + ::std::fs::File::open(::std::ffi::CStr::from_ptr(file.as_ptr()).to_str().unwrap()) + .map(libcc2rs::CppStream::from) + .unwrap(); + let mut s: Vec = { + let mut __bytes: Vec = Vec::new(); + (*(&mut f as *mut libcc2rs::CppStream)) + .read_to_end(&mut __bytes) + .expect("couldn't read the file"); + let mut __buf: Vec = __bytes.iter().map(|&b| b as libc::c_char).collect(); + __buf.push(0); + __buf + }; + (unsafe { check_1(&s) }); + } + { + let mut f: libcc2rs::CppStream = + ::std::fs::File::open(::std::ffi::CStr::from_ptr(file.as_ptr()).to_str().unwrap()) + .map(libcc2rs::CppStream::from) + .unwrap(); + let mut begin: *mut libcc2rs::CppStream = (&mut f as *mut libcc2rs::CppStream); + let mut copy: *mut libcc2rs::CppStream = (begin).clone(); + let mut end: *mut libcc2rs::CppStream = std::ptr::null_mut(); + let mut s: Vec = { + let mut __bytes: Vec = Vec::new(); + (*(copy).clone()) + .read_to_end(&mut __bytes) + .expect("couldn't read the file"); + let mut __buf: Vec = __bytes.iter().map(|&b| b as libc::c_char).collect(); + __buf.push(0); + __buf + }; + (unsafe { check_1(&s) }); + } + libc::unlink(file.as_ptr()); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() { + std::cell::LazyCell::force(&*&raw const data_0); +} diff --git a/tests/unit/stdcopy_istream.cpp b/tests/unit/stdcopy_istream.cpp new file mode 100644 index 0000000000..63a5d57d15 --- /dev/null +++ b/tests/unit/stdcopy_istream.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include + +const unsigned char data[] = {0, 127, 255}; + +void check(const std::string &s) { + assert(s.size() == sizeof(data)); + for (unsigned i = 0; i < sizeof(data); ++i) + assert(static_cast(s[i]) == data[i]); +} + +int main() { + const char file[] = "test_stdcopy_istream.txt"; + int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0600); + assert(fd >= 0); + assert(write(fd, data, sizeof(data)) == sizeof(data)); + assert(close(fd) == 0); + { + std::ifstream f(file, std::ios::binary); + std::string s((std::istreambuf_iterator(f.rdbuf())), + std::istreambuf_iterator()); + check(s); + } + { + std::ifstream f(file, std::ios::binary); + std::istreambuf_iterator begin(f.rdbuf()); + std::istreambuf_iterator copy(begin); + std::istreambuf_iterator end; + std::string s(copy, end); + check(s); + } + unlink(file); + return 0; +}