diff --git a/sys/include/net/gnrc/pktbuf.h b/sys/include/net/gnrc/pktbuf.h index f9f29850b7..ad109bb3ef 100644 --- a/sys/include/net/gnrc/pktbuf.h +++ b/sys/include/net/gnrc/pktbuf.h @@ -298,6 +298,60 @@ gnrc_pktsnip_t *gnrc_pktbuf_reverse_snips(gnrc_pktsnip_t *pkt); */ gnrc_pktsnip_t *gnrc_pktbuf_duplicate_upto(gnrc_pktsnip_t *pkt, gnrc_nettype_t type); +/** + * @brief Merge pktsnip chain to single pktsnip. + * + * Specifically it calls @ref gnrc_pktbuf_realloc_data() on @p pkt, then copies + * the data of all following packet snips into that reallocated space, and + * removes the packet snip the data was copied from afterwards. + * + * ### Example + * #### Input + * + * buffer + * +---------------------------+ +------+ + * | size = 8 | data +-------->| | + * | type = NETTYPE_IPV6 |------------+ +------+ + * +---------------------------+ . . + * | next . . + * v . . + * +---------------------------+ +------+ + * | size = 40 | data +----------->| | + * | type = NETTYPE_UDP |---------+ +------+ + * +---------------------------+ . . + * | next . . + * v + * +---------------------------+ +------+ + * | size = 14 | data +-------------->| | + * | type = NETTYPE_UNDEF |------+ +------+ + * +---------------------------+ . . + * + * + * #### Output + * + * buffer + * +---------------------------+ +------+ + * | size = 62 | data +-------->| | + * | type = NETTYPE_IPV6 |------------+ | | + * +---------------------------+ | | + * | | + * | | + * | | + * +------+ + * . . + * + * @warning @p pkt needs to write protected before calling this function. + * @note Packets in receive order need to call + * @ref gnrc_pktbuf_reverse_snips() first to get the data in the + * correct order. + * + * @param[in,out] pkt The snip to merge. + * + * @return 0, on success + * @return ENOMEM, if no space is left in the packet buffer. + */ +int gnrc_pktbuf_merge(gnrc_pktsnip_t *pkt); + #ifdef DEVELHELP /** * @brief Prints some statistics about the packet buffer to stdout. diff --git a/sys/net/gnrc/pktbuf/gnrc_pktbuf.c b/sys/net/gnrc/pktbuf/gnrc_pktbuf.c index 83e0a89e31..503e66c7b3 100644 --- a/sys/net/gnrc/pktbuf/gnrc_pktbuf.c +++ b/sys/net/gnrc/pktbuf/gnrc_pktbuf.c @@ -109,5 +109,32 @@ gnrc_pktsnip_t *gnrc_pktbuf_reverse_snips(gnrc_pktsnip_t *pkt) return reversed; } +int gnrc_pktbuf_merge(gnrc_pktsnip_t *pkt) +{ + size_t offset = pkt->size; + size_t size = gnrc_pkt_len(pkt); + int res = 0; + + if (pkt->size == size) { + return res; + } + + /* Re-allocate data */ + res = gnrc_pktbuf_realloc_data(pkt, size); + if (res != 0) { + return res; + } + + /* Copy data to new buffer */ + for (gnrc_pktsnip_t *ptr = pkt->next; ptr != NULL; ptr = ptr->next) { + memcpy(((uint8_t *)pkt->data) + offset, ptr->data, ptr->size); + offset += ptr->size; + } + + /* Release old pktsnips and data*/ + gnrc_pktbuf_release(pkt->next); + pkt->next = NULL; + return res; +} /** @} */ diff --git a/tests/unittests/tests-pktbuf/tests-pktbuf.c b/tests/unittests/tests-pktbuf/tests-pktbuf.c index 64d000b26a..8f204afa85 100644 --- a/tests/unittests/tests-pktbuf/tests-pktbuf.c +++ b/tests/unittests/tests-pktbuf/tests-pktbuf.c @@ -653,6 +653,52 @@ static void test_pktbuf_realloc_data__success3(void) TEST_ASSERT(gnrc_pktbuf_is_empty()); } +static void test_pktbuf_merge_data__memfull(void) +{ + gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, NULL, (GNRC_PKTBUF_SIZE / 4), + GNRC_NETTYPE_TEST); + + pkt = gnrc_pktbuf_add(pkt, NULL, (GNRC_PKTBUF_SIZE / 4) + 1, + GNRC_NETTYPE_TEST); + TEST_ASSERT_EQUAL_INT(ENOMEM, gnrc_pktbuf_merge(pkt)); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_pktbuf_merge_data__success1(void) +{ + gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, NULL, 0, GNRC_NETTYPE_TEST); + + TEST_ASSERT_NOT_NULL(pkt); + TEST_ASSERT_NULL(pkt->data); + + TEST_ASSERT_EQUAL_INT(0, gnrc_pktbuf_merge(pkt)); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_empty()); +} + +static void test_pktbuf_merge_data__success2(void) +{ + gnrc_pktsnip_t *pkt = gnrc_pktbuf_add(NULL, TEST_STRING4, + sizeof(TEST_STRING4), + GNRC_NETTYPE_TEST); + + pkt = gnrc_pktbuf_add(pkt, TEST_STRING8, sizeof(TEST_STRING8), GNRC_NETTYPE_TEST); + pkt = gnrc_pktbuf_add(pkt, TEST_STRING16, sizeof(TEST_STRING16), GNRC_NETTYPE_TEST); + + TEST_ASSERT_EQUAL_INT(0, gnrc_pktbuf_merge(pkt)); + TEST_ASSERT_NULL(pkt->next); + TEST_ASSERT_EQUAL_STRING(TEST_STRING16, pkt->data); + TEST_ASSERT_EQUAL_STRING(TEST_STRING8, + (char *) pkt->data + sizeof(TEST_STRING16)); + TEST_ASSERT_EQUAL_STRING(TEST_STRING4, + (char *) pkt->data + sizeof(TEST_STRING16) + + sizeof(TEST_STRING8)); + gnrc_pktbuf_release(pkt); + TEST_ASSERT(gnrc_pktbuf_is_empty()); + TEST_ASSERT(gnrc_pktbuf_is_sane()); +} + static void test_pktbuf_hold__pkt_null(void) { gnrc_pktbuf_hold(NULL, 1); @@ -895,6 +941,9 @@ Test *tests_pktbuf_tests(void) new_TestFixture(test_pktbuf_realloc_data__success), new_TestFixture(test_pktbuf_realloc_data__success2), new_TestFixture(test_pktbuf_realloc_data__success3), + new_TestFixture(test_pktbuf_merge_data__memfull), + new_TestFixture(test_pktbuf_merge_data__success1), + new_TestFixture(test_pktbuf_merge_data__success2), new_TestFixture(test_pktbuf_hold__pkt_null), new_TestFixture(test_pktbuf_hold__pkt_external), new_TestFixture(test_pktbuf_hold__success),