activity_sdk_policy_input_adapter.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #include "activity_sdk_policy_input_adapter.h"
  2. #include <string_view>
  3. #include <utility>
  4. #include "activity_sdk_policy_inventory_internal.h"
  5. namespace sunrise::client::content::activity::sdk_generation::policy_input_adapter {
  6. namespace {
  7. namespace format = state::activity_sdk::format;
  8. namespace catalog = state::build_data::scriptables;
  9. namespace internal = policy_inventory::internal;
  10. /**
  11. * Resolves one owned topology value only while its bounded UTF-8 shape is valid.
  12. * @param source The fixed native text storage.
  13. * @param output Receives a view into source.
  14. * @return True when the retained length, terminator, and UTF-8 are valid.
  15. */
  16. [[nodiscard]] bool text_view(const topology_inventory::Text& source,
  17. std::string_view& output) noexcept {
  18. output = {};
  19. if (source.length >= source.value.size() || source.value[source.length] != '\0') {
  20. return false;
  21. }
  22. output = std::string_view(source.value.data(), source.length);
  23. return internal::valid_text(output);
  24. }
  25. /**
  26. * Converts the exhaustive activity join outcome without relying on enum values.
  27. * @param source The native activity inventory outcome.
  28. * @param output Receives the policy outcome.
  29. * @return True when source names a supported outcome.
  30. */
  31. [[nodiscard]] bool convert_join(activity_inventory::JoinStatus source,
  32. policy::ActivityJoinStatus& output) noexcept {
  33. switch (source) {
  34. case activity_inventory::JoinStatus::exact:
  35. output = policy::ActivityJoinStatus::exact;
  36. return true;
  37. case activity_inventory::JoinStatus::sourceNameMissing:
  38. output = policy::ActivityJoinStatus::sourceNameMissing;
  39. return true;
  40. case activity_inventory::JoinStatus::liveNameMissing:
  41. output = policy::ActivityJoinStatus::liveNameMissing;
  42. return true;
  43. case activity_inventory::JoinStatus::liveNameAmbiguous:
  44. output = policy::ActivityJoinStatus::liveNameAmbiguous;
  45. return true;
  46. }
  47. return false;
  48. }
  49. /**
  50. * Adds final activity rows while cross-checking post-enrichment topology state.
  51. * @param activities The source of definition identity and join outcomes.
  52. * @param topology The final activity rows.
  53. * @param output Receives borrowed policy rows.
  54. * @return True when row order, names, hashes, joins, and flags agree.
  55. */
  56. [[nodiscard]] bool build_activities(const activity_inventory::Snapshot& activities,
  57. const topology_inventory::Snapshot& topology,
  58. Snapshot& output) {
  59. if (activities.activities.size() != topology.activities.size()) {
  60. return false;
  61. }
  62. output.activities.reserve(topology.activities.size());
  63. for (std::size_t index = 0; index < topology.activities.size(); ++index) {
  64. const activity_inventory::ActivityVariant& source = activities.activities[index];
  65. const topology_inventory::Activity& final = topology.activities[index];
  66. std::string_view id{};
  67. std::string_view internalName{};
  68. std::string_view displayName{};
  69. policy::ActivityJoinStatus join{};
  70. const bool joined = source.joinStatus == activity_inventory::JoinStatus::exact;
  71. if (source.definition.internalNameLength >= source.definition.internalName.size()
  72. || source.definition.internalName[source.definition.internalNameLength] != '\0'
  73. || source.definition.activityIndex != index || final.activityIndex != index
  74. || source.definition.definitionHash != final.definitionHash || !text_view(final.id, id)
  75. || !text_view(final.internalName, internalName)
  76. || !text_view(final.displayName, displayName) || !convert_join(source.joinStatus, join)
  77. || internalName
  78. != std::string_view(source.definition.internalName.data(),
  79. source.definition.internalNameLength)
  80. || (joined
  81. && (final.scenarioIndex >= topology.scenarios.size()
  82. || topology.scenarios[final.scenarioIndex].tag != source.scenarioTag
  83. || final.exactFlags != format::kActivityExactMask))
  84. || (!joined && (final.scenarioIndex != catalog::kNoRow || final.exactFlags != 0))) {
  85. return false;
  86. }
  87. output.activities.push_back(
  88. {static_cast<std::uint32_t>(index), id, internalName, displayName, join});
  89. }
  90. return true;
  91. }
  92. /**
  93. * Adds final slots and expands enrichment-local aliases to global slot references.
  94. * @param topology The final base slot order and identities.
  95. * @param enrichment The validated slot names, schemas, flags, and aliases.
  96. * @param output Receives borrowed policy rows.
  97. * @return True when every slot and alias range closes exactly.
  98. */
  99. [[nodiscard]] bool build_slots(const topology_inventory::Snapshot& topology,
  100. const topology_enrichment::Snapshot& enrichment,
  101. Snapshot& output) {
  102. if (topology.slots.size() != enrichment.slots.size()) {
  103. return false;
  104. }
  105. output.slots.reserve(topology.slots.size());
  106. output.slotAliases.reserve(enrichment.slotAliases.size());
  107. std::size_t nextAlias = 0;
  108. for (std::size_t index = 0; index < topology.slots.size(); ++index) {
  109. const topology_inventory::Slot& source = topology.slots[index];
  110. const topology_enrichment::Slot& final = enrichment.slots[index];
  111. std::string_view id{};
  112. std::string_view name{};
  113. std::string_view senseSchemaId{};
  114. std::string_view authSchemaId{};
  115. if (source.objectIndex >= topology.objects.size()
  116. || source.componentClass != format::kAbsentIndex
  117. || source.senseSchema != format::kAbsentIndex
  118. || source.authSchema != format::kAbsentIndex || source.flags != 0
  119. || (final.flags & ~format::kSlotFlagMask) != 0 || final.aliases.first != nextAlias
  120. || final.aliases.count > enrichment.slotAliases.size() - nextAlias
  121. || !text_view(source.id, id) || !text_view(final.name, name)
  122. || !text_view(final.senseSchemaId, senseSchemaId)
  123. || !text_view(final.authSchemaId, authSchemaId)) {
  124. return false;
  125. }
  126. output.slots.push_back({source.objectIndex,
  127. source.slotIndex,
  128. source.slotType,
  129. final.componentClass,
  130. final.senseSchema,
  131. final.authSchema,
  132. id,
  133. name,
  134. senseSchemaId,
  135. authSchemaId,
  136. policy::ExtractionStatus::complete,
  137. final.flags});
  138. for (std::uint32_t ordinal = 0; ordinal < final.aliases.count; ++ordinal) {
  139. std::string_view alias{};
  140. if (!text_view(enrichment.slotAliases[nextAlias + ordinal], alias)) {
  141. return false;
  142. }
  143. output.slotAliases.push_back({static_cast<std::uint32_t>(index), alias});
  144. }
  145. nextAlias += final.aliases.count;
  146. }
  147. return nextAlias == enrichment.slotAliases.size();
  148. }
  149. /**
  150. * Adds occurrence bindings in canonical topology row order.
  151. * @param topology The closed source topology.
  152. * @param output Receives compact policy bindings.
  153. * @return True when every occurrence names valid parent rows.
  154. */
  155. [[nodiscard]] bool build_occurrences(const topology_inventory::Snapshot& topology,
  156. Snapshot& output) {
  157. output.occurrences.reserve(topology.occurrences.size());
  158. for (const topology_inventory::Occurrence& source : topology.occurrences) {
  159. if (source.scenarioIndex >= topology.scenarios.size()
  160. || source.objectIndex >= topology.objects.size()) {
  161. return false;
  162. }
  163. output.occurrences.push_back({source.scenarioIndex, source.objectIndex});
  164. }
  165. return true;
  166. }
  167. } // namespace
  168. policy::Inputs Snapshot::view() const noexcept {
  169. return {activities,
  170. slots,
  171. slotAliases,
  172. occurrences,
  173. policy::host_surfaces(),
  174. scenarioCount,
  175. objectCount};
  176. }
  177. /**
  178. * Adapts closed activity/topology rows and validated topology enrichment transactionally.
  179. * @param activities The final source of activity join outcomes.
  180. * @param topology The closed topology after activity enrichment was applied.
  181. * @param enrichment The validated final slot names, schemas, flags, and local alias ranges.
  182. * @param output Receives borrowed input rows only after every cross-check succeeds.
  183. * @return True when row order, final flags, text, and child ranges close exactly.
  184. */
  185. bool build(const activity_inventory::Snapshot& activities,
  186. const topology_inventory::Snapshot& topology,
  187. const topology_enrichment::Snapshot& enrichment,
  188. Snapshot& output) noexcept {
  189. if (!topology.ready || topology.nameInventoryComplete || topology.stringInventoryComplete
  190. || topology.activities.size() > policy::kMaximumActivityCount
  191. || topology.scenarios.size() > policy::kMaximumScenarioCount
  192. || topology.objects.size() > policy::kMaximumObjectCount
  193. || topology.occurrences.size() > policy::kMaximumOccurrenceCount
  194. || topology.slots.size() > policy::kMaximumSlotCount
  195. || enrichment.slotAliases.size() > policy::kMaximumPolicyTextCount) {
  196. return false;
  197. }
  198. try {
  199. Snapshot pending{};
  200. pending.scenarioCount = static_cast<std::uint32_t>(topology.scenarios.size());
  201. pending.objectCount = static_cast<std::uint32_t>(topology.objects.size());
  202. if (!build_activities(activities, topology, pending)
  203. || !build_slots(topology, enrichment, pending) || !build_occurrences(topology, pending)
  204. || !internal::valid_inputs(pending.view())) {
  205. return false;
  206. }
  207. output = std::move(pending);
  208. return true;
  209. } catch (...) {
  210. return false;
  211. }
  212. }
  213. } // namespace sunrise::client::content::activity::sdk_generation::policy_input_adapter