diff --git a/libudpard/udpard.c b/libudpard/udpard.c
index e37413b..d879c08 100644
--- a/libudpard/udpard.c
+++ b/libudpard/udpard.c
@@ -776,6 +776,14 @@ static inline bool rxParseFrame(const struct UdpardMutablePayload datagram_paylo
return ok;
}
+/// This helper is needed to minimize the risk of argument swapping when passing these two resources around,
+/// as they almost always go side by side.
+typedef struct
+{
+ struct UdpardMemoryResource* fragment;
+ struct UdpardMemoryResource* payload;
+} RxMemory;
+
typedef struct
{
struct UdpardTreeNode base;
@@ -791,16 +799,14 @@ typedef struct RxFragment
uint32_t frame_index;
} RxFragment;
-static inline void rxFragmentFree(struct UdpardFragment* const head,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
+static inline void rxFragmentFree(struct UdpardFragment* const head, const RxMemory memory)
{
struct UdpardFragment* handle = head;
while (handle != NULL)
{
struct UdpardFragment* const next = handle->next;
- memFreePayload(memory_payload, handle->origin); // May be NULL, is okay.
- memFree(memory_fragment, sizeof(RxFragment), handle);
+ memFreePayload(memory.payload, handle->origin); // May be NULL, is okay.
+ memFree(memory.fragment, sizeof(RxFragment), handle);
handle = next;
}
}
@@ -850,7 +856,9 @@ typedef struct
RxSlot slots[RX_SLOT_COUNT];
} RxIface;
-/// This type is forward-declared externally, hence why it has such a long name with the udpard prefix.
+/// This type is forward-declared externally, hence why it has such a long name with the "udpard" prefix.
+/// Keep in mind that we have a dedicated session object per remote node per port; this means that the states
+/// kept here -- the timestamp and the transfer-ID -- are specific per remote node, as it should be.
typedef struct UdpardInternalRxSession
{
struct UdpardTreeNode base;
@@ -866,30 +874,25 @@ typedef struct UdpardInternalRxSession
} UdpardInternalRxSession;
// NOLINTNEXTLINE(misc-no-recursion)
-static inline void rxSlotFree(RxFragment* const self,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
+static inline void rxSlotFree(RxFragment* const self, const RxMemory memory)
{
- UDPARD_ASSERT((self != NULL) && memIsValid(memory_fragment));
- memFreePayload(memory_payload, self->base.origin);
+ UDPARD_ASSERT(self != NULL);
+ memFreePayload(memory.payload, self->base.origin);
for (uint_fast8_t i = 0; i < 2; i++)
{
RxFragmentTreeNode* const child = (RxFragmentTreeNode*) self->tree.base.lr[i];
if (child != NULL)
{
UDPARD_ASSERT(child->base.up == &self->tree.base);
- rxSlotFree(child->this, memory_fragment, memory_payload);
+ rxSlotFree(child->this, memory);
}
}
- memFree(memory_fragment, sizeof(RxFragment), self); // self-destruct
+ memFree(memory.fragment, sizeof(RxFragment), self); // self-destruct
}
-static inline void rxSlotRestart(RxSlot* const self,
- const UdpardTransferID transfer_id,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
+static inline void rxSlotRestart(RxSlot* const self, const UdpardTransferID transfer_id, const RxMemory memory)
{
- UDPARD_ASSERT((self != NULL) && (memory_fragment != NULL) && (memory_payload != NULL));
+ UDPARD_ASSERT(self != NULL);
self->ts_usec = TIMESTAMP_UNSET; // Will be assigned when the first frame of the transfer has arrived.
self->transfer_id = transfer_id;
self->max_index = 0;
@@ -898,7 +901,7 @@ static inline void rxSlotRestart(RxSlot* const self,
self->payload_size = 0;
if (self->fragments != NULL)
{
- rxSlotFree(self->fragments->this, memory_fragment, memory_payload);
+ rxSlotFree(self->fragments->this, memory);
self->fragments = NULL;
}
}
@@ -949,20 +952,19 @@ static inline struct UdpardTreeNode* rxSlotFragmentFactory(void* const user_refe
/// States outliving each level of recursion while ejecting the transfer from the fragment tree.
typedef struct
{
- struct UdpardFragment* head; // Points to the first fragment in the list.
- struct UdpardFragment* predecessor;
- uint32_t crc;
- size_t retain_size;
- size_t offset;
- struct UdpardMemoryResource* memory_fragment;
- struct UdpardMemoryResource* memory_payload;
+ struct UdpardFragment* head; // Points to the first fragment in the list.
+ struct UdpardFragment* predecessor;
+ uint32_t crc;
+ size_t retain_size;
+ size_t offset;
+ RxMemory memory;
} RxSlotEjectContext;
/// See rxSlotEject() for details.
/// NOLINTNEXTLINE(misc-no-recursion)
static inline void rxSlotEjectFragment(RxFragment* const frag, RxSlotEjectContext* const ctx)
{
- UDPARD_ASSERT((frag != NULL) && (ctx != NULL) && memIsValid(ctx->memory_fragment));
+ UDPARD_ASSERT((frag != NULL) && (ctx != NULL));
if (frag->tree.base.lr[0] != NULL)
{
RxFragment* const child = ((RxFragmentTreeNode*) frag->tree.base.lr[0])->this;
@@ -998,8 +1000,8 @@ static inline void rxSlotEjectFragment(RxFragment* const frag, RxSlotEjectContex
// Drop the unneeded fragments and their handles after the sub-tree is fully traversed.
if (!retain)
{
- memFreePayload(ctx->memory_payload, frag->base.origin);
- memFree(ctx->memory_fragment, sizeof(RxFragment), frag);
+ memFreePayload(ctx->memory.payload, frag->base.origin);
+ memFree(ctx->memory.fragment, sizeof(RxFragment), frag);
}
}
@@ -1019,25 +1021,23 @@ static inline void rxSlotEjectFragment(RxFragment* const frag, RxSlotEjectContex
/// There shall be at least one fragment (because a Cyphal transfer contains at least one frame).
///
/// The return value indicates whether the transfer is valid (CRC is correct).
-static inline bool rxSlotEject(size_t* const out_payload_size,
- struct UdpardFragment* const out_payload_head,
- RxFragmentTreeNode* const fragment_tree,
- const size_t received_total_size, // With CRC.
- const size_t extent,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
+static inline bool rxSlotEject(size_t* const out_payload_size,
+ struct UdpardFragment* const out_payload_head,
+ RxFragmentTreeNode* const fragment_tree,
+ const size_t received_total_size, // With CRC.
+ const size_t extent,
+ const RxMemory memory)
{
UDPARD_ASSERT((received_total_size >= TRANSFER_CRC_SIZE_BYTES) && (fragment_tree != NULL) &&
(out_payload_size != NULL) && (out_payload_head != NULL));
bool result = false;
RxSlotEjectContext eject_ctx = {
- .head = NULL,
- .predecessor = NULL,
- .crc = TRANSFER_CRC_INITIAL,
- .retain_size = smaller(received_total_size - TRANSFER_CRC_SIZE_BYTES, extent),
- .offset = 0,
- .memory_fragment = memory_fragment,
- .memory_payload = memory_payload,
+ .head = NULL,
+ .predecessor = NULL,
+ .crc = TRANSFER_CRC_INITIAL,
+ .retain_size = smaller(received_total_size - TRANSFER_CRC_SIZE_BYTES, extent),
+ .offset = 0,
+ .memory = memory,
};
rxSlotEjectFragment(fragment_tree->this, &eject_ctx);
UDPARD_ASSERT(eject_ctx.offset == received_total_size); // Ensure we have traversed the entire tree.
@@ -1051,11 +1051,11 @@ static inline bool rxSlotEject(size_t* const out_payload_si
// This is the single-frame transfer optimization suggested by Scott: we free the first fragment handle
// early by moving the contents into the rx_transfer structure by value.
// No need to free the payload buffer because it has been transferred to the transfer.
- memFree(memory_fragment, sizeof(RxFragment), eject_ctx.head); // May be empty.
+ memFree(memory.fragment, sizeof(RxFragment), eject_ctx.head); // May be empty.
}
else // The transfer turned out to be invalid. We have to free the fragments. Can't use the tree anymore.
{
- rxFragmentFree(eject_ctx.head, memory_fragment, memory_payload);
+ rxFragmentFree(eject_ctx.head, memory);
}
return result;
}
@@ -1063,16 +1063,15 @@ static inline bool rxSlotEject(size_t* const out_payload_si
/// This function will either move the frame payload into the session, or free it if it cannot be made use of.
/// Upon return, certain state variables may be overwritten, so the caller should not rely on them.
/// Returns: 1 -- transfer available, payload written; 0 -- transfer not yet available; <0 -- error.
-static inline int_fast8_t rxSlotAccept(RxSlot* const self,
- size_t* const out_transfer_payload_size,
- struct UdpardFragment* const out_transfer_payload_head,
- const RxFrameBase frame,
- const size_t extent,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
+static inline int_fast8_t rxSlotAccept(RxSlot* const self,
+ size_t* const out_transfer_payload_size,
+ struct UdpardFragment* const out_transfer_payload_head,
+ const RxFrameBase frame,
+ const size_t extent,
+ const RxMemory memory)
{
UDPARD_ASSERT((self != NULL) && (frame.payload.size > 0) && (out_transfer_payload_size != NULL) &&
- (out_transfer_payload_head != NULL) && memIsValid(memory_fragment));
+ (out_transfer_payload_head != NULL));
int_fast8_t result = 0;
bool restart = false;
bool release = true;
@@ -1097,7 +1096,7 @@ static inline int_fast8_t rxSlotAccept(RxSlot* const self,
UDPARD_ASSERT((self->max_index <= self->eot_index) && (self->accepted_frames <= self->eot_index));
RxSlotUpdateContext update_ctx = {.frame_index = frame.index,
.accepted = false,
- .memory_fragment = memory_fragment};
+ .memory_fragment = memory.fragment};
RxFragmentTreeNode* const frag = (RxFragmentTreeNode*) cavlSearch((struct UdpardTreeNode**) &self->fragments, //
&update_ctx,
&rxSlotFragmentSearch,
@@ -1133,8 +1132,7 @@ static inline int_fast8_t rxSlotAccept(RxSlot* const self,
self->fragments,
self->payload_size,
extent,
- memory_fragment,
- memory_payload)
+ memory)
? 1
: 0;
// The tree is now unusable and the data is moved into rx_transfer.
@@ -1145,11 +1143,11 @@ static inline int_fast8_t rxSlotAccept(RxSlot* const self,
if (restart)
{
// Increment is necessary to weed out duplicate transfers.
- rxSlotRestart(self, self->transfer_id + 1U, memory_fragment, memory_payload);
+ rxSlotRestart(self, self->transfer_id + 1U, memory);
}
if (release)
{
- memFreePayload(memory_payload, frame.origin);
+ memFreePayload(memory.payload, frame.origin);
}
return result;
}
@@ -1187,7 +1185,7 @@ static inline bool rxIfaceCheckTransferIDTimeout(const RxIface* const self,
}
}
return (most_recent_ts_usec == TIMESTAMP_UNSET) ||
- ((ts_usec >= most_recent_ts_usec) && ((ts_usec - most_recent_ts_usec) > transfer_id_timeout_usec));
+ ((ts_usec >= most_recent_ts_usec) && ((ts_usec - most_recent_ts_usec) >= transfer_id_timeout_usec));
}
/// Traverses the list of slots trying to find a slot with a matching transfer-ID that is already IN PROGRESS.
@@ -1222,16 +1220,15 @@ static inline RxSlot* rxIfaceFindMatchingSlot(RxSlot slots[RX_SLOT_COUNT], const
/// This function is invoked when a new datagram pertaining to a certain session is received on an interface.
/// This function will either move the frame payload into the session, or free it if it cannot be made use of.
/// Returns: 1 -- transfer available; 0 -- transfer not yet available; <0 -- error.
-static inline int_fast8_t rxIfaceAccept(RxIface* const self,
- const UdpardMicrosecond ts_usec,
- const RxFrame frame,
- struct UdpardRxTransfer* const received_transfer,
- const size_t extent,
- const UdpardMicrosecond transfer_id_timeout_usec,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
-{
- UDPARD_ASSERT((self != NULL) && (frame.base.payload.size > 0) && (received_transfer != NULL));
+static inline int_fast8_t rxIfaceAccept(RxIface* const self,
+ const UdpardMicrosecond ts_usec,
+ const RxFrame frame,
+ const size_t extent,
+ const UdpardMicrosecond transfer_id_timeout_usec,
+ const RxMemory memory,
+ struct UdpardRxTransfer* const out_transfer)
+{
+ UDPARD_ASSERT((self != NULL) && (frame.base.payload.size > 0) && (out_transfer != NULL));
RxSlot* slot = rxIfaceFindMatchingSlot(self->slots, frame.meta.transfer_id);
// If there is no suitable slot, we should check if the transfer is a future one (high transfer-ID),
// or a transfer-ID timeout has occurred. In this case we sacrifice the oldest slot.
@@ -1253,7 +1250,7 @@ static inline int_fast8_t rxIfaceAccept(RxIface* const self,
if (rxIfaceIsFutureTransferID(self, frame.meta.transfer_id) ||
rxIfaceCheckTransferIDTimeout(self, ts_usec, transfer_id_timeout_usec))
{
- rxSlotRestart(victim, frame.meta.transfer_id, memory_fragment, memory_payload);
+ rxSlotRestart(victim, frame.meta.transfer_id, memory);
slot = victim;
UDPARD_ASSERT(slot != NULL);
}
@@ -1270,31 +1267,28 @@ static inline int_fast8_t rxIfaceAccept(RxIface* const self,
const UdpardMicrosecond ts = slot->ts_usec;
UDPARD_ASSERT(slot->transfer_id == frame.meta.transfer_id);
result = rxSlotAccept(slot, // May invalidate state variables such as timestamp or transfer-ID.
- &received_transfer->payload_size,
- &received_transfer->payload,
+ &out_transfer->payload_size,
+ &out_transfer->payload,
frame.base,
extent,
- memory_fragment,
- memory_payload);
+ memory);
if (result > 0) // Transfer successfully received, populate the transfer descriptor for the client.
{
- self->ts_usec = ts; // Update the last valid transfer timestamp on this iface.
- received_transfer->timestamp_usec = ts;
- received_transfer->priority = frame.meta.priority;
- received_transfer->source_node_id = frame.meta.src_node_id;
- received_transfer->transfer_id = frame.meta.transfer_id;
+ self->ts_usec = ts; // Update the last valid transfer timestamp on this iface.
+ out_transfer->timestamp_usec = ts;
+ out_transfer->priority = frame.meta.priority;
+ out_transfer->source_node_id = frame.meta.src_node_id;
+ out_transfer->transfer_id = frame.meta.transfer_id;
}
}
else
{
- memFreePayload(memory_payload, frame.base.origin);
+ memFreePayload(memory.payload, frame.base.origin);
}
return result;
}
-static inline void rxIfaceInit(RxIface* const self,
- struct UdpardMemoryResource* const memory_fragment,
- struct UdpardMemoryResource* const memory_payload)
+static inline void rxIfaceInit(RxIface* const self, const RxMemory memory)
{
UDPARD_ASSERT(self != NULL);
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
@@ -1303,7 +1297,61 @@ static inline void rxIfaceInit(RxIface* const self,
for (uint_fast8_t i = 0; i < RX_SLOT_COUNT; i++)
{
self->slots[i].fragments = NULL;
- rxSlotRestart(&self->slots[i], TRANSFER_ID_UNSET, memory_fragment, memory_payload);
+ rxSlotRestart(&self->slots[i], TRANSFER_ID_UNSET, memory);
+ }
+}
+
+static inline int_fast8_t rxSessionAccept(UdpardInternalRxSession* const self,
+ const uint_fast8_t redundant_iface_index,
+ const UdpardMicrosecond ts_usec,
+ const RxFrame frame,
+ const size_t extent,
+ const UdpardMicrosecond transfer_id_timeout_usec,
+ const RxMemory memory,
+ struct UdpardRxTransfer* const out_transfer)
+{
+ UDPARD_ASSERT((self != NULL) && (redundant_iface_index < UDPARD_NETWORK_INTERFACE_COUNT_MAX) &&
+ (out_transfer != NULL));
+ int_fast8_t result = rxIfaceAccept(&self->ifaces[redundant_iface_index],
+ ts_usec,
+ frame,
+ extent,
+ transfer_id_timeout_usec,
+ memory,
+ out_transfer);
+ UDPARD_ASSERT(result <= 1);
+ if (result > 0)
+ {
+ const bool future_tid = (self->last_transfer_id == TRANSFER_ID_UNSET) || //
+ (out_transfer->transfer_id > self->last_transfer_id);
+ const bool tid_timeout = (self->last_ts_usec == TIMESTAMP_UNSET) ||
+ ((out_transfer->timestamp_usec >= self->last_ts_usec) &&
+ ((out_transfer->timestamp_usec - self->last_ts_usec) >= transfer_id_timeout_usec));
+ if (future_tid || tid_timeout) // Notice that the deduplicator makes no distinction between the interfaces.
+ {
+ self->last_ts_usec = out_transfer->timestamp_usec;
+ self->last_transfer_id = out_transfer->transfer_id;
+ }
+ else // This is a duplicate: received from another interface, a FEC retransmission, or a network glitch.
+ {
+ result = 0;
+ memFreePayload(memory.payload, out_transfer->payload.origin);
+ rxFragmentFree(out_transfer->payload.next, memory);
+ }
+ }
+ return result;
+}
+
+static inline void rxSessionInit(UdpardInternalRxSession* const self, const RxMemory memory)
+{
+ UDPARD_ASSERT(self != NULL);
+ // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
+ (void) memset(self, 0, sizeof(*self));
+ self->last_ts_usec = TIMESTAMP_UNSET;
+ self->last_transfer_id = TRANSFER_ID_UNSET;
+ for (uint_fast8_t i = 0; i < UDPARD_NETWORK_INTERFACE_COUNT_MAX; i++)
+ {
+ rxIfaceInit(&self->ifaces[i], memory);
}
}
@@ -1311,8 +1359,15 @@ void udpardFragmentFree(const struct UdpardFragment head,
struct UdpardMemoryResource* const memory_fragment,
struct UdpardMemoryResource* const memory_payload)
{
- memFreePayload(memory_payload, head.origin); // May be NULL, is okay.
- rxFragmentFree(head.next, memory_fragment, memory_payload); // The head is not heap-allocated so not freed.
+ if ((memory_fragment != NULL) && (memory_payload != NULL))
+ {
+ memFreePayload(memory_payload, head.origin); // May be NULL, is okay.
+ rxFragmentFree(head.next, // The head is not heap-allocated so not freed.
+ (RxMemory){
+ .fragment = memory_fragment,
+ .payload = memory_payload,
+ });
+ }
}
int8_t udpardRxSubscriptionInit(struct UdpardRxSubscription* const self,
@@ -1325,7 +1380,7 @@ int8_t udpardRxSubscriptionInit(struct UdpardRxSubscription* const self,
(void) extent;
(void) memory;
(void) rxParseFrame;
- (void) rxIfaceInit;
- (void) rxIfaceAccept;
+ (void) rxSessionInit;
+ (void) rxSessionAccept;
return 0;
}
diff --git a/libudpard/udpard.h b/libudpard/udpard.h
index 1ddcd7d..3677bcf 100644
--- a/libudpard/udpard.h
+++ b/libudpard/udpard.h
@@ -834,12 +834,13 @@ void udpardRxSubscriptionDestroy(struct UdpardRxSubscription* const self);
/// carry a final part of a valid multi-frame transfer, or carry a valid single-frame transfer.
/// The last two cases are said to complete a transfer.
///
-/// If the datagram completes a transfer, the received_transfer argument is filled with the transfer details
+/// If the datagram completes a transfer, the out_transfer argument is filled with the transfer details
/// and the return value is one.
/// The caller is assigned ownership of the transfer payload buffer memory; it has to be freed after use as described
/// in the documentation for UdpardRxTransfer.
+/// The memory pointed to by out_transfer may be mutated arbitrarily if no transfer is completed.
///
-/// If the datagram does not complete a transfer or is malformed, the function returns zero and the received_transfer
+/// If the datagram does not complete a transfer or is malformed, the function returns zero and the out_transfer
/// is not modified. Observe that malformed frames are not treated as errors, as the local application is not
/// responsible for the behavior of external agents producing the datagrams.
///
@@ -866,7 +867,7 @@ int8_t udpardRxSubscriptionReceive(struct UdpardRxSubscription* const self,
const UdpardMicrosecond timestamp_usec,
const struct UdpardMutablePayload datagram_payload,
const uint_fast8_t redundant_iface_index,
- struct UdpardRxTransfer* const received_transfer);
+ struct UdpardRxTransfer* const out_transfer);
// --------------------------------------------- RPC-SERVICES ---------------------------------------------
@@ -1002,7 +1003,7 @@ int8_t udpardRxRPCDispatcherReceive(struct UdpardRxRPCDispatcher* const self,
const UdpardMicrosecond timestamp_usec,
const struct UdpardMutablePayload datagram_payload,
const uint_fast8_t redundant_iface_index,
- struct UdpardRxRPCTransfer* const received_transfer);
+ struct UdpardRxRPCTransfer* const out_transfer);
#ifdef __cplusplus
}
diff --git a/tests/.idea/dictionaries/pavel.xml b/tests/.idea/dictionaries/pavel.xml
index 60e0200..f1e8005 100644
--- a/tests/.idea/dictionaries/pavel.xml
+++ b/tests/.idea/dictionaries/pavel.xml
@@ -4,6 +4,7 @@
baremetal
cfamily
deallocation
+ deduplicator
discardment
dscp
dudpard
diff --git a/tests/src/test_intrusive_rx.c b/tests/src/test_intrusive_rx.c
index 355c580..39b75a6 100644
--- a/tests/src/test_intrusive_rx.c
+++ b/tests/src/test_intrusive_rx.c
@@ -121,6 +121,11 @@ static RxFrame makeRxFrameString(struct UdpardMemoryResource* const memory_paylo
.meta = meta};
}
+static RxMemory makeRxMemory(InstrumentedAllocator* const fragment, InstrumentedAllocator* const payload)
+{
+ return (RxMemory){.fragment = &fragment->base, .payload = &payload->base};
+}
+
// --------------------------------------------------------------------------------------------------------------------
// Generate reference data using PyCyphal:
@@ -270,7 +275,7 @@ static void testSlotRestartEmpty(void)
.fragments = NULL,
};
InstrumentedAllocator alloc = {0};
- rxSlotRestart(&slot, 0x1122334455667788ULL, &alloc.base, &alloc.base);
+ rxSlotRestart(&slot, 0x1122334455667788ULL, makeRxMemory(&alloc, &alloc));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, slot.ts_usec);
TEST_ASSERT_EQUAL(0x1122334455667788ULL, slot.transfer_id);
TEST_ASSERT_EQUAL(0, slot.max_index);
@@ -324,7 +329,7 @@ static void testSlotRestartNonEmpty(void)
TEST_ASSERT_EQUAL(3, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(sizeof(RxFragment) * 3, mem_fragment.allocated_bytes);
// Now we reset the slot, causing all memory to be freed correctly.
- rxSlotRestart(&slot, 0x1122334455667788ULL, &mem_fragment.base, &mem_payload.base);
+ rxSlotRestart(&slot, 0x1122334455667788ULL, makeRxMemory(&mem_fragment, &mem_payload));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, slot.ts_usec);
TEST_ASSERT_EQUAL(0x1122334455667788ULL, slot.transfer_id);
TEST_ASSERT_EQUAL(0, slot.max_index);
@@ -394,8 +399,7 @@ static void testSlotEjectValidLarge(void)
&root->tree,
mem_payload.allocated_bytes,
1024,
- &mem_fragment.base,
- &mem_payload.base));
+ makeRxMemory(&mem_fragment, &mem_payload)));
TEST_ASSERT_EQUAL(PayloadSize, payload_size); // CRC removed!
TEST_ASSERT( //
compareStringWithPayload("Da Shi, have you ever... considered certain ultimate philosophical questions? ",
@@ -481,8 +485,7 @@ static void testSlotEjectValidSmall(void)
&root->tree,
mem_payload.allocated_bytes,
136, // <-- small extent, rest truncated
- &mem_fragment.base,
- &mem_payload.base));
+ makeRxMemory(&mem_fragment, &mem_payload)));
TEST_ASSERT_EQUAL(136, payload_size); // Equals the extent due to the truncation.
TEST_ASSERT(compareStringWithPayload("Did you build this four-dimensional fragment?\n", payload.view));
TEST_ASSERT(compareStringWithPayload("You told me that you came from the sea. Did you build the sea?\n",
@@ -533,8 +536,7 @@ static void testSlotEjectValidEmpty(void)
&root->tree,
mem_payload.allocated_bytes,
0,
- &mem_fragment.base,
- &mem_payload.base));
+ makeRxMemory(&mem_fragment, &mem_payload)));
TEST_ASSERT_EQUAL(0, payload_size); // Equals the extent due to the truncation.
TEST_ASSERT_NULL(payload.next);
TEST_ASSERT_EQUAL(0, payload.view.size);
@@ -580,8 +582,7 @@ static void testSlotEjectInvalid(void)
&root->tree,
mem_payload.allocated_bytes,
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ makeRxMemory(&mem_fragment, &mem_payload)));
// The call was unsuccessful, so the memory was freed instead of being handed over to the application.
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_payload.allocated_bytes);
@@ -595,6 +596,7 @@ static void testSlotAcceptA(void)
InstrumentedAllocator mem_payload = {0};
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
+ const RxMemory mem = makeRxMemory(&mem_fragment, &mem_payload);
// Set up the RX slot instance we're going to be working with.
RxSlot slot = {
.ts_usec = 1234567890,
@@ -622,8 +624,7 @@ static void testSlotAcceptA(void)
"The fish responsible for drying the sea are not here."
"\x04\x1F\x8C\x1F"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
// Verify the memory utilization. Note that the small transfer optimization is in effect: head fragment moved.
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(53 + TRANSFER_CRC_SIZE_BYTES, mem_payload.allocated_bytes);
@@ -658,8 +659,7 @@ static void testSlotAcceptA(void)
false,
"We're sorry. What you said is really hard to understand.\n"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(0,
rxSlotAccept(&slot,
&payload_size,
@@ -670,8 +670,7 @@ static void testSlotAcceptA(void)
"The fish who dried the sea went onto land before they did "
"this. "),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(1,
rxSlotAccept(&slot,
&payload_size,
@@ -682,8 +681,7 @@ static void testSlotAcceptA(void)
"They moved from one dark forest to another dark forest."
"?\xAC(\xBE"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
// Verify the memory utilization. Note that the small transfer optimization is in effect: head fragment moved.
TEST_ASSERT_EQUAL(3, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(176 + TRANSFER_CRC_SIZE_BYTES, mem_payload.allocated_bytes);
@@ -727,8 +725,7 @@ static void testSlotAcceptA(void)
"Toss it over."
"K(\xBB\xEE"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(1, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0,
@@ -740,8 +737,7 @@ static void testSlotAcceptA(void)
false,
"How do we give it to you?\n"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(2, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(2, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0,
@@ -753,8 +749,7 @@ static void testSlotAcceptA(void)
false,
"DUPLICATE #1"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(2, mem_payload.allocated_fragments); // NO CHANGE, duplicate discarded.
TEST_ASSERT_EQUAL(2, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0,
@@ -766,8 +761,7 @@ static void testSlotAcceptA(void)
true,
"DUPLICATE #2"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(2, mem_payload.allocated_fragments); // NO CHANGE, duplicate discarded.
TEST_ASSERT_EQUAL(2, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(1, // transfer completed
@@ -779,8 +773,7 @@ static void testSlotAcceptA(void)
false,
"I like fish. Can I have it?\n"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
// Verify the memory utilization. Note that the small transfer optimization is in effect: head fragment moved.
// Due to the implicit truncation (the extent is small), the last fragment is already freed.
TEST_ASSERT_EQUAL(2, mem_payload.allocated_fragments); // One freed because of truncation.
@@ -815,8 +808,7 @@ static void testSlotAcceptA(void)
&payload,
makeRxFrameBaseString(&mem_payload.base, 0, true, ":D"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_payload.allocated_bytes);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
@@ -846,8 +838,7 @@ static void testSlotAcceptA(void)
"Toss it over."
"K(\xBB\xEE"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(1, mem_fragment.allocated_fragments); // Limit reached here. Cannot accept next fragment.
TEST_ASSERT_EQUAL(-UDPARD_ERROR_MEMORY,
@@ -859,8 +850,7 @@ static void testSlotAcceptA(void)
false,
"How do we give it to you?\n"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments); // Payload not accepted, cannot alloc fragment.
TEST_ASSERT_EQUAL(1, mem_fragment.allocated_fragments);
mem_fragment.limit_fragments = 2; // Lift the limit and repeat the same frame, this time it is accepted.
@@ -873,8 +863,7 @@ static void testSlotAcceptA(void)
false,
"I like fish. Can I have it?\n"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(2, mem_payload.allocated_fragments); // Accepted!
TEST_ASSERT_EQUAL(2, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(-UDPARD_ERROR_MEMORY, // Cannot alloc third fragment.
@@ -886,8 +875,7 @@ static void testSlotAcceptA(void)
false,
"How do we give it to you?\n"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(2, mem_payload.allocated_fragments); // Payload not accepted, cannot alloc fragment.
TEST_ASSERT_EQUAL(2, mem_fragment.allocated_fragments);
mem_fragment.limit_fragments = 3; // Lift the limit and repeat the same frame, this time it is accepted.
@@ -900,8 +888,7 @@ static void testSlotAcceptA(void)
false,
"How do we give it to you?\n"),
1000,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
// Verify the memory utilization. Note that the small transfer optimization is in effect: head fragment moved.
TEST_ASSERT_EQUAL(3, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(67 + TRANSFER_CRC_SIZE_BYTES, mem_payload.allocated_bytes);
@@ -943,8 +930,7 @@ static void testSlotAcceptA(void)
"Toss it over."
"K(\xBB\xEE"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments); // Okay, accepted, some data stored...
TEST_ASSERT_EQUAL(1, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0,
@@ -956,8 +942,7 @@ static void testSlotAcceptA(void)
true, // SURPRISE! EOT is set in distinct frames!
"How do we give it to you?\n"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments); // This is outrageous. Of course we have to drop everything.
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
// Ensure the slot has been restarted correctly.
@@ -983,8 +968,7 @@ static void testSlotAcceptA(void)
"Toss it over."
"K(\xBB\xEE"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments); // Okay, accepted, some data stored...
TEST_ASSERT_EQUAL(1, mem_fragment.allocated_fragments);
TEST_ASSERT_EQUAL(0,
@@ -996,8 +980,7 @@ static void testSlotAcceptA(void)
false,
"How do we give it to you?\n"),
45,
- &mem_fragment.base,
- &mem_payload.base));
+ mem));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments); // This is outrageous. Of course we have to drop everything.
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
// Ensure the slot has been restarted correctly.
@@ -1017,7 +1000,7 @@ static void testIfaceIsFutureTransferID(void)
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
RxIface iface;
- rxIfaceInit(&iface, &mem_fragment.base, &mem_payload.base);
+ rxIfaceInit(&iface, makeRxMemory(&mem_fragment, &mem_payload));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
for (size_t i = 0; i < RX_SLOT_COUNT; i++)
{
@@ -1049,7 +1032,7 @@ static void testIfaceCheckTransferIDTimeout(void)
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
RxIface iface;
- rxIfaceInit(&iface, &mem_fragment.base, &mem_payload.base);
+ rxIfaceInit(&iface, makeRxMemory(&mem_fragment, &mem_payload));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
for (size_t i = 0; i < RX_SLOT_COUNT; i++)
{
@@ -1114,8 +1097,8 @@ static void testIfaceFindMatchingSlot(void)
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
RxSlot slots[RX_SLOT_COUNT] = {0};
- rxSlotRestart(&slots[0], 1000, &mem_fragment.base, &mem_payload.base);
- rxSlotRestart(&slots[1], 1001, &mem_fragment.base, &mem_payload.base);
+ rxSlotRestart(&slots[0], 1000, makeRxMemory(&mem_fragment, &mem_payload));
+ rxSlotRestart(&slots[1], 1001, makeRxMemory(&mem_fragment, &mem_payload));
// No matching slot.
TEST_ASSERT_NULL(rxIfaceFindMatchingSlot(slots, 123));
// Matching slots.
@@ -1151,8 +1134,9 @@ static void testIfaceAcceptA(void)
InstrumentedAllocator mem_payload = {0};
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
- RxIface iface;
- rxIfaceInit(&iface, &mem_fragment.base, &mem_payload.base);
+ const RxMemory mem = makeRxMemory(&mem_fragment, &mem_payload);
+ RxIface iface;
+ rxIfaceInit(&iface, mem);
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
for (size_t i = 0; i < RX_SLOT_COUNT; i++)
{
@@ -1181,11 +1165,10 @@ static void testIfaceAcceptA(void)
true,
"I am a tomb."
"\x1F\\\xCDs"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(1, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments); // Head fragment is not heap-allocated.
// Check the transfer we just accepted.
@@ -1218,11 +1201,10 @@ static void testIfaceAcceptA(void)
true,
"I am a tomb."
"\x1F\\\xCDs"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
// Check the internal states of the iface.
@@ -1245,11 +1227,10 @@ static void testIfaceAcceptA(void)
true,
"I am a tomb."
"No CRC here."),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
// Check the internal states of the iface.
@@ -1273,11 +1254,10 @@ static void testIfaceAcceptA(void)
true,
"I am a tomb."
"No CRC here, #2."),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
// Check the internal states of the iface.
@@ -1304,11 +1284,10 @@ static void testIfaceAcceptA(void)
true,
"A2"
"v\x1E\xBD]"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(1234567890, iface.ts_usec); // same old timestamp
TEST_ASSERT_EQUAL(TRANSFER_ID_UNSET, iface.slots[0].transfer_id); // Still unused.
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id); // Replaced the old one, it was unneeded.
@@ -1328,11 +1307,10 @@ static void testIfaceAcceptA(void)
true,
"B1"
"g\x8D\x9A\xD7"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(1234567890, iface.ts_usec); // same old timestamp
TEST_ASSERT_EQUAL(1001, iface.slots[0].transfer_id); // Used for B because the other one is taken.
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id); // Keeps A because it is in-progress, can't discard.
@@ -1351,11 +1329,10 @@ static void testIfaceAcceptA(void)
0,
false,
"A0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(1234567890, iface.ts_usec); // same old timestamp
TEST_ASSERT_EQUAL(1001, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id);
@@ -1374,11 +1351,10 @@ static void testIfaceAcceptA(void)
0,
false,
"B0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// TRANSFER B RECEIVED, check it.
TEST_ASSERT_EQUAL(2000000010, iface.ts_usec);
TEST_ASSERT_EQUAL(1002, iface.slots[0].transfer_id); // Incremented to meet the next transfer.
@@ -1411,11 +1387,10 @@ static void testIfaceAcceptA(void)
1,
false,
"A1"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// TRANSFER A RECEIVED, check it.
TEST_ASSERT_EQUAL(2000000020, iface.ts_usec); // same old timestamp
TEST_ASSERT_EQUAL(1002, iface.slots[0].transfer_id);
@@ -1443,8 +1418,9 @@ static void testIfaceAcceptB(void)
InstrumentedAllocator mem_payload = {0};
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
- RxIface iface;
- rxIfaceInit(&iface, &mem_fragment.base, &mem_payload.base);
+ const RxMemory mem = makeRxMemory(&mem_fragment, &mem_payload);
+ RxIface iface;
+ rxIfaceInit(&iface, mem);
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
for (size_t i = 0; i < RX_SLOT_COUNT; i++)
{
@@ -1478,11 +1454,10 @@ static void testIfaceAcceptB(void)
true,
"A2"
"v\x1E\xBD]"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(TRANSFER_ID_UNSET, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id);
@@ -1502,11 +1477,10 @@ static void testIfaceAcceptB(void)
true,
"B1"
"g\x8D\x9A\xD7"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(1001, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id);
@@ -1525,11 +1499,10 @@ static void testIfaceAcceptB(void)
0,
false,
"A0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(1001, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id);
@@ -1548,11 +1521,10 @@ static void testIfaceAcceptB(void)
0,
false,
"C0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(1002, iface.slots[0].transfer_id); // B evicted by C.
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id);
@@ -1571,11 +1543,10 @@ static void testIfaceAcceptB(void)
0,
false,
"B0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(1002, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(1000, iface.slots[1].transfer_id);
@@ -1594,11 +1565,10 @@ static void testIfaceAcceptB(void)
1,
false,
"A1"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// TRANSFER A RECEIVED, check it.
TEST_ASSERT_EQUAL(2000000020, iface.ts_usec); // same old timestamp
TEST_ASSERT_EQUAL(1002, iface.slots[0].transfer_id);
@@ -1631,11 +1601,10 @@ static void testIfaceAcceptB(void)
0,
false,
"C0 DUPLICATE"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(2000000020, iface.ts_usec); // Last transfer timestamp.
TEST_ASSERT_EQUAL(1002, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(1001, iface.slots[1].transfer_id);
@@ -1655,11 +1624,10 @@ static void testIfaceAcceptB(void)
true,
"C1"
"\xA8\xBF}\x19"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// TRANSFER C RECEIVED, check it.
TEST_ASSERT_EQUAL(2000000040, iface.ts_usec);
TEST_ASSERT_EQUAL(1003, iface.slots[0].transfer_id); // Incremented to meet the next transfer.
@@ -1687,8 +1655,9 @@ static void testIfaceAcceptC(void)
InstrumentedAllocator mem_payload = {0};
instrumentedAllocatorNew(&mem_fragment);
instrumentedAllocatorNew(&mem_payload);
- RxIface iface;
- rxIfaceInit(&iface, &mem_fragment.base, &mem_payload.base);
+ const RxMemory mem = makeRxMemory(&mem_fragment, &mem_payload);
+ RxIface iface;
+ rxIfaceInit(&iface, mem);
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
for (size_t i = 0; i < RX_SLOT_COUNT; i++)
{
@@ -1722,11 +1691,10 @@ static void testIfaceAcceptC(void)
0,
false,
"A0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(TRANSFER_ID_UNSET, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(0xA, iface.slots[1].transfer_id);
@@ -1745,11 +1713,10 @@ static void testIfaceAcceptC(void)
0,
false,
"B0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, iface.ts_usec);
TEST_ASSERT_EQUAL(0xB, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(0xA, iface.slots[1].transfer_id);
@@ -1769,11 +1736,10 @@ static void testIfaceAcceptC(void)
true,
"A1"
"\xc7\xac_\x81"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// Check the received transfer.
TEST_ASSERT_EQUAL(2000000010, iface.ts_usec);
TEST_ASSERT_EQUAL(0xB, iface.slots[0].transfer_id);
@@ -1802,11 +1768,10 @@ static void testIfaceAcceptC(void)
0,
false,
"C0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(2000000010, iface.ts_usec); // <- unchanged.
TEST_ASSERT_EQUAL(0xB, iface.slots[0].transfer_id);
TEST_ASSERT_EQUAL(0xC, iface.slots[1].transfer_id); // <- reused for C.
@@ -1826,11 +1791,10 @@ static void testIfaceAcceptC(void)
true,
"B1"
"g\x8D\x9A\xD7"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// Check the received transfer.
TEST_ASSERT_EQUAL(2000000020, iface.ts_usec);
TEST_ASSERT_EQUAL(0xC, iface.slots[0].transfer_id); // <-- INCREMENTED, SO
@@ -1862,11 +1826,10 @@ static void testIfaceAcceptC(void)
true,
"C1"
"\xA8\xBF}\x19"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
// Check the received transfer.
TEST_ASSERT_EQUAL(2000000040, iface.ts_usec);
TEST_ASSERT_EQUAL(0xC, iface.slots[0].transfer_id); // Old, unused.
@@ -1896,11 +1859,10 @@ static void testIfaceAcceptC(void)
0,
false,
"B0"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
// B0 duplicate single-frame; shall be rejected.
@@ -1917,15 +1879,28 @@ static void testIfaceAcceptC(void)
true,
"B0"
"g\x8D\x9A\xD7"),
- &transfer,
1000,
UDPARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC,
- &mem_fragment.base,
- &mem_payload.base));
+ mem,
+ &transfer));
TEST_ASSERT_EQUAL(0, mem_payload.allocated_fragments);
TEST_ASSERT_EQUAL(0, mem_fragment.allocated_fragments);
}
+static void testSessionAcceptA(void)
+{
+ InstrumentedAllocator mem_fragment = {0};
+ InstrumentedAllocator mem_payload = {0};
+ instrumentedAllocatorNew(&mem_fragment);
+ instrumentedAllocatorNew(&mem_payload);
+ const RxMemory mem = makeRxMemory(&mem_fragment, &mem_payload);
+ UdpardInternalRxSession session = {0};
+ rxSessionInit(&session, mem);
+ TEST_ASSERT_EQUAL(TIMESTAMP_UNSET, session.last_ts_usec);
+ TEST_ASSERT_EQUAL(TRANSFER_ID_UNSET, session.last_transfer_id);
+ // TODO FIXME add tests
+}
+
void setUp(void) {}
void tearDown(void) {}
@@ -1959,6 +1934,8 @@ int main(void)
RUN_TEST(testIfaceAcceptA);
RUN_TEST(testIfaceAcceptB);
RUN_TEST(testIfaceAcceptC);
+ // session
+ RUN_TEST(testSessionAcceptA);
return UNITY_END();
}