237 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file Implements OnDiskDataAllocator.10///11//===----------------------------------------------------------------------===//12 13#include "llvm/CAS/OnDiskDataAllocator.h"14#include "DatabaseFile.h"15#include "llvm/Config/llvm-config.h"16 17using namespace llvm;18using namespace llvm::cas;19using namespace llvm::cas::ondisk;20 21#if LLVM_ENABLE_ONDISK_CAS22 23//===----------------------------------------------------------------------===//24// DataAllocator data structures.25//===----------------------------------------------------------------------===//26 27namespace {28/// DataAllocator table layout:29/// - [8-bytes: Generic table header]30/// - 8-bytes: AllocatorOffset (reserved for implementing free lists)31/// - 8-bytes: Size for user data header32/// - <user data buffer>33///34/// Record layout:35/// - <data>36class DataAllocatorHandle {37public:38 static constexpr TableHandle::TableKind Kind =39 TableHandle::TableKind::DataAllocator;40 41 struct Header {42 TableHandle::Header GenericHeader;43 std::atomic<int64_t> AllocatorOffset;44 const uint64_t UserHeaderSize;45 };46 47 operator TableHandle() const {48 if (!H)49 return TableHandle();50 return TableHandle(*Region, H->GenericHeader);51 }52 53 Expected<MutableArrayRef<char>> allocate(MappedFileRegionArena &Alloc,54 size_t DataSize) {55 assert(&Alloc.getRegion() == Region);56 auto Ptr = Alloc.allocate(DataSize);57 if (LLVM_UNLIKELY(!Ptr))58 return Ptr.takeError();59 return MutableArrayRef(*Ptr, DataSize);60 }61 62 explicit operator bool() const { return H; }63 const Header &getHeader() const { return *H; }64 MappedFileRegion &getRegion() const { return *Region; }65 66 MutableArrayRef<uint8_t> getUserHeader() {67 return MutableArrayRef(reinterpret_cast<uint8_t *>(H + 1),68 H->UserHeaderSize);69 }70 71 static Expected<DataAllocatorHandle>72 create(MappedFileRegionArena &Alloc, StringRef Name, uint32_t UserHeaderSize);73 74 DataAllocatorHandle() = default;75 DataAllocatorHandle(MappedFileRegion &Region, Header &H)76 : Region(&Region), H(&H) {}77 DataAllocatorHandle(MappedFileRegion &Region, intptr_t HeaderOffset)78 : DataAllocatorHandle(79 Region, *reinterpret_cast<Header *>(Region.data() + HeaderOffset)) {80 }81 82private:83 MappedFileRegion *Region = nullptr;84 Header *H = nullptr;85};86 87} // end anonymous namespace88 89struct OnDiskDataAllocator::ImplType {90 DatabaseFile File;91 DataAllocatorHandle Store;92};93 94Expected<DataAllocatorHandle>95DataAllocatorHandle::create(MappedFileRegionArena &Alloc, StringRef Name,96 uint32_t UserHeaderSize) {97 // Allocate.98 auto Offset =99 Alloc.allocateOffset(sizeof(Header) + UserHeaderSize + Name.size() + 1);100 if (LLVM_UNLIKELY(!Offset))101 return Offset.takeError();102 103 // Construct the header and the name.104 assert(Name.size() <= UINT16_MAX && "Expected smaller table name");105 auto *H = new (Alloc.getRegion().data() + *Offset)106 Header{{TableHandle::TableKind::DataAllocator,107 static_cast<uint16_t>(Name.size()),108 static_cast<int32_t>(sizeof(Header) + UserHeaderSize)},109 /*AllocatorOffset=*/{0},110 /*UserHeaderSize=*/UserHeaderSize};111 // Memset UserHeader.112 char *UserHeader = reinterpret_cast<char *>(H + 1);113 memset(UserHeader, 0, UserHeaderSize);114 // Write database file name (null-terminated).115 char *NameStorage = UserHeader + UserHeaderSize;116 llvm::copy(Name, NameStorage);117 NameStorage[Name.size()] = 0;118 return DataAllocatorHandle(Alloc.getRegion(), *H);119}120 121Expected<OnDiskDataAllocator> OnDiskDataAllocator::create(122 const Twine &PathTwine, const Twine &TableNameTwine, uint64_t MaxFileSize,123 std::optional<uint64_t> NewFileInitialSize, uint32_t UserHeaderSize,124 function_ref<void(void *)> UserHeaderInit) {125 assert(!UserHeaderSize || UserHeaderInit);126 SmallString<128> PathStorage;127 StringRef Path = PathTwine.toStringRef(PathStorage);128 SmallString<128> TableNameStorage;129 StringRef TableName = TableNameTwine.toStringRef(TableNameStorage);130 131 // Constructor for if the file doesn't exist.132 auto NewDBConstructor = [&](DatabaseFile &DB) -> Error {133 auto Store =134 DataAllocatorHandle::create(DB.getAlloc(), TableName, UserHeaderSize);135 if (LLVM_UNLIKELY(!Store))136 return Store.takeError();137 138 if (auto E = DB.addTable(*Store))139 return E;140 141 if (UserHeaderSize)142 UserHeaderInit(Store->getUserHeader().data());143 return Error::success();144 };145 146 // Get or create the file.147 Expected<DatabaseFile> File =148 DatabaseFile::create(Path, MaxFileSize, NewDBConstructor);149 if (!File)150 return File.takeError();151 152 // Find the table and validate it.153 std::optional<TableHandle> Table = File->findTable(TableName);154 if (!Table)155 return createTableConfigError(std::errc::argument_out_of_domain, Path,156 TableName, "table not found");157 if (Error E = checkTable("table kind", (size_t)DataAllocatorHandle::Kind,158 (size_t)Table->getHeader().Kind, Path, TableName))159 return std::move(E);160 auto Store = Table->cast<DataAllocatorHandle>();161 assert(Store && "Already checked the kind");162 163 // Success.164 OnDiskDataAllocator::ImplType Impl{DatabaseFile(std::move(*File)), Store};165 return OnDiskDataAllocator(std::make_unique<ImplType>(std::move(Impl)));166}167 168Expected<OnDiskDataAllocator::OnDiskPtr>169OnDiskDataAllocator::allocate(size_t Size) {170 auto Data = Impl->Store.allocate(Impl->File.getAlloc(), Size);171 if (LLVM_UNLIKELY(!Data))172 return Data.takeError();173 174 return OnDiskPtr(FileOffset(Data->data() - Impl->Store.getRegion().data()),175 *Data);176}177 178Expected<ArrayRef<char>> OnDiskDataAllocator::get(FileOffset Offset,179 size_t Size) const {180 assert(Offset);181 assert(Impl);182 if (Offset.get() + Size >= Impl->File.getAlloc().size())183 return createStringError(make_error_code(std::errc::protocol_error),184 "requested size too large in allocator");185 return ArrayRef<char>{Impl->File.getRegion().data() + Offset.get(), Size};186}187 188MutableArrayRef<uint8_t> OnDiskDataAllocator::getUserHeader() const {189 return Impl->Store.getUserHeader();190}191 192size_t OnDiskDataAllocator::size() const { return Impl->File.size(); }193size_t OnDiskDataAllocator::capacity() const {194 return Impl->File.getRegion().size();195}196 197OnDiskDataAllocator::OnDiskDataAllocator(std::unique_ptr<ImplType> Impl)198 : Impl(std::move(Impl)) {}199 200#else // !LLVM_ENABLE_ONDISK_CAS201 202struct OnDiskDataAllocator::ImplType {};203 204Expected<OnDiskDataAllocator> OnDiskDataAllocator::create(205 const Twine &Path, const Twine &TableName, uint64_t MaxFileSize,206 std::optional<uint64_t> NewFileInitialSize, uint32_t UserHeaderSize,207 function_ref<void(void *)> UserHeaderInit) {208 return createStringError(make_error_code(std::errc::not_supported),209 "OnDiskDataAllocator is not supported");210}211 212Expected<OnDiskDataAllocator::OnDiskPtr>213OnDiskDataAllocator::allocate(size_t Size) {214 return createStringError(make_error_code(std::errc::not_supported),215 "OnDiskDataAllocator is not supported");216}217 218Expected<ArrayRef<char>> OnDiskDataAllocator::get(FileOffset Offset,219 size_t Size) const {220 return createStringError(make_error_code(std::errc::not_supported),221 "OnDiskDataAllocator is not supported");222}223 224MutableArrayRef<uint8_t> OnDiskDataAllocator::getUserHeader() const {225 return {};226}227 228size_t OnDiskDataAllocator::size() const { return 0; }229size_t OnDiskDataAllocator::capacity() const { return 0; }230 231#endif // LLVM_ENABLE_ONDISK_CAS232 233OnDiskDataAllocator::OnDiskDataAllocator(OnDiskDataAllocator &&RHS) = default;234OnDiskDataAllocator &235OnDiskDataAllocator::operator=(OnDiskDataAllocator &&RHS) = default;236OnDiskDataAllocator::~OnDiskDataAllocator() = default;237