group2 0.1.0
CSE 125 Group 2
Loading...
Searching...
No Matches
FragmentReassembler.hpp
Go to the documentation of this file.
1
19
20#pragma once
21
22#include "PacketHeader.hpp"
23
24#include <SDL3/SDL_stdinc.h>
25
26#include <cstdint>
27#include <vector>
28
29namespace net
30{
31
33{
34public:
37 static constexpr int k_maxFragments = 256;
38
54
67 Result addFragment(const PacketHeader& hdr, const uint8_t* payload, int payloadLen, std::vector<uint8_t>& assembled)
68 {
69 const uint8_t fragIdx = static_cast<uint8_t>(hdr.fragmentInfo >> 8);
70 const uint8_t fragCount = static_cast<uint8_t>(hdr.fragmentInfo & 0xFF);
71
72 if (fragCount == 0 || fragIdx >= fragCount)
73 return Result::Malformed;
74
75 // Older sequence than what we're currently reassembling? Drop.
76 // Glenn-Fiedler "more recent" comparison handles 16-bit wrap.
77 //
78 // PR-17 (BUG FIX): pre-PR-17 the args were swapped — the call
79 // was `seqMoreRecent(active->sequence, hdr.sequence)` which
80 // asks "is hdr more recent than active" (per the docstring on
81 // `seqMoreRecent` below). That made the receiver drop NEWER
82 // fragments and stay stuck on the OLDEST in-progress
83 // sequence forever — the moment a single fragment of the
84 // first multi-fragment snapshot was lost, all subsequent
85 // snapshot fragments returned Stale, freezing the client's
86 // ECS registry at whichever FULL last completed cleanly.
87 //
88 // Pre-PR-15 the bug was masked because each snapshot's
89 // fragments arrived back-to-back with no loss, completed
90 // cleanly, `active_.reset()` cleared state, and the next
91 // sequence started fresh in the `if (!active_)` path below.
92 // PR-15's per-fragment redundancy then exposed it: the
93 // duplicate copy of the LAST fragment arriving after Complete
94 // re-creates an active set at the just-finished sequence with
95 // 1/N fragments, locking out every newer sequence.
96 //
97 // Correct argument order: "is `active->sequence` more recent
98 // than `hdr.sequence`?" → if yes, hdr is older, drop.
99 if (active_ && seqMoreRecent(hdr.sequence, active_->sequence))
100 return Result::Stale;
101
102 // Newer sequence (or first fragment ever)? Reset the in-progress set.
103 if (!active_ || hdr.sequence != active_->sequence) {
104 active_.emplace();
105 active_->sequence = hdr.sequence;
106 active_->fragmentCount = fragCount;
107 active_->received.assign(fragCount, std::vector<uint8_t>{});
108 active_->haveCount = 0;
109 }
110
111 // Set must be self-consistent. If a fragment claims a different
112 // count than the in-progress set, the sender's sequence/count
113 // got mismatched somewhere. Reset and try again with this new
114 // count.
115 if (active_->fragmentCount != fragCount) {
116 active_.emplace();
117 active_->sequence = hdr.sequence;
118 active_->fragmentCount = fragCount;
119 active_->received.assign(fragCount, std::vector<uint8_t>{});
120 active_->haveCount = 0;
121 }
122
123 auto& slot = active_->received[fragIdx];
124 if (!slot.empty())
125 return Result::InProgress; // duplicate; ignore
126
127 slot.assign(payload, payload + payloadLen);
128 ++active_->haveCount;
129
130 if (active_->haveCount < active_->fragmentCount)
131 return Result::InProgress;
132
133 // Complete. Concatenate fragments in index order and hand back.
134 size_t total = 0;
135 for (const auto& s : active_->received)
136 total += s.size();
137 assembled.clear();
138 assembled.reserve(total);
139 for (const auto& s : active_->received)
140 assembled.insert(assembled.end(), s.begin(), s.end());
141
142 active_.reset();
143 return Result::Complete;
144 }
145
148 void reset() noexcept { active_.reset(); }
149
150private:
153 static bool seqMoreRecent(uint16_t s1, uint16_t s2) noexcept
154 {
155 constexpr uint16_t k_half = 32768u;
156 return ((s1 > s2) && (s1 - s2 > k_half)) || ((s2 > s1) && (s2 - s1 < k_half));
157 }
158
160 {
161 uint16_t sequence = 0;
162 uint8_t fragmentCount = 0;
163 uint8_t haveCount = 0;
164 std::vector<std::vector<uint8_t>> received;
165 };
166
167 // Single in-progress set per reassembler. Multi-channel reassembly
168 // would need a small map; not needed at current scope.
170 {
171 bool hasValue = false;
173
175 {
176 value = ActiveSet{};
177 hasValue = true;
178 return *this;
179 }
180 void reset() noexcept { hasValue = false; }
181 ActiveSet* operator->() { return &value; }
182 const ActiveSet* operator->() const { return &value; }
183 ActiveSet& operator*() { return value; }
184 const ActiveSet& operator*() const { return value; }
185 explicit operator bool() const noexcept { return hasValue; }
186 };
188};
189
190} // namespace net
Wire format for the UDP transport layer.
Definition FragmentReassembler.hpp:33
void reset() noexcept
Drop any in-progress reassembly.
Definition FragmentReassembler.hpp:148
OptionalSet active_
Definition FragmentReassembler.hpp:187
static constexpr int k_maxFragments
Maximum fragments per logical message — matches the 8-bit fragment-count field in the wire header.
Definition FragmentReassembler.hpp:37
Result addFragment(const PacketHeader &hdr, const uint8_t *payload, int payloadLen, std::vector< uint8_t > &assembled)
Feed one received fragment into the reassembler.
Definition FragmentReassembler.hpp:67
Result
Result of addFragment.
Definition FragmentReassembler.hpp:41
@ Malformed
Header was malformed (bad fragment index/count).
Definition FragmentReassembler.hpp:52
@ InProgress
Fragment accepted into in-progress set, but more fragments are still needed before the message is com...
Definition FragmentReassembler.hpp:44
@ Complete
Fragment was the final missing piece — assembled is populated with the full logical message.
Definition FragmentReassembler.hpp:47
@ Stale
Fragment was older than the in-progress set and got dropped (drop-stale semantics).
Definition FragmentReassembler.hpp:50
static bool seqMoreRecent(uint16_t s1, uint16_t s2) noexcept
Glenn-Fiedler "is s2 more recent than s1?" with 16-bit wrap.
Definition FragmentReassembler.hpp:153
Definition ShotDebugReport.hpp:56
Definition FragmentReassembler.hpp:160
uint8_t fragmentCount
Definition FragmentReassembler.hpp:162
std::vector< std::vector< uint8_t > > received
Definition FragmentReassembler.hpp:164
uint8_t haveCount
Definition FragmentReassembler.hpp:163
uint16_t sequence
Definition FragmentReassembler.hpp:161
Definition FragmentReassembler.hpp:170
void reset() noexcept
Definition FragmentReassembler.hpp:180
bool hasValue
Definition FragmentReassembler.hpp:171
const ActiveSet * operator->() const
Definition FragmentReassembler.hpp:182
ActiveSet * operator->()
Definition FragmentReassembler.hpp:181
const ActiveSet & operator*() const
Definition FragmentReassembler.hpp:184
OptionalSet & emplace()
Definition FragmentReassembler.hpp:174
ActiveSet & operator*()
Definition FragmentReassembler.hpp:183
ActiveSet value
Definition FragmentReassembler.hpp:172
16-byte header at the start of every UDP datagram we send.
Definition PacketHeader.hpp:102
uint16_t fragmentInfo
hi 8 = fragment index, lo 8 = count; 0 if not fragmented
Definition PacketHeader.hpp:110
uint16_t sequence
Per-(connection, channel).
Definition PacketHeader.hpp:107