| //===----------------------------------------------------------------------===// |
| // |
| // 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 This file implements the common abstractions for CAS database file. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "DatabaseFile.h" |
| |
| using namespace llvm; |
| using namespace llvm::cas; |
| using namespace llvm::cas::ondisk; |
| |
| Error ondisk::createTableConfigError(std::errc ErrC, StringRef Path, |
| StringRef TableName, const Twine &Msg) { |
| return createStringError(make_error_code(ErrC), |
| Path + "[" + TableName + "]: " + Msg); |
| } |
| |
| Error ondisk::checkTable(StringRef Label, size_t Expected, size_t Observed, |
| StringRef Path, StringRef TrieName) { |
| if (Expected == Observed) |
| return Error::success(); |
| return createTableConfigError(std::errc::invalid_argument, Path, TrieName, |
| "mismatched " + Label + |
| " (expected: " + Twine(Expected) + |
| ", observed: " + Twine(Observed) + ")"); |
| } |
| |
| Expected<DatabaseFile> |
| DatabaseFile::create(const Twine &Path, uint64_t Capacity, |
| function_ref<Error(DatabaseFile &)> NewDBConstructor) { |
| // Constructor for if the file doesn't exist. |
| auto NewFileConstructor = [&](MappedFileRegionArena &Alloc) -> Error { |
| if (Alloc.capacity() < |
| sizeof(Header) + sizeof(MappedFileRegionArena::Header)) |
| return createTableConfigError(std::errc::argument_out_of_domain, |
| Path.str(), "datafile", |
| "Allocator too small for header"); |
| (void)new (Alloc.data()) Header{getMagic(), getVersion(), {0}}; |
| DatabaseFile DB(Alloc); |
| return NewDBConstructor(DB); |
| }; |
| |
| // Get or create the file. |
| MappedFileRegionArena Alloc; |
| if (Error E = MappedFileRegionArena::create(Path, Capacity, sizeof(Header), |
| NewFileConstructor) |
| .moveInto(Alloc)) |
| return std::move(E); |
| |
| return DatabaseFile::get( |
| std::make_unique<MappedFileRegionArena>(std::move(Alloc))); |
| } |
| |
| Error DatabaseFile::addTable(TableHandle Table) { |
| assert(Table); |
| assert(&Table.getRegion() == &getRegion()); |
| int64_t ExistingRootOffset = 0; |
| const int64_t NewOffset = |
| reinterpret_cast<const char *>(&Table.getHeader()) - getRegion().data(); |
| if (H->RootTableOffset.compare_exchange_strong(ExistingRootOffset, NewOffset)) |
| return Error::success(); |
| |
| // Silently ignore attempts to set the root to itself. |
| if (ExistingRootOffset == NewOffset) |
| return Error::success(); |
| |
| // Return an proper error message. |
| TableHandle Root(getRegion(), ExistingRootOffset); |
| if (Root.getName() == Table.getName()) |
| return createStringError( |
| make_error_code(std::errc::not_supported), |
| "collision with existing table of the same name '" + Table.getName() + |
| "'"); |
| |
| return createStringError(make_error_code(std::errc::not_supported), |
| "cannot add new table '" + Table.getName() + |
| "'" |
| " to existing root '" + |
| Root.getName() + "'"); |
| } |
| |
| std::optional<TableHandle> DatabaseFile::findTable(StringRef Name) { |
| int64_t RootTableOffset = H->RootTableOffset.load(); |
| if (!RootTableOffset) |
| return std::nullopt; |
| |
| TableHandle Root(getRegion(), RootTableOffset); |
| if (Root.getName() == Name) |
| return Root; |
| |
| return std::nullopt; |
| } |
| |
| Error DatabaseFile::validate(MappedFileRegion &Region) { |
| if (Region.size() < sizeof(Header)) |
| return createStringError(std::errc::invalid_argument, |
| "database: missing header"); |
| |
| // Check the magic and version. |
| auto *H = reinterpret_cast<Header *>(Region.data()); |
| if (H->Magic != getMagic()) |
| return createStringError(std::errc::invalid_argument, |
| "database: bad magic"); |
| if (H->Version != getVersion()) |
| return createStringError(std::errc::invalid_argument, |
| "database: wrong version"); |
| |
| auto *MFH = reinterpret_cast<MappedFileRegionArena::Header *>(Region.data() + |
| sizeof(Header)); |
| // Check the bump-ptr, which should point past the header. |
| if (MFH->BumpPtr.load() < (int64_t)sizeof(Header)) |
| return createStringError(std::errc::invalid_argument, |
| "database: corrupt bump-ptr"); |
| |
| return Error::success(); |
| } |