70#define DEBUG_TYPE "on-disk-cas"
88 return ID.takeError();
91 "corrupt object '" +
toHex(*ID) +
"'");
101 enum class StorageKind : uint8_t {
118 StandaloneLeaf0 = 12,
121 static StringRef getStandaloneFilePrefix(StorageKind SK) {
125 case TrieRecord::StorageKind::Standalone:
127 case TrieRecord::StorageKind::StandaloneLeaf:
129 case TrieRecord::StorageKind::StandaloneLeaf0:
134 enum Limits : int64_t {
136 MaxEmbeddedSize = 64LL * 1024LL - 1,
140 StorageKind SK = StorageKind::Unknown;
145 static uint64_t pack(Data
D) {
146 assert(
D.Offset.get() < (int64_t)(1ULL << 56));
147 uint64_t
Packed = uint64_t(
D.SK) << 56 |
D.Offset.get();
148 assert(
D.SK != StorageKind::Unknown || Packed == 0);
150 Data RoundTrip = unpack(Packed);
152 assert(
D.Offset.get() == RoundTrip.Offset.get());
158 static Data unpack(uint64_t Packed) {
162 D.SK = (StorageKind)(Packed >> 56);
167 TrieRecord() : Storage(0) {}
169 Data
load()
const {
return unpack(Storage); }
170 bool compare_exchange_strong(Data &Existing, Data New);
173 std::atomic<uint64_t> Storage;
183struct DataRecordHandle {
186 enum class NumRefsFlags : uint8_t {
196 enum class DataSizeFlags {
205 enum class RefKindFlags {
214 DataSizeShift = NumRefsShift + NumRefsBits,
216 RefKindShift = DataSizeShift + DataSizeBits,
219 static_assert(((UINT32_MAX << NumRefsBits) & (uint32_t)NumRefsFlags::Max) ==
222 static_assert(((UINT32_MAX << DataSizeBits) & (uint32_t)DataSizeFlags::Max) ==
225 static_assert(((UINT32_MAX << RefKindBits) & (uint32_t)RefKindFlags::Max) ==
231 NumRefsFlags NumRefs;
232 DataSizeFlags DataSize;
233 RefKindFlags RefKind;
235 static uint64_t pack(LayoutFlags LF) {
236 unsigned Packed = ((unsigned)LF.NumRefs << NumRefsShift) |
237 ((
unsigned)LF.DataSize << DataSizeShift) |
238 ((unsigned)LF.RefKind << RefKindShift);
240 LayoutFlags RoundTrip = unpack(Packed);
241 assert(LF.NumRefs == RoundTrip.NumRefs);
242 assert(LF.DataSize == RoundTrip.DataSize);
243 assert(LF.RefKind == RoundTrip.RefKind);
247 static LayoutFlags unpack(uint64_t Storage) {
248 assert(Storage <= UINT8_MAX &&
"Expect storage to fit in a byte");
251 (NumRefsFlags)((Storage >> NumRefsShift) & ((1U << NumRefsBits) - 1));
252 LF.DataSize = (DataSizeFlags)((Storage >> DataSizeShift) &
253 ((1U << DataSizeBits) - 1));
255 (RefKindFlags)((Storage >> RefKindShift) & ((1U << RefKindBits) - 1));
265 using PackTy = uint32_t;
268 static constexpr unsigned LayoutFlagsShift =
269 (
sizeof(PackTy) - 1) * CHAR_BIT;
273 InternalRefArrayRef Refs;
277 LayoutFlags getLayoutFlags()
const {
278 return LayoutFlags::unpack(H->Packed >> Header::LayoutFlagsShift);
282 void skipDataSize(LayoutFlags LF, int64_t &RelOffset)
const;
283 uint32_t getNumRefs()
const;
284 void skipNumRefs(LayoutFlags LF, int64_t &RelOffset)
const;
285 int64_t getRefsRelOffset()
const;
286 int64_t getDataRelOffset()
const;
288 static uint64_t getTotalSize(uint64_t DataRelOffset, uint64_t DataSize) {
289 return DataRelOffset + DataSize + 1;
291 uint64_t getTotalSize()
const {
298 explicit Layout(
const Input &
I);
301 uint64_t DataSize = 0;
302 uint32_t NumRefs = 0;
303 int64_t RefsRelOffset = 0;
304 int64_t DataRelOffset = 0;
305 uint64_t getTotalSize()
const {
306 return DataRecordHandle::getTotalSize(DataRelOffset, DataSize);
310 InternalRefArrayRef getRefs()
const {
311 assert(H &&
"Expected valid handle");
312 auto *BeginByte =
reinterpret_cast<const char *
>(H) + getRefsRelOffset();
313 size_t Size = getNumRefs();
315 return InternalRefArrayRef();
316 if (getLayoutFlags().RefKind == RefKindFlags::InternalRef4B)
317 return ArrayRef(
reinterpret_cast<const InternalRef4B *
>(BeginByte),
Size);
318 return ArrayRef(
reinterpret_cast<const InternalRef *
>(BeginByte),
Size);
321 ArrayRef<char> getData()
const {
322 assert(H &&
"Expected valid handle");
323 return ArrayRef(
reinterpret_cast<const char *
>(H) + getDataRelOffset(),
327 static DataRecordHandle create(function_ref<
char *(
size_t Size)>
Alloc,
329 static Expected<DataRecordHandle>
330 createWithError(function_ref<Expected<char *>(
size_t Size)>
Alloc,
332 static DataRecordHandle construct(
char *Mem,
const Input &
I);
334 static DataRecordHandle
get(
const char *Mem) {
335 return DataRecordHandle(
336 *
reinterpret_cast<const DataRecordHandle::Header *
>(Mem));
338 static Expected<DataRecordHandle>
339 getFromDataPool(
const OnDiskDataAllocator &Pool, FileOffset
Offset);
341 explicit operator bool()
const {
return H; }
342 const Header &getHeader()
const {
return *H; }
344 DataRecordHandle() =
default;
345 explicit DataRecordHandle(
const Header &H) : H(&H) {}
348 static DataRecordHandle constructImpl(
char *Mem,
const Input &
I,
350 const Header *H =
nullptr;
354struct OnDiskContent {
355 std::optional<DataRecordHandle> Record;
356 std::optional<ArrayRef<char>> Bytes;
358 ArrayRef<char> getData()
const {
361 assert(Record &&
"Expected record or bytes");
362 return Record->getData();
367class StandaloneDataInMemory {
369 OnDiskContent getContent()
const;
371 OnDiskGraphDB::FileBackedData
372 getInternalFileBackedObjectData(StringRef RootPath)
const;
379 std::unique_ptr<MemoryBuffer>
380 getStandaloneMemoryBuffer(StringRef RootPath, StringRef Name,
381 bool RequiresNullTerminator)
const;
383 StandaloneDataInMemory(std::unique_ptr<sys::fs::mapped_file_region> Region,
384 TrieRecord::StorageKind SK, FileOffset IndexOffset)
385 : Region(std::
move(Region)), SK(SK), IndexOffset(IndexOffset) {
387 bool IsStandalone =
false;
389 case TrieRecord::StorageKind::Standalone:
390 case TrieRecord::StorageKind::StandaloneLeaf:
391 case TrieRecord::StorageKind::StandaloneLeaf0:
402 std::unique_ptr<sys::fs::mapped_file_region> Region;
403 TrieRecord::StorageKind SK;
404 FileOffset IndexOffset;
408template <
size_t NumShards>
class StandaloneDataMap {
409 static_assert(
isPowerOf2_64(NumShards),
"Expected power of 2");
412 uintptr_t insert(ArrayRef<uint8_t> Hash, TrieRecord::StorageKind SK,
413 std::unique_ptr<sys::fs::mapped_file_region> Region,
414 FileOffset IndexOffset);
416 const StandaloneDataInMemory *
lookup(ArrayRef<uint8_t> Hash)
const;
417 bool count(ArrayRef<uint8_t> Hash)
const {
return bool(
lookup(Hash)); }
422 DenseMap<const uint8_t *, std::unique_ptr<StandaloneDataInMemory>> Map;
423 mutable std::mutex Mutex;
425 Shard &getShard(ArrayRef<uint8_t> Hash) {
426 return const_cast<Shard &
>(
427 const_cast<const StandaloneDataMap *
>(
this)->getShard(Hash));
429 const Shard &getShard(ArrayRef<uint8_t> Hash)
const {
430 static_assert(NumShards <= 256,
"Expected only 8 bits of shard");
431 return Shards[Hash[0] % NumShards];
434 Shard Shards[NumShards];
437using StandaloneDataMapTy = StandaloneDataMap<16>;
440class InternalRefVector {
442 void push_back(InternalRef
Ref) {
444 return FullRefs.push_back(
Ref);
446 return SmallRefs.push_back(*Small);
449 FullRefs.reserve(SmallRefs.size() + 1);
450 for (InternalRef4B Small : SmallRefs)
451 FullRefs.push_back(Small);
452 FullRefs.push_back(
Ref);
456 operator InternalRefArrayRef()
const {
457 assert(SmallRefs.empty() || FullRefs.empty());
458 return NeedsFull ? InternalRefArrayRef(FullRefs)
459 : InternalRefArrayRef(SmallRefs);
463 bool NeedsFull =
false;
473 if (Expected<char *> Mem =
Alloc(
L.getTotalSize()))
474 return constructImpl(*Mem,
I, L);
476 return Mem.takeError();
501 std::unique_ptr<sys::fs::mapped_file_region>
Region,
503 auto &S = getShard(Hash);
504 std::lock_guard<std::mutex> Lock(S.Mutex);
505 auto &V = S.Map[Hash.
data()];
507 V = std::make_unique<StandaloneDataInMemory>(std::move(
Region), SK,
509 return reinterpret_cast<uintptr_t>(V.get());
513const StandaloneDataInMemory *
515 auto &S = getShard(Hash);
516 std::lock_guard<std::mutex> Lock(S.Mutex);
517 auto I = S.Map.find(Hash.
data());
518 if (
I == S.Map.end())
538 TempFile(TempFile &&
Other) { *
this = std::move(
Other); }
539 TempFile &operator=(TempFile &&
Other) {
540 TmpName = std::move(
Other.TmpName);
554 OnDiskCASLogger *Logger =
nullptr;
557 Error keep(
const Twine &Name);
564class MappedTempFile {
566 char *
data()
const {
return Map.
data(); }
567 size_t size()
const {
return Map.
size(); }
570 assert(Map &&
"Map already destroyed");
572 return Temp.discard();
575 Error keep(
const Twine &Name) {
576 assert(Map &&
"Map already destroyed");
578 return Temp.keep(Name);
581 MappedTempFile(TempFile Temp, sys::fs::mapped_file_region Map)
586 sys::fs::mapped_file_region Map;
600 std::error_code RemoveEC;
641 Logger->logTempFileCreate(ResultPath);
643 TempFile Ret(ResultPath,
FD,
Logger);
644 return std::move(Ret);
647bool TrieRecord::compare_exchange_strong(
Data &Existing,
Data New) {
648 uint64_t ExistingPacked = pack(Existing);
650 if (Storage.compare_exchange_strong(ExistingPacked, NewPacked))
652 Existing = unpack(ExistingPacked);
659 auto HeaderData = Pool.
get(
Offset,
sizeof(DataRecordHandle::Header));
661 return HeaderData.takeError();
663 auto Record = DataRecordHandle::get(HeaderData->data());
667 "data record span passed the end of the data pool");
672DataRecordHandle DataRecordHandle::constructImpl(
char *Mem,
const Input &
I,
674 char *
Next = Mem +
sizeof(Header);
677 Header::PackTy Packed = 0;
678 Packed |= LayoutFlags::pack(L.Flags) << Header::LayoutFlagsShift;
681 switch (L.Flags.DataSize) {
682 case DataSizeFlags::Uses1B:
683 assert(
I.Data.size() <= UINT8_MAX);
684 Packed |= (Header::PackTy)
I.Data.size()
685 << ((
sizeof(Packed) - 2) * CHAR_BIT);
687 case DataSizeFlags::Uses2B:
688 assert(
I.Data.size() <= UINT16_MAX);
689 Packed |= (Header::PackTy)
I.Data.size()
690 << ((
sizeof(Packed) - 4) * CHAR_BIT);
692 case DataSizeFlags::Uses4B:
696 case DataSizeFlags::Uses8B:
706 switch (L.Flags.NumRefs) {
707 case NumRefsFlags::Uses0B:
709 case NumRefsFlags::Uses1B:
710 assert(
I.Refs.size() <= UINT8_MAX);
711 Packed |= (Header::PackTy)
I.Refs.size()
712 << ((
sizeof(Packed) - 2) * CHAR_BIT);
714 case NumRefsFlags::Uses2B:
715 assert(
I.Refs.size() <= UINT16_MAX);
716 Packed |= (Header::PackTy)
I.Refs.size()
717 << ((
sizeof(Packed) - 4) * CHAR_BIT);
719 case NumRefsFlags::Uses4B:
723 case NumRefsFlags::Uses8B:
730 if (!
I.Refs.empty()) {
731 assert((
L.Flags.RefKind == RefKindFlags::InternalRef4B) ==
I.Refs.is4B());
732 ArrayRef<uint8_t> RefsBuffer =
I.Refs.getBuffer();
740 Next[
I.Data.size()] = 0;
743 Header *
H =
new (Mem) Header{
Packed};
748 assert(
Record.getLayoutFlags().DataSize ==
L.Flags.DataSize);
754DataRecordHandle::Layout::Layout(
const Input &
I) {
756 uint64_t RelOffset =
sizeof(Header);
759 DataSize =
I.Data.size();
760 NumRefs =
I.Refs.size();
764 I.Refs.is4B() ? RefKindFlags::InternalRef4B : RefKindFlags::InternalRef;
769 if (DataSize <= UINT8_MAX && Has1B) {
770 Flags.DataSize = DataSizeFlags::Uses1B;
772 }
else if (DataSize <= UINT16_MAX && Has2B) {
773 Flags.DataSize = DataSizeFlags::Uses2B;
775 }
else if (DataSize <= UINT32_MAX) {
776 Flags.DataSize = DataSizeFlags::Uses4B;
779 Flags.DataSize = DataSizeFlags::Uses8B;
785 Flags.NumRefs = NumRefsFlags::Uses0B;
786 }
else if (NumRefs <= UINT8_MAX && Has1B) {
787 Flags.NumRefs = NumRefsFlags::Uses1B;
789 }
else if (NumRefs <= UINT16_MAX && Has2B) {
790 Flags.NumRefs = NumRefsFlags::Uses2B;
793 Flags.NumRefs = NumRefsFlags::Uses4B;
804 auto GrowSizeFieldsBy4B = [&]() {
808 assert(Flags.NumRefs != NumRefsFlags::Uses8B &&
809 "Expected to be able to grow NumRefs8B");
815 if (Flags.DataSize < DataSizeFlags::Uses4B)
816 Flags.DataSize = DataSizeFlags::Uses4B;
817 else if (Flags.DataSize < DataSizeFlags::Uses8B)
818 Flags.DataSize = DataSizeFlags::Uses8B;
819 else if (Flags.NumRefs < NumRefsFlags::Uses4B)
820 Flags.NumRefs = NumRefsFlags::Uses4B;
822 Flags.NumRefs = NumRefsFlags::Uses8B;
826 if (Flags.RefKind == RefKindFlags::InternalRef) {
830 GrowSizeFieldsBy4B();
833 RefsRelOffset = RelOffset;
834 RelOffset += 8 * NumRefs;
842 uint64_t RefListSize = 4 * NumRefs;
844 GrowSizeFieldsBy4B();
845 RefsRelOffset = RelOffset;
846 RelOffset += RefListSize;
850 DataRelOffset = RelOffset;
853uint64_t DataRecordHandle::getDataSize()
const {
854 int64_t RelOffset =
sizeof(Header);
855 auto *DataSizePtr =
reinterpret_cast<const char *
>(
H) + RelOffset;
856 switch (getLayoutFlags().DataSize) {
857 case DataSizeFlags::Uses1B:
858 return (
H->Packed >> ((
sizeof(Header::PackTy) - 2) * CHAR_BIT)) & UINT8_MAX;
859 case DataSizeFlags::Uses2B:
860 return (
H->Packed >> ((
sizeof(Header::PackTy) - 4) * CHAR_BIT)) &
862 case DataSizeFlags::Uses4B:
864 case DataSizeFlags::Uses8B:
870void DataRecordHandle::skipDataSize(LayoutFlags LF, int64_t &RelOffset)
const {
871 if (LF.DataSize >= DataSizeFlags::Uses4B)
873 if (LF.DataSize >= DataSizeFlags::Uses8B)
877uint32_t DataRecordHandle::getNumRefs()
const {
878 LayoutFlags LF = getLayoutFlags();
879 int64_t RelOffset =
sizeof(Header);
880 skipDataSize(LF, RelOffset);
881 auto *NumRefsPtr =
reinterpret_cast<const char *
>(
H) + RelOffset;
882 switch (LF.NumRefs) {
883 case NumRefsFlags::Uses0B:
885 case NumRefsFlags::Uses1B:
886 return (
H->Packed >> ((
sizeof(Header::PackTy) - 2) * CHAR_BIT)) & UINT8_MAX;
887 case NumRefsFlags::Uses2B:
888 return (
H->Packed >> ((
sizeof(Header::PackTy) - 4) * CHAR_BIT)) &
890 case NumRefsFlags::Uses4B:
892 case NumRefsFlags::Uses8B:
898void DataRecordHandle::skipNumRefs(LayoutFlags LF, int64_t &RelOffset)
const {
899 if (LF.NumRefs >= NumRefsFlags::Uses4B)
901 if (LF.NumRefs >= NumRefsFlags::Uses8B)
905int64_t DataRecordHandle::getRefsRelOffset()
const {
906 LayoutFlags LF = getLayoutFlags();
907 int64_t RelOffset =
sizeof(Header);
908 skipDataSize(LF, RelOffset);
909 skipNumRefs(LF, RelOffset);
913int64_t DataRecordHandle::getDataRelOffset()
const {
914 LayoutFlags LF = getLayoutFlags();
915 int64_t RelOffset =
sizeof(Header);
916 skipDataSize(LF, RelOffset);
917 skipNumRefs(LF, RelOffset);
918 uint32_t RefSize = LF.RefKind == RefKindFlags::InternalRef4B ? 4 : 8;
919 RelOffset += RefSize * getNumRefs();
925 if (
auto E = UpstreamDB->validate(Deep, Hasher))
930 "data pool bump pointer is not aligned");
942 if (
Record.Data.size() !=
sizeof(TrieRecord))
943 return formatError(
"wrong data record size");
945 return formatError(
"wrong data record alignment");
947 auto *R =
reinterpret_cast<const TrieRecord *
>(
Record.Data.data());
948 TrieRecord::Data
D = R->load();
949 std::unique_ptr<MemoryBuffer> FileBuffer;
955 return formatError(
"invalid record kind value");
958 auto I = getIndexProxyFromRef(
Ref);
960 return I.takeError();
963 case TrieRecord::StorageKind::Unknown:
968 case TrieRecord::StorageKind::DataPool: {
971 if (
D.Offset.get() <= 0 ||
972 D.Offset.get() +
sizeof(DataRecordHandle::Header) >= DataPool.size())
973 return formatError(
"datapool record out of bound");
977 return formatError(
"data record offset is not aligned");
981 DataPool.get(
D.Offset,
sizeof(DataRecordHandle::Header));
983 return formatError(
toString(HeaderData.takeError()));
984 auto LF = DataRecordHandle::get(HeaderData->data()).getLayoutFlags();
985 if (LF.NumRefs > DataRecordHandle::NumRefsFlags::Max ||
986 LF.DataSize > DataRecordHandle::DataSizeFlags::Max)
987 return formatError(
"data record has invalid layout flags");
990 case TrieRecord::StorageKind::Standalone:
991 case TrieRecord::StorageKind::StandaloneLeaf:
992 case TrieRecord::StorageKind::StandaloneLeaf0:
1001 if (!File || !*File)
1002 return formatError(
"record file \'" + Path +
"\' does not exist");
1004 FileBuffer = std::move(*File);
1006 return formatError(
"record file \'" + Path +
"\' does not exist");
1014 "bad data for digest \'" +
toHex(
I->Hash) +
1021 case TrieRecord::StorageKind::Unknown:
1023 case TrieRecord::StorageKind::DataPool: {
1024 auto DataRecord = DataRecordHandle::getFromDataPool(DataPool,
D.Offset);
1026 return dataError(
toString(DataRecord.takeError()));
1028 for (
auto InternRef : DataRecord->getRefs()) {
1029 if (InternRef.getFileOffset().get() <= 0)
1030 return dataError(
"invalid ref offset");
1031 auto Index = getIndexProxyFromRef(InternRef);
1033 return Index.takeError();
1036 StoredData = DataRecord->getData();
1039 case TrieRecord::StorageKind::Standalone: {
1040 if (FileBuffer->getBufferSize() <
sizeof(DataRecordHandle::Header))
1041 return dataError(
"data record is not big enough to read the header");
1042 auto DataRecord = DataRecordHandle::get(FileBuffer->getBufferStart());
1043 if (DataRecord.getTotalSize() < FileBuffer->getBufferSize())
1045 "data record span passed the end of the standalone file");
1046 for (
auto InternRef : DataRecord.getRefs()) {
1047 if (InternRef.getFileOffset().get() <= 0)
1048 return dataError(
"invalid ref offset");
1049 auto Index = getIndexProxyFromRef(InternRef);
1051 return Index.takeError();
1054 StoredData = DataRecord.getData();
1057 case TrieRecord::StorageKind::StandaloneLeaf:
1058 case TrieRecord::StorageKind::StandaloneLeaf0: {
1060 if (
D.SK == TrieRecord::StorageKind::StandaloneLeaf0) {
1061 if (!FileBuffer->getBuffer().ends_with(
'\0'))
1062 return dataError(
"standalone file is not zero terminated");
1070 Hasher(Refs, StoredData, ComputedHash);
1072 return dataError(
"hash mismatch, got \'" +
toHex(ComputedHash) +
1080 auto formatError = [&](
Twine Msg) {
1089 return formatError(
"zero is not a valid ref");
1099 return formatError(
"not found using hash " +
toHex(Hash));
1101 ObjectID OtherRef = getExternalReference(makeInternalRef(OtherI.
Offset));
1102 if (OtherRef != ExternalRef)
1103 return formatError(
"ref does not match indexed offset " +
1105 " for hash " +
toHex(Hash));
1110 OS <<
"on-disk-root-path: " << RootPath <<
"\n";
1122 auto *R =
reinterpret_cast<const TrieRecord *
>(
Data.data());
1123 TrieRecord::Data
D = R->load();
1126 case TrieRecord::StorageKind::Unknown:
1129 case TrieRecord::StorageKind::DataPool:
1133 case TrieRecord::StorageKind::Standalone:
1134 OS <<
"standalone-data ";
1136 case TrieRecord::StorageKind::StandaloneLeaf:
1137 OS <<
"standalone-leaf ";
1139 case TrieRecord::StorageKind::StandaloneLeaf0:
1140 OS <<
"standalone-leaf+0";
1143 OS <<
" Offset=" << (
void *)
D.Offset.get();
1151 Pool, [](PoolInfo LHS, PoolInfo RHS) {
return LHS.Offset < RHS.Offset; });
1152 for (PoolInfo PI : Pool) {
1153 OS <<
"- addr=" << (
void *)PI.Offset <<
" ";
1154 auto D = DataRecordHandle::getFromDataPool(DataPool,
FileOffset(PI.Offset));
1156 OS <<
"error: " <<
toString(
D.takeError());
1160 OS <<
"record refs=" <<
D->getNumRefs() <<
" data=" <<
D->getDataSize()
1161 <<
" size=" <<
D->getTotalSize()
1162 <<
" end=" << (
void *)(PI.Offset +
D->getTotalSize()) <<
"\n";
1168 auto P = Index.insertLazy(
1174 new (TentativeValue.
Data.
data()) TrieRecord();
1177 return P.takeError();
1179 assert(*
P &&
"Expected insertion");
1180 return getIndexProxyFromPointer(*
P);
1187 return IndexProxy{
P.getOffset(),
P->Hash,
1188 *
const_cast<TrieRecord *
>(
1189 reinterpret_cast<const TrieRecord *
>(
P->Data.data()))};
1193 auto I = indexHash(Hash);
1195 return I.takeError();
1196 return getExternalReference(*
I);
1199ObjectID OnDiskGraphDB::getExternalReference(
const IndexProxy &
I) {
1200 return getExternalReference(makeInternalRef(
I.Offset));
1203std::optional<ObjectID>
1205 bool CheckUpstream) {
1207 [&](std::optional<IndexProxy>
I) -> std::optional<ObjectID> {
1208 if (!CheckUpstream || !UpstreamDB)
1209 return std::nullopt;
1210 std::optional<ObjectID> UpstreamID =
1211 UpstreamDB->getExistingReference(Digest);
1213 return std::nullopt;
1216 return std::nullopt;
1219 return getExternalReference(*
I);
1224 return tryUpstream(std::nullopt);
1226 TrieRecord::Data Obj =
I.Ref.load();
1227 if (Obj.SK == TrieRecord::StorageKind::Unknown)
1228 return tryUpstream(
I);
1229 return getExternalReference(makeInternalRef(
I.Offset));
1234 auto P = Index.recoverFromFileOffset(
Ref.getFileOffset());
1236 return P.takeError();
1237 return getIndexProxyFromPointer(*
P);
1241 auto I = getIndexProxyFromRef(
Ref);
1243 return I.takeError();
1251static std::variant<const StandaloneDataInMemory *, DataRecordHandle>
1258 reinterpret_cast<const StandaloneDataInMemory *
>(
Data & (-1ULL << 1));
1264 assert(DataHandle.getData().end()[0] == 0 &&
"Null termination");
1271 if (std::holds_alternative<const StandaloneDataInMemory *>(SDIMOrRecord)) {
1272 return std::get<const StandaloneDataInMemory *>(SDIMOrRecord)->getContent();
1274 auto DataHandle = std::get<DataRecordHandle>(std::move(SDIMOrRecord));
1275 return OnDiskContent{std::move(DataHandle), std::nullopt};
1281 return Content.getData();
1285 if (std::optional<DataRecordHandle>
Record =
1287 return Record->getRefs();
1288 return std::nullopt;
1294 if (std::holds_alternative<const StandaloneDataInMemory *>(SDIMOrRecord)) {
1295 auto *SDIM = std::get<const StandaloneDataInMemory *>(SDIMOrRecord);
1296 return SDIM->getInternalFileBackedObjectData(RootPath);
1298 auto DataHandle = std::get<DataRecordHandle>(std::move(SDIMOrRecord));
1303std::unique_ptr<MemoryBuffer>
1305 bool RequiresNullTerminator)
const {
1310 std::get_if<const StandaloneDataInMemory *>(&SDIMOrRecord)) {
1311 if (std::unique_ptr<MemoryBuffer> Standalone =
1312 (*SDIM)->getStandaloneMemoryBuffer(RootPath, Name,
1313 RequiresNullTerminator))
1323 auto I = getIndexProxyFromRef(
Ref);
1325 return I.takeError();
1326 TrieRecord::Data Object =
I->Ref.load();
1328 if (Object.SK == TrieRecord::StorageKind::Unknown)
1329 return faultInFromUpstream(ExternalRef);
1331 if (Object.SK == TrieRecord::StorageKind::DataPool)
1341 switch (Object.SK) {
1342 case TrieRecord::StorageKind::Unknown:
1343 case TrieRecord::StorageKind::DataPool:
1345 case TrieRecord::StorageKind::Standalone:
1346 case TrieRecord::StorageKind::StandaloneLeaf0:
1347 case TrieRecord::StorageKind::StandaloneLeaf:
1373 auto Region = std::make_unique<sys::fs::mapped_file_region>(
1379 static_cast<StandaloneDataMapTy *
>(StandaloneData)
1380 ->insert(
I->Hash, Object.SK, std::move(
Region),
I->Offset));
1384 auto Presence = getObjectPresence(
Ref,
true);
1386 return Presence.takeError();
1388 switch (*Presence) {
1389 case ObjectPresence::Missing:
1391 case ObjectPresence::InPrimaryDB:
1393 case ObjectPresence::OnlyInUpstreamDB:
1394 if (
auto FaultInResult = faultInFromUpstream(
Ref); !FaultInResult)
1395 return FaultInResult.takeError();
1402OnDiskGraphDB::getObjectPresence(
ObjectID ExternalRef,
1403 bool CheckUpstream)
const {
1405 auto I = getIndexProxyFromRef(
Ref);
1407 return I.takeError();
1409 TrieRecord::Data Object =
I->Ref.load();
1410 if (Object.SK != TrieRecord::StorageKind::Unknown)
1411 return ObjectPresence::InPrimaryDB;
1413 if (!CheckUpstream || !UpstreamDB)
1414 return ObjectPresence::Missing;
1416 std::optional<ObjectID> UpstreamID =
1417 UpstreamDB->getExistingReference(getDigest(*
I));
1418 return UpstreamID.has_value() ? ObjectPresence::OnlyInUpstreamDB
1419 : ObjectPresence::Missing;
1429 Path.assign(RootPath.
begin(), RootPath.
end());
1434void OnDiskGraphDB::getStandalonePath(StringRef Prefix, FileOffset IndexOffset,
1435 SmallVectorImpl<char> &Path)
const {
1436 return ::getStandalonePath(RootPath, Prefix, IndexOffset, Path);
1439OnDiskContent StandaloneDataInMemory::getContent()
const {
1445 case TrieRecord::StorageKind::Standalone:
1447 case TrieRecord::StorageKind::StandaloneLeaf0:
1448 Leaf = Leaf0 =
true;
1450 case TrieRecord::StorageKind::StandaloneLeaf:
1457 assert(
Data.drop_back(Leaf0).end()[0] == 0 &&
1458 "Standalone node data missing null termination");
1459 return OnDiskContent{std::nullopt,
1463 DataRecordHandle
Record = DataRecordHandle::get(
Region->data());
1465 "Standalone object record missing null termination for data");
1466 return OnDiskContent{
Record, std::nullopt};
1469OnDiskGraphDB::FileBackedData
1470StandaloneDataInMemory::getInternalFileBackedObjectData(
1471 StringRef RootPath)
const {
1473 case TrieRecord::StorageKind::Unknown:
1474 case TrieRecord::StorageKind::DataPool:
1476 case TrieRecord::StorageKind::Standalone:
1477 return OnDiskGraphDB::FileBackedData{getContent().getData(),
1479 case TrieRecord::StorageKind::StandaloneLeaf0:
1480 case TrieRecord::StorageKind::StandaloneLeaf:
1481 bool IsFileNulTerminated = SK == TrieRecord::StorageKind::StandaloneLeaf0;
1482 SmallString<256>
Path;
1485 return OnDiskGraphDB::FileBackedData{
1486 getContent().getData(), OnDiskGraphDB::FileBackedData::FileInfoTy{
1487 std::string(Path), IsFileNulTerminated}};
1495class AdoptedMemoryBuffer final :
public MemoryBuffer {
1497 AdoptedMemoryBuffer(std::unique_ptr<MemoryBuffer> Buffer, StringRef Name,
1500 const char *
Start = this->Buffer->getBufferStart() +
Offset;
1504 StringRef getBufferIdentifier()
const final {
return Name; }
1506 BufferKind getBufferKind()
const final {
return Buffer->getBufferKind(); }
1509 std::unique_ptr<MemoryBuffer> Buffer;
1514std::unique_ptr<MemoryBuffer> StandaloneDataInMemory::getStandaloneMemoryBuffer(
1515 StringRef RootPath, StringRef Name,
bool RequiresNullTerminator)
const {
1519 if (RequiresNullTerminator && SK == TrieRecord::StorageKind::StandaloneLeaf)
1526 SmallString<256>
Path;
1530 ErrorOr<std::unique_ptr<MemoryBuffer>> Mapped =
1539 OnDiskContent Content = getContent();
1540 ArrayRef<char>
Data = Content.getData();
1542 if (
Offset +
Data.size() > (*Mapped)->getBufferSize())
1545 return std::make_unique<AdoptedMemoryBuffer>(std::move(*Mapped), Name,
Offset,
1549static Expected<MappedTempFile>
1553 assert(
Size &&
"Unexpected request for an empty temp file");
1556 return File.takeError();
1570 return MappedTempFile(std::move(*File), std::move(Map));
1578Error OnDiskGraphDB::createStandaloneLeaf(IndexProxy &
I, ArrayRef<char>
Data) {
1579 assert(
Data.size() > TrieRecord::MaxEmbeddedSize &&
1580 "Expected a bigger file for external content...");
1583 TrieRecord::StorageKind SK = Leaf0 ? TrieRecord::StorageKind::StandaloneLeaf0
1584 : TrieRecord::StorageKind::StandaloneLeaf;
1586 SmallString<256>
Path;
1587 int64_t FileSize =
Data.size() + Leaf0;
1594 return File.takeError();
1604 TrieRecord::Data Existing;
1606 TrieRecord::Data Leaf{SK, FileOffset()};
1607 if (
I.Ref.compare_exchange_strong(Existing, Leaf)) {
1608 recordStandaloneSizeIncrease(FileSize);
1614 if (Existing.SK == TrieRecord::StorageKind::Unknown)
1622 auto I = getIndexProxyFromRef(getInternalRef(ID));
1624 return I.takeError();
1628 TrieRecord::Data Existing =
I->Ref.load();
1629 if (Existing.SK != TrieRecord::StorageKind::Unknown)
1636 if (Refs.
empty() &&
Data.size() > TrieRecord::MaxEmbeddedSize)
1637 return createStandaloneLeaf(*
I,
Data);
1642 InternalRefVector InternalRefs;
1644 InternalRefs.push_back(getInternalRef(
Ref));
1648 DataRecordHandle::Input
Input{InternalRefs,
Data};
1651 TrieRecord::StorageKind SK = TrieRecord::StorageKind::Unknown;
1654 std::optional<MappedTempFile> File;
1655 std::optional<uint64_t> FileSize;
1658 TrieRecord::StorageKind::Standalone),
1661 return std::move(E);
1664 SK = TrieRecord::StorageKind::Standalone;
1665 return File->data();
1668 if (
Size <= TrieRecord::MaxEmbeddedSize) {
1669 SK = TrieRecord::StorageKind::DataPool;
1670 auto P = DataPool.allocate(
Size);
1672 char *NewAlloc =
nullptr;
1674 P.takeError(), [&](std::unique_ptr<StringError> E) ->
Error {
1675 if (E->convertToErrorCode() == std::errc::not_enough_memory)
1676 return AllocStandaloneFile(Size).moveInto(NewAlloc);
1677 return Error(std::move(E));
1681 return std::move(NewE);
1683 PoolOffset =
P->getOffset();
1685 dbgs() <<
"pool-alloc addr=" << (
void *)PoolOffset.
get()
1687 <<
" end=" << (
void *)(PoolOffset.
get() +
Size) <<
"\n";
1689 return (*P)->data();
1691 return AllocStandaloneFile(
Size);
1698 assert(
Record.getData().end()[0] == 0 &&
"Expected null-termination");
1700 assert(SK != TrieRecord::StorageKind::Unknown);
1701 assert(
bool(File) !=
bool(PoolOffset) &&
1702 "Expected either a mapped file or a pooled offset");
1708 TrieRecord::Data Existing =
I->Ref.load();
1710 TrieRecord::Data NewObject{SK, PoolOffset};
1712 if (Existing.SK == TrieRecord::StorageKind::Unknown) {
1714 if (
Error E = File->keep(Path))
1726 if (Existing.SK == TrieRecord::StorageKind::Unknown) {
1727 if (
I->Ref.compare_exchange_strong(Existing, NewObject)) {
1729 recordStandaloneSizeIncrease(*FileSize);
1735 if (Existing.SK == TrieRecord::StorageKind::Unknown)
1743 return storeFile(ID, FilePath, std::nullopt);
1748 std::optional<InternalUpstreamImportKind> ImportKind) {
1749 auto I = getIndexProxyFromRef(getInternalRef(ID));
1751 return I.takeError();
1755 TrieRecord::Data Existing =
I->Ref.load();
1756 if (Existing.SK != TrieRecord::StorageKind::Unknown)
1766 if (FileSize <= TrieRecord::MaxEmbeddedSize) {
1776 return ExpectedPath.takeError();
1779 TrieRecord::StorageKind SK;
1780 if (ImportKind.has_value()) {
1782 switch (*ImportKind) {
1783 case InternalUpstreamImportKind::Leaf:
1784 SK = TrieRecord::StorageKind::StandaloneLeaf;
1786 case InternalUpstreamImportKind::Leaf0:
1787 SK = TrieRecord::StorageKind::StandaloneLeaf0;
1792 SK = Leaf0 ? TrieRecord::StorageKind::StandaloneLeaf0
1793 : TrieRecord::StorageKind::StandaloneLeaf;
1809 SmallString<256> StandalonePath;
1816 TrieRecord::Data Existing;
1818 TrieRecord::Data Leaf{SK, FileOffset()};
1819 if (
I->Ref.compare_exchange_strong(Existing, Leaf)) {
1820 recordStandaloneSizeIncrease(FileSize);
1826 if (Existing.SK == TrieRecord::StorageKind::Unknown)
1832void OnDiskGraphDB::recordStandaloneSizeIncrease(
size_t SizeIncrease) {
1833 standaloneStorageSize().fetch_add(SizeIncrease, std::memory_order_relaxed);
1836std::atomic<uint64_t> &OnDiskGraphDB::standaloneStorageSize()
const {
1837 MutableArrayRef<uint8_t> UserHeader = DataPool.getUserHeader();
1838 assert(UserHeader.
size() ==
sizeof(std::atomic<uint64_t>));
1840 return *
reinterpret_cast<std::atomic<uint64_t> *
>(UserHeader.
data());
1843uint64_t OnDiskGraphDB::getStandaloneStorageSize()
const {
1844 return standaloneStorageSize().load(std::memory_order_relaxed);
1848 return Index.size() + DataPool.size() + getStandaloneStorageSize();
1852 unsigned IndexPercent = Index.size() * 100ULL / Index.capacity();
1853 unsigned DataPercent = DataPool.size() * 100ULL / DataPool.capacity();
1854 return std::max(IndexPercent, DataPercent);
1859 unsigned HashByteSize, OnDiskGraphDB *UpstreamDB,
1860 std::shared_ptr<OnDiskCASLogger> Logger,
1865 constexpr uint64_t MB = 1024ull * 1024ull;
1866 constexpr uint64_t GB = 1024ull * 1024ull * 1024ull;
1869 uint64_t MaxDataPoolSize = 24 * GB;
1872 MaxIndexSize = 1 * GB;
1873 MaxDataPoolSize = 2 * GB;
1878 return CustomSize.takeError();
1880 MaxIndexSize = MaxDataPoolSize = **CustomSize;
1884 std::optional<OnDiskTrieRawHashMap> Index;
1887 HashByteSize * CHAR_BIT,
1888 sizeof(TrieRecord), MaxIndexSize,
1891 return std::move(E);
1893 uint32_t UserHeaderSize =
sizeof(std::atomic<uint64_t>);
1897 std::optional<OnDiskDataAllocator> DataPool;
1903 MaxDataPoolSize, MB, UserHeaderSize, Logger,
1904 [](
void *UserHeaderPtr) {
1905 new (UserHeaderPtr) std::atomic<uint64_t>(0);
1907 .moveInto(DataPool))
1908 return std::move(E);
1909 if (DataPool->getUserHeader().size() != UserHeaderSize)
1911 "unexpected user header in '" + DataPoolPath +
1914 return std::unique_ptr<OnDiskGraphDB>(
1915 new OnDiskGraphDB(AbsPath, std::move(*Index), std::move(*DataPool),
1916 UpstreamDB, Policy, std::move(Logger)));
1922 std::shared_ptr<OnDiskCASLogger>
Logger)
1924 RootPath(RootPath.str()), UpstreamDB(UpstreamDB), FIPolicy(Policy),
1935 StandaloneData =
new StandaloneDataMapTy();
1939 delete static_cast<StandaloneDataMapTy *
>(StandaloneData);
1948 struct UpstreamCursor {
1965 auto enqueueNode = [&](
ObjectID PrimaryID, std::optional<ObjectHandle>
Node) {
1974 enqueueNode(PrimaryID, UpstreamNode);
1976 while (!CursorStack.
empty()) {
1977 UpstreamCursor &Cur = CursorStack.
back();
1978 if (Cur.RefI == Cur.RefE) {
1983 assert(PrimaryNodesStack.
size() >= Cur.RefsCount + 1);
1984 ObjectID PrimaryID = *(PrimaryNodesStack.
end() - Cur.RefsCount - 1);
1985 auto PrimaryRefs =
ArrayRef(PrimaryNodesStack)
1986 .slice(PrimaryNodesStack.
size() - Cur.RefsCount);
1987 if (
Error E = importUpstreamData(PrimaryID, PrimaryRefs, Cur.Node))
1990 PrimaryNodesStack.
truncate(PrimaryNodesStack.
size() - Cur.RefsCount);
1995 ObjectID UpstreamID = *(Cur.RefI++);
1996 auto PrimaryID =
getReference(UpstreamDB->getDigest(UpstreamID));
1998 return PrimaryID.takeError();
2003 enqueueNode(*PrimaryID, std::nullopt);
2006 Expected<std::optional<ObjectHandle>> UpstreamNode =
2007 UpstreamDB->load(UpstreamID);
2010 enqueueNode(*PrimaryID, *UpstreamNode);
2022 auto UpstreamRefs = UpstreamDB->getObjectRefs(UpstreamNode);
2025 for (ObjectID UpstreamRef : UpstreamRefs) {
2028 return Ref.takeError();
2032 return importUpstreamData(PrimaryID, Refs, UpstreamNode);
2040 if (PrimaryRefs.
empty()) {
2041 auto FBData = UpstreamDB->getInternalFileBackedObjectData(UpstreamNode);
2042 if (FBData.FileInfo.has_value()) {
2046 PrimaryID, FBData.FileInfo->FilePath,
2047 FBData.FileInfo->IsFileNulTerminated
2048 ? InternalUpstreamImportKind::Leaf0
2049 : InternalUpstreamImportKind::Leaf);
2053 auto Data = UpstreamDB->getObjectData(UpstreamNode);
2054 return store(PrimaryID, PrimaryRefs,
Data);
2057Expected<std::optional<ObjectHandle>>
2058OnDiskGraphDB::faultInFromUpstream(
ObjectID PrimaryID) {
2060 return std::nullopt;
2062 auto UpstreamID = UpstreamDB->getReference(
getDigest(PrimaryID));
2064 return UpstreamID.takeError();
2066 Expected<std::optional<ObjectHandle>> UpstreamNode =
2067 UpstreamDB->load(*UpstreamID);
2071 return std::nullopt;
2074 ? importSingleNode(PrimaryID, **UpstreamNode)
2075 : importFullTree(PrimaryID, **UpstreamNode))
2076 return std::move(
E);
2077 return load(PrimaryID);
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
AMDGPU Mark last scratch load
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_UNLIKELY(EXPR)
This file defines the DenseMap class.
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
static bool lookup(const GsymReader &GR, GsymDataExtractor &Data, uint64_t &Offset, uint64_t BaseAddr, uint64_t Addr, SourceLocations &SrcLocs, llvm::Error &Err)
A Lookup helper functions.
This file declares interface for OnDiskCASLogger, an interface that can be used to log CAS events to ...
This file declares interface for OnDiskDataAllocator, a file backed data pool can be used to allocate...
static constexpr StringLiteral FilePrefixLeaf0
static constexpr StringLiteral DataPoolTableName
static constexpr StringLiteral FilePrefixObject
static constexpr StringLiteral FilePrefixLeaf
static constexpr StringLiteral IndexFilePrefix
static OnDiskContent getContentFromHandle(const OnDiskDataAllocator &DataPool, ObjectHandle OH)
static constexpr StringLiteral DataPoolFilePrefix
static Error createCorruptObjectError(Expected< ArrayRef< uint8_t > > ID)
static std::variant< const StandaloneDataInMemory *, DataRecordHandle > getStandaloneDataOrDataRecord(const OnDiskDataAllocator &DataPool, ObjectHandle OH)
static size_t getPageSize()
static void getStandalonePath(StringRef RootPath, StringRef Prefix, FileOffset IndexOffset, SmallVectorImpl< char > &Path)
static Expected< MappedTempFile > createTempFile(StringRef FinalPath, uint64_t Size, OnDiskCASLogger *Logger)
static constexpr StringLiteral IndexTableName
This declares OnDiskGraphDB, an ondisk CAS database with a fixed length hash.
This file declares interface for OnDiskTrieRawHashMap, a thread-safe and (mostly) lock-free hash map ...
Provides a library for accessing information about this process and other processes on the operating ...
This file defines the scope_exit class, which executes user-defined cleanup logic at scope exit.
Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
Get the array size.
ArrayRef< T > drop_back(size_t N=1) const
Drop the last N elements of the array.
bool empty() const
Check if the array is empty.
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
Error takeError()
Take ownership of the stored error.
Logging utility - given an ordered specification of features, and assuming a scalar reward,...
static std::unique_ptr< MemoryBuffer > getMemBufferCopy(StringRef InputData, const Twine &BufferName="")
Open the specified memory range as a MemoryBuffer, copying the contents and taking ownership of it.
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
SmallString - A SmallString is just a SmallVector with methods and accessors that make it work better...
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void reserve(size_type N)
void truncate(size_type N)
Like resize, but requires that N is less than size().
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
A wrapper around a string literal that serves as a proxy for constructing global tables of StringRefs...
Represent a constant reference to a string, i.e.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
FileOffset is a wrapper around uint64_t to represent the offset of data from the beginning of the fil...
Handle to a loaded object in a ObjectStore instance.
LLVM_ABI Expected< ArrayRef< char > > get(FileOffset Offset, size_t Size) const
Get the data of Size stored at the given Offset.
static LLVM_ABI Expected< OnDiskDataAllocator > create(const Twine &Path, const Twine &TableName, uint64_t MaxFileSize, std::optional< uint64_t > NewFileInitialSize, uint32_t UserHeaderSize=0, std::shared_ptr< ondisk::OnDiskCASLogger > Logger=nullptr, function_ref< void(void *)> UserHeaderInit=nullptr)
LLVM_ABI size_t size() const
OnDiskTrieRawHashMap is a persistent trie data structure used as hash maps.
static LLVM_ABI Expected< OnDiskTrieRawHashMap > create(const Twine &Path, const Twine &TrieName, size_t NumHashBits, uint64_t DataSize, uint64_t MaxFileSize, std::optional< uint64_t > NewFileInitialSize, std::shared_ptr< ondisk::OnDiskCASLogger > Logger=nullptr, std::optional< size_t > NewTableNumRootBits=std::nullopt, std::optional< size_t > NewTableNumSubtrieBits=std::nullopt)
Gets or creates a file at Path with a hash-mapped trie named TrieName.
static std::optional< InternalRef4B > tryToShrink(InternalRef Ref)
Shrink to 4B reference.
Array of internal node references.
Standard 8 byte reference inside OnDiskGraphDB.
static InternalRef getFromOffset(FileOffset Offset)
Handle for a loaded node object.
static LLVM_ABI ObjectHandle fromFileOffset(FileOffset Offset)
static LLVM_ABI ObjectHandle fromMemory(uintptr_t Ptr)
ObjectHandle(uint64_t Opaque)
uint64_t getOpaqueData() const
Interface for logging low-level on-disk cas operations.
On-disk CAS nodes database, independent of a particular hashing algorithm.
FaultInPolicy
How to fault-in nodes if an upstream database is used.
@ SingleNode
Copy only the requested node.
LLVM_ABI void print(raw_ostream &OS) const
LLVM_ABI Error validateObjectID(ObjectID ID) const
Checks that ID exists in the index.
LLVM_ABI Expected< std::optional< ObjectHandle > > load(ObjectID Ref)
LLVM_ABI std::unique_ptr< MemoryBuffer > getStandaloneMemoryBuffer(ObjectHandle Node, StringRef Name, bool RequiresNullTerminator) const
Get a MemoryBuffer for Node's data that stays valid after this database is destroyed.
LLVM_ABI Expected< bool > isMaterialized(ObjectID Ref)
Check whether the object associated with Ref is stored in the CAS.
LLVM_ABI Error validate(bool Deep, HashingFuncT Hasher) const
Validate the OnDiskGraphDB.
object_refs_range getObjectRefs(ObjectHandle Node) const
LLVM_ABI unsigned getHardStorageLimitUtilization() const
LLVM_ABI Error store(ObjectID ID, ArrayRef< ObjectID > Refs, ArrayRef< char > Data)
Associate data & references with a particular object ID.
ArrayRef< uint8_t > getDigest(ObjectID Ref) const
LLVM_ABI FileBackedData getInternalFileBackedObjectData(ObjectHandle Node) const
Provides access to the underlying file path, that represents an object leaf node, when available.
LLVM_ABI Error storeFile(ObjectID ID, StringRef FilePath)
Associates the data of a file with a particular object ID.
LLVM_ABI size_t getStorageSize() const
static LLVM_ABI Expected< std::unique_ptr< OnDiskGraphDB > > open(StringRef Path, StringRef HashName, unsigned HashByteSize, OnDiskGraphDB *UpstreamDB=nullptr, std::shared_ptr< OnDiskCASLogger > Logger=nullptr, FaultInPolicy Policy=FaultInPolicy::FullTree)
Open the on-disk store from a directory.
bool containsObject(ObjectID Ref, bool CheckUpstream=true) const
Check whether the object associated with Ref is stored in the CAS.
LLVM_ABI ~OnDiskGraphDB()
LLVM_ABI Expected< ObjectID > getReference(ArrayRef< uint8_t > Hash)
Form a reference for the provided hash.
function_ref< void( ArrayRef< ArrayRef< uint8_t > >, ArrayRef< char >, SmallVectorImpl< uint8_t > &)> HashingFuncT
Hashing function type for validation.
LLVM_ABI ArrayRef< char > getObjectData(ObjectHandle Node) const
LLVM_ABI std::optional< ObjectID > getExistingReference(ArrayRef< uint8_t > Digest, bool CheckUpstream=true)
Get an existing reference to the object Digest.
Helper RAII class for copying a file to a unique file path.
Error renameTo(StringRef RenameToPath)
Rename the new unique file to RenameToPath.
Expected< StringRef > createAndCopyFrom(StringRef ParentPath, StringRef CopyFromPath)
Create a new unique file path under ParentPath and copy the contents of CopyFromPath into it.
An efficient, type-erasing, non-owning reference to a callable.
This class implements an extremely fast bulk output stream that can only output to a stream.
static unsigned getPageSizeEstimate()
Get the process's estimated page size.
LLVM_ABI Error keep(const Twine &Name)
static LLVM_ABI Expected< TempFile > create(const Twine &Model, unsigned Mode=all_read|all_write, OpenFlags ExtraFlags=OF_None)
This creates a temporary file with createUniqueFile and schedules it for deletion with sys::RemoveFil...
Represents the result of a call to sys::fs::status().
This class represents a memory mapped file.
LLVM_ABI size_t size() const
@ readonly
May only access map via const_data as read only.
@ readwrite
May access map via data and modify it. Written to path.
LLVM_ABI char * data() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
constexpr StringLiteral CASFormatVersion
The version for all the ondisk database files.
Expected< std::optional< uint64_t > > getOverriddenMaxMappingSize()
Retrieves an overridden maximum mapping size for CAS files, if any, speicified by LLVM_CAS_MAX_MAPPIN...
Expected< size_t > preallocateFileTail(int FD, size_t CurrentSize, size_t NewSize)
Allocate space for the file FD on disk, if the filesystem supports it.
bool useSmallMappingSize(const Twine &Path)
Whether to use a small file mapping for ondisk databases created in Path.
initializer< Ty > init(const Ty &Val)
uint64_t getDataSize(const FuncRecordTy *Record)
Return the coverage map data size for the function.
uint64_t read64le(const void *P)
void write64le(void *P, uint64_t V)
void write32le(void *P, uint32_t V)
uint32_t read32le(const void *P)
LLVM_ABI std::error_code closeFile(file_t &F)
Close the file object.
LLVM_ABI std::error_code rename(const Twine &from, const Twine &to)
Rename from to to.
std::error_code resize_file_before_mapping_readwrite(int FD, uint64_t Size)
Resize FD to Size before mapping mapped_file_region::readwrite.
LLVM_ABI bool exists(const basic_file_status &status)
Does file exist?
@ OF_Append
The file should be opened in append mode.
LLVM_ABI std::error_code createUniqueFile(const Twine &Model, int &ResultFD, SmallVectorImpl< char > &ResultPath, OpenFlags Flags=OF_None, unsigned Mode=all_read|all_write)
Create a uniquely named file.
LLVM_ABI std::error_code remove(const Twine &path, bool IgnoreNonExisting=true)
Remove path.
@ CD_OpenExisting
CD_OpenExisting - When opening a file:
LLVM_ABI Expected< file_t > openNativeFileForRead(const Twine &Name, OpenFlags Flags=OF_None, SmallVectorImpl< char > *RealPath=nullptr)
Opens the file with the given name in a read-only mode, returning its open file descriptor.
LLVM_ABI std::error_code create_directories(const Twine &path, bool IgnoreExisting=true, perms Perms=owner_all|group_all)
Create all the non-existent directories in path.
LLVM_ABI file_t convertFDToNativeFile(int FD)
Converts from a Posix file descriptor number to a native file handle.
LLVM_ABI std::error_code status(const Twine &path, file_status &result, bool follow=true)
Get file status as if by POSIX stat().
std::error_code file_size(const Twine &Path, uint64_t &Result)
Get file size.
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
ScopedSetting scopedDisable()
This is an optimization pass for GlobalISel generic memory operations.
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
ArrayRef< CharT > arrayRefFromStringRef(StringRef Input)
Construct an array ref of bytes from a string ref.
@ Unknown
Not known to have no common set bits.
std::error_code make_error_code(BitcodeError E)
bool isAligned(Align Lhs, uint64_t SizeInBytes)
Checks that SizeInBytes is a multiple of the alignment.
Error handleErrors(Error E, HandlerTs &&... Hs)
Pass the ErrorInfo(s) contained in E to their respective handlers.
std::string utohexstr(uint64_t X, bool LowerCase=false, unsigned Width=0)
constexpr bool isPowerOf2_64(uint64_t Value)
Return true if the argument is a power of two > 0 (64 bit edition.)
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
std::optional< T > expectedToOptional(Expected< T > &&E)
Convert an Expected to an std::optional without doing anything.
decltype(auto) get(const PointerIntPair< PointerTy, IntBits, IntType, PtrTraits, Info > &Pair)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
@ Ref
The access may reference the value stored in memory.
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
auto count(R &&Range, const E &Element)
Wrapper function around std::count to count the number of times an element Element occurs in the give...
ArrayRef(const T &OneElt) -> ArrayRef< T >
std::string toString(const APInt &I, unsigned Radix, bool Signed, bool formatAsCLiteral=false, bool UpperCase=true, bool InsertSeparators=false)
OutputIt copy(R &&Range, OutputIt Out)
void toHex(ArrayRef< uint8_t > Input, bool LowerCase, SmallVectorImpl< char > &Output)
Convert buffer Input to its hexadecimal representation. The returned string is double the size of Inp...
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
LLVM_ABI Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Next
void consumeError(Error Err)
Consume a Error without doing anything.
StringRef toStringRef(bool B)
Construct a string ref from a boolean.
bool isAddrAligned(Align Lhs, const void *Addr)
Checks that Addr is a multiple of the alignment.
Implement std::hash so that hash_code can be used in STL containers.
Proxy for an on-disk index record.
This struct is a compact representation of a valid (non-zero power of two) alignment.
static constexpr Align Of()
Allow constructions of constexpr Align from types.
Const value proxy to access the records stored in TrieRawHashMap.
Value proxy to access the records stored in TrieRawHashMap.
MutableArrayRef< char > Data
Encapsulates file info for an underlying object node.