From 6181d1e18fb533eb334089f8fbb799c4f793ef29 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 27 Nov 2024 17:17:56 +0200 Subject: [PATCH 01/10] `udpardTxPeek` now returns mutable item - needed for payload ownership transfer. --- libudpard/udpard.c | 91 ++++++++++++-------- libudpard/udpard.h | 56 +++++++----- tests/src/test_e2e.cpp | 26 +++--- tests/src/test_intrusive_tx.c | 156 +++++++++++++++++++++------------- tests/src/test_tx.cpp | 39 ++++++--- 5 files changed, 232 insertions(+), 136 deletions(-) diff --git a/libudpard/udpard.c b/libudpard/udpard.c index 9ff828b..c77b018 100644 --- a/libudpard/udpard.c +++ b/libudpard/udpard.c @@ -5,6 +5,7 @@ #include "udpard.h" #include "_udpard_cavl.h" + #include // --------------------------------------------- BUILD CONFIGURATION --------------------------------------------- @@ -280,9 +281,6 @@ typedef struct { struct UdpardTxItem base; enum UdpardPriority priority; ///< Do we need this exposed in the public structure? We already have DSCP there. - // The MISRA violation here is hard to get rid of without having to allocate a separate memory block for the - // payload, which is much more costly risk-wise. - byte_t payload_buffer[]; // NOSONAR MISRA C 18.7 Flexible array member. } TxItem; /// Chain of TX frames prepared for insertion into a TX queue. @@ -293,15 +291,21 @@ typedef struct size_t count; } TxChain; -static inline TxItem* txNewItem(const struct UdpardMemoryResource memory, - const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], - const UdpardMicrosecond deadline_usec, - const enum UdpardPriority priority, - const struct UdpardUDPIPEndpoint endpoint, - const size_t datagram_payload_size, - void* const user_transfer_reference) +static inline bool txValidateMemoryResources(const struct UdpardTxMemoryResources memory) +{ + return (memory.fragment.allocate != NULL) && (memory.fragment.deallocate != NULL) && + (memory.payload.allocate != NULL) && (memory.payload.deallocate != NULL); +} + +static inline TxItem* txNewItem(const struct UdpardTxMemoryResources memory, + const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], + const UdpardMicrosecond deadline_usec, + const enum UdpardPriority priority, + const struct UdpardUDPIPEndpoint endpoint, + const size_t datagram_payload_size, + void* const user_transfer_reference) { - TxItem* const out = (TxItem*) memAlloc(memory, sizeof(TxItem) + datagram_payload_size); + TxItem* out = memAlloc(memory.fragment, sizeof(TxItem)); if (out != NULL) { // No tree linkage by default. @@ -317,9 +321,18 @@ static inline TxItem* txNewItem(const struct UdpardMemoryResource memory, out->base.dscp = dscp_value_per_priority[priority]; out->base.destination = endpoint; out->base.user_transfer_reference = user_transfer_reference; - // The payload points to the buffer already allocated. - out->base.datagram_payload.size = datagram_payload_size; - out->base.datagram_payload.data = &out->payload_buffer[0]; + + void* const payload_data = memAlloc(memory.payload, datagram_payload_size); + if (NULL != payload_data) + { + out->base.datagram_payload.data = payload_data; + out->base.datagram_payload.size = datagram_payload_size; + } + else + { + memFree(memory.fragment, sizeof(TxItem), out); + out = NULL; + } } return out; } @@ -390,14 +403,14 @@ static inline byte_t* txSerializeHeader(byte_t* const destination_buffe /// Produces a chain of Tx queue items for later insertion into the Tx queue. The tail is NULL if OOM. /// The caller is responsible for freeing the memory allocated for the chain. -static inline TxChain txMakeChain(const struct UdpardMemoryResource memory, - const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], - const size_t mtu, - const UdpardMicrosecond deadline_usec, - const TransferMetadata meta, - const struct UdpardUDPIPEndpoint endpoint, - const struct UdpardPayload payload, - void* const user_transfer_reference) +static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, + const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], + const size_t mtu, + const UdpardMicrosecond deadline_usec, + const TransferMetadata meta, + const struct UdpardUDPIPEndpoint endpoint, + const struct UdpardPayload payload, + void* const user_transfer_reference) { UDPARD_ASSERT(mtu > 0); UDPARD_ASSERT((payload.data != NULL) || (payload.size == 0U)); @@ -430,8 +443,9 @@ static inline TxChain txMakeChain(const struct UdpardMemoryResource memory, { break; } - const bool last = (payload_size_with_crc - offset) <= mtu; - byte_t* write_ptr = txSerializeHeader(&item->payload_buffer[0], meta, (uint32_t) out.count, last); + const bool last = (payload_size_with_crc - offset) <= mtu; + byte_t* const dst_buffer = item->base.datagram_payload.data; + byte_t* write_ptr = txSerializeHeader(dst_buffer, meta, (uint32_t) out.count, last); if (offset < payload.size) { const size_t progress = smaller(payload.size - offset, mtu); @@ -446,7 +460,7 @@ static inline TxChain txMakeChain(const struct UdpardMemoryResource memory, { const size_t crc_offset = offset - payload.size; UDPARD_ASSERT(crc_offset < TRANSFER_CRC_SIZE_BYTES); - const size_t available = item->base.datagram_payload.size - (size_t) (write_ptr - &item->payload_buffer[0]); + const size_t available = item->base.datagram_payload.size - (size_t) (write_ptr - dst_buffer); UDPARD_ASSERT(available <= TRANSFER_CRC_SIZE_BYTES); const size_t write_size = smaller(TRANSFER_CRC_SIZE_BYTES - crc_offset, available); // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) @@ -517,7 +531,7 @@ static inline int32_t txPush(struct UdpardTx* const tx, while (head != NULL) { struct UdpardTxItem* const next = head->next_in_transfer; - memFree(tx->memory, sizeof(TxItem) + head->datagram_payload.size, head); + udpardTxFree(tx->memory, head); head = next; } } @@ -526,13 +540,13 @@ static inline int32_t txPush(struct UdpardTx* const tx, return out; } -int_fast8_t udpardTxInit(struct UdpardTx* const self, - const UdpardNodeID* const local_node_id, - const size_t queue_capacity, - const struct UdpardMemoryResource memory) +int_fast8_t udpardTxInit(struct UdpardTx* const self, + const UdpardNodeID* const local_node_id, + const size_t queue_capacity, + const struct UdpardTxMemoryResources memory) { int_fast8_t ret = -UDPARD_ERROR_ARGUMENT; - if ((NULL != self) && (NULL != local_node_id) && (memory.allocate != NULL) && (memory.deallocate != NULL)) + if ((NULL != self) && (NULL != local_node_id) && txValidateMemoryResources(memory)) { ret = 0; memZero(sizeof(*self), self); @@ -639,14 +653,14 @@ int32_t udpardTxRespond(struct UdpardTx* const self, return out; } -const struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self) +struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self) { - const struct UdpardTxItem* out = NULL; + struct UdpardTxItem* out = NULL; if (self != NULL) { // Paragraph 6.7.2.1.15 of the C standard says: // A pointer to a structure object, suitably converted, points to its initial member, and vice versa. - out = (const struct UdpardTxItem*) (void*) cavlFindExtremum(self->root, false); + out = (struct UdpardTxItem*) (void*) cavlFindExtremum(self->root, false); } return out; } @@ -671,11 +685,16 @@ struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, const struct Udpar return out; } -void udpardTxFree(const struct UdpardMemoryResource memory, struct UdpardTxItem* const item) +void udpardTxFree(const struct UdpardTxMemoryResources memory, struct UdpardTxItem* const item) { if (item != NULL) { - memFree(memory, sizeof(TxItem) + item->datagram_payload.size, item); + if (item->datagram_payload.data != NULL) + { + memFree(memory.payload, item->datagram_payload.size, item->datagram_payload.data); + } + + memFree(memory.fragment, sizeof(TxItem), item); } } diff --git a/libudpard/udpard.h b/libudpard/udpard.h index 0df85b9..3223d32 100644 --- a/libudpard/udpard.h +++ b/libudpard/udpard.h @@ -373,6 +373,28 @@ struct UdpardMemoryResource // ================================================= TX PIPELINE ================================================= // ===================================================================================================================== +/// The set of memory resources is used per an TX pipeline instance. +/// These are used to serve the memory needs of the library to keep state while assembling outgoing frames. +/// Several memory resources are provided to enable fine control over the allocated memory. +/// +/// This TX queue uses these memory resources for allocating the enqueued items (UDP datagrams). +/// There are exactly two allocations per enqueued item: +/// - the first for bookkeeping purposes (UdpardTxItem) +/// - second for payload storage (the frame data) +/// In a simple application, there would be just one memory resource shared by all parts of the library. +/// If the application knows its MTU, it can use block allocation to avoid extrinsic fragmentation. +/// +struct UdpardTxMemoryResources +{ + /// The fragment handles are allocated per payload fragment; each handle contains a pointer to its fragment. + /// Each instance is of a very small fixed size, so a trivial zero-fragmentation block allocator is enough. + struct UdpardMemoryResource fragment; + + /// The payload fragments are allocated per payload frame; each payload fragment is at most MTU-sized buffer, + /// so a trivial zero-fragmentation MTU-sized block allocator is enough if MTU is known in advance. + struct UdpardMemoryResource payload; +}; + /// The transmission pipeline is a prioritized transmission queue that keeps UDP datagrams (aka transport frames) /// destined for transmission via one network interface. /// Applications with redundant network interfaces are expected to have one instance of this type per interface. @@ -424,12 +446,8 @@ struct UdpardTx /// The value can be changed arbitrarily at any time between enqueue operations. uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U]; - /// The memory resource used by this queue for allocating the enqueued items (UDP datagrams). - /// There is exactly one allocation per enqueued item, each allocation contains both the UdpardTxItem - /// and its payload, hence the size is variable. - /// In a simple application there would be just one memory resource shared by all parts of the library. - /// If the application knows its MTU, it can use block allocation to avoid extrinsic fragmentation. - struct UdpardMemoryResource memory; + /// Refer to UdpardTxMemoryResources. + struct UdpardTxMemoryResources memory; /// The number of frames that are currently contained in the queue, initially zero. /// READ-ONLY @@ -490,10 +508,10 @@ struct UdpardTxItem /// /// The return value is zero on success, otherwise it is a negative error code. /// The time complexity is constant. This function does not invoke the dynamic memory manager. -int_fast8_t udpardTxInit(struct UdpardTx* const self, - const UdpardNodeID* const local_node_id, - const size_t queue_capacity, - const struct UdpardMemoryResource memory); +int_fast8_t udpardTxInit(struct UdpardTx* const self, + const UdpardNodeID* const local_node_id, + const size_t queue_capacity, + const struct UdpardTxMemoryResources memory); /// This function serializes a message transfer into a sequence of UDP datagrams and inserts them into the prioritized /// transmission queue at the appropriate position. Afterwards, the application is supposed to take the enqueued frames @@ -613,23 +631,20 @@ int32_t udpardTxRespond(struct UdpardTx* const self, /// /// If the queue is non-empty, the returned value is a pointer to its top element (i.e., the next item to transmit). /// The returned pointer points to an object allocated in the dynamic storage; it should be eventually freed by the -/// application by calling udpardTxFree with UdpardTx::memory. The memory shall not be freed before the item is removed +/// application by calling `udpardTxFree`. The memory shall not be freed before the item is removed /// from the queue by calling udpardTxPop; this is because until udpardTxPop is executed, the library retains /// ownership of the item. The pointer retains validity until explicitly freed by the application; in other words, /// calling udpardTxPop does not invalidate the object. /// -/// The payload buffer is located shortly after the object itself, in the same memory fragment. The application shall -/// not attempt to free it. -/// /// Calling functions that modify the queue may cause the next invocation to return a different pointer. /// /// The time complexity is logarithmic of the queue size. This function does not invoke the dynamic memory manager. -const struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self); +struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self); /// This function transfers the ownership of the specified item of the prioritized transmission queue from the queue /// to the application. The item does not necessarily need to be the top one -- it is safe to dequeue any item. /// The item is dequeued but not invalidated; it is the responsibility of the application to deallocate its memory -/// later. The memory SHALL NOT be deallocated UNTIL this function is invoked. +/// later. The memory SHALL NOT be deallocated UNTIL this function is invoked (use `udpardTxFree` helper). /// The function returns the same pointer that it is given except that it becomes mutable. /// /// If any of the arguments are NULL, the function has no effect and returns NULL. @@ -637,11 +652,12 @@ const struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self); /// The time complexity is logarithmic of the queue size. This function does not invoke the dynamic memory manager. struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, const struct UdpardTxItem* const item); -/// This is a simple helper that frees the memory allocated for the item with the correct size. -/// It is needed because the application does not have access to the required context to compute the size. -/// If the chosen allocator does not leverage the size information, the deallocation function can be invoked directly. +/// This is a simple helper that frees the memory allocated for the item and its payload, +/// using the correct sizes and memory resources. /// If the item argument is NULL, the function has no effect. The time complexity is constant. -void udpardTxFree(const struct UdpardMemoryResource memory, struct UdpardTxItem* const item); +/// If the item frame payload is NULL then it is assumed that the payload buffer was already freed, +/// or moved to a different owner (f.e. to media layer). +void udpardTxFree(const struct UdpardTxMemoryResources memory, struct UdpardTxItem* const item); // ===================================================================================================================== // ================================================= RX PIPELINE ================================================= diff --git a/tests/src/test_e2e.cpp b/tests/src/test_e2e.cpp index d3437aa..b7de0ee 100644 --- a/tests/src/test_e2e.cpp +++ b/tests/src/test_e2e.cpp @@ -73,7 +73,10 @@ void testPubSub() instrumentedAllocatorNew(&alloc_rx_session); instrumentedAllocatorNew(&alloc_rx_fragment); instrumentedAllocatorNew(&alloc_rx_payload); - const auto mem_tx = instrumentedAllocatorMakeMemoryResource(&alloc_tx); + const UdpardTxMemoryResources mem_tx{ + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc_tx), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc_tx), + }; const UdpardRxMemoryResources mem_rx{ .session = instrumentedAllocatorMakeMemoryResource(&alloc_rx_session), .fragment = instrumentedAllocatorMakeMemoryResource(&alloc_rx_fragment), @@ -152,7 +155,7 @@ void testPubSub() makePayload(Dark), nullptr)); TEST_ASSERT_EQUAL(7, tx.queue_size); - TEST_ASSERT_EQUAL(7, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(7 * 2ULL, alloc_tx.allocated_fragments); // Transmit the enqueued frames by pushing them into the subscribers. // Here we pop the frames one by one ensuring that they come out in the correct order. @@ -212,7 +215,7 @@ void testPubSub() TEST_ASSERT_EQUAL(0, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(6, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(6 * 2ULL, alloc_tx.allocated_fragments); // Second transfer. tx_item = udpardTxPeek(&tx); @@ -247,7 +250,7 @@ void testPubSub() TEST_ASSERT_EQUAL(0, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(5, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(5 * 2ULL, alloc_tx.allocated_fragments); // Third transfer. This one is anonymous. tx_item = udpardTxPeek(&tx); @@ -282,7 +285,7 @@ void testPubSub() TEST_ASSERT_EQUAL(0, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(4, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(4 * 2ULL, alloc_tx.allocated_fragments); // Fourth transfer. This one contains multiple frames. We process them one-by-one. // Frame #0. @@ -305,7 +308,7 @@ void testPubSub() TEST_ASSERT_EQUAL(1, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(3, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc_tx.allocated_fragments); // Frame #1. tx_item = udpardTxPeek(&tx); TEST_ASSERT_NOT_NULL(tx_item); @@ -327,7 +330,7 @@ void testPubSub() TEST_ASSERT_EQUAL(2, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(2, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc_tx.allocated_fragments); // Frame #2. tx_item = udpardTxPeek(&tx); TEST_ASSERT_NOT_NULL(tx_item); @@ -349,7 +352,7 @@ void testPubSub() TEST_ASSERT_EQUAL(3, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(1, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc_tx.allocated_fragments); // Frame #3. This is the last frame of the transfer. The payload is truncated, see the extent. tx_item = udpardTxPeek(&tx); TEST_ASSERT_NOT_NULL(tx_item); @@ -410,7 +413,10 @@ void testRPC() instrumentedAllocatorNew(&alloc_rx_session); instrumentedAllocatorNew(&alloc_rx_fragment); instrumentedAllocatorNew(&alloc_rx_payload); - const auto mem_tx = instrumentedAllocatorMakeMemoryResource(&alloc_tx); + const UdpardTxMemoryResources mem_tx{ + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc_tx), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc_tx), + }; const UdpardRxMemoryResources mem_rx{ .session = instrumentedAllocatorMakeMemoryResource(&alloc_rx_session), .fragment = instrumentedAllocatorMakeMemoryResource(&alloc_rx_fragment), @@ -529,7 +535,7 @@ void testRPC() TEST_ASSERT_EQUAL(0, alloc_rx_payload.allocated_fragments); // Free the TX item. udpardTxFree(mem_tx, udpardTxPop(&tx, tx_item)); - TEST_ASSERT_EQUAL(1, alloc_tx.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc_tx.allocated_fragments); // Second transfer. tx_item = udpardTxPeek(&tx); diff --git a/tests/src/test_intrusive_tx.c b/tests/src/test_intrusive_tx.c index bdd57d7..05ef2c5 100644 --- a/tests/src/test_intrusive_tx.c +++ b/tests/src/test_intrusive_tx.c @@ -94,7 +94,10 @@ static void testMakeChainEmpty(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPriorityFast, @@ -111,7 +114,7 @@ static void testMakeChainEmpty(void) (UdpardUDPIPEndpoint){.ip_address = 0x0A0B0C0DU, .udp_port = 0x1234}, (UdpardPayload){.size = 0, .data = ""}, &user_transfer_referent); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(sizeof(TxItem) + HEADER_SIZE_BYTES + 4, alloc.allocated_bytes); TEST_ASSERT_EQUAL(1, chain.count); TEST_ASSERT_EQUAL(chain.head, chain.tail); @@ -130,7 +133,7 @@ static void testMakeChainEmpty(void) (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, 4)); TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + 4, chain.head); + udpardTxFree(mem, &chain.head->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -138,7 +141,10 @@ static void testMakeChainSingleMaxMTU(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPrioritySlow, @@ -155,7 +161,7 @@ static void testMakeChainSingleMaxMTU(void) (UdpardUDPIPEndpoint){.ip_address = 0x0A0B0C00U, .udp_port = 7474}, (UdpardPayload){.size = DetailOfTheCosmosSize, .data = DetailOfTheCosmos}, &user_transfer_referent); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(sizeof(TxItem) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, alloc.allocated_bytes); TEST_ASSERT_EQUAL(1, chain.count); @@ -181,7 +187,7 @@ static void testMakeChainSingleMaxMTU(void) DetailOfTheCosmosSize, TRANSFER_CRC_SIZE_BYTES)); TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, chain.head); + udpardTxFree(mem, &chain.head->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -189,7 +195,10 @@ static void testMakeChainSingleFrameDefaultMTU(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const byte_t payload[UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1] = {0}; { // Ensure UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME bytes fit in a single frame with the default MTU. const TxChain chain = txMakeChain(mem, @@ -204,16 +213,14 @@ static void testMakeChainSingleFrameDefaultMTU(void) (UdpardUDPIPEndpoint){.ip_address = 0x0A0B0C00U, .udp_port = 7474}, (UdpardPayload){.size = UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME, .data = payload}, NULL); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + TRANSFER_CRC_SIZE_BYTES, alloc.allocated_bytes); TEST_ASSERT_EQUAL(1, chain.count); TEST_ASSERT_EQUAL(chain.head, chain.tail); TEST_ASSERT_EQUAL(NULL, chain.head->base.next_in_transfer); - memFree(mem, - sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + TRANSFER_CRC_SIZE_BYTES, - chain.head); + udpardTxFree(mem, &chain.head->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } { // Increase the payload by 1 byte and ensure it spills over. @@ -230,7 +237,7 @@ static void testMakeChainSingleFrameDefaultMTU(void) (UdpardUDPIPEndpoint){.ip_address = 0x0A0B0C00U, .udp_port = 7474}, (UdpardPayload){.size = UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1, .data = payload}, NULL); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL((sizeof(TxItem) + HEADER_SIZE_BYTES) * 2 + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1 + TRANSFER_CRC_SIZE_BYTES, alloc.allocated_bytes); @@ -238,8 +245,8 @@ static void testMakeChainSingleFrameDefaultMTU(void) TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->base.next_in_transfer); TEST_ASSERT_EQUAL(NULL, chain.tail->base.next_in_transfer); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, &chain.head->base); + udpardTxFree(mem, &chain.tail->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } } @@ -248,7 +255,10 @@ static void testMakeChainThreeFrames(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPriorityNominal, @@ -266,14 +276,14 @@ static void testMakeChainThreeFrames(void) (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, (UdpardPayload){.size = EtherealStrengthSize, .data = EtherealStrength}, &user_transfer_referent); - TEST_ASSERT_EQUAL(3, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(3 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, alloc.allocated_bytes); TEST_ASSERT_EQUAL(3, chain.count); - const UdpardTxItem* const first = &chain.head->base; + UdpardTxItem* const first = &chain.head->base; TEST_ASSERT_NOT_EQUAL(NULL, first); - const UdpardTxItem* const second = first->next_in_transfer; + UdpardTxItem* const second = first->next_in_transfer; TEST_ASSERT_NOT_EQUAL(NULL, second); - const UdpardTxItem* const third = second->next_in_transfer; + UdpardTxItem* const third = second->next_in_transfer; TEST_ASSERT_NOT_EQUAL(NULL, third); TEST_ASSERT_EQUAL(NULL, third->next_in_transfer); TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, third); @@ -320,9 +330,9 @@ static void testMakeChainThreeFrames(void) TEST_ASSERT_EQUAL(&user_transfer_referent, third->user_transfer_reference); // Clean up. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, (void*) first); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, (void*) second); - memFree(mem, alloc.allocated_bytes, (void*) third); + udpardTxFree(mem, first); + udpardTxFree(mem, second); + udpardTxFree(mem, third); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -330,7 +340,10 @@ static void testMakeChainCRCSpill1(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPriorityNominal, @@ -348,7 +361,7 @@ static void testMakeChainCRCSpill1(void) (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); @@ -393,8 +406,8 @@ static void testMakeChainCRCSpill1(void) TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); // Clean up. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, &chain.head->base); + udpardTxFree(mem, &chain.tail->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -402,7 +415,10 @@ static void testMakeChainCRCSpill2(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPriorityNominal, @@ -420,7 +436,7 @@ static void testMakeChainCRCSpill2(void) (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); @@ -465,8 +481,8 @@ static void testMakeChainCRCSpill2(void) TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); // Clean up. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, &chain.head->base); + udpardTxFree(mem, &chain.tail->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -474,7 +490,10 @@ static void testMakeChainCRCSpill3(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPriorityNominal, @@ -492,7 +511,7 @@ static void testMakeChainCRCSpill3(void) (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); @@ -537,8 +556,8 @@ static void testMakeChainCRCSpill3(void) TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); // Clean up. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, &chain.head->base); + udpardTxFree(mem, &chain.tail->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -546,7 +565,10 @@ static void testMakeChainCRCSpillFull(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; char user_transfer_referent = '\0'; const TransferMetadata meta = { .priority = UdpardPriorityNominal, @@ -564,7 +586,7 @@ static void testMakeChainCRCSpillFull(void) (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); @@ -605,8 +627,8 @@ static void testMakeChainCRCSpillFull(void) TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); // Clean up. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, &chain.head->base); + udpardTxFree(mem, &chain.tail->base); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -614,7 +636,10 @@ static void testPushPeekPopFree(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx = { @@ -641,7 +666,7 @@ static void testPushPeekPopFree(void) (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, (UdpardPayload){.size = EtherealStrengthSize, .data = EtherealStrength}, &user_transfer_referent)); - TEST_ASSERT_EQUAL(3, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(3 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, alloc.allocated_bytes); TEST_ASSERT_EQUAL(3, tx.queue_size); @@ -656,7 +681,7 @@ static void testPushPeekPopFree(void) TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, false).data, frame->datagram_payload.data, HEADER_SIZE_BYTES)); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(2, tx.queue_size); frame = udpardTxPeek(&tx); @@ -670,7 +695,7 @@ static void testPushPeekPopFree(void) TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 1, false).data, frame->datagram_payload.data, HEADER_SIZE_BYTES)); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); frame = udpardTxPeek(&tx); @@ -693,7 +718,10 @@ static void testPushPrioritization(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx = { @@ -720,7 +748,7 @@ static void testPushPrioritization(void) (UdpardUDPIPEndpoint){.ip_address = 0xAAAAAAAA, .udp_port = 0xAAAA}, (UdpardPayload){.size = EtherealStrengthSize, .data = EtherealStrength}, NULL)); - TEST_ASSERT_EQUAL(3, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(3, tx.queue_size); const UdpardTxItem* frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); @@ -740,7 +768,7 @@ static void testPushPrioritization(void) (UdpardUDPIPEndpoint){.ip_address = 0xBBBBBBBB, .udp_port = 0xBBBB}, (UdpardPayload){.size = DetailOfTheCosmosSize, .data = DetailOfTheCosmos}, NULL)); - TEST_ASSERT_EQUAL(4, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(4 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(4, tx.queue_size); frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); @@ -760,7 +788,7 @@ static void testPushPrioritization(void) (UdpardUDPIPEndpoint){.ip_address = 0xCCCCCCCC, .udp_port = 0xCCCC}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, NULL)); - TEST_ASSERT_EQUAL(5, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(5 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(5, tx.queue_size); frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); @@ -780,7 +808,7 @@ static void testPushPrioritization(void) (UdpardUDPIPEndpoint){.ip_address = 0xDDDDDDDD, .udp_port = 0xDDDD}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, NULL)); - TEST_ASSERT_EQUAL(6, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(6 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(6, tx.queue_size); frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); @@ -800,7 +828,7 @@ static void testPushPrioritization(void) (UdpardUDPIPEndpoint){.ip_address = 0xEEEEEEEE, .udp_port = 0xEEEE}, (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, NULL)); - TEST_ASSERT_EQUAL(7, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(7 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(7, tx.queue_size); frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); @@ -809,14 +837,14 @@ static void testPushPrioritization(void) // Now, unwind the queue and ensure the frames are popped in the right order. // E udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(6, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(6 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(6, tx.queue_size); // B frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); TEST_ASSERT_EQUAL(0xBBBBBBBB, frame->destination.ip_address); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(5, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(5 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(5, tx.queue_size); // A1, three frames. frame = udpardTxPeek(&tx); @@ -824,7 +852,7 @@ static void testPushPrioritization(void) TEST_ASSERT_EQUAL(0xAAAAAAAA, frame->destination.ip_address); TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta_a, 0, false).data, frame->datagram_payload.data, HEADER_SIZE_BYTES)); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(4, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(4 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(4, tx.queue_size); // A2 frame = udpardTxPeek(&tx); @@ -832,7 +860,7 @@ static void testPushPrioritization(void) TEST_ASSERT_EQUAL(0xAAAAAAAA, frame->destination.ip_address); TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta_a, 1, false).data, frame->datagram_payload.data, HEADER_SIZE_BYTES)); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(3, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(3, tx.queue_size); // A3 frame = udpardTxPeek(&tx); @@ -840,14 +868,14 @@ static void testPushPrioritization(void) TEST_ASSERT_EQUAL(0xAAAAAAAA, frame->destination.ip_address); TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta_a, 2, true).data, frame->datagram_payload.data, HEADER_SIZE_BYTES)); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(2, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(2, tx.queue_size); // C frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); TEST_ASSERT_EQUAL(0xCCCCCCCC, frame->destination.ip_address); udpardTxFree(tx.memory, udpardTxPop(&tx, frame)); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); // D frame = udpardTxPeek(&tx); @@ -864,7 +892,10 @@ static void testPushCapacityLimit(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx = { @@ -899,7 +930,10 @@ static void testPushOOM(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx = { @@ -935,7 +969,10 @@ static void testPushAnonymousMultiFrame(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 0xFFFFU; // UdpardTx tx = { @@ -970,7 +1007,10 @@ static void testPushAnonymousService(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 0xFFFFU; // UdpardTx tx = { diff --git a/tests/src/test_tx.cpp b/tests/src/test_tx.cpp index 9579744..9eda8af 100644 --- a/tests/src/test_tx.cpp +++ b/tests/src/test_tx.cpp @@ -29,49 +29,55 @@ void testInit() std::monostate user_referent; const UdpardNodeID node_id = 0; { - const UdpardMemoryResource memory{ + const UdpardMemoryResource mr{ .user_reference = &user_referent, .deallocate = &dummyAllocatorDeallocate, .allocate = &dummyAllocatorAllocate, }; + const UdpardTxMemoryResources memory = {.fragment = mr, .payload = mr}; TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT, udpardTxInit(nullptr, &node_id, 0, memory)); } { UdpardTx tx{}; - const UdpardMemoryResource memory{ + const UdpardMemoryResource mr{ .user_reference = &user_referent, .deallocate = &dummyAllocatorDeallocate, .allocate = &dummyAllocatorAllocate, }; + const UdpardTxMemoryResources memory = {.fragment = mr, .payload = mr}; TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT, udpardTxInit(&tx, nullptr, 0, memory)); } { UdpardTx tx{}; - const UdpardMemoryResource memory{ + const UdpardMemoryResource mr{ .user_reference = &user_referent, .deallocate = &dummyAllocatorDeallocate, .allocate = nullptr, }; + const UdpardTxMemoryResources memory = {.fragment = mr, .payload = mr}; TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT, udpardTxInit(&tx, &node_id, 0, memory)); } { UdpardTx tx{}; - const UdpardMemoryResource memory{ + const UdpardMemoryResource mr{ .user_reference = &user_referent, .deallocate = nullptr, .allocate = &dummyAllocatorAllocate, }; + const UdpardTxMemoryResources memory = {.fragment = mr, .payload = mr}; TEST_ASSERT_EQUAL(-UDPARD_ERROR_ARGUMENT, udpardTxInit(&tx, &node_id, 0, memory)); } { UdpardTx tx{}; - const UdpardMemoryResource memory{ + const UdpardMemoryResource mr{ .user_reference = &user_referent, .deallocate = &dummyAllocatorDeallocate, .allocate = &dummyAllocatorAllocate, }; + const UdpardTxMemoryResources memory = {.fragment = mr, .payload = mr}; TEST_ASSERT_EQUAL(0, udpardTxInit(&tx, &node_id, 0, memory)); - TEST_ASSERT_EQUAL(&user_referent, tx.memory.user_reference); + TEST_ASSERT_EQUAL(&user_referent, tx.memory.fragment.user_reference); + TEST_ASSERT_EQUAL(&user_referent, tx.memory.payload.user_reference); TEST_ASSERT_EQUAL(UDPARD_MTU_DEFAULT, tx.mtu); } } @@ -80,7 +86,10 @@ void testPublish() { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - const struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx{ @@ -102,7 +111,7 @@ void testPublish() transfer_id++, {.size = FleetingEvents.size(), .data = FleetingEvents.data()}, &user_transfer_referent)); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); const auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; @@ -212,7 +221,10 @@ void testRequest() { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - const UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx{ @@ -235,7 +247,7 @@ void testRequest() transfer_id++, {.size = FleetingEvents.size(), .data = FleetingEvents.data()}, &user_transfer_referent)); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); const auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; @@ -362,7 +374,10 @@ void testRespond() { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - const UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); + const UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; const UdpardNodeID node_id = 1234; // UdpardTx tx{ @@ -384,7 +399,7 @@ void testRespond() 9876543210, {.size = FleetingEvents.size(), .data = FleetingEvents.data()}, &user_transfer_referent)); - TEST_ASSERT_EQUAL(1, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); const auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; From d9adeef5835fa69b990c844645e156cb49fe858c Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 27 Nov 2024 17:20:55 +0200 Subject: [PATCH 02/10] fix formatting --- libudpard/udpard.c | 18 ++--- tests/src/test_intrusive_tx.c | 140 +++++++++++++++++----------------- 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/libudpard/udpard.c b/libudpard/udpard.c index c77b018..ce2c132 100644 --- a/libudpard/udpard.c +++ b/libudpard/udpard.c @@ -404,13 +404,13 @@ static inline byte_t* txSerializeHeader(byte_t* const destination_buffe /// Produces a chain of Tx queue items for later insertion into the Tx queue. The tail is NULL if OOM. /// The caller is responsible for freeing the memory allocated for the chain. static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, - const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], - const size_t mtu, - const UdpardMicrosecond deadline_usec, - const TransferMetadata meta, - const struct UdpardUDPIPEndpoint endpoint, - const struct UdpardPayload payload, - void* const user_transfer_reference) + const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], + const size_t mtu, + const UdpardMicrosecond deadline_usec, + const TransferMetadata meta, + const struct UdpardUDPIPEndpoint endpoint, + const struct UdpardPayload payload, + void* const user_transfer_reference) { UDPARD_ASSERT(mtu > 0); UDPARD_ASSERT((payload.data != NULL) || (payload.size == 0U)); @@ -443,9 +443,9 @@ static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, { break; } - const bool last = (payload_size_with_crc - offset) <= mtu; + const bool last = (payload_size_with_crc - offset) <= mtu; byte_t* const dst_buffer = item->base.datagram_payload.data; - byte_t* write_ptr = txSerializeHeader(dst_buffer, meta, (uint32_t) out.count, last); + byte_t* write_ptr = txSerializeHeader(dst_buffer, meta, (uint32_t) out.count, last); if (offset < payload.size) { const size_t progress = smaller(payload.size - offset, mtu); diff --git a/tests/src/test_intrusive_tx.c b/tests/src/test_intrusive_tx.c index 05ef2c5..7aee571 100644 --- a/tests/src/test_intrusive_tx.c +++ b/tests/src/test_intrusive_tx.c @@ -96,15 +96,15 @@ static void testMakeChainEmpty(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPriorityFast, - .src_node_id = 1234, - .dst_node_id = 2345, - .data_specifier = 5432, - .transfer_id = 0xBADC0FFEE0DDF00DULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPriorityFast, + .src_node_id = 1234, + .dst_node_id = 2345, + .data_specifier = 5432, + .transfer_id = 0xBADC0FFEE0DDF00DULL, }; const TxChain chain = txMakeChain(mem, (byte_t[]){11, 22, 33, 44, 55, 66, 77, 88}, @@ -143,15 +143,15 @@ static void testMakeChainSingleMaxMTU(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPrioritySlow, - .src_node_id = 4321, - .dst_node_id = 5432, - .data_specifier = 7766, - .transfer_id = 0x0123456789ABCDEFULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPrioritySlow, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, }; const TxChain chain = txMakeChain(mem, (byte_t[]){11, 22, 33, 44, 55, 66, 77, 88}, @@ -197,9 +197,9 @@ static void testMakeChainSingleFrameDefaultMTU(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const byte_t payload[UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1] = {0}; + const byte_t payload[UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1] = {0}; { // Ensure UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME bytes fit in a single frame with the default MTU. const TxChain chain = txMakeChain(mem, (byte_t[]){11, 22, 33, 44, 55, 66, 77, 88}, @@ -257,15 +257,15 @@ static void testMakeChainThreeFrames(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPriorityNominal, - .src_node_id = 4321, - .dst_node_id = 5432, - .data_specifier = 7766, - .transfer_id = 0x0123456789ABCDEFULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPriorityNominal, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, }; const size_t mtu = (EtherealStrengthSize + 4U + 3U) / 3U; // Force payload split into three frames. const TxChain chain = txMakeChain(mem, @@ -342,15 +342,15 @@ static void testMakeChainCRCSpill1(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPriorityNominal, - .src_node_id = 4321, - .dst_node_id = 5432, - .data_specifier = 7766, - .transfer_id = 0x0123456789ABCDEFULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPriorityNominal, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, }; const size_t mtu = InterstellarWarSize + 3U; const TxChain chain = txMakeChain(mem, @@ -417,15 +417,15 @@ static void testMakeChainCRCSpill2(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPriorityNominal, - .src_node_id = 4321, - .dst_node_id = 5432, - .data_specifier = 7766, - .transfer_id = 0x0123456789ABCDEFULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPriorityNominal, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, }; const size_t mtu = InterstellarWarSize + 2U; const TxChain chain = txMakeChain(mem, @@ -492,15 +492,15 @@ static void testMakeChainCRCSpill3(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPriorityNominal, - .src_node_id = 4321, - .dst_node_id = 5432, - .data_specifier = 7766, - .transfer_id = 0x0123456789ABCDEFULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPriorityNominal, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, }; const size_t mtu = InterstellarWarSize + 1U; const TxChain chain = txMakeChain(mem, @@ -567,15 +567,15 @@ static void testMakeChainCRCSpillFull(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - char user_transfer_referent = '\0'; - const TransferMetadata meta = { - .priority = UdpardPriorityNominal, - .src_node_id = 4321, - .dst_node_id = 5432, - .data_specifier = 7766, - .transfer_id = 0x0123456789ABCDEFULL, + char user_transfer_referent = '\0'; + const TransferMetadata meta = { + .priority = UdpardPriorityNominal, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, }; const size_t mtu = InterstellarWarSize; const TxChain chain = txMakeChain(mem, @@ -638,9 +638,9 @@ static void testPushPeekPopFree(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -720,9 +720,9 @@ static void testPushPrioritization(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -894,9 +894,9 @@ static void testPushCapacityLimit(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -932,9 +932,9 @@ static void testPushOOM(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -971,9 +971,9 @@ static void testPushAnonymousMultiFrame(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 0xFFFFU; + const UdpardNodeID node_id = 0xFFFFU; // UdpardTx tx = { .local_node_id = &node_id, @@ -1009,9 +1009,9 @@ static void testPushAnonymousService(void) instrumentedAllocatorNew(&alloc); const struct UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 0xFFFFU; + const UdpardNodeID node_id = 0xFFFFU; // UdpardTx tx = { .local_node_id = &node_id, From 36a5aecb80658d2b41c2100a0b7e7bc1b26ac683 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 27 Nov 2024 17:24:26 +0200 Subject: [PATCH 03/10] fix formatting --- tests/src/test_e2e.cpp | 4 ++-- tests/src/test_tx.cpp | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/src/test_e2e.cpp b/tests/src/test_e2e.cpp index b7de0ee..a90fa3d 100644 --- a/tests/src/test_e2e.cpp +++ b/tests/src/test_e2e.cpp @@ -75,7 +75,7 @@ void testPubSub() instrumentedAllocatorNew(&alloc_rx_payload); const UdpardTxMemoryResources mem_tx{ .fragment = instrumentedAllocatorMakeMemoryResource(&alloc_tx), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc_tx), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc_tx), }; const UdpardRxMemoryResources mem_rx{ .session = instrumentedAllocatorMakeMemoryResource(&alloc_rx_session), @@ -415,7 +415,7 @@ void testRPC() instrumentedAllocatorNew(&alloc_rx_payload); const UdpardTxMemoryResources mem_tx{ .fragment = instrumentedAllocatorMakeMemoryResource(&alloc_tx), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc_tx), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc_tx), }; const UdpardRxMemoryResources mem_rx{ .session = instrumentedAllocatorMakeMemoryResource(&alloc_rx_session), diff --git a/tests/src/test_tx.cpp b/tests/src/test_tx.cpp index 9eda8af..7ca4c10 100644 --- a/tests/src/test_tx.cpp +++ b/tests/src/test_tx.cpp @@ -88,9 +88,9 @@ void testPublish() instrumentedAllocatorNew(&alloc); const UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx{ .local_node_id = &node_id, @@ -223,9 +223,9 @@ void testRequest() instrumentedAllocatorNew(&alloc); const UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx{ .local_node_id = &node_id, @@ -376,9 +376,9 @@ void testRespond() instrumentedAllocatorNew(&alloc); const UdpardTxMemoryResources mem = { .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), - .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), }; - const UdpardNodeID node_id = 1234; + const UdpardNodeID node_id = 1234; // UdpardTx tx{ .local_node_id = &node_id, @@ -399,7 +399,7 @@ void testRespond() 9876543210, {.size = FleetingEvents.size(), .data = FleetingEvents.data()}, &user_transfer_referent)); - TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); const auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; From e8469fd02a2aabfededff93e944571f583279264 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 27 Nov 2024 17:49:56 +0200 Subject: [PATCH 04/10] Eliminate private TxItem --- libudpard/udpard.c | 89 +++++------ libudpard/udpard.h | 3 + tests/src/test_intrusive_tx.c | 286 +++++++++++++++------------------- 3 files changed, 171 insertions(+), 207 deletions(-) diff --git a/libudpard/udpard.c b/libudpard/udpard.c index ce2c132..80e0bbb 100644 --- a/libudpard/udpard.c +++ b/libudpard/udpard.c @@ -272,23 +272,12 @@ static inline uint32_t transferCRCCompute(const size_t size, const void* const d // ================================================= TX PIPELINE ================================================= // ===================================================================================================================== -/// This is a subclass of UdpardTxItem. A pointer to this type can be cast to UdpardTxItem safely. -/// This is compliant with the C99 standard; paragraph 6.7.2.1.15 says: -/// A pointer to a structure object, suitably converted, points to its initial member (or if that member is a -/// bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a -/// structure object, but not at its beginning. -typedef struct -{ - struct UdpardTxItem base; - enum UdpardPriority priority; ///< Do we need this exposed in the public structure? We already have DSCP there. -} TxItem; - /// Chain of TX frames prepared for insertion into a TX queue. typedef struct { - TxItem* head; - TxItem* tail; - size_t count; + struct UdpardTxItem* head; + struct UdpardTxItem* tail; + size_t count; } TxChain; static inline bool txValidateMemoryResources(const struct UdpardTxMemoryResources memory) @@ -297,40 +286,40 @@ static inline bool txValidateMemoryResources(const struct UdpardTxMemoryResource (memory.payload.allocate != NULL) && (memory.payload.deallocate != NULL); } -static inline TxItem* txNewItem(const struct UdpardTxMemoryResources memory, - const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], - const UdpardMicrosecond deadline_usec, - const enum UdpardPriority priority, - const struct UdpardUDPIPEndpoint endpoint, - const size_t datagram_payload_size, - void* const user_transfer_reference) +static inline struct UdpardTxItem* txNewItem(const struct UdpardTxMemoryResources memory, + const uint_least8_t dscp_value_per_priority[UDPARD_PRIORITY_MAX + 1U], + const UdpardMicrosecond deadline_usec, + const enum UdpardPriority priority, + const struct UdpardUDPIPEndpoint endpoint, + const size_t datagram_payload_size, + void* const user_transfer_reference) { - TxItem* out = memAlloc(memory.fragment, sizeof(TxItem)); + struct UdpardTxItem* out = memAlloc(memory.fragment, sizeof(struct UdpardTxItem)); if (out != NULL) { // No tree linkage by default. - out->base.base.up = NULL; - out->base.base.lr[0] = NULL; - out->base.base.lr[1] = NULL; - out->base.base.bf = 0; + out->base.up = NULL; + out->base.lr[0] = NULL; + out->base.lr[1] = NULL; + out->base.bf = 0; // Init metadata. - out->priority = priority; - out->base.next_in_transfer = NULL; // Last by default. - out->base.deadline_usec = deadline_usec; + out->priority = priority; + out->next_in_transfer = NULL; // Last by default. + out->deadline_usec = deadline_usec; UDPARD_ASSERT(priority <= UDPARD_PRIORITY_MAX); - out->base.dscp = dscp_value_per_priority[priority]; - out->base.destination = endpoint; - out->base.user_transfer_reference = user_transfer_reference; + out->dscp = dscp_value_per_priority[priority]; + out->destination = endpoint; + out->user_transfer_reference = user_transfer_reference; void* const payload_data = memAlloc(memory.payload, datagram_payload_size); if (NULL != payload_data) { - out->base.datagram_payload.data = payload_data; - out->base.datagram_payload.size = datagram_payload_size; + out->datagram_payload.data = payload_data; + out->datagram_payload.size = datagram_payload_size; } else { - memFree(memory.fragment, sizeof(TxItem), out); + memFree(memory.fragment, sizeof(struct UdpardTxItem), out); out = NULL; } } @@ -342,8 +331,8 @@ static inline TxItem* txNewItem(const struct UdpardTxMemoryResources memory, static inline int_fast8_t txAVLPredicate(void* const user_reference, // NOSONAR Cavl API requires pointer to non-const. const struct UdpardTreeNode* const node) { - const TxItem* const target = (const TxItem*) user_reference; - const TxItem* const other = (const TxItem*) (const void*) node; + const struct UdpardTxItem* const target = (const struct UdpardTxItem*) user_reference; + const struct UdpardTxItem* const other = (const struct UdpardTxItem*) (const void*) node; UDPARD_ASSERT((target != NULL) && (other != NULL)); return (target->priority >= other->priority) ? +1 : -1; } @@ -421,13 +410,13 @@ static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, size_t offset = 0U; while (offset < payload_size_with_crc) { - TxItem* const item = txNewItem(memory, - dscp_value_per_priority, - deadline_usec, - meta.priority, - endpoint, - smaller(payload_size_with_crc - offset, mtu) + HEADER_SIZE_BYTES, - user_transfer_reference); + struct UdpardTxItem* const item = txNewItem(memory, + dscp_value_per_priority, + deadline_usec, + meta.priority, + endpoint, + smaller(payload_size_with_crc - offset, mtu) + HEADER_SIZE_BYTES, + user_transfer_reference); if (NULL == out.head) { out.head = item; @@ -436,7 +425,7 @@ static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, { // C std, 6.7.2.1.15: A pointer to a structure object <...> points to its initial member, and vice versa. // Can't just read tqi->base because tqi may be NULL; https://github.com/OpenCyphal/libcanard/issues/203. - out.tail->base.next_in_transfer = (struct UdpardTxItem*) item; + out.tail->next_in_transfer = item; } out.tail = item; if (NULL == out.tail) @@ -444,7 +433,7 @@ static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, break; } const bool last = (payload_size_with_crc - offset) <= mtu; - byte_t* const dst_buffer = item->base.datagram_payload.data; + byte_t* const dst_buffer = item->datagram_payload.data; byte_t* write_ptr = txSerializeHeader(dst_buffer, meta, (uint32_t) out.count, last); if (offset < payload.size) { @@ -460,7 +449,7 @@ static inline TxChain txMakeChain(const struct UdpardTxMemoryResources memory, { const size_t crc_offset = offset - payload.size; UDPARD_ASSERT(crc_offset < TRANSFER_CRC_SIZE_BYTES); - const size_t available = item->base.datagram_payload.size - (size_t) (write_ptr - dst_buffer); + const size_t available = item->datagram_payload.size - (size_t) (write_ptr - dst_buffer); UDPARD_ASSERT(available <= TRANSFER_CRC_SIZE_BYTES); const size_t write_size = smaller(TRANSFER_CRC_SIZE_BYTES - crc_offset, available); // NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) @@ -509,7 +498,7 @@ static inline int32_t txPush(struct UdpardTx* const tx, if (chain.tail != NULL) { UDPARD_ASSERT(frame_count == chain.count); - struct UdpardTxItem* next = &chain.head->base; + struct UdpardTxItem* next = chain.head; do { const struct UdpardTreeNode* const res = @@ -527,7 +516,7 @@ static inline int32_t txPush(struct UdpardTx* const tx, else // The queue is large enough but we ran out of heap memory, so we have to unwind the chain. { out = -UDPARD_ERROR_MEMORY; - struct UdpardTxItem* head = &chain.head->base; + struct UdpardTxItem* head = chain.head; while (head != NULL) { struct UdpardTxItem* const next = head->next_in_transfer; @@ -694,7 +683,7 @@ void udpardTxFree(const struct UdpardTxMemoryResources memory, struct UdpardTxIt memFree(memory.payload, item->datagram_payload.size, item->datagram_payload.data); } - memFree(memory.fragment, sizeof(TxItem), item); + memFree(memory.fragment, sizeof(struct UdpardTxItem), item); } } diff --git a/libudpard/udpard.h b/libudpard/udpard.h index 3223d32..88fcde9 100644 --- a/libudpard/udpard.h +++ b/libudpard/udpard.h @@ -482,6 +482,9 @@ struct UdpardTxItem /// LibUDPard selects the DSCP value based on the transfer priority level and the configured DSCP mapping. uint_least8_t dscp; + /// Holds original transfer priority level (before DSCP mapping, see above `dscp`). + enum UdpardPriority priority; + /// This UDP/IP datagram compiled by libudpard should be sent to this endpoint. /// The endpoint is always at a multicast address. struct UdpardUDPIPEndpoint destination; diff --git a/tests/src/test_intrusive_tx.c b/tests/src/test_intrusive_tx.c index 7aee571..ec78f07 100644 --- a/tests/src/test_intrusive_tx.c +++ b/tests/src/test_intrusive_tx.c @@ -115,25 +115,22 @@ static void testMakeChainEmpty(void) (UdpardPayload){.size = 0, .data = ""}, &user_transfer_referent); TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(sizeof(TxItem) + HEADER_SIZE_BYTES + 4, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES + 4, alloc.allocated_bytes); TEST_ASSERT_EQUAL(1, chain.count); TEST_ASSERT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL(NULL, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(1234567890, chain.head->base.deadline_usec); - TEST_ASSERT_EQUAL(33, chain.head->base.dscp); - TEST_ASSERT_EQUAL(0x0A0B0C0DU, chain.head->base.destination.ip_address); - TEST_ASSERT_EQUAL(0x1234, chain.head->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 4, chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 0, true).data, - chain.head->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(NULL, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(1234567890, chain.head->deadline_usec); + TEST_ASSERT_EQUAL(33, chain.head->dscp); + TEST_ASSERT_EQUAL(0x0A0B0C0DU, chain.head->destination.ip_address); + TEST_ASSERT_EQUAL(0x1234, chain.head->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 4, chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, true).data, chain.head->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp("\x00\x00\x00\x00", // CRC of the empty transfer. - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES, 4)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); - udpardTxFree(mem, &chain.head->base); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); + udpardTxFree(mem, chain.head); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -162,32 +159,28 @@ static void testMakeChainSingleMaxMTU(void) (UdpardPayload){.size = DetailOfTheCosmosSize, .data = DetailOfTheCosmos}, &user_transfer_referent); TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(sizeof(TxItem) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, + TEST_ASSERT_EQUAL(sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, alloc.allocated_bytes); TEST_ASSERT_EQUAL(1, chain.count); TEST_ASSERT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL(NULL, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(1234567890, chain.head->base.deadline_usec); - TEST_ASSERT_EQUAL(77, chain.head->base.dscp); - TEST_ASSERT_EQUAL(0x0A0B0C00U, chain.head->base.destination.ip_address); - TEST_ASSERT_EQUAL(7474, chain.head->base.destination.udp_port); + TEST_ASSERT_EQUAL(NULL, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(1234567890, chain.head->deadline_usec); + TEST_ASSERT_EQUAL(77, chain.head->dscp); + TEST_ASSERT_EQUAL(0x0A0B0C00U, chain.head->destination.ip_address); + TEST_ASSERT_EQUAL(7474, chain.head->destination.udp_port); TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, - chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 0, true).data, - chain.head->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, true).data, chain.head->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(DetailOfTheCosmos, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES, DetailOfTheCosmosSize)); TEST_ASSERT_EQUAL(0, memcmp(DetailOfTheCosmosCRC, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES + - DetailOfTheCosmosSize, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize, TRANSFER_CRC_SIZE_BYTES)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); - udpardTxFree(mem, &chain.head->base); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); + udpardTxFree(mem, chain.head); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -214,13 +207,13 @@ static void testMakeChainSingleFrameDefaultMTU(void) (UdpardPayload){.size = UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME, .data = payload}, NULL); TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + + TEST_ASSERT_EQUAL(sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + TRANSFER_CRC_SIZE_BYTES, alloc.allocated_bytes); TEST_ASSERT_EQUAL(1, chain.count); TEST_ASSERT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL(NULL, chain.head->base.next_in_transfer); - udpardTxFree(mem, &chain.head->base); + TEST_ASSERT_EQUAL(NULL, chain.head->next_in_transfer); + udpardTxFree(mem, chain.head); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } { // Increase the payload by 1 byte and ensure it spills over. @@ -238,15 +231,15 @@ static void testMakeChainSingleFrameDefaultMTU(void) (UdpardPayload){.size = UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1, .data = payload}, NULL); TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL((sizeof(TxItem) + HEADER_SIZE_BYTES) * 2 + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1 + - TRANSFER_CRC_SIZE_BYTES, + TEST_ASSERT_EQUAL((sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) * 2 + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + + 1 + TRANSFER_CRC_SIZE_BYTES, alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(NULL, chain.tail->base.next_in_transfer); - udpardTxFree(mem, &chain.head->base); - udpardTxFree(mem, &chain.tail->base); + TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(NULL, chain.tail->next_in_transfer); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } } @@ -277,9 +270,10 @@ static void testMakeChainThreeFrames(void) (UdpardPayload){.size = EtherealStrengthSize, .data = EtherealStrength}, &user_transfer_referent); TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(3 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(3 * (sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, + alloc.allocated_bytes); TEST_ASSERT_EQUAL(3, chain.count); - UdpardTxItem* const first = &chain.head->base; + UdpardTxItem* const first = chain.head; TEST_ASSERT_NOT_EQUAL(NULL, first); UdpardTxItem* const second = first->next_in_transfer; TEST_ASSERT_NOT_EQUAL(NULL, second); @@ -362,52 +356,46 @@ static void testMakeChainCRCSpill1(void) (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * (sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, + alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(NULL, chain.tail->base.next_in_transfer); + TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(NULL, chain.tail->next_in_transfer); // FIRST FRAME -- contains the payload and the first three bytes of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.head->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.head->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.head->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 0, false).data, - chain.head->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.head->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.head->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.head->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, false).data, chain.head->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWar, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES, InterstellarWarSize)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES + - InterstellarWarSize, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES + InterstellarWarSize, 3U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); // SECOND FRAME -- contains the last byte of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.tail->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.tail->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.tail->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 1U, chain.tail->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 1, true).data, - chain.tail->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.tail->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.tail->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.tail->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 1U, chain.tail->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 1, true).data, chain.tail->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC + 3U, - (byte_t*) (chain.tail->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.tail->datagram_payload.data) + HEADER_SIZE_BYTES, 1U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->user_transfer_reference); // Clean up. - udpardTxFree(mem, &chain.head->base); - udpardTxFree(mem, &chain.tail->base); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -437,52 +425,46 @@ static void testMakeChainCRCSpill2(void) (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * (sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, + alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(NULL, chain.tail->base.next_in_transfer); + TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(NULL, chain.tail->next_in_transfer); // FIRST FRAME -- contains the payload and the first two bytes of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.head->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.head->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.head->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 0, false).data, - chain.head->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.head->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.head->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.head->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, false).data, chain.head->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWar, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES, InterstellarWarSize)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES + - InterstellarWarSize, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES + InterstellarWarSize, 2U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); // SECOND FRAME -- contains the last two bytes of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.tail->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.tail->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.tail->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 2U, chain.tail->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 1, true).data, - chain.tail->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.tail->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.tail->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.tail->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 2U, chain.tail->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 1, true).data, chain.tail->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC + 2U, - (byte_t*) (chain.tail->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.tail->datagram_payload.data) + HEADER_SIZE_BYTES, 2U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->user_transfer_reference); // Clean up. - udpardTxFree(mem, &chain.head->base); - udpardTxFree(mem, &chain.tail->base); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -512,52 +494,46 @@ static void testMakeChainCRCSpill3(void) (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * (sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, + alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(NULL, chain.tail->base.next_in_transfer); + TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(NULL, chain.tail->next_in_transfer); // FIRST FRAME -- contains the payload and the first byte of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.head->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.head->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.head->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 0, false).data, - chain.head->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.head->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.head->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.head->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, false).data, chain.head->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWar, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES, InterstellarWarSize)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES + - InterstellarWarSize, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES + InterstellarWarSize, 1U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); // SECOND FRAME -- contains the last three bytes of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.tail->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.tail->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.tail->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 3U, chain.tail->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 1, true).data, - chain.tail->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.tail->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.tail->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.tail->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 3U, chain.tail->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 1, true).data, chain.tail->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC + 1U, - (byte_t*) (chain.tail->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.tail->datagram_payload.data) + HEADER_SIZE_BYTES, 3U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->user_transfer_reference); // Clean up. - udpardTxFree(mem, &chain.head->base); - udpardTxFree(mem, &chain.tail->base); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -587,48 +563,43 @@ static void testMakeChainCRCSpillFull(void) (UdpardPayload){.size = InterstellarWarSize, .data = InterstellarWar}, &user_transfer_referent); TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(2 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * (sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, + alloc.allocated_bytes); TEST_ASSERT_EQUAL(2, chain.count); TEST_ASSERT_NOT_EQUAL(chain.head, chain.tail); - TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->base.next_in_transfer); - TEST_ASSERT_EQUAL(NULL, chain.tail->base.next_in_transfer); + TEST_ASSERT_EQUAL((UdpardTxItem*) chain.tail, chain.head->next_in_transfer); + TEST_ASSERT_EQUAL(NULL, chain.tail->next_in_transfer); // FIRST FRAME -- contains the payload only. - TEST_ASSERT_EQUAL(223574680, chain.head->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.head->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.head->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + InterstellarWarSize, chain.head->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 0, false).data, - chain.head->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.head->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.head->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.head->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.head->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + mtu, chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + InterstellarWarSize, chain.head->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 0, false).data, chain.head->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWar, - (byte_t*) (chain.head->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.head->datagram_payload.data) + HEADER_SIZE_BYTES, InterstellarWarSize)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); // SECOND FRAME -- contains the last byte of the CRC. - TEST_ASSERT_EQUAL(223574680, chain.tail->base.deadline_usec); - TEST_ASSERT_EQUAL(55, chain.tail->base.dscp); - TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->base.destination.ip_address); - TEST_ASSERT_EQUAL(0xD0ED, chain.tail->base.destination.udp_port); - TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 4U, chain.tail->base.datagram_payload.size); - TEST_ASSERT_EQUAL(0, - memcmp(makeHeader(meta, 1, true).data, - chain.tail->base.datagram_payload.data, - HEADER_SIZE_BYTES)); + TEST_ASSERT_EQUAL(223574680, chain.tail->deadline_usec); + TEST_ASSERT_EQUAL(55, chain.tail->dscp); + TEST_ASSERT_EQUAL(0xBABADEDAU, chain.tail->destination.ip_address); + TEST_ASSERT_EQUAL(0xD0ED, chain.tail->destination.udp_port); + TEST_ASSERT_EQUAL(HEADER_SIZE_BYTES + 4U, chain.tail->datagram_payload.size); + TEST_ASSERT_EQUAL(0, memcmp(makeHeader(meta, 1, true).data, chain.tail->datagram_payload.data, HEADER_SIZE_BYTES)); TEST_ASSERT_EQUAL(0, memcmp(InterstellarWarCRC, - (byte_t*) (chain.tail->base.datagram_payload.data) + HEADER_SIZE_BYTES, + (byte_t*) (chain.tail->datagram_payload.data) + HEADER_SIZE_BYTES, 4U)); - TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->base.user_transfer_reference); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.tail->user_transfer_reference); // Clean up. - udpardTxFree(mem, &chain.head->base); - udpardTxFree(mem, &chain.tail->base); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -667,7 +638,8 @@ static void testPushPeekPopFree(void) (UdpardPayload){.size = EtherealStrengthSize, .data = EtherealStrength}, &user_transfer_referent)); TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); - TEST_ASSERT_EQUAL(3 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(3 * (sizeof(struct UdpardTxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, + alloc.allocated_bytes); TEST_ASSERT_EQUAL(3, tx.queue_size); const UdpardTxItem* frame = udpardTxPeek(&tx); From 025da745dfd7278ae4d732ad0981989abeab6e3b Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 27 Nov 2024 17:55:59 +0200 Subject: [PATCH 05/10] added readme todo #sonar --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6892fa4..c61e713 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,9 @@ For complete usage examples, please refer to Date: Wed, 27 Nov 2024 18:00:33 +0200 Subject: [PATCH 06/10] Try to fix Sonar warning: Property 'sonar.cfamily.build-wrapper-output' is deprecated; build-wrapper now generates a compilation database. #sonar --- tools/run_sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/run_sonar.sh b/tools/run_sonar.sh index c0440d1..e4d2ae3 100755 --- a/tools/run_sonar.sh +++ b/tools/run_sonar.sh @@ -69,7 +69,7 @@ sonar-scanner \ --define sonar.projectKey=libudpard \ --define sonar.sources=libudpard \ --define sonar.exclusions=libudpard/_udpard_cavl.h \ ---define sonar.cfamily.build-wrapper-output="$BUILD_DIR" \ +--define sonar.cfamily.compile-commands="$BUILD_DIR/compile_commands.json" --define sonar.cfamily.llvm-cov.reportPath="$BUILD_DIR/coverage.txt" \ --define sonar.cfamily.threads="$(nproc)" \ || die From 790163849a1924b8a540cd0a9541b9cd4e9e3211 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Wed, 27 Nov 2024 18:04:58 +0200 Subject: [PATCH 07/10] fix sonar build #sonar --- tools/run_sonar.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/run_sonar.sh b/tools/run_sonar.sh index e4d2ae3..0fcba11 100755 --- a/tools/run_sonar.sh +++ b/tools/run_sonar.sh @@ -69,7 +69,7 @@ sonar-scanner \ --define sonar.projectKey=libudpard \ --define sonar.sources=libudpard \ --define sonar.exclusions=libudpard/_udpard_cavl.h \ ---define sonar.cfamily.compile-commands="$BUILD_DIR/compile_commands.json" +--define sonar.cfamily.compile-commands="$BUILD_DIR/compile_commands.json" \ --define sonar.cfamily.llvm-cov.reportPath="$BUILD_DIR/coverage.txt" \ --define sonar.cfamily.threads="$(nproc)" \ || die From 01b14e77c484a4cde3b82d1784bb525cdc217302 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Thu, 28 Nov 2024 12:54:50 +0200 Subject: [PATCH 08/10] more docs and unit tests #sonar --- libudpard/udpard.h | 34 +++++++++++++++++++++-------- tests/src/test_intrusive_tx.c | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/libudpard/udpard.h b/libudpard/udpard.h index 88fcde9..20d5f18 100644 --- a/libudpard/udpard.h +++ b/libudpard/udpard.h @@ -174,6 +174,7 @@ /// Typically, if block pool allocators are used, the following block sizes should be served: /// /// - (MTU+library overhead) blocks for the TX and RX pipelines (usually less than 2048 bytes); +/// - TX fragment item sized blocks for the TX pipeline (less than 128 bytes). /// - RX session object sized blocks for the RX pipeline (less than 512 bytes); /// - RX fragment handle sized blocks for the RX pipeline (less than 128 bytes). /// @@ -373,11 +374,11 @@ struct UdpardMemoryResource // ================================================= TX PIPELINE ================================================= // ===================================================================================================================== -/// The set of memory resources is used per an TX pipeline instance. +/// The set of memory resources is used per a TX pipeline instance. /// These are used to serve the memory needs of the library to keep state while assembling outgoing frames. /// Several memory resources are provided to enable fine control over the allocated memory. /// -/// This TX queue uses these memory resources for allocating the enqueued items (UDP datagrams). +/// A TX queue uses these memory resources for allocating the enqueued items (UDP datagrams). /// There are exactly two allocations per enqueued item: /// - the first for bookkeeping purposes (UdpardTxItem) /// - second for payload storage (the frame data) @@ -401,8 +402,6 @@ struct UdpardTxMemoryResources /// Applications that are not interested in transmission may have zero such instances. /// /// All operations are logarithmic in complexity on the number of enqueued items. -/// There is exactly one memory allocation per element; -/// the size of each allocation is sizeof(UdpardTxItem) plus the size of the datagram. /// /// Once initialized, instances cannot be copied. /// @@ -461,7 +460,9 @@ struct UdpardTx /// One transport frame (UDP datagram) stored in the UdpardTx transmission queue along with its metadata. /// The datagram should be sent to the indicated UDP/IP endpoint with the specified DSCP value. /// The datagram should be discarded (transmission aborted) if the deadline has expired. -/// All fields are READ-ONLY. +/// All fields are READ-ONLY except mutable payload `datagram_payload` field, which could be nullified to indicate +/// a transfer of the payload memory ownership to somewhere else. +/// struct UdpardTxItem { /// Internal use only; do not access this field. @@ -568,10 +569,13 @@ int_fast8_t udpardTxInit(struct UdpardTx* const self, /// In such cases, all frames allocated for this transfer (if any) will be deallocated automatically. /// In other words, either all frames of the transfer are enqueued successfully, or none are. /// -/// The memory allocation requirement is one allocation per datagram: -/// a single-frame transfer takes one allocation; a multi-frame transfer of N frames takes N allocations. -/// The size of each allocation is (sizeof(UdpardTxItem) + MTU) except for the last datagram where the payload may be -/// smaller than the MTU. +/// The memory allocation requirement is two allocations per datagram: +/// a single-frame transfer takes two allocations; a multi-frame transfer of N frames takes N*2 allocations. +/// In each pair of allocations: +/// - the first allocation is for `UdpardTxItem`; the size is `sizeof(UdpardTxItem)`; +/// the TX queue `memory.fragment` memory resource is used for this allocation (and later for deallocation); +/// - the second allocation is for payload storage (the frame data) - size is normally MTU but could be less for +/// the last frame of the transfer; the TX queue `memory.payload` memory resource is used for this allocation. /// /// The time complexity is O(p + log e), where p is the amount of payload in the transfer, and e is the number of /// frames already enqueued in the transmission queue. @@ -641,6 +645,18 @@ int32_t udpardTxRespond(struct UdpardTx* const self, /// /// Calling functions that modify the queue may cause the next invocation to return a different pointer. /// +/// The payload buffer is allocated in the dynamic storage of the queue. The application may transfer ownership of +/// the payload to a different application component (f.e. to transmission media) by copying the pointer and then +/// (if the ownership transfer was accepted) by nullifying `datagram_payload` fields of the frame (`size` & `data`). +/// If these fields stay with their original values, the `udpardTxFree` (after proper `udpardTxPop` of course) will +/// deallocate the payload buffer. In any case, the payload has to be eventually deallocated by using the TX queue +/// `memory.payload` memory resource. It will be automatically done by the `udpardTxFree` (if the payload still +/// stays in the item), OR if moved, it is the responsibility of the application to eventually (f.e. at the end of +/// transmission) deallocate the memory with the TX queue `memory.payload` memory resource. +/// Note that the mentioned above nullification of the `datagram_payload` fields is the +/// only reason why a returned TX item pointer is mutable. It was constant in the past (before v2), +/// but it was changed to be mutable to allow the payload ownership transfer. +/// /// The time complexity is logarithmic of the queue size. This function does not invoke the dynamic memory manager. struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self); diff --git a/tests/src/test_intrusive_tx.c b/tests/src/test_intrusive_tx.c index ec78f07..2618aa5 100644 --- a/tests/src/test_intrusive_tx.c +++ b/tests/src/test_intrusive_tx.c @@ -937,6 +937,46 @@ static void testPushOOM(void) TEST_ASSERT_EQUAL(0, tx.queue_size); } +static void testPushPayloadOOM(void) +{ + InstrumentedAllocator alloc; + instrumentedAllocatorNew(&alloc); + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; + // + UdpardTx tx = { + .local_node_id = &node_id, + .queue_capacity = 10000U, + .mtu = EtherealStrengthSize + HEADER_CRC_SIZE_BYTES, + .dscp_value_per_priority = {0, 1, 2, 3, 4, 5, 6, 7}, + .memory = mem, + .queue_size = 0, + .root = NULL, + }; + const TransferMetadata meta = { + .priority = UdpardPriorityNominal, + .src_node_id = 4321, + .dst_node_id = 5432, + .data_specifier = 7766, + .transfer_id = 0x0123456789ABCDEFULL, + }; + // There is memory of the item, but 1 byte short for payload. + alloc.limit_bytes = sizeof(UdpardTxItem) + (HEADER_SIZE_BYTES + EtherealStrengthSize + HEADER_CRC_SIZE_BYTES - 1); + TEST_ASSERT_EQUAL(-UDPARD_ERROR_MEMORY, + txPush(&tx, + 1234567890U, + meta, + (UdpardUDPIPEndpoint){.ip_address = 0xBABADEDAU, .udp_port = 0xD0ED}, + (UdpardPayload){.size = EtherealStrengthSize, .data = EtherealStrength}, + NULL)); + TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(0, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(0, tx.queue_size); +} + static void testPushAnonymousMultiFrame(void) { InstrumentedAllocator alloc; @@ -1033,6 +1073,7 @@ int main(void) RUN_TEST(testPushPrioritization); RUN_TEST(testPushCapacityLimit); RUN_TEST(testPushOOM); + RUN_TEST(testPushPayloadOOM); RUN_TEST(testPushAnonymousMultiFrame); RUN_TEST(testPushAnonymousService); return UNITY_END(); From 142590dce3edb85898eeefa65e704d810980edb6 Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Thu, 28 Nov 2024 15:30:05 +0200 Subject: [PATCH 09/10] udpardTxPop now accepts mutable item #sonar --- libudpard/udpard.c | 9 ++------- libudpard/udpard.h | 2 +- tests/src/test_e2e.cpp | 4 ++-- tests/src/test_intrusive_tx.c | 4 ++-- tests/src/test_tx.cpp | 6 +++--- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/libudpard/udpard.c b/libudpard/udpard.c index 80e0bbb..2f9d317 100644 --- a/libudpard/udpard.c +++ b/libudpard/udpard.c @@ -654,15 +654,10 @@ struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self) return out; } -struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, const struct UdpardTxItem* const item) +struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, struct UdpardTxItem* const item) { - struct UdpardTxItem* out = NULL; if ((self != NULL) && (item != NULL)) { - // Intentional violation of MISRA: casting away const qualifier. This is considered safe because the API - // contract dictates that the pointer shall point to a mutable entity in RAM previously allocated by the - // memory manager. It is difficult to avoid this cast in this context. - out = (struct UdpardTxItem*) item; // NOSONAR casting away const qualifier. // Paragraph 6.7.2.1.15 of the C standard says: // A pointer to a structure object, suitably converted, points to its initial member, and vice versa. // Note that the highest-priority frame is always a leaf node in the AVL tree, which means that it is very @@ -671,7 +666,7 @@ struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, const struct Udpar UDPARD_ASSERT(self->queue_size > 0U); self->queue_size--; } - return out; + return item; } void udpardTxFree(const struct UdpardTxMemoryResources memory, struct UdpardTxItem* const item) diff --git a/libudpard/udpard.h b/libudpard/udpard.h index 20d5f18..9cd22f5 100644 --- a/libudpard/udpard.h +++ b/libudpard/udpard.h @@ -669,7 +669,7 @@ struct UdpardTxItem* udpardTxPeek(const struct UdpardTx* const self); /// If any of the arguments are NULL, the function has no effect and returns NULL. /// /// The time complexity is logarithmic of the queue size. This function does not invoke the dynamic memory manager. -struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, const struct UdpardTxItem* const item); +struct UdpardTxItem* udpardTxPop(struct UdpardTx* const self, struct UdpardTxItem* const item); /// This is a simple helper that frees the memory allocated for the item and its payload, /// using the correct sizes and memory resources. diff --git a/tests/src/test_e2e.cpp b/tests/src/test_e2e.cpp index a90fa3d..8ea7800 100644 --- a/tests/src/test_e2e.cpp +++ b/tests/src/test_e2e.cpp @@ -164,7 +164,7 @@ void testPubSub() TEST_ASSERT_EQUAL(0, alloc_rx_session.allocated_fragments); TEST_ASSERT_EQUAL(0, alloc_rx_fragment.allocated_fragments); TEST_ASSERT_EQUAL(0, alloc_rx_payload.allocated_fragments); - const UdpardTxItem* tx_item = udpardTxPeek(&tx); + UdpardTxItem* tx_item = udpardTxPeek(&tx); TEST_ASSERT_NOT_NULL(tx_item); TEST_ASSERT_EQUAL(sub.at(1).udp_ip_endpoint.ip_address, tx_item->destination.ip_address); TEST_ASSERT_NULL(tx_item->next_in_transfer); @@ -478,7 +478,7 @@ void testRPC() TEST_ASSERT_EQUAL(0, alloc_rx_session.allocated_fragments); TEST_ASSERT_EQUAL(0, alloc_rx_fragment.allocated_fragments); TEST_ASSERT_EQUAL(0, alloc_rx_payload.allocated_fragments); - const UdpardTxItem* tx_item = udpardTxPeek(&tx); + UdpardTxItem* tx_item = udpardTxPeek(&tx); TEST_ASSERT_NOT_NULL(tx_item); TEST_ASSERT_EQUAL(udp_ip_endpoint.ip_address, tx_item->destination.ip_address); TEST_ASSERT_NULL(tx_item->next_in_transfer); diff --git a/tests/src/test_intrusive_tx.c b/tests/src/test_intrusive_tx.c index 2618aa5..f3ac844 100644 --- a/tests/src/test_intrusive_tx.c +++ b/tests/src/test_intrusive_tx.c @@ -642,7 +642,7 @@ static void testPushPeekPopFree(void) alloc.allocated_bytes); TEST_ASSERT_EQUAL(3, tx.queue_size); - const UdpardTxItem* frame = udpardTxPeek(&tx); + UdpardTxItem* frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); TEST_ASSERT_NOT_EQUAL(NULL, frame->next_in_transfer); TEST_ASSERT_EQUAL(1234567890U, frame->deadline_usec); @@ -722,7 +722,7 @@ static void testPushPrioritization(void) NULL)); TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(3, tx.queue_size); - const UdpardTxItem* frame = udpardTxPeek(&tx); + UdpardTxItem* frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); TEST_ASSERT_EQUAL(0xAAAAAAAA, frame->destination.ip_address); diff --git a/tests/src/test_tx.cpp b/tests/src/test_tx.cpp index 7ca4c10..5f5a025 100644 --- a/tests/src/test_tx.cpp +++ b/tests/src/test_tx.cpp @@ -113,7 +113,7 @@ void testPublish() &user_transfer_referent)); TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); - const auto* frame = udpardTxPeek(&tx); + auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; TEST_ASSERT_NOT_EQUAL(nullptr, frame); TEST_ASSERT_EQUAL(nullptr, frame->next_in_transfer); @@ -249,7 +249,7 @@ void testRequest() &user_transfer_referent)); TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); - const auto* frame = udpardTxPeek(&tx); + auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; TEST_ASSERT_NOT_EQUAL(nullptr, frame); TEST_ASSERT_EQUAL(nullptr, frame->next_in_transfer); @@ -401,7 +401,7 @@ void testRespond() &user_transfer_referent)); TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); TEST_ASSERT_EQUAL(1, tx.queue_size); - const auto* frame = udpardTxPeek(&tx); + auto* frame = udpardTxPeek(&tx); std::cout << hexdump::hexdump(frame->datagram_payload.data, frame->datagram_payload.size) << "\n\n"; TEST_ASSERT_NOT_EQUAL(nullptr, frame); TEST_ASSERT_EQUAL(nullptr, frame->next_in_transfer); From 5260af7a00cb8459f97e9118fb8363a0f7c9e09b Mon Sep 17 00:00:00 2001 From: Sergei Shirokov Date: Fri, 29 Nov 2024 11:33:01 +0200 Subject: [PATCH 10/10] minor fixes #sonar --- libudpard/udpard.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libudpard/udpard.h b/libudpard/udpard.h index 9cd22f5..70e6388 100644 --- a/libudpard/udpard.h +++ b/libudpard/udpard.h @@ -173,7 +173,7 @@ /// /// Typically, if block pool allocators are used, the following block sizes should be served: /// -/// - (MTU+library overhead) blocks for the TX and RX pipelines (usually less than 2048 bytes); +/// - MTU sized blocks for the TX and RX pipelines (usually less than 2048 bytes); /// - TX fragment item sized blocks for the TX pipeline (less than 128 bytes). /// - RX session object sized blocks for the RX pipeline (less than 512 bytes); /// - RX fragment handle sized blocks for the RX pipeline (less than 128 bytes). @@ -201,7 +201,7 @@ extern "C" { /// Semantic version of this library (not the Cyphal specification). /// API will be backward compatible within the same major version. -#define UDPARD_VERSION_MAJOR 1 +#define UDPARD_VERSION_MAJOR 2 #define UDPARD_VERSION_MINOR 0 /// The version number of the Cyphal specification implemented by this library. @@ -460,7 +460,7 @@ struct UdpardTx /// One transport frame (UDP datagram) stored in the UdpardTx transmission queue along with its metadata. /// The datagram should be sent to the indicated UDP/IP endpoint with the specified DSCP value. /// The datagram should be discarded (transmission aborted) if the deadline has expired. -/// All fields are READ-ONLY except mutable payload `datagram_payload` field, which could be nullified to indicate +/// All fields are READ-ONLY except the mutable `datagram_payload` field, which could be nullified to indicate /// a transfer of the payload memory ownership to somewhere else. /// struct UdpardTxItem @@ -483,7 +483,7 @@ struct UdpardTxItem /// LibUDPard selects the DSCP value based on the transfer priority level and the configured DSCP mapping. uint_least8_t dscp; - /// Holds original transfer priority level (before DSCP mapping, see above `dscp`). + /// Holds the original transfer priority level (before DSCP mapping, see above `dscp`). enum UdpardPriority priority; /// This UDP/IP datagram compiled by libudpard should be sent to this endpoint.