blob: 609e5f135719080f07b15e2c52d6ff98a5a96f1e [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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 declares the common interface for a DatabaseFile that is used to
/// implement OnDiskCAS.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_CAS_DATABASEFILE_H
#define LLVM_LIB_CAS_DATABASEFILE_H
#include "llvm/ADT/StringRef.h"
#include "llvm/CAS/MappedFileRegionArena.h"
#include "llvm/Support/Error.h"
namespace llvm::cas::ondisk {
using MappedFileRegion = MappedFileRegionArena::RegionT;
/// Generic handle for a table.
///
/// Generic table header layout:
/// - 2-bytes: TableKind
/// - 2-bytes: TableNameSize
/// - 4-bytes: TableNameRelOffset (relative to header)
class TableHandle {
public:
enum class TableKind : uint16_t {
TrieRawHashMap = 1,
DataAllocator = 2,
};
struct Header {
TableKind Kind;
uint16_t NameSize;
int32_t NameRelOffset; ///< Relative to Header.
};
explicit operator bool() const { return H; }
const Header &getHeader() const { return *H; }
MappedFileRegion &getRegion() const { return *Region; }
template <class T> static void check() {
static_assert(
std::is_same<decltype(T::Header::GenericHeader), Header>::value,
"T::GenericHeader should be of type TableHandle::Header");
static_assert(offsetof(typename T::Header, GenericHeader) == 0,
"T::GenericHeader must be the head of T::Header");
}
template <class T> bool is() const { return T::Kind == H->Kind; }
template <class T> T dyn_cast() const {
check<T>();
if (is<T>())
return T(*Region, *reinterpret_cast<typename T::Header *>(H));
return T();
}
template <class T> T cast() const {
assert(is<T>());
return dyn_cast<T>();
}
StringRef getName() const {
auto *Begin = reinterpret_cast<const char *>(H) + H->NameRelOffset;
return StringRef(Begin, H->NameSize);
}
TableHandle() = default;
TableHandle(MappedFileRegion &Region, Header &H) : Region(&Region), H(&H) {}
TableHandle(MappedFileRegion &Region, intptr_t HeaderOffset)
: TableHandle(Region,
*reinterpret_cast<Header *>(Region.data() + HeaderOffset)) {
}
private:
MappedFileRegion *Region = nullptr;
Header *H = nullptr;
};
/// Encapsulate a database file, which:
/// - Sets/checks magic.
/// - Sets/checks version.
/// - Points at an arbitrary root table.
/// - Sets up a MappedFileRegionArena for allocation.
///
/// Top-level layout:
/// - 4-bytes: Magic
/// - 4-bytes: Version
/// - 8-bytes: RootTableOffset (16-bits: Kind; 48-bits: Offset)
/// - 8-bytes: BumpPtr from MappedFileRegionArena
class DatabaseFile {
public:
static constexpr uint32_t getMagic() { return 0xDA7ABA53UL; }
static constexpr uint32_t getVersion() { return 1UL; }
struct Header {
uint32_t Magic;
uint32_t Version;
std::atomic<int64_t> RootTableOffset;
};
const Header &getHeader() { return *H; }
MappedFileRegionArena &getAlloc() { return Alloc; }
MappedFileRegion &getRegion() { return Alloc.getRegion(); }
/// Add a table. This is currently not thread safe and should be called inside
/// NewDBConstructor.
Error addTable(TableHandle Table);
/// Find a table. May return null.
std::optional<TableHandle> findTable(StringRef Name);
/// Create the DatabaseFile at Path with Capacity.
static Expected<DatabaseFile>
create(const Twine &Path, uint64_t Capacity,
function_ref<Error(DatabaseFile &)> NewDBConstructor);
size_t size() const { return Alloc.size(); }
private:
static Expected<DatabaseFile>
get(std::unique_ptr<MappedFileRegionArena> Alloc) {
if (Error E = validate(Alloc->getRegion()))
return std::move(E);
return DatabaseFile(std::move(Alloc));
}
static Error validate(MappedFileRegion &Region);
DatabaseFile(MappedFileRegionArena &Alloc)
: H(reinterpret_cast<Header *>(Alloc.data())), Alloc(Alloc) {}
DatabaseFile(std::unique_ptr<MappedFileRegionArena> Alloc)
: DatabaseFile(*Alloc) {
OwnedAlloc = std::move(Alloc);
}
Header *H = nullptr;
MappedFileRegionArena &Alloc;
std::unique_ptr<MappedFileRegionArena> OwnedAlloc;
};
Error createTableConfigError(std::errc ErrC, StringRef Path,
StringRef TableName, const Twine &Msg);
Error checkTable(StringRef Label, size_t Expected, size_t Observed,
StringRef Path, StringRef TrieName);
} // namespace llvm::cas::ondisk
#endif