1 //===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "DraftStore.h"
10 #include "support/Logger.h"
11 #include "llvm/ADT/StringExtras.h"
12 #include "llvm/Support/VirtualFileSystem.h"
13 #include <memory>
14 
15 namespace clang {
16 namespace clangd {
17 
getDraft(PathRef File) const18 llvm::Optional<DraftStore::Draft> DraftStore::getDraft(PathRef File) const {
19   std::lock_guard<std::mutex> Lock(Mutex);
20 
21   auto It = Drafts.find(File);
22   if (It == Drafts.end())
23     return None;
24 
25   return It->second.D;
26 }
27 
getActiveFiles() const28 std::vector<Path> DraftStore::getActiveFiles() const {
29   std::lock_guard<std::mutex> Lock(Mutex);
30   std::vector<Path> ResultVector;
31 
32   for (auto DraftIt = Drafts.begin(); DraftIt != Drafts.end(); DraftIt++)
33     ResultVector.push_back(std::string(DraftIt->getKey()));
34 
35   return ResultVector;
36 }
37 
increment(std::string & S)38 static void increment(std::string &S) {
39   // Ensure there is a numeric suffix.
40   if (S.empty() || !llvm::isDigit(S.back())) {
41     S.push_back('0');
42     return;
43   }
44   // Increment the numeric suffix.
45   auto I = S.rbegin(), E = S.rend();
46   for (;;) {
47     if (I == E || !llvm::isDigit(*I)) {
48       // Reached start of numeric section, it was all 9s.
49       S.insert(I.base(), '1');
50       break;
51     }
52     if (*I != '9') {
53       // Found a digit we can increment, we're done.
54       ++*I;
55       break;
56     }
57     *I = '0'; // and keep incrementing to the left.
58   }
59 }
60 
updateVersion(DraftStore::Draft & D,llvm::StringRef SpecifiedVersion)61 static void updateVersion(DraftStore::Draft &D,
62                           llvm::StringRef SpecifiedVersion) {
63   if (!SpecifiedVersion.empty()) {
64     // We treat versions as opaque, but the protocol says they increase.
65     if (SpecifiedVersion.compare_numeric(D.Version) <= 0)
66       log("File version went from {0} to {1}", D.Version, SpecifiedVersion);
67     D.Version = SpecifiedVersion.str();
68   } else {
69     // Note that if D was newly-created, this will bump D.Version from "" to 1.
70     increment(D.Version);
71   }
72 }
73 
addDraft(PathRef File,llvm::StringRef Version,llvm::StringRef Contents)74 std::string DraftStore::addDraft(PathRef File, llvm::StringRef Version,
75                                  llvm::StringRef Contents) {
76   std::lock_guard<std::mutex> Lock(Mutex);
77 
78   auto &D = Drafts[File];
79   updateVersion(D.D, Version);
80   std::time(&D.MTime);
81   D.D.Contents = std::make_shared<std::string>(Contents);
82   return D.D.Version;
83 }
84 
removeDraft(PathRef File)85 void DraftStore::removeDraft(PathRef File) {
86   std::lock_guard<std::mutex> Lock(Mutex);
87 
88   Drafts.erase(File);
89 }
90 
91 namespace {
92 
93 /// A read only MemoryBuffer shares ownership of a ref counted string. The
94 /// shared string object must not be modified while an owned by this buffer.
95 class SharedStringBuffer : public llvm::MemoryBuffer {
96   const std::shared_ptr<const std::string> BufferContents;
97   const std::string Name;
98 
99 public:
getBufferKind() const100   BufferKind getBufferKind() const override {
101     return MemoryBuffer::MemoryBuffer_Malloc;
102   }
103 
getBufferIdentifier() const104   StringRef getBufferIdentifier() const override { return Name; }
105 
SharedStringBuffer(std::shared_ptr<const std::string> Data,StringRef Name)106   SharedStringBuffer(std::shared_ptr<const std::string> Data, StringRef Name)
107       : BufferContents(std::move(Data)), Name(Name) {
108     assert(BufferContents && "Can't create from empty shared_ptr");
109     MemoryBuffer::init(BufferContents->c_str(),
110                        BufferContents->c_str() + BufferContents->size(),
111                        /*RequiresNullTerminator=*/true);
112   }
113 };
114 } // namespace
115 
asVFS() const116 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> DraftStore::asVFS() const {
117   auto MemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
118   std::lock_guard<std::mutex> Guard(Mutex);
119   for (const auto &Draft : Drafts)
120     MemFS->addFile(Draft.getKey(), Draft.getValue().MTime,
121                    std::make_unique<SharedStringBuffer>(
122                        Draft.getValue().D.Contents, Draft.getKey()));
123   return MemFS;
124 }
125 } // namespace clangd
126 } // namespace clang
127