Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
687301c5
Commit
687301c5
authored
Jan 26, 2013
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Parameterize BasicFormatter on char type.
parent
dbfd021a
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
334 additions
and
306 deletions
+334
-306
format.cc
format.cc
+13
-253
format.h
format.h
+284
-24
format_test.cc
format_test.cc
+37
-29
No files found.
format.cc
View file @
687301c5
...
...
@@ -64,32 +64,6 @@ using fmt::StringRef;
namespace
{
template
<
typename
T
>
struct
IsLongDouble
{
enum
{
VALUE
=
0
};
};
template
<
>
struct
IsLongDouble
<
long
double
>
{
enum
{
VALUE
=
1
};
};
const
char
DIGITS
[]
=
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"
;
// Fills the padding around the content and returns the pointer to the
// content area.
char
*
FillPadding
(
char
*
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
char
fill
)
{
std
::
size_t
padding
=
total_size
-
content_size
;
std
::
size_t
left_padding
=
padding
/
2
;
std
::
fill_n
(
buffer
,
left_padding
,
fill
);
buffer
+=
left_padding
;
char
*
content
=
buffer
;
std
::
fill_n
(
buffer
+
content_size
,
padding
-
left_padding
,
fill
);
return
content
;
}
#ifdef _MSC_VER
int
SignBit
(
double
value
)
{
if
(
value
<
0
)
return
1
;
...
...
@@ -101,237 +75,23 @@ int SignBit(double value) {
#endif
}
void
BasicFormatter
::
FormatDecimal
(
char
*
buffer
,
uint64_t
value
,
unsigned
num_digits
)
{
--
num_digits
;
while
(
value
>=
100
)
{
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned
index
=
(
value
%
100
)
*
2
;
value
/=
100
;
buffer
[
num_digits
]
=
DIGITS
[
index
+
1
];
buffer
[
num_digits
-
1
]
=
DIGITS
[
index
];
num_digits
-=
2
;
}
if
(
value
<
10
)
{
*
buffer
=
static_cast
<
char
>
(
'0'
+
value
);
return
;
}
unsigned
index
=
static_cast
<
unsigned
>
(
value
*
2
);
buffer
[
1
]
=
DIGITS
[
index
+
1
];
buffer
[
0
]
=
DIGITS
[
index
];
}
const
char
fmt
::
internal
::
DIGITS
[]
=
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899"
;
void
BasicFormatter
::
ReportUnknownType
(
char
code
,
const
char
*
type
)
{
void
fmt
::
internal
::
ReportUnknownType
(
char
code
,
const
char
*
type
)
{
if
(
std
::
isprint
(
static_cast
<
unsigned
char
>
(
code
)))
{
throw
fmt
::
FormatError
(
fmt
::
str
(
fmt
::
Format
(
"unknown format code '{
0}' for {1
}"
)
<<
code
<<
type
));
fmt
::
Format
(
"unknown format code '{
}' for {
}"
)
<<
code
<<
type
));
}
throw
fmt
::
FormatError
(
fmt
::
str
(
fmt
::
Format
(
"unknown format code '
\\
x{
0:02x}' for {1
}"
)
fmt
::
str
(
fmt
::
Format
(
"unknown format code '
\\
x{
:02x}' for {
}"
)
<<
static_cast
<
unsigned
>
(
code
)
<<
type
));
}
char
*
BasicFormatter
::
PrepareFilledBuffer
(
unsigned
size
,
const
AlignSpec
&
spec
,
char
sign
)
{
unsigned
width
=
spec
.
width
();
if
(
width
<=
size
)
{
char
*
p
=
GrowBuffer
(
size
);
*
p
=
sign
;
return
p
+
size
-
1
;
}
char
*
p
=
GrowBuffer
(
width
);
char
*
end
=
p
+
width
;
Alignment
align
=
spec
.
align
();
if
(
align
==
ALIGN_LEFT
)
{
*
p
=
sign
;
p
+=
size
;
std
::
fill
(
p
,
end
,
spec
.
fill
());
}
else
if
(
align
==
ALIGN_CENTER
)
{
p
=
FillPadding
(
p
,
width
,
size
,
spec
.
fill
());
*
p
=
sign
;
p
+=
size
;
}
else
{
if
(
align
==
ALIGN_NUMERIC
)
{
if
(
sign
)
{
*
p
++
=
sign
;
--
size
;
}
}
else
{
*
(
end
-
size
)
=
sign
;
}
std
::
fill
(
p
,
end
-
size
,
spec
.
fill
());
p
=
end
;
}
return
p
-
1
;
}
template
<
typename
T
>
void
BasicFormatter
::
FormatDouble
(
T
value
,
const
FormatSpec
&
spec
,
int
precision
)
{
// Check type.
char
type
=
spec
.
type
();
bool
upper
=
false
;
switch
(
type
)
{
case
0
:
type
=
'g'
;
break
;
case
'e'
:
case
'f'
:
case
'g'
:
break
;
case
'F'
:
#ifdef _MSC_VER
// MSVC's printf doesn't support 'F'.
type
=
'f'
;
#endif
// Fall through.
case
'E'
:
case
'G'
:
upper
=
true
;
break
;
default:
ReportUnknownType
(
type
,
"double"
);
break
;
}
char
sign
=
0
;
// Use SignBit instead of value < 0 because the latter is always
// false for NaN.
if
(
SignBit
(
value
))
{
sign
=
'-'
;
value
=
-
value
;
}
else
if
(
spec
.
sign_flag
())
{
sign
=
spec
.
plus_flag
()
?
'+'
:
' '
;
}
if
(
value
!=
value
)
{
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
size
=
4
;
const
char
*
nan
=
upper
?
" NAN"
:
" nan"
;
if
(
!
sign
)
{
--
size
;
++
nan
;
}
char
*
out
=
FormatString
(
nan
,
size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
if
(
isinf
(
value
))
{
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
size
=
4
;
const
char
*
inf
=
upper
?
" INF"
:
" inf"
;
if
(
!
sign
)
{
--
size
;
++
inf
;
}
char
*
out
=
FormatString
(
inf
,
size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
size_t
offset
=
buffer_
.
size
();
unsigned
width
=
spec
.
width
();
if
(
sign
)
{
buffer_
.
reserve
(
buffer_
.
size
()
+
std
::
max
(
width
,
1u
));
if
(
width
>
0
)
--
width
;
++
offset
;
}
// Build format string.
enum
{
MAX_FORMAT_SIZE
=
10
};
// longest format: %#-*.*Lg
char
format
[
MAX_FORMAT_SIZE
];
char
*
format_ptr
=
format
;
*
format_ptr
++
=
'%'
;
unsigned
width_for_sprintf
=
width
;
if
(
spec
.
hash_flag
())
*
format_ptr
++
=
'#'
;
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
width_for_sprintf
=
0
;
}
else
{
if
(
spec
.
align
()
==
ALIGN_LEFT
)
*
format_ptr
++
=
'-'
;
if
(
width
!=
0
)
*
format_ptr
++
=
'*'
;
}
if
(
precision
>=
0
)
{
*
format_ptr
++
=
'.'
;
*
format_ptr
++
=
'*'
;
}
if
(
IsLongDouble
<
T
>::
VALUE
)
*
format_ptr
++
=
'L'
;
*
format_ptr
++
=
type
;
*
format_ptr
=
'\0'
;
// Format using snprintf.
for
(;;)
{
size_t
size
=
buffer_
.
capacity
()
-
offset
;
int
n
=
0
;
char
*
start
=
&
buffer_
[
offset
];
if
(
width_for_sprintf
==
0
)
{
n
=
precision
<
0
?
snprintf
(
start
,
size
,
format
,
value
)
:
snprintf
(
start
,
size
,
format
,
precision
,
value
);
}
else
{
n
=
precision
<
0
?
snprintf
(
start
,
size
,
format
,
width_for_sprintf
,
value
)
:
snprintf
(
start
,
size
,
format
,
width_for_sprintf
,
precision
,
value
);
}
if
(
n
>=
0
&&
offset
+
n
<
buffer_
.
capacity
())
{
if
(
sign
)
{
if
((
spec
.
align
()
!=
ALIGN_RIGHT
&&
spec
.
align
()
!=
ALIGN_DEFAULT
)
||
*
start
!=
' '
)
{
*
(
start
-
1
)
=
sign
;
sign
=
0
;
}
else
{
*
(
start
-
1
)
=
spec
.
fill
();
}
++
n
;
}
if
(
spec
.
align
()
==
ALIGN_CENTER
&&
spec
.
width
()
>
static_cast
<
unsigned
>
(
n
))
{
char
*
p
=
GrowBuffer
(
spec
.
width
());
std
::
copy
(
p
,
p
+
n
,
p
+
(
spec
.
width
()
-
n
)
/
2
);
FillPadding
(
p
,
spec
.
width
(),
n
,
spec
.
fill
());
return
;
}
if
(
spec
.
fill
()
!=
' '
||
sign
)
{
while
(
*
start
==
' '
)
*
start
++
=
spec
.
fill
();
if
(
sign
)
*
(
start
-
1
)
=
sign
;
}
GrowBuffer
(
n
);
return
;
}
buffer_
.
reserve
(
n
>=
0
?
offset
+
n
+
1
:
2
*
buffer_
.
capacity
());
}
}
char
*
BasicFormatter
::
FormatString
(
const
char
*
s
,
std
::
size_t
size
,
const
FormatSpec
&
spec
)
{
char
*
out
=
0
;
if
(
spec
.
width
()
>
size
)
{
out
=
GrowBuffer
(
spec
.
width
());
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
std
::
fill_n
(
out
,
spec
.
width
()
-
size
,
spec
.
fill
());
out
+=
spec
.
width
()
-
size
;
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
out
=
FillPadding
(
out
,
spec
.
width
(),
size
,
spec
.
fill
());
}
else
{
std
::
fill_n
(
out
+
size
,
spec
.
width
()
-
size
,
spec
.
fill
());
}
}
else
{
out
=
GrowBuffer
(
size
);
}
std
::
copy
(
s
,
s
+
size
,
out
);
return
out
;
}
// Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
...
...
@@ -573,7 +333,7 @@ void Formatter::DoFormat() {
break
;
case
CHAR
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
'c'
)
ReportUnknownType
(
spec
.
type_
,
"char"
);
internal
::
ReportUnknownType
(
spec
.
type_
,
"char"
);
char
*
out
=
0
;
if
(
spec
.
width_
>
1
)
{
out
=
GrowBuffer
(
spec
.
width_
);
...
...
@@ -593,7 +353,7 @@ void Formatter::DoFormat() {
}
case
STRING
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
's'
)
ReportUnknownType
(
spec
.
type_
,
"string"
);
internal
::
ReportUnknownType
(
spec
.
type_
,
"string"
);
const
char
*
str
=
arg
.
string
.
value
;
size_t
size
=
arg
.
string
.
size
;
if
(
size
==
0
)
{
...
...
@@ -607,14 +367,14 @@ void Formatter::DoFormat() {
}
case
POINTER
:
if
(
spec
.
type_
&&
spec
.
type_
!=
'p'
)
ReportUnknownType
(
spec
.
type_
,
"pointer"
);
internal
::
ReportUnknownType
(
spec
.
type_
,
"pointer"
);
spec
.
flags_
=
HASH_FLAG
;
spec
.
type_
=
'x'
;
FormatInt
(
reinterpret_cast
<
uintptr_t
>
(
arg
.
pointer_value
),
spec
);
break
;
case
CUSTOM
:
if
(
spec
.
type_
)
ReportUnknownType
(
spec
.
type_
,
"object"
);
internal
::
ReportUnknownType
(
spec
.
type_
,
"object"
);
(
this
->*
arg
.
custom
.
format
)(
arg
.
custom
.
value
,
spec
);
break
;
default:
...
...
format.h
View file @
687301c5
...
...
@@ -136,6 +136,16 @@ struct IntTraits<int> : SignedIntTraits<int, unsigned> {};
template
<
>
struct
IntTraits
<
long
>
:
SignedIntTraits
<
long
,
unsigned
long
>
{};
template
<
typename
T
>
struct
IsLongDouble
{
enum
{
VALUE
=
0
};
};
template
<
>
struct
IsLongDouble
<
long
double
>
{
enum
{
VALUE
=
1
};
};
extern
const
char
DIGITS
[];
void
ReportUnknownType
(
char
code
,
const
char
*
type
);
class
ArgInserter
;
class
FormatterProxy
;
}
...
...
@@ -341,6 +351,7 @@ DEFINE_INT_FORMATTERS(long)
DEFINE_INT_FORMATTERS
(
unsigned
)
DEFINE_INT_FORMATTERS
(
unsigned
long
)
template
<
typename
Char
>
class
BasicFormatter
{
private:
// Returns the number of decimal digits in n. Trailing zeros are not counted
...
...
@@ -360,29 +371,30 @@ class BasicFormatter {
}
}
static
void
FormatDecimal
(
c
har
*
buffer
,
uint64_t
value
,
unsigned
num_digits
);
static
void
FormatDecimal
(
C
har
*
buffer
,
uint64_t
value
,
unsigned
num_digits
);
protected:
static
void
ReportUnknownType
(
char
code
,
const
char
*
type
);
static
Char
*
FillPadding
(
Char
*
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
char
fill
);
enum
{
INLINE_BUFFER_SIZE
=
500
};
mutable
internal
::
Array
<
c
har
,
INLINE_BUFFER_SIZE
>
buffer_
;
// Output buffer.
mutable
internal
::
Array
<
C
har
,
INLINE_BUFFER_SIZE
>
buffer_
;
// Output buffer.
// Grows the buffer by n characters and returns a pointer to the newly
// allocated area.
c
har
*
GrowBuffer
(
std
::
size_t
n
)
{
C
har
*
GrowBuffer
(
std
::
size_t
n
)
{
std
::
size_t
size
=
buffer_
.
size
();
buffer_
.
resize
(
size
+
n
);
return
&
buffer_
[
size
];
}
c
har
*
PrepareFilledBuffer
(
unsigned
size
,
const
Spec
&
,
char
sign
)
{
c
har
*
p
=
GrowBuffer
(
size
);
C
har
*
PrepareFilledBuffer
(
unsigned
size
,
const
Spec
&
,
char
sign
)
{
C
har
*
p
=
GrowBuffer
(
size
);
*
p
=
sign
;
return
p
+
size
-
1
;
}
c
har
*
PrepareFilledBuffer
(
unsigned
size
,
const
AlignSpec
&
spec
,
char
sign
);
C
har
*
PrepareFilledBuffer
(
unsigned
size
,
const
AlignSpec
&
spec
,
char
sign
);
// Formats an integer.
template
<
typename
T
>
...
...
@@ -406,13 +418,13 @@ class BasicFormatter {
Returns a pointer to the output buffer content. No terminating null
character is appended.
*/
const
c
har
*
data
()
const
{
return
&
buffer_
[
0
];
}
const
C
har
*
data
()
const
{
return
&
buffer_
[
0
];
}
/**
Returns a pointer to the output buffer content with terminating null
character appended.
*/
const
c
har
*
c_str
()
const
{
const
C
har
*
c_str
()
const
{
std
::
size_t
size
=
buffer_
.
size
();
buffer_
.
reserve
(
size
+
1
);
buffer_
[
size
]
=
'\0'
;
...
...
@@ -422,7 +434,9 @@ class BasicFormatter {
/**
Returns the content of the output buffer as an `std::string`.
*/
std
::
string
str
()
const
{
return
std
::
string
(
&
buffer_
[
0
],
buffer_
.
size
());
}
std
::
basic_string
<
Char
>
str
()
const
{
return
std
::
basic_string
<
Char
>
(
&
buffer_
[
0
],
buffer_
.
size
());
}
BasicFormatter
&
operator
<<
(
int
value
)
{
return
*
this
<<
IntFormatter
<
int
,
TypeSpec
<
0
>
>
(
value
,
TypeSpec
<
0
>
());
...
...
@@ -431,12 +445,12 @@ class BasicFormatter {
return
*
this
<<
IntFormatter
<
unsigned
,
TypeSpec
<
0
>
>
(
value
,
TypeSpec
<
0
>
());
}
BasicFormatter
&
operator
<<
(
c
har
value
)
{
BasicFormatter
&
operator
<<
(
C
har
value
)
{
*
GrowBuffer
(
1
)
=
value
;
return
*
this
;
}
BasicFormatter
&
operator
<<
(
const
c
har
*
value
)
{
BasicFormatter
&
operator
<<
(
const
C
har
*
value
)
{
std
::
size_t
size
=
std
::
strlen
(
value
);
std
::
strncpy
(
GrowBuffer
(
size
),
value
,
size
);
return
*
this
;
...
...
@@ -445,7 +459,7 @@ class BasicFormatter {
template
<
typename
T
,
typename
Spec
>
BasicFormatter
&
operator
<<
(
const
IntFormatter
<
T
,
Spec
>
&
f
);
void
Write
(
const
std
::
string
&
s
,
const
FormatSpec
&
spec
)
{
void
Write
(
const
std
::
basic_string
<
char
>
&
s
,
const
FormatSpec
&
spec
)
{
FormatString
(
s
.
data
(),
s
.
size
(),
spec
);
}
...
...
@@ -454,8 +468,249 @@ class BasicFormatter {
}
};
// Fills the padding around the content and returns the pointer to the
// content area.
template
<
typename
Char
>
Char
*
BasicFormatter
<
Char
>::
FillPadding
(
Char
*
buffer
,
unsigned
total_size
,
std
::
size_t
content_size
,
char
fill
)
{
std
::
size_t
padding
=
total_size
-
content_size
;
std
::
size_t
left_padding
=
padding
/
2
;
std
::
fill_n
(
buffer
,
left_padding
,
fill
);
buffer
+=
left_padding
;
char
*
content
=
buffer
;
std
::
fill_n
(
buffer
+
content_size
,
padding
-
left_padding
,
fill
);
return
content
;
}
template
<
typename
Char
>
void
BasicFormatter
<
Char
>::
FormatDecimal
(
Char
*
buffer
,
uint64_t
value
,
unsigned
num_digits
)
{
--
num_digits
;
while
(
value
>=
100
)
{
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
unsigned
index
=
(
value
%
100
)
*
2
;
value
/=
100
;
buffer
[
num_digits
]
=
internal
::
DIGITS
[
index
+
1
];
buffer
[
num_digits
-
1
]
=
internal
::
DIGITS
[
index
];
num_digits
-=
2
;
}
if
(
value
<
10
)
{
*
buffer
=
static_cast
<
char
>
(
'0'
+
value
);
return
;
}
unsigned
index
=
static_cast
<
unsigned
>
(
value
*
2
);
buffer
[
1
]
=
internal
::
DIGITS
[
index
+
1
];
buffer
[
0
]
=
internal
::
DIGITS
[
index
];
}
template
<
typename
Char
>
Char
*
BasicFormatter
<
Char
>::
PrepareFilledBuffer
(
unsigned
size
,
const
AlignSpec
&
spec
,
char
sign
)
{
unsigned
width
=
spec
.
width
();
if
(
width
<=
size
)
{
char
*
p
=
GrowBuffer
(
size
);
*
p
=
sign
;
return
p
+
size
-
1
;
}
char
*
p
=
GrowBuffer
(
width
);
char
*
end
=
p
+
width
;
Alignment
align
=
spec
.
align
();
if
(
align
==
ALIGN_LEFT
)
{
*
p
=
sign
;
p
+=
size
;
std
::
fill
(
p
,
end
,
spec
.
fill
());
}
else
if
(
align
==
ALIGN_CENTER
)
{
p
=
FillPadding
(
p
,
width
,
size
,
spec
.
fill
());
*
p
=
sign
;
p
+=
size
;
}
else
{
if
(
align
==
ALIGN_NUMERIC
)
{
if
(
sign
)
{
*
p
++
=
sign
;
--
size
;
}
}
else
{
*
(
end
-
size
)
=
sign
;
}
std
::
fill
(
p
,
end
-
size
,
spec
.
fill
());
p
=
end
;
}
return
p
-
1
;
}
template
<
typename
Char
>
template
<
typename
T
>
void
BasicFormatter
<
Char
>::
FormatDouble
(
T
value
,
const
FormatSpec
&
spec
,
int
precision
)
{
// Check type.
char
type
=
spec
.
type
();
bool
upper
=
false
;
switch
(
type
)
{
case
0
:
type
=
'g'
;
break
;
case
'e'
:
case
'f'
:
case
'g'
:
break
;
case
'F'
:
#ifdef _MSC_VER
// MSVC's printf doesn't support 'F'.
type
=
'f'
;
#endif
// Fall through.
case
'E'
:
case
'G'
:
upper
=
true
;
break
;
default:
internal
::
ReportUnknownType
(
type
,
"double"
);
break
;
}
char
sign
=
0
;
// Use SignBit instead of value < 0 because the latter is always
// false for NaN.
if
(
SignBit
(
value
))
{
sign
=
'-'
;
value
=
-
value
;
}
else
if
(
spec
.
sign_flag
())
{
sign
=
spec
.
plus_flag
()
?
'+'
:
' '
;
}
if
(
value
!=
value
)
{
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
size
=
4
;
const
char
*
nan
=
upper
?
" NAN"
:
" nan"
;
if
(
!
sign
)
{
--
size
;
++
nan
;
}
char
*
out
=
FormatString
(
nan
,
size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
if
(
isinf
(
value
))
{
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std
::
size_t
size
=
4
;
const
char
*
inf
=
upper
?
" INF"
:
" inf"
;
if
(
!
sign
)
{
--
size
;
++
inf
;
}
char
*
out
=
FormatString
(
inf
,
size
,
spec
);
if
(
sign
)
*
out
=
sign
;
return
;
}
size_t
offset
=
buffer_
.
size
();
unsigned
width
=
spec
.
width
();
if
(
sign
)
{
buffer_
.
reserve
(
buffer_
.
size
()
+
std
::
max
(
width
,
1u
));
if
(
width
>
0
)
--
width
;
++
offset
;
}
// Build format string.
enum
{
MAX_FORMAT_SIZE
=
10
};
// longest format: %#-*.*Lg
char
format
[
MAX_FORMAT_SIZE
];
char
*
format_ptr
=
format
;
*
format_ptr
++
=
'%'
;
unsigned
width_for_sprintf
=
width
;
if
(
spec
.
hash_flag
())
*
format_ptr
++
=
'#'
;
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
width_for_sprintf
=
0
;
}
else
{
if
(
spec
.
align
()
==
ALIGN_LEFT
)
*
format_ptr
++
=
'-'
;
if
(
width
!=
0
)
*
format_ptr
++
=
'*'
;
}
if
(
precision
>=
0
)
{
*
format_ptr
++
=
'.'
;
*
format_ptr
++
=
'*'
;
}
if
(
internal
::
IsLongDouble
<
T
>::
VALUE
)
*
format_ptr
++
=
'L'
;
*
format_ptr
++
=
type
;
*
format_ptr
=
'\0'
;
// Format using snprintf.
for
(;;)
{
size_t
size
=
buffer_
.
capacity
()
-
offset
;
int
n
=
0
;
char
*
start
=
&
buffer_
[
offset
];
if
(
width_for_sprintf
==
0
)
{
n
=
precision
<
0
?
snprintf
(
start
,
size
,
format
,
value
)
:
snprintf
(
start
,
size
,
format
,
precision
,
value
);
}
else
{
n
=
precision
<
0
?
snprintf
(
start
,
size
,
format
,
width_for_sprintf
,
value
)
:
snprintf
(
start
,
size
,
format
,
width_for_sprintf
,
precision
,
value
);
}
if
(
n
>=
0
&&
offset
+
n
<
buffer_
.
capacity
())
{
if
(
sign
)
{
if
((
spec
.
align
()
!=
ALIGN_RIGHT
&&
spec
.
align
()
!=
ALIGN_DEFAULT
)
||
*
start
!=
' '
)
{
*
(
start
-
1
)
=
sign
;
sign
=
0
;
}
else
{
*
(
start
-
1
)
=
spec
.
fill
();
}
++
n
;
}
if
(
spec
.
align
()
==
ALIGN_CENTER
&&
spec
.
width
()
>
static_cast
<
unsigned
>
(
n
))
{
char
*
p
=
GrowBuffer
(
spec
.
width
());
std
::
copy
(
p
,
p
+
n
,
p
+
(
spec
.
width
()
-
n
)
/
2
);
FillPadding
(
p
,
spec
.
width
(),
n
,
spec
.
fill
());
return
;
}
if
(
spec
.
fill
()
!=
' '
||
sign
)
{
while
(
*
start
==
' '
)
*
start
++
=
spec
.
fill
();
if
(
sign
)
*
(
start
-
1
)
=
sign
;
}
GrowBuffer
(
n
);
return
;
}
buffer_
.
reserve
(
n
>=
0
?
offset
+
n
+
1
:
2
*
buffer_
.
capacity
());
}
}
template
<
typename
Char
>
char
*
BasicFormatter
<
Char
>::
FormatString
(
const
char
*
s
,
std
::
size_t
size
,
const
FormatSpec
&
spec
)
{
char
*
out
=
0
;
if
(
spec
.
width
()
>
size
)
{
out
=
GrowBuffer
(
spec
.
width
());
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
std
::
fill_n
(
out
,
spec
.
width
()
-
size
,
spec
.
fill
());
out
+=
spec
.
width
()
-
size
;
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
out
=
FillPadding
(
out
,
spec
.
width
(),
size
,
spec
.
fill
());
}
else
{
std
::
fill_n
(
out
+
size
,
spec
.
width
()
-
size
,
spec
.
fill
());
}
}
else
{
out
=
GrowBuffer
(
size
);
}
std
::
copy
(
s
,
s
+
size
,
out
);
return
out
;
}
template
<
typename
Char
>
template
<
typename
T
,
typename
Spec
>
BasicFormatter
&
BasicFormatter
::
operator
<<
(
const
IntFormatter
<
T
,
Spec
>
&
f
)
{
BasicFormatter
<
Char
>
&
BasicFormatter
<
Char
>::
operator
<<
(
const
IntFormatter
<
T
,
Spec
>
&
f
)
{
T
value
=
f
.
value
();
unsigned
size
=
0
;
char
sign
=
0
;
...
...
@@ -472,7 +727,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
switch
(
f
.
type
())
{
case
0
:
case
'd'
:
{
unsigned
num_digits
=
BasicFormatter
::
CountDigits
(
abs_value
);
c
har
*
p
=
PrepareFilledBuffer
(
size
+
num_digits
,
f
,
sign
)
-
num_digits
+
1
;
C
har
*
p
=
PrepareFilledBuffer
(
size
+
num_digits
,
f
,
sign
)
-
num_digits
+
1
;
BasicFormatter
::
FormatDecimal
(
p
,
abs_value
,
num_digits
);
break
;
}
...
...
@@ -483,7 +738,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
do
{
++
size
;
}
while
((
n
>>=
4
)
!=
0
);
c
har
*
p
=
PrepareFilledBuffer
(
size
,
f
,
sign
);
C
har
*
p
=
PrepareFilledBuffer
(
size
,
f
,
sign
);
n
=
abs_value
;
const
char
*
digits
=
f
.
type
()
==
'x'
?
"0123456789abcdef"
:
"0123456789ABCDEF"
;
...
...
@@ -503,7 +758,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
do
{
++
size
;
}
while
((
n
>>=
3
)
!=
0
);
c
har
*
p
=
PrepareFilledBuffer
(
size
,
f
,
sign
);
C
har
*
p
=
PrepareFilledBuffer
(
size
,
f
,
sign
);
n
=
abs_value
;
do
{
*
p
--
=
'0'
+
(
n
&
7
);
...
...
@@ -513,16 +768,16 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
break
;
}
default:
BasicFormatter
::
ReportUnknownType
(
f
.
type
(),
"integer"
);
internal
::
ReportUnknownType
(
f
.
type
(),
"integer"
);
break
;
}
return
*
this
;
}
// The default formatting function.
template
<
typename
T
>
void
Format
(
BasicFormatter
&
f
,
const
FormatSpec
&
spec
,
const
T
&
value
)
{
std
::
ostringstream
os
;
template
<
typename
Char
,
typename
T
>
void
Format
(
BasicFormatter
<
Char
>
&
f
,
const
FormatSpec
&
spec
,
const
T
&
value
)
{
std
::
basic_ostringstream
<
Char
>
os
;
os
<<
value
;
f
.
Write
(
os
.
str
(),
spec
);
}
...
...
@@ -551,7 +806,7 @@ void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) {
The buffer can be accessed using :meth:`data` or :meth:`c_str`.
\endrst
*/
class
Formatter
:
public
BasicFormatter
{
class
Formatter
:
public
BasicFormatter
<
char
>
{
private:
enum
Type
{
// Numeric types should go first.
...
...
@@ -704,8 +959,13 @@ class Formatter : public BasicFormatter {
internal
::
ArgInserter
operator
()(
StringRef
format
);
}
;
inline
std
::
string
str
(
const
BasicFormatter
&
f
)
{
return
f
.
str
();
}
inline
const
char
*
c_str
(
const
BasicFormatter
&
f
)
{
return
f
.
c_str
();
}
template
<
typename
Char
>
inline
std
::
basic_string
<
Char
>
str
(
const
BasicFormatter
<
Char
>
&
f
)
{
return
f
.
str
();
}
template
<
typename
Char
>
inline
const
Char
*
c_str
(
const
BasicFormatter
<
Char
>
&
f
)
{
return
f
.
c_str
();
}
std
::
string
str
(
internal
::
FormatterProxy
p
);
const
char
*
c_str
(
internal
::
FormatterProxy
p
);
...
...
format_test.cc
View file @
687301c5
...
...
@@ -862,7 +862,9 @@ class Date {
return
os
;
}
friend
BasicFormatter
&
operator
<<
(
BasicFormatter
&
f
,
const
Date
&
d
)
{
template
<
typename
Char
>
friend
BasicFormatter
<
Char
>
&
operator
<<
(
BasicFormatter
<
Char
>
&
f
,
const
Date
&
d
)
{
return
f
<<
d
.
year_
<<
'-'
<<
d
.
month_
<<
'-'
<<
d
.
day_
;
}
};
...
...
@@ -877,7 +879,8 @@ TEST(FormatterTest, FormatUsingIOStreams) {
class
Answer
{};
void
Format
(
fmt
::
BasicFormatter
&
f
,
const
fmt
::
FormatSpec
&
spec
,
Answer
)
{
template
<
typename
Char
>
void
Format
(
fmt
::
BasicFormatter
<
Char
>
&
f
,
const
fmt
::
FormatSpec
&
spec
,
Answer
)
{
f
.
Write
(
"42"
,
spec
);
}
...
...
@@ -918,7 +921,7 @@ TEST(FormatterTest, FormatterAppend) {
TEST
(
FormatterTest
,
FormatterExamples
)
{
using
fmt
::
hex
;
EXPECT_EQ
(
"0000cafe"
,
str
(
BasicFormatter
()
<<
pad
(
hex
(
0xcafe
),
8
,
'0'
)));
EXPECT_EQ
(
"0000cafe"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
hex
(
0xcafe
),
8
,
'0'
)));
std
::
string
message
=
str
(
Format
(
"The answer is {}"
)
<<
42
);
EXPECT_EQ
(
"The answer is 42"
,
message
);
...
...
@@ -1068,11 +1071,11 @@ TEST(TempFormatterTest, Examples) {
TEST
(
StrTest
,
oct
)
{
using
fmt
::
oct
;
EXPECT_EQ
(
"12"
,
(
BasicFormatter
()
<<
oct
(
static_cast
<
short
>
(
012
))).
str
(
));
EXPECT_EQ
(
"12"
,
(
BasicFormatter
()
<<
oct
(
012
)).
str
(
));
EXPECT_EQ
(
"34"
,
(
BasicFormatter
()
<<
oct
(
034u
)).
str
(
));
EXPECT_EQ
(
"56"
,
(
BasicFormatter
()
<<
oct
(
056l
)).
str
(
));
EXPECT_EQ
(
"70"
,
(
BasicFormatter
()
<<
oct
(
070ul
)).
str
(
));
EXPECT_EQ
(
"12"
,
str
(
BasicFormatter
<
char
>
()
<<
oct
(
static_cast
<
short
>
(
012
))
));
EXPECT_EQ
(
"12"
,
str
(
BasicFormatter
<
char
>
()
<<
oct
(
012
)
));
EXPECT_EQ
(
"34"
,
str
(
BasicFormatter
<
char
>
()
<<
oct
(
034u
)
));
EXPECT_EQ
(
"56"
,
str
(
BasicFormatter
<
char
>
()
<<
oct
(
056l
)
));
EXPECT_EQ
(
"70"
,
str
(
BasicFormatter
<
char
>
()
<<
oct
(
070ul
)
));
}
TEST
(
StrTest
,
hex
)
{
...
...
@@ -1082,17 +1085,17 @@ TEST(StrTest, hex) {
// This shouldn't compile:
//fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
EXPECT_EQ
(
"cafe"
,
(
BasicFormatter
()
<<
hex
(
0xcafe
)).
str
(
));
EXPECT_EQ
(
"babe"
,
(
BasicFormatter
()
<<
hex
(
0xbabeu
)).
str
(
));
EXPECT_EQ
(
"dead"
,
(
BasicFormatter
()
<<
hex
(
0xdeadl
)).
str
(
));
EXPECT_EQ
(
"beef"
,
(
BasicFormatter
()
<<
hex
(
0xbeeful
)).
str
(
));
EXPECT_EQ
(
"cafe"
,
str
(
BasicFormatter
<
char
>
()
<<
hex
(
0xcafe
)
));
EXPECT_EQ
(
"babe"
,
str
(
BasicFormatter
<
char
>
()
<<
hex
(
0xbabeu
)
));
EXPECT_EQ
(
"dead"
,
str
(
BasicFormatter
<
char
>
()
<<
hex
(
0xdeadl
)
));
EXPECT_EQ
(
"beef"
,
str
(
BasicFormatter
<
char
>
()
<<
hex
(
0xbeeful
)
));
}
TEST
(
StrTest
,
hexu
)
{
EXPECT_EQ
(
"CAFE"
,
(
BasicFormatter
()
<<
hexu
(
0xcafe
)).
str
(
));
EXPECT_EQ
(
"BABE"
,
(
BasicFormatter
()
<<
hexu
(
0xbabeu
)).
str
(
));
EXPECT_EQ
(
"DEAD"
,
(
BasicFormatter
()
<<
hexu
(
0xdeadl
)).
str
(
));
EXPECT_EQ
(
"BEEF"
,
(
BasicFormatter
()
<<
hexu
(
0xbeeful
)).
str
(
));
EXPECT_EQ
(
"CAFE"
,
str
(
BasicFormatter
<
char
>
()
<<
hexu
(
0xcafe
)
));
EXPECT_EQ
(
"BABE"
,
str
(
BasicFormatter
<
char
>
()
<<
hexu
(
0xbabeu
)
));
EXPECT_EQ
(
"DEAD"
,
str
(
BasicFormatter
<
char
>
()
<<
hexu
(
0xdeadl
)
));
EXPECT_EQ
(
"BEEF"
,
str
(
BasicFormatter
<
char
>
()
<<
hexu
(
0xbeeful
)
));
}
class
ISO8601DateFormatter
{
...
...
@@ -1101,8 +1104,9 @@ class ISO8601DateFormatter {
public:
ISO8601DateFormatter
(
const
Date
&
d
)
:
date_
(
&
d
)
{}
friend
BasicFormatter
&
operator
<<
(
BasicFormatter
&
f
,
const
ISO8601DateFormatter
&
d
)
{
template
<
typename
Char
>
friend
BasicFormatter
<
Char
>
&
operator
<<
(
BasicFormatter
<
Char
>
&
f
,
const
ISO8601DateFormatter
&
d
)
{
return
f
<<
pad
(
d
.
date_
->
year
(),
4
,
'0'
)
<<
'-'
<<
pad
(
d
.
date_
->
month
(),
2
,
'0'
)
<<
'-'
<<
pad
(
d
.
date_
->
day
(),
2
,
'0'
);
}
...
...
@@ -1112,17 +1116,17 @@ ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
TEST
(
StrTest
,
pad
)
{
using
fmt
::
hex
;
EXPECT_EQ
(
" cafe"
,
(
BasicFormatter
()
<<
pad
(
hex
(
0xcafe
),
8
)).
str
(
));
EXPECT_EQ
(
" babe"
,
(
BasicFormatter
()
<<
pad
(
hex
(
0xbabeu
),
8
)).
str
(
));
EXPECT_EQ
(
" dead"
,
(
BasicFormatter
()
<<
pad
(
hex
(
0xdeadl
),
8
)).
str
(
));
EXPECT_EQ
(
" beef"
,
(
BasicFormatter
()
<<
pad
(
hex
(
0xbeeful
),
8
)).
str
(
));
EXPECT_EQ
(
" cafe"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
hex
(
0xcafe
),
8
)
));
EXPECT_EQ
(
" babe"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
hex
(
0xbabeu
),
8
)
));
EXPECT_EQ
(
" dead"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
hex
(
0xdeadl
),
8
)
));
EXPECT_EQ
(
" beef"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
hex
(
0xbeeful
),
8
)
));
EXPECT_EQ
(
" 11"
,
(
BasicFormatter
()
<<
pad
(
11
,
7
)).
str
(
));
EXPECT_EQ
(
" 22"
,
(
BasicFormatter
()
<<
pad
(
22u
,
7
)).
str
(
));
EXPECT_EQ
(
" 33"
,
(
BasicFormatter
()
<<
pad
(
33l
,
7
)).
str
(
));
EXPECT_EQ
(
" 44"
,
(
BasicFormatter
()
<<
pad
(
44lu
,
7
)).
str
(
));
EXPECT_EQ
(
" 11"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
11
,
7
)
));
EXPECT_EQ
(
" 22"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
22u
,
7
)
));
EXPECT_EQ
(
" 33"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
33l
,
7
)
));
EXPECT_EQ
(
" 44"
,
str
(
BasicFormatter
<
char
>
()
<<
pad
(
44lu
,
7
)
));
BasicFormatter
f
;
BasicFormatter
<
char
>
f
;
f
.
Clear
();
f
<<
pad
(
42
,
5
,
'0'
);
EXPECT_EQ
(
"00042"
,
f
.
str
());
...
...
@@ -1137,8 +1141,12 @@ TEST(StrTest, pad) {
TEST
(
StrTest
,
NoConflictWithIOManip
)
{
using
namespace
std
;
using
namespace
fmt
;
EXPECT_EQ
(
"cafe"
,
(
BasicFormatter
()
<<
hex
(
0xcafe
)).
str
());
EXPECT_EQ
(
"12"
,
(
BasicFormatter
()
<<
oct
(
012
)).
str
());
EXPECT_EQ
(
"cafe"
,
str
(
BasicFormatter
<
char
>
()
<<
hex
(
0xcafe
)));
EXPECT_EQ
(
"12"
,
str
(
BasicFormatter
<
char
>
()
<<
oct
(
012
)));
}
TEST
(
StrTest
,
BasicFormatterWChar
)
{
EXPECT_EQ
(
L"cafe"
,
str
(
BasicFormatter
<
wchar_t
>
()
<<
fmt
::
hex
(
0xcafe
)));
}
template
<
typename
T
>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment