util/string: use string_view in LineReader
What changed, and why it matters
This is a routine code-quality refactor. It changes an internal string-line reader to return lightweight string views instead of making full string copies. There is no security fix or behavior change visible in the commit.
No security action required. Treat as normal code review/merge.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit converts util::LineReader::ReadLine() and ReadLength() from returning std::string to returning std::string_view, and updates callers in bitcoin-cli.cpp and torcontrol.cpp to use string_view. The LIFETIMEBOUND annotation is added to signal that returned views must not outlive the LineReader’s underlying buffer. The change avoids unnecessary allocations but does not alter parsing logic, validation, or trust boundaries.
Changed components
src/util/string.hsrc/util/string.cppsrc/bitcoin-cli.cppsrc/torcontrol.cppInspect captured patch +13 / −11
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 938ace35..a724f13d 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -104,7 +104,7 @@ void HTTPResponseHeaders::Read(util::LineReader& reader)
// Headers https://httpwg.org/specs/rfc9110.html#rfc.section.6.3
// A sequence of Field Lines https://httpwg.org/specs/rfc9110.html#rfc.section.5.2
while (auto maybe_line = reader.ReadLine()) {
- const std::string& line = *maybe_line;
+ const std::string_view line = *maybe_line;
// An empty line indicates end of the headers section https://www.rfc-editor.org/rfc/rfc2616#section-4
if (line.empty()) return;
@@ -965,7 +965,7 @@ HTTPResponse HTTPClient::ReadResponse()
throw HTTPError{"Failed to read status line"};
}
- const std::string& status_str = *status_line;
+ const std::string_view status_str = *status_line;
// Minimum status line is "HTTP/X.Y NNN" (e.g. "HTTP/1.1 200"), 12 characters.
if (status_str.size() < 12 || !status_str.starts_with("HTTP/")) {
throw HTTPError{"Invalid status line"};
@@ -976,7 +976,7 @@ HTTPResponse HTTPClient::ReadResponse()
throw HTTPError{"Invalid status line format"};
}
- std::string status_code_str = status_str.substr(space1 + 1, 3);
+ const std::string_view status_code_str = status_str.substr(space1 + 1, 3);
auto status_code = ToIntegral<int>(status_code_str);
if (!status_code) {
throw HTTPError{"Invalid status code"};
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 5393ba7f..56cad019 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -191,7 +191,7 @@ bool TorControlConnection::ProcessBuffer()
// Parse: <code><separator><data>
// <status>(-|+| )<data>
m_message.code = ToIntegral<int>(line->substr(0, 3)).value_or(0);
- m_message.lines.push_back(line->substr(4));
+ m_message.lines.emplace_back(line->substr(4));
char separator = (*line)[3]; // '-', '+', or ' '
if (separator == ' ') {
diff --git a/src/util/string.cpp b/src/util/string.cpp
index d1ef7a18..42d1418a 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -20,7 +20,7 @@ void ReplaceAll(std::string& in_out, const std::string& search, const std::strin
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()) {}
-std::optional<std::string> LineReader::ReadLine()
+std::optional<std::string_view> LineReader::ReadLine()
{
if (it == end) {
return std::nullopt;
@@ -38,7 +38,7 @@ std::optional<std::string> LineReader::ReadLine()
if (c == '\n') {
const std::string_view untrimmed_line(reinterpret_cast<const char*>(std::to_address(line_start)), count);
const std::string_view line = TrimStringView(untrimmed_line); // delete leading and trailing whitespace including \r and \n
- return std::string(line);
+ return line;
}
// 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,
@@ -57,11 +57,11 @@ std::optional<std::string> LineReader::ReadLine()
}
// Ignores max_line_length but won't overflow
-std::string LineReader::ReadLength(size_t len)
+std::string_view LineReader::ReadLength(size_t len)
{
- if (len == 0) return "";
+ if (len == 0) return {};
if (Remaining() < len) throw std::runtime_error("Not enough data in buffer");
- std::string out(reinterpret_cast<const char*>(std::to_address(it)), len);
+ std::string_view out(reinterpret_cast<const char*>(std::to_address(it)), len);
it += len;
return out;
}
diff --git a/src/util/string.h b/src/util/string.h
index da4ce5a3..797d5344 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -18,6 +18,8 @@
#include <string_view>
#include <vector>
+#include <attributes.h>
+
namespace util {
namespace detail {
template <unsigned num_params>
@@ -280,7 +282,7 @@ struct LineReader {
* std::nullopt if end of buffer is reached without finding a \n.
* @throws a std::runtime_error if max_line_length + 1 bytes are read without finding \n.
*/
- std::optional<std::string> ReadLine();
+ std::optional<std::string_view> ReadLine() LIFETIMEBOUND;
/**
* Returns string from current iterator position of specified length
@@ -290,7 +292,7 @@ struct LineReader {
* @returns a string of the expected length.
* @throws a std::runtime_error if there is not enough data in the buffer.
*/
- std::string ReadLength(size_t len);
+ std::string_view ReadLength(size_t len) LIFETIMEBOUND;
/**
* Returns remaining size of bytes in buffer
Why this scored 12/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.