载入中...
搜索中...
未找到
Container.cpp
浏览该文件的文档.
1#include "common/Container.h"
2
3#include <algorithm>
4#include <exception>
5#include <limits>
6#include <unordered_set>
7#include <utility>
8
9namespace eve::container {
10namespace {
11
12[[nodiscard]]
13[[nodiscard]] const MembershipEntry* findEntry(const ContainerSnapshot& snapshot, const MembershipId& id) {
14 const MembershipEntry* found = nullptr;
15 for (const auto& entry : snapshot.entries) {
16 if (entry.membership.object != id) continue;
17 if (found != nullptr) return nullptr;
18 found = &entry;
19 }
20 return found;
21}
22
23[[nodiscard]] bool hasDuplicateMemberships(const ContainerSnapshot& snapshot) {
24 std::unordered_set<MembershipId> ids;
25 std::unordered_set<std::int32_t> slots;
26 for (const auto& entry : snapshot.entries) {
27 if (!entry.membership.object.isValid() || !ids.insert(entry.membership.object).second) return true;
28 if (!entry.membership.slot.isValid() || !slots.insert(entry.membership.slot.value()).second) return true;
29 if (entry.object.id != entry.membership.object || entry.object.quantity == 0 ||
30 entry.membership.generation.isZero())
31 return true;
32 }
33 return false;
34}
35
36[[nodiscard]] Result<void> validateSnapshot(const ContainerSnapshot& snapshot, const IContainer& container) {
37 if (!snapshot.id.isValid() || snapshot.id != container.descriptor().id)
38 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "container snapshot identity does not match adapter", {}));
39 if (hasDuplicateMemberships(snapshot))
40 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "container snapshot contains duplicate or invalid membership", {}));
41 const auto& descriptor = container.descriptor();
42 if (!descriptor.capacity.isUnlimited() && snapshot.entries.size() > descriptor.capacity.value())
43 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "container snapshot exceeds capacity", {}));
44 if (!descriptor.capacity.isUnlimited()) {
45 for (const auto& entry : snapshot.entries) {
46 if (static_cast<std::size_t>(entry.membership.slot.value()) >= descriptor.capacity.value())
47 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "container snapshot contains a slot outside capacity", {}));
48 }
49 }
50 return Result<void>::success();
51}
52
53[[nodiscard]] Result<ContainerSnapshot> removeEntry(const ContainerSnapshot& original,
54 const ContainerDescriptor& descriptor, const MembershipId& object) {
55 ContainerSnapshot candidate = original;
56 const auto it = std::find_if(candidate.entries.begin(), candidate.entries.end(),
57 [&](const MembershipEntry& entry) { return entry.membership.object == object; });
58 if (it == candidate.entries.end())
60 Diagnostic::error(DiagnosticCode::NotFound, "snapshot membership was not found"));
61 candidate.entries.erase(it);
62 if (descriptor.ordering != Ordering::ExplicitSlots) {
63 if (candidate.entries.size() > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
65 Diagnostic::error(DiagnosticCode::InvariantViolation, "container has too many indexed memberships"));
66 for (std::size_t index = 0; index < candidate.entries.size(); ++index)
67 candidate.entries[index].membership.slot = SlotIndex(static_cast<std::int32_t>(index));
68 }
69 return Result<ContainerSnapshot>::success(std::move(candidate));
70}
71
72[[nodiscard]] Result<SlotIndex> chooseDestinationSlot(const ContainerSnapshot& snapshot,
74 std::optional<SlotIndex> requested) {
75 if (requested) {
76 if (!requested->isValid())
78 Diagnostic::error(DiagnosticCode::InvalidArgument, "destination slot is invalid"));
79 const auto value = static_cast<std::size_t>(requested->value());
80 if (descriptor.ordering == Ordering::ExplicitSlots) {
81 if (!descriptor.capacity.isUnlimited() && value >= descriptor.capacity.value())
83 Diagnostic::error(DiagnosticCode::Conflict, "destination slot exceeds container capacity"));
84 for (const auto& entry : snapshot.entries)
85 if (entry.membership.slot == *requested)
87 Diagnostic::error(DiagnosticCode::Conflict, "destination slot is occupied"));
88 return Result<SlotIndex>::success(*requested);
89 }
90 if (value > snapshot.entries.size())
92 Diagnostic::error(DiagnosticCode::InvalidArgument, "destination slot is out of range"));
93 return Result<SlotIndex>::success(*requested);
94 }
95
96 if (descriptor.ordering != Ordering::ExplicitSlots) {
97 if (snapshot.entries.size() > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max()))
99 Diagnostic::error(DiagnosticCode::InvariantViolation, "container has too many indexed memberships"));
100 return Result<SlotIndex>::success(SlotIndex(static_cast<std::int32_t>(snapshot.entries.size())));
101 }
102
103 const std::size_t limit =
104 descriptor.capacity.isUnlimited() ? snapshot.entries.size() + 1u : descriptor.capacity.value();
105 std::unordered_set<std::int32_t> occupied;
106 for (const auto& entry : snapshot.entries) occupied.insert(entry.membership.slot.value());
107 for (std::size_t index = 0; index < limit; ++index) {
108 if (index > static_cast<std::size_t>(std::numeric_limits<std::int32_t>::max())) break;
109 if (!occupied.contains(static_cast<std::int32_t>(index)))
110 return Result<SlotIndex>::success(SlotIndex(static_cast<std::int32_t>(index)));
111 }
112 return Result<SlotIndex>::failure(Diagnostic::error(DiagnosticCode::Conflict, "container has no available slot"));
113}
114
116 const MembershipEntry& sourceEntry,
117 std::optional<SlotIndex> requested) {
118 auto destinationResult = chooseDestinationSlot(base, descriptor, requested);
119 if (!destinationResult) return Result<ContainerSnapshot>::failure(destinationResult.status());
120 const SlotIndex destination = destinationResult.value();
121 MembershipEntry moved = sourceEntry;
122 moved.membership.slot = destination;
123 if (descriptor.ordering == Ordering::ExplicitSlots) {
124 base.entries.push_back(std::move(moved));
125 } else {
126 base.entries.insert(base.entries.begin() + destination.value(), std::move(moved));
127 for (std::size_t index = 0; index < base.entries.size(); ++index)
128 base.entries[index].membership.slot = SlotIndex(static_cast<std::int32_t>(index));
129 }
130 return Result<ContainerSnapshot>::success(std::move(base));
131}
132
133} // namespace
134
136 if (request.source == nullptr || request.destination == nullptr || !request.object.isValid()) {
138 DiagnosticCode::InvalidArgument, "transfer requires two containers and a membership object"));
139 }
140
141 const bool sameContainer = request.source == request.destination;
142 auto sourceSnapshotResult = request.source->snapshot();
143 if (!sourceSnapshotResult) {
144 return Result<TransferReceipt>::failure(sourceSnapshotResult.status());
145 }
146 ContainerSnapshot sourceSnapshot = std::move(sourceSnapshotResult).takeValue();
147 ContainerSnapshot destinationSnapshot;
148 if (sameContainer) {
149 destinationSnapshot = sourceSnapshot;
150 } else {
151 auto destinationSnapshotResult = request.destination->snapshot();
152 if (!destinationSnapshotResult) {
153 return Result<TransferReceipt>::failure(destinationSnapshotResult.status());
154 }
155 destinationSnapshot = std::move(destinationSnapshotResult).takeValue();
156 }
157 auto sourceSnapshotValid = validateSnapshot(sourceSnapshot, *request.source);
158 if (!sourceSnapshotValid) {
159 return Result<TransferReceipt>::failure(sourceSnapshotValid.status());
160 }
161 auto destinationSnapshotValid = validateSnapshot(destinationSnapshot, *request.destination);
162 if (!destinationSnapshotValid) {
163 return Result<TransferReceipt>::failure(destinationSnapshotValid.status());
164 }
165
166 if (request.expectedSourceRevision && *request.expectedSourceRevision != sourceSnapshot.revision) {
168 Diagnostic::error(DiagnosticCode::StaleHandle, "source container revision is stale"));
169 }
170 if (request.expectedDestinationRevision && *request.expectedDestinationRevision != destinationSnapshot.revision) {
172 Diagnostic::error(DiagnosticCode::StaleHandle, "destination container revision is stale"));
173 }
174
175 const MembershipEntry* sourceEntry = findEntry(sourceSnapshot, request.object);
176 if (sourceEntry == nullptr) {
177 bool duplicate = false;
178 for (const auto& entry : sourceSnapshot.entries)
179 if (entry.membership.object == request.object) duplicate = true;
182 duplicate ? "source contains duplicate membership" : "source membership was not found"));
183 }
184 if (request.sourceSlot && *request.sourceSlot != sourceEntry->membership.slot) {
186 Diagnostic::error(DiagnosticCode::StaleHandle, "source slot no longer matches object"));
187 }
188
189 if (!sameContainer && request.source->descriptor().id == request.destination->descriptor().id) {
191 DiagnosticCode::Conflict, "different container adapters must not expose the same container identity"));
192 }
193 for (const auto& entry : destinationSnapshot.entries) {
194 if (entry.membership.object == request.object && !sameContainer)
196 Diagnostic::error(DiagnosticCode::Conflict, "destination already contains this membership object"));
197 if (request.destinationSlot && entry.membership.slot == *request.destinationSlot &&
198 !(sameContainer && entry.membership.object == request.object)) {
200 Diagnostic::error(DiagnosticCode::Conflict, "destination slot is occupied"));
201 }
202 }
203
204 auto accepted =
205 request.destination->validateInsert(sourceEntry->object, request.destinationSlot,
206 sameContainer ? std::optional<MembershipId>(request.object) : std::nullopt);
207 if (!accepted) {
208 return Result<TransferReceipt>::failure(accepted.status());
209 }
210
211 if (sameContainer && request.destinationSlot && sourceEntry->membership.slot == *request.destinationSlot) {
212 TransferReceipt receipt{sourceSnapshot.id,
213 destinationSnapshot.id,
214 request.object,
215 sourceEntry->membership.slot,
216 sourceEntry->membership.slot,
217 sourceSnapshot.revision,
218 destinationSnapshot.revision};
220 }
221
222 auto sourceRevision = sourceSnapshot.revision.incremented();
223 if (!sourceRevision)
225 Diagnostic::error(DiagnosticCode::InvariantViolation, "source revision exhausted"));
226 auto sourceCandidateResult = removeEntry(sourceSnapshot, request.source->descriptor(), request.object);
227 if (!sourceCandidateResult) return Result<TransferReceipt>::failure(sourceCandidateResult.status());
228 ContainerSnapshot sourceCandidate = std::move(sourceCandidateResult).takeValue();
229 sourceCandidate.revision = *sourceRevision;
230
231 ContainerSnapshot destinationCandidate;
232 SlotIndex destinationSlot = SlotIndex::invalid();
233 if (sameContainer) {
234 auto candidateResult = insertEntry(std::move(sourceCandidate), request.source->descriptor(), *sourceEntry,
235 request.destinationSlot);
236 if (!candidateResult) {
237 return Result<TransferReceipt>::failure(candidateResult.status());
238 }
239 destinationCandidate = std::move(candidateResult).takeValue();
240 destinationCandidate.revision = *sourceRevision;
241 const auto* moved = findEntry(destinationCandidate, request.object);
242 if (moved == nullptr)
244 Diagnostic::error(DiagnosticCode::InvariantViolation, "same-container candidate lost membership"));
245 destinationSlot = moved->membership.slot;
246 } else {
247 auto destinationRevision = destinationSnapshot.revision.incremented();
248 if (!destinationRevision)
250 Diagnostic::error(DiagnosticCode::InvariantViolation, "destination revision exhausted"));
251 auto candidateResult =
252 insertEntry(destinationSnapshot, request.destination->descriptor(), *sourceEntry, request.destinationSlot);
253 if (!candidateResult) return Result<TransferReceipt>::failure(candidateResult.status());
254 destinationCandidate = std::move(candidateResult).takeValue();
255 destinationCandidate.revision = *destinationRevision;
256 const auto* moved = findEntry(destinationCandidate, request.object);
257 if (moved == nullptr)
259 Diagnostic::error(DiagnosticCode::InvariantViolation, "destination candidate lost membership"));
260 destinationSlot = moved->membership.slot;
261 }
262
263 // For a same-container move, `sourceCandidate` was consumed while building
264 // the final candidate. The adapter must validate and commit that final
265 // layout, not the moved-from removal-only intermediate.
266 const auto& sourceCommitCandidate = sameContainer ? destinationCandidate : sourceCandidate;
267 auto sourcePreparedResult = request.source->prepare(sourceSnapshot, sourceCommitCandidate);
268 if (!sourcePreparedResult) return Result<TransferReceipt>::failure(sourcePreparedResult.status());
269 auto sourcePrepared = std::move(sourcePreparedResult).takeValue();
270
271 if (sameContainer) {
272 sourcePrepared->commit();
273 } else {
274 auto destinationPreparedResult = request.destination->prepare(destinationSnapshot, destinationCandidate);
275 if (!destinationPreparedResult) {
276 sourcePrepared->rollback();
277 return Result<TransferReceipt>::failure(destinationPreparedResult.status());
278 }
279 auto destinationPrepared = std::move(destinationPreparedResult).takeValue();
280 sourcePrepared->commit();
281 destinationPrepared->commit();
282 }
283
284 TransferReceipt receipt{sourceSnapshot.id,
285 destinationSnapshot.id,
286 request.object,
287 sourceEntry->membership.slot,
288 destinationSlot,
289 *sourceRevision,
290 sameContainer ? *sourceRevision : destinationCandidate.revision};
292 if (eventSink) {
293 try {
294 eventSink(TransferEvent{receipt.source, receipt.destination, receipt.object, receipt.sourceSlot,
295 receipt.destinationSlot, receipt.sourceRevision, receipt.destinationRevision});
296 } catch (const std::exception&) {
297 try {
298 std::vector<Diagnostic> diagnostics;
300 "transfer committed but its event callback threw"));
301 resultStatus = Status(StatusCode::Applied, std::move(diagnostics));
302 } catch (...) {
303 resultStatus = Status::success(StatusCode::Applied);
304 }
305 } catch (...) {
306 try {
307 std::vector<Diagnostic> diagnostics;
309 "transfer committed but its event callback threw"));
310 resultStatus = Status(StatusCode::Applied, std::move(diagnostics));
311 } catch (...) {
312 resultStatus = Status::success(StatusCode::Applied);
313 }
314 }
315 }
316 return Result<TransferReceipt>::success(std::move(receipt), std::move(resultStatus));
317}
318
319} // namespace eve::container
double value
std::string descriptor
Low-level container membership, zone and atomic transfer contracts.
const GltfImportRequest & request
glm::uvec4 ids
bool found
bool occupied
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
int limit
Definition TreeMesh.cpp:164
uint32_t index
static Diagnostic warning(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct a warning diagnostic.
Definition Diagnostic.h:134
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
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
bool isValid() const noexcept
Return whether this ID is usable by a transfer request.
Definition Container.h:58
Borrowed container implementation owned by a domain adapter.
Definition Container.h:287
virtual const ContainerDescriptor & descriptor() const noexcept=0
Return immutable container metadata.
Strong identity of an object which can have one container membership.
Definition Container.h:68
Explicit slot number; it never implicitly converts to an integer.
Definition Container.h:117
static constexpr SlotIndex invalid() noexcept
Invalid.
Definition Container.h:124
constexpr std::int32_t value() const noexcept
Value.
Definition Container.h:128
static Result< TransferReceipt > transfer(const TransferRequest &request, TransferEventSink eventSink={})
Preflight and atomically transfer one object.
constexpr std::optional< StrongUint64 > incremented() const noexcept
Returns the next value, or empty instead of unsigned wraparound.
std::function< void(const TransferEvent &)> TransferEventSink
Synchronous callback receiving a committed transfer payload.
Definition Container.h:351
Static metadata and acceptance policy of a container.
Definition Container.h:197
Lossless snapshot of one container at one revision.
Definition Container.h:190
std::vector< MembershipEntry > entries
Definition Container.h:193
Complete adapter-owned state used to build a candidate replacement.
Definition Container.h:184
Strong event payload emitted only after a transfer is fully committed.
Definition Container.h:334
Observable receipt for one committed or explicit no-op transfer.
Definition Container.h:365
Requested optimistic-concurrency transfer. All pointers are borrowed.
Definition Container.h:354