diff options
| author | Stefan Boberg <[email protected]> | 2023-05-02 10:01:47 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-02 10:01:47 +0200 |
| commit | 075d17f8ada47e990fe94606c3d21df409223465 (patch) | |
| tree | e50549b766a2f3c354798a54ff73404217b4c9af /src/zenutil/cache/cachepolicy.cpp | |
| parent | fix: bundle shouldn't append content zip to zen (diff) | |
| download | zen-075d17f8ada47e990fe94606c3d21df409223465.tar.xz zen-075d17f8ada47e990fe94606c3d21df409223465.zip | |
moved source directories into `/src` (#264)
* moved source directories into `/src`
* updated bundle.lua for new `src` path
* moved some docs, icon
* removed old test trees
Diffstat (limited to 'src/zenutil/cache/cachepolicy.cpp')
| -rw-r--r-- | src/zenutil/cache/cachepolicy.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/src/zenutil/cache/cachepolicy.cpp b/src/zenutil/cache/cachepolicy.cpp new file mode 100644 index 000000000..3bca363bb --- /dev/null +++ b/src/zenutil/cache/cachepolicy.cpp @@ -0,0 +1,282 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include <zenutil/cache/cachepolicy.h> + +#include <zencore/compactbinary.h> +#include <zencore/compactbinarybuilder.h> +#include <zencore/enumflags.h> +#include <zencore/string.h> + +#include <algorithm> +#include <unordered_map> + +namespace zen::Private { +class CacheRecordPolicyShared; +} + +namespace zen { + +using namespace std::literals; + +namespace DerivedData::Private { + + constinit char CachePolicyDelimiter = ','; + + struct CachePolicyToTextData + { + CachePolicy Policy; + std::string_view Text; + }; + + constinit CachePolicyToTextData CachePolicyToText[]{ + // Flags with multiple bits are ordered by bit count to minimize token count in the text format. + {CachePolicy::Default, "Default"sv}, + {CachePolicy::Remote, "Remote"sv}, + {CachePolicy::Local, "Local"sv}, + {CachePolicy::Store, "Store"sv}, + {CachePolicy::Query, "Query"sv}, + // Flags with only one bit can be in any order. Match the order in CachePolicy. + {CachePolicy::QueryLocal, "QueryLocal"sv}, + {CachePolicy::QueryRemote, "QueryRemote"sv}, + {CachePolicy::StoreLocal, "StoreLocal"sv}, + {CachePolicy::StoreRemote, "StoreRemote"sv}, + {CachePolicy::SkipMeta, "SkipMeta"sv}, + {CachePolicy::SkipData, "SkipData"sv}, + {CachePolicy::PartialRecord, "PartialRecord"sv}, + {CachePolicy::KeepAlive, "KeepAlive"sv}, + // None must be last because it matches every policy. + {CachePolicy::None, "None"sv}, + }; + + constinit CachePolicy CachePolicyKnownFlags = + CachePolicy::Default | CachePolicy::SkipMeta | CachePolicy::SkipData | CachePolicy::PartialRecord | CachePolicy::KeepAlive; + + StringBuilderBase& CachePolicyToString(StringBuilderBase& Builder, CachePolicy Policy) + { + // Mask out unknown flags. None will be written if no flags are known. + Policy &= CachePolicyKnownFlags; + for (const CachePolicyToTextData& Pair : CachePolicyToText) + { + if (EnumHasAllFlags(Policy, Pair.Policy)) + { + EnumRemoveFlags(Policy, Pair.Policy); + Builder << Pair.Text << CachePolicyDelimiter; + if (Policy == CachePolicy::None) + { + break; + } + } + } + Builder.RemoveSuffix(1); + return Builder; + } + + CachePolicy ParseCachePolicy(const std::string_view Text) + { + ZEN_ASSERT(!Text.empty()); // ParseCachePolicy requires a non-empty string + CachePolicy Policy = CachePolicy::None; + ForEachStrTok(Text, CachePolicyDelimiter, [&Policy, Index = int32_t(0)](const std::string_view& Token) mutable { + const int32_t EndIndex = Index; + for (; size_t(Index) < sizeof(CachePolicyToText) / sizeof(CachePolicyToText[0]); ++Index) + { + if (CachePolicyToText[Index].Text == Token) + { + Policy |= CachePolicyToText[Index].Policy; + ++Index; + return true; + } + } + for (Index = 0; Index < EndIndex; ++Index) + { + if (CachePolicyToText[Index].Text == Token) + { + Policy |= CachePolicyToText[Index].Policy; + ++Index; + return true; + } + } + return true; + }); + return Policy; + } + +} // namespace DerivedData::Private + +StringBuilderBase& +operator<<(StringBuilderBase& Builder, CachePolicy Policy) +{ + return DerivedData::Private::CachePolicyToString(Builder, Policy); +} + +CachePolicy +ParseCachePolicy(std::string_view Text) +{ + return DerivedData::Private::ParseCachePolicy(Text); +} + +CachePolicy +ConvertToUpstream(CachePolicy Policy) +{ + // Set Local flags equal to downstream's Remote flags. + // Delete Skip flags if StoreLocal is true, otherwise use the downstream value. + // Use the downstream value for all other flags. + + CachePolicy UpstreamPolicy = CachePolicy::None; + + if (EnumHasAllFlags(Policy, CachePolicy::QueryRemote)) + { + UpstreamPolicy |= CachePolicy::QueryLocal; + } + + if (EnumHasAllFlags(Policy, CachePolicy::StoreRemote)) + { + UpstreamPolicy |= CachePolicy::StoreLocal; + } + + if (!EnumHasAllFlags(Policy, CachePolicy::StoreLocal)) + { + UpstreamPolicy |= (Policy & (CachePolicy::SkipData | CachePolicy::SkipMeta)); + } + + UpstreamPolicy |= Policy & ~(CachePolicy::Local | CachePolicy::SkipData | CachePolicy::SkipMeta); + + return UpstreamPolicy; +} + +class Private::CacheRecordPolicyShared final : public Private::ICacheRecordPolicyShared +{ +public: + inline void AddValuePolicy(const CacheValuePolicy& Value) final + { + ZEN_ASSERT(Value.Id); // Failed to add value policy because the ID is null. + const auto Insert = + std::lower_bound(Values.begin(), Values.end(), Value, [](const CacheValuePolicy& Existing, const CacheValuePolicy& New) { + return Existing.Id < New.Id; + }); + ZEN_ASSERT( + !(Insert < Values.end() && + Insert->Id == Value.Id)); // Failed to add value policy with ID %s because it has an existing value policy with that ID. ") + Values.insert(Insert, Value); + } + + inline std::span<const CacheValuePolicy> GetValuePolicies() const final { return Values; } + +private: + std::vector<CacheValuePolicy> Values; +}; + +CachePolicy +CacheRecordPolicy::GetValuePolicy(const Oid& Id) const +{ + if (Shared) + { + const std::span<const CacheValuePolicy> Values = Shared->GetValuePolicies(); + const auto Iter = + std::lower_bound(Values.begin(), Values.end(), Id, [](const CacheValuePolicy& A, const Oid& B) { return A.Id < B; }); + if (Iter != Values.end() && Iter->Id == Id) + { + return Iter->Policy; + } + } + return DefaultValuePolicy; +} + +void +CacheRecordPolicy::Save(CbWriter& Writer) const +{ + Writer.BeginObject(); + // The RecordPolicy is calculated from the ValuePolicies and does not need to be saved separately. + Writer.AddString("BasePolicy"sv, WriteToString<128>(GetBasePolicy())); + if (!IsUniform()) + { + Writer.BeginArray("ValuePolicies"sv); + for (const CacheValuePolicy& Value : GetValuePolicies()) + { + Writer.BeginObject(); + Writer.AddObjectId("Id"sv, Value.Id); + Writer.AddString("Policy"sv, WriteToString<128>(Value.Policy)); + Writer.EndObject(); + } + Writer.EndArray(); + } + Writer.EndObject(); +} + +OptionalCacheRecordPolicy +CacheRecordPolicy::Load(const CbObjectView Object) +{ + std::string_view BasePolicyText = Object["BasePolicy"sv].AsString(); + if (BasePolicyText.empty()) + { + return {}; + } + + CacheRecordPolicyBuilder Builder(ParseCachePolicy(BasePolicyText)); + for (CbFieldView ValueField : Object["ValuePolicies"sv]) + { + const CbObjectView Value = ValueField.AsObjectView(); + const Oid Id = Value["Id"sv].AsObjectId(); + const std::string_view PolicyText = Value["Policy"sv].AsString(); + if (!Id || PolicyText.empty()) + { + return {}; + } + CachePolicy Policy = ParseCachePolicy(PolicyText); + if (EnumHasAnyFlags(Policy, ~CacheValuePolicy::PolicyMask)) + { + return {}; + } + Builder.AddValuePolicy(Id, Policy); + } + + return Builder.Build(); +} + +CacheRecordPolicy +CacheRecordPolicy::ConvertToUpstream() const +{ + CacheRecordPolicyBuilder Builder(zen::ConvertToUpstream(GetBasePolicy())); + for (const CacheValuePolicy& ValuePolicy : GetValuePolicies()) + { + Builder.AddValuePolicy(ValuePolicy.Id, zen::ConvertToUpstream(ValuePolicy.Policy)); + } + return Builder.Build(); +} + +void +CacheRecordPolicyBuilder::AddValuePolicy(const CacheValuePolicy& Value) +{ + ZEN_ASSERT(!EnumHasAnyFlags(Value.Policy, + ~Value.PolicyMask)); // Value policy contains flags that only make sense on the record policy. Policy: %s + if (Value.Policy == (BasePolicy & Value.PolicyMask)) + { + return; + } + if (!Shared) + { + Shared = new Private::CacheRecordPolicyShared; + } + Shared->AddValuePolicy(Value); +} + +CacheRecordPolicy +CacheRecordPolicyBuilder::Build() +{ + CacheRecordPolicy Policy(BasePolicy); + if (Shared) + { + const auto Add = [](const CachePolicy A, const CachePolicy B) { + return ((A | B) & ~CachePolicy::SkipData) | ((A & B) & CachePolicy::SkipData); + }; + const std::span<const CacheValuePolicy> Values = Shared->GetValuePolicies(); + Policy.RecordPolicy = BasePolicy; + for (const CacheValuePolicy& ValuePolicy : Values) + { + Policy.RecordPolicy = Add(Policy.RecordPolicy, ValuePolicy.Policy); + } + Policy.Shared = std::move(Shared); + } + return Policy; +} + +} // namespace zen |