@@ -63,7 +63,9 @@ typedef unsigned char byte_t;
6363
6464#define FOREACH_IFACE (i ) for (size_t i = 0; (i) < CANARD_IFACE_COUNT; (i)++)
6565
66- #if CANARD_IFACE_COUNT <= 4
66+ #if CANARD_IFACE_COUNT <= 2
67+ #define IFACE_INDEX_BIT_LENGTH 1U
68+ #elif CANARD_IFACE_COUNT <= 4
6769#define IFACE_INDEX_BIT_LENGTH 2U
6870#elif CANARD_IFACE_COUNT <= 8
6971#define IFACE_INDEX_BIT_LENGTH 3U
@@ -1193,7 +1195,6 @@ static byte_t rx_transfer_id_forward_difference(const byte_t a, const byte_t b)
11931195
11941196// Reassembly state at a specific priority level.
11951197// Maintaining separate state per priority level allows preemption of higher-priority transfers without loss.
1196- // Each state is only kept as long as the transfer reassembly is in progress; once it's completed, the slot is deleted.
11971198typedef struct
11981199{
11991200 canard_us_t start_ts ;
@@ -1211,14 +1212,6 @@ typedef struct
12111212// resulting in 1 KiB o1heap blocks per session, very expensive. Here we use a much less RAM-heavy approach with
12121213// sparse nodes in a tree with log-time lookup.
12131214//
1214- // The session keeps the last transfer-ID, plus each slot holds one as well. This is needed because a low-priority
1215- // transfer may be temporarily preempted by a higher-priority one; each transfer has a unique transfer-ID within
1216- // the transfer-ID wraparound window. It is possible that a low-priority transfer with ID N is preempted by a sequence
1217- // of transfers whose count is a multiple of the wraparound window; if the transfer-ID deduplication was done at the
1218- // slot level only, then such sequence could cause the slot to reject a new transfer as a duplicate. To avoid this,
1219- // transfer-ID state in the slots is only used for start-of-transfer loss detection, while deduplication relies on the
1220- // single shared state at the session level.
1221- //
12221215// Core invariants:
12231216// - Only start-of-transfer may create/replace a slot.
12241217// - Non-start frames never create state.
@@ -1280,8 +1273,9 @@ static canard_tree_t* rx_session_factory(void* const user)
12801273 FOREACH_SLOT (i ) {
12811274 ses -> slots [i ] = NULL ;
12821275 }
1283- ses -> owner = ctx -> owner ;
1284- ses -> node_id = ctx -> node_id ;
1276+ ses -> last_admitted_start_ts = BIG_BANG ;
1277+ ses -> owner = ctx -> owner ;
1278+ ses -> node_id = ctx -> node_id ;
12851279 enlist_tail (& ctx -> owner -> owner -> rx .list_session_by_animation , & ses -> list_animation );
12861280 return & ses -> index ;
12871281}
@@ -1356,16 +1350,15 @@ static bool rx_session_update(canard_subscription_t* const sub,
13561350
13571351 // Frame admittance state machine. A highly complex piece, redesigned after v4 to support priority preemption.
13581352 // TID forward difference illustration: f(2,3)==31, f(2,2)==0, f(2,1)==1
1359- const bool tid_new = rx_transfer_id_forward_difference (ses -> last_admitted_transfer_id , frame -> transfer_id ) > 1 ;
1360- const canard_us_t timed_out = (ts - ses -> last_admitted_start_ts ) > ses -> owner -> transfer_id_timeout ;
1361- bool accept = false;
1353+ const bool tid_new = rx_transfer_id_forward_difference (ses -> last_admitted_transfer_id , frame -> transfer_id ) > 1 ;
1354+ const bool timed_out = ts > (ses -> last_admitted_start_ts + ses -> owner -> transfer_id_timeout );
1355+ bool accept = false;
1356+ const rx_slot_t * const slot = ses -> slots [frame -> priority ];
13621357 if (!frame -> start ) {
1363- const rx_slot_t * const slot = ses -> slots [frame -> priority ];
13641358 accept = (slot != NULL ) && (slot -> transfer_id == frame -> transfer_id ) && (slot -> iface_index == iface_index ) &&
13651359 (slot -> expected_toggle == frame -> toggle );
13661360 } else {
1367- const rx_slot_t * const slot = ses -> slots [frame -> priority ];
1368- accept = (slot == NULL ) || (slot -> transfer_id != frame -> transfer_id ) || tid_new || timed_out ; // --
1361+ accept = ((slot == NULL ) || (slot -> transfer_id != frame -> transfer_id )) && (tid_new || timed_out );
13691362 }
13701363 if (!accept ) {
13711364 return true; // Frame not needed; not a failure to accept.
@@ -1374,8 +1367,6 @@ static bool rx_session_update(canard_subscription_t* const sub,
13741367 // The frame must be accepted. If this is the start of a new transfer, we must update state.
13751368 enlist_tail (& sub -> owner -> rx .list_session_by_animation , & ses -> list_animation );
13761369 if (frame -> start ) {
1377- ses -> last_admitted_start_ts = ts ;
1378- ses -> last_admitted_transfer_id = frame -> transfer_id ;
13791370 rx_slot_destroy (sub , ses -> slots [frame -> priority ]);
13801371 ses -> slots [frame -> priority ] = NULL ;
13811372 if (!frame -> end ) { // more frames to follow, must store in-progress state
@@ -1385,6 +1376,8 @@ static bool rx_session_update(canard_subscription_t* const sub,
13851376 return false;
13861377 }
13871378 }
1379+ ses -> last_admitted_start_ts = ts ;
1380+ ses -> last_admitted_transfer_id = frame -> transfer_id ;
13881381 }
13891382
13901383 // TODO acceptance
0 commit comments