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(); }