Skip to content

Commit

Permalink
Add udpardGather #sonar
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-kirienko committed Aug 9, 2023
1 parent c92f8df commit df1108b
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 0 deletions.
24 changes: 24 additions & 0 deletions libudpard/udpard.c
Original file line number Diff line number Diff line change
Expand Up @@ -1651,3 +1651,27 @@ int_fast8_t udpardRxSubscriptionInit(struct UdpardRxSubscription* const self,
(void) &rxPortFree;
return 0;
}

// =====================================================================================================================
// ==================================================== MISC =====================================================
// =====================================================================================================================

size_t udpardGather(const struct UdpardFragment head, const size_t destination_size, void* const destination)
{
size_t offset = 0;
if (NULL != destination)
{
const struct UdpardFragment* frag = &head;
while ((frag != NULL) && (offset < destination_size))
{
UDPARD_ASSERT(frag->view.data != NULL);
const size_t frag_size = smaller(frag->view.size, destination_size - offset);
// NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling)
(void) memmove(((byte_t*) destination) + offset, frag->view.data, frag_size);
offset += frag_size;
UDPARD_ASSERT(offset <= destination_size);
frag = frag->next;
}
}
return offset;
}
16 changes: 16 additions & 0 deletions libudpard/udpard.h
Original file line number Diff line number Diff line change
Expand Up @@ -1021,6 +1021,22 @@ int_fast8_t udpardRxRPCDispatcherReceive(struct UdpardRxRPCDispatcher* const sel
const uint_fast8_t redundant_iface_index,
struct UdpardRxRPCTransfer* const out_transfer);

// =====================================================================================================================
// ==================================================== MISC =====================================================
// =====================================================================================================================

/// This helper function takes the head of a fragmented buffer list and copies the data into the contiguous buffer
/// provided by the user. If the total size of all fragments combined exceeds the size of the user-provided buffer,
/// copying will stop early after the buffer is filled, thus truncating the fragmented data short.
///
/// The source list is not modified. Do not forget to free its memory afterward if it was dynamically allocated.
///
/// The function has no effect and returns zero if the destination buffer is NULL.
/// The data pointers in the fragment list shall be valid, otherwise the behavior is undefined.
///
/// Returns the number of bytes copied into the contiguous destination buffer.
size_t udpardGather(const struct UdpardFragment head, const size_t destination_size, void* const destination);

#ifdef __cplusplus
}
#endif
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ endfunction()
gen_test_matrix(test_helpers "src/test_helpers.c")
gen_test_matrix(test_cavl "src/test_cavl.cpp")
gen_test_matrix(test_tx "${library_dir}/udpard.c;src/test_tx.cpp")
gen_test_matrix(test_misc "${library_dir}/udpard.c;src/test_misc.cpp")
gen_test_matrix(test_intrusive_crc "src/test_intrusive_crc.c")
gen_test_matrix(test_intrusive_tx "src/test_intrusive_tx.c")
gen_test_matrix(test_intrusive_rx "src/test_intrusive_rx.c")
79 changes: 79 additions & 0 deletions tests/src/test_misc.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/// This software is distributed under the terms of the MIT License.
/// Copyright (C) OpenCyphal Development Team <opencyphal.org>
/// Copyright Amazon.com Inc. or its affiliates.
/// SPDX-License-Identifier: MIT

#include <udpard.h>
#include "hexdump.hpp"
#include <unity.h>
#include <iostream>
#include <array>
#include <algorithm>

namespace
{
void testGather()
{
const std::string_view payload =
"It's very simple. The attacker must first transform themselves into life forms that can survive in a "
"low-dimensional universe. For instance, a four-dimensional species can transform itself into "
"three-dimensional creatures, or a three-dimensional species can transform itself into two-dimensional life. "
"After the entire civilization has entered a lower dimension, they can initiate a dimensional strike against "
"the enemy without concern for the consequences.";

std::array<UdpardFragment, 4> frags{{}};
frags.at(0).next = &frags.at(1);
frags.at(1).next = &frags.at(2);
frags.at(2).next = &frags.at(3);
frags.at(3).next = nullptr;

frags.at(0).view.data = payload.data();
frags.at(0).view.size = 100;

frags.at(1).view.data = payload.data() + frags.at(0).view.size;
frags.at(1).view.size = 100;

frags.at(2).view.data = payload.data() + frags.at(1).view.size + frags.at(0).view.size;
frags.at(2).view.size = 0; // Edge case.

frags.at(3).view.data = payload.data() + frags.at(2).view.size + frags.at(1).view.size + frags.at(0).view.size;
frags.at(3).view.size = payload.size() - frags.at(2).view.size - frags.at(1).view.size - frags.at(0).view.size;

std::array<std::uint8_t, 1024> mono{};

// Copy full size payload.
std::generate(mono.begin(), mono.end(), [] { return std::rand() % 256; });
TEST_ASSERT_EQUAL(payload.size(), udpardGather(frags.at(0), mono.size(), mono.data()));
TEST_ASSERT_EQUAL_MEMORY(payload.data(), mono.data(), payload.size());

// Truncation mid-fragment.
std::generate(mono.begin(), mono.end(), [] { return std::rand() % 256; });
TEST_ASSERT_EQUAL(150, udpardGather(frags.at(0), 150, mono.data()));
TEST_ASSERT_EQUAL_MEMORY(payload.data(), mono.data(), 150);

// Truncation at the fragment boundary.
std::generate(mono.begin(), mono.end(), [] { return std::rand() % 256; });
TEST_ASSERT_EQUAL(200, udpardGather(frags.at(0), 200, mono.data()));
TEST_ASSERT_EQUAL_MEMORY(payload.data(), mono.data(), 200);

// Empty destination.
mono.fill(0xA5);
TEST_ASSERT_EQUAL(0, udpardGather(frags.at(0), 0, mono.data()));
TEST_ASSERT_EQUAL(0, std::count_if(mono.begin(), mono.end(), [](const auto x) { return x != 0xA5; }));

// Edge cases.
TEST_ASSERT_EQUAL(0, udpardGather(frags.at(0), 0, nullptr));
TEST_ASSERT_EQUAL(0, udpardGather(frags.at(0), 100, nullptr));
}
} // namespace

void setUp() {}

void tearDown() {}

int main()
{
UNITY_BEGIN();
RUN_TEST(testGather);
return UNITY_END();
}

0 comments on commit df1108b

Please sign in to comment.