Repository: bitcoin/bitcoin
File: src/util/string.cpp (C++)

Code before:
        // Read a character from the incoming buffer and increment the iterator
        auto c = static_cast<char>(*it);
        ++it;
        ++count;
        // If the character we just consumed was \n, the line is terminated.
        // The \n itself does not count against max_read.
        if (c == '\n') break;
        // If the character we just consumed gives us a line length greater
        // than max_read, 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_read) throw std::runtime_error("max_read exceeded by LineReader");
    }
    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 trailing \r and/or \n
    return std::string(line);
}

// Ignores max_read but won't overflow
std::string LineReader::ReadLength(size_t len)
{
    if (len == 0) return "";
    if (Left() < len) throw std::runtime_error("Not enough data in buffer");
    std::string out(reinterpret_cast<const char*>(std::to_address(it)), len);
    it += len;
    return out;
}

size_t LineReader::Left() const
{
    return std::distance(it, end);
}
} // namespace util


Review comment (suggestion):
Since we advance the class member iterator `it` while scanning the span, throwing a `max_read` exception drops the bytes already read. We should advance it only at the very end to avoid loosing them (a test for this case would be good).

Also, small extra nit: what if there only char is a \n? The returned string will be empty?

As a separate topic, could use [std::memchr](https://en.cppreference.com/w/cpp/string/byte/memchr.html) to find the first instance of `\n` which would avoid the loop (haven't fully tried it but seems to be an option as well - dropping it in case you want to investigate it, all good if not).

Code after:
        // Read a character from the incoming buffer and increment the iterator
        auto c = static_cast<char>(*it);
        ++it;
        ++count;
        // 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') {
            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);
        }
        // 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) {
            // Reset iterator
            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;
    return std::nullopt;
}

// Ignores max_line_length but won't overflow
std::string LineReader::ReadLength(size_t len)
{
    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);
    it += len;
    return out;
}

size_t LineReader::Remaining() const
{
    return std::distance(it, end);
}
} // namespace util