aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/CAS/DatabaseFile.h
blob: 609e5f135719080f07b15e2c52d6ff98a5a96f1e (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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//===----------------------------------------------------------------------===//
//
// 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