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 @@ -126,22 +126,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() { + // This stores the message in buffer owned by this class, as such once this + // method is called, any previous StringRefs held from this are invalidated. + llvm::Optional readRawMessage() { return Style == JSONStreamStyle::Delimited ? readDelimitedMessage() : readStandardMessage(); } - llvm::Optional readDelimitedMessage(); - llvm::Optional readStandardMessage(); + llvm::Optional readDelimitedMessage(); + llvm::Optional readStandardMessage(); + + // Accept that these buffers are going to be large enough that there is no + // point in inline storage. Using a SmallString also saves the need for + // unnecessary things null terminators. + llvm::SmallString<0> JsonContentBuffer; + llvm::SmallString<0> JsonLineBuffer; + llvm::SmallString<0> OutputBuffer; std::FILE *In; llvm::raw_ostream &Out; @@ -190,7 +199,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 +224,16 @@ // 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() { // 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, JsonLineBuffer)) return llvm::None; - InMirror << Line; + InMirror << JsonLineBuffer; - llvm::StringRef LineRef(Line); + llvm::StringRef LineRef(JsonLineBuffer); // We allow comments in headers. Technically this isn't part @@ -264,23 +272,23 @@ return llvm::None; } - std::string JSON(ContentLength, '\0'); + JsonContentBuffer.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(&JsonContentBuffer[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(&JsonContentBuffer[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(JsonContentBuffer); } // For lit tests we support a simplified syntax: @@ -288,12 +296,11 @@ // - 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() { + JsonContentBuffer.clear(); + while (readLine(In, JsonLineBuffer)) { + InMirror << JsonLineBuffer; + auto LineRef = llvm::StringRef(JsonLineBuffer).trim(); if (LineRef.startswith("#")) // comment continue; @@ -301,7 +308,7 @@ if (LineRef.rtrim() == "---") break; - JSON += Line; + JsonContentBuffer += JsonLineBuffer; } if (shutdownRequested()) @@ -310,7 +317,7 @@ elog("Input error while reading message!"); return llvm::None; } - return std::move(JSON); // Including at EOF + return StringRef(JsonContentBuffer); // Including at EOF } } // namespace