util: Make LineReader consistently use string_views
What changed, and why it matters
This is a small internal code cleanup in Bitcoin Core. It rewrites a helper class called LineReader so it works directly with string data instead of converting bytes to strings back and forth. There is no user-facing change, no bug fix, and no security-relevant behavior change visible in the diff.
No security action needed. Treat as normal refactoring review.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit refactors util::LineReader from a struct using std::span
Changed components
src/util/string.hsrc/util/string.cppsrc/torcontrol.cppInspect captured patch +27 / −28
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index f7749d4a..9e00c715 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -179,7 +179,6 @@ bool TorControlConnection::ReceiveAndProcess()
bool TorControlConnection::ProcessBuffer()
{
util::LineReader reader(m_recv_buffer, MAX_LINE_LENGTH);
- auto start = reader.it;
while (auto line = reader.ReadLine()) {
if (m_message.lines.size() == MAX_LINE_COUNT) {
@@ -210,7 +209,7 @@ bool TorControlConnection::ProcessBuffer()
}
}
- m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + std::distance(start, reader.it));
+ m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + reader.Consumed());
return true;
}
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 8962513d..d9d59ef5 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -17,26 +17,24 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin
in_out = std::regex_replace(in_out, std::regex(search), substitute);
}
-LineReader::LineReader(std::span<const std::byte> buffer, size_t max_line_length)
- : start(buffer.begin()), end(buffer.end()), max_line_length(max_line_length), it(buffer.begin()) {}
+LineReader::LineReader(std::string_view str, size_t max_line_length)
+ : m_str{str}, m_max_line_length{max_line_length}, m_it{str.begin()} {}
std::optional<std::string_view> LineReader::ReadLine()
{
- if (it == end) {
+ if (m_it == m_str.end()) {
return std::nullopt;
}
- auto line_start = it;
- size_t count = 0;
- while (it != end) {
+ const auto line_start = m_it;
+ while (m_it != m_str.end()) {
// Read a character from the incoming buffer and increment the iterator
- auto c = static_cast<char>(*it);
- ++it;
- ++count;
+ const bool new_line{*m_it == '\n'};
+ ++m_it;
// If the character we just consumed was \n, the line is terminated.
// The \n itself does not count against max_line_length.
- if (c == '\n') {
- std::string_view line{reinterpret_cast<const char*>(std::to_address(line_start)), count - 1};
+ if (new_line) {
+ std::string_view line{line_start, m_it - 1};
if (!line.empty() && line.back() == '\r')
line.remove_suffix(1);
return line;
@@ -44,16 +42,16 @@ std::optional<std::string_view> LineReader::ReadLine()
// If the character we just consumed gives us a line length greater
// than max_line_length, and we are not at the end of the line (or buffer) yet,
// that means the line we are currently reading is too long, and we throw.
- if (count > max_line_length) {
+ if (static_cast<size_t>(std::distance(line_start, m_it)) > m_max_line_length) {
// Reset iterator
- it = line_start;
+ m_it = line_start;
throw std::runtime_error("max_line_length exceeded by LineReader");
}
}
// End of buffer reached without finding a \n or exceeding max_line_length.
// Reset the iterator so the rest of the buffer can be read granularly
// with ReadLength() and return null to indicate a line was not found.
- it = line_start;
+ m_it = line_start;
return std::nullopt;
}
@@ -62,18 +60,18 @@ std::string_view LineReader::ReadLength(size_t len)
{
if (len == 0) return {};
if (Remaining() < len) throw std::runtime_error("Not enough data in buffer");
- std::string_view out(reinterpret_cast<const char*>(std::to_address(it)), len);
- it += len;
+ std::string_view out(std::to_address(m_it), len);
+ m_it += len;
return out;
}
size_t LineReader::Remaining() const
{
- return std::distance(it, end);
+ return std::distance(m_it, m_str.end());
}
size_t LineReader::Consumed() const
{
- return std::distance(start, it);
+ return std::distance(m_str.begin(), m_it);
}
} // namespace util
diff --git a/src/util/string.h b/src/util/string.h
index 797d5344..e8f6ddf8 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -265,14 +265,16 @@ template <typename T1, size_t PREFIX_LEN>
std::equal(std::begin(prefix), std::end(prefix), std::begin(obj));
}
-struct LineReader {
- const std::span<const std::byte>::iterator start;
- const std::span<const std::byte>::iterator end;
- const size_t max_line_length;
- std::span<const std::byte>::iterator it;
-
- explicit LineReader(std::span<const std::byte> buffer, size_t max_line_length);
- explicit LineReader(std::string_view str, size_t max_line_length) : LineReader{std::as_bytes(std::span{str}), max_line_length} {}
+class LineReader
+{
+ const std::string_view m_str;
+ const size_t m_max_line_length;
+ std::string_view::iterator m_it;
+
+public:
+ explicit LineReader(std::span<const std::byte> buffer, size_t max_line_length)
+ : LineReader{std::string_view{reinterpret_cast<const char*>(buffer.data()), buffer.size()}, max_line_length} {}
+ explicit LineReader(std::string_view str, size_t max_line_length);
/**
* Returns a string from current iterator position up to (but not including) next \n
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.