Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
spdlog
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
spdlog
Commits
0db4b04a
Commit
0db4b04a
authored
Dec 04, 2019
by
gabime
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Bump bundled fmt to version 6.1.0
parent
1aa9ea92
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
2341 additions
and
1640 deletions
+2341
-1640
include/spdlog/fmt/bundled/chrono.h
include/spdlog/fmt/bundled/chrono.h
+292
-18
include/spdlog/fmt/bundled/color.h
include/spdlog/fmt/bundled/color.h
+31
-46
include/spdlog/fmt/bundled/compile.h
include/spdlog/fmt/bundled/compile.h
+441
-320
include/spdlog/fmt/bundled/core.h
include/spdlog/fmt/bundled/core.h
+177
-73
include/spdlog/fmt/bundled/format-inl.h
include/spdlog/fmt/bundled/format-inl.h
+633
-237
include/spdlog/fmt/bundled/format.h
include/spdlog/fmt/bundled/format.h
+590
-597
include/spdlog/fmt/bundled/ostream.h
include/spdlog/fmt/bundled/ostream.h
+14
-9
include/spdlog/fmt/bundled/posix.h
include/spdlog/fmt/bundled/posix.h
+27
-17
include/spdlog/fmt/bundled/printf.h
include/spdlog/fmt/bundled/printf.h
+14
-18
include/spdlog/fmt/bundled/ranges.h
include/spdlog/fmt/bundled/ranges.h
+78
-1
include/spdlog/fmt/bundled/safe-duration-cast.h
include/spdlog/fmt/bundled/safe-duration-cast.h
+0
-293
src/fmt.cpp
src/fmt.cpp
+44
-11
No files found.
include/spdlog/fmt/bundled/chrono.h
View file @
0db4b04a
...
...
@@ -16,16 +16,291 @@
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace
safe_duration_cast
{
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
!
std
::
is_same
<
From
,
To
>
::
value
&&
std
::
numeric_limits
<
From
>::
is_signed
==
std
::
numeric_limits
<
To
>::
is_signed
)
>
FMT_CONSTEXPR
To
lossless_integral_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
using
F
=
std
::
numeric_limits
<
From
>
;
using
T
=
std
::
numeric_limits
<
To
>
;
static_assert
(
F
::
is_integer
,
"From must be integral"
);
static_assert
(
T
::
is_integer
,
"To must be integral"
);
// A and B are both signed, or both unsigned.
if
(
F
::
digits
<=
T
::
digits
)
{
// From fits in To without any problem.
}
else
{
// From does not always fit in To, resort to a dynamic check.
if
(
from
<
T
::
min
()
||
from
>
T
::
max
())
{
// outside range.
ec
=
1
;
return
{};
}
}
return
static_cast
<
To
>
(
from
);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
!
std
::
is_same
<
From
,
To
>
::
value
&&
std
::
numeric_limits
<
From
>::
is_signed
!=
std
::
numeric_limits
<
To
>::
is_signed
)
>
FMT_CONSTEXPR
To
lossless_integral_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
using
F
=
std
::
numeric_limits
<
From
>
;
using
T
=
std
::
numeric_limits
<
To
>
;
static_assert
(
F
::
is_integer
,
"From must be integral"
);
static_assert
(
T
::
is_integer
,
"To must be integral"
);
if
(
F
::
is_signed
&&
!
T
::
is_signed
)
{
// From may be negative, not allowed!
if
(
fmt
::
internal
::
is_negative
(
from
))
{
ec
=
1
;
return
{};
}
// From is positive. Can it always fit in To?
if
(
F
::
digits
<=
T
::
digits
)
{
// yes, From always fits in To.
}
else
{
// from may not fit in To, we have to do a dynamic check
if
(
from
>
static_cast
<
From
>
(
T
::
max
()))
{
ec
=
1
;
return
{};
}
}
}
if
(
!
F
::
is_signed
&&
T
::
is_signed
)
{
// can from be held in To?
if
(
F
::
digits
<
T
::
digits
)
{
// yes, From always fits in To.
}
else
{
// from may not fit in To, we have to do a dynamic check
if
(
from
>
static_cast
<
From
>
(
T
::
max
()))
{
// outside range.
ec
=
1
;
return
{};
}
}
}
// reaching here means all is ok for lossless conversion.
return
static_cast
<
To
>
(
from
);
}
// function
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
std
::
is_same
<
From
,
To
>
::
value
)
>
FMT_CONSTEXPR
To
lossless_integral_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
return
from
;
}
// function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
!
std
::
is_same
<
From
,
To
>
::
value
)
>
FMT_CONSTEXPR
To
safe_float_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
using
T
=
std
::
numeric_limits
<
To
>
;
static_assert
(
std
::
is_floating_point
<
From
>::
value
,
"From must be floating"
);
static_assert
(
std
::
is_floating_point
<
To
>::
value
,
"To must be floating"
);
// catch the only happy case
if
(
std
::
isfinite
(
from
))
{
if
(
from
>=
T
::
lowest
()
&&
from
<=
T
::
max
())
{
return
static_cast
<
To
>
(
from
);
}
// not within range.
ec
=
1
;
return
{};
}
// nan and inf will be preserved
return
static_cast
<
To
>
(
from
);
}
// function
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
std
::
is_same
<
From
,
To
>
::
value
)
>
FMT_CONSTEXPR
To
safe_float_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
static_assert
(
std
::
is_floating_point
<
From
>::
value
,
"From must be floating"
);
return
from
;
}
/**
* safe duration cast between integral durations
*/
template
<
typename
To
,
typename
FromRep
,
typename
FromPeriod
,
FMT_ENABLE_IF
(
std
::
is_integral
<
FromRep
>
::
value
),
FMT_ENABLE_IF
(
std
::
is_integral
<
typename
To
::
rep
>::
value
)
>
To
safe_duration_cast
(
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
from
,
int
&
ec
)
{
using
From
=
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
;
ec
=
0
;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct
Factor
:
std
::
ratio_divide
<
typename
From
::
period
,
typename
To
::
period
>
{};
static_assert
(
Factor
::
num
>
0
,
"num must be positive"
);
static_assert
(
Factor
::
den
>
0
,
"den must be positive"
);
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using
IntermediateRep
=
typename
std
::
common_type
<
typename
From
::
rep
,
typename
To
::
rep
,
decltype
(
Factor
::
num
)
>::
type
;
// safe conversion to IntermediateRep
IntermediateRep
count
=
lossless_integral_conversion
<
IntermediateRep
>
(
from
.
count
(),
ec
);
if
(
ec
)
{
return
{};
}
// multiply with Factor::num without overflow or underflow
if
(
Factor
::
num
!=
1
)
{
const
auto
max1
=
internal
::
max_value
<
IntermediateRep
>
()
/
Factor
::
num
;
if
(
count
>
max1
)
{
ec
=
1
;
return
{};
}
const
auto
min1
=
std
::
numeric_limits
<
IntermediateRep
>::
min
()
/
Factor
::
num
;
if
(
count
<
min1
)
{
ec
=
1
;
return
{};
}
count
*=
Factor
::
num
;
}
// this can't go wrong, right? den>0 is checked earlier.
if
(
Factor
::
den
!=
1
)
{
count
/=
Factor
::
den
;
}
// convert to the to type, safely
using
ToRep
=
typename
To
::
rep
;
const
ToRep
tocount
=
lossless_integral_conversion
<
ToRep
>
(
count
,
ec
);
if
(
ec
)
{
return
{};
}
return
To
{
tocount
};
}
/**
* safe duration_cast between floating point durations
*/
template
<
typename
To
,
typename
FromRep
,
typename
FromPeriod
,
FMT_ENABLE_IF
(
std
::
is_floating_point
<
FromRep
>
::
value
),
FMT_ENABLE_IF
(
std
::
is_floating_point
<
typename
To
::
rep
>::
value
)
>
To
safe_duration_cast
(
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
from
,
int
&
ec
)
{
using
From
=
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
;
ec
=
0
;
if
(
std
::
isnan
(
from
.
count
()))
{
// nan in, gives nan out. easy.
return
To
{
std
::
numeric_limits
<
typename
To
::
rep
>::
quiet_NaN
()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if
(
std
::
isinf
(
from
.
count
()))
{
return
To
{
from
.
count
()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct
Factor
:
std
::
ratio_divide
<
typename
From
::
period
,
typename
To
::
period
>
{};
static_assert
(
Factor
::
num
>
0
,
"num must be positive"
);
static_assert
(
Factor
::
den
>
0
,
"den must be positive"
);
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using
IntermediateRep
=
typename
std
::
common_type
<
typename
From
::
rep
,
typename
To
::
rep
,
decltype
(
Factor
::
num
)
>::
type
;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep
count
=
safe_float_conversion
<
IntermediateRep
>
(
from
.
count
(),
ec
);
if
(
ec
)
{
return
{};
}
// multiply with Factor::num without overflow or underflow
if
(
Factor
::
num
!=
1
)
{
constexpr
auto
max1
=
internal
::
max_value
<
IntermediateRep
>
()
/
static_cast
<
IntermediateRep
>
(
Factor
::
num
);
if
(
count
>
max1
)
{
ec
=
1
;
return
{};
}
constexpr
auto
min1
=
std
::
numeric_limits
<
IntermediateRep
>::
lowest
()
/
static_cast
<
IntermediateRep
>
(
Factor
::
num
);
if
(
count
<
min1
)
{
ec
=
1
;
return
{};
}
count
*=
static_cast
<
IntermediateRep
>
(
Factor
::
num
);
}
// this can't go wrong, right? den>0 is checked earlier.
if
(
Factor
::
den
!=
1
)
{
using
common_t
=
typename
std
::
common_type
<
IntermediateRep
,
intmax_t
>::
type
;
count
/=
static_cast
<
common_t
>
(
Factor
::
den
);
}
// convert to the to type, safely
using
ToRep
=
typename
To
::
rep
;
const
ToRep
tocount
=
safe_float_conversion
<
ToRep
>
(
count
,
ec
);
if
(
ec
)
{
return
{};
}
return
To
{
tocount
};
}
}
// namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
...
...
@@ -403,7 +678,7 @@ inline bool isfinite(T value) {
return
std
::
isfinite
(
value
);
}
// Convers value to int and checks that it's in the range [0, upper).
// Conver
t
s value to int and checks that it's in the range [0, upper).
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
inline
int
to_nonnegative_int
(
T
value
,
int
upper
)
{
FMT_ASSERT
(
value
>=
0
&&
value
<=
upper
,
"invalid value"
);
...
...
@@ -582,8 +857,8 @@ struct chrono_formatter {
void
write
(
Rep
value
,
int
width
)
{
write_sign
();
if
(
isnan
(
value
))
return
write_nan
();
uint32_or_64_
t
<
int
>
n
=
to_unsigned
(
to_
nonnegative_int
(
value
,
(
std
::
numeric_limits
<
int
>::
max
)
()));
uint32_or_64_
or_128_t
<
int
>
n
=
to_
unsigned
(
to_nonnegative_int
(
value
,
max_value
<
int
>
()));
int
num_digits
=
internal
::
count_digits
(
n
);
if
(
width
>
num_digits
)
out
=
std
::
fill_n
(
out
,
width
-
num_digits
,
'0'
);
out
=
format_decimal
<
char_type
>
(
out
,
n
,
num_digits
);
...
...
@@ -728,7 +1003,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct
spec_handler
{
formatter
&
f
;
basic_parse_context
<
Char
>&
context
;
basic_
format_
parse_context
<
Char
>&
context
;
basic_string_view
<
Char
>
format_str
;
template
<
typename
Id
>
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
Id
arg_id
)
{
...
...
@@ -738,8 +1013,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
basic_string_view
<
Char
>
arg_id
)
{
context
.
check_arg_id
(
arg_id
);
const
auto
str_val
=
internal
::
string_view_metadata
(
format_str
,
arg_id
);
return
arg_ref_type
(
str_val
);
return
arg_ref_type
(
arg_id
);
}
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
internal
::
auto_id
)
{
...
...
@@ -750,7 +1024,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void
on_fill
(
Char
fill
)
{
f
.
specs
.
fill
[
0
]
=
fill
;
}
void
on_align
(
align_t
align
)
{
f
.
specs
.
align
=
align
;
}
void
on_width
(
unsigned
width
)
{
f
.
specs
.
width
=
width
;
}
void
on_precision
(
unsigned
precision
)
{
f
.
precision
=
precision
;
}
void
on_precision
(
unsigned
_precision
)
{
f
.
precision
=
_
precision
;
}
void
end_precision
()
{}
template
<
typename
Id
>
void
on_dynamic_width
(
Id
arg_id
)
{
...
...
@@ -762,13 +1036,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
using
iterator
=
typename
basic_parse_context
<
Char
>::
iterator
;
using
iterator
=
typename
basic_
format_
parse_context
<
Char
>::
iterator
;
struct
parse_range
{
iterator
begin
;
iterator
end
;
};
FMT_CONSTEXPR
parse_range
do_parse
(
basic_parse_context
<
Char
>&
ctx
)
{
FMT_CONSTEXPR
parse_range
do_parse
(
basic_
format_
parse_context
<
Char
>&
ctx
)
{
auto
begin
=
ctx
.
begin
(),
end
=
ctx
.
end
();
if
(
begin
==
end
||
*
begin
==
'}'
)
return
{
begin
,
begin
};
spec_handler
handler
{
*
this
,
ctx
,
format_str
};
...
...
@@ -789,7 +1063,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public:
formatter
()
:
precision
(
-
1
)
{}
FMT_CONSTEXPR
auto
parse
(
basic_parse_context
<
Char
>&
ctx
)
FMT_CONSTEXPR
auto
parse
(
basic_
format_
parse_context
<
Char
>&
ctx
)
->
decltype
(
ctx
.
begin
())
{
auto
range
=
do_parse
(
ctx
);
format_str
=
basic_string_view
<
Char
>
(
...
...
@@ -806,10 +1080,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto
out
=
std
::
back_inserter
(
buf
);
using
range
=
internal
::
output_range
<
decltype
(
ctx
.
out
()),
Char
>
;
internal
::
basic_writer
<
range
>
w
(
range
(
ctx
.
out
()));
internal
::
handle_dynamic_spec
<
internal
::
width_checker
>
(
specs
.
width
,
width_ref
,
ctx
,
format_str
.
begin
()
);
internal
::
handle_dynamic_spec
<
internal
::
width_checker
>
(
specs
.
width
,
width_ref
,
ctx
);
internal
::
handle_dynamic_spec
<
internal
::
precision_checker
>
(
precision
,
precision_ref
,
ctx
,
format_str
.
begin
()
);
precision
,
precision_ref
,
ctx
);
if
(
begin
==
end
||
*
begin
==
'}'
)
{
out
=
internal
::
format_chrono_duration_value
(
out
,
d
.
count
(),
precision
);
internal
::
format_chrono_duration_unit
<
Period
>
(
out
);
...
...
include/spdlog/fmt/bundled/color.h
View file @
0db4b04a
...
...
@@ -299,15 +299,15 @@ class text_style {
return
static_cast
<
uint8_t
>
(
ems
)
!=
0
;
}
FMT_CONSTEXPR
internal
::
color_type
get_foreground
()
const
FMT_NOEXCEPT
{
assert
(
has_foreground
()
&&
"no foreground specified for this style"
);
FMT_ASSERT
(
has_foreground
(),
"no foreground specified for this style"
);
return
foreground_color
;
}
FMT_CONSTEXPR
internal
::
color_type
get_background
()
const
FMT_NOEXCEPT
{
assert
(
has_background
()
&&
"no background specified for this style"
);
FMT_ASSERT
(
has_background
(),
"no background specified for this style"
);
return
background_color
;
}
FMT_CONSTEXPR
emphasis
get_emphasis
()
const
FMT_NOEXCEPT
{
assert
(
has_emphasis
()
&&
"no emphasis specified for this style"
);
FMT_ASSERT
(
has_emphasis
(),
"no emphasis specified for this style"
);
return
ems
;
}
...
...
@@ -470,58 +470,41 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
}
template
<
typename
Char
>
std
::
basic_string
<
Char
>
vformat
(
const
text_style
&
ts
,
basic_string_view
<
Char
>
format_str
,
basic_format_args
<
buffer_context
<
Char
>
>
args
)
{
basic_memory_buffer
<
Char
>
buffer
;
void
vformat_to
(
basic_memory_buffer
<
Char
>&
buf
,
const
text_style
&
ts
,
basic_string_view
<
Char
>
format_str
,
basic_format_args
<
buffer_context
<
Char
>>
args
)
{
bool
has_style
=
false
;
if
(
ts
.
has_emphasis
())
{
has_style
=
true
;
a
nsi_color_escape
<
Char
>
escape
=
make_emphasis
<
Char
>
(
ts
.
get_emphasis
());
buf
fer
.
append
(
escape
.
begin
(),
escape
.
end
());
a
uto
emphasis
=
internal
::
make_emphasis
<
Char
>
(
ts
.
get_emphasis
());
buf
.
append
(
emphasis
.
begin
(),
emphasis
.
end
());
}
if
(
ts
.
has_foreground
())
{
has_style
=
true
;
a
nsi_color_escape
<
Char
>
escape
=
make_foreground_color
<
Char
>
(
ts
.
get_foreground
());
buf
fer
.
append
(
escape
.
begin
(),
escape
.
end
());
a
uto
foreground
=
internal
::
make_foreground_color
<
Char
>
(
ts
.
get_foreground
());
buf
.
append
(
foreground
.
begin
(),
foreground
.
end
());
}
if
(
ts
.
has_background
())
{
has_style
=
true
;
a
nsi_color_escape
<
Char
>
escape
=
make_background_color
<
Char
>
(
ts
.
get_background
());
buf
fer
.
append
(
escape
.
begin
(),
escape
.
end
());
a
uto
background
=
internal
::
make_background_color
<
Char
>
(
ts
.
get_background
());
buf
.
append
(
background
.
begin
(),
background
.
end
());
}
internal
::
vformat_to
(
buffer
,
format_str
,
args
);
vformat_to
(
buf
,
format_str
,
args
);
if
(
has_style
)
{
reset_color
<
Char
>
(
buffer
);
internal
::
reset_color
<
Char
>
(
buf
);
}
return
fmt
::
to_string
(
buffer
);
}
}
// namespace internal
template
<
typename
S
,
typename
Char
=
char_t
<
S
>
>
template
<
typename
S
,
typename
Char
=
char_t
<
S
>
>
void
vprint
(
std
::
FILE
*
f
,
const
text_style
&
ts
,
const
S
&
format
,
basic_format_args
<
buffer_context
<
Char
>
>
args
)
{
bool
has_style
=
false
;
if
(
ts
.
has_emphasis
())
{
has_style
=
true
;
internal
::
fputs
<
Char
>
(
internal
::
make_emphasis
<
Char
>
(
ts
.
get_emphasis
()),
f
);
}
if
(
ts
.
has_foreground
())
{
has_style
=
true
;
internal
::
fputs
<
Char
>
(
internal
::
make_foreground_color
<
Char
>
(
ts
.
get_foreground
()),
f
);
}
if
(
ts
.
has_background
())
{
has_style
=
true
;
internal
::
fputs
<
Char
>
(
internal
::
make_background_color
<
Char
>
(
ts
.
get_background
()),
f
);
}
vprint
(
f
,
format
,
args
);
if
(
has_style
)
{
internal
::
reset_color
<
Char
>
(
f
);
}
basic_format_args
<
buffer_context
<
Char
>>
args
)
{
basic_memory_buffer
<
Char
>
buf
;
internal
::
vformat_to
(
buf
,
ts
,
to_string_view
(
format
),
args
);
buf
.
push_back
(
Char
(
0
));
internal
::
fputs
(
buf
.
data
(),
f
);
}
/**
...
...
@@ -536,7 +519,7 @@ template <typename S, typename... Args,
void
print
(
std
::
FILE
*
f
,
const
text_style
&
ts
,
const
S
&
format_str
,
const
Args
&
...
args
)
{
internal
::
check_format_string
<
Args
...
>
(
format_str
);
using
context
=
buffer_context
<
char_t
<
S
>
>
;
using
context
=
buffer_context
<
char_t
<
S
>>
;
format_arg_store
<
context
,
Args
...
>
as
{
args
...};
vprint
(
f
,
ts
,
format_str
,
basic_format_args
<
context
>
(
as
));
}
...
...
@@ -554,11 +537,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return
print
(
stdout
,
ts
,
format_str
,
args
...);
}
template
<
typename
S
,
typename
Char
=
char_t
<
S
>
>
template
<
typename
S
,
typename
Char
=
char_t
<
S
>
>
inline
std
::
basic_string
<
Char
>
vformat
(
const
text_style
&
ts
,
const
S
&
format_str
,
basic_format_args
<
buffer_context
<
Char
>
>
args
)
{
return
internal
::
vformat
(
ts
,
to_string_view
(
format_str
),
args
);
basic_format_args
<
buffer_context
<
Char
>>
args
)
{
basic_memory_buffer
<
Char
>
buf
;
internal
::
vformat_to
(
buf
,
ts
,
to_string_view
(
format_str
),
args
);
return
fmt
::
to_string
(
buf
);
}
/**
...
...
@@ -573,11 +558,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
template
<
typename
S
,
typename
...
Args
,
typename
Char
=
char_t
<
S
>
>
template
<
typename
S
,
typename
...
Args
,
typename
Char
=
char_t
<
S
>
>
inline
std
::
basic_string
<
Char
>
format
(
const
text_style
&
ts
,
const
S
&
format_str
,
const
Args
&
...
args
)
{
return
internal
::
vformat
(
ts
,
to_string_view
(
format_str
),
{
internal
::
make_args_checked
(
format_str
,
args
...)});
return
vformat
(
ts
,
to_string_view
(
format_str
),
{
internal
::
make_args_checked
<
Args
...
>
(
format_str
,
args
...)});
}
FMT_END_NAMESPACE
...
...
include/spdlog/fmt/bundled/compile.h
View file @
0db4b04a
...
...
@@ -14,433 +14,557 @@
FMT_BEGIN_NAMESPACE
namespace
internal
{
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template
<
typename
Char
>
struct
format_part
{
public:
struct
named_argument_id
{
FMT_CONSTEXPR
named_argument_id
(
internal
::
string_view_metadata
id
)
:
id
(
id
)
{}
internal
::
string_view_metadata
id
;
};
struct
argument_id
{
FMT_CONSTEXPR
argument_id
()
:
argument_id
(
0u
)
{}
FMT_CONSTEXPR
argument_id
(
unsigned
id
)
:
which
(
which_arg_id
::
index
),
val
(
id
)
{}
FMT_CONSTEXPR
argument_id
(
internal
::
string_view_metadata
id
)
:
which
(
which_arg_id
::
named_index
),
val
(
id
)
{}
enum
class
kind
{
arg_index
,
arg_name
,
text
,
replacement
};
enum
class
which_arg_id
{
index
,
named_index
};
which_arg_id
which
;
union
value
{
FMT_CONSTEXPR
value
()
:
index
(
0u
)
{}
FMT_CONSTEXPR
value
(
unsigned
id
)
:
index
(
id
)
{}
FMT_CONSTEXPR
value
(
internal
::
string_view_metadata
id
)
:
named_index
(
id
)
{}
unsigned
index
;
internal
::
string_view_metadata
named_index
;
}
val
;
struct
replacement
{
arg_ref
<
Char
>
arg_id
;
dynamic_format_specs
<
Char
>
specs
;
};
struct
specification
{
FMT_CONSTEXPR
specification
()
:
arg_id
(
0u
)
{}
FMT_CONSTEXPR
specification
(
unsigned
id
)
:
arg_id
(
id
)
{}
kind
part_kind
;
union
value
{
unsigned
arg_index
;
basic_string_view
<
Char
>
str
;
replacement
repl
;
FMT_CONSTEXPR
specification
(
internal
::
string_view_metadata
id
)
:
arg_id
(
id
)
{}
FMT_CONSTEXPR
value
(
unsigned
index
=
0
)
:
arg_index
(
index
)
{}
FMT_CONSTEXPR
value
(
basic_string_view
<
Char
>
s
)
:
str
(
s
)
{}
FMT_CONSTEXPR
value
(
replacement
r
)
:
repl
(
r
)
{}
}
val
;
// Position past the end of the argument id.
const
Char
*
arg_id_end
=
nullptr
;
argument_id
arg_id
;
internal
::
dynamic_format_specs
<
Char
>
parsed_specs
;
};
FMT_CONSTEXPR
format_part
(
kind
k
=
kind
::
arg_index
,
value
v
=
{})
:
part_kind
(
k
),
val
(
v
)
{}
FMT_CONSTEXPR
format_part
()
:
which
(
kind
::
argument_id
),
end_of_argument_id
(
0u
),
val
(
0u
)
{}
static
FMT_CONSTEXPR
format_part
make_arg_index
(
unsigned
index
)
{
return
format_part
(
kind
::
arg_index
,
index
);
}
static
FMT_CONSTEXPR
format_part
make_arg_name
(
basic_string_view
<
Char
>
name
)
{
return
format_part
(
kind
::
arg_name
,
name
);
}
static
FMT_CONSTEXPR
format_part
make_text
(
basic_string_view
<
Char
>
text
)
{
return
format_part
(
kind
::
text
,
text
);
}
static
FMT_CONSTEXPR
format_part
make_replacement
(
replacement
repl
)
{
return
format_part
(
kind
::
replacement
,
repl
);
}
};
FMT_CONSTEXPR
format_part
(
internal
::
string_view_metadata
text
)
:
which
(
kind
::
text
),
end_of_argument_id
(
0u
),
val
(
text
)
{}
template
<
typename
Char
>
struct
part_counter
{
unsigned
num_parts
=
0
;
FMT_CONSTEXPR
format_part
(
unsigned
id
)
:
which
(
kind
::
argument_id
),
end_of_argument_id
(
0u
),
val
(
id
)
{}
FMT_CONSTEXPR
void
on_text
(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
!=
end
)
++
num_parts
;
}
FMT_CONSTEXPR
format_part
(
named_argument_id
arg_id
)
:
which
(
kind
::
named_argument_id
),
end_of_argument_id
(
0u
),
val
(
arg_id
)
{}
FMT_CONSTEXPR
void
on_arg_id
()
{
++
num_parts
;
}
FMT_CONSTEXPR
void
on_arg_id
(
unsigned
)
{
++
num_parts
;
}
FMT_CONSTEXPR
void
on_arg_id
(
basic_string_view
<
Char
>
)
{
++
num_parts
;
}
FMT_CONSTEXPR
format_part
(
specification
spec
)
:
which
(
kind
::
specification
),
end_of_argument_id
(
0u
),
val
(
spec
)
{}
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
)
{}
enum
class
kind
{
argument_id
,
named_argument_id
,
text
,
specification
};
FMT_CONSTEXPR
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
end
)
{
// Find the matching brace.
unsigned
brace_counter
=
0
;
for
(;
begin
!=
end
;
++
begin
)
{
if
(
*
begin
==
'{'
)
{
++
brace_counter
;
}
else
if
(
*
begin
==
'}'
)
{
if
(
brace_counter
==
0u
)
break
;
--
brace_counter
;
}
}
return
begin
;
}
kind
which
;
std
::
size_t
end_of_argument_id
;
union
value
{
FMT_CONSTEXPR
value
()
:
arg_id
(
0u
)
{}
FMT_CONSTEXPR
value
(
unsigned
id
)
:
arg_id
(
id
)
{}
FMT_CONSTEXPR
value
(
named_argument_id
named_id
)
:
named_arg_id
(
named_id
.
id
)
{}
FMT_CONSTEXPR
value
(
internal
::
string_view_metadata
t
)
:
text
(
t
)
{}
FMT_CONSTEXPR
value
(
specification
s
)
:
spec
(
s
)
{}
unsigned
arg_id
;
internal
::
string_view_metadata
named_arg_id
;
internal
::
string_view_metadata
text
;
specification
spec
;
}
val
;
FMT_CONSTEXPR
void
on_error
(
const
char
*
)
{}
};
template
<
typename
Char
,
typename
PartsContainer
>
class
format_preparation_handler
:
public
internal
::
error_handler
{
// Counts the number of parts in a format string.
template
<
typename
Char
>
FMT_CONSTEXPR
unsigned
count_parts
(
basic_string_view
<
Char
>
format_str
)
{
part_counter
<
Char
>
counter
;
parse_format_string
<
true
>
(
format_str
,
counter
);
return
counter
.
num_parts
;
}
template
<
typename
Char
,
typename
PartHandler
>
class
format_string_compiler
:
public
error_handler
{
private:
using
part
=
format_part
<
Char
>
;
public:
using
iterator
=
typename
basic_string_view
<
Char
>::
iterator
;
PartHandler
handler_
;
part
part_
;
basic_string_view
<
Char
>
format_str_
;
basic_format_parse_context
<
Char
>
parse_context_
;
FMT_CONSTEXPR
format_preparation_handler
(
basic_string_view
<
Char
>
format
,
PartsContainer
&
parts
)
:
parts_
(
parts
),
format_
(
format
),
parse_context_
(
format
)
{}
public:
FMT_CONSTEXPR
format_string_compiler
(
basic_string_view
<
Char
>
format_str
,
PartHandler
handler
)
:
handler_
(
handler
),
format_str_
(
format_str
),
parse_context_
(
format_str
)
{}
FMT_CONSTEXPR
void
on_text
(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
==
end
)
return
;
const
auto
offset
=
begin
-
format_
.
data
();
const
auto
size
=
end
-
begin
;
parts_
.
push_back
(
part
(
string_view_metadata
(
offset
,
size
)));
if
(
begin
!=
end
)
handler_
(
part
::
make_text
({
begin
,
to_unsigned
(
end
-
begin
)}));
}
FMT_CONSTEXPR
void
on_arg_id
()
{
part
s_
.
push_back
(
part
(
parse_context_
.
next_arg_id
()
));
part
_
=
part
::
make_arg_index
(
parse_context_
.
next_arg_id
(
));
}
FMT_CONSTEXPR
void
on_arg_id
(
unsigned
id
)
{
parse_context_
.
check_arg_id
(
id
);
part
s_
.
push_back
(
part
(
id
)
);
part
_
=
part
::
make_arg_index
(
id
);
}
FMT_CONSTEXPR
void
on_arg_id
(
basic_string_view
<
Char
>
id
)
{
const
auto
view
=
string_view_metadata
(
format_
,
id
);
const
auto
arg_id
=
typename
part
::
named_argument_id
(
view
);
parts_
.
push_back
(
part
(
arg_id
));
part_
=
part
::
make_arg_name
(
id
);
}
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
ptr
)
{
parts_
.
back
().
end_of_argument_id
=
ptr
-
format_
.
begin
();
part_
.
arg_id_end
=
ptr
;
handler_
(
part_
);
}
FMT_CONSTEXPR
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
end
)
{
const
auto
specs_offset
=
to_unsigned
(
begin
-
format_
.
begin
());
using
parse_context
=
basic_parse_context
<
Char
>
;
internal
::
dynamic_format_specs
<
Char
>
parsed_specs
;
dynamic_specs_handler
<
parse_context
>
handler
(
parsed_specs
,
parse_context_
);
begin
=
parse_format_specs
(
begin
,
end
,
handler
);
if
(
*
begin
!=
'}'
)
on_error
(
"missing '}' in format string"
);
auto
&
last_part
=
parts_
.
back
();
auto
specs
=
last_part
.
which
==
part
::
kind
::
argument_id
?
typename
part
::
specification
(
last_part
.
val
.
arg_id
)
:
typename
part
::
specification
(
last_part
.
val
.
named_arg_id
);
specs
.
parsed_specs
=
parsed_specs
;
last_part
=
part
(
specs
);
last_part
.
end_of_argument_id
=
specs_offset
;
return
begin
;
auto
repl
=
typename
part
::
replacement
();
dynamic_specs_handler
<
basic_format_parse_context
<
Char
>>
handler
(
repl
.
specs
,
parse_context_
);
auto
it
=
parse_format_specs
(
begin
,
end
,
handler
);
if
(
*
it
!=
'}'
)
on_error
(
"missing '}' in format string"
);
repl
.
arg_id
=
part_
.
part_kind
==
part
::
kind
::
arg_index
?
arg_ref
<
Char
>
(
part_
.
val
.
arg_index
)
:
arg_ref
<
Char
>
(
part_
.
val
.
str
);
auto
part
=
part
::
make_replacement
(
repl
);
part
.
arg_id_end
=
begin
;
handler_
(
part
);
return
it
;
}
private:
PartsContainer
&
parts_
;
basic_string_view
<
Char
>
format_
;
basic_parse_context
<
Char
>
parse_context_
;
};
template
<
typename
Format
,
typename
PreparedPartsProvider
,
typename
...
Args
>
class
prepared_format
{
public:
using
char_type
=
char_t
<
Format
>
;
using
format_part_t
=
format_part
<
char_type
>
;
constexpr
prepared_format
(
Format
f
)
:
format_
(
std
::
move
(
f
)),
parts_provider_
(
to_string_view
(
format_
))
{}
prepared_format
()
=
delete
;
using
context
=
buffer_context
<
char_type
>
;
template
<
typename
Range
,
typename
Context
>
auto
vformat_to
(
Range
out
,
basic_format_args
<
Context
>
args
)
const
->
typename
Context
::
iterator
{
const
auto
format_view
=
internal
::
to_string_view
(
format_
);
basic_parse_context
<
char_type
>
parse_ctx
(
format_view
);
Context
ctx
(
out
.
begin
(),
args
);
const
auto
&
parts
=
parts_provider_
.
parts
();
for
(
auto
part_it
=
parts
.
begin
();
part_it
!=
parts
.
end
();
++
part_it
)
{
const
auto
&
part
=
*
part_it
;
const
auto
&
value
=
part
.
val
;
switch
(
part
.
which
)
{
case
format_part_t
:
:
kind
::
text
:
{
const
auto
text
=
value
.
text
.
to_view
(
format_view
.
data
());
auto
output
=
ctx
.
out
();
auto
&&
it
=
internal
::
reserve
(
output
,
text
.
size
());
it
=
std
::
copy_n
(
text
.
begin
(),
text
.
size
(),
it
);
ctx
.
advance_to
(
output
);
}
break
;
case
format_part_t
:
:
kind
::
argument_id
:
{
advance_parse_context_to_specification
(
parse_ctx
,
part
);
format_arg
<
Range
>
(
parse_ctx
,
ctx
,
value
.
arg_id
);
}
break
;
case
format_part_t
:
:
kind
::
named_argument_id
:
{
advance_parse_context_to_specification
(
parse_ctx
,
part
);
const
auto
named_arg_id
=
value
.
named_arg_id
.
to_view
(
format_view
.
data
());
format_arg
<
Range
>
(
parse_ctx
,
ctx
,
named_arg_id
);
}
break
;
case
format_part_t
:
:
kind
::
specification
:
{
const
auto
&
arg_id_value
=
value
.
spec
.
arg_id
.
val
;
const
auto
arg
=
value
.
spec
.
arg_id
.
which
==
format_part_t
::
argument_id
::
which_arg_id
::
index
?
ctx
.
arg
(
arg_id_value
.
index
)
:
ctx
.
arg
(
arg_id_value
.
named_index
.
to_view
(
to_string_view
(
format_
).
data
()));
auto
specs
=
value
.
spec
.
parsed_specs
;
handle_dynamic_spec
<
internal
::
width_checker
>
(
specs
.
width
,
specs
.
width_ref
,
ctx
,
format_view
.
begin
());
handle_dynamic_spec
<
internal
::
precision_checker
>
(
specs
.
precision
,
specs
.
precision_ref
,
ctx
,
format_view
.
begin
());
check_prepared_specs
(
specs
,
arg
.
type
());
advance_parse_context_to_specification
(
parse_ctx
,
part
);
ctx
.
advance_to
(
visit_format_arg
(
arg_formatter
<
Range
>
(
ctx
,
nullptr
,
&
specs
),
arg
));
}
break
;
}
}
// Compiles a format string and invokes handler(part) for each parsed part.
template
<
bool
IS_CONSTEXPR
,
typename
Char
,
typename
PartHandler
>
FMT_CONSTEXPR
void
compile_format_string
(
basic_string_view
<
Char
>
format_str
,
PartHandler
handler
)
{
parse_format_string
<
IS_CONSTEXPR
>
(
format_str
,
format_string_compiler
<
Char
,
PartHandler
>
(
format_str
,
handler
));
}
return
ctx
.
out
();
}
template
<
typename
Range
,
typename
Context
,
typename
Id
>
void
format_arg
(
basic_format_parse_context
<
typename
Range
::
value_type
>&
parse_ctx
,
Context
&
ctx
,
Id
arg_id
)
{
ctx
.
advance_to
(
visit_format_arg
(
arg_formatter
<
Range
>
(
ctx
,
&
parse_ctx
),
ctx
.
arg
(
arg_id
)));
}
private:
void
advance_parse_context_to_specification
(
basic_parse_context
<
char_type
>&
parse_ctx
,
const
format_part_t
&
part
)
const
{
const
auto
view
=
to_string_view
(
format_
);
const
auto
specification_begin
=
view
.
data
()
+
part
.
end_of_argument_id
;
advance_to
(
parse_ctx
,
specification_begin
);
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace
cf
{
template
<
typename
Context
,
typename
Range
,
typename
CompiledFormat
>
auto
vformat_to
(
Range
out
,
CompiledFormat
&
cf
,
basic_format_args
<
Context
>
args
)
->
typename
Context
::
iterator
{
using
char_type
=
typename
Context
::
char_type
;
basic_format_parse_context
<
char_type
>
parse_ctx
(
to_string_view
(
cf
.
format_str_
));
Context
ctx
(
out
.
begin
(),
args
);
const
auto
&
parts
=
cf
.
parts
();
for
(
auto
part_it
=
std
::
begin
(
parts
);
part_it
!=
std
::
end
(
parts
);
++
part_it
)
{
const
auto
&
part
=
*
part_it
;
const
auto
&
value
=
part
.
val
;
using
format_part_t
=
format_part
<
char_type
>
;
switch
(
part
.
part_kind
)
{
case
format_part_t
:
:
kind
::
text
:
{
const
auto
text
=
value
.
str
;
auto
output
=
ctx
.
out
();
auto
&&
it
=
reserve
(
output
,
text
.
size
());
it
=
std
::
copy_n
(
text
.
begin
(),
text
.
size
(),
it
);
ctx
.
advance_to
(
output
);
break
;
}
template
<
typename
Range
,
typename
Context
,
typename
Id
>
void
format_arg
(
basic_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
,
Id
arg_id
)
const
{
parse_ctx
.
check_arg_id
(
arg_id
);
const
auto
stopped_at
=
visit_format_arg
(
arg_formatter
<
Range
>
(
ctx
),
ctx
.
arg
(
arg_id
));
ctx
.
advance_to
(
stopped_at
);
case
format_part_t
:
:
kind
::
arg_index
:
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
internal
::
format_arg
<
Range
>
(
parse_ctx
,
ctx
,
value
.
arg_index
);
break
;
case
format_part_t
:
:
kind
::
arg_name
:
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
internal
::
format_arg
<
Range
>
(
parse_ctx
,
ctx
,
value
.
str
);
break
;
case
format_part_t
:
:
kind
::
replacement
:
{
const
auto
&
arg_id_value
=
value
.
repl
.
arg_id
.
val
;
const
auto
arg
=
value
.
repl
.
arg_id
.
kind
==
arg_id_kind
::
index
?
ctx
.
arg
(
arg_id_value
.
index
)
:
ctx
.
arg
(
arg_id_value
.
name
);
auto
specs
=
value
.
repl
.
specs
;
handle_dynamic_spec
<
width_checker
>
(
specs
.
width
,
specs
.
width_ref
,
ctx
);
handle_dynamic_spec
<
precision_checker
>
(
specs
.
precision
,
specs
.
precision_ref
,
ctx
);
error_handler
h
;
numeric_specs_checker
<
error_handler
>
checker
(
h
,
arg
.
type
());
if
(
specs
.
align
==
align
::
numeric
)
checker
.
require_numeric_argument
();
if
(
specs
.
sign
!=
sign
::
none
)
checker
.
check_sign
();
if
(
specs
.
alt
)
checker
.
require_numeric_argument
();
if
(
specs
.
precision
>=
0
)
checker
.
check_precision
();
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
ctx
.
advance_to
(
visit_format_arg
(
arg_formatter
<
Range
>
(
ctx
,
nullptr
,
&
specs
),
arg
));
break
;
}
}
}
return
ctx
.
out
();
}
}
// namespace cf
template
<
typename
Char
>
void
check_prepared_specs
(
const
basic_format_specs
<
Char
>&
specs
,
internal
::
type
arg_type
)
const
{
internal
::
error_handler
h
;
numeric_specs_checker
<
internal
::
error_handler
>
checker
(
h
,
arg_type
);
if
(
specs
.
align
==
align
::
numeric
)
checker
.
require_numeric_argument
();
if
(
specs
.
sign
!=
sign
::
none
)
checker
.
check_sign
();
if
(
specs
.
alt
)
checker
.
require_numeric_argument
();
if
(
specs
.
precision
>=
0
)
checker
.
check_precision
();
}
struct
basic_compiled_format
{};
private:
Format
format_
;
PreparedPartsProvider
parts_provider_
;
}
;
template
<
typename
S
,
typename
=
void
>
struct
compiled_format_base
:
basic_compiled_format
{
using
char_type
=
char_t
<
S
>
;
using
parts_container
=
std
::
vector
<
internal
::
format_part
<
char_type
>>
;
template
<
typename
Char
>
struct
part_counter
{
unsigned
num_parts
=
0
;
parts_container
compiled_parts
;
FMT_CONSTEXPR
void
on_text
(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
!=
end
)
++
num_parts
;
explicit
compiled_format_base
(
basic_string_view
<
char_type
>
format_str
)
{
compile_format_string
<
false
>
(
format_str
,
[
this
](
const
format_part
<
char_type
>&
part
)
{
compiled_parts
.
push_back
(
part
);
});
}
FMT_CONSTEXPR
void
on_arg_id
()
{
++
num_parts
;
}
FMT_CONSTEXPR
void
on_arg_id
(
unsigned
)
{
++
num_parts
;
}
FMT_CONSTEXPR
void
on_arg_id
(
basic_string_view
<
Char
>
)
{
++
num_parts
;
}
const
parts_container
&
parts
()
const
{
return
compiled_parts
;
}
};
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
)
{}
template
<
typename
Char
,
unsigned
N
>
struct
format_part_array
{
format_part
<
Char
>
data
[
N
]
=
{};
FMT_CONSTEXPR
format_part_array
()
=
default
;
};
FMT_CONSTEXPR
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
end
)
{
// Find the matching brace.
unsigned
braces_counter
=
0
;
for
(;
begin
!=
end
;
++
begin
)
{
if
(
*
begin
==
'{'
)
{
++
braces_counter
;
}
else
if
(
*
begin
==
'}'
)
{
if
(
braces_counter
==
0u
)
break
;
--
braces_counter
;
}
template
<
typename
Char
,
unsigned
N
>
FMT_CONSTEXPR
format_part_array
<
Char
,
N
>
compile_to_parts
(
basic_string_view
<
Char
>
format_str
)
{
format_part_array
<
Char
,
N
>
parts
;
unsigned
counter
=
0
;
// This is not a lambda for compatibility with older compilers.
struct
{
format_part
<
Char
>*
parts
;
unsigned
*
counter
;
FMT_CONSTEXPR
void
operator
()(
const
format_part
<
Char
>&
part
)
{
parts
[(
*
counter
)
++
]
=
part
;
}
return
begin
;
}
collector
{
parts
.
data
,
&
counter
};
compile_format_string
<
true
>
(
format_str
,
collector
);
if
(
counter
<
N
)
{
parts
.
data
[
counter
]
=
format_part
<
Char
>::
make_text
(
basic_string_view
<
Char
>
());
}
return
parts
;
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
)
{}
};
template
<
typename
T
>
constexpr
const
T
&
constexpr_max
(
const
T
&
a
,
const
T
&
b
)
{
return
(
a
<
b
)
?
b
:
a
;
}
template
<
typename
Format
>
class
compiletime_prepared_parts_type_provider
{
private:
using
char_type
=
char_t
<
Format
>
;
template
<
typename
S
>
struct
compiled_format_base
<
S
,
enable_if_t
<
is_compile_string
<
S
>::
value
>>
:
basic_compiled_format
{
using
char_type
=
char_t
<
S
>
;
static
FMT_CONSTEXPR
unsigned
count_parts
()
{
FMT_CONSTEXPR_DECL
const
auto
text
=
to_string_view
(
Format
{});
part_counter
<
char_type
>
counter
;
internal
::
parse_format_string
<
/*IS_CONSTEXPR=*/
true
>
(
text
,
counter
);
return
counter
.
num_parts
;
}
FMT_CONSTEXPR
explicit
compiled_format_base
(
basic_string_view
<
char_type
>
)
{}
// Workaround for old compilers.
Compiletime parts prepar
ation will not be
// performed
with them
anyway.
// Workaround for old compilers.
Format string compil
ation will not be
// performed
there
anyway.
#if FMT_USE_CONSTEXPR
static
FMT_CONSTEXPR_DECL
const
unsigned
num
ber_of
_format_parts
=
co
mpiletime_prepared_parts_type_provider
::
count_parts
(
);
static
FMT_CONSTEXPR_DECL
const
unsigned
num_format_parts
=
co
nstexpr_max
(
count_parts
(
to_string_view
(
S
())),
1u
);
#else
static
const
unsigned
num
ber_of_format_parts
=
0u
;
static
const
unsigned
num
_format_parts
=
1
;
#endif
using
parts_container
=
format_part
<
char_type
>
[
num_format_parts
];
const
parts_container
&
parts
()
const
{
static
FMT_CONSTEXPR_DECL
const
auto
compiled_parts
=
compile_to_parts
<
char_type
,
num_format_parts
>
(
internal
::
to_string_view
(
S
()));
return
compiled_parts
.
data
;
}
};
template
<
typename
S
,
typename
...
Args
>
class
compiled_format
:
private
compiled_format_base
<
S
>
{
public:
template
<
unsigned
N
>
struct
format_parts_array
{
using
value_type
=
format_part
<
char_type
>
;
using
typename
compiled_format_base
<
S
>::
char_type
;
FMT_CONSTEXPR
format_parts_array
()
:
arr
{}
{}
private:
basic_string_view
<
char_type
>
format_str_
;
FMT_CONSTEXPR
value_type
&
operator
[](
unsigned
ind
)
{
return
arr
[
ind
];
}
template
<
typename
Context
,
typename
Range
,
typename
CompiledFormat
>
friend
auto
cf
::
vformat_to
(
Range
out
,
CompiledFormat
&
cf
,
basic_format_args
<
Context
>
args
)
->
typename
Context
::
iterator
;
FMT_CONSTEXPR
const
value_type
*
begin
()
const
{
return
arr
;
}
FMT_CONSTEXPR
const
value_type
*
end
()
const
{
return
begin
()
+
N
;
}
public:
compiled_format
()
=
delete
;
explicit
constexpr
compiled_format
(
basic_string_view
<
char_type
>
format_str
)
:
compiled_format_base
<
S
>
(
format_str
),
format_str_
(
format_str
)
{}
};
private:
value_type
arr
[
N
];
};
#ifdef __cpp_if_constexpr
template
<
typename
...
Args
>
struct
type_list
{};
// Returns a reference to the argument at index N from [first, rest...].
template
<
int
N
,
typename
T
,
typename
...
Args
>
constexpr
const
auto
&
get
(
const
T
&
first
,
const
Args
&
...
rest
)
{
static_assert
(
N
<
1
+
sizeof
...(
Args
),
"index is out of bounds"
);
if
constexpr
(
N
==
0
)
return
first
;
else
return
get
<
N
-
1
>
(
rest
...);
}
struct
empty
{
// Parts preparator will search for it
using
value_type
=
format_part
<
char_type
>
;
};
template
<
int
N
,
typename
>
struct
get_type_impl
;
using
type
=
conditional_t
<
number_of_format_parts
!=
0
,
format_parts_array
<
number_of_format_parts
>
,
empty
>
;
template
<
int
N
,
typename
...
Args
>
struct
get_type_impl
<
N
,
type_list
<
Args
...
>>
{
using
type
=
remove_cvref_t
<
decltype
(
get
<
N
>
(
std
::
declval
<
Args
>
()...))
>
;
};
template
<
typename
Parts
>
class
compiletime_prepared_parts_collector
{
private:
using
format_part
=
typename
Parts
::
value_type
;
template
<
int
N
,
typename
T
>
using
get_type
=
typename
get_type_impl
<
N
,
T
>::
type
;
public:
FMT_CONSTEXPR
explicit
compiletime_prepared_parts_collector
(
Parts
&
parts
)
:
parts_
{
parts
},
counter_
{
0u
}
{}
template
<
typename
Char
>
struct
text
{
basic_string_view
<
Char
>
data
;
using
char_type
=
Char
;
FMT_CONSTEXPR
void
push_back
(
format_part
part
)
{
parts_
[
counter_
++
]
=
part
;
}
template
<
typename
OutputIt
,
typename
...
Args
>
OutputIt
format
(
OutputIt
out
,
const
Args
&
...)
const
{
// TODO: reserve
return
copy_str
<
Char
>
(
data
.
begin
(),
data
.
end
(),
out
);
}
};
FMT_CONSTEXPR
format_part
&
back
()
{
return
parts_
[
counter_
-
1
];
}
template
<
typename
Char
>
constexpr
text
<
Char
>
make_text
(
basic_string_view
<
Char
>
s
,
size_t
pos
,
size_t
size
)
{
return
{{
&
s
[
pos
],
size
}};
}
private:
Parts
&
parts_
;
unsigned
counter_
;
};
template
<
typename
Char
,
typename
OutputIt
,
typename
T
,
std
::
enable_if_t
<
std
::
is_integral_v
<
T
>,
int
>
=
0
>
OutputIt
format_default
(
OutputIt
out
,
T
value
)
{
// TODO: reserve
format_int
fi
(
value
);
return
std
::
copy
(
fi
.
data
(),
fi
.
data
()
+
fi
.
size
(),
out
);
}
template
<
typename
PartsContainer
,
typename
Char
>
FMT_CONSTEXPR
PartsContainer
prepare_parts
(
basic_string_view
<
Char
>
format
)
{
PartsContainer
parts
;
internal
::
parse_format_string
<
/*IS_CONSTEXPR=*/
false
>
(
format
,
format_preparation_handler
<
Char
,
PartsContainer
>
(
format
,
parts
));
return
parts
;
template
<
typename
Char
,
typename
OutputIt
>
OutputIt
format_default
(
OutputIt
out
,
double
value
)
{
writer
w
(
out
);
w
.
write
(
value
);
return
w
.
out
();
}
template
<
typename
PartsContainer
,
typename
Char
>
FMT_CONSTEXPR
PartsContainer
prepare_compiletime_parts
(
basic_string_view
<
Char
>
format
)
{
using
collector
=
compiletime_prepared_parts_collector
<
PartsContainer
>
;
template
<
typename
Char
,
typename
OutputIt
>
OutputIt
format_default
(
OutputIt
out
,
Char
value
)
{
*
out
++
=
value
;
return
out
;
}
PartsContainer
parts
;
collector
c
(
parts
);
internal
::
parse_format_string
<
/*IS_CONSTEXPR=*/
true
>
(
format
,
format_preparation_handler
<
Char
,
collector
>
(
format
,
c
));
return
parts
;
template
<
typename
Char
,
typename
OutputIt
>
OutputIt
format_default
(
OutputIt
out
,
const
Char
*
value
)
{
auto
length
=
std
::
char_traits
<
Char
>::
length
(
value
);
return
copy_str
<
Char
>
(
value
,
value
+
length
,
out
);
}
template
<
typename
PartsContainer
>
class
runtime_parts_provider
{
public:
runtime_parts_provider
()
=
delete
;
template
<
typename
Char
>
runtime_parts_provider
(
basic_string_view
<
Char
>
format
)
:
parts_
(
prepare_parts
<
PartsContainer
>
(
format
))
{}
// A replacement field that refers to argument N.
template
<
typename
Char
,
typename
T
,
int
N
>
struct
field
{
using
char_type
=
Char
;
const
PartsContainer
&
parts
()
const
{
return
parts_
;
}
template
<
typename
OutputIt
,
typename
...
Args
>
OutputIt
format
(
OutputIt
out
,
const
Args
&
...
args
)
const
{
// This ensures that the argument type is convertile to `const T&`.
const
T
&
arg
=
get
<
N
>
(
args
...);
return
format_default
<
Char
>
(
out
,
arg
);
}
};
private:
PartsContainer
parts_
;
template
<
typename
L
,
typename
R
>
struct
concat
{
L
lhs
;
R
rhs
;
using
char_type
=
typename
L
::
char_type
;
template
<
typename
OutputIt
,
typename
...
Args
>
OutputIt
format
(
OutputIt
out
,
const
Args
&
...
args
)
const
{
out
=
lhs
.
format
(
out
,
args
...);
return
rhs
.
format
(
out
,
args
...);
}
};
template
<
typename
Format
,
typename
PartsContainer
>
struct
compiletime_parts_provider
{
compiletime_parts_provider
()
=
delete
;
template
<
typename
Char
>
FMT_CONSTEXPR
compiletime_parts_provider
(
basic_string_view
<
Char
>
)
{}
template
<
typename
L
,
typename
R
>
constexpr
concat
<
L
,
R
>
make_concat
(
L
lhs
,
R
rhs
)
{
return
{
lhs
,
rhs
};
}
const
PartsContainer
&
parts
()
const
{
static
FMT_CONSTEXPR_DECL
const
PartsContainer
prepared_parts
=
prepare_compiletime_parts
<
PartsContainer
>
(
internal
::
to_string_view
(
Format
{}));
struct
unknown_format
{};
return
prepared_parts
;
template
<
typename
Char
>
constexpr
size_t
parse_text
(
basic_string_view
<
Char
>
str
,
size_t
pos
)
{
for
(
size_t
size
=
str
.
size
();
pos
!=
size
;
++
pos
)
{
if
(
str
[
pos
]
==
'{'
||
str
[
pos
]
==
'}'
)
break
;
}
};
return
pos
;
}
template
<
typename
Args
,
size_t
POS
,
int
ID
,
typename
S
>
constexpr
auto
compile_format_string
(
S
format_str
);
template
<
typename
Args
,
size_t
POS
,
int
ID
,
typename
T
,
typename
S
>
constexpr
auto
parse_tail
(
T
head
,
S
format_str
)
{
if
constexpr
(
POS
!=
to_string_view
(
format_str
).
size
())
{
constexpr
auto
tail
=
compile_format_string
<
Args
,
POS
,
ID
>
(
format_str
);
if
constexpr
(
std
::
is_same
<
remove_cvref_t
<
decltype
(
tail
)
>
,
unknown_format
>
())
return
tail
;
else
return
make_concat
(
head
,
tail
);
}
else
{
return
head
;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template
<
typename
Args
,
size_t
POS
,
int
ID
,
typename
S
>
constexpr
auto
compile_format_string
(
S
format_str
)
{
using
char_type
=
typename
S
::
char_type
;
constexpr
basic_string_view
<
char_type
>
str
=
format_str
;
if
constexpr
(
str
[
POS
]
==
'{'
)
{
if
(
POS
+
1
==
str
.
size
())
throw
format_error
(
"unmatched '{' in format string"
);
if
constexpr
(
str
[
POS
+
1
]
==
'{'
)
{
return
parse_tail
<
Args
,
POS
+
2
,
ID
>
(
make_text
(
str
,
POS
,
1
),
format_str
);
}
else
if
constexpr
(
str
[
POS
+
1
]
==
'}'
)
{
using
type
=
get_type
<
ID
,
Args
>
;
if
constexpr
(
std
::
is_same
<
type
,
int
>::
value
)
{
return
parse_tail
<
Args
,
POS
+
2
,
ID
+
1
>
(
field
<
char_type
,
type
,
ID
>
(),
format_str
);
}
else
{
return
unknown_format
();
}
}
else
{
return
unknown_format
();
}
}
else
if
constexpr
(
str
[
POS
]
==
'}'
)
{
if
(
POS
+
1
==
str
.
size
())
throw
format_error
(
"unmatched '}' in format string"
);
return
parse_tail
<
Args
,
POS
+
2
,
ID
>
(
make_text
(
str
,
POS
,
1
),
format_str
);
}
else
{
constexpr
auto
end
=
parse_text
(
str
,
POS
+
1
);
return
parse_tail
<
Args
,
end
,
ID
>
(
make_text
(
str
,
POS
,
end
-
POS
),
format_str
);
}
}
#endif // __cpp_if_constexpr
}
// namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template
<
typename
...
Args
,
typename
S
,
FMT_ENABLE_IF
(
is_compile_string
<
S
>
::
value
)
>
FMT_CONSTEXPR
auto
compile
(
S
format_str
)
->
internal
::
prepared_format
<
S
,
internal
::
compiletime_parts_provider
<
S
,
typename
internal
::
compiletime_prepared_parts_type_provider
<
S
>::
type
>
,
Args
...
>
{
return
format_str
;
constexpr
auto
compile
(
S
format_str
)
{
constexpr
basic_string_view
<
typename
S
::
char_type
>
str
=
format_str
;
if
constexpr
(
str
.
size
()
==
0
)
{
return
internal
::
make_text
(
str
,
0
,
0
);
}
else
{
constexpr
auto
result
=
internal
::
compile_format_string
<
internal
::
type_list
<
Args
...
>
,
0
,
0
>
(
format_str
);
if
constexpr
(
std
::
is_same
<
remove_cvref_t
<
decltype
(
result
)
>
,
internal
::
unknown_format
>
())
{
return
internal
::
compiled_format
<
S
,
Args
...
>
(
to_string_view
(
format_str
));
}
else
{
return
result
;
}
}
}
#endif
template
<
typename
CompiledFormat
,
typename
...
Args
,
typename
Char
=
typename
CompiledFormat
::
char_type
,
FMT_ENABLE_IF
(
!
std
::
is_base_of
<
internal
::
basic_compiled_format
,
CompiledFormat
>
::
value
)
>
std
::
basic_string
<
Char
>
format
(
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
basic_memory_buffer
<
Char
>
buffer
;
using
range
=
buffer_range
<
Char
>
;
using
context
=
buffer_context
<
Char
>
;
cf
.
format
(
std
::
back_inserter
(
buffer
),
args
...);
return
to_string
(
buffer
);
}
template
<
typename
OutputIt
,
typename
CompiledFormat
,
typename
...
Args
,
FMT_ENABLE_IF
(
!
std
::
is_base_of
<
internal
::
basic_compiled_format
,
CompiledFormat
>
::
value
)
>
OutputIt
format_to
(
OutputIt
out
,
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
return
cf
.
format
(
out
,
args
...);
}
# else
template
<
typename
...
Args
,
typename
S
,
FMT_ENABLE_IF
(
is_compile_string
<
S
>
::
value
)
>
constexpr
auto
compile
(
S
format_str
)
->
internal
::
compiled_format
<
S
,
Args
...
>
{
return
internal
::
compiled_format
<
S
,
Args
...
>
(
to_string_view
(
format_str
));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// Compiles the format string which must be a string literal.
template
<
typename
...
Args
,
typename
Char
,
size_t
N
>
auto
compile
(
const
Char
(
&
format_str
)[
N
])
->
internal
::
prepared_format
<
std
::
basic_string
<
Char
>
,
internal
::
runtime_parts_provider
<
std
::
vector
<
internal
::
format_part
<
Char
>>>
,
Args
...
>
{
return
std
::
basic_string
<
Char
>
(
format_str
,
N
-
1
);
auto
compile
(
const
Char
(
&
format_str
)[
N
])
->
internal
::
compiled_format
<
const
Char
*
,
Args
...
>
{
return
internal
::
compiled_format
<
const
Char
*
,
Args
...
>
(
basic_string_view
<
Char
>
(
format_str
,
N
-
1
));
}
template
<
typename
CompiledFormat
,
typename
...
Args
,
typename
Char
=
typename
CompiledFormat
::
char_type
>
typename
Char
=
typename
CompiledFormat
::
char_type
,
FMT_ENABLE_IF
(
std
::
is_base_of
<
internal
::
basic_compiled_format
,
CompiledFormat
>
::
value
)
>
std
::
basic_string
<
Char
>
format
(
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
basic_memory_buffer
<
Char
>
buffer
;
using
range
=
internal
::
buffer_range
<
Char
>
;
using
range
=
buffer_range
<
Char
>
;
using
context
=
buffer_context
<
Char
>
;
cf
.
template
vformat_to
<
range
,
context
>(
range
(
buffer
)
,
{
make_format_args
<
context
>
(
args
...)});
internal
::
cf
::
vformat_to
<
context
>
(
range
(
buffer
),
cf
,
{
make_format_args
<
context
>
(
args
...)});
return
to_string
(
buffer
);
}
template
<
typename
OutputIt
,
typename
CompiledFormat
,
typename
...
Args
>
template
<
typename
OutputIt
,
typename
CompiledFormat
,
typename
...
Args
,
FMT_ENABLE_IF
(
std
::
is_base_of
<
internal
::
basic_compiled_format
,
CompiledFormat
>
::
value
)
>
OutputIt
format_to
(
OutputIt
out
,
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
using
char_type
=
typename
CompiledFormat
::
char_type
;
using
range
=
internal
::
output_range
<
OutputIt
,
char_type
>
;
using
context
=
format_context_t
<
OutputIt
,
char_type
>
;
return
cf
.
template
vformat_to
<
range
,
context
>(
range
(
out
),
{
make_format_args
<
context
>
(
args
...)});
return
internal
::
cf
::
vformat_to
<
context
>
(
range
(
out
),
cf
,
{
make_format_args
<
context
>
(
args
...)});
}
template
<
typename
OutputIt
,
typename
CompiledFormat
,
typename
...
Args
,
...
...
@@ -455,10 +579,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template
<
typename
CompiledFormat
,
typename
...
Args
>
std
::
size_t
formatted_size
(
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
return
fmt
::
format_to
(
internal
::
counting_iterator
<
typename
CompiledFormat
::
char_type
>
(),
cf
,
args
...)
.
count
();
return
format_to
(
internal
::
counting_iterator
(),
cf
,
args
...).
count
();
}
FMT_END_NAMESPACE
...
...
include/spdlog/fmt/bundled/core.h
View file @
0db4b04a
...
...
@@ -8,7 +8,6 @@
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
#include <cassert>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
...
...
@@ -16,7 +15,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60
0
00
#define FMT_VERSION 60
1
00
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
...
...
@@ -49,6 +48,12 @@
# define FMT_HAS_GXX_CXX11 0
#endif
#ifdef __NVCC__
# define FMT_NVCC __NVCC__
#else
# define FMT_NVCC 0
#endif
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
#else
...
...
@@ -60,7 +65,8 @@
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
...
...
@@ -133,6 +139,13 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
#endif
#ifndef FMT_BEGIN_NAMESPACE
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
FMT_MSC_VER >= 1900
...
...
@@ -154,9 +167,9 @@
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
# define FMT_API __
pragma(warning(suppress : 4275)) __
declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport)
# define FMT_API __
pragma(warning(suppress : 4275)) __
declspec(dllimport)
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
#endif
...
...
@@ -173,10 +186,6 @@
# define FMT_EXTERN
#endif
#ifndef FMT_ASSERT
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
...
...
@@ -200,6 +209,8 @@ template <typename T>
using
remove_reference_t
=
typename
std
::
remove_reference
<
T
>::
type
;
template
<
typename
T
>
using
remove_const_t
=
typename
std
::
remove_const
<
T
>::
type
;
template
<
typename
T
>
using
remove_cvref_t
=
typename
std
::
remove_cv
<
remove_reference_t
<
T
>>::
type
;
struct
monostate
{};
...
...
@@ -213,6 +224,19 @@ namespace internal {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template
<
typename
...
Ts
>
struct
void_t_impl
{
using
type
=
void
;
};
void
assert_fail
(
const
char
*
file
,
int
line
,
const
char
*
message
);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#if defined(FMT_USE_STRING_VIEW)
template
<
typename
Char
>
using
std_string_view
=
std
::
basic_string_view
<
Char
>
;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
...
...
@@ -222,7 +246,21 @@ using std_string_view = std::experimental::basic_string_view<Char>;
template
<
typename
T
>
struct
std_string_view
{};
#endif
// Casts nonnegative integer to unsigned.
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
using
int128_t
=
__int128_t
;
using
uint128_t
=
__uint128_t
;
#else
# define FMT_USE_INT128 0
#endif
#if !FMT_USE_INT128
struct
int128_t
{};
struct
uint128_t
{};
#endif
// Casts a nonnegative integer to unsigned.
template
<
typename
Int
>
FMT_CONSTEXPR
typename
std
::
make_unsigned
<
Int
>::
type
to_unsigned
(
Int
value
)
{
FMT_ASSERT
(
value
>=
0
,
"negative value"
);
...
...
@@ -266,10 +304,11 @@ template <typename Char> class basic_string_view {
:
data_
(
s
),
size_
(
std
::
char_traits
<
Char
>::
length
(
s
))
{}
/** Constructs a string reference from a ``std::basic_string`` object. */
template
<
typename
Alloc
>
FMT_CONSTEXPR
basic_string_view
(
const
std
::
basic_string
<
Char
,
Alloc
>&
s
)
FMT_NOEXCEPT
:
data_
(
s
.
data
()),
size_
(
s
.
size
())
{}
template
<
typename
Traits
,
typename
Alloc
>
FMT_CONSTEXPR
basic_string_view
(
const
std
::
basic_string
<
Char
,
Traits
,
Alloc
>&
s
)
FMT_NOEXCEPT
:
data_
(
s
.
data
()),
size_
(
s
.
size
())
{}
template
<
typename
S
,
...
...
@@ -286,6 +325,8 @@ template <typename Char> class basic_string_view {
FMT_CONSTEXPR
iterator
begin
()
const
{
return
data_
;
}
FMT_CONSTEXPR
iterator
end
()
const
{
return
data_
+
size_
;
}
FMT_CONSTEXPR
const
Char
&
operator
[](
size_t
pos
)
const
{
return
data_
[
pos
];
}
FMT_CONSTEXPR
void
remove_prefix
(
size_t
n
)
{
data_
+=
n
;
size_
-=
n
;
...
...
@@ -357,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
return
s
;
}
template
<
typename
Char
,
typename
Traits
,
typename
Alloc
ator
>
template
<
typename
Char
,
typename
Traits
,
typename
Alloc
>
inline
basic_string_view
<
Char
>
to_string_view
(
const
std
::
basic_string
<
Char
,
Traits
,
Alloc
ator
>&
s
)
{
return
{
s
.
data
(),
s
.
size
()}
;
const
std
::
basic_string
<
Char
,
Traits
,
Alloc
>&
s
)
{
return
s
;
}
template
<
typename
Char
>
...
...
@@ -405,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
};
struct
error_handler
{
FMT_CONSTEXPR
error_handler
()
{}
FMT_CONSTEXPR
error_handler
(
const
error_handler
&
)
{}
FMT_CONSTEXPR
error_handler
()
=
default
;
FMT_CONSTEXPR
error_handler
(
const
error_handler
&
)
=
default
;
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN
FMT_API
void
on_error
(
const
char
*
message
);
...
...
@@ -416,10 +457,24 @@ struct error_handler {
/** String's character type. */
template
<
typename
S
>
using
char_t
=
typename
internal
::
char_t_impl
<
S
>::
type
;
// Parsing context consisting of a format string range being parsed and an
// argument counter for automatic indexing.
/**
\rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
You can use one of the following type aliases for common character types:
+-----------------------+-------------------------------------+
| Type | Definition |
+=======================+=====================================+
| format_parse_context | basic_format_parse_context<char> |
+-----------------------+-------------------------------------+
| wformat_parse_context | basic_format_parse_context<wchar_t> |
+-----------------------+-------------------------------------+
\endrst
*/
template
<
typename
Char
,
typename
ErrorHandler
=
internal
::
error_handler
>
class
basic_parse_context
:
private
ErrorHandler
{
class
basic_
format_
parse_context
:
private
ErrorHandler
{
private:
basic_string_view
<
Char
>
format_str_
;
int
next_arg_id_
;
...
...
@@ -428,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
using
char_type
=
Char
;
using
iterator
=
typename
basic_string_view
<
Char
>::
iterator
;
explicit
FMT_CONSTEXPR
basic_
parse_context
(
basic_string_view
<
Char
>
format_str
,
ErrorHandler
eh
=
ErrorHandler
())
explicit
FMT_CONSTEXPR
basic_
format_parse_context
(
basic_string_view
<
Char
>
format_str
,
ErrorHandler
eh
=
ErrorHandler
())
:
ErrorHandler
(
eh
),
format_str_
(
format_str
),
next_arg_id_
(
0
)
{}
// Returns an iterator to the beginning of the format string range being
// parsed.
/**
Returns an iterator to the beginning of the format string range being
parsed.
*/
FMT_CONSTEXPR
iterator
begin
()
const
FMT_NOEXCEPT
{
return
format_str_
.
begin
();
}
// Returns an iterator past the end of the format string range being parsed.
/**
Returns an iterator past the end of the format string range being parsed.
*/
FMT_CONSTEXPR
iterator
end
()
const
FMT_NOEXCEPT
{
return
format_str_
.
end
();
}
/
/ Advances the begin iterator to ``it``.
/
** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR
void
advance_to
(
iterator
it
)
{
format_str_
.
remove_prefix
(
internal
::
to_unsigned
(
it
-
begin
()));
}
// Returns the next argument index.
/**
Reports an error if using the manual argument indexing; otherwise returns
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR
int
next_arg_id
()
{
if
(
next_arg_id_
>=
0
)
return
next_arg_id_
++
;
on_error
(
"cannot switch from manual to automatic argument indexing"
);
return
0
;
}
FMT_CONSTEXPR
bool
check_arg_id
(
int
)
{
if
(
next_arg_id_
>
0
)
{
/**
Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
FMT_CONSTEXPR
void
check_arg_id
(
int
)
{
if
(
next_arg_id_
>
0
)
on_error
(
"cannot switch from automatic to manual argument indexing"
);
return
false
;
}
next_arg_id_
=
-
1
;
return
true
;
else
next_arg_id_
=
-
1
;
}
FMT_CONSTEXPR
void
check_arg_id
(
basic_string_view
<
Char
>
)
{}
...
...
@@ -471,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR
ErrorHandler
error_handler
()
const
{
return
*
this
;
}
};
using
format_parse_context
=
basic_parse_context
<
char
>
;
using
wformat_parse_context
=
basic_parse_context
<
wchar_t
>
;
using
format_parse_context
=
basic_
format_
parse_context
<
char
>
;
using
wformat_parse_context
=
basic_
format_
parse_context
<
wchar_t
>
;
using
parse_context
FMT_DEPRECATED
=
basic_parse_context
<
char
>
;
using
wparse_context
FMT_DEPRECATED
=
basic_parse_context
<
wchar_t
>
;
template
<
typename
Char
,
typename
ErrorHandler
=
internal
::
error_handler
>
using
basic_parse_context
FMT_DEPRECATED_ALIAS
=
basic_format_parse_context
<
Char
,
ErrorHandler
>
;
using
parse_context
FMT_DEPRECATED_ALIAS
=
basic_format_parse_context
<
char
>
;
using
wparse_context
FMT_DEPRECATED_ALIAS
=
basic_format_parse_context
<
wchar_t
>
;
template
<
typename
Context
>
class
basic_format_arg
;
template
<
typename
Context
>
class
basic_format_args
;
...
...
@@ -492,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
:
bool_constant
<!
std
::
is_arithmetic
<
T
>::
value
&&
std
::
is_convertible
<
T
,
int
>::
value
>
{};
namespace
internal
{
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template
<
typename
T
,
typename
Context
>
using
has_formatter
=
std
::
is_constructible
<
typename
Context
::
template
formatter_type
<
T
>
>
;
namespace
internal
{
/** A contiguous memory buffer with an optional growing ability. */
template
<
typename
T
>
class
buffer
{
private:
buffer
(
const
buffer
&
)
=
delete
;
void
operator
=
(
const
buffer
&
)
=
delete
;
T
*
ptr_
;
std
::
size_t
size_
;
std
::
size_t
capacity_
;
...
...
@@ -532,7 +596,9 @@ template <typename T> class buffer {
using
value_type
=
T
;
using
const_reference
=
const
T
&
;
virtual
~
buffer
()
{}
buffer
(
const
buffer
&
)
=
delete
;
void
operator
=
(
const
buffer
&
)
=
delete
;
virtual
~
buffer
()
=
default
;
T
*
begin
()
FMT_NOEXCEPT
{
return
ptr_
;
}
T
*
end
()
FMT_NOEXCEPT
{
return
ptr_
+
size_
;
}
...
...
@@ -626,10 +692,13 @@ enum type {
uint_type
,
long_long_type
,
ulong_long_type
,
int128_type
,
uint128_type
,
bool_type
,
char_type
,
last_integer_type
=
char_type
,
// followed by floating-point types.
float_type
,
double_type
,
long_double_type
,
last_numeric_type
=
long_double_type
,
...
...
@@ -652,20 +721,23 @@ FMT_TYPE_CONSTANT(int, int_type);
FMT_TYPE_CONSTANT
(
unsigned
,
uint_type
);
FMT_TYPE_CONSTANT
(
long
long
,
long_long_type
);
FMT_TYPE_CONSTANT
(
unsigned
long
long
,
ulong_long_type
);
FMT_TYPE_CONSTANT
(
int128_t
,
int128_type
);
FMT_TYPE_CONSTANT
(
uint128_t
,
uint128_type
);
FMT_TYPE_CONSTANT
(
bool
,
bool_type
);
FMT_TYPE_CONSTANT
(
Char
,
char_type
);
FMT_TYPE_CONSTANT
(
float
,
float_type
);
FMT_TYPE_CONSTANT
(
double
,
double_type
);
FMT_TYPE_CONSTANT
(
long
double
,
long_double_type
);
FMT_TYPE_CONSTANT
(
const
Char
*
,
cstring_type
);
FMT_TYPE_CONSTANT
(
basic_string_view
<
Char
>
,
string_type
);
FMT_TYPE_CONSTANT
(
const
void
*
,
pointer_type
);
FMT_CONSTEXPR
bool
is_integral
(
type
t
)
{
FMT_CONSTEXPR
bool
is_integral
_type
(
type
t
)
{
FMT_ASSERT
(
t
!=
named_arg_type
,
"invalid argument type"
);
return
t
>
none_type
&&
t
<=
last_integer_type
;
}
FMT_CONSTEXPR
bool
is_arithmetic
(
type
t
)
{
FMT_CONSTEXPR
bool
is_arithmetic
_type
(
type
t
)
{
FMT_ASSERT
(
t
!=
named_arg_type
,
"invalid argument type"
);
return
t
>
none_type
&&
t
<=
last_numeric_type
;
}
...
...
@@ -676,7 +748,7 @@ template <typename Char> struct string_value {
};
template
<
typename
Context
>
struct
custom_value
{
using
parse_context
=
basic_parse_context
<
typename
Context
::
char_type
>
;
using
parse_context
=
basic_
format_
parse_context
<
typename
Context
::
char_type
>
;
const
void
*
value
;
void
(
*
format
)(
const
void
*
arg
,
parse_context
&
parse_ctx
,
Context
&
ctx
);
};
...
...
@@ -691,8 +763,11 @@ template <typename Context> class value {
unsigned
uint_value
;
long
long
long_long_value
;
unsigned
long
long
ulong_long_value
;
int128_t
int128_value
;
uint128_t
uint128_value
;
bool
bool_value
;
char_type
char_value
;
float
float_value
;
double
double_value
;
long
double
long_double_value
;
const
void
*
pointer
;
...
...
@@ -705,6 +780,9 @@ template <typename Context> class value {
FMT_CONSTEXPR
value
(
unsigned
val
)
:
uint_value
(
val
)
{}
value
(
long
long
val
)
:
long_long_value
(
val
)
{}
value
(
unsigned
long
long
val
)
:
ulong_long_value
(
val
)
{}
value
(
int128_t
val
)
:
int128_value
(
val
)
{}
value
(
uint128_t
val
)
:
uint128_value
(
val
)
{}
value
(
float
val
)
:
float_value
(
val
)
{}
value
(
double
val
)
:
double_value
(
val
)
{}
value
(
long
double
val
)
:
long_double_value
(
val
)
{}
value
(
bool
val
)
:
bool_value
(
val
)
{}
...
...
@@ -732,9 +810,9 @@ template <typename Context> class value {
private:
// Formats an argument of a custom type, such as a user-defined class.
template
<
typename
T
,
typename
Formatter
>
static
void
format_custom_arg
(
const
void
*
arg
,
basic
_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
{
static
void
format_custom_arg
(
const
void
*
arg
,
basic_format
_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
{
Formatter
f
;
parse_ctx
.
advance_to
(
f
.
parse
(
parse_ctx
));
ctx
.
advance_to
(
f
.
format
(
*
static_cast
<
const
T
*>
(
arg
),
ctx
));
...
...
@@ -764,6 +842,8 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR
ulong_type
map
(
unsigned
long
val
)
{
return
val
;
}
FMT_CONSTEXPR
long
long
map
(
long
long
val
)
{
return
val
;
}
FMT_CONSTEXPR
unsigned
long
long
map
(
unsigned
long
long
val
)
{
return
val
;
}
FMT_CONSTEXPR
int128_t
map
(
int128_t
val
)
{
return
val
;
}
FMT_CONSTEXPR
uint128_t
map
(
uint128_t
val
)
{
return
val
;
}
FMT_CONSTEXPR
bool
map
(
bool
val
)
{
return
val
;
}
template
<
typename
T
,
FMT_ENABLE_IF
(
is_char
<
T
>
::
value
)
>
...
...
@@ -774,7 +854,7 @@ template <typename Context> struct arg_mapper {
return
val
;
}
FMT_CONSTEXPR
double
map
(
float
val
)
{
return
static_cast
<
double
>
(
val
)
;
}
FMT_CONSTEXPR
float
map
(
float
val
)
{
return
val
;
}
FMT_CONSTEXPR
double
map
(
double
val
)
{
return
val
;
}
FMT_CONSTEXPR
long
double
map
(
long
double
val
)
{
return
val
;
}
...
...
@@ -793,6 +873,15 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR
basic_string_view
<
char_type
>
map
(
const
T
&
val
)
{
return
basic_string_view
<
char_type
>
(
val
);
}
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_constructible
<
std_string_view
<
char_type
>,
T
>::
value
&&
!
std
::
is_constructible
<
basic_string_view
<
char_type
>
,
T
>::
value
&&
!
is_string
<
T
>::
value
)
>
FMT_CONSTEXPR
basic_string_view
<
char_type
>
map
(
const
T
&
val
)
{
return
std_string_view
<
char_type
>
(
val
);
}
FMT_CONSTEXPR
const
char
*
map
(
const
signed
char
*
val
)
{
static_assert
(
std
::
is_same
<
char_type
,
char
>::
value
,
"invalid string type"
);
return
reinterpret_cast
<
const
char
*>
(
val
);
...
...
@@ -818,11 +907,14 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF
(
std
::
is_enum
<
T
>
::
value
&&
!
has_formatter
<
T
,
Context
>::
value
&&
!
has_fallback_formatter
<
T
,
Context
>::
value
)
>
FMT_CONSTEXPR
int
map
(
const
T
&
val
)
{
return
static_cast
<
int
>
(
val
);
FMT_CONSTEXPR
auto
map
(
const
T
&
val
)
->
decltype
(
map
(
static_cast
<
typename
std
::
underlying_type
<
T
>::
type
>
(
val
)))
{
return
map
(
static_cast
<
typename
std
::
underlying_type
<
T
>::
type
>
(
val
));
}
template
<
typename
T
,
FMT_ENABLE_IF
(
!
is_string
<
T
>
::
value
&&
!
is_char
<
T
>::
value
&&
!
std
::
is_constructible
<
basic_string_view
<
char_type
>
,
T
>::
value
&&
(
has_formatter
<
T
,
Context
>::
value
||
has_fallback_formatter
<
T
,
Context
>::
value
))
>
FMT_CONSTEXPR
const
T
&
map
(
const
T
&
val
)
{
...
...
@@ -841,12 +933,13 @@ template <typename Context> struct arg_mapper {
// A type constant after applying arg_mapper<Context>.
template
<
typename
T
,
typename
Context
>
using
mapped_type_constant
=
type_constant
<
decltype
(
arg_mapper
<
Context
>
().
map
(
std
::
declval
<
T
>
())),
type_constant
<
decltype
(
arg_mapper
<
Context
>
().
map
(
std
::
declval
<
const
T
&
>
())),
typename
Context
::
char_type
>
;
enum
{
packed_arg_bits
=
5
};
// Maximum number of arguments with packed types.
enum
{
max_packed_args
=
15
};
enum
:
unsigned
long
long
{
is_unpacked_bit
=
1
ull
<<
63
};
enum
{
max_packed_args
=
63
/
packed_arg_bits
};
enum
:
unsigned
long
long
{
is_unpacked_bit
=
1
ULL
<<
63
};
template
<
typename
Context
>
class
arg_map
;
}
// namespace internal
...
...
@@ -877,7 +970,8 @@ template <typename Context> class basic_format_arg {
public:
explicit
handle
(
internal
::
custom_value
<
Context
>
custom
)
:
custom_
(
custom
)
{}
void
format
(
basic_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
const
{
void
format
(
basic_format_parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
const
{
custom_
.
format
(
custom_
.
value
,
parse_ctx
,
ctx
);
}
...
...
@@ -893,8 +987,8 @@ template <typename Context> class basic_format_arg {
internal
::
type
type
()
const
{
return
type_
;
}
bool
is_integral
()
const
{
return
internal
::
is_integral
(
type_
);
}
bool
is_arithmetic
()
const
{
return
internal
::
is_arithmetic
(
type_
);
}
bool
is_integral
()
const
{
return
internal
::
is_integral
_type
(
type_
);
}
bool
is_arithmetic
()
const
{
return
internal
::
is_arithmetic
_type
(
type_
);
}
};
/**
...
...
@@ -923,10 +1017,22 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return
vis
(
arg
.
value_
.
long_long_value
);
case
internal
:
:
ulong_long_type
:
return
vis
(
arg
.
value_
.
ulong_long_value
);
#if FMT_USE_INT128
case
internal
:
:
int128_type
:
return
vis
(
arg
.
value_
.
int128_value
);
case
internal
:
:
uint128_type
:
return
vis
(
arg
.
value_
.
uint128_value
);
#else
case
internal
:
:
int128_type
:
case
internal
:
:
uint128_type
:
break
;
#endif
case
internal
:
:
bool_type
:
return
vis
(
arg
.
value_
.
bool_value
);
case
internal
:
:
char_type
:
return
vis
(
arg
.
value_
.
char_value
);
case
internal
:
:
float_type
:
return
vis
(
arg
.
value_
.
float_value
);
case
internal
:
:
double_type
:
return
vis
(
arg
.
value_
.
double_value
);
case
internal
:
:
long_double_type
:
...
...
@@ -948,9 +1054,6 @@ namespace internal {
// A map from argument names to their values for named arguments.
template
<
typename
Context
>
class
arg_map
{
private:
arg_map
(
const
arg_map
&
)
=
delete
;
void
operator
=
(
const
arg_map
&
)
=
delete
;
using
char_type
=
typename
Context
::
char_type
;
struct
entry
{
...
...
@@ -968,6 +1071,8 @@ template <typename Context> class arg_map {
}
public:
arg_map
(
const
arg_map
&
)
=
delete
;
void
operator
=
(
const
arg_map
&
)
=
delete
;
arg_map
()
:
map_
(
nullptr
),
size_
(
0
)
{}
void
init
(
const
basic_format_args
<
Context
>&
args
);
~
arg_map
()
{
delete
[]
map_
;
}
...
...
@@ -990,6 +1095,8 @@ class locale_ref {
locale_ref
()
:
locale_
(
nullptr
)
{}
template
<
typename
Locale
>
explicit
locale_ref
(
const
Locale
&
loc
);
explicit
operator
bool
()
const
FMT_NOEXCEPT
{
return
locale_
!=
nullptr
;
}
template
<
typename
Locale
>
Locale
get
()
const
;
};
...
...
@@ -998,7 +1105,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template
<
typename
Context
,
typename
Arg
,
typename
...
Args
>
constexpr
unsigned
long
long
encode_types
()
{
return
mapped_type_constant
<
Arg
,
Context
>::
value
|
(
encode_types
<
Context
,
Args
...
>
()
<<
4
);
(
encode_types
<
Context
,
Args
...
>
()
<<
packed_arg_bits
);
}
template
<
typename
Context
,
typename
T
>
...
...
@@ -1034,14 +1141,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal
::
arg_map
<
basic_format_context
>
map_
;
internal
::
locale_ref
loc_
;
basic_format_context
(
const
basic_format_context
&
)
=
delete
;
void
operator
=
(
const
basic_format_context
&
)
=
delete
;
public:
using
iterator
=
OutputIt
;
using
format_arg
=
basic_format_arg
<
basic_format_context
>
;
template
<
typename
T
>
using
formatter_type
=
formatter
<
T
,
char_type
>
;
basic_format_context
(
const
basic_format_context
&
)
=
delete
;
void
operator
=
(
const
basic_format_context
&
)
=
delete
;
/**
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
...
...
@@ -1143,8 +1249,9 @@ template <typename Context> class basic_format_args {
bool
is_packed
()
const
{
return
(
types_
&
internal
::
is_unpacked_bit
)
==
0
;
}
internal
::
type
type
(
int
index
)
const
{
int
shift
=
index
*
4
;
return
static_cast
<
internal
::
type
>
((
types_
&
(
0xfull
<<
shift
))
>>
shift
);
int
shift
=
index
*
internal
::
packed_arg_bits
;
unsigned
int
mask
=
(
1
<<
internal
::
packed_arg_bits
)
-
1
;
return
static_cast
<
internal
::
type
>
((
types_
>>
shift
)
&
mask
);
}
friend
class
internal
::
arg_map
<
Context
>
;
...
...
@@ -1371,7 +1478,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
}
FMT_API
void
vprint
(
std
::
FILE
*
f
,
string_view
format_str
,
format_args
args
);
FMT_API
void
vprint
(
st
d
::
FILE
*
f
,
wstring_view
format_str
,
w
format_args
args
);
FMT_API
void
vprint
(
st
ring_view
format_str
,
format_args
args
);
/**
\rst
...
...
@@ -1391,9 +1498,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
internal
::
make_args_checked
<
Args
...
>
(
format_str
,
args
...));
}
FMT_API
void
vprint
(
string_view
format_str
,
format_args
args
);
FMT_API
void
vprint
(
wstring_view
format_str
,
wformat_args
args
);
/**
\rst
Prints formatted data to ``stdout``.
...
...
include/spdlog/fmt/bundled/format-inl.h
View file @
0db4b04a
// Formatting library for C++
// Formatting library for C++
- implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
...
...
@@ -10,14 +10,11 @@
#include "format.h"
#include <string.h>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#include <cstring> // for std::memmove
#include <cwchar>
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
...
...
@@ -47,25 +44,22 @@
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4127) // conditional expression is constant
# pragma warning(disable : 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable : 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
inline
fmt
::
internal
::
null
<>
strerror_r
(
int
,
char
*
,
...)
{
return
fmt
::
internal
::
null
<>
();
}
inline
fmt
::
internal
::
null
<>
strerror_s
(
char
*
,
std
::
size_t
,
...)
{
return
fmt
::
internal
::
null
<>
();
}
inline
fmt
::
internal
::
null
<>
strerror_r
(
int
,
char
*
,
...)
{
return
{};
}
inline
fmt
::
internal
::
null
<>
strerror_s
(
char
*
,
std
::
size_t
,
...)
{
return
{};
}
FMT_BEGIN_NAMESPACE
namespace
internal
{
FMT_FUNC
void
assert_fail
(
const
char
*
file
,
int
line
,
const
char
*
message
)
{
print
(
stderr
,
"{}:{}: assertion failed: {}"
,
file
,
line
,
message
);
std
::
abort
();
}
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
...
...
@@ -81,7 +75,7 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
using
format_func
=
void
(
*
)(
internal
::
buffer
<
char
>&
,
int
,
string_view
);
//
P
ortable thread-safe version of strerror.
//
A p
ortable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
...
...
@@ -158,7 +152,7 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static
const
char
ERROR_STR
[]
=
"error "
;
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std
::
size_t
error_code_size
=
sizeof
(
SEP
)
+
sizeof
(
ERROR_STR
)
-
2
;
auto
abs_value
=
static_cast
<
uint32_or_64_t
<
int
>>
(
error_code
);
auto
abs_value
=
static_cast
<
uint32_or_64_
or_128_
t
<
int
>>
(
error_code
);
if
(
internal
::
is_negative
(
error_code
))
{
abs_value
=
0
-
abs_value
;
++
error_code_size
;
...
...
@@ -206,6 +200,9 @@ template <typename Locale> Locale locale_ref::get() const {
return
locale_
?
*
static_cast
<
const
std
::
locale
*>
(
locale_
)
:
std
::
locale
();
}
template
<
typename
Char
>
FMT_FUNC
std
::
string
grouping_impl
(
locale_ref
loc
)
{
return
std
::
use_facet
<
std
::
numpunct
<
Char
>>
(
loc
.
get
<
std
::
locale
>
()).
grouping
();
}
template
<
typename
Char
>
FMT_FUNC
Char
thousands_sep_impl
(
locale_ref
loc
)
{
return
std
::
use_facet
<
std
::
numpunct
<
Char
>>
(
loc
.
get
<
std
::
locale
>
())
.
thousands_sep
();
...
...
@@ -217,6 +214,10 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
}
// namespace internal
#else
template
<
typename
Char
>
FMT_FUNC
std
::
string
internal
::
grouping_impl
(
locale_ref
)
{
return
"
\03
"
;
}
template
<
typename
Char
>
FMT_FUNC
Char
internal
::
thousands_sep_impl
(
locale_ref
)
{
return
FMT_STATIC_THOUSANDS_SEPARATOR
;
}
...
...
@@ -226,8 +227,8 @@ FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
}
#endif
FMT_API
FMT_FUNC
format_error
::~
format_error
()
FMT_NOEXCEPT
{}
FMT_API
FMT_FUNC
system_error
::~
system_error
()
FMT_NOEXCEPT
{}
FMT_API
FMT_FUNC
format_error
::~
format_error
()
FMT_NOEXCEPT
=
default
;
FMT_API
FMT_FUNC
system_error
::~
system_error
()
FMT_NOEXCEPT
=
default
;
FMT_FUNC
void
system_error
::
init
(
int
err_code
,
string_view
format_str
,
format_args
args
)
{
...
...
@@ -241,27 +242,13 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
namespace
internal
{
template
<
>
FMT_FUNC
int
count_digits
<
4
>
(
internal
::
fallback_uintptr
n
)
{
//
Assume little endian; pointer formatting is implementation-defined anyway
.
//
fallback_uintptr is always stored in little endian
.
int
i
=
static_cast
<
int
>
(
sizeof
(
void
*
))
-
1
;
while
(
i
>
0
&&
n
.
value
[
i
]
==
0
)
--
i
;
auto
char_digits
=
std
::
numeric_limits
<
unsigned
char
>::
digits
/
4
;
return
i
>=
0
?
i
*
char_digits
+
count_digits
<
4
,
unsigned
>
(
n
.
value
[
i
])
:
1
;
}
template
<
typename
T
>
int
format_float
(
char
*
buf
,
std
::
size_t
size
,
const
char
*
format
,
int
precision
,
T
value
)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if
(
precision
>
100000
)
throw
std
::
runtime_error
(
"fuzz mode - avoid large allocation inside snprintf"
);
#endif
// Suppress the warning about nonliteral format string.
auto
snprintf_ptr
=
FMT_SNPRINTF
;
return
precision
<
0
?
snprintf_ptr
(
buf
,
size
,
format
,
value
)
:
snprintf_ptr
(
buf
,
size
,
format
,
precision
,
value
);
}
template
<
typename
T
>
const
char
basic_data
<
T
>::
digits
[]
=
"0001020304050607080910111213141516171819"
...
...
@@ -274,14 +261,14 @@ template <typename T>
const
char
basic_data
<
T
>::
hex_digits
[]
=
"0123456789abcdef"
;
#define FMT_POWERS_OF_10(factor) \
factor * 10,
factor * 100, factor * 1000, factor * 10000, factor *
100000, \
factor * 1000000, factor * 10000000, factor *
100000000, \
factor *
1000000000
factor * 10,
(factor)*100, (factor)*1000, (factor)*10000, (factor)*
100000, \
(factor)*1000000, (factor)*10000000, (factor)*
100000000, \
(factor)*
1000000000
template
<
typename
T
>
const
uint64_t
basic_data
<
T
>::
powers_of_10_64
[]
=
{
1
,
FMT_POWERS_OF_10
(
1
),
FMT_POWERS_OF_10
(
1000000000
ull
),
10000000000000000000
ull
};
1
,
FMT_POWERS_OF_10
(
1
),
FMT_POWERS_OF_10
(
1000000000
ULL
),
10000000000000000000
ULL
};
template
<
typename
T
>
const
uint32_t
basic_data
<
T
>::
zero_or_powers_of_10_32
[]
=
{
0
,
...
...
@@ -289,8 +276,8 @@ const uint32_t basic_data<T>::zero_or_powers_of_10_32[] = {0,
template
<
typename
T
>
const
uint64_t
basic_data
<
T
>::
zero_or_powers_of_10_64
[]
=
{
0
,
FMT_POWERS_OF_10
(
1
),
FMT_POWERS_OF_10
(
1000000000
ull
),
10000000000000000000
ull
};
0
,
FMT_POWERS_OF_10
(
1
),
FMT_POWERS_OF_10
(
1000000000
ULL
),
10000000000000000000
ULL
};
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
// These are generated by support/compute-powers.py.
...
...
@@ -346,12 +333,24 @@ template <typename T>
const
char
basic_data
<
T
>::
background_color
[]
=
"
\x1b
[48;2;"
;
template
<
typename
T
>
const
char
basic_data
<
T
>::
reset_color
[]
=
"
\x1b
[0m"
;
template
<
typename
T
>
const
wchar_t
basic_data
<
T
>::
wreset_color
[]
=
L"
\x1b
[0m"
;
template
<
typename
T
>
const
char
basic_data
<
T
>::
signs
[]
=
{
0
,
'-'
,
'+'
,
' '
};
template
<
typename
T
>
struct
bits
{
static
FMT_CONSTEXPR_DECL
const
int
value
=
static_cast
<
int
>
(
sizeof
(
T
)
*
std
::
numeric_limits
<
unsigned
char
>::
digits
);
};
class
fp
;
template
<
int
SHIFT
=
0
>
fp
normalize
(
fp
value
);
// Lower (upper) boundary is a value half way between a floating-point value
// and its predecessor (successor). Boundaries have the same exponent as the
// value so only significands are stored.
struct
boundaries
{
uint64_t
lower
;
uint64_t
upper
;
};
// A handmade floating-point number f * pow(2, e).
class
fp
{
private:
...
...
@@ -363,7 +362,7 @@ class fp {
static
FMT_CONSTEXPR_DECL
const
int
double_significand_size
=
std
::
numeric_limits
<
double
>::
digits
-
1
;
static
FMT_CONSTEXPR_DECL
const
uint64_t
implicit_bit
=
1
ull
<<
double_significand_size
;
1
ULL
<<
double_significand_size
;
public:
significand_type
f
;
...
...
@@ -377,95 +376,377 @@ class fp {
// Constructs fp from an IEEE754 double. It is a template to prevent compile
// errors on platforms where double is not IEEE754.
template
<
typename
Double
>
explicit
fp
(
Double
d
)
{
template
<
typename
Double
>
explicit
fp
(
Double
d
)
{
assign
(
d
);
}
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template
<
int
SHIFT
>
friend
fp
normalize
(
fp
value
)
{
// Handle subnormals.
const
auto
shifted_implicit_bit
=
fp
::
implicit_bit
<<
SHIFT
;
while
((
value
.
f
&
shifted_implicit_bit
)
==
0
)
{
value
.
f
<<=
1
;
--
value
.
e
;
}
// Subtract 1 to account for hidden bit.
const
auto
offset
=
fp
::
significand_size
-
fp
::
double_significand_size
-
SHIFT
-
1
;
value
.
f
<<=
offset
;
value
.
e
-=
offset
;
return
value
;
}
// Assigns d to this and return true iff predecessor is closer than successor.
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
==
sizeof
(
uint64_t
))>
bool
assign
(
Double
d
)
{
// Assume double is in the format [sign][exponent][significand].
using
limits
=
std
::
numeric_limits
<
Double
>
;
const
int
exponent_size
=
bits
<
Double
>::
value
-
double_significand_size
-
1
;
// -1 for sign
const
uint64_t
significand_mask
=
implicit_bit
-
1
;
const
uint64_t
exponent_mask
=
(
~
0
ull
>>
1
)
&
~
significand_mask
;
const
uint64_t
exponent_mask
=
(
~
0
ULL
>>
1
)
&
~
significand_mask
;
const
int
exponent_bias
=
(
1
<<
exponent_size
)
-
limits
::
max_exponent
-
1
;
auto
u
=
bit_cast
<
uint64_t
>
(
d
);
auto
biased_e
=
(
u
&
exponent_mask
)
>>
double_significand_size
;
f
=
u
&
significand_mask
;
auto
biased_e
=
(
u
&
exponent_mask
)
>>
double_significand_size
;
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
// the smallest normalized number (biased_e > 1).
bool
is_predecessor_closer
=
f
==
0
&&
biased_e
>
1
;
if
(
biased_e
!=
0
)
f
+=
implicit_bit
;
else
biased_e
=
1
;
// Subnormals use biased exponent 1 (min exponent).
e
=
static_cast
<
int
>
(
biased_e
-
exponent_bias
-
double_significand_size
);
return
is_predecessor_closer
;
}
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template
<
int
SHIFT
=
0
>
void
normalize
()
{
// Handle subnormals.
auto
shifted_implicit_bit
=
implicit_bit
<<
SHIFT
;
while
((
f
&
shifted_implicit_bit
)
==
0
)
{
f
<<=
1
;
--
e
;
}
// Subtract 1 to account for hidden bit.
auto
offset
=
significand_size
-
double_significand_size
-
SHIFT
-
1
;
f
<<=
offset
;
e
-=
offset
;
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
!=
sizeof
(
uint64_t
))>
bool
assign
(
Double
)
{
*
this
=
fp
();
return
false
;
}
//
Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
// a boundary is a value half way between the number and its predecessor
//
Assigns d to this together with computing lower and upper boundaries,
//
where
a boundary is a value half way between the number and its predecessor
// (lower) or successor (upper). The upper boundary is normalized and lower
// has the same exponent but may be not normalized.
void
compute_boundaries
(
fp
&
lower
,
fp
&
upper
)
const
{
lower
=
f
==
implicit_bit
?
fp
((
f
<<
2
)
-
1
,
e
-
2
)
:
fp
((
f
<<
1
)
-
1
,
e
-
1
);
upper
=
fp
((
f
<<
1
)
+
1
,
e
-
1
);
upper
.
normalize
<
1
>
();
// 1 is to account for the exponent shift above.
template
<
typename
Double
>
boundaries
assign_with_boundaries
(
Double
d
)
{
bool
is_lower_closer
=
assign
(
d
);
fp
lower
=
is_lower_closer
?
fp
((
f
<<
2
)
-
1
,
e
-
2
)
:
fp
((
f
<<
1
)
-
1
,
e
-
1
);
// 1 in normalize accounts for the exponent shift above.
fp
upper
=
normalize
<
1
>
(
fp
((
f
<<
1
)
+
1
,
e
-
1
));
lower
.
f
<<=
lower
.
e
-
upper
.
e
;
return
boundaries
{
lower
.
f
,
upper
.
f
};
}
template
<
typename
Double
>
boundaries
assign_float_with_boundaries
(
Double
d
)
{
assign
(
d
);
constexpr
int
min_normal_e
=
std
::
numeric_limits
<
float
>::
min_exponent
-
std
::
numeric_limits
<
double
>::
digits
;
significand_type
half_ulp
=
1
<<
(
std
::
numeric_limits
<
double
>::
digits
-
std
::
numeric_limits
<
float
>::
digits
-
1
);
if
(
min_normal_e
>
e
)
half_ulp
<<=
min_normal_e
-
e
;
fp
upper
=
normalize
<
0
>
(
fp
(
f
+
half_ulp
,
e
));
fp
lower
=
fp
(
f
-
(
half_ulp
>>
((
f
==
implicit_bit
&&
e
>
min_normal_e
)
?
1
:
0
)),
e
);
lower
.
f
<<=
lower
.
e
-
upper
.
e
;
lower
.
e
=
upper
.
e
;
return
boundaries
{
lower
.
f
,
upper
.
f
}
;
}
};
// Returns an fp number representing x - y. Result may not be normalized.
inline
fp
operator
-
(
fp
x
,
fp
y
)
{
FMT_ASSERT
(
x
.
f
>=
y
.
f
&&
x
.
e
==
y
.
e
,
"invalid operands"
);
return
fp
(
x
.
f
-
y
.
f
,
x
.
e
);
}
inline
bool
operator
==
(
fp
x
,
fp
y
)
{
return
x
.
f
==
y
.
f
&&
x
.
e
==
y
.
e
;
}
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be
// normalized.
FMT_FUNC
fp
operator
*
(
fp
x
,
fp
y
)
{
int
exp
=
x
.
e
+
y
.
e
+
64
;
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
inline
uint64_t
multiply
(
uint64_t
lhs
,
uint64_t
rhs
)
{
#if FMT_USE_INT128
auto
product
=
static_cast
<
__uint128_t
>
(
x
.
f
)
*
y
.
f
;
auto
product
=
static_cast
<
__uint128_t
>
(
lhs
)
*
rhs
;
auto
f
=
static_cast
<
uint64_t
>
(
product
>>
64
);
if
((
static_cast
<
uint64_t
>
(
product
)
&
(
1ULL
<<
63
))
!=
0
)
++
f
;
return
fp
(
f
,
exp
);
return
(
static_cast
<
uint64_t
>
(
product
)
&
(
1ULL
<<
63
))
!=
0
?
f
+
1
:
f
;
#else
// Multiply 32-bit parts of significands.
uint64_t
mask
=
(
1ULL
<<
32
)
-
1
;
uint64_t
a
=
x
.
f
>>
32
,
b
=
x
.
f
&
mask
;
uint64_t
c
=
y
.
f
>>
32
,
d
=
y
.
f
&
mask
;
uint64_t
a
=
lhs
>>
32
,
b
=
lhs
&
mask
;
uint64_t
c
=
rhs
>>
32
,
d
=
rhs
&
mask
;
uint64_t
ac
=
a
*
c
,
bc
=
b
*
c
,
ad
=
a
*
d
,
bd
=
b
*
d
;
// Compute mid 64-bit of result and round.
uint64_t
mid
=
(
bd
>>
32
)
+
(
ad
&
mask
)
+
(
bc
&
mask
)
+
(
1U
<<
31
);
return
fp
(
ac
+
(
ad
>>
32
)
+
(
bc
>>
32
)
+
(
mid
>>
32
),
exp
);
return
ac
+
(
ad
>>
32
)
+
(
bc
>>
32
)
+
(
mid
>>
32
);
#endif
}
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 28.
inline
fp
operator
*
(
fp
x
,
fp
y
)
{
return
{
multiply
(
x
.
f
,
y
.
f
),
x
.
e
+
y
.
e
+
64
};
}
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
FMT_FUNC
fp
get_cached_power
(
int
min_exponent
,
int
&
pow10_exponent
)
{
const
double
one_over_log2_10
=
0.30102999566398114
;
// 1 / log2(10
)
const
uint64_t
one_over_log2_10
=
0x4d104d42
;
// round(pow(2, 32) / log2(10)
)
int
index
=
static_cast
<
int
>
(
std
::
ceil
((
min_exponent
+
fp
::
significand_size
-
1
)
*
one_over_log2_10
));
static_cast
<
int64_t
>
(
(
min_exponent
+
fp
::
significand_size
-
1
)
*
one_over_log2_10
+
((
uint64_t
(
1
)
<<
32
)
-
1
)
// ceil
)
>>
32
// arithmetic shift
);
// Decimal exponent of the first (smallest) cached power of 10.
const
int
first_dec_exp
=
-
348
;
// Difference between 2 consecutive decimal exponents in cached powers of 10.
const
int
dec_exp_step
=
8
;
index
=
(
index
-
first_dec_exp
-
1
)
/
dec_exp_step
+
1
;
pow10_exponent
=
first_dec_exp
+
index
*
dec_exp_step
;
return
fp
(
data
::
pow10_significands
[
index
],
data
::
pow10_exponents
[
index
])
;
return
{
data
::
pow10_significands
[
index
],
data
::
pow10_exponents
[
index
]}
;
}
// A simple accumulator to hold the sums of terms in bigint::square if uint128_t
// is not available.
struct
accumulator
{
uint64_t
lower
;
uint64_t
upper
;
accumulator
()
:
lower
(
0
),
upper
(
0
)
{}
explicit
operator
uint32_t
()
const
{
return
static_cast
<
uint32_t
>
(
lower
);
}
void
operator
+=
(
uint64_t
n
)
{
lower
+=
n
;
if
(
lower
<
n
)
++
upper
;
}
void
operator
>>=
(
int
shift
)
{
assert
(
shift
==
32
);
(
void
)
shift
;
lower
=
(
upper
<<
32
)
|
(
lower
>>
32
);
upper
>>=
32
;
}
};
class
bigint
{
private:
// A bigint is stored as an array of bigits (big digits), with bigit at index
// 0 being the least significant one.
using
bigit
=
uint32_t
;
using
double_bigit
=
uint64_t
;
enum
{
bigits_capacity
=
32
};
basic_memory_buffer
<
bigit
,
bigits_capacity
>
bigits_
;
int
exp_
;
static
FMT_CONSTEXPR_DECL
const
int
bigit_bits
=
bits
<
bigit
>::
value
;
friend
struct
formatter
<
bigint
>
;
void
subtract_bigits
(
int
index
,
bigit
other
,
bigit
&
borrow
)
{
auto
result
=
static_cast
<
double_bigit
>
(
bigits_
[
index
])
-
other
-
borrow
;
bigits_
[
index
]
=
static_cast
<
bigit
>
(
result
);
borrow
=
static_cast
<
bigit
>
(
result
>>
(
bigit_bits
*
2
-
1
));
}
void
remove_leading_zeros
()
{
int
num_bigits
=
static_cast
<
int
>
(
bigits_
.
size
())
-
1
;
while
(
num_bigits
>
0
&&
bigits_
[
num_bigits
]
==
0
)
--
num_bigits
;
bigits_
.
resize
(
num_bigits
+
1
);
}
// Computes *this -= other assuming aligned bigints and *this >= other.
void
subtract_aligned
(
const
bigint
&
other
)
{
FMT_ASSERT
(
other
.
exp_
>=
exp_
,
"unaligned bigints"
);
FMT_ASSERT
(
compare
(
*
this
,
other
)
>=
0
,
""
);
bigit
borrow
=
0
;
int
i
=
other
.
exp_
-
exp_
;
for
(
int
j
=
0
,
n
=
static_cast
<
int
>
(
other
.
bigits_
.
size
());
j
!=
n
;
++
i
,
++
j
)
{
subtract_bigits
(
i
,
other
.
bigits_
[
j
],
borrow
);
}
while
(
borrow
>
0
)
subtract_bigits
(
i
,
0
,
borrow
);
remove_leading_zeros
();
}
void
multiply
(
uint32_t
value
)
{
const
double_bigit
wide_value
=
value
;
bigit
carry
=
0
;
for
(
size_t
i
=
0
,
n
=
bigits_
.
size
();
i
<
n
;
++
i
)
{
double_bigit
result
=
bigits_
[
i
]
*
wide_value
+
carry
;
bigits_
[
i
]
=
static_cast
<
bigit
>
(
result
);
carry
=
static_cast
<
bigit
>
(
result
>>
bigit_bits
);
}
if
(
carry
!=
0
)
bigits_
.
push_back
(
carry
);
}
void
multiply
(
uint64_t
value
)
{
const
bigit
mask
=
~
bigit
(
0
);
const
double_bigit
lower
=
value
&
mask
;
const
double_bigit
upper
=
value
>>
bigit_bits
;
double_bigit
carry
=
0
;
for
(
size_t
i
=
0
,
n
=
bigits_
.
size
();
i
<
n
;
++
i
)
{
double_bigit
result
=
bigits_
[
i
]
*
lower
+
(
carry
&
mask
);
carry
=
bigits_
[
i
]
*
upper
+
(
result
>>
bigit_bits
)
+
(
carry
>>
bigit_bits
);
bigits_
[
i
]
=
static_cast
<
bigit
>
(
result
);
}
while
(
carry
!=
0
)
{
bigits_
.
push_back
(
carry
&
mask
);
carry
>>=
bigit_bits
;
}
}
public:
bigint
()
:
exp_
(
0
)
{}
explicit
bigint
(
uint64_t
n
)
{
assign
(
n
);
}
~
bigint
()
{
assert
(
bigits_
.
capacity
()
<=
bigits_capacity
);
}
bigint
(
const
bigint
&
)
=
delete
;
void
operator
=
(
const
bigint
&
)
=
delete
;
void
assign
(
const
bigint
&
other
)
{
bigits_
.
resize
(
other
.
bigits_
.
size
());
auto
data
=
other
.
bigits_
.
data
();
std
::
copy
(
data
,
data
+
other
.
bigits_
.
size
(),
bigits_
.
data
());
exp_
=
other
.
exp_
;
}
void
assign
(
uint64_t
n
)
{
int
num_bigits
=
0
;
do
{
bigits_
[
num_bigits
++
]
=
n
&
~
bigit
(
0
);
n
>>=
bigit_bits
;
}
while
(
n
!=
0
);
bigits_
.
resize
(
num_bigits
);
exp_
=
0
;
}
int
num_bigits
()
const
{
return
static_cast
<
int
>
(
bigits_
.
size
())
+
exp_
;
}
bigint
&
operator
<<=
(
int
shift
)
{
assert
(
shift
>=
0
);
exp_
+=
shift
/
bigit_bits
;
shift
%=
bigit_bits
;
if
(
shift
==
0
)
return
*
this
;
bigit
carry
=
0
;
for
(
size_t
i
=
0
,
n
=
bigits_
.
size
();
i
<
n
;
++
i
)
{
bigit
c
=
bigits_
[
i
]
>>
(
bigit_bits
-
shift
);
bigits_
[
i
]
=
(
bigits_
[
i
]
<<
shift
)
+
carry
;
carry
=
c
;
}
if
(
carry
!=
0
)
bigits_
.
push_back
(
carry
);
return
*
this
;
}
template
<
typename
Int
>
bigint
&
operator
*=
(
Int
value
)
{
FMT_ASSERT
(
value
>
0
,
""
);
multiply
(
uint32_or_64_or_128_t
<
Int
>
(
value
));
return
*
this
;
}
friend
int
compare
(
const
bigint
&
lhs
,
const
bigint
&
rhs
)
{
int
num_lhs_bigits
=
lhs
.
num_bigits
(),
num_rhs_bigits
=
rhs
.
num_bigits
();
if
(
num_lhs_bigits
!=
num_rhs_bigits
)
return
num_lhs_bigits
>
num_rhs_bigits
?
1
:
-
1
;
int
i
=
static_cast
<
int
>
(
lhs
.
bigits_
.
size
())
-
1
;
int
j
=
static_cast
<
int
>
(
rhs
.
bigits_
.
size
())
-
1
;
int
end
=
i
-
j
;
if
(
end
<
0
)
end
=
0
;
for
(;
i
>=
end
;
--
i
,
--
j
)
{
bigit
lhs_bigit
=
lhs
.
bigits_
[
i
],
rhs_bigit
=
rhs
.
bigits_
[
j
];
if
(
lhs_bigit
!=
rhs_bigit
)
return
lhs_bigit
>
rhs_bigit
?
1
:
-
1
;
}
if
(
i
!=
j
)
return
i
>
j
?
1
:
-
1
;
return
0
;
}
// Returns compare(lhs1 + lhs2, rhs).
friend
int
add_compare
(
const
bigint
&
lhs1
,
const
bigint
&
lhs2
,
const
bigint
&
rhs
)
{
int
max_lhs_bigits
=
(
std
::
max
)(
lhs1
.
num_bigits
(),
lhs2
.
num_bigits
());
int
num_rhs_bigits
=
rhs
.
num_bigits
();
if
(
max_lhs_bigits
+
1
<
num_rhs_bigits
)
return
-
1
;
if
(
max_lhs_bigits
>
num_rhs_bigits
)
return
1
;
auto
get_bigit
=
[](
const
bigint
&
n
,
int
i
)
->
bigit
{
return
i
>=
n
.
exp_
&&
i
<
n
.
num_bigits
()
?
n
.
bigits_
[
i
-
n
.
exp_
]
:
0
;
};
double_bigit
borrow
=
0
;
int
min_exp
=
(
std
::
min
)((
std
::
min
)(
lhs1
.
exp_
,
lhs2
.
exp_
),
rhs
.
exp_
);
for
(
int
i
=
num_rhs_bigits
-
1
;
i
>=
min_exp
;
--
i
)
{
double_bigit
sum
=
static_cast
<
double_bigit
>
(
get_bigit
(
lhs1
,
i
))
+
get_bigit
(
lhs2
,
i
);
bigit
rhs_bigit
=
get_bigit
(
rhs
,
i
);
if
(
sum
>
rhs_bigit
+
borrow
)
return
1
;
borrow
=
rhs_bigit
+
borrow
-
sum
;
if
(
borrow
>
1
)
return
-
1
;
borrow
<<=
bigit_bits
;
}
return
borrow
!=
0
?
-
1
:
0
;
}
// Assigns pow(10, exp) to this bigint.
void
assign_pow10
(
int
exp
)
{
assert
(
exp
>=
0
);
if
(
exp
==
0
)
return
assign
(
1
);
// Find the top bit.
int
bitmask
=
1
;
while
(
exp
>=
bitmask
)
bitmask
<<=
1
;
bitmask
>>=
1
;
// pow(10, exp) = pow(5, exp) * pow(2, exp). First compute pow(5, exp) by
// repeated squaring and multiplication.
assign
(
5
);
bitmask
>>=
1
;
while
(
bitmask
!=
0
)
{
square
();
if
((
exp
&
bitmask
)
!=
0
)
*
this
*=
5
;
bitmask
>>=
1
;
}
*
this
<<=
exp
;
// Multiply by pow(2, exp) by shifting.
}
void
square
()
{
basic_memory_buffer
<
bigit
,
bigits_capacity
>
n
(
std
::
move
(
bigits_
));
int
num_bigits
=
static_cast
<
int
>
(
bigits_
.
size
());
int
num_result_bigits
=
2
*
num_bigits
;
bigits_
.
resize
(
num_result_bigits
);
using
accumulator_t
=
conditional_t
<
FMT_USE_INT128
,
uint128_t
,
accumulator
>
;
auto
sum
=
accumulator_t
();
for
(
int
bigit_index
=
0
;
bigit_index
<
num_bigits
;
++
bigit_index
)
{
// Compute bigit at position bigit_index of the result by adding
// cross-product terms n[i] * n[j] such that i + j == bigit_index.
for
(
int
i
=
0
,
j
=
bigit_index
;
j
>=
0
;
++
i
,
--
j
)
{
// Most terms are multiplied twice which can be optimized in the future.
sum
+=
static_cast
<
double_bigit
>
(
n
[
i
])
*
n
[
j
];
}
bigits_
[
bigit_index
]
=
static_cast
<
bigit
>
(
sum
);
sum
>>=
bits
<
bigit
>::
value
;
// Compute the carry.
}
// Do the same for the top half.
for
(
int
bigit_index
=
num_bigits
;
bigit_index
<
num_result_bigits
;
++
bigit_index
)
{
for
(
int
j
=
num_bigits
-
1
,
i
=
bigit_index
-
j
;
i
<
num_bigits
;)
sum
+=
static_cast
<
double_bigit
>
(
n
[
i
++
])
*
n
[
j
--
];
bigits_
[
bigit_index
]
=
static_cast
<
bigit
>
(
sum
);
sum
>>=
bits
<
bigit
>::
value
;
}
--
num_result_bigits
;
remove_leading_zeros
();
exp_
*=
2
;
}
// Divides this bignum by divisor, assigning the remainder to this and
// returning the quotient.
int
divmod_assign
(
const
bigint
&
divisor
)
{
FMT_ASSERT
(
this
!=
&
divisor
,
""
);
if
(
compare
(
*
this
,
divisor
)
<
0
)
return
0
;
int
num_bigits
=
static_cast
<
int
>
(
bigits_
.
size
());
FMT_ASSERT
(
divisor
.
bigits_
[
divisor
.
bigits_
.
size
()
-
1
]
!=
0
,
""
);
int
exp_difference
=
exp_
-
divisor
.
exp_
;
if
(
exp_difference
>
0
)
{
// Align bigints by adding trailing zeros to simplify subtraction.
bigits_
.
resize
(
num_bigits
+
exp_difference
);
for
(
int
i
=
num_bigits
-
1
,
j
=
i
+
exp_difference
;
i
>=
0
;
--
i
,
--
j
)
bigits_
[
j
]
=
bigits_
[
i
];
std
::
uninitialized_fill_n
(
bigits_
.
data
(),
exp_difference
,
0
);
exp_
-=
exp_difference
;
}
int
quotient
=
0
;
do
{
subtract_aligned
(
divisor
);
++
quotient
;
}
while
(
compare
(
*
this
,
divisor
)
>=
0
);
return
quotient
;
}
};
enum
round_direction
{
unknown
,
up
,
down
};
// Given the divisor (normally a power of 10), the remainder = v % divisor for
...
...
@@ -500,13 +781,13 @@ enum result {
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
template
<
typename
Handler
>
digits
::
result
grisu_gen_digits
(
fp
value
,
uint64_t
error
,
int
&
exp
,
Handler
&
handler
)
{
fp
one
(
1ull
<<
-
value
.
e
,
value
.
e
);
FMT_ALWAYS_INLINE
digits
::
result
grisu_gen_digits
(
fp
value
,
uint64_t
error
,
int
&
exp
,
Handler
&
handler
)
{
const
fp
one
(
1ULL
<<
-
value
.
e
,
value
.
e
);
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
// zero because it contains a product of two 64-bit numbers with MSB set (due
// to normalization) - 1, shifted right by at most 60 bits.
uint32_t
integral
=
static_cast
<
uint32_t
>
(
value
.
f
>>
-
one
.
e
);
auto
integral
=
static_cast
<
uint32_t
>
(
value
.
f
>>
-
one
.
e
);
FMT_ASSERT
(
integral
!=
0
,
""
);
FMT_ASSERT
(
integral
==
value
.
f
>>
-
one
.
e
,
""
);
// The fractional part of scaled value (p2 in Grisu) c = value % one.
...
...
@@ -519,44 +800,39 @@ digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
// Generate digits for the integral part. This can produce up to 10 digits.
do
{
uint32_t
digit
=
0
;
// This optimization by miloyip reduces the number of integer divisions by
auto
divmod_integral
=
[
&
](
uint32_t
divisor
)
{
digit
=
integral
/
divisor
;
integral
%=
divisor
;
};
// This optimization by Milo Yip reduces the number of integer divisions by
// one per iteration.
switch
(
exp
)
{
case
10
:
digit
=
integral
/
1000000000
;
integral
%=
1000000000
;
divmod_integral
(
1000000000
);
break
;
case
9
:
digit
=
integral
/
100000000
;
integral
%=
100000000
;
divmod_integral
(
100000000
);
break
;
case
8
:
digit
=
integral
/
10000000
;
integral
%=
10000000
;
divmod_integral
(
10000000
);
break
;
case
7
:
digit
=
integral
/
1000000
;
integral
%=
1000000
;
divmod_integral
(
1000000
);
break
;
case
6
:
digit
=
integral
/
100000
;
integral
%=
100000
;
divmod_integral
(
100000
);
break
;
case
5
:
digit
=
integral
/
10000
;
integral
%=
10000
;
divmod_integral
(
10000
);
break
;
case
4
:
digit
=
integral
/
1000
;
integral
%=
1000
;
divmod_integral
(
1000
);
break
;
case
3
:
digit
=
integral
/
100
;
integral
%=
100
;
divmod_integral
(
100
);
break
;
case
2
:
digit
=
integral
/
10
;
integral
%=
10
;
divmod_integral
(
10
);
break
;
case
1
:
digit
=
integral
;
...
...
@@ -640,7 +916,7 @@ struct fixed_handler {
};
// The shortest representation digit handler.
template
<
int
GRISU_VERSION
>
struct
grisu_shortest_handler
{
struct
grisu_shortest_handler
{
char
*
buf
;
int
size
;
// Distance between scaled value and upper bound (wp_W in Grisu3).
...
...
@@ -666,11 +942,6 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
uint64_t
error
,
int
exp
,
bool
integral
)
{
buf
[
size
++
]
=
digit
;
if
(
remainder
>=
error
)
return
digits
::
more
;
if
(
GRISU_VERSION
!=
3
)
{
uint64_t
d
=
integral
?
diff
:
diff
*
data
::
powers_of_10_64
[
-
exp
];
round
(
d
,
divisor
,
remainder
,
error
);
return
digits
::
done
;
}
uint64_t
unit
=
integral
?
1
:
data
::
powers_of_10_64
[
-
exp
];
uint64_t
up
=
(
diff
-
1
)
*
unit
;
// wp_Wup
round
(
up
,
divisor
,
remainder
,
error
);
...
...
@@ -686,151 +957,289 @@ template <int GRISU_VERSION> struct grisu_shortest_handler {
}
};
template
<
typename
Double
,
enable_if_t
<
(
sizeof
(
Double
)
==
sizeof
(
uint64_t
)),
int
>
>
FMT_API
bool
grisu_format
(
Double
value
,
buffer
<
char
>&
buf
,
int
precision
,
unsigned
options
,
int
&
exp
)
{
// Formats value using a variation of the Fixed-Precision Positive
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
// https://fmt.dev/p372-steele.pdf.
template
<
typename
Double
>
void
fallback_format
(
Double
d
,
buffer
<
char
>&
buf
,
int
&
exp10
)
{
bigint
numerator
;
// 2 * R in (FPP)^2.
bigint
denominator
;
// 2 * S in (FPP)^2.
// lower and upper are differences between value and corresponding boundaries.
bigint
lower
;
// (M^- in (FPP)^2).
bigint
upper_store
;
// upper's value if different from lower.
bigint
*
upper
=
nullptr
;
// (M^+ in (FPP)^2).
fp
value
;
// Shift numerator and denominator by an extra bit or two (if lower boundary
// is closer) to make lower and upper integers. This eliminates multiplication
// by 2 during later computations.
// TODO: handle float
int
shift
=
value
.
assign
(
d
)
?
2
:
1
;
uint64_t
significand
=
value
.
f
<<
shift
;
if
(
value
.
e
>=
0
)
{
numerator
.
assign
(
significand
);
numerator
<<=
value
.
e
;
lower
.
assign
(
1
);
lower
<<=
value
.
e
;
if
(
shift
!=
1
)
{
upper_store
.
assign
(
1
);
upper_store
<<=
value
.
e
+
1
;
upper
=
&
upper_store
;
}
denominator
.
assign_pow10
(
exp10
);
denominator
<<=
1
;
}
else
if
(
exp10
<
0
)
{
numerator
.
assign_pow10
(
-
exp10
);
lower
.
assign
(
numerator
);
if
(
shift
!=
1
)
{
upper_store
.
assign
(
numerator
);
upper_store
<<=
1
;
upper
=
&
upper_store
;
}
numerator
*=
significand
;
denominator
.
assign
(
1
);
denominator
<<=
shift
-
value
.
e
;
}
else
{
numerator
.
assign
(
significand
);
denominator
.
assign_pow10
(
exp10
);
denominator
<<=
shift
-
value
.
e
;
lower
.
assign
(
1
);
if
(
shift
!=
1
)
{
upper_store
.
assign
(
1ULL
<<
1
);
upper
=
&
upper_store
;
}
}
if
(
!
upper
)
upper
=
&
lower
;
// Invariant: value == (numerator / denominator) * pow(10, exp10).
bool
even
=
(
value
.
f
&
1
)
==
0
;
int
num_digits
=
0
;
char
*
data
=
buf
.
data
();
for
(;;)
{
int
digit
=
numerator
.
divmod_assign
(
denominator
);
bool
low
=
compare
(
numerator
,
lower
)
-
even
<
0
;
// numerator <[=] lower.
// numerator + upper >[=] pow10:
bool
high
=
add_compare
(
numerator
,
*
upper
,
denominator
)
+
even
>
0
;
data
[
num_digits
++
]
=
static_cast
<
char
>
(
'0'
+
digit
);
if
(
low
||
high
)
{
if
(
!
low
)
{
++
data
[
num_digits
-
1
];
}
else
if
(
high
)
{
int
result
=
add_compare
(
numerator
,
numerator
,
denominator
);
// Round half to even.
if
(
result
>
0
||
(
result
==
0
&&
(
digit
%
2
)
!=
0
))
++
data
[
num_digits
-
1
];
}
buf
.
resize
(
num_digits
);
exp10
-=
num_digits
-
1
;
return
;
}
numerator
*=
10
;
lower
*=
10
;
if
(
upper
!=
&
lower
)
*
upper
*=
10
;
}
}
// Formats value using the Grisu algorithm
// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template
<
typename
T
>
int
format_float
(
T
value
,
int
precision
,
float_specs
specs
,
buffer
<
char
>&
buf
)
{
static_assert
(
!
std
::
is_same
<
T
,
float
>
(),
""
);
FMT_ASSERT
(
value
>=
0
,
"value is negative"
);
bool
fixed
=
(
options
&
grisu_options
::
fixed
)
!=
0
;
const
bool
fixed
=
specs
.
format
==
float_format
::
fixed
;
if
(
value
<=
0
)
{
// <= instead of == to silence a warning.
if
(
precision
<=
0
||
!
fixed
)
{
exp
=
0
;
buf
.
push_back
(
'0'
);
}
else
{
exp
=
-
precision
;
buf
.
resize
(
to_unsigned
(
precision
));
std
::
uninitialized_fill_n
(
buf
.
data
(),
precision
,
'0'
);
return
0
;
}
return
true
;
buf
.
resize
(
to_unsigned
(
precision
));
std
::
uninitialized_fill_n
(
buf
.
data
(),
precision
,
'0'
);
return
-
precision
;
}
fp
fp_value
(
value
);
if
(
!
specs
.
use_grisu
)
return
snprintf_float
(
value
,
precision
,
specs
,
buf
);
int
exp
=
0
;
const
int
min_exp
=
-
60
;
// alpha in Grisu.
int
cached_exp10
=
0
;
// K in Grisu.
if
(
precision
!=
-
1
)
{
if
(
precision
>
17
)
return
false
;
fp
_value
.
normalize
(
);
auto
cached_pow
=
get_cached_power
(
min_exp
-
(
fp_value
.
e
+
fp
::
significand_size
),
cached_exp10
);
fp_value
=
fp_value
*
cached_pow
;
if
(
precision
>
17
)
return
snprintf_float
(
value
,
precision
,
specs
,
buf
)
;
fp
normalized
=
normalize
(
fp
(
value
)
);
const
auto
cached_pow
=
get_cached_power
(
min_exp
-
(
normalized
.
e
+
fp
::
significand_size
),
cached_exp10
);
normalized
=
normalized
*
cached_pow
;
fixed_handler
handler
{
buf
.
data
(),
0
,
precision
,
-
cached_exp10
,
fixed
};
if
(
grisu_gen_digits
(
fp_value
,
1
,
exp
,
handler
)
==
digits
::
error
)
return
false
;
buf
.
resize
(
to_unsigned
(
handler
.
size
));
if
(
grisu_gen_digits
(
normalized
,
1
,
exp
,
handler
)
==
digits
::
error
)
return
snprintf_float
(
value
,
precision
,
specs
,
buf
);
int
num_digits
=
handler
.
size
;
if
(
!
fixed
)
{
// Remove trailing zeros.
while
(
num_digits
>
0
&&
buf
[
num_digits
-
1
]
==
'0'
)
{
--
num_digits
;
++
exp
;
}
}
buf
.
resize
(
to_unsigned
(
num_digits
));
}
else
{
fp
lower
,
upper
;
// w^- and w^+ in the Grisu paper.
fp_value
.
compute_boundaries
(
lower
,
upper
);
// Find a cached power of 10 such that multiplying upper by it will bring
fp
fp_value
;
auto
boundaries
=
specs
.
binary32
?
fp_value
.
assign_float_with_boundaries
(
value
)
:
fp_value
.
assign_with_boundaries
(
value
);
fp_value
=
normalize
(
fp_value
);
// Find a cached power of 10 such that multiplying value by it will bring
// the exponent in the range [min_exp, -32].
auto
cached_pow
=
get_cached_power
(
// \tilde{c}_{-k} in Grisu.
min_exp
-
(
upper
.
e
+
fp
::
significand_size
),
cached_exp10
);
fp_value
.
normalize
();
const
fp
cached_pow
=
get_cached_power
(
min_exp
-
(
fp_value
.
e
+
fp
::
significand_size
),
cached_exp10
);
// Multiply value and boundaries by the cached power of 10.
fp_value
=
fp_value
*
cached_pow
;
lower
=
lower
*
cached_pow
;
// \tilde{M}^- in Grisu.
upper
=
upper
*
cached_pow
;
// \tilde{M}^+ in Grisu.
assert
(
min_exp
<=
upper
.
e
&&
upper
.
e
<=
-
32
);
auto
result
=
digits
::
result
();
int
size
=
0
;
if
((
options
&
grisu_options
::
grisu3
)
!=
0
)
{
--
lower
.
f
;
// \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
++
upper
.
f
;
// \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
// Numbers outside of (lower, upper) definitely do not round to value.
grisu_shortest_handler
<
3
>
handler
{
buf
.
data
(),
0
,
(
upper
-
fp_value
).
f
};
result
=
grisu_gen_digits
(
upper
,
upper
.
f
-
lower
.
f
,
exp
,
handler
);
size
=
handler
.
size
;
}
else
{
++
lower
.
f
;
// \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
--
upper
.
f
;
// \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
grisu_shortest_handler
<
2
>
handler
{
buf
.
data
(),
0
,
(
upper
-
fp_value
).
f
};
result
=
grisu_gen_digits
(
upper
,
upper
.
f
-
lower
.
f
,
exp
,
handler
);
size
=
handler
.
size
;
boundaries
.
lower
=
multiply
(
boundaries
.
lower
,
cached_pow
.
f
);
boundaries
.
upper
=
multiply
(
boundaries
.
upper
,
cached_pow
.
f
);
assert
(
min_exp
<=
fp_value
.
e
&&
fp_value
.
e
<=
-
32
);
--
boundaries
.
lower
;
// \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
++
boundaries
.
upper
;
// \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
// Numbers outside of (lower, upper) definitely do not round to value.
grisu_shortest_handler
handler
{
buf
.
data
(),
0
,
boundaries
.
upper
-
fp_value
.
f
};
auto
result
=
grisu_gen_digits
(
fp
(
boundaries
.
upper
,
fp_value
.
e
),
boundaries
.
upper
-
boundaries
.
lower
,
exp
,
handler
);
if
(
result
==
digits
::
error
)
{
exp
+=
handler
.
size
-
cached_exp10
-
1
;
fallback_format
(
value
,
buf
,
exp
);
return
exp
;
}
if
(
result
==
digits
::
error
)
return
false
;
buf
.
resize
(
to_unsigned
(
size
));
buf
.
resize
(
to_unsigned
(
handler
.
size
));
}
exp
-=
cached_exp10
;
return
true
;
return
exp
-
cached_exp10
;
}
template
<
typename
Double
>
char
*
sprintf_format
(
Double
value
,
internal
::
buffer
<
char
>&
buf
,
sprintf_specs
specs
)
{
template
<
typename
T
>
int
snprintf_float
(
T
value
,
int
precision
,
float_specs
specs
,
buffer
<
char
>&
buf
)
{
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT
(
buf
.
capacity
()
!=
0
,
"empty buffer"
);
FMT_ASSERT
(
buf
.
capacity
()
>
buf
.
size
(),
"empty buffer"
);
static_assert
(
!
std
::
is_same
<
T
,
float
>
(),
""
);
// Build format string.
enum
{
max_format_size
=
10
};
// longest format: %#-*.*Lg
// Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format.
if
(
specs
.
format
==
float_format
::
general
||
specs
.
format
==
float_format
::
exp
)
precision
=
(
precision
>=
0
?
precision
:
6
)
-
1
;
// Build the format string.
enum
{
max_format_size
=
7
};
// Ths longest format is "%#.*Le".
char
format
[
max_format_size
];
char
*
format_ptr
=
format
;
*
format_ptr
++
=
'%'
;
if
(
specs
.
alt
||
!
specs
.
type
)
*
format_ptr
++
=
'#'
;
if
(
specs
.
precision
>=
0
)
{
if
(
specs
.
trailing_zeros
)
*
format_ptr
++
=
'#'
;
if
(
precision
>=
0
)
{
*
format_ptr
++
=
'.'
;
*
format_ptr
++
=
'*'
;
}
if
(
std
::
is_same
<
Double
,
long
double
>::
value
)
*
format_ptr
++
=
'L'
;
char
type
=
specs
.
type
;
if
(
type
==
'%'
)
type
=
'f'
;
else
if
(
type
==
0
||
type
==
'n'
)
type
=
'g'
;
#if FMT_MSC_VER
if
(
type
==
'F'
)
{
// MSVC's printf doesn't support 'F'.
type
=
'f'
;
}
#endif
*
format_ptr
++
=
type
;
if
(
std
::
is_same
<
T
,
long
double
>
())
*
format_ptr
++
=
'L'
;
*
format_ptr
++
=
specs
.
format
!=
float_format
::
hex
?
(
specs
.
format
==
float_format
::
fixed
?
'f'
:
'e'
)
:
(
specs
.
upper
?
'A'
:
'a'
);
*
format_ptr
=
'\0'
;
// Format using snprintf.
char
*
start
=
nullptr
;
char
*
decimal_point_pos
=
nullptr
;
auto
offset
=
buf
.
size
();
for
(;;)
{
std
::
size_t
buffer_size
=
buf
.
capacity
();
start
=
&
buf
[
0
];
int
result
=
format_float
(
start
,
buffer_size
,
format
,
specs
.
precision
,
value
);
if
(
result
>=
0
)
{
unsigned
n
=
internal
::
to_unsigned
(
result
);
if
(
n
<
buf
.
capacity
())
{
// Find the decimal point.
auto
p
=
buf
.
data
(),
end
=
p
+
n
;
if
(
*
p
==
'+'
||
*
p
==
'-'
)
++
p
;
if
(
specs
.
type
!=
'a'
&&
specs
.
type
!=
'A'
)
{
while
(
p
<
end
&&
*
p
>=
'0'
&&
*
p
<=
'9'
)
++
p
;
if
(
p
<
end
&&
*
p
!=
'e'
&&
*
p
!=
'E'
)
{
decimal_point_pos
=
p
;
if
(
!
specs
.
type
)
{
// Keep only one trailing zero after the decimal point.
++
p
;
if
(
*
p
==
'0'
)
++
p
;
while
(
p
!=
end
&&
*
p
>=
'1'
&&
*
p
<=
'9'
)
++
p
;
char
*
where
=
p
;
while
(
p
!=
end
&&
*
p
==
'0'
)
++
p
;
if
(
p
==
end
||
*
p
<
'0'
||
*
p
>
'9'
)
{
if
(
p
!=
end
)
std
::
memmove
(
where
,
p
,
to_unsigned
(
end
-
p
));
n
-=
static_cast
<
unsigned
>
(
p
-
where
);
}
}
}
}
buf
.
resize
(
n
);
break
;
// The buffer is large enough - continue with formatting.
auto
begin
=
buf
.
data
()
+
offset
;
auto
capacity
=
buf
.
capacity
()
-
offset
;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if
(
precision
>
100000
)
throw
std
::
runtime_error
(
"fuzz mode - avoid large allocation inside snprintf"
);
#endif
// Suppress the warning about a nonliteral format string.
auto
snprintf_ptr
=
FMT_SNPRINTF
;
int
result
=
precision
>=
0
?
snprintf_ptr
(
begin
,
capacity
,
format
,
precision
,
value
)
:
snprintf_ptr
(
begin
,
capacity
,
format
,
value
);
if
(
result
<
0
)
{
buf
.
reserve
(
buf
.
capacity
()
+
1
);
// The buffer will grow exponentially.
continue
;
}
unsigned
size
=
to_unsigned
(
result
);
// Size equal to capacity means that the last character was truncated.
if
(
size
>=
capacity
)
{
buf
.
reserve
(
size
+
offset
+
1
);
// Add 1 for the terminating '\0'.
continue
;
}
auto
is_digit
=
[](
char
c
)
{
return
c
>=
'0'
&&
c
<=
'9'
;
};
if
(
specs
.
format
==
float_format
::
fixed
)
{
if
(
precision
==
0
)
{
buf
.
resize
(
size
);
return
0
;
}
buf
.
reserve
(
n
+
1
);
}
else
{
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf
.
reserve
(
buf
.
capacity
()
+
1
);
// Find and remove the decimal point.
auto
end
=
begin
+
size
,
p
=
end
;
do
{
--
p
;
}
while
(
is_digit
(
*
p
));
int
fraction_size
=
static_cast
<
int
>
(
end
-
p
-
1
);
std
::
memmove
(
p
,
p
+
1
,
fraction_size
);
buf
.
resize
(
size
-
1
);
return
-
fraction_size
;
}
if
(
specs
.
format
==
float_format
::
hex
)
{
buf
.
resize
(
size
+
offset
);
return
0
;
}
// Find and parse the exponent.
auto
end
=
begin
+
size
,
exp_pos
=
end
;
do
{
--
exp_pos
;
}
while
(
*
exp_pos
!=
'e'
);
char
sign
=
exp_pos
[
1
];
assert
(
sign
==
'+'
||
sign
==
'-'
);
int
exp
=
0
;
auto
p
=
exp_pos
+
2
;
// Skip 'e' and sign.
do
{
assert
(
is_digit
(
*
p
));
exp
=
exp
*
10
+
(
*
p
++
-
'0'
);
}
while
(
p
!=
end
);
if
(
sign
==
'-'
)
exp
=
-
exp
;
int
fraction_size
=
0
;
if
(
exp_pos
!=
begin
+
1
)
{
// Remove trailing zeros.
auto
fraction_end
=
exp_pos
-
1
;
while
(
*
fraction_end
==
'0'
)
--
fraction_end
;
// Move the fractional part left to get rid of the decimal point.
fraction_size
=
static_cast
<
int
>
(
fraction_end
-
begin
-
1
);
std
::
memmove
(
begin
+
1
,
begin
+
2
,
fraction_size
);
}
buf
.
resize
(
fraction_size
+
offset
+
1
);
return
exp
-
fraction_size
;
}
return
decimal_point_pos
;
}
}
// namespace internal
template
<
>
struct
formatter
<
internal
::
bigint
>
{
format_parse_context
::
iterator
parse
(
format_parse_context
&
ctx
)
{
return
ctx
.
begin
();
}
format_context
::
iterator
format
(
const
internal
::
bigint
&
n
,
format_context
&
ctx
)
{
auto
out
=
ctx
.
out
();
bool
first
=
true
;
for
(
auto
i
=
n
.
bigits_
.
size
();
i
>
0
;
--
i
)
{
auto
value
=
n
.
bigits_
[
i
-
1
];
if
(
first
)
{
out
=
format_to
(
out
,
"{:x}"
,
value
);
first
=
false
;
continue
;
}
out
=
format_to
(
out
,
"{:08x}"
,
value
);
}
if
(
n
.
exp_
>
0
)
out
=
format_to
(
out
,
"p{}"
,
n
.
exp_
*
internal
::
bigint
::
bigit_bits
);
return
out
;
}
};
#if FMT_USE_WINDOWS_H
FMT_FUNC
internal
::
utf8_to_utf16
::
utf8_to_utf16
(
string_view
s
)
{
...
...
@@ -974,23 +1383,10 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
internal
::
fwrite_fully
(
buffer
.
data
(),
1
,
buffer
.
size
(),
f
);
}
FMT_FUNC
void
vprint
(
std
::
FILE
*
f
,
wstring_view
format_str
,
wformat_args
args
)
{
wmemory_buffer
buffer
;
internal
::
vformat_to
(
buffer
,
format_str
,
args
);
buffer
.
push_back
(
L'\0'
);
if
(
std
::
fputws
(
buffer
.
data
(),
f
)
==
-
1
)
{
FMT_THROW
(
system_error
(
errno
,
"cannot write to file"
));
}
}
FMT_FUNC
void
vprint
(
string_view
format_str
,
format_args
args
)
{
vprint
(
stdout
,
format_str
,
args
);
}
FMT_FUNC
void
vprint
(
wstring_view
format_str
,
wformat_args
args
)
{
vprint
(
stdout
,
format_str
,
args
);
}
FMT_END_NAMESPACE
#ifdef _MSC_VER
...
...
include/spdlog/fmt/bundled/format.h
View file @
0db4b04a
...
...
@@ -33,18 +33,16 @@
#ifndef FMT_FORMAT_H_
#define FMT_FORMAT_H_
#include "core.h"
#include <algorithm>
#include <c
assert
>
#include <c
errno
>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <iterator>
#include <limits>
#include <memory>
#include <stdexcept>
#include "core.h"
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
...
...
@@ -71,6 +69,12 @@
# define FMT_HAS_BUILTIN(x) 0
#endif
#if FMT_HAS_CPP_ATTRIBUTE(fallthrough) >= 201603 && __cplusplus >= 201703
# define FMT_FALLTHROUGH [[fallthrough]]
#else
# define FMT_FALLTHROUGH
#endif
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# if FMT_MSC_VER
...
...
@@ -84,7 +88,7 @@ template <typename Exception> inline void do_throw(const Exception& x) {
}
}
// namespace internal
FMT_END_NAMESPACE
# define FMT_THROW(x)
fmt::
internal::do_throw(x)
# define FMT_THROW(x) internal::do_throw(x)
# else
# define FMT_THROW(x) throw x
# endif
...
...
@@ -92,7 +96,7 @@ FMT_END_NAMESPACE
# define FMT_THROW(x) \
do { \
static_cast<void>(sizeof(x)); \
assert(false);
\
FMT_ASSERT(false, "");
\
} while (false)
# endif
#endif
...
...
@@ -123,14 +127,6 @@ FMT_END_NAMESPACE
# endif
#endif
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
# define FMT_USE_INT128 1
#else
# define FMT_USE_INT128 0
#endif
// __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519
#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER
...
...
@@ -156,14 +152,14 @@ inline uint32_t clz(uint32_t x) {
unsigned
long
r
=
0
;
_BitScanReverse
(
&
r
,
x
);
assert
(
x
!=
0
);
FMT_ASSERT
(
x
!=
0
,
""
);
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
# pragma warning(suppress : 6102)
return
31
-
r
;
}
# define FMT_BUILTIN_CLZ(n)
fmt::
internal::clz(n)
# define FMT_BUILTIN_CLZ(n) internal::clz(n)
# if defined(_WIN64) && !defined(__clang__)
# pragma intrinsic(_BitScanReverse64)
...
...
@@ -181,40 +177,88 @@ inline uint32_t clzll(uint64_t x) {
_BitScanReverse
(
&
r
,
static_cast
<
uint32_t
>
(
x
));
# endif
assert
(
x
!=
0
);
FMT_ASSERT
(
x
!=
0
,
""
);
// Static analysis complains about using uninitialized data
// "r", but the only way that can happen is if "x" is 0,
// which the callers guarantee to not happen.
# pragma warning(suppress : 6102)
return
63
-
r
;
}
# define FMT_BUILTIN_CLZLL(n)
fmt::
internal::clzll(n)
# define FMT_BUILTIN_CLZLL(n) internal::clzll(n)
}
// namespace internal
FMT_END_NAMESPACE
#endif
// Enable the deprecated numeric alignment.
#ifndef FMT_NUMERIC_ALIGN
# define FMT_NUMERIC_ALIGN 1
#endif
// Enable the deprecated percent specifier.
#ifndef FMT_DEPRECATED_PERCENT
# define FMT_DEPRECATED_PERCENT 0
#endif
FMT_BEGIN_NAMESPACE
namespace
internal
{
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template
<
typename
T
>
inline
T
const_check
(
T
value
)
{
return
value
;
}
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't have
// undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
template
<
typename
Dest
,
typename
Source
>
inline
Dest
bit_cast
(
const
Source
&
source
)
{
static_assert
(
sizeof
(
Dest
)
==
sizeof
(
Source
),
"size mismatch"
);
Dest
dest
;
std
::
memcpy
(
&
dest
,
&
source
,
sizeof
(
dest
));
return
dest
;
}
inline
bool
is_big_endian
()
{
auto
u
=
1u
;
struct
bytes
{
char
data
[
sizeof
(
u
)];
};
return
bit_cast
<
bytes
>
(
u
).
data
[
0
]
==
0
;
}
// A fallback implementation of uintptr_t for systems that lack it.
struct
fallback_uintptr
{
unsigned
char
value
[
sizeof
(
void
*
)];
fallback_uintptr
()
=
default
;
explicit
fallback_uintptr
(
const
void
*
p
)
{
*
this
=
bit_cast
<
fallback_uintptr
>
(
p
);
if
(
is_big_endian
())
{
for
(
size_t
i
=
0
,
j
=
sizeof
(
void
*
)
-
1
;
i
<
j
;
++
i
,
--
j
)
std
::
swap
(
value
[
i
],
value
[
j
]);
}
}
};
#ifdef UINTPTR_MAX
using
uintptr_t
=
::
uintptr_t
;
inline
uintptr_t
to_uintptr
(
const
void
*
p
)
{
return
bit_cast
<
uintptr_t
>
(
p
);
}
#else
using
uintptr_t
=
fallback_uintptr
;
inline
fallback_uintptr
to_uintptr
(
const
void
*
p
)
{
return
fallback_uintptr
(
p
);
}
#endif
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't produce
// undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718);
template
<
typename
Dest
,
typename
Source
>
inline
Dest
bit_cast
(
const
Source
&
source
)
{
static_assert
(
sizeof
(
Dest
)
==
sizeof
(
Source
),
"size mismatch"
);
Dest
dest
;
std
::
memcpy
(
&
dest
,
&
source
,
sizeof
(
dest
));
return
dest
;
// Returns the largest possible value for type T. Same as
// std::numeric_limits<T>::max() but shorter and not affected by the max macro.
template
<
typename
T
>
constexpr
T
max_value
()
{
return
(
std
::
numeric_limits
<
T
>::
max
)();
}
template
<
typename
T
>
constexpr
int
num_bits
()
{
return
std
::
numeric_limits
<
T
>::
digits
;
}
template
<
>
constexpr
int
num_bits
<
fallback_uintptr
>
()
{
return
static_cast
<
int
>
(
sizeof
(
void
*
)
*
std
::
numeric_limits
<
unsigned
char
>::
digits
);
}
// An approximation of iterator_t for pre-C++20 systems.
...
...
@@ -290,19 +334,21 @@ inline Iterator& reserve(Iterator& it, std::size_t) {
// An output iterator that counts the number of objects written to it and
// discards them.
template
<
typename
T
>
class
counting_iterator
{
class
counting_iterator
{
private:
std
::
size_t
count_
;
mutable
T
blackhole_
;
public:
using
iterator_category
=
std
::
output_iterator_tag
;
using
value_type
=
T
;
using
difference_type
=
std
::
ptrdiff_t
;
using
pointer
=
T
*
;
using
reference
=
T
&
;
using
pointer
=
void
;
using
reference
=
void
;
using
_Unchecked_type
=
counting_iterator
;
// Mark iterator as checked.
struct
value_type
{
template
<
typename
T
>
void
operator
=
(
const
T
&
)
{}
};
counting_iterator
()
:
count_
(
0
)
{}
std
::
size_t
count
()
const
{
return
count_
;
}
...
...
@@ -318,7 +364,7 @@ template <typename T> class counting_iterator {
return
it
;
}
T
&
operator
*
()
const
{
return
blackhole_
;
}
value_type
operator
*
()
const
{
return
{}
;
}
};
template
<
typename
OutputIt
>
class
truncating_iterator_base
{
...
...
@@ -413,17 +459,6 @@ class output_range {
sentinel
end
()
const
{
return
{};
}
// Sentinel is not used yet.
};
// A range with an iterator appending to a buffer.
template
<
typename
T
>
class
buffer_range
:
public
output_range
<
std
::
back_insert_iterator
<
buffer
<
T
>>
,
T
>
{
public:
using
iterator
=
std
::
back_insert_iterator
<
buffer
<
T
>>
;
using
output_range
<
iterator
,
T
>::
output_range
;
buffer_range
(
buffer
<
T
>&
buf
)
:
output_range
<
iterator
,
T
>
(
std
::
back_inserter
(
buf
))
{}
};
template
<
typename
Char
>
inline
size_t
count_code_points
(
basic_string_view
<
Char
>
s
)
{
return
s
.
size
();
...
...
@@ -439,6 +474,24 @@ inline size_t count_code_points(basic_string_view<char8_t> s) {
return
num_code_points
;
}
template
<
typename
Char
>
inline
size_t
code_point_index
(
basic_string_view
<
Char
>
s
,
size_t
n
)
{
size_t
size
=
s
.
size
();
return
n
<
size
?
n
:
size
;
}
// Calculates the index of the nth code point in a UTF-8 string.
inline
size_t
code_point_index
(
basic_string_view
<
char8_t
>
s
,
size_t
n
)
{
const
char8_t
*
data
=
s
.
data
();
size_t
num_code_points
=
0
;
for
(
size_t
i
=
0
,
size
=
s
.
size
();
i
!=
size
;
++
i
)
{
if
((
data
[
i
]
&
0xc0
)
!=
0x80
&&
++
num_code_points
>
n
)
{
return
i
;
}
}
return
s
.
size
();
}
inline
char8_t
to_char8_t
(
char
c
)
{
return
static_cast
<
char8_t
>
(
c
);
}
template
<
typename
InputIt
,
typename
OutChar
>
...
...
@@ -460,7 +513,7 @@ OutputIt copy_str(InputIt begin, InputIt end, OutputIt it) {
}
#ifndef FMT_USE_GRISU
# define FMT_USE_GRISU
0
# define FMT_USE_GRISU
1
#endif
template
<
typename
T
>
constexpr
bool
use_grisu
()
{
...
...
@@ -478,6 +531,17 @@ void buffer<T>::append(const U* begin, const U* end) {
}
}
// namespace internal
// A range with an iterator appending to a buffer.
template
<
typename
T
>
class
buffer_range
:
public
internal
::
output_range
<
std
::
back_insert_iterator
<
internal
::
buffer
<
T
>>
,
T
>
{
public:
using
iterator
=
std
::
back_insert_iterator
<
internal
::
buffer
<
T
>>
;
using
internal
::
output_range
<
iterator
,
T
>::
output_range
;
buffer_range
(
internal
::
buffer
<
T
>&
buf
)
:
internal
::
output_range
<
iterator
,
T
>
(
std
::
back_inserter
(
buf
))
{}
};
// A UTF-8 string view.
class
u8string_view
:
public
basic_string_view
<
char8_t
>
{
public:
...
...
@@ -552,7 +616,7 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
:
Allocator
(
alloc
)
{
this
->
set
(
store_
,
SIZE
);
}
~
basic_memory_buffer
()
{
deallocate
();
}
~
basic_memory_buffer
()
FMT_OVERRIDE
{
deallocate
();
}
private:
// Move data from other to this buffer.
...
...
@@ -581,15 +645,15 @@ class basic_memory_buffer : private Allocator, public internal::buffer<T> {
of the other object to it.
\endrst
*/
basic_memory_buffer
(
basic_memory_buffer
&&
other
)
{
move
(
other
);
}
basic_memory_buffer
(
basic_memory_buffer
&&
other
)
FMT_NOEXCEPT
{
move
(
other
);
}
/**
\rst
Moves the content of the other ``basic_memory_buffer`` object to this one.
\endrst
*/
basic_memory_buffer
&
operator
=
(
basic_memory_buffer
&&
other
)
{
assert
(
this
!=
&
other
);
basic_memory_buffer
&
operator
=
(
basic_memory_buffer
&&
other
)
FMT_NOEXCEPT
{
FMT_ASSERT
(
this
!=
&
other
,
""
);
deallocate
();
move
(
other
);
return
*
this
;
...
...
@@ -628,7 +692,11 @@ class FMT_API format_error : public std::runtime_error {
explicit
format_error
(
const
char
*
message
)
:
std
::
runtime_error
(
message
)
{}
explicit
format_error
(
const
std
::
string
&
message
)
:
std
::
runtime_error
(
message
)
{}
~
format_error
()
FMT_NOEXCEPT
;
format_error
(
const
format_error
&
)
=
default
;
format_error
&
operator
=
(
const
format_error
&
)
=
default
;
format_error
(
format_error
&&
)
=
default
;
format_error
&
operator
=
(
format_error
&&
)
=
default
;
~
format_error
()
FMT_NOEXCEPT
FMT_OVERRIDE
;
};
namespace
internal
{
...
...
@@ -644,11 +712,12 @@ FMT_CONSTEXPR bool is_negative(T) {
return
false
;
}
// Smallest of uint32_t
and uint64_t that is large enough to represent all
// values of T.
// Smallest of uint32_t
, uint64_t, uint128_t that is large enough to
//
represent all
values of T.
template
<
typename
T
>
using
uint32_or_64_t
=
conditional_t
<
std
::
numeric_limits
<
T
>::
digits
<=
32
,
uint32_t
,
uint64_t
>
;
using
uint32_or_64_or_128_t
=
conditional_t
<
std
::
numeric_limits
<
T
>::
digits
<=
32
,
uint32_t
,
conditional_t
<
std
::
numeric_limits
<
T
>::
digits
<=
64
,
uint64_t
,
uint128_t
>>
;
// Static data is placed in this class template for the header-only config.
template
<
typename
T
=
void
>
struct
FMT_EXTERN_TEMPLATE_API
basic_data
{
...
...
@@ -663,6 +732,7 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
static
const
char
background_color
[];
static
const
char
reset_color
[
5
];
static
const
wchar_t
wreset_color
[
5
];
static
const
char
signs
[];
};
FMT_EXTERN
template
struct
basic_data
<
void
>;
...
...
@@ -697,6 +767,23 @@ inline int count_digits(uint64_t n) {
}
#endif
#if FMT_USE_INT128
inline
int
count_digits
(
uint128_t
n
)
{
int
count
=
1
;
for
(;;)
{
// Integer division is slow so do it for a group of four 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.
if
(
n
<
10
)
return
count
;
if
(
n
<
100
)
return
count
+
1
;
if
(
n
<
1000
)
return
count
+
2
;
if
(
n
<
10000
)
return
count
+
3
;
n
/=
10000U
;
count
+=
4
;
}
}
#endif
// Counts the number of digits in n. BITS = log2(radix).
template
<
unsigned
BITS
,
typename
UInt
>
inline
int
count_digits
(
UInt
n
)
{
int
num_digits
=
0
;
...
...
@@ -708,17 +795,14 @@ template <unsigned BITS, typename UInt> inline int count_digits(UInt n) {
template
<
>
int
count_digits
<
4
>
(
internal
::
fallback_uintptr
n
);
#if FMT_
HAS_CPP_ATTRIBUTE(always_inline)
# define FMT_ALWAYS_INLINE __attribute__((always_inline))
#if FMT_
GCC_VERSION || FMT_CLANG_VERSION
# define FMT_ALWAYS_INLINE
inline
__attribute__((always_inline))
#else
# define FMT_ALWAYS_INLINE
#endif
template
<
typename
Handler
>
inline
char
*
lg
(
uint32_t
n
,
Handler
h
)
FMT_ALWAYS_INLINE
;
// Computes g = floor(log10(n)) and calls h.on<g>(n);
template
<
typename
Handler
>
inline
char
*
lg
(
uint32_t
n
,
Handler
h
)
{
template
<
typename
Handler
>
FMT_ALWAYS_INLINE
char
*
lg
(
uint32_t
n
,
Handler
h
)
{
return
n
<
100
?
n
<
10
?
h
.
template
on
<
0
>(
n
)
:
h
.
template
on
<
1
>(
n
)
:
n
<
1000000
?
n
<
10000
?
n
<
1000
?
h
.
template
on
<
2
>(
n
)
...
...
@@ -779,6 +863,14 @@ inline int count_digits(uint32_t n) {
}
#endif
template
<
typename
Char
>
FMT_API
std
::
string
grouping_impl
(
locale_ref
loc
);
template
<
typename
Char
>
inline
std
::
string
grouping
(
locale_ref
loc
)
{
return
grouping_impl
<
char
>
(
loc
);
}
template
<
>
inline
std
::
string
grouping
<
wchar_t
>
(
locale_ref
loc
)
{
return
grouping_impl
<
wchar_t
>
(
loc
);
}
template
<
typename
Char
>
FMT_API
Char
thousands_sep_impl
(
locale_ref
loc
);
template
<
typename
Char
>
inline
Char
thousands_sep
(
locale_ref
loc
)
{
return
Char
(
thousands_sep_impl
<
char
>
(
loc
));
...
...
@@ -808,7 +900,7 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
// 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
=
static_cast
<
unsigned
>
((
value
%
100
)
*
2
);
auto
index
=
static_cast
<
unsigned
>
((
value
%
100
)
*
2
);
value
/=
100
;
*--
buffer
=
static_cast
<
Char
>
(
data
::
digits
[
index
+
1
]);
add_thousands_sep
(
buffer
);
...
...
@@ -819,20 +911,26 @@ inline Char* format_decimal(Char* buffer, UInt value, int num_digits,
*--
buffer
=
static_cast
<
Char
>
(
'0'
+
value
);
return
end
;
}
unsigned
index
=
static_cast
<
unsigned
>
(
value
*
2
);
auto
index
=
static_cast
<
unsigned
>
(
value
*
2
);
*--
buffer
=
static_cast
<
Char
>
(
data
::
digits
[
index
+
1
]);
add_thousands_sep
(
buffer
);
*--
buffer
=
static_cast
<
Char
>
(
data
::
digits
[
index
]);
return
end
;
}
template
<
typename
Int
>
constexpr
int
digits10
()
noexcept
{
return
std
::
numeric_limits
<
Int
>::
digits10
;
}
template
<
>
constexpr
int
digits10
<
int128_t
>
()
noexcept
{
return
38
;
}
template
<
>
constexpr
int
digits10
<
uint128_t
>
()
noexcept
{
return
38
;
}
template
<
typename
Char
,
typename
UInt
,
typename
Iterator
,
typename
F
>
inline
Iterator
format_decimal
(
Iterator
out
,
UInt
value
,
int
num_digits
,
F
add_thousands_sep
)
{
FMT_ASSERT
(
num_digits
>=
0
,
"invalid digit count"
);
// Buffer should be large enough to hold all digits (<= digits10 + 1).
enum
{
max_size
=
std
::
numeric_limits
<
UInt
>::
digits10
+
1
};
Char
buffer
[
max_size
+
max_size
/
3
];
enum
{
max_size
=
digits10
<
UInt
>
()
+
1
};
Char
buffer
[
2
*
max_size
];
auto
end
=
format_decimal
(
buffer
,
value
,
num_digits
,
add_thousands_sep
);
return
internal
::
copy_str
<
Char
>
(
buffer
,
end
,
out
);
}
...
...
@@ -881,7 +979,7 @@ Char* format_uint(Char* buffer, internal::fallback_uintptr n, int num_digits,
template
<
unsigned
BASE_BITS
,
typename
Char
,
typename
It
,
typename
UInt
>
inline
It
format_uint
(
It
out
,
UInt
value
,
int
num_digits
,
bool
upper
=
false
)
{
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
char
buffer
[
std
::
numeric_limits
<
UInt
>::
digits
/
BASE_BITS
+
1
];
char
buffer
[
num_bits
<
UInt
>
()
/
BASE_BITS
+
1
];
format_uint
<
BASE_BITS
>
(
buffer
,
value
,
num_digits
,
upper
);
return
internal
::
copy_str
<
Char
>
(
buffer
,
buffer
+
num_digits
,
out
);
}
...
...
@@ -929,9 +1027,8 @@ class utf16_to_utf8 {
FMT_API
int
convert
(
wstring_view
s
);
};
FMT_API
void
format_windows_error
(
fmt
::
internal
::
buffer
<
char
>&
out
,
int
error_code
,
fmt
::
string_view
message
)
FMT_NOEXCEPT
;
FMT_API
void
format_windows_error
(
internal
::
buffer
<
char
>&
out
,
int
error_code
,
string_view
message
)
FMT_NOEXCEPT
;
#endif
template
<
typename
T
=
void
>
struct
null
{};
...
...
@@ -991,9 +1088,29 @@ using format_specs = basic_format_specs<char>;
namespace
internal
{
// A floating-point presentation format.
enum
class
float_format
:
unsigned
char
{
general
,
// General: exponent notation or fixed point based on magnitude.
exp
,
// Exponent notation with the default precision of 6, e.g. 1.2e-3.
fixed
,
// Fixed point with the default precision of 6, e.g. 0.0012.
hex
};
struct
float_specs
{
int
precision
;
float_format
format
:
8
;
sign_t
sign
:
8
;
bool
upper
:
1
;
bool
locale
:
1
;
bool
percent
:
1
;
bool
binary32
:
1
;
bool
use_grisu
:
1
;
bool
trailing_zeros
:
1
;
};
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
template
<
typename
Char
,
typename
It
>
It
write_exponent
(
int
exp
,
It
it
)
{
FMT_ASSERT
(
-
1000
<
exp
&&
exp
<
1
000
,
"exponent out of range"
);
FMT_ASSERT
(
-
1000
0
<
exp
&&
exp
<
10
000
,
"exponent out of range"
);
if
(
exp
<
0
)
{
*
it
++
=
static_cast
<
Char
>
(
'-'
);
exp
=
-
exp
;
...
...
@@ -1001,7 +1118,9 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
*
it
++
=
static_cast
<
Char
>
(
'+'
);
}
if
(
exp
>=
100
)
{
*
it
++
=
static_cast
<
Char
>
(
static_cast
<
char
>
(
'0'
+
exp
/
100
));
const
char
*
top
=
data
::
digits
+
(
exp
/
100
)
*
2
;
if
(
exp
>=
1000
)
*
it
++
=
static_cast
<
Char
>
(
top
[
0
]);
*
it
++
=
static_cast
<
Char
>
(
top
[
1
]);
exp
%=
100
;
}
const
char
*
d
=
data
::
digits
+
exp
*
2
;
...
...
@@ -1010,103 +1129,121 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
return
it
;
}
struct
gen_digits_params
{
int
num_digits
;
bool
fixed
;
bool
upper
;
bool
trailing_zeros
;
};
// The number is given as v = digits * pow(10, exp).
template
<
typename
Char
,
typename
It
>
It
grisu_prettify
(
const
char
*
digits
,
int
size
,
int
exp
,
It
it
,
gen_digits_params
params
,
Char
decimal_point
)
{
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int
full_exp
=
size
+
exp
;
if
(
!
params
.
fixed
)
{
// Insert a decimal point after the first digit and add an exponent.
*
it
++
=
static_cast
<
Char
>
(
*
digits
);
if
(
size
>
1
)
*
it
++
=
decimal_point
;
exp
+=
size
-
1
;
it
=
copy_str
<
Char
>
(
digits
+
1
,
digits
+
size
,
it
);
if
(
size
<
params
.
num_digits
)
it
=
std
::
fill_n
(
it
,
params
.
num_digits
-
size
,
static_cast
<
Char
>
(
'0'
));
*
it
++
=
static_cast
<
Char
>
(
params
.
upper
?
'E'
:
'e'
);
return
write_exponent
<
Char
>
(
exp
,
it
);
}
if
(
size
<=
full_exp
)
{
// 1234e7 -> 12340000000[.0+]
it
=
copy_str
<
Char
>
(
digits
,
digits
+
size
,
it
);
it
=
std
::
fill_n
(
it
,
full_exp
-
size
,
static_cast
<
Char
>
(
'0'
));
int
num_zeros
=
(
std
::
max
)(
params
.
num_digits
-
full_exp
,
1
);
if
(
params
.
trailing_zeros
)
{
*
it
++
=
decimal_point
;
template
<
typename
Char
>
class
float_writer
{
private:
// The number is given as v = digits_ * pow(10, exp_).
const
char
*
digits_
;
int
num_digits_
;
int
exp_
;
size_t
size_
;
float_specs
specs_
;
Char
decimal_point_
;
template
<
typename
It
>
It
prettify
(
It
it
)
const
{
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int
full_exp
=
num_digits_
+
exp_
;
if
(
specs_
.
format
==
float_format
::
exp
)
{
// Insert a decimal point after the first digit and add an exponent.
*
it
++
=
static_cast
<
Char
>
(
*
digits_
);
if
(
num_digits_
>
1
)
*
it
++
=
decimal_point_
;
it
=
copy_str
<
Char
>
(
digits_
+
1
,
digits_
+
num_digits_
,
it
);
int
num_zeros
=
specs_
.
precision
-
num_digits_
;
if
(
num_zeros
>
0
&&
specs_
.
trailing_zeros
)
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
*
it
++
=
static_cast
<
Char
>
(
specs_
.
upper
?
'E'
:
'e'
);
return
write_exponent
<
Char
>
(
full_exp
-
1
,
it
);
}
if
(
num_digits_
<=
full_exp
)
{
// 1234e7 -> 12340000000[.0+]
it
=
copy_str
<
Char
>
(
digits_
,
digits_
+
num_digits_
,
it
);
it
=
std
::
fill_n
(
it
,
full_exp
-
num_digits_
,
static_cast
<
Char
>
(
'0'
));
if
(
specs_
.
trailing_zeros
)
{
*
it
++
=
decimal_point_
;
int
num_zeros
=
specs_
.
precision
-
full_exp
;
if
(
num_zeros
<=
0
)
{
if
(
specs_
.
format
!=
float_format
::
fixed
)
*
it
++
=
static_cast
<
Char
>
(
'0'
);
return
it
;
}
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if
(
num_zeros
>
1000
)
throw
std
::
runtime_error
(
"fuzz mode - avoiding excessive cpu use"
);
if
(
num_zeros
>
1000
)
throw
std
::
runtime_error
(
"fuzz mode - avoiding excessive cpu use"
);
#endif
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
}
}
else
if
(
full_exp
>
0
)
{
// 1234e-2 -> 12.34[0+]
it
=
copy_str
<
Char
>
(
digits
,
digits
+
full_exp
,
it
);
if
(
!
params
.
trailing_zeros
)
{
// Remove trailing zeros.
while
(
size
>
full_exp
&&
digits
[
size
-
1
]
==
'0'
)
--
size
;
if
(
size
!=
full_exp
)
*
it
++
=
decimal_point
;
return
copy_str
<
Char
>
(
digits
+
full_exp
,
digits
+
size
,
it
);
}
*
it
++
=
decimal_point
;
it
=
copy_str
<
Char
>
(
digits
+
full_exp
,
digits
+
size
,
it
);
if
(
params
.
num_digits
>
size
)
{
// Add trailing zeros.
int
num_zeros
=
params
.
num_digits
-
size
;
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
}
}
else
{
// 1234e-6 -> 0.001234
*
it
++
=
static_cast
<
Char
>
(
'0'
);
int
num_zeros
=
-
full_exp
;
if
(
params
.
num_digits
>=
0
&&
params
.
num_digits
<
num_zeros
)
num_zeros
=
params
.
num_digits
;
if
(
!
params
.
trailing_zeros
)
while
(
size
>
0
&&
digits
[
size
-
1
]
==
'0'
)
--
size
;
if
(
num_zeros
!=
0
||
size
!=
0
)
{
*
it
++
=
decimal_point
;
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
it
=
copy_str
<
Char
>
(
digits
,
digits
+
size
,
it
);
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
}
}
else
if
(
full_exp
>
0
)
{
// 1234e-2 -> 12.34[0+]
it
=
copy_str
<
Char
>
(
digits_
,
digits_
+
full_exp
,
it
);
if
(
!
specs_
.
trailing_zeros
)
{
// Remove trailing zeros.
int
num_digits
=
num_digits_
;
while
(
num_digits
>
full_exp
&&
digits_
[
num_digits
-
1
]
==
'0'
)
--
num_digits
;
if
(
num_digits
!=
full_exp
)
*
it
++
=
decimal_point_
;
return
copy_str
<
Char
>
(
digits_
+
full_exp
,
digits_
+
num_digits
,
it
);
}
*
it
++
=
decimal_point_
;
it
=
copy_str
<
Char
>
(
digits_
+
full_exp
,
digits_
+
num_digits_
,
it
);
if
(
specs_
.
precision
>
num_digits_
)
{
// Add trailing zeros.
int
num_zeros
=
specs_
.
precision
-
num_digits_
;
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
}
}
else
{
// 1234e-6 -> 0.001234
*
it
++
=
static_cast
<
Char
>
(
'0'
);
int
num_zeros
=
-
full_exp
;
if
(
specs_
.
precision
>=
0
&&
specs_
.
precision
<
num_zeros
)
num_zeros
=
specs_
.
precision
;
int
num_digits
=
num_digits_
;
if
(
!
specs_
.
trailing_zeros
)
while
(
num_digits
>
0
&&
digits_
[
num_digits
-
1
]
==
'0'
)
--
num_digits
;
if
(
num_zeros
!=
0
||
num_digits
!=
0
)
{
*
it
++
=
decimal_point_
;
it
=
std
::
fill_n
(
it
,
num_zeros
,
static_cast
<
Char
>
(
'0'
));
it
=
copy_str
<
Char
>
(
digits_
,
digits_
+
num_digits
,
it
);
}
}
return
it
;
}
return
it
;
}
namespace
grisu_options
{
enum
{
fixed
=
1
,
grisu3
=
2
};
}
public:
float_writer
(
const
char
*
digits
,
int
num_digits
,
int
exp
,
float_specs
specs
,
Char
decimal_point
)
:
digits_
(
digits
),
num_digits_
(
num_digits
),
exp_
(
exp
),
specs_
(
specs
),
decimal_point_
(
decimal_point
)
{
int
full_exp
=
num_digits
+
exp
-
1
;
int
precision
=
specs
.
precision
>
0
?
specs
.
precision
:
16
;
if
(
specs_
.
format
==
float_format
::
general
&&
!
(
full_exp
>=
-
4
&&
full_exp
<
precision
))
{
specs_
.
format
=
float_format
::
exp
;
}
size_
=
prettify
(
counting_iterator
()).
count
();
size_
+=
specs
.
sign
?
1
:
0
;
}
// Formats value using the Grisu algorithm:
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
==
sizeof
(
uint64_t
))>
FMT_API
bool
grisu_format
(
Double
,
buffer
<
char
>&
,
int
,
unsigned
,
int
&
);
template
<
typename
Double
,
FMT_ENABLE_IF
(
sizeof
(
Double
)
!=
sizeof
(
uint64_t
))>
inline
bool
grisu_format
(
Double
,
buffer
<
char
>&
,
int
,
unsigned
,
int
&
)
{
return
false
;
}
size_t
size
()
const
{
return
size_
;
}
size_t
width
()
const
{
return
size
();
}
struct
sprintf_specs
{
int
precision
;
char
type
;
bool
alt
:
1
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
specs_
.
sign
)
*
it
++
=
static_cast
<
Char
>
(
data
::
signs
[
specs_
.
sign
]);
it
=
prettify
(
it
);
}
};
template
<
typename
Char
>
constexpr
sprintf_specs
(
basic_format_specs
<
Char
>
specs
)
:
precision
(
specs
.
precision
),
type
(
specs
.
type
),
alt
(
specs
.
alt
)
{}
template
<
typename
T
>
int
format_float
(
T
value
,
int
precision
,
float_specs
specs
,
buffer
<
char
>&
buf
);
constexpr
bool
has_precision
()
const
{
return
precision
>=
0
;
}
};
// Formats a floating-point number with snprintf.
template
<
typename
T
>
int
snprintf_float
(
T
value
,
int
precision
,
float_specs
specs
,
buffer
<
char
>&
buf
);
template
<
typename
Double
>
char
*
sprintf_format
(
Double
,
internal
::
buffer
<
char
>&
,
sprintf_specs
);
template
<
typename
T
>
T
promote_float
(
T
value
)
{
return
value
;
}
inline
double
promote_float
(
float
value
)
{
return
value
;
}
template
<
typename
Handler
>
FMT_CONSTEXPR
void
handle_int_type_spec
(
char
spec
,
Handler
&&
handler
)
{
...
...
@@ -1134,36 +1271,56 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
}
}
template
<
typename
Handler
>
FMT_CONSTEXPR
void
handle_float_type_spec
(
char
spec
,
Handler
&&
handler
)
{
switch
(
spec
)
{
template
<
typename
ErrorHandler
=
error_handler
,
typename
Char
>
FMT_CONSTEXPR
float_specs
parse_float_type_spec
(
const
basic_format_specs
<
Char
>&
specs
,
ErrorHandler
&&
eh
=
{})
{
auto
result
=
float_specs
();
result
.
trailing_zeros
=
specs
.
alt
;
switch
(
specs
.
type
)
{
case
0
:
case
'g'
:
result
.
format
=
float_format
::
general
;
result
.
trailing_zeros
|=
specs
.
precision
!=
0
;
break
;
case
'G'
:
handler
.
on_general
();
result
.
upper
=
true
;
FMT_FALLTHROUGH
;
case
'g'
:
result
.
format
=
float_format
::
general
;
break
;
case
'e'
:
case
'E'
:
handler
.
on_exp
();
result
.
upper
=
true
;
FMT_FALLTHROUGH
;
case
'e'
:
result
.
format
=
float_format
::
exp
;
result
.
trailing_zeros
|=
specs
.
precision
!=
0
;
break
;
case
'f'
:
case
'F'
:
handler
.
on_fixed
();
result
.
upper
=
true
;
FMT_FALLTHROUGH
;
case
'f'
:
result
.
format
=
float_format
::
fixed
;
result
.
trailing_zeros
|=
specs
.
precision
!=
0
;
break
;
#if FMT_DEPRECATED_PERCENT
case
'%'
:
handler
.
on_percent
();
result
.
format
=
float_format
::
fixed
;
result
.
percent
=
true
;
break
;
case
'a'
:
#endif
case
'A'
:
handler
.
on_hex
();
result
.
upper
=
true
;
FMT_FALLTHROUGH
;
case
'a'
:
result
.
format
=
float_format
::
hex
;
break
;
case
'n'
:
handler
.
on_num
()
;
result
.
locale
=
true
;
break
;
default:
handler
.
on_error
(
);
eh
.
on_error
(
"invalid type specifier"
);
break
;
}
return
result
;
}
template
<
typename
Char
,
typename
Handler
>
...
...
@@ -1211,24 +1368,6 @@ template <typename ErrorHandler> class int_type_checker : private ErrorHandler {
}
};
template
<
typename
ErrorHandler
>
class
float_type_checker
:
private
ErrorHandler
{
public:
FMT_CONSTEXPR
explicit
float_type_checker
(
ErrorHandler
eh
)
:
ErrorHandler
(
eh
)
{}
FMT_CONSTEXPR
void
on_general
()
{}
FMT_CONSTEXPR
void
on_exp
()
{}
FMT_CONSTEXPR
void
on_fixed
()
{}
FMT_CONSTEXPR
void
on_percent
()
{}
FMT_CONSTEXPR
void
on_hex
()
{}
FMT_CONSTEXPR
void
on_num
()
{}
FMT_CONSTEXPR
void
on_error
()
{
ErrorHandler
::
on_error
(
"invalid type specifier"
);
}
};
template
<
typename
ErrorHandler
>
class
char_specs_checker
:
public
ErrorHandler
{
private:
...
...
@@ -1271,6 +1410,20 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
}
}
template
<
typename
Char
>
struct
nonfinite_writer
{
sign_t
sign
;
const
char
*
str
;
static
constexpr
size_t
str_size
=
3
;
size_t
size
()
const
{
return
str_size
+
(
sign
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
sign
)
*
it
++
=
static_cast
<
Char
>
(
data
::
signs
[
sign
]);
it
=
copy_str
<
Char
>
(
str
,
str
+
str_size
,
it
);
}
};
// This template provides operations for formatting and writing data into a
// character range.
template
<
typename
Range
>
class
basic_writer
{
...
...
@@ -1281,7 +1434,7 @@ template <typename Range> class basic_writer {
private:
iterator
out_
;
// Output iterator.
internal
::
locale_ref
locale_
;
locale_ref
locale_
;
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
...
...
@@ -1301,7 +1454,7 @@ template <typename Range> class basic_writer {
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
prefix
.
size
()
!=
0
)
it
=
internal
::
copy_str
<
char_type
>
(
prefix
.
begin
(),
prefix
.
end
(),
it
);
it
=
copy_str
<
char_type
>
(
prefix
.
begin
(),
prefix
.
end
(),
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
}
...
...
@@ -1312,18 +1465,18 @@ template <typename Range> class basic_writer {
// where <digits> are written by f(it).
template
<
typename
F
>
void
write_int
(
int
num_digits
,
string_view
prefix
,
format_specs
specs
,
F
f
)
{
std
::
size_t
size
=
prefix
.
size
()
+
internal
::
to_unsigned
(
num_digits
);
std
::
size_t
size
=
prefix
.
size
()
+
to_unsigned
(
num_digits
);
char_type
fill
=
specs
.
fill
[
0
];
std
::
size_t
padding
=
0
;
if
(
specs
.
align
==
align
::
numeric
)
{
auto
unsiged_width
=
internal
::
to_unsigned
(
specs
.
width
);
auto
unsiged_width
=
to_unsigned
(
specs
.
width
);
if
(
unsiged_width
>
size
)
{
padding
=
unsiged_width
-
size
;
size
=
unsiged_width
;
}
}
else
if
(
specs
.
precision
>
num_digits
)
{
size
=
prefix
.
size
()
+
internal
::
to_unsigned
(
specs
.
precision
);
padding
=
internal
::
to_unsigned
(
specs
.
precision
-
num_digits
);
size
=
prefix
.
size
()
+
to_unsigned
(
specs
.
precision
);
padding
=
to_unsigned
(
specs
.
precision
-
num_digits
);
fill
=
static_cast
<
char_type
>
(
'0'
);
}
if
(
specs
.
align
==
align
::
none
)
specs
.
align
=
align
::
right
;
...
...
@@ -1332,19 +1485,19 @@ template <typename Range> class basic_writer {
// Writes a decimal integer.
template
<
typename
Int
>
void
write_decimal
(
Int
value
)
{
auto
abs_value
=
static_cast
<
uint32_or_64_t
<
Int
>>
(
value
);
bool
is_negative
=
internal
::
is_negative
(
value
);
if
(
is_negative
)
abs_value
=
0
-
abs_value
;
i
nt
num_digits
=
internal
::
count_digits
(
abs_value
)
;
auto
&&
it
=
reserve
((
is_
negative
?
1
:
0
)
+
static_cast
<
size_t
>
(
num_digits
));
if
(
is_
negative
)
*
it
++
=
static_cast
<
char_type
>
(
'-'
);
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
num_digits
);
auto
abs_value
=
static_cast
<
uint32_or_64_
or_128_
t
<
Int
>>
(
value
);
bool
negative
=
is_negative
(
value
);
// Don't do -abs_value since it trips unsigned-integer-overflow sanitizer.
i
f
(
negative
)
abs_value
=
~
abs_value
+
1
;
int
num_digits
=
count_digits
(
abs_value
);
auto
&&
it
=
reserve
((
negative
?
1
:
0
)
+
static_cast
<
size_t
>
(
num_digits
));
if
(
negative
)
*
it
++
=
static_cast
<
char_type
>
(
'-'
);
it
=
format_decimal
<
char_type
>
(
it
,
abs_value
,
num_digits
);
}
// The handle_int_type_spec handler that writes an integer.
template
<
typename
Int
,
typename
Specs
>
struct
int_writer
{
using
unsigned_type
=
uint32_or_64_t
<
Int
>
;
using
unsigned_type
=
uint32_or_64_
or_128_
t
<
Int
>
;
basic_writer
<
Range
>&
writer
;
const
Specs
&
specs
;
...
...
@@ -1359,7 +1512,7 @@ template <typename Range> class basic_writer {
specs
(
s
),
abs_value
(
static_cast
<
unsigned_type
>
(
value
)),
prefix_size
(
0
)
{
if
(
i
nternal
::
i
s_negative
(
value
))
{
if
(
is_negative
(
value
))
{
prefix
[
0
]
=
'-'
;
++
prefix_size
;
abs_value
=
0
-
abs_value
;
...
...
@@ -1379,7 +1532,7 @@ template <typename Range> class basic_writer {
};
void
on_dec
()
{
int
num_digits
=
internal
::
count_digits
(
abs_value
);
int
num_digits
=
count_digits
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
specs
,
dec_writer
{
abs_value
,
num_digits
});
}
...
...
@@ -1389,8 +1542,8 @@ template <typename Range> class basic_writer {
int
num_digits
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_uint
<
4
,
char_type
>
(
it
,
self
.
abs_value
,
num_digits
,
self
.
specs
.
type
!=
'x'
);
it
=
format_uint
<
4
,
char_type
>
(
it
,
self
.
abs_value
,
num_digits
,
self
.
specs
.
type
!=
'x'
);
}
};
...
...
@@ -1399,7 +1552,7 @@ template <typename Range> class basic_writer {
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
specs
.
type
;
}
int
num_digits
=
internal
::
count_digits
<
4
>
(
abs_value
);
int
num_digits
=
count_digits
<
4
>
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
specs
,
hex_writer
{
*
this
,
num_digits
});
}
...
...
@@ -1409,7 +1562,7 @@ template <typename Range> class basic_writer {
int
num_digits
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
format_uint
<
BITS
,
char_type
>
(
it
,
abs_value
,
num_digits
);
it
=
format_uint
<
BITS
,
char_type
>
(
it
,
abs_value
,
num_digits
);
}
};
...
...
@@ -1418,14 +1571,14 @@ template <typename Range> class basic_writer {
prefix
[
prefix_size
++
]
=
'0'
;
prefix
[
prefix_size
++
]
=
static_cast
<
char
>
(
specs
.
type
);
}
int
num_digits
=
internal
::
count_digits
<
1
>
(
abs_value
);
int
num_digits
=
count_digits
<
1
>
(
abs_value
);
writer
.
write_int
(
num_digits
,
get_prefix
(),
specs
,
bin_writer
<
1
>
{
abs_value
,
num_digits
});
}
void
on_oct
()
{
int
num_digits
=
internal
::
count_digits
<
3
>
(
abs_value
);
if
(
specs
.
alt
&&
specs
.
precision
<=
num_digits
)
{
int
num_digits
=
count_digits
<
3
>
(
abs_value
);
if
(
specs
.
alt
&&
specs
.
precision
<=
num_digits
&&
abs_value
!=
0
)
{
// Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits.
prefix
[
prefix_size
++
]
=
'0'
;
...
...
@@ -1439,30 +1592,50 @@ template <typename Range> class basic_writer {
struct
num_writer
{
unsigned_type
abs_value
;
int
size
;
const
std
::
string
&
groups
;
char_type
sep
;
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
basic_string_view
<
char_type
>
s
(
&
sep
,
sep_size
);
// Index of a decimal digit with the least significant digit having
// index 0.
unsigned
digit_index
=
0
;
it
=
internal
::
format_decimal
<
char_type
>
(
it
,
abs_value
,
size
,
[
s
,
&
digit_index
](
char_type
*&
buffer
)
{
if
(
++
digit_index
%
3
!=
0
)
return
;
int
digit_index
=
0
;
std
::
string
::
const_iterator
group
=
groups
.
cbegin
();
it
=
format_decimal
<
char_type
>
(
it
,
abs_value
,
size
,
[
this
,
s
,
&
group
,
&
digit_index
](
char_type
*&
buffer
)
{
if
(
*
group
<=
0
||
++
digit_index
%
*
group
!=
0
||
*
group
==
max_value
<
char
>
())
return
;
if
(
group
+
1
!=
groups
.
cend
())
{
digit_index
=
0
;
++
group
;
}
buffer
-=
s
.
size
();
std
::
uninitialized_copy
(
s
.
data
(),
s
.
data
()
+
s
.
size
(),
internal
::
make_checked
(
buffer
,
s
.
size
()));
make_checked
(
buffer
,
s
.
size
()));
});
}
};
void
on_num
()
{
char_type
sep
=
internal
::
thousands_sep
<
char_type
>
(
writer
.
locale_
);
std
::
string
groups
=
grouping
<
char_type
>
(
writer
.
locale_
);
if
(
groups
.
empty
())
return
on_dec
();
auto
sep
=
thousands_sep
<
char_type
>
(
writer
.
locale_
);
if
(
!
sep
)
return
on_dec
();
int
num_digits
=
internal
::
count_digits
(
abs_value
);
int
size
=
num_digits
+
sep_size
*
((
num_digits
-
1
)
/
3
);
int
num_digits
=
count_digits
(
abs_value
);
int
size
=
num_digits
;
std
::
string
::
const_iterator
group
=
groups
.
cbegin
();
while
(
group
!=
groups
.
cend
()
&&
num_digits
>
*
group
&&
*
group
>
0
&&
*
group
!=
max_value
<
char
>
())
{
size
+=
sep_size
;
num_digits
-=
*
group
;
++
group
;
}
if
(
group
==
groups
.
cend
())
size
+=
sep_size
*
((
num_digits
-
1
)
/
groups
.
back
());
writer
.
write_int
(
size
,
get_prefix
(),
specs
,
num_writer
{
abs_value
,
size
,
sep
});
num_writer
{
abs_value
,
size
,
groups
,
sep
});
}
FMT_NORETURN
void
on_error
()
{
...
...
@@ -1470,98 +1643,17 @@ template <typename Range> class basic_writer {
}
};
enum
{
inf_size
=
3
};
// This is an enum to workaround a bug in MSVC.
struct
inf_or_nan_writer
{
char
sign
;
bool
as_percentage
;
const
char
*
str
;
size_t
size
()
const
{
return
static_cast
<
std
::
size_t
>
(
inf_size
+
(
sign
?
1
:
0
)
+
(
as_percentage
?
1
:
0
));
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
if
(
sign
)
*
it
++
=
static_cast
<
char_type
>
(
sign
);
it
=
internal
::
copy_str
<
char_type
>
(
str
,
str
+
static_cast
<
std
::
size_t
>
(
inf_size
),
it
);
if
(
as_percentage
)
*
it
++
=
static_cast
<
char_type
>
(
'%'
);
}
};
struct
double_writer
{
char
sign
;
internal
::
buffer
<
char
>&
buffer
;
char
*
decimal_point_pos
;
char_type
decimal_point
;
size_t
size
()
const
{
return
buffer
.
size
()
+
(
sign
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign
)
*
it
++
=
static_cast
<
char_type
>
(
sign
);
auto
begin
=
buffer
.
begin
();
if
(
decimal_point_pos
)
{
it
=
internal
::
copy_str
<
char_type
>
(
begin
,
decimal_point_pos
,
it
);
*
it
++
=
decimal_point
;
begin
=
decimal_point_pos
+
1
;
}
it
=
internal
::
copy_str
<
char_type
>
(
begin
,
buffer
.
end
(),
it
);
}
};
class
grisu_writer
{
private:
internal
::
buffer
<
char
>&
digits_
;
size_t
size_
;
char
sign_
;
int
exp_
;
internal
::
gen_digits_params
params_
;
char_type
decimal_point_
;
public:
grisu_writer
(
char
sign
,
internal
::
buffer
<
char
>&
digits
,
int
exp
,
const
internal
::
gen_digits_params
&
params
,
char_type
decimal_point
)
:
digits_
(
digits
),
sign_
(
sign
),
exp_
(
exp
),
params_
(
params
),
decimal_point_
(
decimal_point
)
{
int
num_digits
=
static_cast
<
int
>
(
digits
.
size
());
int
full_exp
=
num_digits
+
exp
-
1
;
int
precision
=
params
.
num_digits
>
0
?
params
.
num_digits
:
11
;
params_
.
fixed
|=
full_exp
>=
-
4
&&
full_exp
<
precision
;
auto
it
=
internal
::
grisu_prettify
<
char
>
(
digits
.
data
(),
num_digits
,
exp
,
internal
::
counting_iterator
<
char
>
(),
params_
,
'.'
);
size_
=
it
.
count
();
}
size_t
size
()
const
{
return
size_
+
(
sign_
?
1
:
0
);
}
size_t
width
()
const
{
return
size
();
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
{
if
(
sign_
)
*
it
++
=
static_cast
<
char_type
>
(
sign_
);
int
num_digits
=
static_cast
<
int
>
(
digits_
.
size
());
it
=
internal
::
grisu_prettify
<
char_type
>
(
digits_
.
data
(),
num_digits
,
exp_
,
it
,
params_
,
decimal_point_
);
}
};
template
<
typename
Char
>
struct
str_writer
{
const
Char
*
s
;
size_t
size_
;
size_t
size
()
const
{
return
size_
;
}
size_t
width
()
const
{
return
internal
::
count_code_points
(
basic_string_view
<
Char
>
(
s
,
size_
));
return
count_code_points
(
basic_string_view
<
Char
>
(
s
,
size_
));
}
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
it
=
internal
::
copy_str
<
char_type
>
(
s
,
s
+
size_
,
it
);
it
=
copy_str
<
char_type
>
(
s
,
s
+
size_
,
it
);
}
};
...
...
@@ -1575,14 +1667,12 @@ template <typename Range> class basic_writer {
template
<
typename
It
>
void
operator
()(
It
&&
it
)
const
{
*
it
++
=
static_cast
<
char_type
>
(
'0'
);
*
it
++
=
static_cast
<
char_type
>
(
'x'
);
it
=
internal
::
format_uint
<
4
,
char_type
>
(
it
,
value
,
num_digits
);
it
=
format_uint
<
4
,
char_type
>
(
it
,
value
,
num_digits
);
}
};
public:
/** Constructs a ``basic_writer`` object. */
explicit
basic_writer
(
Range
out
,
internal
::
locale_ref
loc
=
internal
::
locale_ref
())
explicit
basic_writer
(
Range
out
,
locale_ref
loc
=
locale_ref
())
:
out_
(
out
.
begin
()),
locale_
(
loc
)
{}
iterator
out
()
const
{
return
out_
;
}
...
...
@@ -1621,32 +1711,70 @@ template <typename Range> class basic_writer {
void
write
(
unsigned
long
value
)
{
write_decimal
(
value
);
}
void
write
(
unsigned
long
long
value
)
{
write_decimal
(
value
);
}
// Writes a formatted integer.
#if FMT_USE_INT128
void
write
(
int128_t
value
)
{
write_decimal
(
value
);
}
void
write
(
uint128_t
value
)
{
write_decimal
(
value
);
}
#endif
template
<
typename
T
,
typename
Spec
>
void
write_int
(
T
value
,
const
Spec
&
spec
)
{
internal
::
handle_int_type_spec
(
spec
.
type
,
int_writer
<
T
,
Spec
>
(
*
this
,
value
,
spec
));
handle_int_type_spec
(
spec
.
type
,
int_writer
<
T
,
Spec
>
(
*
this
,
value
,
spec
));
}
void
write
(
double
value
,
const
format_specs
&
specs
=
format_specs
())
{
write_double
(
value
,
specs
);
}
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_floating_point
<
T
>
::
value
)
>
void
write
(
T
value
,
format_specs
specs
=
{})
{
float_specs
fspecs
=
parse_float_type_spec
(
specs
);
fspecs
.
sign
=
specs
.
sign
;
if
(
std
::
signbit
(
value
))
{
// value < 0 is false for NaN so use signbit.
fspecs
.
sign
=
sign
::
minus
;
value
=
-
value
;
}
else
if
(
fspecs
.
sign
==
sign
::
minus
)
{
fspecs
.
sign
=
sign
::
none
;
}
/**
\rst
Formats *value* using the general format for floating-point numbers
(``'g'``) and writes it to the buffer.
\endrst
*/
void
write
(
long
double
value
,
const
format_specs
&
specs
=
format_specs
())
{
write_double
(
value
,
specs
);
}
if
(
!
std
::
isfinite
(
value
))
{
auto
str
=
std
::
isinf
(
value
)
?
(
fspecs
.
upper
?
"INF"
:
"inf"
)
:
(
fspecs
.
upper
?
"NAN"
:
"nan"
);
return
write_padded
(
specs
,
nonfinite_writer
<
char_type
>
{
fspecs
.
sign
,
str
});
}
if
(
specs
.
align
==
align
::
none
)
{
specs
.
align
=
align
::
right
;
}
else
if
(
specs
.
align
==
align
::
numeric
)
{
if
(
fspecs
.
sign
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
static_cast
<
char_type
>
(
data
::
signs
[
fspecs
.
sign
]);
fspecs
.
sign
=
sign
::
none
;
if
(
specs
.
width
!=
0
)
--
specs
.
width
;
}
specs
.
align
=
align
::
right
;
}
// Formats a floating-point number (double or long double).
template
<
typename
T
,
bool
USE_GRISU
=
fmt
::
internal
::
use_grisu
<
T
>()
>
void
write_double
(
T
value
,
const
format_specs
&
specs
);
memory_buffer
buffer
;
if
(
fspecs
.
format
==
float_format
::
hex
)
{
if
(
fspecs
.
sign
)
buffer
.
push_back
(
data
::
signs
[
fspecs
.
sign
]);
snprintf_float
(
promote_float
(
value
),
specs
.
precision
,
fspecs
,
buffer
);
write_padded
(
specs
,
str_writer
<
char
>
{
buffer
.
data
(),
buffer
.
size
()});
return
;
}
int
precision
=
specs
.
precision
>=
0
||
!
specs
.
type
?
specs
.
precision
:
6
;
if
(
fspecs
.
format
==
float_format
::
exp
)
++
precision
;
if
(
const_check
(
std
::
is_same
<
T
,
float
>
()))
fspecs
.
binary32
=
true
;
fspecs
.
use_grisu
=
use_grisu
<
T
>
();
if
(
const_check
(
FMT_DEPRECATED_PERCENT
)
&&
fspecs
.
percent
)
value
*=
100
;
int
exp
=
format_float
(
promote_float
(
value
),
precision
,
fspecs
,
buffer
);
if
(
const_check
(
FMT_DEPRECATED_PERCENT
)
&&
fspecs
.
percent
)
{
buffer
.
push_back
(
'%'
);
--
exp
;
// Adjust decimal place position.
}
fspecs
.
precision
=
precision
;
char_type
point
=
fspecs
.
locale
?
decimal_point
<
char_type
>
(
locale_
)
:
static_cast
<
char_type
>
(
'.'
);
write_padded
(
specs
,
float_writer
<
char_type
>
(
buffer
.
data
(),
static_cast
<
int
>
(
buffer
.
size
()),
exp
,
fspecs
,
point
));
}
/** Writes a character to the buffer. */
void
write
(
char
value
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
value
;
...
...
@@ -1658,14 +1786,9 @@ template <typename Range> class basic_writer {
*
it
++
=
value
;
}
/**
\rst
Writes *value* to the buffer.
\endrst
*/
void
write
(
string_view
value
)
{
auto
&&
it
=
reserve
(
value
.
size
());
it
=
internal
::
copy_str
<
char_type
>
(
value
.
begin
(),
value
.
end
(),
it
);
it
=
copy_str
<
char_type
>
(
value
.
begin
(),
value
.
end
(),
it
);
}
void
write
(
wstring_view
value
)
{
static_assert
(
std
::
is_same
<
char_type
,
wchar_t
>::
value
,
""
);
...
...
@@ -1673,25 +1796,23 @@ template <typename Range> class basic_writer {
it
=
std
::
copy
(
value
.
begin
(),
value
.
end
(),
it
);
}
// Writes a formatted string.
template
<
typename
Char
>
void
write
(
const
Char
*
s
,
std
::
size_t
size
,
const
format_specs
&
specs
)
{
write_padded
(
specs
,
str_writer
<
Char
>
{
s
,
size
});
}
template
<
typename
Char
>
void
write
(
basic_string_view
<
Char
>
s
,
const
format_specs
&
specs
=
format_specs
())
{
void
write
(
basic_string_view
<
Char
>
s
,
const
format_specs
&
specs
=
{})
{
const
Char
*
data
=
s
.
data
();
std
::
size_t
size
=
s
.
size
();
if
(
specs
.
precision
>=
0
&&
internal
::
to_unsigned
(
specs
.
precision
)
<
size
)
size
=
internal
::
to_unsigned
(
specs
.
precision
);
if
(
specs
.
precision
>=
0
&&
to_unsigned
(
specs
.
precision
)
<
size
)
size
=
code_point_index
(
s
,
to_unsigned
(
specs
.
precision
)
);
write
(
data
,
size
,
specs
);
}
template
<
typename
UIntPtr
>
void
write_pointer
(
UIntPtr
value
,
const
format_specs
*
specs
)
{
int
num_digits
=
internal
::
count_digits
<
4
>
(
value
);
int
num_digits
=
count_digits
<
4
>
(
value
);
auto
pw
=
pointer_writer
<
UIntPtr
>
{
value
,
num_digits
};
if
(
!
specs
)
return
pw
(
reserve
(
to_unsigned
(
num_digits
)
+
2
));
format_specs
specs_copy
=
*
specs
;
...
...
@@ -1702,6 +1823,10 @@ template <typename Range> class basic_writer {
using
writer
=
basic_writer
<
buffer_range
<
char
>>
;
template
<
typename
T
>
struct
is_integral
:
std
::
is_integral
<
T
>
{};
template
<
>
struct
is_integral
<
int128_t
>
:
std
::
true_type
{};
template
<
>
struct
is_integral
<
uint128_t
>
:
std
::
true_type
{};
template
<
typename
Range
,
typename
ErrorHandler
=
internal
::
error_handler
>
class
arg_formatter_base
{
public:
...
...
@@ -1731,7 +1856,7 @@ class arg_formatter_base {
}
void
write_pointer
(
const
void
*
p
)
{
writer_
.
write_pointer
(
internal
::
bit_cast
<
internal
::
uintptr_t
>
(
p
),
specs_
);
writer_
.
write_pointer
(
internal
::
to_uintptr
(
p
),
specs_
);
}
protected:
...
...
@@ -1764,7 +1889,7 @@ class arg_formatter_base {
return
out
();
}
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
template
<
typename
T
,
FMT_ENABLE_IF
(
is_integral
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
if
(
specs_
)
writer_
.
write_int
(
value
,
*
specs_
);
...
...
@@ -1787,7 +1912,7 @@ class arg_formatter_base {
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_floating_point
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
writer_
.
write
_double
(
value
,
specs_
?
*
specs_
:
format_specs
());
writer_
.
write
(
value
,
specs_
?
*
specs_
:
format_specs
());
return
out
();
}
...
...
@@ -1852,14 +1977,14 @@ template <typename Char> FMT_CONSTEXPR bool is_name_start(Char c) {
template
<
typename
Char
,
typename
ErrorHandler
>
FMT_CONSTEXPR
int
parse_nonnegative_int
(
const
Char
*&
begin
,
const
Char
*
end
,
ErrorHandler
&&
eh
)
{
assert
(
begin
!=
end
&&
'0'
<=
*
begin
&&
*
begin
<=
'9'
);
FMT_ASSERT
(
begin
!=
end
&&
'0'
<=
*
begin
&&
*
begin
<=
'9'
,
""
);
if
(
*
begin
==
'0'
)
{
++
begin
;
return
0
;
}
unsigned
value
=
0
;
// Convert to unsigned to prevent a warning.
constexpr
unsigned
max_int
=
(
std
::
numeric_limits
<
int
>::
max
)
();
constexpr
unsigned
max_int
=
max_value
<
int
>
();
unsigned
big
=
max_int
/
10
;
do
{
// Check for overflow.
...
...
@@ -1878,11 +2003,11 @@ template <typename Context> class custom_formatter {
private:
using
char_type
=
typename
Context
::
char_type
;
basic_parse_context
<
char_type
>&
parse_ctx_
;
basic_
format_
parse_context
<
char_type
>&
parse_ctx_
;
Context
&
ctx_
;
public:
explicit
custom_formatter
(
basic_parse_context
<
char_type
>&
parse_ctx
,
explicit
custom_formatter
(
basic_
format_
parse_context
<
char_type
>&
parse_ctx
,
Context
&
ctx
)
:
parse_ctx_
(
parse_ctx
),
ctx_
(
ctx
)
{}
...
...
@@ -1896,7 +2021,7 @@ template <typename Context> class custom_formatter {
template
<
typename
T
>
using
is_integer
=
bool_constant
<
std
::
is_integral
<
T
>::
value
&&
!
std
::
is_same
<
T
,
bool
>::
value
&&
bool_constant
<
is_integral
<
T
>::
value
&&
!
std
::
is_same
<
T
,
bool
>::
value
&&
!
std
::
is_same
<
T
,
char
>::
value
&&
!
std
::
is_same
<
T
,
wchar_t
>::
value
>
;
...
...
@@ -1981,20 +2106,20 @@ template <typename ErrorHandler> class numeric_specs_checker {
:
error_handler_
(
eh
),
arg_type_
(
arg_type
)
{}
FMT_CONSTEXPR
void
require_numeric_argument
()
{
if
(
!
is_arithmetic
(
arg_type_
))
if
(
!
is_arithmetic
_type
(
arg_type_
))
error_handler_
.
on_error
(
"format specifier requires numeric argument"
);
}
FMT_CONSTEXPR
void
check_sign
()
{
require_numeric_argument
();
if
(
is_integral
(
arg_type_
)
&&
arg_type_
!=
int_type
&&
if
(
is_integral
_type
(
arg_type_
)
&&
arg_type_
!=
int_type
&&
arg_type_
!=
long_long_type
&&
arg_type_
!=
internal
::
char_type
)
{
error_handler_
.
on_error
(
"format specifier requires signed argument"
);
}
}
FMT_CONSTEXPR
void
check_precision
()
{
if
(
is_integral
(
arg_type_
)
||
arg_type_
==
internal
::
pointer_type
)
if
(
is_integral
_type
(
arg_type_
)
||
arg_type_
==
internal
::
pointer_type
)
error_handler_
.
on_error
(
"precision not allowed for this argument type"
);
}
...
...
@@ -2049,14 +2174,12 @@ template <typename Handler> class specs_checker : public Handler {
numeric_specs_checker
<
Handler
>
checker_
;
};
template
<
template
<
typename
>
class
Handler
,
typename
T
,
typename
FormatArg
,
template
<
template
<
typename
>
class
Handler
,
typename
FormatArg
,
typename
ErrorHandler
>
FMT_CONSTEXPR
void
set_dynamic_spec
(
T
&
value
,
FormatArg
arg
,
ErrorHandler
eh
)
{
unsigned
long
long
big_value
=
visit_format_arg
(
Handler
<
ErrorHandler
>
(
eh
),
arg
);
if
(
big_value
>
to_unsigned
((
std
::
numeric_limits
<
int
>::
max
)()))
eh
.
on_error
(
"number is too big"
);
value
=
static_cast
<
T
>
(
big_value
);
FMT_CONSTEXPR
int
get_dynamic_spec
(
FormatArg
arg
,
ErrorHandler
eh
)
{
unsigned
long
long
value
=
visit_format_arg
(
Handler
<
ErrorHandler
>
(
eh
),
arg
);
if
(
value
>
to_unsigned
(
max_value
<
int
>
()))
eh
.
on_error
(
"number is too big"
);
return
static_cast
<
int
>
(
value
);
}
struct
auto_id
{};
...
...
@@ -2081,13 +2204,13 @@ class specs_handler : public specs_setter<typename Context::char_type> {
context_
(
ctx
)
{}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_width
(
Id
arg_id
)
{
set_dynamic_spec
<
width_checker
>
(
this
->
specs_
.
width
,
get_arg
(
arg_id
),
context_
.
error_handler
());
this
->
specs_
.
width
=
get_dynamic_spec
<
width_checker
>
(
get_arg
(
arg_id
),
context_
.
error_handler
());
}
template
<
typename
Id
>
FMT_CONSTEXPR
void
on_dynamic_precision
(
Id
arg_id
)
{
set_dynamic_spec
<
precision_checker
>
(
this
->
specs_
.
precision
,
get_arg
(
arg_id
),
context_
.
error_handler
());
this
->
specs_
.
precision
=
get_dynamic_spec
<
precision_checker
>
(
get_arg
(
arg_id
),
context_
.
error_handler
());
}
void
on_error
(
const
char
*
message
)
{
context_
.
on_error
(
message
);
}
...
...
@@ -2114,24 +2237,6 @@ class specs_handler : public specs_setter<typename Context::char_type> {
Context
&
context_
;
};
struct
string_view_metadata
{
FMT_CONSTEXPR
string_view_metadata
()
:
offset_
(
0u
),
size_
(
0u
)
{}
template
<
typename
Char
>
FMT_CONSTEXPR
string_view_metadata
(
basic_string_view
<
Char
>
primary_string
,
basic_string_view
<
Char
>
view
)
:
offset_
(
to_unsigned
(
view
.
data
()
-
primary_string
.
data
())),
size_
(
view
.
size
())
{}
FMT_CONSTEXPR
string_view_metadata
(
std
::
size_t
offset
,
std
::
size_t
size
)
:
offset_
(
offset
),
size_
(
size
)
{}
template
<
typename
Char
>
FMT_CONSTEXPR
basic_string_view
<
Char
>
to_view
(
const
Char
*
str
)
const
{
return
{
str
+
offset_
,
size_
};
}
std
::
size_t
offset_
;
std
::
size_t
size_
;
};
enum
class
arg_id_kind
{
none
,
index
,
name
};
// An argument reference.
...
...
@@ -2139,7 +2244,7 @@ template <typename Char> struct arg_ref {
FMT_CONSTEXPR
arg_ref
()
:
kind
(
arg_id_kind
::
none
),
val
()
{}
FMT_CONSTEXPR
explicit
arg_ref
(
int
index
)
:
kind
(
arg_id_kind
::
index
),
val
(
index
)
{}
FMT_CONSTEXPR
explicit
arg_ref
(
string_view_metadata
name
)
FMT_CONSTEXPR
explicit
arg_ref
(
basic_string_view
<
Char
>
name
)
:
kind
(
arg_id_kind
::
name
),
val
(
name
)
{}
FMT_CONSTEXPR
arg_ref
&
operator
=
(
int
idx
)
{
...
...
@@ -2150,12 +2255,11 @@ template <typename Char> struct arg_ref {
arg_id_kind
kind
;
union
value
{
FMT_CONSTEXPR
value
()
:
index
(
0u
)
{}
FMT_CONSTEXPR
value
(
int
id
)
:
index
(
id
)
{}
FMT_CONSTEXPR
value
(
string_view_metadata
n
)
:
name
(
n
)
{}
FMT_CONSTEXPR
value
(
int
id
=
0
)
:
index
{
id
}
{}
FMT_CONSTEXPR
value
(
basic_string_view
<
Char
>
n
)
:
name
(
n
)
{}
int
index
;
string_view_metadata
name
;
basic_string_view
<
Char
>
name
;
}
val
;
};
...
...
@@ -2213,8 +2317,7 @@ class dynamic_specs_handler
context_
.
check_arg_id
(
arg_id
);
basic_string_view
<
char_type
>
format_str
(
context_
.
begin
(),
to_unsigned
(
context_
.
end
()
-
context_
.
begin
()));
const
auto
id_metadata
=
string_view_metadata
(
format_str
,
arg_id
);
return
arg_ref_type
(
id_metadata
);
return
arg_ref_type
(
arg_id
);
}
dynamic_format_specs
<
char_type
>&
specs_
;
...
...
@@ -2224,18 +2327,24 @@ class dynamic_specs_handler
template
<
typename
Char
,
typename
IDHandler
>
FMT_CONSTEXPR
const
Char
*
parse_arg_id
(
const
Char
*
begin
,
const
Char
*
end
,
IDHandler
&&
handler
)
{
assert
(
begin
!=
end
);
FMT_ASSERT
(
begin
!=
end
,
""
);
Char
c
=
*
begin
;
if
(
c
==
'}'
||
c
==
':'
)
return
handler
(),
begin
;
if
(
c
==
'}'
||
c
==
':'
)
{
handler
();
return
begin
;
}
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
int
index
=
parse_nonnegative_int
(
begin
,
end
,
handler
);
if
(
begin
==
end
||
(
*
begin
!=
'}'
&&
*
begin
!=
':'
))
return
handler
.
on_error
(
"invalid format string"
),
begin
;
handler
(
index
);
handler
.
on_error
(
"invalid format string"
);
else
handler
(
index
);
return
begin
;
}
if
(
!
is_name_start
(
c
))
{
handler
.
on_error
(
"invalid format string"
);
return
begin
;
}
if
(
!
is_name_start
(
c
))
return
handler
.
on_error
(
"invalid format string"
),
begin
;
auto
it
=
begin
;
do
{
++
it
;
...
...
@@ -2294,9 +2403,11 @@ FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
case
'>'
:
align
=
align
::
right
;
break
;
#if FMT_NUMERIC_ALIGN
case
'='
:
align
=
align
::
numeric
;
break
;
#endif
case
'^'
:
align
=
align
::
center
;
break
;
...
...
@@ -2439,7 +2550,7 @@ template <typename Handler, typename Char> struct id_adapter {
template
<
bool
IS_CONSTEXPR
,
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
void
parse_format_string
(
basic_string_view
<
Char
>
format_str
,
Handler
&&
handler
)
{
struct
writer
{
struct
pfs_
writer
{
FMT_CONSTEXPR
void
operator
()(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
==
end
)
return
;
for
(;;)
{
...
...
@@ -2497,10 +2608,9 @@ FMT_CONSTEXPR const typename ParseContext::char_type* parse_format_specs(
conditional_t
<
internal
::
mapped_type_constant
<
T
,
context
>::
value
!=
internal
::
custom_type
,
decltype
(
arg_mapper
<
context
>
().
map
(
std
::
declval
<
T
>
())),
T
>
;
conditional_t
<
has_formatter
<
mapped_type
,
context
>::
value
,
formatter
<
mapped_type
,
char_type
>
,
internal
::
fallback_formatter
<
T
,
char_type
>>
f
;
auto
f
=
conditional_t
<
has_formatter
<
mapped_type
,
context
>::
value
,
formatter
<
mapped_type
,
char_type
>
,
internal
::
fallback_formatter
<
T
,
char_type
>>
();
return
f
.
parse
(
ctx
);
}
...
...
@@ -2509,7 +2619,7 @@ class format_string_checker {
public:
explicit
FMT_CONSTEXPR
format_string_checker
(
basic_string_view
<
Char
>
format_str
,
ErrorHandler
eh
)
:
arg_id_
(
(
std
::
numeric_limits
<
unsigned
>::
max
)
()),
:
arg_id_
(
max_value
<
unsigned
>
()),
context_
(
format_str
,
eh
),
parse_funcs_
{
&
parse_format_specs
<
Args
,
parse_context_type
>
...}
{}
...
...
@@ -2540,7 +2650,7 @@ class format_string_checker {
}
private:
using
parse_context_type
=
basic_parse_context
<
Char
,
ErrorHandler
>
;
using
parse_context_type
=
basic_
format_
parse_context
<
Char
,
ErrorHandler
>
;
enum
{
num_args
=
sizeof
...(
Args
)
};
FMT_CONSTEXPR
void
check_arg_id
()
{
...
...
@@ -2573,32 +2683,29 @@ void check_format_string(S format_str) {
(
void
)
invalid_format
;
}
template
<
template
<
typename
>
class
Handler
,
typename
Spec
,
typename
Context
>
void
handle_dynamic_spec
(
Spec
&
value
,
arg_ref
<
typename
Context
::
char_type
>
ref
,
Context
&
ctx
,
const
typename
Context
::
char_type
*
format_str
)
{
template
<
template
<
typename
>
class
Handler
,
typename
Context
>
void
handle_dynamic_spec
(
int
&
value
,
arg_ref
<
typename
Context
::
char_type
>
ref
,
Context
&
ctx
)
{
switch
(
ref
.
kind
)
{
case
arg_id_kind
:
:
none
:
break
;
case
arg_id_kind
:
:
index
:
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
arg
(
ref
.
val
.
index
),
ctx
.
error_handler
());
value
=
internal
::
get_dynamic_spec
<
Handler
>
(
ctx
.
arg
(
ref
.
val
.
index
),
ctx
.
error_handler
());
break
;
case
arg_id_kind
:
:
name
:
{
const
auto
arg_id
=
ref
.
val
.
name
.
to_view
(
format_str
);
internal
::
set_dynamic_spec
<
Handler
>
(
value
,
ctx
.
arg
(
arg_id
),
ctx
.
error_handler
());
case
arg_id_kind
:
:
name
:
value
=
internal
::
get_dynamic_spec
<
Handler
>
(
ctx
.
arg
(
ref
.
val
.
name
),
ctx
.
error_handler
());
break
;
}
}
}
}
// namespace internal
template
<
typename
Range
>
using
basic_writer
FMT_DEPRECATED
=
internal
::
basic_writer
<
Range
>
;
using
writer
FMT_DEPRECATED
=
internal
::
writer
;
using
wwriter
FMT_DEPRECATED
=
internal
::
basic_writer
<
internal
::
buffer_range
<
wchar_t
>>
;
using
basic_writer
FMT_DEPRECATED
_ALIAS
=
internal
::
basic_writer
<
Range
>
;
using
writer
FMT_DEPRECATED
_ALIAS
=
internal
::
writer
;
using
wwriter
FMT_DEPRECATED
_ALIAS
=
internal
::
basic_writer
<
buffer_range
<
wchar_t
>>
;
/** The default argument formatter. */
template
<
typename
Range
>
...
...
@@ -2609,7 +2716,7 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
using
context_type
=
basic_format_context
<
typename
base
::
iterator
,
char_type
>
;
context_type
&
ctx_
;
basic_parse_context
<
char_type
>*
parse_ctx_
;
basic_
format_
parse_context
<
char_type
>*
parse_ctx_
;
public:
using
range
=
Range
;
...
...
@@ -2623,9 +2730,10 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
*specs* contains format specifier information for standard argument types.
\endrst
*/
explicit
arg_formatter
(
context_type
&
ctx
,
basic_parse_context
<
char_type
>*
parse_ctx
=
nullptr
,
format_specs
*
specs
=
nullptr
)
explicit
arg_formatter
(
context_type
&
ctx
,
basic_format_parse_context
<
char_type
>*
parse_ctx
=
nullptr
,
format_specs
*
specs
=
nullptr
)
:
base
(
Range
(
ctx
.
out
()),
specs
,
ctx
.
locale
()),
ctx_
(
ctx
),
parse_ctx_
(
parse_ctx
)
{}
...
...
@@ -2635,7 +2743,7 @@ class arg_formatter : public internal::arg_formatter_base<Range> {
/** Formats an argument of a user-defined type. */
iterator
operator
()(
typename
basic_format_arg
<
context_type
>::
handle
handle
)
{
handle
.
format
(
*
parse_ctx_
,
ctx_
);
return
this
->
out
();
return
ctx_
.
out
();
}
};
...
...
@@ -2676,7 +2784,11 @@ class FMT_API system_error : public std::runtime_error {
:
std
::
runtime_error
(
""
)
{
init
(
error_code
,
message
,
make_format_args
(
args
...));
}
~
system_error
()
FMT_NOEXCEPT
;
system_error
(
const
system_error
&
)
=
default
;
system_error
&
operator
=
(
const
system_error
&
)
=
default
;
system_error
(
system_error
&&
)
=
default
;
system_error
&
operator
=
(
system_error
&&
)
=
default
;
~
system_error
()
FMT_NOEXCEPT
FMT_OVERRIDE
;
int
error_code
()
const
{
return
error_code_
;
}
};
...
...
@@ -2698,126 +2810,7 @@ class FMT_API system_error : public std::runtime_error {
\endrst
*/
FMT_API
void
format_system_error
(
internal
::
buffer
<
char
>&
out
,
int
error_code
,
fmt
::
string_view
message
)
FMT_NOEXCEPT
;
struct
float_spec_handler
{
char
type
;
bool
upper
;
bool
fixed
;
bool
as_percentage
;
bool
use_locale
;
explicit
float_spec_handler
(
char
t
)
:
type
(
t
),
upper
(
false
),
fixed
(
false
),
as_percentage
(
false
),
use_locale
(
false
)
{}
void
on_general
()
{
if
(
type
==
'G'
)
upper
=
true
;
}
void
on_exp
()
{
if
(
type
==
'E'
)
upper
=
true
;
}
void
on_fixed
()
{
fixed
=
true
;
if
(
type
==
'F'
)
upper
=
true
;
}
void
on_percent
()
{
fixed
=
true
;
as_percentage
=
true
;
}
void
on_hex
()
{
if
(
type
==
'A'
)
upper
=
true
;
}
void
on_num
()
{
use_locale
=
true
;
}
FMT_NORETURN
void
on_error
()
{
FMT_THROW
(
format_error
(
"invalid type specifier"
));
}
};
template
<
typename
Range
>
template
<
typename
T
,
bool
USE_GRISU
>
void
internal
::
basic_writer
<
Range
>::
write_double
(
T
value
,
const
format_specs
&
specs
)
{
// Check type.
float_spec_handler
handler
(
static_cast
<
char
>
(
specs
.
type
));
internal
::
handle_float_type_spec
(
handler
.
type
,
handler
);
char
sign
=
0
;
// Use signbit instead of value < 0 since the latter is always false for NaN.
if
(
std
::
signbit
(
value
))
{
sign
=
'-'
;
value
=
-
value
;
}
else
if
(
specs
.
sign
!=
sign
::
none
)
{
if
(
specs
.
sign
==
sign
::
plus
)
sign
=
'+'
;
else
if
(
specs
.
sign
==
sign
::
space
)
sign
=
' '
;
}
if
(
!
std
::
isfinite
(
value
))
{
// Format infinity and NaN ourselves because sprintf's output is not
// consistent across platforms.
const
char
*
str
=
std
::
isinf
(
value
)
?
(
handler
.
upper
?
"INF"
:
"inf"
)
:
(
handler
.
upper
?
"NAN"
:
"nan"
);
return
write_padded
(
specs
,
inf_or_nan_writer
{
sign
,
handler
.
as_percentage
,
str
});
}
if
(
handler
.
as_percentage
)
value
*=
100
;
memory_buffer
buffer
;
int
exp
=
0
;
int
precision
=
specs
.
precision
>=
0
||
!
specs
.
type
?
specs
.
precision
:
6
;
bool
use_grisu
=
USE_GRISU
&&
(
specs
.
type
!=
'a'
&&
specs
.
type
!=
'A'
&&
specs
.
type
!=
'e'
&&
specs
.
type
!=
'E'
)
&&
internal
::
grisu_format
(
static_cast
<
double
>
(
value
),
buffer
,
precision
,
handler
.
fixed
?
internal
::
grisu_options
::
fixed
:
0
,
exp
);
char
*
decimal_point_pos
=
nullptr
;
if
(
!
use_grisu
)
decimal_point_pos
=
internal
::
sprintf_format
(
value
,
buffer
,
specs
);
if
(
handler
.
as_percentage
)
{
buffer
.
push_back
(
'%'
);
--
exp
;
// Adjust decimal place position.
}
format_specs
as
=
specs
;
if
(
specs
.
align
==
align
::
numeric
)
{
if
(
sign
)
{
auto
&&
it
=
reserve
(
1
);
*
it
++
=
static_cast
<
char_type
>
(
sign
);
sign
=
0
;
if
(
as
.
width
)
--
as
.
width
;
}
as
.
align
=
align
::
right
;
}
else
if
(
specs
.
align
==
align
::
none
)
{
as
.
align
=
align
::
right
;
}
char_type
decimal_point
=
handler
.
use_locale
?
internal
::
decimal_point
<
char_type
>
(
locale_
)
:
static_cast
<
char_type
>
(
'.'
);
if
(
use_grisu
)
{
auto
params
=
internal
::
gen_digits_params
();
params
.
fixed
=
handler
.
fixed
;
params
.
num_digits
=
precision
;
params
.
trailing_zeros
=
(
precision
!=
0
&&
(
handler
.
fixed
||
!
specs
.
type
))
||
specs
.
alt
;
write_padded
(
as
,
grisu_writer
(
sign
,
buffer
,
exp
,
params
,
decimal_point
));
}
else
{
write_padded
(
as
,
double_writer
{
sign
,
buffer
,
decimal_point_pos
,
decimal_point
});
}
}
string_view
message
)
FMT_NOEXCEPT
;
// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
...
...
@@ -2889,7 +2882,7 @@ class format_int {
// 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
=
static_cast
<
unsigned
>
((
value
%
100
)
*
2
);
auto
index
=
static_cast
<
unsigned
>
((
value
%
100
)
*
2
);
value
/=
100
;
*--
ptr
=
internal
::
data
::
digits
[
index
+
1
];
*--
ptr
=
internal
::
data
::
digits
[
index
];
...
...
@@ -2898,14 +2891,14 @@ class format_int {
*--
ptr
=
static_cast
<
char
>
(
'0'
+
value
);
return
ptr
;
}
unsigned
index
=
static_cast
<
unsigned
>
(
value
*
2
);
auto
index
=
static_cast
<
unsigned
>
(
value
*
2
);
*--
ptr
=
internal
::
data
::
digits
[
index
+
1
];
*--
ptr
=
internal
::
data
::
digits
[
index
];
return
ptr
;
}
void
format_signed
(
long
long
value
)
{
unsigned
long
long
abs_value
=
static_cast
<
unsigned
long
long
>
(
value
);
auto
abs_value
=
static_cast
<
unsigned
long
long
>
(
value
);
bool
negative
=
value
<
0
;
if
(
negative
)
abs_value
=
0
-
abs_value
;
str_
=
format_decimal
(
abs_value
);
...
...
@@ -2954,13 +2947,12 @@ template <typename T, typename Char>
struct
formatter
<
T
,
Char
,
enable_if_t
<
internal
::
type_constant
<
T
,
Char
>::
value
!=
internal
::
custom_type
>>
{
FMT_CONSTEXPR
formatter
()
:
format_str_
(
nullptr
)
{}
FMT_CONSTEXPR
formatter
()
=
default
;
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template
<
typename
ParseContext
>
FMT_CONSTEXPR
auto
parse
(
ParseContext
&
ctx
)
->
decltype
(
ctx
.
begin
())
{
format_str_
=
ctx
.
begin
();
using
handler_type
=
internal
::
dynamic_specs_handler
<
ParseContext
>
;
auto
type
=
internal
::
type_constant
<
T
,
Char
>::
value
;
internal
::
specs_checker
<
handler_type
>
handler
(
handler_type
(
specs_
,
ctx
),
...
...
@@ -2976,6 +2968,8 @@ struct formatter<T, Char,
case
internal
:
:
uint_type
:
case
internal
:
:
long_long_type
:
case
internal
:
:
ulong_long_type
:
case
internal
:
:
int128_type
:
case
internal
:
:
uint128_type
:
case
internal
:
:
bool_type
:
handle_int_type_spec
(
specs_
.
type
,
internal
::
int_type_checker
<
decltype
(
eh
)
>
(
eh
));
...
...
@@ -2984,10 +2978,10 @@ struct formatter<T, Char,
handle_char_specs
(
&
specs_
,
internal
::
char_specs_checker
<
decltype
(
eh
)
>
(
specs_
.
type
,
eh
));
break
;
case
internal
:
:
float_type
:
case
internal
:
:
double_type
:
case
internal
:
:
long_double_type
:
handle_float_type_spec
(
specs_
.
type
,
internal
::
float_type_checker
<
decltype
(
eh
)
>
(
eh
));
internal
::
parse_float_type_spec
(
specs_
,
eh
);
break
;
case
internal
:
:
cstring_type
:
internal
::
handle_cstring_type_spec
(
...
...
@@ -3010,9 +3004,9 @@ struct formatter<T, Char,
template
<
typename
FormatContext
>
auto
format
(
const
T
&
val
,
FormatContext
&
ctx
)
->
decltype
(
ctx
.
out
())
{
internal
::
handle_dynamic_spec
<
internal
::
width_checker
>
(
specs_
.
width
,
specs_
.
width_ref
,
ctx
,
format_str_
);
specs_
.
width
,
specs_
.
width_ref
,
ctx
);
internal
::
handle_dynamic_spec
<
internal
::
precision_checker
>
(
specs_
.
precision
,
specs_
.
precision_ref
,
ctx
,
format_str_
);
specs_
.
precision
,
specs_
.
precision_ref
,
ctx
);
using
range_type
=
internal
::
output_range
<
typename
FormatContext
::
iterator
,
typename
FormatContext
::
char_type
>
;
...
...
@@ -3022,7 +3016,6 @@ struct formatter<T, Char,
private:
internal
::
dynamic_format_specs
<
Char
>
specs_
;
const
Char
*
format_str_
;
};
#define FMT_FORMAT_AS(Type, Base) \
...
...
@@ -3040,7 +3033,6 @@ FMT_FORMAT_AS(short, int);
FMT_FORMAT_AS
(
unsigned
short
,
unsigned
);
FMT_FORMAT_AS
(
long
,
long
long
);
FMT_FORMAT_AS
(
unsigned
long
,
unsigned
long
long
);
FMT_FORMAT_AS
(
float
,
double
);
FMT_FORMAT_AS
(
Char
*
,
const
Char
*
);
FMT_FORMAT_AS
(
std
::
basic_string
<
Char
>
,
basic_string_view
<
Char
>
);
FMT_FORMAT_AS
(
std
::
nullptr_t
,
const
void
*
);
...
...
@@ -3123,9 +3115,9 @@ template <typename Char = char> class dynamic_formatter {
private:
template
<
typename
Context
>
void
handle_specs
(
Context
&
ctx
)
{
internal
::
handle_dynamic_spec
<
internal
::
width_checker
>
(
specs_
.
width
,
specs_
.
width_ref
,
ctx
,
format_str_
);
specs_
.
width
,
specs_
.
width_ref
,
ctx
);
internal
::
handle_dynamic_spec
<
internal
::
precision_checker
>
(
specs_
.
precision
,
specs_
.
precision_ref
,
ctx
,
format_str_
);
specs_
.
precision
,
specs_
.
precision_ref
,
ctx
);
}
internal
::
dynamic_format_specs
<
Char
>
specs_
;
...
...
@@ -3142,8 +3134,8 @@ basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
}
template
<
typename
Char
,
typename
ErrorHandler
>
FMT_CONSTEXPR
void
advance_to
(
basic_parse_context
<
Char
,
ErrorHandler
>&
ctx
,
const
Char
*
p
)
{
FMT_CONSTEXPR
void
advance_to
(
basic_format_parse_context
<
Char
,
ErrorHandler
>&
ctx
,
const
Char
*
p
)
{
ctx
.
advance_to
(
ctx
.
begin
()
+
(
p
-
&*
ctx
.
begin
()));
}
...
...
@@ -3175,10 +3167,8 @@ struct format_handler : internal::error_handler {
void
on_replacement_field
(
const
Char
*
p
)
{
advance_to
(
parse_context
,
p
);
internal
::
custom_formatter
<
Context
>
f
(
parse_context
,
context
);
if
(
!
visit_format_arg
(
f
,
arg
))
context
.
advance_to
(
visit_format_arg
(
ArgFormatter
(
context
,
&
parse_context
),
arg
));
context
.
advance_to
(
visit_format_arg
(
ArgFormatter
(
context
,
&
parse_context
),
arg
));
}
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
end
)
{
...
...
@@ -3187,7 +3177,7 @@ struct format_handler : internal::error_handler {
if
(
visit_format_arg
(
f
,
arg
))
return
parse_context
.
begin
();
basic_format_specs
<
Char
>
specs
;
using
internal
::
specs_handler
;
using
parse_context_t
=
basic_parse_context
<
Char
>
;
using
parse_context_t
=
basic_
format_
parse_context
<
Char
>
;
internal
::
specs_checker
<
specs_handler
<
parse_context_t
,
Context
>>
handler
(
specs_handler
<
parse_context_t
,
Context
>
(
specs
,
parse_context
,
context
),
arg
.
type
());
...
...
@@ -3199,7 +3189,7 @@ struct format_handler : internal::error_handler {
return
begin
;
}
basic_parse_context
<
Char
>
parse_context
;
basic_
format_
parse_context
<
Char
>
parse_context
;
Context
context
;
basic_format_arg
<
Context
>
arg
;
};
...
...
@@ -3396,7 +3386,7 @@ template <typename OutputIt> struct format_to_n_result {
template
<
typename
OutputIt
,
typename
Char
=
typename
OutputIt
::
value_type
>
using
format_to_n_context
=
format_context_t
<
fmt
::
internal
::
truncating_iterator
<
OutputIt
>
,
Char
>
;
format_context_t
<
internal
::
truncating_iterator
<
OutputIt
>
,
Char
>
;
template
<
typename
OutputIt
,
typename
Char
=
typename
OutputIt
::
value_type
>
using
format_to_n_args
=
basic_format_args
<
format_to_n_context
<
OutputIt
,
Char
>>
;
...
...
@@ -3443,7 +3433,7 @@ inline std::basic_string<Char> internal::vformat(
basic_format_args
<
buffer_context
<
Char
>>
args
)
{
basic_memory_buffer
<
Char
>
buffer
;
internal
::
vformat_to
(
buffer
,
format_str
,
args
);
return
fmt
::
to_string
(
buffer
);
return
to_string
(
buffer
);
}
/**
...
...
@@ -3452,8 +3442,22 @@ inline std::basic_string<Char> internal::vformat(
*/
template
<
typename
...
Args
>
inline
std
::
size_t
formatted_size
(
string_view
format_str
,
const
Args
&
...
args
)
{
auto
it
=
format_to
(
internal
::
counting_iterator
<
char
>
(),
format_str
,
args
...);
return
it
.
count
();
return
format_to
(
internal
::
counting_iterator
(),
format_str
,
args
...).
count
();
}
template
<
typename
Char
,
FMT_ENABLE_IF
(
std
::
is_same
<
Char
,
wchar_t
>
::
value
)
>
void
vprint
(
std
::
FILE
*
f
,
basic_string_view
<
Char
>
format_str
,
wformat_args
args
)
{
wmemory_buffer
buffer
;
internal
::
vformat_to
(
buffer
,
format_str
,
args
);
buffer
.
push_back
(
L'\0'
);
if
(
std
::
fputws
(
buffer
.
data
(),
f
)
==
-
1
)
FMT_THROW
(
system_error
(
errno
,
"cannot write to file"
));
}
template
<
typename
Char
,
FMT_ENABLE_IF
(
std
::
is_same
<
Char
,
wchar_t
>
::
value
)
>
void
vprint
(
basic_string_view
<
Char
>
format_str
,
wformat_args
args
)
{
vprint
(
stdout
,
format_str
,
args
);
}
#if FMT_USE_USER_DEFINED_LITERALS
...
...
@@ -3466,7 +3470,7 @@ template <typename Char, Char... CHARS> class udl_formatter {
std
::
basic_string
<
Char
>
operator
()(
Args
&&
...
args
)
const
{
FMT_CONSTEXPR_DECL
Char
s
[]
=
{
CHARS
...,
'\0'
};
FMT_CONSTEXPR_DECL
bool
invalid_format
=
do_check_format_string
<
Char
,
error_handler
,
Args
...
>
(
do_check_format_string
<
Char
,
error_handler
,
remove_cvref_t
<
Args
>
...
>
(
basic_string_view
<
Char
>
(
s
,
sizeof
...(
CHARS
)));
(
void
)
invalid_format
;
return
format
(
s
,
std
::
forward
<
Args
>
(
args
)...);
...
...
@@ -3547,6 +3551,22 @@ FMT_CONSTEXPR internal::udl_arg<wchar_t> operator"" _a(const wchar_t* s,
#endif // FMT_USE_USER_DEFINED_LITERALS
FMT_END_NAMESPACE
#define FMT_STRING_IMPL(s, ...) \
[] { \
struct str : fmt::compile_string { \
using char_type = typename std::remove_cv<std::remove_pointer< \
typename std::decay<decltype(s)>::type>::type>::type; \
__VA_ARGS__ FMT_CONSTEXPR \
operator fmt::basic_string_view<char_type>() const { \
return {s, sizeof(s) / sizeof(char_type) - 1}; \
} \
} result; \
/* Suppress Qt Creator warning about unused operator. */
\
(void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
result); \
return result; \
}()
/**
\rst
Constructs a compile-time format string.
...
...
@@ -3557,37 +3577,10 @@ FMT_END_NAMESPACE
std::string s = format(FMT_STRING("{:d}"), "foo");
\endrst
*/
#define FMT_STRING(s) \
[] { \
struct str : fmt::compile_string { \
using char_type = typename std::remove_cv<std::remove_pointer< \
typename std::decay<decltype(s)>::type>::type>::type; \
FMT_CONSTEXPR operator fmt::basic_string_view<char_type>() const { \
return {s, sizeof(s) / sizeof(char_type) - 1}; \
} \
} result; \
/* Suppress Qt Creator warning about unused operator. */
\
(void)static_cast<fmt::basic_string_view<typename str::char_type>>( \
result); \
return result; \
}()
#define FMT_STRING(s) FMT_STRING_IMPL(s, )
#if defined(FMT_STRING_ALIAS) && FMT_STRING_ALIAS
/**
\rst
Constructs a compile-time format string. This macro is disabled by default to
prevent potential name collisions. To enable it define ``FMT_STRING_ALIAS`` to
1 before including ``fmt/format.h``.
**Example**::
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
// A compile-time error because 'd' is an invalid specifier for strings.
std::string s = format(fmt("{:d}"), "foo");
\endrst
*/
# define fmt(s) FMT_STRING(s)
# define fmt(s) FMT_STRING_IMPL(s, [[deprecated]])
#endif
#ifdef FMT_HEADER_ONLY
...
...
include/spdlog/fmt/bundled/ostream.h
View file @
0db4b04a
...
...
@@ -46,9 +46,13 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
template
<
typename
Char
>
struct
test_stream
:
std
::
basic_ostream
<
Char
>
{
private:
struct
null
;
// Hide all operator<< from std::basic_ostream<Char>.
void
operator
<<
(
null
);
void_t
<>
operator
<<
(
null
<>
);
void_t
<>
operator
<<
(
const
Char
*
);
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_convertible
<
T
,
int
>
::
value
&&
!
std
::
is_enum
<
T
>::
value
)
>
void_t
<>
operator
<<
(
T
);
};
// Checks if T has a user-defined operator<< (e.g. not a member of
...
...
@@ -56,9 +60,9 @@ template <typename Char> struct test_stream : std::basic_ostream<Char> {
template
<
typename
T
,
typename
Char
>
class
is_streamable
{
private:
template
<
typename
U
>
static
decltype
((
void
)
(
std
::
declval
<
test_stream
<
Char
>&>
()
<<
std
::
declval
<
U
>
()),
std
::
true_type
())
static
bool_constant
<!
std
::
is_same
<
decltype
(
std
::
declval
<
test_stream
<
Char
>&>
()
<<
std
::
declval
<
U
>
()),
void_t
<>>::
value
>
test
(
int
);
template
<
typename
>
static
std
::
false_type
test
(...);
...
...
@@ -75,8 +79,7 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const
Char
*
buf_data
=
buf
.
data
();
using
unsigned_streamsize
=
std
::
make_unsigned
<
std
::
streamsize
>::
type
;
unsigned_streamsize
size
=
buf
.
size
();
unsigned_streamsize
max_size
=
to_unsigned
((
std
::
numeric_limits
<
std
::
streamsize
>::
max
)());
unsigned_streamsize
max_size
=
to_unsigned
(
max_value
<
std
::
streamsize
>
());
do
{
unsigned_streamsize
n
=
size
<=
max_size
?
size
:
max_size
;
os
.
write
(
buf_data
,
static_cast
<
std
::
streamsize
>
(
n
));
...
...
@@ -86,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template
<
typename
Char
,
typename
T
>
void
format_value
(
buffer
<
Char
>&
buf
,
const
T
&
value
)
{
void
format_value
(
buffer
<
Char
>&
buf
,
const
T
&
value
,
locale_ref
loc
=
locale_ref
())
{
formatbuf
<
Char
>
format_buf
(
buf
);
std
::
basic_ostream
<
Char
>
output
(
&
format_buf
);
if
(
loc
)
output
.
imbue
(
loc
.
get
<
std
::
locale
>
());
output
.
exceptions
(
std
::
ios_base
::
failbit
|
std
::
ios_base
::
badbit
);
output
<<
value
;
buf
.
resize
(
buf
.
size
());
...
...
@@ -101,7 +106,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template
<
typename
Context
>
auto
format
(
const
T
&
value
,
Context
&
ctx
)
->
decltype
(
ctx
.
out
())
{
basic_memory_buffer
<
Char
>
buffer
;
format_value
(
buffer
,
value
);
format_value
(
buffer
,
value
,
ctx
.
locale
()
);
basic_string_view
<
Char
>
str
(
buffer
.
data
(),
buffer
.
size
());
return
formatter
<
basic_string_view
<
Char
>
,
Char
>::
format
(
str
,
ctx
);
}
...
...
include/spdlog/fmt/bundled/posix.h
View file @
0db4b04a
...
...
@@ -13,11 +13,10 @@
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
...
...
@@ -27,6 +26,18 @@
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
...
...
@@ -54,8 +65,8 @@
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression);
\
} while (
result == error_result
&& errno == EINTR)
(result) = (expression);
\
} while (
(result) == (error_result)
&& errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
...
...
@@ -132,16 +143,15 @@ class buffered_file {
explicit
buffered_file
(
FILE
*
f
)
:
file_
(
f
)
{}
public:
buffered_file
(
const
buffered_file
&
)
=
delete
;
void
operator
=
(
const
buffered_file
&
)
=
delete
;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file
()
FMT_NOEXCEPT
:
file_
(
nullptr
)
{}
// Destroys the object closing the file it represents if any.
FMT_API
~
buffered_file
()
FMT_NOEXCEPT
;
private:
buffered_file
(
const
buffered_file
&
)
=
delete
;
void
operator
=
(
const
buffered_file
&
)
=
delete
;
public:
buffered_file
(
buffered_file
&&
other
)
FMT_NOEXCEPT
:
file_
(
other
.
file_
)
{
other
.
file_
=
nullptr
;
...
...
@@ -177,6 +187,7 @@ class buffered_file {
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
...
...
@@ -204,14 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API
file
(
cstring_view
path
,
int
oflag
);
p
rivate
:
p
ublic
:
file
(
const
file
&
)
=
delete
;
void
operator
=
(
const
file
&
)
=
delete
;
public:
file
(
file
&&
other
)
FMT_NOEXCEPT
:
fd_
(
other
.
fd_
)
{
other
.
fd_
=
-
1
;
}
file
&
operator
=
(
file
&&
other
)
{
file
&
operator
=
(
file
&&
other
)
FMT_NOEXCEPT
{
close
();
fd_
=
other
.
fd_
;
other
.
fd_
=
-
1
;
...
...
@@ -260,6 +270,7 @@ class file {
// Returns the memory page size.
long
getpagesize
();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
...
...
@@ -283,11 +294,10 @@ class Locale {
locale_t
locale_
;
Locale
(
const
Locale
&
)
=
delete
;
void
operator
=
(
const
Locale
&
)
=
delete
;
public:
using
type
=
locale_t
;
Locale
(
const
Locale
&
)
=
delete
;
void
operator
=
(
const
Locale
&
)
=
delete
;
Locale
()
:
locale_
(
newlocale
(
LC_NUMERIC_MASK
,
"C"
,
nullptr
))
{
if
(
!
locale_
)
FMT_THROW
(
system_error
(
errno
,
"cannot create locale"
));
...
...
include/spdlog/fmt/bundled/printf.h
View file @
0db4b04a
// Formatting library for C++
// Formatting library for C++
- legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
...
...
@@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::
fill_n
#include <algorithm> // std::
max
#include <limits> // std::numeric_limits
#include "ostream.h"
...
...
@@ -16,15 +16,11 @@
FMT_BEGIN_NAMESPACE
namespace
internal
{
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template
<
typename
T
>
inline
T
const_check
(
T
value
)
{
return
value
;
}
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template
<
bool
IsSigned
>
struct
int_checker
{
template
<
typename
T
>
static
bool
fits_in_int
(
T
value
)
{
unsigned
max
=
std
::
numeric_limits
<
int
>::
max
();
unsigned
max
=
max_value
<
int
>
();
return
value
<=
max
;
}
static
bool
fits_in_int
(
bool
)
{
return
true
;
}
...
...
@@ -33,7 +29,7 @@ template <bool IsSigned> struct int_checker {
template
<
>
struct
int_checker
<
true
>
{
template
<
typename
T
>
static
bool
fits_in_int
(
T
value
)
{
return
value
>=
std
::
numeric_limits
<
int
>::
min
()
&&
value
<=
std
::
numeric_limits
<
int
>::
max
();
value
<=
max_value
<
int
>
();
}
static
bool
fits_in_int
(
int
)
{
return
true
;
}
};
...
...
@@ -158,12 +154,12 @@ template <typename Char> class printf_width_handler {
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
unsigned
operator
()(
T
value
)
{
auto
width
=
static_cast
<
uint32_or_64_t
<
T
>>
(
value
);
auto
width
=
static_cast
<
uint32_or_64_
or_128_
t
<
T
>>
(
value
);
if
(
internal
::
is_negative
(
value
))
{
specs_
.
align
=
align
::
left
;
width
=
0
-
width
;
}
unsigned
int_max
=
std
::
numeric_limits
<
int
>::
max
();
unsigned
int_max
=
max_value
<
int
>
();
if
(
width
>
int_max
)
FMT_THROW
(
format_error
(
"number is too big"
));
return
static_cast
<
unsigned
>
(
width
);
}
...
...
@@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
printf_arg_formatter
(
iterator
iter
,
format_specs
&
specs
,
context_type
&
ctx
)
:
base
(
Range
(
iter
),
&
specs
,
internal
::
locale_ref
()),
context_
(
ctx
)
{}
template
<
typename
T
,
FMT_ENABLE_IF
(
std
::
is_integral
<
T
>
::
value
)
>
template
<
typename
T
,
FMT_ENABLE_IF
(
fmt
::
internal
::
is_integral
<
T
>
::
value
)
>
iterator
operator
()(
T
value
)
{
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
...
...
@@ -332,14 +328,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt
out_
;
basic_format_args
<
basic_printf_context
>
args_
;
basic_parse_context
<
Char
>
parse_ctx_
;
basic_
format_
parse_context
<
Char
>
parse_ctx_
;
static
void
parse_flags
(
format_specs
&
specs
,
const
Char
*&
it
,
const
Char
*
end
);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg
get_arg
(
unsigned
arg_index
=
std
::
numeric_limits
<
unsigned
>::
max
());
format_arg
get_arg
(
unsigned
arg_index
=
internal
::
max_value
<
unsigned
>
());
// Parses argument index, flags and width and returns the argument index.
unsigned
parse_header
(
const
Char
*&
it
,
const
Char
*
end
,
format_specs
&
specs
);
...
...
@@ -361,15 +357,14 @@ template <typename OutputIt, typename Char> class basic_printf_context {
format_arg
arg
(
unsigned
id
)
const
{
return
args_
.
get
(
id
);
}
basic_parse_context
<
Char
>&
parse_context
()
{
return
parse_ctx_
;
}
basic_
format_
parse_context
<
Char
>&
parse_context
()
{
return
parse_ctx_
;
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
message
)
{
parse_ctx_
.
on_error
(
message
);
}
/** Formats stored arguments and writes the output to the range. */
template
<
typename
ArgFormatter
=
printf_arg_formatter
<
internal
::
buffer_range
<
Char
>
>>
template
<
typename
ArgFormatter
=
printf_arg_formatter
<
buffer_range
<
Char
>
>>
OutputIt
format
();
};
...
...
@@ -403,7 +398,7 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template
<
typename
OutputIt
,
typename
Char
>
typename
basic_printf_context
<
OutputIt
,
Char
>::
format_arg
basic_printf_context
<
OutputIt
,
Char
>::
get_arg
(
unsigned
arg_index
)
{
if
(
arg_index
==
std
::
numeric_limits
<
unsigned
>::
max
())
if
(
arg_index
==
internal
::
max_value
<
unsigned
>
())
arg_index
=
parse_ctx_
.
next_arg_id
();
else
parse_ctx_
.
check_arg_id
(
--
arg_index
);
...
...
@@ -413,7 +408,7 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
template
<
typename
OutputIt
,
typename
Char
>
unsigned
basic_printf_context
<
OutputIt
,
Char
>::
parse_header
(
const
Char
*&
it
,
const
Char
*
end
,
format_specs
&
specs
)
{
unsigned
arg_index
=
std
::
numeric_limits
<
unsigned
>::
max
();
unsigned
arg_index
=
internal
::
max_value
<
unsigned
>
();
char_type
c
=
*
it
;
if
(
c
>=
'0'
&&
c
<=
'9'
)
{
// Parse an argument index (if followed by '$') or a width possibly
...
...
@@ -470,6 +465,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width.
unsigned
arg_index
=
parse_header
(
it
,
end
,
specs
);
if
(
arg_index
==
0
)
on_error
(
"argument index out of range"
);
// Parse precision.
if
(
it
!=
end
&&
*
it
==
'.'
)
{
...
...
include/spdlog/fmt/bundled/ranges.h
View file @
0db4b04a
...
...
@@ -246,7 +246,8 @@ template <typename T, typename Char> struct is_range {
static
FMT_CONSTEXPR_DECL
const
bool
value
=
internal
::
is_range_
<
T
>::
value
&&
!
internal
::
is_like_std_string
<
T
>::
value
&&
!
std
::
is_convertible
<
T
,
std
::
basic_string
<
Char
>>::
value
;
!
std
::
is_convertible
<
T
,
std
::
basic_string
<
Char
>>::
value
&&
!
std
::
is_constructible
<
internal
::
std_string_view
<
Char
>
,
T
>::
value
;
};
template
<
typename
RangeT
,
typename
Char
>
...
...
@@ -283,6 +284,82 @@ struct formatter<RangeT, Char,
}
};
template
<
typename
Char
,
typename
...
T
>
struct
tuple_arg_join
:
internal
::
view
{
const
std
::
tuple
<
T
...
>&
tuple
;
basic_string_view
<
Char
>
sep
;
tuple_arg_join
(
const
std
::
tuple
<
T
...
>&
t
,
basic_string_view
<
Char
>
s
)
:
tuple
{
t
},
sep
{
s
}
{}
};
template
<
typename
Char
,
typename
...
T
>
struct
formatter
<
tuple_arg_join
<
Char
,
T
...
>
,
Char
>
{
template
<
typename
ParseContext
>
FMT_CONSTEXPR
auto
parse
(
ParseContext
&
ctx
)
->
decltype
(
ctx
.
begin
())
{
return
ctx
.
begin
();
}
template
<
typename
FormatContext
>
typename
FormatContext
::
iterator
format
(
const
tuple_arg_join
<
Char
,
T
...
>&
value
,
FormatContext
&
ctx
)
{
return
format
(
value
,
ctx
,
internal
::
make_index_sequence
<
sizeof
...(
T
)
>
{});
}
private:
template
<
typename
FormatContext
,
size_t
...
N
>
typename
FormatContext
::
iterator
format
(
const
tuple_arg_join
<
Char
,
T
...
>&
value
,
FormatContext
&
ctx
,
internal
::
index_sequence
<
N
...
>
)
{
return
format_args
(
value
,
ctx
,
std
::
get
<
N
>
(
value
.
tuple
)...);
}
template
<
typename
FormatContext
>
typename
FormatContext
::
iterator
format_args
(
const
tuple_arg_join
<
Char
,
T
...
>&
,
FormatContext
&
ctx
)
{
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return
ctx
.
out
();
}
template
<
typename
FormatContext
,
typename
Arg
,
typename
...
Args
>
typename
FormatContext
::
iterator
format_args
(
const
tuple_arg_join
<
Char
,
T
...
>&
value
,
FormatContext
&
ctx
,
const
Arg
&
arg
,
const
Args
&
...
args
)
{
using
base
=
formatter
<
typename
std
::
decay
<
Arg
>::
type
,
Char
>
;
auto
out
=
ctx
.
out
();
out
=
base
{}.
format
(
arg
,
ctx
);
if
(
sizeof
...(
Args
)
>
0
)
{
out
=
std
::
copy
(
value
.
sep
.
begin
(),
value
.
sep
.
end
(),
out
);
ctx
.
advance_to
(
out
);
return
format_args
(
value
,
ctx
,
args
...);
}
return
out
;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template
<
typename
...
T
>
FMT_CONSTEXPR
tuple_arg_join
<
char
,
T
...
>
join
(
const
std
::
tuple
<
T
...
>&
tuple
,
string_view
sep
)
{
return
{
tuple
,
sep
};
}
template
<
typename
...
T
>
FMT_CONSTEXPR
tuple_arg_join
<
wchar_t
,
T
...
>
join
(
const
std
::
tuple
<
T
...
>&
tuple
,
wstring_view
sep
)
{
return
{
tuple
,
sep
};
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
include/spdlog/fmt/bundled/safe-duration-cast.h
deleted
100644 → 0
View file @
1aa9ea92
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace
safe_duration_cast
{
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
!
std
::
is_same
<
From
,
To
>
::
value
&&
std
::
numeric_limits
<
From
>::
is_signed
==
std
::
numeric_limits
<
To
>::
is_signed
)
>
FMT_CONSTEXPR
To
lossless_integral_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
using
F
=
std
::
numeric_limits
<
From
>
;
using
T
=
std
::
numeric_limits
<
To
>
;
static_assert
(
F
::
is_integer
,
"From must be integral"
);
static_assert
(
T
::
is_integer
,
"To must be integral"
);
// A and B are both signed, or both unsigned.
if
(
F
::
digits
<=
T
::
digits
)
{
// From fits in To without any problem.
}
else
{
// From does not always fit in To, resort to a dynamic check.
if
(
from
<
T
::
min
()
||
from
>
T
::
max
())
{
// outside range.
ec
=
1
;
return
{};
}
}
return
static_cast
<
To
>
(
from
);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
!
std
::
is_same
<
From
,
To
>
::
value
&&
std
::
numeric_limits
<
From
>::
is_signed
!=
std
::
numeric_limits
<
To
>::
is_signed
)
>
FMT_CONSTEXPR
To
lossless_integral_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
using
F
=
std
::
numeric_limits
<
From
>
;
using
T
=
std
::
numeric_limits
<
To
>
;
static_assert
(
F
::
is_integer
,
"From must be integral"
);
static_assert
(
T
::
is_integer
,
"To must be integral"
);
if
(
F
::
is_signed
&&
!
T
::
is_signed
)
{
// From may be negative, not allowed!
if
(
from
<
0
)
{
ec
=
1
;
return
{};
}
// From is positive. Can it always fit in To?
if
(
F
::
digits
<=
T
::
digits
)
{
// yes, From always fits in To.
}
else
{
// from may not fit in To, we have to do a dynamic check
if
(
from
>
static_cast
<
From
>
(
T
::
max
()))
{
ec
=
1
;
return
{};
}
}
}
if
(
!
F
::
is_signed
&&
T
::
is_signed
)
{
// can from be held in To?
if
(
F
::
digits
<
T
::
digits
)
{
// yes, From always fits in To.
}
else
{
// from may not fit in To, we have to do a dynamic check
if
(
from
>
static_cast
<
From
>
(
T
::
max
()))
{
// outside range.
ec
=
1
;
return
{};
}
}
}
// reaching here means all is ok for lossless conversion.
return
static_cast
<
To
>
(
from
);
}
// function
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
std
::
is_same
<
From
,
To
>
::
value
)
>
FMT_CONSTEXPR
To
lossless_integral_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
return
from
;
}
// function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// clang-format on
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
!
std
::
is_same
<
From
,
To
>
::
value
)
>
FMT_CONSTEXPR
To
safe_float_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
using
T
=
std
::
numeric_limits
<
To
>
;
static_assert
(
std
::
is_floating_point
<
From
>::
value
,
"From must be floating"
);
static_assert
(
std
::
is_floating_point
<
To
>::
value
,
"To must be floating"
);
// catch the only happy case
if
(
std
::
isfinite
(
from
))
{
if
(
from
>=
T
::
lowest
()
&&
from
<=
T
::
max
())
{
return
static_cast
<
To
>
(
from
);
}
// not within range.
ec
=
1
;
return
{};
}
// nan and inf will be preserved
return
static_cast
<
To
>
(
from
);
}
// function
template
<
typename
To
,
typename
From
,
FMT_ENABLE_IF
(
std
::
is_same
<
From
,
To
>
::
value
)
>
FMT_CONSTEXPR
To
safe_float_conversion
(
const
From
from
,
int
&
ec
)
{
ec
=
0
;
static_assert
(
std
::
is_floating_point
<
From
>::
value
,
"From must be floating"
);
return
from
;
}
/**
* safe duration cast between integral durations
*/
template
<
typename
To
,
typename
FromRep
,
typename
FromPeriod
,
FMT_ENABLE_IF
(
std
::
is_integral
<
FromRep
>
::
value
),
FMT_ENABLE_IF
(
std
::
is_integral
<
typename
To
::
rep
>::
value
)
>
To
safe_duration_cast
(
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
from
,
int
&
ec
)
{
using
From
=
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
;
ec
=
0
;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using
Factor
=
std
::
ratio_divide
<
typename
From
::
period
,
typename
To
::
period
>
;
static_assert
(
Factor
::
num
>
0
,
"num must be positive"
);
static_assert
(
Factor
::
den
>
0
,
"den must be positive"
);
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using
IntermediateRep
=
typename
std
::
common_type
<
typename
From
::
rep
,
typename
To
::
rep
,
decltype
(
Factor
::
num
)
>::
type
;
// safe conversion to IntermediateRep
IntermediateRep
count
=
lossless_integral_conversion
<
IntermediateRep
>
(
from
.
count
(),
ec
);
if
(
ec
)
{
return
{};
}
// multiply with Factor::num without overflow or underflow
if
(
Factor
::
num
!=
1
)
{
constexpr
auto
max1
=
std
::
numeric_limits
<
IntermediateRep
>::
max
()
/
Factor
::
num
;
if
(
count
>
max1
)
{
ec
=
1
;
return
{};
}
constexpr
auto
min1
=
std
::
numeric_limits
<
IntermediateRep
>::
min
()
/
Factor
::
num
;
if
(
count
<
min1
)
{
ec
=
1
;
return
{};
}
count
*=
Factor
::
num
;
}
// this can't go wrong, right? den>0 is checked earlier.
if
(
Factor
::
den
!=
1
)
{
count
/=
Factor
::
den
;
}
// convert to the to type, safely
using
ToRep
=
typename
To
::
rep
;
const
ToRep
tocount
=
lossless_integral_conversion
<
ToRep
>
(
count
,
ec
);
if
(
ec
)
{
return
{};
}
return
To
{
tocount
};
}
/**
* safe duration_cast between floating point durations
*/
template
<
typename
To
,
typename
FromRep
,
typename
FromPeriod
,
FMT_ENABLE_IF
(
std
::
is_floating_point
<
FromRep
>
::
value
),
FMT_ENABLE_IF
(
std
::
is_floating_point
<
typename
To
::
rep
>::
value
)
>
To
safe_duration_cast
(
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
from
,
int
&
ec
)
{
using
From
=
std
::
chrono
::
duration
<
FromRep
,
FromPeriod
>
;
ec
=
0
;
if
(
std
::
isnan
(
from
.
count
()))
{
// nan in, gives nan out. easy.
return
To
{
std
::
numeric_limits
<
typename
To
::
rep
>::
quiet_NaN
()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if
(
std
::
isinf
(
from
.
count
()))
{
return
To
{
from
.
count
()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
using
Factor
=
std
::
ratio_divide
<
typename
From
::
period
,
typename
To
::
period
>
;
static_assert
(
Factor
::
num
>
0
,
"num must be positive"
);
static_assert
(
Factor
::
den
>
0
,
"den must be positive"
);
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using
IntermediateRep
=
typename
std
::
common_type
<
typename
From
::
rep
,
typename
To
::
rep
,
decltype
(
Factor
::
num
)
>::
type
;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep
count
=
safe_float_conversion
<
IntermediateRep
>
(
from
.
count
(),
ec
);
if
(
ec
)
{
return
{};
}
// multiply with Factor::num without overflow or underflow
if
(
Factor
::
num
!=
1
)
{
constexpr
auto
max1
=
std
::
numeric_limits
<
IntermediateRep
>::
max
()
/
static_cast
<
IntermediateRep
>
(
Factor
::
num
);
if
(
count
>
max1
)
{
ec
=
1
;
return
{};
}
constexpr
auto
min1
=
std
::
numeric_limits
<
IntermediateRep
>::
lowest
()
/
static_cast
<
IntermediateRep
>
(
Factor
::
num
);
if
(
count
<
min1
)
{
ec
=
1
;
return
{};
}
count
*=
static_cast
<
IntermediateRep
>
(
Factor
::
num
);
}
// this can't go wrong, right? den>0 is checked earlier.
if
(
Factor
::
den
!=
1
)
{
using
common_t
=
typename
std
::
common_type
<
IntermediateRep
,
intmax_t
>::
type
;
count
/=
static_cast
<
common_t
>
(
Factor
::
den
);
}
// convert to the to type, safely
using
ToRep
=
typename
To
::
rep
;
const
ToRep
tocount
=
safe_float_conversion
<
ToRep
>
(
count
,
ec
);
if
(
ec
)
{
return
{};
}
return
To
{
tocount
};
}
}
// namespace safe_duration_cast
FMT_END_NAMESPACE
src/fmt.cpp
View file @
0db4b04a
...
...
@@ -9,27 +9,60 @@
#if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/bundled/format-inl.h"
FMT_BEGIN_NAMESPACE
template
struct
internal
::
basic_data
<
void
>;
template
FMT_API
internal
::
locale_ref
::
locale_ref
(
const
std
::
locale
&
loc
);
template
struct
FMT_API
internal
::
basic_data
<
void
>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int
(
*
instantiate_format_float
)(
double
,
int
,
internal
::
float_specs
,
internal
::
buffer
<
char
>&
)
=
internal
::
format_float
;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template
FMT_API
internal
::
locale_ref
::
locale_ref
(
const
std
::
locale
&
loc
);
template
FMT_API
std
::
locale
internal
::
locale_ref
::
get
<
std
::
locale
>()
const
;
#endif
// Explicit instantiations for char.
template
FMT_API
std
::
string
internal
::
grouping_impl
<
char
>(
locale_ref
);
template
FMT_API
char
internal
::
thousands_sep_impl
(
locale_ref
);
template
FMT_API
char
internal
::
decimal_point_impl
(
locale_ref
);
template
FMT_API
void
internal
::
buffer
<
char
>
::
append
(
const
char
*
,
const
char
*
);
template
FMT_API
void
internal
::
arg_map
<
format_context
>
::
init
(
const
basic_format_args
<
format_context
>
&
args
);
template
FMT_API
std
::
string
internal
::
vformat
<
char
>(
string_view
,
basic_format_args
<
format_context
>);
template
FMT_API
format_context
::
iterator
internal
::
vformat_to
(
internal
::
buffer
<
char
>
&
,
string_view
,
basic_format_args
<
format_context
>);
template
FMT_API
char
*
internal
::
sprintf_format
(
double
,
internal
::
buffer
<
char
>
&
,
sprintf_specs
);
template
FMT_API
char
*
internal
::
sprintf_format
(
long
double
,
internal
::
buffer
<
char
>
&
,
sprintf_specs
);
template
FMT_API
void
internal
::
buffer
<
char
>
::
append
(
const
char
*
,
const
char
*
);
template
FMT_API
void
internal
::
arg_map
<
format_context
>
::
init
(
const
basic_format_args
<
format_context
>
&
args
);
template
FMT_API
std
::
string
internal
::
vformat
<
char
>(
string_view
,
basic_format_args
<
format_context
>);
template
FMT_API
format_context
::
iterator
internal
::
vformat_to
(
internal
::
buffer
<
char
>
&
,
string_view
,
basic_format_args
<
format_context
>);
template
FMT_API
int
internal
::
snprintf_float
(
double
,
int
,
internal
::
float_specs
,
internal
::
buffer
<
char
>
&
);
template
FMT_API
int
internal
::
snprintf_float
(
long
double
,
int
,
internal
::
float_specs
,
internal
::
buffer
<
char
>
&
);
template
FMT_API
int
internal
::
format_float
(
double
,
int
,
internal
::
float_specs
,
internal
::
buffer
<
char
>
&
);
template
FMT_API
int
internal
::
format_float
(
long
double
,
int
,
internal
::
float_specs
,
internal
::
buffer
<
char
>
&
);
// Explicit instantiations for wchar_t.
template
FMT_API
std
::
string
internal
::
grouping_impl
<
wchar_t
>(
locale_ref
);
template
FMT_API
wchar_t
internal
::
thousands_sep_impl
(
locale_ref
);
template
FMT_API
wchar_t
internal
::
decimal_point_impl
(
locale_ref
);
template
FMT_API
void
internal
::
buffer
<
wchar_t
>
::
append
(
const
wchar_t
*
,
const
wchar_t
*
);
template
FMT_API
void
internal
::
arg_map
<
wformat_context
>
::
init
(
const
basic_format_args
<
wformat_context
>
&
);
template
FMT_API
std
::
wstring
internal
::
vformat
<
wchar_t
>(
wstring_view
,
basic_format_args
<
wformat_context
>);
template
FMT_API
void
internal
::
buffer
<
wchar_t
>
::
append
(
const
wchar_t
*
,
const
wchar_t
*
);
template
FMT_API
std
::
wstring
internal
::
vformat
<
wchar_t
>(
wstring_view
,
basic_format_args
<
wformat_context
>);
FMT_END_NAMESPACE
#endif
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