aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CAS/DatabaseFile.cpp
blob: db8ce1dc5bb14da666f21d88c773759d412e1f58 (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
//===----------------------------------------------------------------------===//
//
// 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();
}