Skip to content

Commit 0158c70

Browse files
Greatly improved color formatting performance. Color scanning is now single pass
1 parent 5f5a7fa commit 0158c70

5 files changed

Lines changed: 96 additions & 65 deletions

File tree

CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ message("Adding source files")
1212
set(LWLOG_SOURCE_FILES
1313
${CMAKE_SOURCE_DIR}/lwlog/include/details/topic_registry.cpp
1414
${CMAKE_SOURCE_DIR}/lwlog/include/details/pattern/attribute.cpp
15-
${CMAKE_SOURCE_DIR}/lwlog/include/details/pattern/sgr_encoder.cpp
1615
)
1716

1817
message("Creating lwlog library")

lwlog/include/details/pattern/pattern.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ namespace lwlog::details
3636
std::vector<std::string_view> parse_short_flags();
3737

3838
private:
39-
sgr_encoder color_encoder;
40-
4139
details::memory_buffer<BufferLimits::pattern> m_pattern_buffer;
4240

4341
char m_cached_pattern_buffer[BufferLimits::pattern];

lwlog/include/details/pattern/pattern_impl.h

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#pragma once
1+
#pragma once
22

33
#include "pattern.h"
44
#include "formatters.h"
@@ -93,69 +93,74 @@ namespace lwlog::details
9393
template<typename BufferLimits>
9494
void pattern<BufferLimits>::process_color_flags(bool use_color)
9595
{
96-
memory_buffer<10> color_buffer;
96+
memory_buffer<BufferLimits::pattern> temp_buffer;
9797

98-
const char* const reset_seq{ use_color ? sgr_encoder::reset : "" };
99-
const std::uint8_t reset_seq_len{ use_color ? sgr_encoder::reset_length : static_cast<std::uint8_t>(0) };
98+
const std::size_t pattern_buffer_size{ m_pattern_buffer.size() };
10099

101100
std::size_t pos{ 0 };
102-
std::size_t dot_pos{ std::string_view::npos };
103-
std::size_t open_brace_pos{ std::string_view::npos };
104-
std::size_t close_brace_pos{ std::string_view::npos };
105-
106-
bool is_flag_valid{ true };
107-
108-
while (pos < m_pattern_buffer.size())
101+
std::size_t open_pos{ 0 };
102+
std::size_t close_pos{ 0 };
103+
while (pos < pattern_buffer_size)
109104
{
110-
if (m_pattern_buffer[pos] == '.')
105+
if (m_pattern_buffer[pos] != '.')
111106
{
112-
dot_pos = pos;
113-
is_flag_valid = true;
107+
temp_buffer.append(m_pattern_buffer[pos]);
108+
++pos;
109+
continue;
114110
}
115-
else if (m_pattern_buffer[pos] == '(')
111+
112+
open_pos = pos + 1;
113+
while (open_pos < pattern_buffer_size && sgr_encoder::is_base_name_char(m_pattern_buffer[open_pos]))
116114
{
117-
open_brace_pos = pos;
115+
++open_pos;
118116
}
119-
else if (m_pattern_buffer[pos] == ')')
117+
118+
if (open_pos >= pattern_buffer_size || open_pos == pos + 2 || m_pattern_buffer[open_pos] != '(')
120119
{
121-
close_brace_pos = pos;
120+
temp_buffer.append('.');
121+
++pos;
122+
continue;
122123
}
123-
else if (m_pattern_buffer[pos] == ' ' && dot_pos != std::string_view::npos
124-
&& (pos > dot_pos) && (pos < open_brace_pos))
124+
125+
close_pos = open_pos + 1;
126+
while (close_pos < pattern_buffer_size && m_pattern_buffer[close_pos] != ')')
125127
{
126-
is_flag_valid = false;
128+
++close_pos;
127129
}
128130

129-
if (dot_pos != std::string_view::npos && open_brace_pos != std::string_view::npos
130-
&& close_brace_pos != std::string_view::npos)
131+
if (close_pos == pattern_buffer_size)
131132
{
132-
if ((dot_pos < open_brace_pos && open_brace_pos < close_brace_pos) && is_flag_valid == true)
133-
{
134-
const std::size_t color_flag_len{ open_brace_pos - dot_pos + 1 };
135-
const std::size_t color_name_len{ open_brace_pos - dot_pos - 1 };
136-
const std::string_view color_name{ &m_pattern_buffer[dot_pos + 1], color_name_len };
137-
138-
color_encoder.encode(color_name, color_buffer);
139-
140-
const char* const color_seq{ use_color ? color_buffer.c_str() : "" };
141-
const std::size_t color_seq_len{ use_color ? color_buffer.size() : 0 };
142-
143-
m_pattern_buffer.replace(dot_pos, color_flag_len, color_seq, color_seq_len);
144-
145-
const std::size_t recalculate_close_brace_pos{ close_brace_pos - color_flag_len + color_seq_len };
146-
m_pattern_buffer.replace(recalculate_close_brace_pos, 1, reset_seq, reset_seq_len);
133+
temp_buffer.append(&m_pattern_buffer[pos], (open_pos + 1) - pos);
134+
pos = open_pos + 1;
135+
continue;
136+
}
147137

148-
pos = recalculate_close_brace_pos + reset_seq_len;
138+
const std::string_view color_name{ &m_pattern_buffer[pos + 1], open_pos - pos - 1 };
139+
const std::string_view inner_text{ &m_pattern_buffer[open_pos + 1], close_pos - open_pos - 1 };
140+
const bool is_encodable{ sgr_encoder::can_encode(color_name) };
149141

150-
is_flag_valid = true;
151-
dot_pos = std::string_view::npos;
152-
open_brace_pos = std::string_view::npos;
153-
close_brace_pos = std::string_view::npos;
154-
}
142+
if (use_color && is_encodable)
143+
{
144+
sgr_encoder::encode(color_name, temp_buffer);
145+
temp_buffer.append(inner_text);
146+
temp_buffer.append(sgr_encoder::reset);
147+
}
148+
else if (!is_encodable)
149+
{
150+
temp_buffer.append(&m_pattern_buffer[pos], open_pos - pos + 1);
151+
temp_buffer.append(inner_text);
152+
temp_buffer.append(')');
153+
}
154+
else
155+
{
156+
temp_buffer.append(inner_text);
155157
}
156158

157-
++pos;
159+
pos = close_pos + 1;
158160
}
161+
162+
m_pattern_buffer.reset();
163+
m_pattern_buffer.append(temp_buffer.data());
159164
}
160165

161166
template<typename BufferLimits>

lwlog/include/details/pattern/sgr_encoder.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,22 @@ namespace lwlog::details
3131
static constexpr std::uint8_t intensity_offset{ 60 };
3232

3333
public:
34-
static constexpr const char* const reset{ "\x1b[0m" };
35-
static constexpr std::uint8_t reset_length{ 4 };
34+
static constexpr const char* reset{ "\x1b[0m" };
3635

3736
public:
38-
static void encode(std::string_view token, memory_buffer<10>& out);
37+
template<std::size_t Size>
38+
static void encode(std::string_view token, memory_buffer<Size>& out);
39+
40+
static bool can_encode(std::string_view token);
41+
42+
public:
43+
static bool is_base_name_char(unsigned char ch);
3944

4045
private:
46+
static bool has_prefix(std::string_view str, std::string_view prefix);
4147
static const sgr_color_spec* find_color_spec(std::string_view color);
4248
static std::uint8_t resolve_color(const sgr_color_spec&, bool is_background, bool is_dark);
4349
};
44-
}
50+
}
51+
52+
#include "sgr_encoder_impl.h"

lwlog/include/details/pattern/sgr_encoder.cpp renamed to lwlog/include/details/pattern/sgr_encoder_impl.h

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,21 @@
1-
#include "sgr_encoder.h"
2-
#include <iostream>
1+
#pragma once
32

43
namespace lwlog::details
54
{
6-
void sgr_encoder::encode(std::string_view token, memory_buffer<10>& out)
5+
template<std::size_t Size>
6+
void sgr_encoder::encode(std::string_view token, memory_buffer<Size>& out)
77
{
88
std::string_view name{ token };
99

1010
bool is_background{ false };
1111
bool is_dark{ false };
1212

13-
constexpr auto starts_with = [](std::string_view str, std::string_view prefix) {
14-
return str.compare(0, prefix.size(), prefix) == 0;
15-
};
16-
17-
if (starts_with(name, "bg_")) { is_background = true; name.remove_prefix(3); }
18-
if (starts_with(name, "dark_")) { is_dark = true; name.remove_prefix(5); }
13+
if (sgr_encoder::has_prefix(name, "bg_")) { is_background = true; name.remove_prefix(3); }
14+
if (sgr_encoder::has_prefix(name, "dark_")) { is_dark = true; name.remove_prefix(5); }
1915

2016
const sgr_color_spec* spec{ sgr_encoder::find_color_spec(name) };
2117
if (!spec)
2218
{
23-
out.append(token);
2419
return;
2520
}
2621

@@ -47,6 +42,32 @@ namespace lwlog::details
4742
out.append('m');
4843
}
4944

45+
bool sgr_encoder::can_encode(std::string_view token)
46+
{
47+
if (sgr_encoder::has_prefix(token, "bg_")) { token.remove_prefix(3); }
48+
if (sgr_encoder::has_prefix(token, "dark_")) { token.remove_prefix(5); }
49+
50+
for (const auto& spec : sgr_colors)
51+
{
52+
if (spec.base_name == token)
53+
{
54+
return true;
55+
}
56+
}
57+
58+
return false;
59+
}
60+
61+
bool sgr_encoder::is_base_name_char(unsigned char ch)
62+
{
63+
return (ch >= 'a' && ch <= 'z') || ch == '_';
64+
}
65+
66+
bool sgr_encoder::has_prefix(std::string_view str, std::string_view prefix)
67+
{
68+
return str.compare(0, prefix.size(), prefix) == 0;
69+
}
70+
5071
const sgr_color_spec* sgr_encoder::find_color_spec(std::string_view color)
5172
{
5273
for (const auto& spec : sgr_colors)
@@ -62,12 +83,12 @@ namespace lwlog::details
6283

6384
std::uint8_t sgr_encoder::resolve_color(const sgr_color_spec& spec, bool is_background, bool is_dark)
6485
{
65-
const std::uint8_t channel_offset{ static_cast<std::uint8_t>(is_background ?
86+
const std::uint8_t channel_offset{ static_cast<std::uint8_t>(is_background ?
6687
color_channel_offset::background : color_channel_offset::foreground) };
6788

6889
if (spec.base_name == "grey")
6990
{
70-
const std::uint8_t grey_offset{ static_cast<std::uint8_t>(is_dark ?
91+
const std::uint8_t grey_offset{ static_cast<std::uint8_t>(is_dark ?
7192
sgr_encoder::intensity_offset - 7 : 0) };
7293
return spec.fg_base_code + channel_offset + grey_offset;
7394
}
@@ -78,7 +99,7 @@ namespace lwlog::details
7899
}
79100

80101
const std::uint8_t color_intensity_offset{ static_cast<std::uint8_t>(
81-
!is_dark ? sgr_encoder::intensity_offset : 0)
102+
!is_dark ? sgr_encoder::intensity_offset : 0)
82103
};
83104
return spec.fg_base_code + channel_offset + color_intensity_offset;
84105
}

0 commit comments

Comments
 (0)