aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CAS/OnDiskDataAllocator.cpp
blob: 13bbd6613917890dff9d99da0a95952ef5ca5101 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file Implements OnDiskDataAllocator.
///
//===----------------------------------------------------------------------===//

#include "llvm/CAS/OnDiskDataAllocator.h"
#include "DatabaseFile.h"
#include "llvm/Config/llvm-config.h"

using namespace llvm;
using namespace llvm::cas;
using namespace llvm::cas::ondisk;

#if LLVM_ENABLE_ONDISK_CAS

//===----------------------------------------------------------------------===//
// DataAllocator data structures.
//===----------------------------------------------------------------------===//

namespace {
/// DataAllocator table layout:
/// - [8-bytes: Generic table header]
/// - 8-bytes: AllocatorOffset (reserved for implementing free lists)
/// - 8-bytes: Size for user data header
/// - <user data buffer>
///
/// Record layout:
/// - <data>
class DataAllocatorHandle {
public:
  static constexpr TableHandle::TableKind Kind =
      TableHandle::TableKind::DataAllocator;

  struct Header {
    TableHandle::Header GenericHeader;
    std::atomic<int64_t> AllocatorOffset;
    const uint64_t UserHeaderSize;
  };

  operator TableHandle() const {
    if (!H)
      return TableHandle();
    return TableHandle(*Region, H->GenericHeader);
  }

  Expected<MutableArrayRef<char>> allocate(MappedFileRegionArena &Alloc,
                                           size_t DataSize) {
    assert(&Alloc.getRegion() == Region);
    auto Ptr = Alloc.allocate(DataSize);
    if (LLVM_UNLIKELY(!Ptr))
      return Ptr.takeError();
    return MutableArrayRef(*Ptr, DataSize);
  }

  explicit operator bool() const { return H; }
  const Header &getHeader() const { return *H; }
  MappedFileRegion &getRegion() const { return *Region; }

  MutableArrayRef<uint8_t> getUserHeader() {
    return MutableArrayRef(reinterpret_cast<uint8_t *>(H + 1),
                           H->UserHeaderSize);
  }

  static Expected<DataAllocatorHandle>
  create(MappedFileRegionArena &Alloc, StringRef Name, uint32_t UserHeaderSize);

  DataAllocatorHandle() = default;
  DataAllocatorHandle(MappedFileRegion &Region, Header &H)
      : Region(&Region), H(&H) {}
  DataAllocatorHandle(MappedFileRegion &Region, intptr_t HeaderOffset)
      : DataAllocatorHandle(
            Region, *reinterpret_cast<Header *>(Region.data() + HeaderOffset)) {
  }

private:
  MappedFileRegion *Region = nullptr;
  Header *H = nullptr;
};

} // end anonymous namespace

struct OnDiskDataAllocator::ImplType {
  DatabaseFile File;
  DataAllocatorHandle Store;
};

Expected<DataAllocatorHandle>
DataAllocatorHandle::create(MappedFileRegionArena &Alloc, StringRef Name,
                            uint32_t UserHeaderSize) {
  // Allocate.
  auto Offset =
      Alloc.allocateOffset(sizeof(Header) + UserHeaderSize + Name.size() + 1);
  if (LLVM_UNLIKELY(!Offset))
    return Offset.takeError();

  // Construct the header and the name.
  assert(Name.size() <= UINT16_MAX && "Expected smaller table name");
  auto *H = new (Alloc.getRegion().data() + *Offset)
      Header{{TableHandle::TableKind::DataAllocator,
              static_cast<uint16_t>(Name.size()),
              static_cast<int32_t>(sizeof(Header) + UserHeaderSize)},
             /*AllocatorOffset=*/{0},
             /*UserHeaderSize=*/UserHeaderSize};
  // Memset UserHeader.
  char *UserHeader = reinterpret_cast<char *>(H + 1);
  memset(UserHeader, 0, UserHeaderSize);
  // Write database file name (null-terminated).
  char *NameStorage = UserHeader + UserHeaderSize;
  llvm::copy(Name, NameStorage);
  NameStorage[Name.size()] = 0;
  return DataAllocatorHandle(Alloc.getRegion(), *H);
}

Expected<OnDiskDataAllocator> OnDiskDataAllocator::create(
    const Twine &PathTwine, const Twine &TableNameTwine, uint64_t MaxFileSize,
    std::optional<uint64_t> NewFileInitialSize, uint32_t UserHeaderSize,
    function_ref<void(void *)> UserHeaderInit) {
  assert(!UserHeaderSize || UserHeaderInit);
  SmallString<128> PathStorage;
  StringRef Path = PathTwine.toStringRef(PathStorage);
  SmallString<128> TableNameStorage;
  StringRef TableName = TableNameTwine.toStringRef(TableNameStorage);

  // Constructor for if the file doesn't exist.
  auto NewDBConstructor = [&](DatabaseFile &DB) -> Error {
    auto Store =
        DataAllocatorHandle::create(DB.getAlloc(), TableName, UserHeaderSize);
    if (LLVM_UNLIKELY(!Store))
      return Store.takeError();

    if (auto E = DB.addTable(*Store))
      return E;

    if (UserHeaderSize)
      UserHeaderInit(Store->getUserHeader().data());
    return Error::success();
  };

  // Get or create the file.
  Expected<DatabaseFile> File =
      DatabaseFile::create(Path, MaxFileSize, NewDBConstructor);
  if (!File)
    return File.takeError();

  // Find the table and validate it.
  std::optional<TableHandle> Table = File->findTable(TableName);
  if (!Table)
    return createTableConfigError(std::errc::argument_out_of_domain, Path,
                                  TableName, "table not found");
  if (Error E = checkTable("table kind", (size_t)DataAllocatorHandle::Kind,
                           (size_t)Table->getHeader().Kind, Path, TableName))
    return std::move(E);
  auto Store = Table->cast<DataAllocatorHandle>();
  assert(Store && "Already checked the kind");

  // Success.
  OnDiskDataAllocator::ImplType Impl{DatabaseFile(std::move(*File)), Store};
  return OnDiskDataAllocator(std::make_unique<ImplType>(std::move(Impl)));
}

Expected<OnDiskDataAllocator::OnDiskPtr>
OnDiskDataAllocator::allocate(size_t Size) {
  auto Data = Impl->Store.allocate(Impl->File.getAlloc(), Size);
  if (LLVM_UNLIKELY(!Data))
    return Data.takeError();

  return OnDiskPtr(FileOffset(Data->data() - Impl->Store.getRegion().data()),
                   *Data);
}

Expected<ArrayRef<char>> OnDiskDataAllocator::get(FileOffset Offset,
                                                  size_t Size) const {
  assert(Offset);
  assert(Impl);
  if (Offset.get() + Size >= Impl->File.getAlloc().size())
    return createStringError(make_error_code(std::errc::protocol_error),
                             "requested size too large in allocator");
  return ArrayRef<char>{Impl->File.getRegion().data() + Offset.get(), Size};
}

MutableArrayRef<uint8_t> OnDiskDataAllocator::getUserHeader() {
  return Impl->Store.getUserHeader();
}

size_t OnDiskDataAllocator::size() const { return Impl->File.size(); }
size_t OnDiskDataAllocator::capacity() const {
  return Impl->File.getRegion().size();
}

OnDiskDataAllocator::OnDiskDataAllocator(std::unique_ptr<ImplType> Impl)
    : Impl(std::move(Impl)) {}

#else // !LLVM_ENABLE_ONDISK_CAS

struct OnDiskDataAllocator::ImplType {};

Expected<OnDiskDataAllocator> OnDiskDataAllocator::create(
    const Twine &Path, const Twine &TableName, uint64_t MaxFileSize,
    std::optional<uint64_t> NewFileInitialSize, uint32_t UserHeaderSize,
    function_ref<void(void *)> UserHeaderInit) {
  return createStringError(make_error_code(std::errc::not_supported),
                           "OnDiskDataAllocator is not supported");
}

Expected<OnDiskDataAllocator::OnDiskPtr>
OnDiskDataAllocator::allocate(size_t Size) {
  return createStringError(make_error_code(std::errc::not_supported),
                           "OnDiskDataAllocator is not supported");
}

Expected<ArrayRef<char>> OnDiskDataAllocator::get(FileOffset Offset,
                                                  size_t Size) const {
  return createStringError(make_error_code(std::errc::not_supported),
                           "OnDiskDataAllocator is not supported");
}

MutableArrayRef<uint8_t> OnDiskDataAllocator::getUserHeader() { return {}; }

size_t OnDiskDataAllocator::size() const { return 0; }
size_t OnDiskDataAllocator::capacity() const { return 0; }

#endif // LLVM_ENABLE_ONDISK_CAS

OnDiskDataAllocator::OnDiskDataAllocator(OnDiskDataAllocator &&RHS) = default;
OnDiskDataAllocator &
OnDiskDataAllocator::operator=(OnDiskDataAllocator &&RHS) = default;
OnDiskDataAllocator::~OnDiskDataAllocator() = default;