diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index ac7b55b6d59..7c1eb495cbc 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -77,6 +77,17 @@ class StringRefBase { void copy_utf8_truncated(char *dst, int64_t dst_size) const; template void copy_utf8_truncated(char (&dst)[N]) const; + /** + * Copy the string into a char array. The copied string will be null-terminated. If it does not + * fit, it will be truncated. + * + * \note #copy_utf8_truncated should be used UTF8 strings, + * this should be used for strings which are allowed to contain arbitrary + * byte sequences without a known encoding such as file-paths. + */ + void copy_bytes_truncated(char *dst, int64_t dst_size) const; + template void copy_bytes_truncated(char (&dst)[N]) const; + /** * Copy the string into a buffer. The buffer has to be one byte larger than the size of the * string, because the copied string will be null-terminated. Only use this when you are @@ -231,6 +242,11 @@ template inline void StringRefBase::copy_utf8_truncated(char (&dst)[N] this->copy_utf8_truncated(dst, N); } +template inline void StringRefBase::copy_bytes_truncated(char (&dst)[N]) const +{ + this->copy_bytes_truncated(dst, N); +} + /** * Return true when the string starts with the given prefix. */ diff --git a/source/blender/blenlib/intern/string_ref.cc b/source/blender/blenlib/intern/string_ref.cc index 97bd5eb1503..e96ca15a8c5 100644 --- a/source/blender/blenlib/intern/string_ref.cc +++ b/source/blender/blenlib/intern/string_ref.cc @@ -44,4 +44,20 @@ void StringRefBase::copy_utf8_truncated(char *dst, const int64_t dst_size) const dst[new_len] = '\0'; } +void StringRefBase::copy_bytes_truncated(char *dst, const int64_t dst_size) const +{ + /* Destination must at least hold the null terminator. */ + BLI_assert(dst_size >= 1); + + /* Common case when the string can just be copied over entirely. */ + if (size_ < dst_size) { + this->copy_unsafe(dst); + return; + } + + const int64_t new_len = std::min(size_, dst_size - 1); + memcpy(dst, data_, new_len); + dst[new_len] = '\0'; +} + } // namespace blender diff --git a/source/blender/blenlib/tests/BLI_string_ref_test.cc b/source/blender/blenlib/tests/BLI_string_ref_test.cc index 356e9a56118..1922fc2682b 100644 --- a/source/blender/blenlib/tests/BLI_string_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_string_ref_test.cc @@ -464,6 +464,58 @@ TEST(string_ref, CopyUtf8Truncated) } } +TEST(string_ref, CopyBytesTruncated) +{ + { + StringRef ref("hello"); + char dst[10]; + memset(dst, 0xFF, 10); + ref.copy_bytes_truncated(dst); + EXPECT_EQ(dst[5], '\0'); + EXPECT_EQ(dst[6], 0xFF); + EXPECT_EQ(ref, dst); + } + { + StringRef ref("0123456789"); + char dst[4]; + memset(dst, 0xFF, 4); + ref.copy_bytes_truncated(dst); + EXPECT_EQ(dst[0], '0'); + EXPECT_EQ(dst[1], '1'); + EXPECT_EQ(dst[2], '2'); + EXPECT_EQ(dst[3], '\0'); + } + { + /* Simple 4 byte string. */ + StringRef ref("01234"); + { + char dst[1]; + ref.copy_bytes_truncated(dst); + EXPECT_EQ(dst[0], '\0'); + } + { + char dst[2]; + ref.copy_bytes_truncated(dst); + EXPECT_EQ(dst[0], '0'); + } + { + char dst[3]; + ref.copy_bytes_truncated(dst); + EXPECT_EQ(StringRef(dst), "01"); + } + { + char dst[4]; + ref.copy_bytes_truncated(dst); + EXPECT_EQ(StringRef(dst), "012"); + } + { + char dst[5]; + ref.copy_bytes_truncated(dst); + EXPECT_EQ(StringRef(dst), "0123"); + } + } +} + TEST(string_ref, FromStringView) { std::string_view view = "hello";