Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
7cad3356
Commit
7cad3356
authored
Sep 01, 2019
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor format string compilation
parent
e1ab6bc0
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
115 additions
and
127 deletions
+115
-127
include/fmt/compile.h
include/fmt/compile.h
+115
-127
No files found.
include/fmt/compile.h
View file @
7cad3356
...
...
@@ -14,117 +14,156 @@
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
{
struct
named_arg_id
{
FMT_CONSTEXPR
named_arg_id
(
string_view_metadata
id
)
:
id
(
id
)
{}
string_view_metadata
id
;
};
enum
class
kind
{
arg_index
,
arg_name
,
text
,
replacement
};
struct
replacement
{
FMT_CONSTEXPR
replacement
()
:
arg_id
(
0u
)
{}
FMT_CONSTEXPR
replacement
(
unsigned
id
)
:
arg_id
(
id
)
{}
FMT_CONSTEXPR
replacement
(
string_view_metadata
id
)
:
arg_id
(
id
)
{}
arg_ref
<
Char
>
arg_id
;
dynamic_format_specs
<
Char
>
parsed_
specs
;
dynamic_format_specs
<
Char
>
specs
;
};
enum
class
kind
{
arg_index
,
arg_name
,
text
,
replacement
};
kind
part_kind
;
std
::
size_t
end_of_argument_id
;
union
value
{
FMT_CONSTEXPR
value
()
:
arg_index
(
0u
)
{}
FMT_CONSTEXPR
value
(
unsigned
id
)
:
arg_index
(
id
)
{}
FMT_CONSTEXPR
value
(
named_arg_id
named_id
)
:
arg_name
(
named_id
.
id
)
{}
FMT_CONSTEXPR
value
(
string_view_metadata
t
)
:
text
(
t
)
{}
FMT_CONSTEXPR
value
(
replacement
r
)
:
repl
(
r
)
{}
unsigned
arg_index
;
string_view_metadata
arg_name
;
string_view_metadata
text
;
string_view_metadata
str
;
replacement
repl
;
FMT_CONSTEXPR
value
(
unsigned
index
=
0
)
:
arg_index
(
index
)
{}
FMT_CONSTEXPR
value
(
string_view_metadata
s
)
:
str
(
s
)
{}
FMT_CONSTEXPR
value
(
replacement
r
)
:
repl
(
r
)
{}
}
val
;
std
::
size_t
arg_id_end
=
0
;
// Position past the end of the argument id.
FMT_CONSTEXPR
format_part
()
:
part_kind
(
kind
::
arg_index
),
end_of_argument_id
(
0u
),
val
(
0u
)
{}
FMT_CONSTEXPR
format_part
(
kind
k
=
kind
::
arg_index
,
value
v
=
{})
:
part_kind
(
k
),
val
(
v
)
{}
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
(
string_view_metadata
name
)
{
return
format_part
(
kind
::
arg_name
,
name
);
}
static
FMT_CONSTEXPR
format_part
make_text
(
string_view_metadata
text
)
{
return
format_part
(
kind
::
text
,
text
);
}
static
FMT_CONSTEXPR
format_part
make_replacement
(
replacement
repl
)
{
return
format_part
(
kind
::
replacement
,
repl
);
}
};
template
<
typename
Char
>
struct
part_counter
{
unsigned
num_parts
=
0
;
FMT_CONSTEXPR
void
on_text
(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
!=
end
)
++
num_parts
;
}
FMT_CONSTEXPR
format_part
(
unsigned
arg_index
)
:
part_kind
(
kind
::
arg_index
),
end_of_argument_id
(
0u
),
val
(
arg_index
)
{}
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
(
named_arg_id
arg_name
)
:
part_kind
(
kind
::
arg_name
),
end_of_argument_id
(
0u
),
val
(
arg_name
)
{}
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
)
{}
FMT_CONSTEXPR
format_part
(
string_view_metadata
text
)
:
part_kind
(
kind
::
text
),
end_of_argument_id
(
0u
),
val
(
text
)
{}
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
;
}
FMT_CONSTEXPR
format_part
(
replacement
repl
)
:
part_kind
(
kind
::
replacement
),
end_of_argument_id
(
0u
),
val
(
repl
)
{}
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:
FMT_CONSTEXPR
format_preparation_handler
(
basic_string_view
<
Char
>
format
,
PartsContainer
&
parts
)
:
parts_
(
parts
),
format_
(
format
),
parse_context_
(
format
)
{}
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
offset
=
begin
-
format_
str_
.
data
();
const
auto
size
=
end
-
begin
;
parts_
.
push_back
(
par
t
(
string_view_metadata
(
offset
,
size
)));
handler_
(
part
::
make_tex
t
(
string_view_metadata
(
offset
,
size
)));
}
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_arg_id
(
view
);
parts_
.
push_back
(
part
(
arg_id
));
part_
=
part
::
make_arg_name
(
string_view_metadata
(
format_str_
,
id
));
}
FMT_CONSTEXPR
void
on_replacement_field
(
const
Char
*
ptr
)
{
parts_
.
back
().
end_of_argument_id
=
ptr
-
format_
.
begin
();
part_
.
arg_id_end
=
ptr
-
format_str_
.
begin
();
handler_
(
part_
);
}
FMT_CONSTEXPR
const
Char
*
on_format_specs
(
const
Char
*
begin
,
const
Char
*
end
)
{
const
auto
specs_offset
=
to_unsigned
(
begin
-
format_
.
begin
());
const
auto
specs_offset
=
to_unsigned
(
begin
-
format_
str_
.
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_
);
auto
repl
=
typename
part
::
replacement
()
;
dynamic_specs_handler
<
basic_parse_context
<
Char
>>
handler
(
repl
.
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
.
part_kind
==
part
::
kind
::
arg_index
?
typename
part
::
replacement
(
last_part
.
val
.
arg_index
)
:
typename
part
::
replacement
(
last_part
.
val
.
arg_name
);
specs
.
parsed_specs
=
parsed_specs
;
last_part
=
part
(
specs
);
last_part
.
end_of_argument_id
=
specs_offset
;
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
=
specs_offset
;
handler_
(
part
);
return
begin
;
}
private:
PartsContainer
&
parts_
;
basic_string_view
<
Char
>
format_
;
PartHandler
handler_
;
part
part_
;
basic_string_view
<
Char
>
format_str_
;
basic_parse_context
<
Char
>
parse_context_
;
};
// 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
));
}
template
<
typename
Format
,
typename
PreparedPartsProvider
,
typename
...
Args
>
class
compiled_format
{
public:
...
...
@@ -152,7 +191,7 @@ class compiled_format {
switch
(
part
.
part_kind
)
{
case
format_part_t
:
:
kind
::
text
:
{
const
auto
text
=
value
.
text
.
to_view
(
format_view
.
data
());
const
auto
text
=
value
.
str
.
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
);
...
...
@@ -166,7 +205,7 @@ class compiled_format {
case
format_part_t
:
:
kind
::
arg_name
:
{
advance_parse_context_to_specification
(
parse_ctx
,
part
);
const
auto
named_arg_id
=
value
.
arg_name
.
to_view
(
format_view
.
data
());
const
auto
named_arg_id
=
value
.
str
.
to_view
(
format_view
.
data
());
format_arg
<
Range
>
(
parse_ctx
,
ctx
,
named_arg_id
);
}
break
;
case
format_part_t
:
:
kind
::
replacement
:
{
...
...
@@ -176,7 +215,7 @@ class compiled_format {
:
ctx
.
arg
(
arg_id_value
.
name
.
to_view
(
to_string_view
(
format_
).
data
()));
auto
specs
=
value
.
repl
.
parsed_
specs
;
auto
specs
=
value
.
repl
.
specs
;
handle_dynamic_spec
<
internal
::
width_checker
>
(
specs
.
width
,
specs
.
width_ref
,
ctx
,
format_view
.
begin
());
...
...
@@ -199,7 +238,7 @@ class compiled_format {
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_i
d
;
const
auto
specification_begin
=
view
.
data
()
+
part
.
arg_id_en
d
;
advance_to
(
parse_ctx
,
specification_begin
);
}
...
...
@@ -228,53 +267,15 @@ class compiled_format {
PreparedPartsProvider
parts_provider_
;
};
template
<
typename
Char
>
struct
part_counter
{
unsigned
num_parts
=
0
;
FMT_CONSTEXPR
void
on_text
(
const
Char
*
begin
,
const
Char
*
end
)
{
if
(
begin
!=
end
)
++
num_parts
;
}
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
void
on_replacement_field
(
const
Char
*
)
{}
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
;
}
}
return
begin
;
}
FMT_CONSTEXPR
void
on_error
(
const
char
*
)
{}
};
template
<
typename
Format
>
class
compiletime_prepared_parts_type_provider
{
private:
using
char_type
=
char_t
<
Format
>
;
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
;
}
// Workaround for old compilers. Compiletime parts preparation will not be
// performed with them anyway.
#if FMT_USE_CONSTEXPR
static
FMT_CONSTEXPR_DECL
const
unsigned
number_of_format_parts
=
co
mpiletime_prepared_parts_type_provider
::
count_parts
(
);
co
unt_parts
(
to_string_view
(
Format
())
);
#else
static
const
unsigned
number_of_format_parts
=
0u
;
#endif
...
...
@@ -303,32 +304,19 @@ template <typename Format> class compiletime_prepared_parts_type_provider {
format_parts_array
<
number_of_format_parts
>
,
empty
>
;
};
template
<
typename
Parts
>
class
compiletime_prepared_parts_collector
{
private:
using
format_part
=
typename
Parts
::
value_type
;
public:
FMT_CONSTEXPR
explicit
compiletime_prepared_parts_collector
(
Parts
&
parts
)
:
parts_
{
parts
},
counter_
{
0u
}
{}
FMT_CONSTEXPR
void
push_back
(
format_part
part
)
{
parts_
[
counter_
++
]
=
part
;
}
FMT_CONSTEXPR
format_part
&
back
()
{
return
parts_
[
counter_
-
1
];
}
private:
Parts
&
parts_
;
unsigned
counter_
;
};
template
<
typename
PartsContainer
,
typename
Char
>
FMT_CONSTEXPR
PartsContainer
prepare_compiletime_parts
(
basic_string_view
<
Char
>
format
)
{
using
collector
=
compiletime_prepared_parts_collector
<
PartsContainer
>
;
prepare_compiletime_parts
(
basic_string_view
<
Char
>
format_str
)
{
// This is not a lambda for compatibility with older compilers.
struct
collector
{
PartsContainer
&
parts
;
unsigned
counter
=
0
;
FMT_CONSTEXPR
void
operator
()(
const
format_part
<
Char
>&
part
)
{
parts
[
counter
++
]
=
part
;
}
};
PartsContainer
parts
;
collector
c
(
parts
);
internal
::
parse_format_string
<
/*IS_CONSTEXPR=*/
true
>
(
format
,
format_preparation_handler
<
Char
,
collector
>
(
format
,
c
));
compile_format_string
<
true
>
(
format_str
,
collector
{
parts
});
return
parts
;
}
...
...
@@ -336,10 +324,10 @@ template <typename Char> class runtime_parts_provider {
public:
using
parts_container
=
std
::
vector
<
internal
::
format_part
<
Char
>>
;
runtime_parts_provider
(
basic_string_view
<
Char
>
format
)
{
internal
::
pars
e_format_string
<
false
>
(
format
,
format_preparation_handler
<
Char
,
parts_container
>
(
format
,
parts_
)
);
runtime_parts_provider
(
basic_string_view
<
Char
>
format
_str
)
{
compil
e_format_string
<
false
>
(
format
_str
,
[
this
](
const
format_part
<
Char
>&
part
)
{
parts_
.
push_back
(
part
);
}
);
}
const
parts_container
&
parts
()
const
{
return
parts_
;
}
...
...
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