From c9c29ab7d65b921f2a37f95cddccfee62d7b6181 Mon Sep 17 00:00:00 2001 From: Sergei Date: Fri, 29 Nov 2024 11:43:51 +0200 Subject: [PATCH] Sshirokov/v2 single tx copy (#60) - `udpardTxPeek` now returns mutable item - needed for payload ownership transfer. - `udpardTxPop` now accepts mutable item - eliminated private `TxItem` Also: - fixed Sonar warning: property 'sonar.cfamily.build-wrapper-output' is deprecated --- README.md | 3 + libudpard/udpard.c | 161 +++++----- libudpard/udpard.h | 95 ++++-- tests/src/test_e2e.cpp | 30 +- tests/src/test_intrusive_tx.c | 571 +++++++++++++++++++--------------- tests/src/test_tx.cpp | 51 +-- tools/run_sonar.sh | 2 +- 7 files changed, 514 insertions(+), 399 deletions(-) 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 // --------------------------------------------- BUILD CONFIGURATION --------------------------------------------- @@ -271,55 +272,56 @@ 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. - // 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. typedef struct { - TxItem* head; - TxItem* tail; - size_t count; + struct UdpardTxItem* head; + struct UdpardTxItem* tail; + 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 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* const out = (TxItem*) memAlloc(memory, sizeof(TxItem) + datagram_payload_size); + 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; - // 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]; + 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->datagram_payload.data = payload_data; + out->datagram_payload.size = datagram_payload_size; + } + else + { + memFree(memory.fragment, sizeof(struct UdpardTxItem), out); + out = NULL; + } } return out; } @@ -329,8 +331,8 @@ static inline TxItem* txNewItem(const struct UdpardMemoryResource 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; } @@ -390,14 +392,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)); @@ -408,13 +410,13 @@ static inline TxChain txMakeChain(const struct UdpardMemoryResource 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; @@ -423,15 +425,16 @@ static inline TxChain txMakeChain(const struct UdpardMemoryResource 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) { 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->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 +449,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->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) @@ -495,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 = @@ -513,11 +516,11 @@ 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; - memFree(tx->memory, sizeof(TxItem) + head->datagram_payload.size, head); + udpardTxFree(tx->memory, head); head = next; } } @@ -526,13 +529,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,27 +642,22 @@ 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; } -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 @@ -668,14 +666,19 @@ 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 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(struct UdpardTxItem), item); } } diff --git a/libudpard/udpard.h b/libudpard/udpard.h index 0df85b9..70e6388 100644 --- a/libudpard/udpard.h +++ b/libudpard/udpard.h @@ -173,7 +173,8 @@ /// /// 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). /// @@ -200,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. @@ -373,14 +374,34 @@ struct UdpardMemoryResource // ================================================= TX PIPELINE ================================================= // ===================================================================================================================== +/// 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. +/// +/// 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) +/// 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. /// 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. /// @@ -424,12 +445,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 @@ -443,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 the mutable `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. @@ -464,6 +483,9 @@ struct UdpardTxItem /// LibUDPard selects the DSCP value based on the transfer priority level and the configured DSCP mapping. uint_least8_t 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. /// The endpoint is always at a multicast address. struct UdpardUDPIPEndpoint destination; @@ -490,10 +512,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 @@ -547,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. @@ -613,35 +638,45 @@ 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 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. -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. /// /// 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 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..8ea7800 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. @@ -161,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); @@ -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), @@ -472,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); @@ -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..f3ac844 100644 --- a/tests/src/test_intrusive_tx.c +++ b/tests/src/test_intrusive_tx.c @@ -94,14 +94,17 @@ static void testMakeChainEmpty(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const TxChain chain = txMakeChain(mem, (byte_t[]){11, 22, 33, 44, 55, 66, 77, 88}, @@ -111,26 +114,23 @@ 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(sizeof(TxItem) + HEADER_SIZE_BYTES + 4, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); + 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); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + 4, chain.head); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); + udpardTxFree(mem, chain.head); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -138,14 +138,17 @@ static void testMakeChainSingleMaxMTU(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const TxChain chain = txMakeChain(mem, (byte_t[]){11, 22, 33, 44, 55, 66, 77, 88}, @@ -155,33 +158,29 @@ 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(sizeof(TxItem) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); + 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); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + DetailOfTheCosmosSize + TRANSFER_CRC_SIZE_BYTES, chain.head); + TEST_ASSERT_EQUAL(&user_transfer_referent, chain.head->user_transfer_reference); + udpardTxFree(mem, chain.head); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -189,8 +188,11 @@ static void testMakeChainSingleFrameDefaultMTU(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const byte_t payload[UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1] = {0}; + 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, (byte_t[]){11, 22, 33, 44, 55, 66, 77, 88}, @@ -204,16 +206,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(sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + + TEST_ASSERT_EQUAL(1 * 2ULL, alloc.allocated_fragments); + 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); - memFree(mem, - sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + TRANSFER_CRC_SIZE_BYTES, - chain.head); + 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. @@ -230,16 +230,16 @@ 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((sizeof(TxItem) + HEADER_SIZE_BYTES) * 2 + UDPARD_MTU_DEFAULT_MAX_SINGLE_FRAME + 1 + - TRANSFER_CRC_SIZE_BYTES, + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); + 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); - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + UDPARD_MTU_DEFAULT, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + 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); } } @@ -248,14 +248,17 @@ static void testMakeChainThreeFrames(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const size_t mtu = (EtherealStrengthSize + 4U + 3U) / 3U; // Force payload split into three frames. const TxChain chain = txMakeChain(mem, @@ -266,14 +269,15 @@ 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 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); + TEST_ASSERT_EQUAL(3 * (sizeof(struct UdpardTxItem) + 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; 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 +324,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,14 +334,17 @@ static void testMakeChainCRCSpill1(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const size_t mtu = InterstellarWarSize + 3U; const TxChain chain = txMakeChain(mem, @@ -348,53 +355,47 @@ 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 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); + 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. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -402,14 +403,17 @@ static void testMakeChainCRCSpill2(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const size_t mtu = InterstellarWarSize + 2U; const TxChain chain = txMakeChain(mem, @@ -420,53 +424,47 @@ 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 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); + 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. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -474,14 +472,17 @@ static void testMakeChainCRCSpill3(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const size_t mtu = InterstellarWarSize + 1U; const TxChain chain = txMakeChain(mem, @@ -492,53 +493,47 @@ 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 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); + 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. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -546,14 +541,17 @@ static void testMakeChainCRCSpillFull(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = 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, + const struct UdpardTxMemoryResources mem = { + .fragment = 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, }; const size_t mtu = InterstellarWarSize; const TxChain chain = txMakeChain(mem, @@ -564,49 +562,44 @@ 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 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + InterstellarWarSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(2 * 2ULL, alloc.allocated_fragments); + 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. - memFree(mem, sizeof(TxItem) + HEADER_SIZE_BYTES + mtu, chain.head); - memFree(mem, alloc.allocated_bytes, chain.tail); + udpardTxFree(mem, chain.head); + udpardTxFree(mem, chain.tail); TEST_ASSERT_EQUAL(0, alloc.allocated_fragments); } @@ -614,8 +607,11 @@ static void testPushPeekPopFree(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -641,11 +637,12 @@ 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 * (sizeof(TxItem) + HEADER_SIZE_BYTES) + EtherealStrengthSize + 4U, alloc.allocated_bytes); + TEST_ASSERT_EQUAL(3 * 2ULL, alloc.allocated_fragments); + 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); + 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); @@ -656,7 +653,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 +667,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,8 +690,11 @@ static void testPushPrioritization(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -720,9 +720,9 @@ 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); + UdpardTxItem* frame = udpardTxPeek(&tx); TEST_ASSERT_NOT_EQUAL(NULL, frame); TEST_ASSERT_EQUAL(0xAAAAAAAA, frame->destination.ip_address); @@ -740,7 +740,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 +760,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 +780,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 +800,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 +809,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 +824,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 +832,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 +840,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,8 +864,11 @@ static void testPushCapacityLimit(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -899,8 +902,11 @@ static void testPushOOM(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx = { .local_node_id = &node_id, @@ -931,12 +937,55 @@ 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; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 0xFFFFU; + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 0xFFFFU; // UdpardTx tx = { .local_node_id = &node_id, @@ -970,8 +1019,11 @@ static void testPushAnonymousService(void) { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 0xFFFFU; + const struct UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 0xFFFFU; // UdpardTx tx = { .local_node_id = &node_id, @@ -1021,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(); diff --git a/tests/src/test_tx.cpp b/tests/src/test_tx.cpp index 9579744..5f5a025 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,8 +86,11 @@ void testPublish() { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - const struct UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx{ .local_node_id = &node_id, @@ -102,9 +111,9 @@ 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); + 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); @@ -212,8 +221,11 @@ void testRequest() { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - const UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx{ .local_node_id = &node_id, @@ -235,9 +247,9 @@ 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); + 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); @@ -362,8 +374,11 @@ void testRespond() { InstrumentedAllocator alloc; instrumentedAllocatorNew(&alloc); - const UdpardMemoryResource mem = instrumentedAllocatorMakeMemoryResource(&alloc); - const UdpardNodeID node_id = 1234; + const UdpardTxMemoryResources mem = { + .fragment = instrumentedAllocatorMakeMemoryResource(&alloc), + .payload = instrumentedAllocatorMakeMemoryResource(&alloc), + }; + const UdpardNodeID node_id = 1234; // UdpardTx tx{ .local_node_id = &node_id, @@ -384,9 +399,9 @@ 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); + 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); diff --git a/tools/run_sonar.sh b/tools/run_sonar.sh index c0440d1..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.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