forked from OSchip/llvm-project
[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
This commit is contained in:
parent
66d5994bd3
commit
466acd6948
|
@ -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 @@ public:
|
|||
}
|
||||
|
||||
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 @@ public:
|
|||
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 @@ private:
|
|||
}
|
||||
|
||||
// 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
|
||||
|
|
Loading…
Reference in New Issue