activity_sdk_generation_inventory.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #include <Windows.h>
  2. #include <string_view>
  3. #include <utility>
  4. #include "activity_sdk_generation_worker_internal.h"
  5. namespace sunrise::client::content::activity::sdk_generation::worker_internal {
  6. /** Adapts the selected cancellation source to the inventory builder. */
  7. [[nodiscard]] bool inventory_cancelled(void*) noexcept {
  8. return cancel_requested();
  9. }
  10. /** Appends one scenario only when its complete identity fits the public shard schema. */
  11. [[nodiscard]] bool
  12. append_scenario(std::vector<Scenario>& output, std::uint32_t tag, std::string_view name) noexcept {
  13. if (tag == 0 || name.empty() || name.size() >= Scenario{}.name.size()) {
  14. return false;
  15. }
  16. for (const Scenario& existing : output) {
  17. if (existing.tag == tag) {
  18. return true;
  19. }
  20. }
  21. if (output.size() >= manifest::kMaximumRecords) {
  22. return false;
  23. }
  24. Scenario row{};
  25. row.tag = tag;
  26. row.nameLength = static_cast<std::uint8_t>(name.size());
  27. std::copy(name.begin(), name.end(), row.name.begin());
  28. try {
  29. output.push_back(row);
  30. return true;
  31. } catch (...) {
  32. return false;
  33. }
  34. }
  35. /** Maps every inventory name status and refuses unknown enum values. */
  36. [[nodiscard]] bool convert_name_status(inventory::NameStatus input,
  37. manifest::ActivityRootSelectionStatus& output) noexcept {
  38. switch (input) {
  39. case inventory::NameStatus::exact:
  40. output = manifest::ActivityRootSelectionStatus::exact;
  41. return true;
  42. case inventory::NameStatus::ambiguous:
  43. output = manifest::ActivityRootSelectionStatus::ambiguous;
  44. return true;
  45. case inventory::NameStatus::staleAliasesOnly:
  46. output = manifest::ActivityRootSelectionStatus::staleAliasesOnly;
  47. return true;
  48. case inventory::NameStatus::unnamed:
  49. output = manifest::ActivityRootSelectionStatus::unnamed;
  50. return true;
  51. }
  52. return false;
  53. }
  54. /** Maps every inventory join status and refuses unknown enum values. */
  55. [[nodiscard]] bool convert_join_status(inventory::JoinStatus input,
  56. manifest::ActivityJoinStatus& output) noexcept {
  57. switch (input) {
  58. case inventory::JoinStatus::exact:
  59. output = manifest::ActivityJoinStatus::exact;
  60. return true;
  61. case inventory::JoinStatus::liveNameMissing:
  62. output = manifest::ActivityJoinStatus::liveNameMissing;
  63. return true;
  64. case inventory::JoinStatus::sourceNameMissing:
  65. output = manifest::ActivityJoinStatus::sourceNameMissing;
  66. return true;
  67. case inventory::JoinStatus::liveNameAmbiguous:
  68. output = manifest::ActivityJoinStatus::ambiguous;
  69. return true;
  70. }
  71. return false;
  72. }
  73. /** Maps every binding disposition and refuses unknown enum values. */
  74. [[nodiscard]] bool convert_binding_disposition(inventory::BindingDisposition input,
  75. manifest::BindingDisposition& output) noexcept {
  76. switch (input) {
  77. case inventory::BindingDisposition::fixedScenario:
  78. output = manifest::BindingDisposition::fixedScenario;
  79. return true;
  80. case inventory::BindingDisposition::namedDefinitionUnavailable:
  81. output = manifest::BindingDisposition::namedDefinitionUnavailable;
  82. return true;
  83. case inventory::BindingDisposition::noDirectFixedActivityName:
  84. output = manifest::BindingDisposition::noDirectFixedActivityName;
  85. return true;
  86. case inventory::BindingDisposition::unresolvedRunnable:
  87. output = manifest::BindingDisposition::unresolvedRunnable;
  88. return true;
  89. }
  90. return false;
  91. }
  92. /** Maps every stable binding reason and refuses unknown enum values. */
  93. [[nodiscard]] bool convert_binding_reason(inventory::BindingReason input,
  94. manifest::BindingReason& output) noexcept {
  95. switch (input) {
  96. case inventory::BindingReason::exactActivityRootScenarioEdge:
  97. output = manifest::BindingReason::exactActivityRootScenarioEdge;
  98. return true;
  99. case inventory::BindingReason::installedRouteAbsent:
  100. output = manifest::BindingReason::installedRouteAbsent;
  101. return true;
  102. case inventory::BindingReason::noDirectFixedActivityName:
  103. output = manifest::BindingReason::noDirectFixedActivityName;
  104. return true;
  105. case inventory::BindingReason::activityRootNameAmbiguous:
  106. output = manifest::BindingReason::activityRootNameAmbiguous;
  107. return true;
  108. case inventory::BindingReason::activityRootEdgeMissing:
  109. output = manifest::BindingReason::activityRootEdgeMissing;
  110. return true;
  111. }
  112. return false;
  113. }
  114. /** Maps every evidence basis and refuses unknown enum values. */
  115. [[nodiscard]] bool convert_evidence_basis(inventory::BindingEvidenceBasis input,
  116. manifest::BindingEvidenceBasis& output) noexcept {
  117. switch (input) {
  118. case inventory::BindingEvidenceBasis::effectiveActivityRootNamePlusPayloadScenarioEdge:
  119. output = manifest::BindingEvidenceBasis::effectiveActivityRootNamePlusPayloadScenarioEdge;
  120. return true;
  121. case inventory::BindingEvidenceBasis::effectiveActivityAndScenarioRootNameCensus:
  122. output = manifest::BindingEvidenceBasis::effectiveActivityAndScenarioRootNameCensus;
  123. return true;
  124. case inventory::BindingEvidenceBasis::activityRecordInternalNameEmpty:
  125. output = manifest::BindingEvidenceBasis::activityRecordInternalNameEmpty;
  126. return true;
  127. case inventory::BindingEvidenceBasis::effectiveActivityRootNameCensus:
  128. output = manifest::BindingEvidenceBasis::effectiveActivityRootNameCensus;
  129. return true;
  130. }
  131. return false;
  132. }
  133. /** Maps every runtime interpretation and refuses unknown enum values. */
  134. [[nodiscard]] bool convert_runnable_status(inventory::RunnableStatus input,
  135. manifest::RunnableStatus& output) noexcept {
  136. switch (input) {
  137. case inventory::RunnableStatus::fixedScenarioBound:
  138. output = manifest::RunnableStatus::fixedScenarioBound;
  139. return true;
  140. case inventory::RunnableStatus::unavailableInInstalledEstate:
  141. output = manifest::RunnableStatus::unavailableInInstalledEstate;
  142. return true;
  143. case inventory::RunnableStatus::fixedScenarioNotApplicable:
  144. output = manifest::RunnableStatus::fixedScenarioNotApplicable;
  145. return true;
  146. case inventory::RunnableStatus::unresolved:
  147. output = manifest::RunnableStatus::unresolved;
  148. return true;
  149. }
  150. return false;
  151. }
  152. /** Converts the independently measured native completeness partition. */
  153. [[nodiscard]] bool convert_binding_completeness(const inventory::BindingCompleteness& input,
  154. manifest::BindingCompleteness& output) noexcept {
  155. manifest::BindingCompletenessStatus status{};
  156. switch (input.status) {
  157. case inventory::BindingCompletenessStatus::ready:
  158. status = manifest::BindingCompletenessStatus::ready;
  159. break;
  160. case inventory::BindingCompletenessStatus::blockedUnresolvedRunnable:
  161. status = manifest::BindingCompletenessStatus::blockedUnresolvedRunnable;
  162. break;
  163. default:
  164. return false;
  165. }
  166. output = {input.total,
  167. input.fixedScenario,
  168. input.namedDefinitionUnavailable,
  169. input.noDirectFixedActivityName,
  170. input.unresolvedRunnable,
  171. status};
  172. return true;
  173. }
  174. /** Converts the checked native inventory into the bounded persistent catalog rows. */
  175. [[nodiscard]] bool build_inventory(Work& work, const package_reader::Source& source) noexcept {
  176. inventory::Snapshot snapshot{};
  177. if (!inventory::build(source, &inventory_cancelled, nullptr, snapshot) || cancel_requested()
  178. || snapshot.scenarios.size() > manifest::kMaximumScenarioRecords
  179. || snapshot.activityRoots.size() > manifest::kMaximumActivityRootRecords
  180. || snapshot.activities.size() > manifest::kMaximumActivityVariantRecords) {
  181. return false;
  182. }
  183. try {
  184. work.scenarios.clear();
  185. work.activityRoots.clear();
  186. work.activityVariants.clear();
  187. work.scenarios.reserve(snapshot.scenarios.size());
  188. work.activityRoots.reserve(snapshot.activityRoots.size());
  189. work.activityVariants.reserve(snapshot.activities.size());
  190. for (const inventory::ScenarioRoot& sourceScenario : snapshot.scenarios) {
  191. if (!append_scenario(
  192. work.scenarios,
  193. sourceScenario.tag,
  194. std::string_view(sourceScenario.name.data(), sourceScenario.nameLength))) {
  195. return false;
  196. }
  197. }
  198. for (const inventory::ActivityRoot& sourceRoot : snapshot.activityRoots) {
  199. manifest::ActivityRootRecord root{};
  200. root.activityRootTag = sourceRoot.tag;
  201. root.scenarioTag = sourceRoot.scenarioTag;
  202. root.transitionDescriptorTag = sourceRoot.transitionDescriptorTag;
  203. if (sourceRoot.nameLength >= root.preferredName.size()
  204. || !convert_name_status(sourceRoot.nameStatus, root.selectionStatus)) {
  205. return false;
  206. }
  207. root.preferredNameLength = static_cast<std::uint8_t>(sourceRoot.nameLength);
  208. std::copy_n(sourceRoot.name.begin(), sourceRoot.nameLength, root.preferredName.begin());
  209. work.activityRoots.push_back(root);
  210. }
  211. for (const inventory::ActivityVariant& sourceActivity : snapshot.activities) {
  212. manifest::ActivityVariantRecord activity{};
  213. activity.activityIndex = sourceActivity.definition.activityIndex;
  214. activity.definitionHash = sourceActivity.definition.definitionHash;
  215. activity.activityRootTag = sourceActivity.joinStatus == inventory::JoinStatus::exact
  216. ? sourceActivity.activityRootTag
  217. : manifest::kAbsentTag;
  218. activity.scenarioTag = sourceActivity.joinStatus == inventory::JoinStatus::exact
  219. ? sourceActivity.scenarioTag
  220. : manifest::kAbsentTag;
  221. activity.matchmakingConfigTag = sourceActivity.bindingEvidence.matchmakingConfigTag;
  222. if (sourceActivity.definition.internalNameLength >= activity.internalName.size()
  223. || !convert_join_status(sourceActivity.joinStatus, activity.joinStatus)
  224. || !convert_binding_disposition(sourceActivity.bindingDisposition,
  225. activity.bindingDisposition)
  226. || !convert_binding_reason(sourceActivity.bindingReason, activity.bindingReason)
  227. || !convert_evidence_basis(sourceActivity.bindingEvidenceBasis,
  228. activity.bindingEvidenceBasis)
  229. || !convert_runnable_status(sourceActivity.runnableStatus,
  230. activity.runnableStatus)) {
  231. return false;
  232. }
  233. activity.internalNameLength =
  234. static_cast<std::uint8_t>(sourceActivity.definition.internalNameLength);
  235. std::copy_n(sourceActivity.definition.internalName.begin(),
  236. sourceActivity.definition.internalNameLength,
  237. activity.internalName.begin());
  238. activity.fullSdkAcceptable = sourceActivity.fullSdkAcceptable;
  239. activity.hasInternalName = sourceActivity.bindingEvidence.hasInternalName;
  240. activity.hasMatchmakingConfig = sourceActivity.bindingEvidence.hasMatchmakingConfig;
  241. activity.activityRootCandidateTags =
  242. sourceActivity.bindingEvidence.activityRootCandidateTags;
  243. activity.scenarioNameCandidateTags =
  244. sourceActivity.bindingEvidence.scenarioNameCandidateTags;
  245. activity.evidenceRootTags = sourceActivity.bindingEvidence.evidenceRootTags;
  246. activity.locators.reserve(sourceActivity.bindingEvidence.locators.size());
  247. for (const inventory::PackageLocator& locator :
  248. sourceActivity.bindingEvidence.locators) {
  249. activity.locators.push_back({locator.tag, locator.offset});
  250. }
  251. work.activityVariants.push_back(activity);
  252. }
  253. const bool converted = work.scenarios.size() == snapshot.scenarios.size()
  254. && work.activityRoots.size() == snapshot.activityRoots.size()
  255. && work.activityVariants.size() == snapshot.activities.size();
  256. if (!converted) {
  257. return false;
  258. }
  259. work.activityInventory = std::move(snapshot);
  260. return true;
  261. } catch (...) {
  262. work.scenarios.clear();
  263. work.activityRoots.clear();
  264. work.activityVariants.clear();
  265. return false;
  266. }
  267. }
  268. /** A native inventory may publish only when every row has a truthful terminal disposition. */
  269. [[nodiscard]] bool binding_ready(const inventory::BindingCompleteness& completeness,
  270. std::size_t activityCount) noexcept {
  271. return completeness.total == activityCount
  272. && completeness.fixedScenario + completeness.namedDefinitionUnavailable
  273. + completeness.noDirectFixedActivityName + completeness.unresolvedRunnable
  274. == completeness.total
  275. && completeness.unresolvedRunnable == 0
  276. && completeness.status == inventory::BindingCompletenessStatus::ready;
  277. }
  278. /** A persisted catalog is usable only when its recomputed partition is publishable. */
  279. [[nodiscard]] bool binding_ready(const manifest::BindingCompleteness& completeness,
  280. std::size_t activityCount) noexcept {
  281. return completeness.total == activityCount
  282. && completeness.fixedScenario + completeness.namedDefinitionUnavailable
  283. + completeness.noDirectFixedActivityName + completeness.unresolvedRunnable
  284. == completeness.total
  285. && completeness.unresolvedRunnable == 0
  286. && completeness.status == manifest::BindingCompletenessStatus::ready;
  287. }
  288. } // namespace sunrise::client::content::activity::sdk_generation::worker_internal