//===----------------------------------------------------------------------===// // // 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::create(const Twine &Path, uint64_t Capacity, function_ref 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(std::move(Alloc))); } Error DatabaseFile::addTable(TableHandle Table) { assert(Table); assert(&Table.getRegion() == &getRegion()); int64_t ExistingRootOffset = 0; const int64_t NewOffset = reinterpret_cast(&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 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
(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(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(); }