Skip to content

Commit 024b8d5

Browse files
add fragment tree utils
1 parent 821018e commit 024b8d5

File tree

2 files changed

+142
-3
lines changed

2 files changed

+142
-3
lines changed

lib/cavl/cavl2.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,86 @@ static inline CAVL2_T* cavl2_next_greater(CAVL2_T* const node)
206206
return c;
207207
}
208208

209+
/// Find the smallest node whose value is greater than or equal to the search target, in O(log n).
210+
/// Returns the first node for which the comparator returns a non-positive result.
211+
/// If no such node exists (all nodes compare less than target), returns NULL.
212+
/// The comparator returns: positive if target>candidate, zero if target==candidate, negative if target<candidate.
213+
/// Example: tree={1,3,5,7}, target=4 => 5; target=5 => 5; target=8 => NULL.
214+
static inline CAVL2_T* cavl2_lower_bound(CAVL2_T* const root,
215+
const void* const user,
216+
const cavl2_comparator_t comparator)
217+
{
218+
CAVL2_T* result = NULL;
219+
if ((root != NULL) && (comparator != NULL)) {
220+
CAVL2_T* n = root;
221+
while (n != NULL) {
222+
const CAVL2_RELATION cmp = comparator(user, n);
223+
if (cmp <= 0) {
224+
result = n;
225+
n = n->lr[0];
226+
} else {
227+
n = n->lr[1];
228+
}
229+
}
230+
}
231+
return result;
232+
}
233+
234+
/// Find the smallest node whose value is strictly greater than the search target (upper bound).
235+
/// Returns the first node for which the comparator returns a negative result.
236+
/// See cavl2_lower_bound() for details.
237+
/// Example: tree={1,3,5,7}, target=4 => 5; target=5 => 7; target=7 => NULL.
238+
static inline CAVL2_T* cavl2_upper_bound(CAVL2_T* const root,
239+
const void* const user,
240+
const cavl2_comparator_t comparator)
241+
{
242+
CAVL2_T* result = NULL;
243+
if ((root != NULL) && (comparator != NULL)) {
244+
CAVL2_T* n = root;
245+
while (n != NULL) {
246+
const CAVL2_RELATION cmp = comparator(user, n);
247+
if (cmp < 0) {
248+
result = n;
249+
n = n->lr[0];
250+
} else {
251+
n = n->lr[1];
252+
}
253+
}
254+
}
255+
return result;
256+
}
257+
258+
/// Find the largest node whose value is less than or equal to the search target, in O(log n).
259+
/// Returns the last node for which the comparator returns a non-negative result.
260+
/// See cavl2_lower_bound() for details.
261+
/// Example: tree={1,3,5,7}, target=4 => 3; target=5 => 5; target=0 => NULL.
262+
static inline CAVL2_T* cavl2_predecessor(CAVL2_T* const root,
263+
const void* const user,
264+
const cavl2_comparator_t comparator)
265+
{
266+
CAVL2_T* result = NULL;
267+
if ((root != NULL) && (comparator != NULL)) {
268+
CAVL2_T* n = root;
269+
while (n != NULL) {
270+
const CAVL2_RELATION cmp = comparator(user, n);
271+
if (cmp >= 0) {
272+
result = n;
273+
n = n->lr[1];
274+
} else {
275+
n = n->lr[0];
276+
}
277+
}
278+
}
279+
return result;
280+
}
281+
282+
/// The successor counterpart of cavl2_predecessor() is an alias of cavl2_lower_bound(), provided for completeness only.
283+
/// Example: tree={1,3,5,7}, target=4 => 5; target=5 => 5; target=8 => NULL.
284+
static inline CAVL2_T* cavl2_successor(CAVL2_T* const root, const void* const user, const cavl2_comparator_t comparator)
285+
{
286+
return cavl2_lower_bound(root, user, comparator);
287+
}
288+
209289
/// The trivial factory is useful in most applications. It simply returns the user pointed converted to CAVL2_T.
210290
/// It is meant for use with cavl2_find_or_insert().
211291
static inline CAVL2_T* cavl2_trivial_factory(void* const user)

libudpard/udpard.c

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -687,8 +687,7 @@ void udpard_tx_free(const udpard_tx_mem_resources_t memory, udpard_tx_item_t* co
687687
typedef struct
688688
{
689689
uint32_t index;
690-
uint32_t offset; ///< Offset of this fragment's payload within the full transfer payload.
691-
bool end_of_transfer;
690+
uint32_t offset; ///< Offset of this fragment's payload within the full transfer payload.
692691
udpard_bytes_t payload; ///< Also contains the transfer CRC (but not the header CRC).
693692
udpard_bytes_mut_t origin; ///< The entirety of the free-able buffer passed from the application.
694693
} rx_frame_base_t;
@@ -708,6 +707,66 @@ typedef struct rx_fragment_t
708707
udpard_tree_t tree;
709708
} rx_fragment_t;
710709

710+
/// We require that the fragment tree does not contain fully-contained or equal-range fragments.
711+
/// One implication is that no two fragments can have the same offset.
712+
static int32_t rx_cavl_compare_fragment_offset(const void* const user, const udpard_tree_t* const node)
713+
{
714+
return ((*(const size_t*)user) - CAVL2_TO_OWNER(node, rx_fragment_t, tree)->base.offset) ? +1 : -1;
715+
}
716+
717+
/// Find the first fragment where offset >= left in log time. Returns NULL if no such fragment exists.
718+
/// This is intended for overlap removal and gap detection when deciding if a new fragment is needed.
719+
static rx_fragment_t* rx_fragment_tree_lower_bound(udpard_tree_t* const root, const size_t left)
720+
{
721+
return CAVL2_TO_OWNER(cavl2_lower_bound(root, &left, &rx_cavl_compare_fragment_offset), rx_fragment_t, tree);
722+
}
723+
724+
/// True if the fragment tree does not have a contiguous payload coverage in [left, right).
725+
/// This function requires that the tree does not have fully-contained fragments; one implication is that
726+
/// no two fragments may have the same offset.
727+
/// The complexity is O(log n + k), where n is the number of fragments in the tree and k is the number of fragments
728+
/// overlapping the specified range, assuming there are no fully-contained fragments.
729+
static bool rx_fragment_tree_has_gap_in_range(udpard_tree_t* const root, const size_t left, const size_t right)
730+
{
731+
if (left >= right) {
732+
return false; // empty fragment; no gap by convention
733+
}
734+
rx_fragment_t* frag =
735+
CAVL2_TO_OWNER(cavl2_predecessor(root, &left, &rx_cavl_compare_fragment_offset), rx_fragment_t, tree);
736+
if (frag == NULL) {
737+
return true;
738+
}
739+
if ((frag->base.offset + frag->base.view.size) <= left) { // The predecessor ends before the left edge.
740+
return true;
741+
}
742+
size_t covered = left; // This is the O(k) part. The scan starting point search is O(log n).
743+
while ((frag != NULL) && (frag->base.offset < right)) {
744+
if (frag->base.offset > covered) {
745+
return true;
746+
}
747+
covered = larger(covered, frag->base.offset + frag->base.view.size);
748+
if (covered >= right) {
749+
return false; // Reached the end of the requested range without gaps.
750+
}
751+
frag = CAVL2_TO_OWNER(cavl2_next_greater(&frag->tree), rx_fragment_t, tree);
752+
}
753+
return covered < right;
754+
}
755+
756+
/// True if the specified fragment should be retained. Otherwise, it is redundant and should be discarded.
757+
/// The complexity is O(log n + k), see rx_fragment_tree_has_gap_in_range() for details.
758+
static bool rx_fragment_is_needed(udpard_tree_t* const root,
759+
const size_t fragment_offset,
760+
const size_t fragment_size,
761+
const size_t transfer_size,
762+
const size_t extent)
763+
{
764+
const size_t total_size = smaller(transfer_size, extent);
765+
const size_t left = fragment_offset;
766+
const size_t right = smaller(fragment_offset + fragment_size, total_size);
767+
return (left < total_size) && rx_fragment_tree_has_gap_in_range(root, left, right);
768+
}
769+
711770
typedef enum
712771
{
713772
rx_slot_idle = 0,
@@ -804,7 +863,7 @@ typedef struct
804863
udpard_microsecond_t last_animated_ts;
805864

806865
/// To weed out duplicates and to retransmit lost ACKs.
807-
rx_transfer_id_window_t transfer_id_received;
866+
rx_transfer_id_window_t acknowledged;
808867

809868
rx_slot_t slots[RX_SLOT_COUNT];
810869
} rx_session_t;

0 commit comments

Comments
 (0)