[clang-tools-extra] 466acd6 - [clangd] Avoid reallocating buffers for each message read:
Sam McCall via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 13 08:40:54 PST 2021
Author: Sam McCall
Date: 2021-01-13T17:40:33+01:00
New Revision: 466acd694861138997d668a3f9cb29aa87bd316e
URL: https://github.com/llvm/llvm-project/commit/466acd694861138997d668a3f9cb29aa87bd316e
DIFF: https://github.com/llvm/llvm-project/commit/466acd694861138997d668a3f9cb29aa87bd316e.diff
LOG: [clangd] Avoid reallocating buffers for each message read:
- reuse std::string we read messages into
- when reading line-wise, use SmallVector<128> and read in chunks of 128
(this affects headers, which are short, and tests, which don't matter)
Differential Revision: https://reviews.llvm.org/D93653
Added:
Modified:
clang-tools-extra/clangd/JSONTransport.cpp
Removed:
################################################################################
diff --git a/clang-tools-extra/clangd/JSONTransport.cpp b/clang-tools-extra/clangd/JSONTransport.cpp
index 662e5df4e27b..3e8caceda21c 100644
--- a/clang-tools-extra/clangd/JSONTransport.cpp
+++ b/clang-tools-extra/clangd/JSONTransport.cpp
@@ -10,6 +10,7 @@
#include "support/Cancellation.h"
#include "support/Logger.h"
#include "support/Shutdown.h"
+#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Errno.h"
#include "llvm/Support/Error.h"
#include <system_error>
@@ -99,6 +100,7 @@ class JSONTransport : public Transport {
}
llvm::Error loop(MessageHandler &Handler) override {
+ std::string JSON; // Messages may be large, reuse same big buffer.
while (!feof(In)) {
if (shutdownRequested())
return error(std::make_error_code(std::errc::operation_canceled),
@@ -106,14 +108,14 @@ class JSONTransport : public Transport {
if (ferror(In))
return llvm::errorCodeToError(
std::error_code(errno, std::system_category()));
- if (auto JSON = readRawMessage()) {
- if (auto Doc = llvm::json::parse(*JSON)) {
+ if (readRawMessage(JSON)) {
+ if (auto Doc = llvm::json::parse(JSON)) {
vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc);
if (!handleMessage(std::move(*Doc), Handler))
return llvm::Error::success(); // we saw the "exit" notification.
} else {
// Parse error. Log the raw message.
- vlog("<<< {0}\n", *JSON);
+ vlog("<<< {0}\n", JSON);
elog("JSON parse error: {0}", llvm::toString(Doc.takeError()));
}
}
@@ -136,12 +138,12 @@ class JSONTransport : public Transport {
}
// Read raw string messages from input stream.
- llvm::Optional<std::string> readRawMessage() {
- return Style == JSONStreamStyle::Delimited ? readDelimitedMessage()
- : readStandardMessage();
+ bool readRawMessage(std::string &JSON) {
+ return Style == JSONStreamStyle::Delimited ? readDelimitedMessage(JSON)
+ : readStandardMessage(JSON);
}
- llvm::Optional<std::string> readDelimitedMessage();
- llvm::Optional<std::string> readStandardMessage();
+ bool readDelimitedMessage(std::string &JSON);
+ bool readStandardMessage(std::string &JSON);
llvm::SmallVector<char, 0> OutputBuffer;
std::FILE *In;
@@ -191,12 +193,14 @@ bool JSONTransport::handleMessage(llvm::json::Value Message,
// Tries to read a line up to and including \n.
// If failing, feof(), ferror(), or shutdownRequested() will be set.
-bool readLine(std::FILE *In, std::string &Out) {
- static constexpr int BufSize = 1024;
+bool readLine(std::FILE *In, llvm::SmallVectorImpl<char> &Out) {
+ // Big enough to hold any reasonable header line. May not fit content lines
+ // in delimited mode, but performance doesn't matter for that mode.
+ static constexpr int BufSize = 128;
size_t Size = 0;
Out.clear();
for (;;) {
- Out.resize(Size + BufSize);
+ Out.resize_for_overwrite(Size + BufSize);
// Handle EINTR which is sent when a debugger attaches on some platforms.
if (!retryAfterSignalUnlessShutdown(
nullptr, [&] { return std::fgets(&Out[Size], BufSize, In); }))
@@ -216,14 +220,14 @@ bool readLine(std::FILE *In, std::string &Out) {
// Returns None when:
// - ferror(), feof(), or shutdownRequested() are set.
// - Content-Length is missing or empty (protocol error)
-llvm::Optional<std::string> JSONTransport::readStandardMessage() {
+bool JSONTransport::readStandardMessage(std::string &JSON) {
// A Language Server Protocol message starts with a set of HTTP headers,
// delimited by \r\n, and terminated by an empty line (\r\n).
unsigned long long ContentLength = 0;
- std::string Line;
+ llvm::SmallString<128> Line;
while (true) {
if (feof(In) || ferror(In) || !readLine(In, Line))
- return llvm::None;
+ return false;
InMirror << Line;
llvm::StringRef LineRef(Line);
@@ -258,14 +262,14 @@ llvm::Optional<std::string> JSONTransport::readStandardMessage() {
elog("Refusing to read message with long Content-Length: {0}. "
"Expect protocol errors",
ContentLength);
- return llvm::None;
+ return false;
}
if (ContentLength == 0) {
log("Warning: Missing Content-Length header, or zero-length message.");
- return llvm::None;
+ return false;
}
- std::string JSON(ContentLength, '\0');
+ JSON.resize(ContentLength);
for (size_t Pos = 0, Read; Pos < ContentLength; Pos += Read) {
// Handle EINTR which is sent when a debugger attaches on some platforms.
Read = retryAfterSignalUnlessShutdown(0, [&]{
@@ -274,24 +278,24 @@ llvm::Optional<std::string> JSONTransport::readStandardMessage() {
if (Read == 0) {
elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos,
ContentLength);
- return llvm::None;
+ return false;
}
InMirror << llvm::StringRef(&JSON[Pos], Read);
clearerr(In); // If we're done, the error was transient. If we're not done,
// either it was transient or we'll see it again on retry.
Pos += Read;
}
- return std::move(JSON);
+ return true;
}
// For lit tests we support a simplified syntax:
// - messages are delimited by '---' on a line by itself
// - lines starting with # are ignored.
// This is a testing path, so favor simplicity over performance here.
-// When returning None, feof(), ferror(), or shutdownRequested() will be set.
-llvm::Optional<std::string> JSONTransport::readDelimitedMessage() {
- std::string JSON;
- std::string Line;
+// When returning false: feof(), ferror(), or shutdownRequested() will be set.
+bool JSONTransport::readDelimitedMessage(std::string &JSON) {
+ JSON.clear();
+ llvm::SmallString<128> Line;
while (readLine(In, Line)) {
InMirror << Line;
auto LineRef = llvm::StringRef(Line).trim();
@@ -306,12 +310,12 @@ llvm::Optional<std::string> JSONTransport::readDelimitedMessage() {
}
if (shutdownRequested())
- return llvm::None;
+ return false;
if (ferror(In)) {
elog("Input error while reading message!");
- return llvm::None;
+ return false;
}
- return std::move(JSON); // Including at EOF
+ return true; // Including at EOF
}
} // namespace
More information about the cfe-commits
mailing list