@@ -27,17 +27,27 @@ static udpard_fragment_t* fragment_at(udpard_tree_t* const root, uint32_t index)
2727 return NULL ;
2828}
2929
30- /// Allocates the payload on the heap, emulating normal transfer reception.
31- /// The payload shall not contain NUL characters.
32- static rx_frame_base_t make_frame_base (const udpard_mem_resource_t mem , const size_t offset , const char * const payload )
30+ /// Allocates the payload on the heap, emulating normal frame reception.
31+ static rx_frame_base_t make_frame_base (const udpard_mem_resource_t mem ,
32+ const size_t offset ,
33+ const size_t size ,
34+ const void * const payload )
3335{
34- const size_t size = (payload != NULL ) ? (strlen (payload ) + 1 ) : 0U ;
35- void * data = mem .alloc (mem .user , size );
36- memcpy (data , payload , size );
36+ void * data = mem .alloc (mem .user , size );
37+ if (size > 0 ) {
38+ memcpy (data , payload , size );
39+ }
3740 return (rx_frame_base_t ){ .offset = offset ,
3841 .payload = { .data = data , .size = size },
3942 .origin = { .data = data , .size = size } };
4043}
44+ /// The payload string cannot contain NUL characters.
45+ static rx_frame_base_t make_frame_base_str (const udpard_mem_resource_t mem ,
46+ const size_t offset ,
47+ const char * const payload )
48+ {
49+ return make_frame_base (mem , offset , (payload != NULL ) ? (strlen (payload ) + 1 ) : 0U , payload );
50+ }
4151
4252static void test_rx_fragment_tree_update_a (void )
4353{
@@ -59,7 +69,7 @@ static void test_rx_fragment_tree_update_a(void)
5969 res = rx_fragment_tree_update (& root , //
6070 mem_frag ,
6171 del_payload ,
62- make_frame_base (mem_payload , 0 , NULL ),
72+ make_frame_base_str (mem_payload , 0 , NULL ),
6373 0 ,
6474 0 ,
6575 & cov );
@@ -101,7 +111,7 @@ static void test_rx_fragment_tree_update_a(void)
101111 res = rx_fragment_tree_update (& root , //
102112 mem_frag ,
103113 del_payload ,
104- make_frame_base (mem_payload , 0 , "abc" ),
114+ make_frame_base_str (mem_payload , 0 , "abc" ),
105115 4 ,
106116 0 ,
107117 & cov );
@@ -143,7 +153,7 @@ static void test_rx_fragment_tree_update_a(void)
143153 res = rx_fragment_tree_update (& root , //
144154 mem_frag ,
145155 del_payload ,
146- make_frame_base (mem_payload , 0 , "abcdef" ),
156+ make_frame_base_str (mem_payload , 0 , "abcdef" ),
147157 7 ,
148158 3 ,
149159 & cov );
@@ -186,7 +196,7 @@ static void test_rx_fragment_tree_update_a(void)
186196 res = rx_fragment_tree_update (& root , //
187197 mem_frag ,
188198 del_payload ,
189- make_frame_base (mem_payload , 0 , "abc" ),
199+ make_frame_base_str (mem_payload , 0 , "abc" ),
190200 100 ,
191201 10 ,
192202 & cov );
@@ -198,7 +208,7 @@ static void test_rx_fragment_tree_update_a(void)
198208 res = rx_fragment_tree_update (& root , //
199209 mem_frag ,
200210 del_payload ,
201- make_frame_base (mem_payload , 8 , "xyz" ),
211+ make_frame_base_str (mem_payload , 8 , "xyz" ),
202212 100 ,
203213 11 ,
204214 & cov );
@@ -210,7 +220,7 @@ static void test_rx_fragment_tree_update_a(void)
210220 res = rx_fragment_tree_update (& root , //
211221 mem_frag ,
212222 del_payload ,
213- make_frame_base (mem_payload , 4 , "def" ),
223+ make_frame_base_str (mem_payload , 4 , "def" ),
214224 100 ,
215225 11 ,
216226 & cov );
@@ -246,6 +256,186 @@ static void test_rx_fragment_tree_update_a(void)
246256 TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .count_free );
247257 TEST_ASSERT_EQUAL_size_t (3 , alloc_payload .count_free );
248258 }
259+
260+ instrumented_allocator_reset (& alloc_frag );
261+ instrumented_allocator_reset (& alloc_payload );
262+
263+ // Multi-frame reassembly test with defragmentation: "0123456789".
264+ {
265+ udpard_tree_t * root = NULL ;
266+ size_t cov = 0 ;
267+ rx_fragment_tree_update_result_t res = rx_fragment_tree_not_done ;
268+ // Add fragment.
269+ res = rx_fragment_tree_update (& root , //
270+ mem_frag ,
271+ del_payload ,
272+ make_frame_base (mem_payload , 0 , 2 , "01" ),
273+ 100 ,
274+ 10 ,
275+ & cov );
276+ TEST_ASSERT_EQUAL (rx_fragment_tree_not_done , res );
277+ TEST_ASSERT_EQUAL_size_t (2 , cov );
278+ TEST_ASSERT_NOT_NULL (root );
279+ TEST_ASSERT_EQUAL (1 , tree_count (root ));
280+ // Add fragment.
281+ res = rx_fragment_tree_update (& root , //
282+ mem_frag ,
283+ del_payload ,
284+ make_frame_base (mem_payload , 4 , 2 , "45" ),
285+ 100 ,
286+ 10 ,
287+ & cov );
288+ TEST_ASSERT_EQUAL (rx_fragment_tree_not_done , res );
289+ TEST_ASSERT_EQUAL_size_t (2 , cov ); // not extended
290+ TEST_ASSERT_NOT_NULL (root );
291+ TEST_ASSERT_EQUAL (2 , tree_count (root ));
292+ // Add fragment.
293+ res = rx_fragment_tree_update (& root , //
294+ mem_frag ,
295+ del_payload ,
296+ make_frame_base (mem_payload , 3 , 2 , "34" ),
297+ 100 ,
298+ 10 ,
299+ & cov );
300+ TEST_ASSERT_EQUAL (rx_fragment_tree_not_done , res );
301+ TEST_ASSERT_EQUAL_size_t (2 , cov ); // not extended
302+ TEST_ASSERT_NOT_NULL (root );
303+ TEST_ASSERT_EQUAL (3 , tree_count (root ));
304+ // Intermediate check on the current state of the tree so far.
305+ TEST_ASSERT_EQUAL_size_t (0 , fragment_at (root , 0 )-> offset );
306+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 0 )-> view .size );
307+ TEST_ASSERT_EQUAL_STRING_LEN ("01" , fragment_at (root , 0 )-> view .data , 2 );
308+ TEST_ASSERT_EQUAL_size_t (3 , fragment_at (root , 1 )-> offset );
309+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> view .size );
310+ TEST_ASSERT_EQUAL_STRING_LEN ("34" , fragment_at (root , 1 )-> view .data , 2 );
311+ TEST_ASSERT_EQUAL_size_t (4 , fragment_at (root , 2 )-> offset );
312+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 2 )-> view .size );
313+ TEST_ASSERT_EQUAL_STRING_LEN ("45" , fragment_at (root , 2 )-> view .data , 2 );
314+ TEST_ASSERT_NULL (fragment_at (root , 3 ));
315+ TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .allocated_fragments );
316+ TEST_ASSERT_EQUAL_size_t (3 , alloc_payload .allocated_fragments );
317+ TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .count_alloc );
318+ TEST_ASSERT_EQUAL_size_t (3 , alloc_payload .count_alloc );
319+ TEST_ASSERT_EQUAL_size_t (0 , alloc_frag .count_free );
320+ TEST_ASSERT_EQUAL_size_t (0 , alloc_payload .count_free );
321+ // Add fragment. BRIDGE THE LEFT GAP, EVICT `34` FRAGMENT AS REDUNDANT.
322+ res = rx_fragment_tree_update (& root , //
323+ mem_frag ,
324+ del_payload ,
325+ make_frame_base (mem_payload , 2 , 2 , "23" ),
326+ 100 ,
327+ 10 ,
328+ & cov );
329+ TEST_ASSERT_EQUAL (rx_fragment_tree_not_done , res );
330+ TEST_ASSERT_EQUAL_size_t (6 , cov ); // extended!
331+ TEST_ASSERT_NOT_NULL (root );
332+ TEST_ASSERT_EQUAL (3 , tree_count (root ));
333+ // Check the updated tree state after the eviction. Fragment `34` should be gone.
334+ TEST_ASSERT_EQUAL_size_t (0 , fragment_at (root , 0 )-> offset );
335+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 0 )-> view .size );
336+ TEST_ASSERT_EQUAL_STRING_LEN ("01" , fragment_at (root , 0 )-> view .data , 2 );
337+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> offset );
338+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> view .size );
339+ TEST_ASSERT_EQUAL_STRING_LEN ("23" , fragment_at (root , 1 )-> view .data , 2 );
340+ TEST_ASSERT_EQUAL_size_t (4 , fragment_at (root , 2 )-> offset );
341+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 2 )-> view .size );
342+ TEST_ASSERT_EQUAL_STRING_LEN ("45" , fragment_at (root , 2 )-> view .data , 2 );
343+ TEST_ASSERT_NULL (fragment_at (root , 3 ));
344+ TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .allocated_fragments );
345+ TEST_ASSERT_EQUAL_size_t (3 , alloc_payload .allocated_fragments );
346+ TEST_ASSERT_EQUAL_size_t (4 , alloc_frag .count_alloc );
347+ TEST_ASSERT_EQUAL_size_t (4 , alloc_payload .count_alloc );
348+ TEST_ASSERT_EQUAL_size_t (1 , alloc_frag .count_free );
349+ TEST_ASSERT_EQUAL_size_t (1 , alloc_payload .count_free );
350+ // Add a fully-contained (redundant) fragment. Should be discarded.
351+ res = rx_fragment_tree_update (& root , //
352+ mem_frag ,
353+ del_payload ,
354+ make_frame_base (mem_payload , 1 , 2 , ":3" ),
355+ 100 ,
356+ 10 ,
357+ & cov );
358+ TEST_ASSERT_EQUAL (rx_fragment_tree_not_done , res );
359+ TEST_ASSERT_EQUAL_size_t (6 , cov ); // no new information is added
360+ TEST_ASSERT_NOT_NULL (root );
361+ TEST_ASSERT_EQUAL (3 , tree_count (root )); // no new frames added
362+ TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .allocated_fragments ); // no new allocations
363+ TEST_ASSERT_EQUAL_size_t (3 , alloc_payload .allocated_fragments );
364+ TEST_ASSERT_EQUAL_size_t (4 , alloc_frag .count_alloc );
365+ TEST_ASSERT_EQUAL_size_t (5 , alloc_payload .count_alloc ); // the payload was briefly allocated and discarded
366+ TEST_ASSERT_EQUAL_size_t (1 , alloc_frag .count_free );
367+ TEST_ASSERT_EQUAL_size_t (2 , alloc_payload .count_free ); // yeah, discarded
368+ // Add fragment. Slight overlap on the right, candidate for eviction in the future.
369+ res = rx_fragment_tree_update (& root , //
370+ mem_frag ,
371+ del_payload ,
372+ make_frame_base (mem_payload , 5 , 2 , "56" ),
373+ 100 ,
374+ 10 ,
375+ & cov );
376+ TEST_ASSERT_EQUAL (rx_fragment_tree_not_done , res );
377+ TEST_ASSERT_EQUAL_size_t (7 , cov ); // extended by 1 byte
378+ TEST_ASSERT_NOT_NULL (root );
379+ TEST_ASSERT_EQUAL (4 , tree_count (root ));
380+ // Check the updated tree state.
381+ TEST_ASSERT_EQUAL_size_t (0 , fragment_at (root , 0 )-> offset );
382+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 0 )-> view .size );
383+ TEST_ASSERT_EQUAL_STRING_LEN ("01" , fragment_at (root , 0 )-> view .data , 2 );
384+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> offset );
385+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> view .size );
386+ TEST_ASSERT_EQUAL_STRING_LEN ("23" , fragment_at (root , 1 )-> view .data , 2 );
387+ TEST_ASSERT_EQUAL_size_t (4 , fragment_at (root , 2 )-> offset );
388+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 2 )-> view .size );
389+ TEST_ASSERT_EQUAL_STRING_LEN ("45" , fragment_at (root , 2 )-> view .data , 2 );
390+ TEST_ASSERT_EQUAL_size_t (5 , fragment_at (root , 3 )-> offset );
391+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 3 )-> view .size );
392+ TEST_ASSERT_EQUAL_STRING_LEN ("56" , fragment_at (root , 3 )-> view .data , 2 );
393+ TEST_ASSERT_NULL (fragment_at (root , 4 ));
394+ TEST_ASSERT_EQUAL_size_t (4 , alloc_frag .allocated_fragments );
395+ TEST_ASSERT_EQUAL_size_t (4 , alloc_payload .allocated_fragments );
396+ TEST_ASSERT_EQUAL_size_t (5 , alloc_frag .count_alloc );
397+ TEST_ASSERT_EQUAL_size_t (6 , alloc_payload .count_alloc );
398+ TEST_ASSERT_EQUAL_size_t (1 , alloc_frag .count_free );
399+ TEST_ASSERT_EQUAL_size_t (2 , alloc_payload .count_free );
400+ // Add fragment. Completes the transfer and evicts redundant `45` and `56` fragments.
401+ res = rx_fragment_tree_update (& root , //
402+ mem_frag ,
403+ del_payload ,
404+ make_frame_base (mem_payload , 4 , 8 , "456789--" ),
405+ 100 ,
406+ 10 ,
407+ & cov );
408+ TEST_ASSERT_EQUAL (rx_fragment_tree_done , res );
409+ TEST_ASSERT_EQUAL_size_t (12 , cov ); // extended all the way, beyond the extent.
410+ TEST_ASSERT_NOT_NULL (root );
411+ TEST_ASSERT_EQUAL (3 , tree_count (root )); // the tree shrunk due to evictions
412+ // Check the updated tree state.
413+ TEST_ASSERT_EQUAL_size_t (0 , fragment_at (root , 0 )-> offset );
414+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 0 )-> view .size );
415+ TEST_ASSERT_EQUAL_STRING_LEN ("01" , fragment_at (root , 0 )-> view .data , 2 );
416+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> offset );
417+ TEST_ASSERT_EQUAL_size_t (2 , fragment_at (root , 1 )-> view .size );
418+ TEST_ASSERT_EQUAL_STRING_LEN ("23" , fragment_at (root , 1 )-> view .data , 2 );
419+ TEST_ASSERT_EQUAL_size_t (4 , fragment_at (root , 2 )-> offset );
420+ TEST_ASSERT_EQUAL_size_t (8 , fragment_at (root , 2 )-> view .size );
421+ TEST_ASSERT_EQUAL_STRING_LEN ("456789--" , fragment_at (root , 2 )-> view .data , 8 );
422+ TEST_ASSERT_NULL (fragment_at (root , 3 ));
423+ TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .allocated_fragments );
424+ TEST_ASSERT_EQUAL_size_t (3 , alloc_payload .allocated_fragments );
425+ TEST_ASSERT_EQUAL_size_t (6 , alloc_frag .count_alloc );
426+ TEST_ASSERT_EQUAL_size_t (7 , alloc_payload .count_alloc );
427+ TEST_ASSERT_EQUAL_size_t (3 , alloc_frag .count_free );
428+ TEST_ASSERT_EQUAL_size_t (4 , alloc_payload .count_free );
429+ // Free the tree (as in freedom). The free tree is free to manifest its own destiny.
430+ udpard_fragment_free_all ((udpard_fragment_t * )root , mem_frag );
431+ // Check the heap.
432+ TEST_ASSERT_EQUAL_size_t (0 , alloc_frag .allocated_fragments );
433+ TEST_ASSERT_EQUAL_size_t (0 , alloc_payload .allocated_fragments );
434+ TEST_ASSERT_EQUAL_size_t (6 , alloc_frag .count_alloc );
435+ TEST_ASSERT_EQUAL_size_t (7 , alloc_payload .count_alloc );
436+ TEST_ASSERT_EQUAL_size_t (6 , alloc_frag .count_free );
437+ TEST_ASSERT_EQUAL_size_t (7 , alloc_payload .count_free );
438+ }
249439}
250440
251441static void test_rx_transfer_id_forward_distance (void )
0 commit comments