载入中...
搜索中...
未找到
EvpackRegistry.cpp
浏览该文件的文档.
2
3#include <limits>
4#include <set>
5#include <exception>
6
7namespace eve::asset {
8namespace {
9
10template <class Entry>
11bool sameGeneration(const EvpackHandle& handle, const Entry& entry) {
12 return handle.generation == entry.generation && handle.buildId == entry.pack->buildId();
13}
14
15std::vector<Diagnostic> dispatchCallbacks(
16 const std::vector<EvpackRegistry::Callback>& callbacks,
17 const EvpackRegistryEvent& event) {
18 std::vector<Diagnostic> diagnostics;
19 for (const auto& callback : callbacks) {
20 try {
21 auto notified = callback(event);
22 if (!notified && notified.error()) diagnostics.push_back(*notified.error());
23 } catch (const std::exception& error) {
25 "registry callback threw: " + std::string(error.what()),
26 event.handle.packageId.format(), {},
27 "asset.evpack.registry"));
28 } catch (...) {
30 "registry callback threw a non-standard exception",
31 event.handle.packageId.format(), {},
32 "asset.evpack.registry"));
33 }
34 }
35 return diagnostics;
36}
37
38} // namespace
39
41 const EvpackLimits& limits,
42 const EvpackTrust& trust) {
43 auto parsed = parseEvpack(bytes, limits, trust);
44 if (!parsed) return Result<PreparedEvpackMount>::failure(parsed.status());
45 auto pack = std::make_shared<const Evpack>(std::move(parsed).takeValue());
47}
48
50 if (!callback)
52 DiagnosticCode::InvalidArgument, "registry callback is empty", {}, {}, "asset.evpack.registry"));
53 std::lock_guard lock(mutex_);
54 if (nextSubscription_ == std::numeric_limits<std::uint64_t>::max())
56 Diagnostic::error(DiagnosticCode::InvariantViolation, "registry subscription identity is exhausted", {}, {},
57 "asset.evpack.registry"));
58 const auto id = ++nextSubscription_;
59 callbacks_.emplace(id, std::move(callback));
61}
62
64 if (subscription.value == 0)
65 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "registry subscription identity is invalid", {}, {},
66 "asset.evpack.registry"));
67 std::lock_guard lock(mutex_);
68 if (callbacks_.erase(subscription.value) == 0)
69 return Result<void>::failure(Diagnostic::error(DiagnosticCode::NotFound, "registry subscription is not active", {}, {},
70 "asset.evpack.registry"));
71 return Result<void>::success();
72}
73
75 if (!candidate.pack_)
77 "mount candidate has already been consumed", {},
78 {}, "asset.evpack.registry"));
79 const PersistentId packageId = candidate.pack_->packageId();
80 bool replaced = false;
82 std::vector<Callback> callbacks;
83 {
84 std::lock_guard lock(mutex_);
85 std::set<PersistentId> available;
86 std::set<PersistentId> candidateAssets;
87 for (const auto& chunk : candidate.pack_->chunks()) {
88 available.emplace(chunk.assetId);
89 candidateAssets.emplace(chunk.assetId);
90 }
91 for (const auto& [mountedId, entry] : packages_) {
92 if (mountedId == packageId) continue;
93 for (const auto& chunk : entry.pack->chunks()) {
94 if (candidateAssets.contains(chunk.assetId))
96 DiagnosticCode::Conflict, "runtime asset identity already has another mounted provider",
97 chunk.assetId.format(), {}, "asset.evpack.registry"));
98 available.emplace(chunk.assetId);
99 }
100 }
101 for (const auto& chunk : candidate.pack_->chunks()) {
102 for (const auto& dependency : chunk.dependencies) {
103 if (!available.contains(dependency))
105 Diagnostic::error(DiagnosticCode::NotFound, "required runtime dependency is not mounted",
106 dependency.format(), {}, "asset.evpack.registry"));
107 }
108 }
109 for (const auto& [mountedId, entry] : packages_) {
110 if (mountedId == packageId) continue;
111 for (const auto& chunk : entry.pack->chunks()) {
112 for (const auto& dependency : chunk.dependencies) {
113 if (!available.contains(dependency))
115 DiagnosticCode::NotFound, "replacement would invalidate a mounted package dependency",
116 dependency.format(), {}, "asset.evpack.registry"));
117 }
118 }
119 }
120 replaced = packages_.contains(packageId);
121 std::uint64_t& next = nextGenerations_[packageId];
122 if (next == std::numeric_limits<std::uint64_t>::max())
124 Diagnostic::error(DiagnosticCode::InvariantViolation, "package generation is exhausted",
125 packageId.format(), {}, "asset.evpack.registry"));
126 ++next;
127 auto pack = std::move(candidate.pack_);
128 handle = {packageId, pack->buildId(), next};
129 packages_.insert_or_assign(packageId, Entry{std::move(pack), next});
130 callbacks.reserve(callbacks_.size());
131 for (const auto& [id, callback] : callbacks_) {
132 (void)id;
133 callbacks.push_back(callback);
134 }
135 }
138 handle};
139 auto callbackDiagnostics = dispatchCallbacks(callbacks, event);
140 return Result<EvpackMountReceipt>::success({handle, replaced, std::move(callbackDiagnostics)});
141}
142
144 std::lock_guard lock(mutex_);
145 const auto found = packages_.find(handle.packageId);
146 if (found == packages_.end())
148 Diagnostic::error(DiagnosticCode::NotFound, "runtime package is not mounted", handle.packageId.format(), {},
149 "asset.evpack.registry"));
150 if (!sameGeneration(handle, found->second))
152 Diagnostic::error(DiagnosticCode::StaleHandle, "runtime package handle is stale", handle.packageId.format(),
153 {}, "asset.evpack.registry"));
154 return Result<std::shared_ptr<const Evpack>>::success(found->second.pack);
155}
156
158 const AssetRef& assetRef, std::string_view expectedType,
159 const EvpackCapabilities& capabilities) const {
160 if (expectedType.empty())
162 DiagnosticCode::InvalidArgument, "expected runtime asset type is empty", {}, {}, "asset.evpack.registry"));
163 std::lock_guard lock(mutex_);
164 bool identityFound = false;
165 bool typeFound = false;
166 for (const auto& [packageId, entry] : packages_) {
167 const EvpackChunk* representative = nullptr;
168 for (const auto& chunk : entry.pack->chunks()) {
169 if (chunk.assetId != assetRef.id()) continue;
170 identityFound = true;
171 representative = &chunk;
172 const std::string actual = chunk.type + "/" +
173 std::to_string(chunk.schemaVersion.value());
174 if (actual == expectedType) typeFound = true;
175 else
177 DiagnosticCode::TypeMismatch, "runtime asset provider has a different canonical type",
178 assetRef.format(), {}, "asset.evpack.registry"));
179 }
180 if (!representative) continue;
181 auto selected = selectEvpackVariant(*entry.pack, capabilities);
182 if (!selected) continue;
184 {{packageId, entry.pack->buildId(), entry.generation}, assetRef,
185 std::string(expectedType), capabilities});
186 }
187 if (identityFound && typeFound)
189 "asset provider has no compatible variant",
190 assetRef.format(), {}, "asset.evpack.registry"));
192 "runtime asset has no mounted provider",
193 assetRef.format(), {}, "asset.evpack.registry"));
194}
195
197 const EvpackAssetHandle& handle, std::uint64_t maximumDecodedBytes) const {
198 auto pack = resolve(handle.package);
199 if (!pack) return Result<RuntimeAssetPayload>::failure(pack.status());
200 EvpackResourceReader reader(std::move(pack).takeValue());
201 return reader.read(handle.asset, handle.expectedType, handle.capabilities,
202 maximumDecodedBytes);
203}
204
206 const PersistentId& assetId, std::string_view type,
208 std::uint32_t chunkId,
209 std::uint32_t variantIndex) const {
210 auto resolved = resolve(handle);
211 if (!resolved) return Result<EvpackChunkHandle>::failure(resolved.status());
212 const auto pack = std::move(resolved).takeValue();
213 for (std::size_t index = 0; index < pack->chunks().size(); ++index) {
214 const auto& chunk = pack->chunks()[index];
215 if (chunk.assetId == assetId && chunk.type == type && chunk.kind == kind && chunk.chunkId == chunkId &&
216 chunk.variantIndex == variantIndex)
217 return Result<EvpackChunkHandle>::success({handle, static_cast<std::uint32_t>(index)});
218 }
220 DiagnosticCode::NotFound, "runtime chunk is not present", assetId.format(), {}, "asset.evpack.registry"));
221}
222
224 auto resolved = resolve(handle.package);
225 if (!resolved) return Result<std::vector<std::uint8_t>>::failure(resolved.status());
226 const auto pack = std::move(resolved).takeValue();
227 if (handle.chunkIndex >= pack->chunks().size())
229 DiagnosticCode::InvalidArgument, "chunk index is outside the TOC", {}, {}, "asset.evpack.registry"));
230 return pack->decodeChunk(handle.chunkIndex, pack->chunks()[handle.chunkIndex].decodedSize);
231}
232
234 std::vector<Callback> callbacks;
235 {
236 std::lock_guard lock(mutex_);
237 const auto found = packages_.find(handle.packageId);
238 if (found == packages_.end())
240 DiagnosticCode::NotFound, "runtime package is not mounted", {}, {}, "asset.evpack.registry"));
241 if (!sameGeneration(handle, found->second))
243 DiagnosticCode::StaleHandle, "runtime package handle is stale", {}, {}, "asset.evpack.registry"));
244 packages_.erase(found);
245 callbacks.reserve(callbacks_.size());
246 for (const auto& [id, callback] : callbacks_) {
247 (void)id;
248 callbacks.push_back(callback);
249 }
250 }
252 auto callbackDiagnostics = dispatchCallbacks(callbacks, event);
253 return Result<EvpackUnmountReceipt>::success({handle, std::move(callbackDiagnostics)});
254}
255
256} // namespace eve::asset
std::string packageId
Prepare/commit runtime package publication with stale-generation detection.
TokenKind kind
std::uint64_t bytes
std::string error
Definition Package.cpp:60
PrimitiveHandle handle
bool found
float size
Definition TreeMesh.cpp:156
uint32_t index
const AssetImportLimits & limits
ViewPreparation callback
Stable asset identity backed by PersistentId.
Definition ResourceRef.h:53
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Result< RuntimeAssetPayload > readAsset(const EvpackAssetHandle &handle, std::uint64_t maximumDecodedBytes) const
Decode an asset through its generation-qualified provider handle.
Result< std::vector< std::uint8_t > > copyChunkBytes(const EvpackChunkHandle &handle) const
Copy verified chunk bytes so the result remains valid across hot replacement.
Result< std::shared_ptr< const Evpack > > resolve(const EvpackHandle &handle) const
Resolve a current package handle to shared immutable ownership.
Result< EvpackChunkHandle > resolveChunk(const EvpackHandle &handle, const PersistentId &assetId, std::string_view type, EvpackChunkKind kind, std::uint32_t chunkId, std::uint32_t variantIndex) const
Resolve an exact asset/type/kind/variant tuple to a generation-scoped chunk handle.
Result< void > unsubscribe(EvpackRegistrySubscription subscription)
Cancel a live subscription; an unknown token returns NotFound.
Result< EvpackRegistrySubscription > subscribe(Callback callback)
Subscribe to committed lifecycle changes.
Result< EvpackAssetHandle > resolveAsset(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities) const
Resolve one stable AssetRef across uniquely owning mounted packages.
std::function< Result< void >(const EvpackRegistryEvent &)> Callback
Result< EvpackUnmountReceipt > unmount(const EvpackHandle &handle)
Unmount exactly the referenced current generation and dispatch a lock-free event.
Result< EvpackMountReceipt > commit(PreparedEvpackMount candidate)
Atomically publish an admitted package and invalidate its previous generation.
Immutable reader retaining an admitted pack by shared ownership.
Result< RuntimeAssetPayload > read(const AssetRef &asset, std::string_view expectedType, const EvpackCapabilities &capabilities, std::uint64_t maximumDecodedBytes) const
Resolve, type-check, variant-select and decode a canonical runtime asset.
Owning admitted mount candidate that has not changed observable registry state.
Id128 public API.
Definition Identity.h:112
std::string format() const
Formats the ID in lower-case canonical UUID text.
Definition Identity.h:173
Result< Evpack > parseEvpack(std::span< const std::uint8_t > bytes, const EvpackLimits &limits, const EvpackTrust &trust)
Parse, bound-check and hash-verify a complete untrusted .evpack.
Definition Evpack.cpp:542
Result< EvpackVariantSelection > selectEvpackVariant(const Evpack &pack, const EvpackCapabilities &capabilities)
Select the first explicitly ordered variant satisfied by device capabilities.
Definition Evpack.cpp:585
Result< PreparedEvpackMount > prepareEvpackMount(std::span< const std::uint8_t > bytes, const EvpackLimits &limits, const EvpackTrust &trust)
Fully parse and verify package bytes without publishing them.
EvpackChunkKind
Semantic role of a runtime chunk.
Definition Evpack.h:26
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
Generation-scoped stable-reference resolution across mounted packages.
Actual runtime device capabilities used for variant selection.
Definition Evpack.h:92
Generation-scoped handle to one verified chunk in a mounted package.
Admitted immutable chunk metadata.
Definition Evpack.h:138
Generation-scoped handle to one mounted runtime package.
Resource limits checked before package allocations and hash work.
Definition Evpack.h:126
Immutable lifecycle event delivered to registry subscribers.
Opaque identity used to cancel a registry subscription.
Trust inputs kept separate from size/resource limits.
Definition Evpack.h:75