//===----------------------------------------------------------------------===// // // 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 // //===----------------------------------------------------------------------===// #include "BuiltinCAS.h" #include "llvm/CAS/BuiltinCASContext.h" #include "llvm/CAS/BuiltinObjectHasher.h" #include "llvm/CAS/OnDiskGraphDB.h" #include "llvm/CAS/UnifiedOnDiskCache.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" using namespace llvm; using namespace llvm::cas; using namespace llvm::cas::builtin; namespace { class OnDiskCAS : public BuiltinCAS { public: Expected storeImpl(ArrayRef ComputedHash, ArrayRef Refs, ArrayRef Data) final; Expected> loadIfExists(ObjectRef Ref) final; CASID getID(ObjectRef Ref) const final; std::optional getReference(const CASID &ID) const final; Expected isMaterialized(ObjectRef Ref) const final; ArrayRef getDataConst(ObjectHandle Node) const final; void print(raw_ostream &OS) const final; Error validate(bool CheckHash) const final; static Expected> open(StringRef Path); OnDiskCAS(std::shared_ptr UniDB) : UnifiedDB(std::move(UniDB)), DB(&UnifiedDB->getGraphDB()) {} private: ObjectHandle convertHandle(ondisk::ObjectHandle Node) const { return makeObjectHandle(Node.getOpaqueData()); } ondisk::ObjectHandle convertHandle(ObjectHandle Node) const { return ondisk::ObjectHandle(Node.getInternalRef(*this)); } ObjectRef convertRef(ondisk::ObjectID Ref) const { return makeObjectRef(Ref.getOpaqueData()); } ondisk::ObjectID convertRef(ObjectRef Ref) const { return ondisk::ObjectID::fromOpaqueData(Ref.getInternalRef(*this)); } size_t getNumRefs(ObjectHandle Node) const final { auto RefsRange = DB->getObjectRefs(convertHandle(Node)); return std::distance(RefsRange.begin(), RefsRange.end()); } ObjectRef readRef(ObjectHandle Node, size_t I) const final { auto RefsRange = DB->getObjectRefs(convertHandle(Node)); return convertRef(RefsRange.begin()[I]); } Error forEachRef(ObjectHandle Node, function_ref Callback) const final; Error setSizeLimit(std::optional SizeLimit) final; Expected> getStorageSize() const final; Error pruneStorageData() final; OnDiskCAS(std::unique_ptr GraphDB) : OwnedDB(std::move(GraphDB)), DB(OwnedDB.get()) {} std::unique_ptr OwnedDB; std::shared_ptr UnifiedDB; ondisk::OnDiskGraphDB *DB; }; } // end anonymous namespace void OnDiskCAS::print(raw_ostream &OS) const { DB->print(OS); } Error OnDiskCAS::validate(bool CheckHash) const { auto Hasher = [](ArrayRef> Refs, ArrayRef Data, SmallVectorImpl &Result) { auto Hash = BuiltinObjectHasher::hashObject( Refs, Data); Result.assign(Hash.begin(), Hash.end()); }; if (auto E = DB->validate(CheckHash, Hasher)) return E; return Error::success(); } CASID OnDiskCAS::getID(ObjectRef Ref) const { ArrayRef Hash = DB->getDigest(convertRef(Ref)); return CASID::create(&getContext(), toStringRef(Hash)); } std::optional OnDiskCAS::getReference(const CASID &ID) const { std::optional ObjID = DB->getExistingReference(ID.getHash()); if (!ObjID) return std::nullopt; return convertRef(*ObjID); } Expected OnDiskCAS::isMaterialized(ObjectRef ExternalRef) const { return DB->isMaterialized(convertRef(ExternalRef)); } ArrayRef OnDiskCAS::getDataConst(ObjectHandle Node) const { return DB->getObjectData(convertHandle(Node)); } Expected> OnDiskCAS::loadIfExists(ObjectRef ExternalRef) { Expected> ObjHnd = DB->load(convertRef(ExternalRef)); if (!ObjHnd) return ObjHnd.takeError(); if (!*ObjHnd) return std::nullopt; return convertHandle(**ObjHnd); } Expected OnDiskCAS::storeImpl(ArrayRef ComputedHash, ArrayRef Refs, ArrayRef Data) { SmallVector IDs; IDs.reserve(Refs.size()); for (ObjectRef Ref : Refs) { IDs.push_back(convertRef(Ref)); } auto StoredID = DB->getReference(ComputedHash); if (LLVM_UNLIKELY(!StoredID)) return StoredID.takeError(); if (Error E = DB->store(*StoredID, IDs, Data)) return std::move(E); return convertRef(*StoredID); } Error OnDiskCAS::forEachRef(ObjectHandle Node, function_ref Callback) const { auto RefsRange = DB->getObjectRefs(convertHandle(Node)); for (ondisk::ObjectID Ref : RefsRange) { if (Error E = Callback(convertRef(Ref))) return E; } return Error::success(); } Error OnDiskCAS::setSizeLimit(std::optional SizeLimit) { UnifiedDB->setSizeLimit(SizeLimit); return Error::success(); } Expected> OnDiskCAS::getStorageSize() const { return UnifiedDB->getStorageSize(); } Error OnDiskCAS::pruneStorageData() { return UnifiedDB->collectGarbage(); } Expected> OnDiskCAS::open(StringRef AbsPath) { Expected> DB = ondisk::OnDiskGraphDB::open(AbsPath, BuiltinCASContext::getHashName(), sizeof(HashType)); if (!DB) return DB.takeError(); return std::unique_ptr(new OnDiskCAS(std::move(*DB))); } bool cas::isOnDiskCASEnabled() { #if LLVM_ENABLE_ONDISK_CAS return true; #else return false; #endif } Expected> cas::createOnDiskCAS(const Twine &Path) { #if LLVM_ENABLE_ONDISK_CAS // FIXME: An absolute path isn't really good enough. Should open a directory // and use openat() for files underneath. SmallString<256> AbsPath; Path.toVector(AbsPath); sys::fs::make_absolute(AbsPath); return OnDiskCAS::open(AbsPath); #else return createStringError(inconvertibleErrorCode(), "OnDiskCAS is disabled"); #endif /* LLVM_ENABLE_ONDISK_CAS */ } std::unique_ptr cas::builtin::createObjectStoreFromUnifiedOnDiskCache( std::shared_ptr UniDB) { return std::make_unique(std::move(UniDB)); }