@ -12,14 +12,13 @@
# include "absl/base/port.h"
# include "absl/strings/internal/str_format/float_conversion.h"
# include "absl/strings/numbers.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace str_format_internal {
namespace {
const char kDigit [ 2 ] [ 32 ] = { " 0123456789abcdef " , " 0123456789ABCDEF " } ;
// Reduce *capacity by s.size(), clipped to a 0 minimum.
void ReducePadding ( string_view s , size_t * capacity ) {
* capacity = Excess ( s . size ( ) , * capacity ) ;
@ -48,94 +47,144 @@ struct IsSigned<absl::int128> : std::true_type {};
template < >
struct IsSigned < absl : : uint128 > : std : : false_type { } ;
class ConvertedIntInfo {
// Integral digit printer.
// Call one of the PrintAs* routines after construction once.
// Use with_neg_and_zero/without_neg_or_zero/is_negative to access the results.
class IntDigits {
public :
// Print the unsigned integer as octal.
// Supports unsigned integral types and uint128.
template < typename T >
void PrintAsOct ( T v ) {
static_assert ( ! IsSigned < T > : : value , " " ) ;
char * p = storage_ + sizeof ( storage_ ) ;
do {
* - - p = static_cast < char > ( ' 0 ' + ( static_cast < size_t > ( v ) & 7 ) ) ;
v > > = 3 ;
} while ( v ) ;
start_ = p ;
size_ = storage_ + sizeof ( storage_ ) - p ;
}
// Print the signed or unsigned integer as decimal.
// Supports all integral types.
template < typename T >
ConvertedIntInfo ( T v , ConversionChar conv ) {
using Unsigned = typename MakeUnsigned < T > : : type ;
auto u = static_cast < Unsigned > ( v ) ;
if ( IsNeg ( v ) ) {
is_neg_ = true ;
u = Unsigned { } - u ;
} else {
is_neg_ = false ;
void PrintAsDec ( T v ) {
static_assert ( std : : is_integral < T > : : value , " " ) ;
start_ = storage_ ;
size_ = numbers_internal : : FastIntToBuffer ( v , storage_ ) - storage_ ;
}
void PrintAsDec ( int128 v ) {
auto u = static_cast < uint128 > ( v ) ;
bool add_neg = false ;
if ( v < 0 ) {
add_neg = true ;
u = uint128 { } - u ;
}
UnsignedToStringRight ( u , conv ) ;
PrintAsDec ( u , add_neg ) ;
}
string_view digits ( ) const {
return { end ( ) - size_ , static_cast < size_t > ( size_ ) } ;
void PrintAsDec ( uint128 v , bool add_neg = false ) {
// This function can be sped up if needed. We can call FastIntToBuffer
// twice, or fix FastIntToBuffer to support uint128.
char * p = storage_ + sizeof ( storage_ ) ;
do {
p - = 2 ;
numbers_internal : : PutTwoDigits ( static_cast < size_t > ( v % 100 ) , p ) ;
v / = 100 ;
} while ( v ) ;
if ( p [ 0 ] = = ' 0 ' ) {
// We printed one too many hexits.
+ + p ;
}
if ( add_neg ) {
* - - p = ' - ' ;
}
size_ = storage_ + sizeof ( storage_ ) - p ;
start_ = p ;
}
bool is_neg ( ) const { return is_neg_ ; }
private :
template < typename T , bool IsSigned >
struct IsNegImpl {
static bool Eval ( T v ) { return v < 0 ; }
} ;
// Print the unsigned integer as hex using lowercase.
// Supports unsigned integral types and uint128.
template < typename T >
struct IsNegImpl < T , false > {
static bool Eval ( T ) {
return false ;
void PrintAsHexLower ( T v ) {
static_assert ( ! IsSigned < T > : : value , " " ) ;
char * p = storage_ + sizeof ( storage_ ) ;
do {
p - = 2 ;
constexpr const char * table = numbers_internal : : kHexTable ;
std : : memcpy ( p , table + 2 * ( static_cast < size_t > ( v ) & 0xFF ) , 2 ) ;
if ( sizeof ( T ) = = 1 ) break ;
v > > = 8 ;
} while ( v ) ;
if ( p [ 0 ] = = ' 0 ' ) {
// We printed one too many digits.
+ + p ;
}
} ;
start_ = p ;
size_ = storage_ + sizeof ( storage_ ) - p ;
}
// Print the unsigned integer as hex using uppercase.
// Supports unsigned integral types and uint128.
template < typename T >
bool IsNeg ( T v ) {
return IsNegImpl < T , IsSigned < T > : : value > : : Eval ( v ) ;
void PrintAsHexUpper ( T v ) {
static_assert ( ! IsSigned < T > : : value , " " ) ;
char * p = storage_ + sizeof ( storage_ ) ;
// kHexTable is only lowercase, so do it manually for uppercase.
do {
* - - p = " 0123456789ABCDEF " [ static_cast < size_t > ( v ) & 15 ] ;
v > > = 4 ;
} while ( v ) ;
start_ = p ;
size_ = storage_ + sizeof ( storage_ ) - p ;
}
template < typename T >
void UnsignedToStringRight ( T u , ConversionChar conv ) {
char * p = end ( ) ;
switch ( FormatConversionCharRadix ( conv ) ) {
default :
case 10 :
for ( ; u ; u / = 10 )
* - - p = static_cast < char > ( ' 0 ' + static_cast < size_t > ( u % 10 ) ) ;
break ;
case 8 :
for ( ; u ; u / = 8 )
* - - p = static_cast < char > ( ' 0 ' + static_cast < size_t > ( u % 8 ) ) ;
break ;
case 16 : {
const char * digits = kDigit [ FormatConversionCharIsUpper ( conv ) ? 1 : 0 ] ;
for ( ; u ; u / = 16 ) * - - p = digits [ static_cast < size_t > ( u % 16 ) ] ;
break ;
}
}
size_ = static_cast < int > ( end ( ) - p ) ;
// The printed value including the '-' sign if available.
// For inputs of value `0`, this will return "0"
string_view with_neg_and_zero ( ) const { return { start_ , size_ } ; }
// The printed value not including the '-' sign.
// For inputs of value `0`, this will return "".
string_view without_neg_or_zero ( ) const {
static_assert ( ' - ' < ' 0 ' , " The check below verifies both. " ) ;
size_t advance = start_ [ 0 ] < = ' 0 ' ? 1 : 0 ;
return { start_ + advance , size_ - advance } ;
}
const char * end ( ) const { return storage_ + sizeof ( storage_ ) ; }
char * end ( ) { return storage_ + sizeof ( storage_ ) ; }
bool is_negative ( ) const { return start_ [ 0 ] = = ' - ' ; }
bool is_neg_ ;
int size_ ;
// Max size: 128 bit value as octal -> 43 digits
char storage_ [ 128 / 3 + 1 ] ;
private :
const char * start_ ;
size_t size_ ;
// Max size: 128 bit value as octal -> 43 digits, plus sign char
char storage_ [ 128 / 3 + 1 + 1 ] ;
} ;
// Note: 'o' conversions do not have a base indicator, it's just that
// the '#' flag is specified to modify the precision for 'o' conversions.
string_view BaseIndicator ( const ConvertedIntInfo & info ,
string_view BaseIndicator ( const IntDigits & as_digits ,
const ConversionSpec conv ) {
bool alt = conv . has_alt_flag ( ) ;
int radix = FormatConversionCharRadix ( conv . conversion_char ( ) ) ;
if ( conv . conversion_char ( ) = = ConversionChar : : p )
alt = true ; // always show 0x for %p.
// always show 0x for %p.
bool alt = conv . has_alt_flag ( ) | | conv . conversion_char ( ) = = ConversionChar : : p ;
bool hex = ( conv . conversion_char ( ) = = FormatConversionChar : : x | |
conv . conversion_char ( ) = = FormatConversionChar : : X | |
conv . conversion_char ( ) = = FormatConversionChar : : p ) ;
// From the POSIX description of '#' flag:
// "For x or X conversion specifiers, a non-zero result shall have
// 0x (or 0X) prefixed to it."
if ( alt & & radix = = 16 & & ! info . digits ( ) . empty ( ) ) {
if ( FormatConversionCharIsUpper ( conv . conversion_char ( ) ) ) return " 0X " ;
return " 0x " ;
if ( alt & & hex & & ! as_digits . without_neg_or_zero ( ) . empty ( ) ) {
return conv . conversion_char ( ) = = FormatConversionChar : : X ? " 0X " : " 0x " ;
}
return { } ;
}
string_view SignColumn ( bool neg , const ConversionSpec conv ) {
if ( FormatConversionCharIsSigned ( conv . conversion_char ( ) ) ) {
if ( conv . conversion_char ( ) = = FormatConversionChar : : d | |
conv . conversion_char ( ) = = FormatConversionChar : : i ) {
if ( neg ) return " - " ;
if ( conv . has_show_pos_flag ( ) ) return " + " ;
if ( conv . has_sign_col_flag ( ) ) return " " ;
@ -154,20 +203,20 @@ bool ConvertCharImpl(unsigned char v, const ConversionSpec conv,
return true ;
}
bool ConvertIntImplInner ( const ConvertedIntInfo & info ,
const ConversionSpec conv , FormatSinkImpl * sink ) {
bool ConvertIntImplInnerSlow ( const IntDigits & as_digits ,
const ConversionSpec conv , FormatSinkImpl * sink ) {
// Print as a sequence of Substrings:
// [left_spaces][sign][base_indicator][zeroes][formatted][right_spaces]
size_t fill = 0 ;
if ( conv . width ( ) > = 0 ) fill = conv . width ( ) ;
string_view formatted = info . digits ( ) ;
string_view formatted = as_digits . without_neg_or_zero ( ) ;
ReducePadding ( formatted , & fill ) ;
string_view sign = SignColumn ( info . is_neg ( ) , conv ) ;
string_view sign = SignColumn ( as_digits . is_negative ( ) , conv ) ;
ReducePadding ( sign , & fill ) ;
string_view base_indicator = BaseIndicator ( info , conv ) ;
string_view base_indicator = BaseIndicator ( as_digits , conv ) ;
ReducePadding ( base_indicator , & fill ) ;
int precision = conv . precision ( ) ;
@ -208,35 +257,54 @@ bool ConvertIntImplInner(const ConvertedIntInfo &info,
return true ;
}
template < typename T >
bool ConvertIntImplInner ( T v , const ConversionSpec conv , FormatSinkImpl * sink ) {
ConvertedIntInfo info ( v , conv . conversion_char ( ) ) ;
if ( conv . is_basic ( ) & & ( conv . conversion_char ( ) ! = ConversionChar : : p ) ) {
if ( info . is_neg ( ) ) sink - > Append ( 1 , ' - ' ) ;
if ( info . digits ( ) . empty ( ) ) {
sink - > Append ( 1 , ' 0 ' ) ;
} else {
sink - > Append ( info . digits ( ) ) ;
}
return true ;
}
return ConvertIntImplInner ( info , conv , sink ) ;
}
template < typename T >
bool ConvertIntArg ( T v , const ConversionSpec conv , FormatSinkImpl * sink ) {
if ( FormatConversionCharIsFloat ( conv . conversion_char ( ) ) ) {
return FormatConvertImpl ( static_cast < double > ( v ) , conv , sink ) . value ;
using U = typename MakeUnsigned < T > : : type ;
IntDigits as_digits ;
switch ( conv . conversion_char ( ) ) {
case FormatConversionChar : : c :
return ConvertCharImpl ( static_cast < unsigned char > ( v ) , conv , sink ) ;
case FormatConversionChar : : o :
as_digits . PrintAsOct ( static_cast < U > ( v ) ) ;
break ;
case FormatConversionChar : : x :
as_digits . PrintAsHexLower ( static_cast < U > ( v ) ) ;
break ;
case FormatConversionChar : : X :
as_digits . PrintAsHexUpper ( static_cast < U > ( v ) ) ;
break ;
case FormatConversionChar : : u :
as_digits . PrintAsDec ( static_cast < U > ( v ) ) ;
break ;
case FormatConversionChar : : d :
case FormatConversionChar : : i :
as_digits . PrintAsDec ( v ) ;
break ;
case FormatConversionChar : : a :
case FormatConversionChar : : e :
case FormatConversionChar : : f :
case FormatConversionChar : : g :
case FormatConversionChar : : A :
case FormatConversionChar : : E :
case FormatConversionChar : : F :
case FormatConversionChar : : G :
return ConvertFloatImpl ( static_cast < double > ( v ) , conv , sink ) ;
default :
return false ;
}
if ( conv . conversion_char ( ) = = ConversionChar : : c )
return ConvertCharImpl ( static_cast < unsigned char > ( v ) , conv , sink ) ;
if ( ! FormatConversionCharIsIntegral ( conv . conversion_char ( ) ) ) return false ;
if ( ! FormatConversionCharIsSigned ( conv . conversion_char ( ) ) & &
IsSigned < T > : : value ) {
using U = typename MakeUnsigned < T > : : type ;
return FormatConvertImpl ( static_cast < U > ( v ) , conv , sink ) . value ;
if ( conv . is_basic ( ) ) {
sink - > Append ( as_digits . with_neg_and_zero ( ) ) ;
return true ;
}
return ConvertIntImplInner ( v , conv , sink ) ;
return ConvertIntImplInnerSlow ( as_digits , conv , sink ) ;
}
template < typename T >
@ -296,7 +364,9 @@ ConvertResult<Conv::p> FormatConvertImpl(VoidPtr v, const ConversionSpec conv,
sink - > Append ( " (nil) " ) ;
return { true } ;
}
return { ConvertIntImplInner ( v . value , conv , sink ) } ;
IntDigits as_digits ;
as_digits . PrintAsHexLower ( v . value ) ;
return { ConvertIntImplInnerSlow ( as_digits , conv , sink ) } ;
}
// ==================== Floats ====================