diff --git a/clang-tools-extra/clangd/JSONTransport.cpp b/clang-tools-extra/clangd/JSONTransport.cpp --- 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 @@ -99,6 +100,7 @@ } 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 @@ 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 @@ } // Read raw string messages from input stream. - llvm::Optional readRawMessage() { - return Style == JSONStreamStyle::Delimited ? readDelimitedMessage() - : readStandardMessage(); + bool readRawMessage(std::string &JSON) { + return Style == JSONStreamStyle::Delimited ? readDelimitedMessage(JSON) + : readStandardMessage(JSON); } - llvm::Optional readDelimitedMessage(); - llvm::Optional readStandardMessage(); + bool readDelimitedMessage(std::string &JSON); + bool readStandardMessage(std::string &JSON); llvm::SmallVector OutputBuffer; std::FILE *In; @@ -191,12 +193,14 @@ // 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 &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 @@ // Returns None when: // - ferror(), feof(), or shutdownRequested() are set. // - Content-Length is missing or empty (protocol error) -llvm::Optional 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 @@ 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 @@ 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 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 @@ } 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