From d6e57b538962eecbff57fdfcf2ad328b236782ec Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Wed, 21 Dec 2022 21:57:32 +0900 Subject: [PATCH] util: Add spdk_strcpy_replace() to replace substrings spdk_nvme_cpl_get_status_string() returns a string which contains upper cases, spaces, and hyphens. To use the returned string for JSON RPC, we have to convert it to a string which contains only lowercases and underscores. For our convenience, add a new API spdk_strcpy_replace() to replace all occurrences of the search string with the replacement string. Signed-off-by: Shuhei Matsumoto Change-Id: I3ca9774d0bfb2d0bb7bd7412bc671e6f69104b7d Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16054 Tested-by: SPDK CI Jenkins Community-CI: Mellanox Build Bot Reviewed-by: Konrad Sztyber Reviewed-by: Aleksey Marchuk Reviewed-by: Jim Harris --- CHANGELOG.md | 3 ++ include/spdk/string.h | 17 +++++++ lib/util/spdk_util.map | 1 + lib/util/string.c | 47 +++++++++++++++++ test/unit/lib/util/string.c/string_ut.c | 68 +++++++++++++++++++++++++ 5 files changed, 136 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f31b0f493..a529da6ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,9 @@ available trace point groups and mask of the available trace points for each gro New API `spdk_fd_group_get_epoll_event` that returns the epoll(7) event that caused a function callback in file descriptor group to execute. +A new API `spdk_strcpy_replace` was added to replace all occurrences of the search string +with the replacement string. + ### json Added API `spdk_json_write_double` and `spdk_json_write_named_double` to allow diff --git a/include/spdk/string.h b/include/spdk/string.h index dea244422..9e96dcc8c 100644 --- a/include/spdk/string.h +++ b/include/spdk/string.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2015 Intel Corporation. + * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. * All rights reserved. */ @@ -265,6 +266,22 @@ char **spdk_strarray_dup(const char **strarray); */ void spdk_strarray_free(char **strarray); +/** + * Copy a string into a fixed-size buffer with all occurrences of the search string + * replaced with the given replace substring. The fixed-size buffer must not be less + * than the string with the replaced values including the terminating null byte. + * + * \param dst Pointer to destination fixed-size buffer to fill. + * \param size Size of the destination fixed-size buffer in bytes. + * \param src Pointer to source null-terminated string to copy into dst. + * \param search The string being searched for. + * \param replace the replacement substring the replaces the found search substring. + * + * \return 0 on success, or negated errno on failure. + */ +int spdk_strcpy_replace(char *dst, size_t size, const char *src, const char *search, + const char *replace); + #ifdef __cplusplus } #endif diff --git a/lib/util/spdk_util.map b/lib/util/spdk_util.map index 65d1b354e..e3ab94774 100644 --- a/lib/util/spdk_util.map +++ b/lib/util/spdk_util.map @@ -126,6 +126,7 @@ spdk_strarray_from_string; spdk_strarray_dup; spdk_strarray_free; + spdk_strcpy_replace; # public functions in util.h spdk_u32log2; diff --git a/lib/util/string.c b/lib/util/string.c index ded464d60..d4baecf1f 100644 --- a/lib/util/string.c +++ b/lib/util/string.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2015 Intel Corporation. + * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. * All rights reserved. */ @@ -542,3 +543,49 @@ spdk_strarray_dup(const char **strarray) return result; } + +int +spdk_strcpy_replace(char *dst, size_t size, const char *src, const char *search, + const char *replace) +{ + const char *p, *q; + char *r; + size_t c, search_size, replace_size, dst_size; + + if (dst == NULL || src == NULL || search == NULL || replace == NULL) { + return -EINVAL; + } + + search_size = strlen(search); + replace_size = strlen(replace); + + c = 0; + for (p = strstr(src, search); p != NULL; p = strstr(p + search_size, search)) { + c++; + } + + dst_size = strlen(src) + (replace_size - search_size) * c; + if (dst_size >= size) { + return -EINVAL; + } + + q = src; + r = dst; + + for (p = strstr(src, search); p != NULL; p = strstr(p + search_size, search)) { + memcpy(r, q, p - q); + r += p - q; + + memcpy(r, replace, replace_size); + r += replace_size; + + q = p + search_size; + } + + memcpy(r, q, strlen(q)); + r += strlen(q); + + *r = '\0'; + + return 0; +} diff --git a/test/unit/lib/util/string.c/string_ut.c b/test/unit/lib/util/string.c/string_ut.c index 90c4f24f1..cb73514b3 100644 --- a/test/unit/lib/util/string.c/string_ut.c +++ b/test/unit/lib/util/string.c/string_ut.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C) 2017 Intel Corporation. + * Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. * All rights reserved. */ @@ -436,6 +437,72 @@ test_strarray(void) spdk_strarray_free(r2); } +static void +test_strcpy_replace(void) +{ + const char *original = "good morning, hello, thank you"; + const char *search1 = "evening"; + const char *replace1 = "unexpected"; + const char *search2 = "morning"; + const char *replace2 = "afternoon"; + const char *expected2 = "good afternoon, hello, thank you"; + const char *search3 = "morning"; + const char *replace3 = "night"; + const char *expected3 = "good night, hello, thank you"; + const char *search4 = "hello"; + const char *replace4 = "good bye"; + const char *expected4 = "good morning, good bye, thank you"; + const char *search5 = "thank you"; + const char *replace5 = "you are welcome"; + const char *expected5 = "good morning, hello, you are welcome"; + const char *search6 = " "; + const char *replace6 = "-"; + const char *expected6 = "good-morning,-hello,-thank-you"; + const char *search7 = ","; + const char *replace7 = "."; + const char *expected7 = "good morning. hello. thank you"; + char result[256]; + int rc; + + rc = spdk_strcpy_replace(NULL, 0, NULL, NULL, NULL); + CU_ASSERT(rc == -EINVAL); + + rc = spdk_strcpy_replace(result, sizeof(result), original, search1, replace1); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, original) == 0); + + rc = spdk_strcpy_replace(result, sizeof(result), original, search2, replace2); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, expected2) == 0); + + /* A case that sizeof(replace) is less than sizeof(search), and the result array is + * smaller than the original string. */ + rc = spdk_strcpy_replace(result, strlen(expected3) + 1, original, search3, replace3); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, expected3) == 0); + + /* An error case that the result array is smaller than the string with replaced values + * and a terminated null byte. */ + rc = spdk_strcpy_replace(result, strlen(expected3), original, search3, replace3); + CU_ASSERT(rc == -EINVAL); + + rc = spdk_strcpy_replace(result, sizeof(result), original, search4, replace4); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, expected4) == 0); + + rc = spdk_strcpy_replace(result, sizeof(result), original, search5, replace5); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, expected5) == 0); + + rc = spdk_strcpy_replace(result, sizeof(result), original, search6, replace6); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, expected6) == 0); + + rc = spdk_strcpy_replace(result, sizeof(result), original, search7, replace7); + CU_ASSERT(rc == 0); + CU_ASSERT(strcmp(result, expected7) == 0); +} + int main(int argc, char **argv) { @@ -454,6 +521,7 @@ main(int argc, char **argv) CU_ADD_TEST(suite, test_strtol); CU_ADD_TEST(suite, test_strtoll); CU_ADD_TEST(suite, test_strarray); + CU_ADD_TEST(suite, test_strcpy_replace); CU_basic_set_mode(CU_BRM_VERBOSE);