@@ -7,7 +7,8 @@ use crate::char::{EscapeDebugExtArgs, MAX_LEN_UTF8};
77use crate :: marker:: { PhantomData , PointeeSized } ;
88use crate :: num:: fmt as numfmt;
99use crate :: ops:: Deref ;
10- use crate :: { iter, result, str} ;
10+ use crate :: ptr:: NonNull ;
11+ use crate :: { iter, mem, result, str} ;
1112
1213mod builders;
1314#[ cfg( not( no_fp_fmt_parse) ) ]
@@ -616,15 +617,8 @@ impl<'a> Formatter<'a> {
616617#[ stable( feature = "rust1" , since = "1.0.0" ) ]
617618#[ derive( Copy , Clone ) ]
618619pub struct Arguments < ' a > {
619- // Format string pieces to print.
620- pieces : & ' a [ & ' static str ] ,
621-
622- // Placeholder specs, or `None` if all specs are default (as in "{}{}").
623- fmt : Option < & ' a [ rt:: Placeholder ] > ,
624-
625- // Dynamic arguments for interpolation, to be interleaved with string
626- // pieces. (Every argument is preceded by a string piece.)
627- args : & ' a [ rt:: Argument < ' a > ] ,
620+ template : NonNull < u8 > ,
621+ args : NonNull < rt:: Argument < ' a > > ,
628622}
629623
630624#[ doc( hidden) ]
@@ -636,20 +630,49 @@ impl<'a> Arguments<'a> {
636630 /// when using `format!`. Note: this is neither the lower nor upper bound.
637631 #[ inline]
638632 pub fn estimated_capacity ( & self ) -> usize {
639- let pieces_length: usize = self . pieces . iter ( ) . map ( |x| x. len ( ) ) . sum ( ) ;
633+ if let Some ( s) = self . as_str ( ) {
634+ return s. len ( ) ;
635+ }
636+ // Iterate over the template, counting the length of literal pieces.
637+ let mut length = 0usize ;
638+ let mut starts_with_placeholder = false ;
639+ let mut template = self . template ;
640+ loop {
641+ // SAFETY: We can assume the template is valid.
642+ unsafe {
643+ let n = template. read ( ) ;
644+ if n == 0 {
645+ // End of template.
646+ break ;
647+ } else if n < 128 {
648+ // Literal string piece.
649+ length += n as usize ;
650+ template = template. add ( 1 + n as usize ) ;
651+ } else {
652+ // Placeholder piece.
653+ if length == 0 {
654+ starts_with_placeholder = true ;
655+ }
656+ // Skip remainder of placeholder:
657+ let skip = ( n & 1 == 1 ) as usize * 4
658+ + ( n & 2 == 2 ) as usize * 2
659+ + ( n & 4 == 4 ) as usize * 2
660+ + ( n & 8 == 8 ) as usize * 2 ;
661+ template = template. add ( 1 + skip as usize ) ;
662+ }
663+ }
664+ }
640665
641- if self . args . is_empty ( ) {
642- pieces_length
643- } else if !self . pieces . is_empty ( ) && self . pieces [ 0 ] . is_empty ( ) && pieces_length < 16 {
644- // If the format string starts with an argument,
666+ if starts_with_placeholder && length < 16 {
667+ // If the format string starts with a placeholder,
645668 // don't preallocate anything, unless length
646- // of pieces is significant.
669+ // of literal pieces is significant.
647670 0
648671 } else {
649- // There are some arguments , so any additional push
672+ // There are some placeholders , so any additional push
650673 // will reallocate the string. To avoid that,
651674 // we're "pre-doubling" the capacity here.
652- pieces_length . checked_mul ( 2 ) . unwrap_or ( 0 )
675+ length . wrapping_mul ( 2 )
653676 }
654677 }
655678}
@@ -702,10 +725,20 @@ impl<'a> Arguments<'a> {
702725 #[ must_use]
703726 #[ inline]
704727 pub const fn as_str ( & self ) -> Option < & ' static str > {
705- match ( self . pieces , self . args ) {
706- ( [ ] , [ ] ) => Some ( "" ) ,
707- ( [ s] , [ ] ) => Some ( s) ,
708- _ => None ,
728+ // SAFETY: During const eval, `self.args` must have come from a usize,
729+ // not a pointer, because that's the only way to creat a fmt::Arguments in const.
730+ // Outside const eval, transmuting a pointer to a usize is fine.
731+ let bits: usize = unsafe { mem:: transmute ( self . args ) } ;
732+ if bits & 1 == 1 {
733+ // SAFETY: This fmt::Arguments stores a &'static str.
734+ Some ( unsafe {
735+ str:: from_utf8_unchecked ( crate :: slice:: from_raw_parts (
736+ self . template . as_ptr ( ) ,
737+ bits >> 1 ,
738+ ) )
739+ } )
740+ } else {
741+ None
709742 }
710743 }
711744
@@ -1448,86 +1481,96 @@ pub trait UpperExp: PointeeSized {
14481481///
14491482/// [`write!`]: crate::write!
14501483#[ stable( feature = "rust1" , since = "1.0.0" ) ]
1451- pub fn write ( output : & mut dyn Write , args : Arguments < ' _ > ) -> Result {
1452- let mut formatter = Formatter :: new ( output, FormattingOptions :: new ( ) ) ;
1453- let mut idx = 0 ;
1454-
1455- match args. fmt {
1456- None => {
1457- // We can use default formatting parameters for all arguments.
1458- for ( i, arg) in args. args . iter ( ) . enumerate ( ) {
1459- // SAFETY: args.args and args.pieces come from the same Arguments,
1460- // which guarantees the indexes are always within bounds.
1461- let piece = unsafe { args. pieces . get_unchecked ( i) } ;
1462- if !piece. is_empty ( ) {
1463- formatter. buf . write_str ( * piece) ?;
1464- }
1465-
1466- // SAFETY: There are no formatting parameters and hence no
1467- // count arguments.
1468- unsafe {
1469- arg. fmt ( & mut formatter) ?;
1470- }
1471- idx += 1 ;
1472- }
1473- }
1474- Some ( fmt) => {
1475- // Every spec has a corresponding argument that is preceded by
1476- // a string piece.
1477- for ( i, arg) in fmt. iter ( ) . enumerate ( ) {
1478- // SAFETY: fmt and args.pieces come from the same Arguments,
1479- // which guarantees the indexes are always within bounds.
1480- let piece = unsafe { args. pieces . get_unchecked ( i) } ;
1481- if !piece. is_empty ( ) {
1482- formatter. buf . write_str ( * piece) ?;
1483- }
1484- // SAFETY: arg and args.args come from the same Arguments,
1485- // which guarantees the indexes are always within bounds.
1486- unsafe { run ( & mut formatter, arg, args. args ) } ?;
1487- idx += 1 ;
1488- }
1489- }
1484+ pub fn write ( output : & mut dyn Write , fmt : Arguments < ' _ > ) -> Result {
1485+ if let Some ( s) = fmt. as_str ( ) {
1486+ return output. write_str ( s) ;
14901487 }
14911488
1492- // There can be only one trailing string piece left.
1493- if let Some ( piece) = args. pieces . get ( idx) {
1494- formatter. buf . write_str ( * piece) ?;
1495- }
1496-
1497- Ok ( ( ) )
1498- }
1489+ let mut template = fmt. template ;
1490+ let args = fmt. args ;
14991491
1500- unsafe fn run ( fmt : & mut Formatter < ' _ > , arg : & rt:: Placeholder , args : & [ rt:: Argument < ' _ > ] ) -> Result {
1501- let ( width, precision) =
1502- // SAFETY: arg and args come from the same Arguments,
1503- // which guarantees the indexes are always within bounds.
1504- unsafe { ( getcount ( args, & arg. width ) , getcount ( args, & arg. precision ) ) } ;
1492+ let mut arg_index = 0 ;
15051493
1506- let options = FormattingOptions { flags : arg. flags , width, precision } ;
1494+ // This must match the encoding from `expand_format_args` in
1495+ // compiler/rustc_ast_lowering/src/format.rs.
1496+ loop {
1497+ // SAFETY: We can assume the template is valid.
1498+ let n = unsafe {
1499+ let n = template. read ( ) ;
1500+ template = template. add ( 1 ) ;
1501+ n
1502+ } ;
15071503
1508- // Extract the correct argument
1509- debug_assert ! ( arg. position < args. len( ) ) ;
1510- // SAFETY: arg and args come from the same Arguments,
1511- // which guarantees its index is always within bounds.
1512- let value = unsafe { args. get_unchecked ( arg. position ) } ;
1504+ if n == 0 {
1505+ // End of template.
1506+ return Ok ( ( ) ) ;
1507+ } else if n < 128 {
1508+ // Literal string piece of length `n`.
1509+
1510+ // SAFETY: We can assume the strings in the template are valid.
1511+ let s = unsafe {
1512+ let s = crate :: str:: from_raw_parts ( template. as_ptr ( ) , n as usize ) ;
1513+ template = template. add ( n as usize ) ;
1514+ s
1515+ } ;
1516+ output. write_str ( s) ?;
1517+ } else if n == 128 {
1518+ // Placeholder for next argument with default options.
1519+ //
1520+ // Having this as a separate case improves performance for the common case.
1521+
1522+ // SAFETY: We can assume the template only refers to arguments that exist.
1523+ unsafe {
1524+ args. add ( arg_index)
1525+ . as_ref ( )
1526+ . fmt ( & mut Formatter :: new ( output, FormattingOptions :: new ( ) ) ) ?;
1527+ }
1528+ arg_index += 1 ;
1529+ } else {
1530+ // Placeholder with custom options.
15131531
1514- // Set all the formatting options.
1515- fmt. options = options;
1532+ let mut opt = FormattingOptions :: new ( ) ;
15161533
1517- // Then actually do some printing
1518- // SAFETY: this is a placeholder argument.
1519- unsafe { value. fmt ( fmt) }
1520- }
1534+ // SAFETY: We can assume the template is valid.
1535+ unsafe {
1536+ if n & 1 != 0 {
1537+ opt. flags = u32:: from_le_bytes ( template. cast_array ( ) . read ( ) ) ;
1538+ template = template. add ( 4 ) ;
1539+ }
1540+ if n & 2 != 0 {
1541+ opt. width = u16:: from_le_bytes ( template. cast_array ( ) . read ( ) ) ;
1542+ template = template. add ( 2 ) ;
1543+ }
1544+ if n & 4 != 0 {
1545+ opt. precision = u16:: from_le_bytes ( template. cast_array ( ) . read ( ) ) ;
1546+ template = template. add ( 2 ) ;
1547+ }
1548+ if n & 8 != 0 {
1549+ arg_index = usize:: from ( u16:: from_le_bytes ( template. cast_array ( ) . read ( ) ) ) ;
1550+ template = template. add ( 2 ) ;
1551+ }
1552+ }
1553+ if n & 16 != 0 {
1554+ // Dynamic width from a usize argument.
1555+ // SAFETY: We can assume the template only refers to arguments that exist.
1556+ unsafe {
1557+ opt. width = args. add ( opt. width as usize ) . as_ref ( ) . as_u16 ( ) . unwrap_unchecked ( ) ;
1558+ }
1559+ }
1560+ if n & 32 != 0 {
1561+ // Dynamic precision from a usize argument.
1562+ // SAFETY: We can assume the template only refers to arguments that exist.
1563+ unsafe {
1564+ opt. precision =
1565+ args. add ( opt. precision as usize ) . as_ref ( ) . as_u16 ( ) . unwrap_unchecked ( ) ;
1566+ }
1567+ }
15211568
1522- unsafe fn getcount ( args : & [ rt:: Argument < ' _ > ] , cnt : & rt:: Count ) -> u16 {
1523- match * cnt {
1524- rt:: Count :: Is ( n) => n,
1525- rt:: Count :: Implied => 0 ,
1526- rt:: Count :: Param ( i) => {
1527- debug_assert ! ( i < args. len( ) ) ;
1528- // SAFETY: cnt and args come from the same Arguments,
1529- // which guarantees this index is always within bounds.
1530- unsafe { args. get_unchecked ( i) . as_u16 ( ) . unwrap_unchecked ( ) }
1569+ // SAFETY: We can assume the template only refers to arguments that exist.
1570+ unsafe {
1571+ args. add ( arg_index) . as_ref ( ) . fmt ( & mut Formatter :: new ( output, opt) ) ?;
1572+ }
1573+ arg_index += 1 ;
15311574 }
15321575 }
15331576}
0 commit comments