@@ -8,7 +8,8 @@ use crate::fmt;
88use crate :: fs:: File ;
99use crate :: io:: prelude:: * ;
1010use crate :: io:: {
11- self , BorrowedCursor , BufReader , IoSlice , IoSliceMut , LineWriter , Lines , SpecReadByte ,
11+ self , BorrowedCursor , BufReader , BufWriter , IoSlice , IoSliceMut , LineWriter , Lines ,
12+ SpecReadByte ,
1213} ;
1314use crate :: panic:: { RefUnwindSafe , UnwindSafe } ;
1415use crate :: sync:: atomic:: { Atomic , AtomicBool , Ordering } ;
@@ -43,19 +44,19 @@ static OUTPUT_CAPTURE_USED: Atomic<bool> = AtomicBool::new(false);
4344///
4445/// This handle is not synchronized or buffered in any fashion. Constructed via
4546/// the `std::io::stdio::stdin_raw` function.
46- struct StdinRaw ( stdio:: Stdin ) ;
47+ pub ( crate ) struct StdinRaw ( stdio:: Stdin ) ;
4748
4849/// A handle to a raw instance of the standard output stream of this process.
4950///
5051/// This handle is not synchronized or buffered in any fashion. Constructed via
5152/// the `std::io::stdio::stdout_raw` function.
52- struct StdoutRaw ( stdio:: Stdout ) ;
53+ pub ( crate ) struct StdoutRaw ( stdio:: Stdout ) ;
5354
5455/// A handle to a raw instance of the standard output stream of this process.
5556///
5657/// This handle is not synchronized or buffered in any fashion. Constructed via
5758/// the `std::io::stdio::stderr_raw` function.
58- struct StderrRaw ( stdio:: Stderr ) ;
59+ pub ( crate ) struct StderrRaw ( stdio:: Stderr ) ;
5960
6061/// Constructs a new raw handle to the standard input of this process.
6162///
@@ -576,6 +577,76 @@ impl fmt::Debug for StdinLock<'_> {
576577 }
577578}
578579
580+ /// A buffered writer for stdout and stderr.
581+ ///
582+ /// This writer may be either [line-buffered](LineWriter) or [block-buffered](BufWriter), depending
583+ /// on whether the underlying file is a terminal or not.
584+ #[ derive( Debug ) ]
585+ enum StdioBufWriter < W : Write > {
586+ LineBuffered ( LineWriter < W > ) ,
587+ BlockBuffered ( BufWriter < W > ) ,
588+ }
589+
590+ impl < W : Write + IsTerminal > StdioBufWriter < W > {
591+ /// Wraps a writer using the most appropriate buffering method.
592+ ///
593+ /// If `w` is a terminal, then the resulting `StdioBufWriter` will be line-buffered, otherwise
594+ /// it will be block-buffered.
595+ fn new ( w : W ) -> Self {
596+ if w. is_terminal ( ) {
597+ Self :: LineBuffered ( LineWriter :: new ( w) )
598+ } else {
599+ Self :: BlockBuffered ( BufWriter :: new ( w) )
600+ }
601+ }
602+ }
603+
604+ impl < W : Write > StdioBufWriter < W > {
605+ /// Wraps a writer using a block-buffer with the given capacity.
606+ fn with_capacity ( cap : usize , w : W ) -> Self {
607+ Self :: BlockBuffered ( BufWriter :: with_capacity ( cap, w) )
608+ }
609+ }
610+
611+ impl < W : Write > Write for StdioBufWriter < W > {
612+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
613+ match self {
614+ Self :: LineBuffered ( w) => w. write ( buf) ,
615+ Self :: BlockBuffered ( w) => w. write ( buf) ,
616+ }
617+ }
618+ fn write_vectored ( & mut self , bufs : & [ IoSlice < ' _ > ] ) -> io:: Result < usize > {
619+ match self {
620+ Self :: LineBuffered ( w) => w. write_vectored ( bufs) ,
621+ Self :: BlockBuffered ( w) => w. write_vectored ( bufs) ,
622+ }
623+ }
624+ fn is_write_vectored ( & self ) -> bool {
625+ match self {
626+ Self :: LineBuffered ( w) => w. is_write_vectored ( ) ,
627+ Self :: BlockBuffered ( w) => w. is_write_vectored ( ) ,
628+ }
629+ }
630+ fn flush ( & mut self ) -> io:: Result < ( ) > {
631+ match self {
632+ Self :: LineBuffered ( w) => w. flush ( ) ,
633+ Self :: BlockBuffered ( w) => w. flush ( ) ,
634+ }
635+ }
636+ fn write_all ( & mut self , buf : & [ u8 ] ) -> io:: Result < ( ) > {
637+ match self {
638+ Self :: LineBuffered ( w) => w. write_all ( buf) ,
639+ Self :: BlockBuffered ( w) => w. write_all ( buf) ,
640+ }
641+ }
642+ fn write_all_vectored ( & mut self , bufs : & mut [ IoSlice < ' _ > ] ) -> io:: Result < ( ) > {
643+ match self {
644+ Self :: LineBuffered ( w) => w. write_all_vectored ( bufs) ,
645+ Self :: BlockBuffered ( w) => w. write_all_vectored ( bufs) ,
646+ }
647+ }
648+ }
649+
579650/// A handle to the global standard output stream of the current process.
580651///
581652/// Each handle shares a global buffer of data to be written to the standard
@@ -606,10 +677,9 @@ impl fmt::Debug for StdinLock<'_> {
606677/// [`io::stdout`]: stdout
607678#[ stable( feature = "rust1" , since = "1.0.0" ) ]
608679pub struct Stdout {
609- // FIXME: this should be LineWriter or BufWriter depending on the state of
610- // stdout (tty or not). Note that if this is not line buffered it
611- // should also flush-on-panic or some form of flush-on-abort.
612- inner : & ' static ReentrantLock < RefCell < LineWriter < StdoutRaw > > > ,
680+ // FIXME: if this is not line buffered it should flush-on-panic or some
681+ // form of flush-on-abort.
682+ inner : & ' static ReentrantLock < RefCell < StdioBufWriter < StdoutRaw > > > ,
613683}
614684
615685/// A locked reference to the [`Stdout`] handle.
@@ -638,10 +708,10 @@ pub struct Stdout {
638708#[ must_use = "if unused stdout will immediately unlock" ]
639709#[ stable( feature = "rust1" , since = "1.0.0" ) ]
640710pub struct StdoutLock < ' a > {
641- inner : ReentrantLockGuard < ' a , RefCell < LineWriter < StdoutRaw > > > ,
711+ inner : ReentrantLockGuard < ' a , RefCell < StdioBufWriter < StdoutRaw > > > ,
642712}
643713
644- static STDOUT : OnceLock < ReentrantLock < RefCell < LineWriter < StdoutRaw > > > > = OnceLock :: new ( ) ;
714+ static STDOUT : OnceLock < ReentrantLock < RefCell < StdioBufWriter < StdoutRaw > > > > = OnceLock :: new ( ) ;
645715
646716/// Constructs a new handle to the standard output of the current process.
647717///
@@ -716,7 +786,7 @@ static STDOUT: OnceLock<ReentrantLock<RefCell<LineWriter<StdoutRaw>>>> = OnceLoc
716786pub fn stdout ( ) -> Stdout {
717787 Stdout {
718788 inner : STDOUT
719- . get_or_init ( || ReentrantLock :: new ( RefCell :: new ( LineWriter :: new ( stdout_raw ( ) ) ) ) ) ,
789+ . get_or_init ( || ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: new ( stdout_raw ( ) ) ) ) ) ,
720790 }
721791}
722792
@@ -727,7 +797,7 @@ pub fn cleanup() {
727797 let mut initialized = false ;
728798 let stdout = STDOUT . get_or_init ( || {
729799 initialized = true ;
730- ReentrantLock :: new ( RefCell :: new ( LineWriter :: with_capacity ( 0 , stdout_raw ( ) ) ) )
800+ ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: with_capacity ( 0 , stdout_raw ( ) ) ) )
731801 } ) ;
732802
733803 if !initialized {
@@ -736,7 +806,7 @@ pub fn cleanup() {
736806 // might have leaked a StdoutLock, which would
737807 // otherwise cause a deadlock here.
738808 if let Some ( lock) = stdout. try_lock ( ) {
739- * lock. borrow_mut ( ) = LineWriter :: with_capacity ( 0 , stdout_raw ( ) ) ;
809+ * lock. borrow_mut ( ) = StdioBufWriter :: with_capacity ( 0 , stdout_raw ( ) ) ;
740810 }
741811 }
742812}
@@ -1262,7 +1332,18 @@ macro_rules! impl_is_terminal {
12621332 ) * }
12631333}
12641334
1265- impl_is_terminal ! ( File , Stdin , StdinLock <' _>, Stdout , StdoutLock <' _>, Stderr , StderrLock <' _>) ;
1335+ impl_is_terminal ! (
1336+ File ,
1337+ Stdin ,
1338+ StdinLock <' _>,
1339+ StdinRaw ,
1340+ Stdout ,
1341+ StdoutLock <' _>,
1342+ StdoutRaw ,
1343+ Stderr ,
1344+ StderrLock <' _>,
1345+ StderrRaw ,
1346+ ) ;
12661347
12671348#[ unstable(
12681349 feature = "print_internals" ,
0 commit comments