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 @@ -99,6 +99,11 @@ } llvm::Error loop(MessageHandler &Handler) override { + // We dont want any inline storage because these are expected to grow quite + // a lot, SmallString happens to give slightly better codegen than + // std::string when we aren't expecting to use any inline storage. + llvm::SmallString<0> JsonContents; + llvm::SmallString<0> ScratchSpace; while (!feof(In)) { if (shutdownRequested()) return error(std::make_error_code(std::errc::operation_canceled), @@ -106,7 +111,7 @@ if (ferror(In)) return llvm::errorCodeToError( std::error_code(errno, std::system_category())); - if (auto JSON = readRawMessage()) { + if (auto JSON = readRawMessage(JsonContents, ScratchSpace)) { if (auto Doc = llvm::json::parse(*JSON)) { vlog(Pretty ? "<<< {0:2}\n" : "<<< {0}\n", *Doc); if (!handleMessage(std::move(*Doc), Handler)) @@ -126,22 +131,31 @@ bool handleMessage(llvm::json::Value Message, MessageHandler &Handler); // Writes outgoing message to Out stream. void sendMessage(llvm::json::Value Message) { - std::string S; - llvm::raw_string_ostream OS(S); + OutputBuffer.clear(); + llvm::raw_svector_ostream OS(OutputBuffer); OS << llvm::formatv(Pretty ? "{0:2}" : "{0}", Message); - OS.flush(); - Out << "Content-Length: " << S.size() << "\r\n\r\n" << S; + Out << "Content-Length: " << OutputBuffer.size() << "\r\n\r\n" + << OutputBuffer; Out.flush(); - vlog(">>> {0}\n", S); + vlog(">>> {0}\n", OutputBuffer); } // Read raw string messages from input stream. - llvm::Optional readRawMessage() { - return Style == JSONStreamStyle::Delimited ? readDelimitedMessage() - : readStandardMessage(); + llvm::Optional readRawMessage(SmallVectorImpl &Buffer, + SmallVectorImpl &Scratch) { + return Style == JSONStreamStyle::Delimited + ? readDelimitedMessage(Buffer, Scratch) + : readStandardMessage(Buffer, Scratch); } - llvm::Optional readDelimitedMessage(); - llvm::Optional readStandardMessage(); + llvm::Optional + readDelimitedMessage(SmallVectorImpl &Buffer, + SmallVectorImpl &Scratch); + llvm::Optional readStandardMessage(SmallVectorImpl &Buffer, + SmallVectorImpl &Scratch); + + // Accept that these buffers are going to be large enough that there is no + // point in inline storage. + llvm::SmallString<0> OutputBuffer; std::FILE *In; llvm::raw_ostream &Out; @@ -190,7 +204,7 @@ // 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) { +bool readLine(std::FILE *In, llvm::SmallVectorImpl &Out) { static constexpr int BufSize = 1024; size_t Size = 0; Out.clear(); @@ -215,17 +229,18 @@ // Returns None when: // - ferror(), feof(), or shutdownRequested() are set. // - Content-Length is missing or empty (protocol error) -llvm::Optional JSONTransport::readStandardMessage() { +llvm::Optional +JSONTransport::readStandardMessage(SmallVectorImpl &Buffer, + SmallVectorImpl &Scratch) { // 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; while (true) { - if (feof(In) || ferror(In) || !readLine(In, Line)) + if (feof(In) || ferror(In) || !readLine(In, Scratch)) return llvm::None; - InMirror << Line; + InMirror << Scratch; - llvm::StringRef LineRef(Line); + llvm::StringRef LineRef(Scratch.begin(), Scratch.size()); // We allow comments in headers. Technically this isn't part @@ -264,23 +279,23 @@ return llvm::None; } - std::string JSON(ContentLength, '\0'); + Buffer.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, [&]{ - return std::fread(&JSON[Pos], 1, ContentLength - Pos, In); + Read = retryAfterSignalUnlessShutdown(0, [&] { + return std::fread(&Buffer[Pos], 1, ContentLength - Pos, In); }); if (Read == 0) { elog("Input was aborted. Read only {0} bytes of expected {1}.", Pos, ContentLength); return llvm::None; } - InMirror << llvm::StringRef(&JSON[Pos], Read); + InMirror << llvm::StringRef(&Buffer[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 StringRef(Buffer.begin(), Buffer.size()); } // For lit tests we support a simplified syntax: @@ -288,12 +303,13 @@ // - 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; - while (readLine(In, Line)) { - InMirror << Line; - auto LineRef = llvm::StringRef(Line).trim(); +llvm::Optional +JSONTransport::readDelimitedMessage(SmallVectorImpl &Buffer, + SmallVectorImpl &Scratch) { + Buffer.clear(); + while (readLine(In, Scratch)) { + InMirror << Scratch; + auto LineRef = llvm::StringRef(Scratch.begin(), Scratch.size()).trim(); if (LineRef.startswith("#")) // comment continue; @@ -301,7 +317,7 @@ if (LineRef.rtrim() == "---") break; - JSON += Line; + Buffer.append(Scratch.begin(), Scratch.end()); } if (shutdownRequested()) @@ -310,7 +326,7 @@ elog("Input error while reading message!"); return llvm::None; } - return std::move(JSON); // Including at EOF + return StringRef(Buffer.begin(), Buffer.size()); // Including at EOF } } // namespace