Optimisation of strings

This commit is contained in:
John Wellbelove 2025-05-31 21:47:25 +01:00
parent 15ba2b71cb
commit 0428118553
13 changed files with 280 additions and 109 deletions

View File

@ -675,7 +675,7 @@ namespace etl
{
if (&other != this)
{
append_impl(begin(), other.begin(), other.end(), other.is_truncated(), other.is_secure());
append_impl<T>(begin(), other.begin(), other.end(), other.is_truncated(), other.is_secure());
}
}
@ -697,7 +697,7 @@ namespace etl
ETL_ASSERT(subposition <= other.size(), ETL_ERROR(string_out_of_bounds));
append_impl(begin(), other.begin() + subposition, other.begin() + subposition + sublength, other.is_truncated(), other.is_secure());
append_impl<T>(begin(), other.begin() + subposition, other.begin() + subposition + sublength, other.is_truncated(), other.is_secure());
}
}
@ -711,7 +711,7 @@ namespace etl
template <typename TIterator>
void assign(TIterator first, TIterator last)
{
append_impl(begin(), first, last, false, false);
append_impl<T>(begin(), first, last, false, false);
}
//*********************************************************************
@ -721,7 +721,7 @@ namespace etl
//*********************************************************************
void assign(const_pointer str)
{
append_impl(begin(), str, false, false);
append_impl<T>(begin(), str, false, false);
}
//*********************************************************************
@ -732,7 +732,7 @@ namespace etl
//*********************************************************************
void assign(const_pointer str, size_type n)
{
append_impl(begin(), str, str + n, false, false);
append_impl<T>(begin(), str, str + n, false, false);
}
//*********************************************************************
@ -741,7 +741,7 @@ namespace etl
template <typename TOtherTraits>
void assign(const etl::basic_string_view<T, TOtherTraits>& view)
{
append_impl(begin(), view.begin(), view.end(), false, false);
append_impl<T>(begin(), view.begin(), view.end(), false, false);
}
//*********************************************************************
@ -820,7 +820,7 @@ namespace etl
//*********************************************************************
ibasic_string& append(const ibasic_string& str)
{
append_impl(end(), str.begin(), str.end(), str.is_truncated(), str.is_secure());
append_impl<T>(end(), str.begin(), str.end(), str.is_truncated(), str.is_secure());
return *this;
}
@ -840,7 +840,7 @@ namespace etl
ETL_ASSERT(subposition <= str.size(), ETL_ERROR(string_out_of_bounds));
append_impl(end(), str.begin() + subposition, str.begin() + subposition + sublength, str.is_truncated(), str.is_secure());
append_impl<T>(end(), str.begin() + subposition, str.begin() + subposition + sublength, str.is_truncated(), str.is_secure());
return *this;
}
@ -853,7 +853,7 @@ namespace etl
template <class TIterator>
ibasic_string& append(TIterator first, TIterator last)
{
append_impl(end(), first, last, false, false);
append_impl<T>(end(), first, last, false, false);
return *this;
}
@ -864,7 +864,7 @@ namespace etl
//*********************************************************************
ibasic_string& append(const_pointer str)
{
append_impl(end(), str, false, false);
append_impl<T>(end(), str, false, false);
return *this;
}
@ -876,7 +876,7 @@ namespace etl
//*********************************************************************
ibasic_string& append(const_pointer str, size_type n)
{
append_impl(end(), str, str + n, false, false);
append_impl<T>(end(), str, str + n, false, false);
return *this;
}
@ -888,7 +888,7 @@ namespace etl
template <typename TOtherTraits>
ibasic_string& append(const etl::basic_string_view<T, TOtherTraits>& view)
{
append_impl(end(), view.begin(), view.end(), false, false);
append_impl<T>(end(), view.begin(), view.end(), false, false);
return *this;
}
@ -2697,7 +2697,7 @@ namespace etl
//*********************************************************************
/// Common implementation for 'assign' and 'append' for iterators.
//*********************************************************************
template <typename TIterator>
template <typename TChar, typename TIterator>
void append_impl(iterator position, TIterator first, TIterator last, bool truncated, bool secure)
{
difference_type start = etl::distance(p_buffer, position);
@ -2733,20 +2733,76 @@ namespace etl
}
//*********************************************************************
/// Common implementation for 'assign' and 'append' for single pointer.
/// Common implementation for 'assign' and 'append' for single C string pointer.
/// Enabled for char.
//*********************************************************************
void append_impl(iterator position, const_pointer first, bool truncated, bool secure)
template <typename TChar>
typename etl::enable_if<etl::is_same<TChar, char>::value, void>::type
append_impl(iterator position, const TChar* src, bool truncated, bool secure)
{
if (src == ETL_NULLPTR)
{
clear();
return;
}
difference_type start = etl::distance(p_buffer, position);
difference_type free_space = etl::distance(position, p_buffer + CAPACITY);
etl::str_n_copy_result result = etl::str_n_copy(first, size_t(free_space), position);
pointer dst = position;
size_t length = ::strlen(src);
size_t count = (length < size_t(free_space)) ? length : size_t(free_space);
etl::mem_copy(src, count, dst);
current_size = size_t(start) + result.count;
truncated |= (src[count] != 0);
current_size = size_t(start) + count;
p_buffer[current_size] = 0;
#if ETL_HAS_STRING_TRUNCATION_CHECKS
set_truncated(result.truncated || truncated);
set_truncated(truncated);
#if ETL_HAS_ERROR_ON_STRING_TRUNCATION
ETL_ASSERT(is_truncated == false, ETL_ERROR(string_truncation));
#endif
#endif
#if ETL_HAS_STRING_CLEAR_AFTER_USE
if (secure)
{
set_secure();
}
#endif
cleanup();
}
//*********************************************************************
/// Common implementation for 'assign' and 'append' for single C string pointer.
/// Enabled for wchar, char8_t, char16_t or char32_t.
//*********************************************************************
template <typename TChar>
typename etl::enable_if<!etl::is_same<TChar, char>::value, void>::type
append_impl(iterator position, const TChar* src, bool truncated, bool secure)
{
if (src == ETL_NULLPTR)
{
clear();
return;
}
difference_type start = etl::distance(p_buffer, position);
difference_type free_space = etl::distance(position, p_buffer + CAPACITY);
pointer dst = position;
size_t length = etl::strlen(src, free_space);
size_t count = (length < size_t(free_space)) ? length : size_t(free_space);
etl::mem_copy(src, count, dst);
truncated |= (src[count] != 0);
current_size = size_t(start) + count;
p_buffer[current_size] = 0;
#if ETL_HAS_STRING_TRUNCATION_CHECKS
set_truncated(truncated);
#if ETL_HAS_ERROR_ON_STRING_TRUNCATION
ETL_ASSERT(is_truncated == false, ETL_ERROR(string_truncation));
#endif

View File

@ -903,31 +903,77 @@ namespace etl
{
if ((src == ETL_NULLPTR) || (dst == ETL_NULLPTR))
{
str_n_copy_result result = { 0, false, false };
return result;
return { 0, false, false };
}
size_t count = 0;
while ((count != n) && (*src != 0))
#if defined(__GNUC__) || defined(_MSC_VER)
// Use restrict if available, for better optimization
const T* __restrict s = src;
T* __restrict d = dst;
#else
const T* s = src;
T* d = dst;
#endif
// Copy in blocks of 4 for loop unrolling
count += 4;
while (count <= n)
{
*dst++ = *src++;
if (s[0] == 0)
{
break;
}
d[0] = s[0];
if (s[1] == 0)
{
count += 1;
d[1] = 0;
return { count, false, true };
}
d[1] = s[1];
if (s[2] == 0)
{
count += 2; d[2] = 0;
return { count, false, true };
}
d[2] = s[2];
if (s[3] == 0)
{
count += 3;
d[3] = 0;
return { count, false, true };
}
d[3] = s[3];
s += 4;
d += 4;
count += 4;
}
// Copy remaining
while ((count < n) && (*s != 0))
{
*d++ = *s++;
++count;
}
// Did we stop because of a terminating zero?
if (count != n)
//
if (count <= n)
{
// Yes we did.
*dst = 0;
str_n_copy_result result = { count, false, true };
return result;
}
else
*d = 0;
return { count, false, true };
}
else
{
// No. Truncation depends on the next src character being a terminating zero or not.
str_n_copy_result result = { count, *src != 0, false };
return result;
return { count, *s != 0, false };
}
}
}

View File

@ -165,6 +165,20 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
Text text(shorter_text.c_str());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -219,6 +219,21 @@ namespace
#endif
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextBuffer buffer{0};
TextSTD compare_text(shorter_text.c_str());
Text text(shorter_text.c_str(), buffer.data(), buffer.size());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer)
{

View File

@ -179,6 +179,20 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
Text text(shorter_text.c_str());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -248,6 +248,21 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
TextBuffer buffer{0};
Text text(shorter_text.c_str(), buffer.data(), buffer.size());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -179,6 +179,20 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
Text text(shorter_text.c_str());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -248,6 +248,21 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
TextBuffer buffer{0};
Text text(shorter_text.c_str(), buffer.data(), buffer.size());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -182,6 +182,20 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
Text text(shorter_text.c_str());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -251,6 +251,21 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
TextBuffer buffer{0};
Text text(shorter_text.c_str(), buffer.data(), buffer.size());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -1784,81 +1784,5 @@ namespace
CHECK(text == expected);
}
//*************************************************************************
TEST(test_str_n_copy_destination_larger)
{
STR_PTR src = STR("Hello World");
STR_TYPE dst[15];
etl::str_n_copy_result result = etl::str_n_copy(src, 15, dst);
CHECK_EQUAL(etl::strlen(src), result.count);
CHECK_FALSE(result.truncated);
CHECK_TRUE(result.terminated);
CHECK(dst[11] == 0);
bool are_equal = etl::equal(src, src + 12, dst);
CHECK(are_equal);
}
//*************************************************************************
TEST(test_str_n_copy_destination_exact_include_space_for_terminator)
{
STR_PTR src = STR("Hello World");
STR_TYPE dst[12];
etl::str_n_copy_result result = etl::str_n_copy(src, 12, dst);
CHECK_EQUAL(11, result.count);
CHECK_FALSE(result.truncated);
CHECK_TRUE(result.terminated);
CHECK(dst[11] == 0);
bool are_equal = etl::equal(src, src + 12, dst);
CHECK(are_equal);
}
//*************************************************************************
TEST(test_str_n_copy_destination_exclude_space_for_terminator_exact_no_termination)
{
STR_PTR src = STR("Hello World");
STR_TYPE dst[11];
etl::str_n_copy_result result = etl::str_n_copy(src, 11, dst);
CHECK_EQUAL(11, result.count);
CHECK_FALSE(result.truncated);
CHECK_FALSE(result.terminated);
CHECK(dst[10] != 0);
bool are_equal = etl::equal(src, src + 11, dst);
CHECK(are_equal);
}
//*************************************************************************
TEST(test_str_n_copy_destination_smaller_no_termination)
{
STR_PTR src = STR("Hello World");
STR_TYPE dst[9];
etl::str_n_copy_result result = etl::str_n_copy(src, 9, dst);
CHECK_EQUAL(9, result.count);
CHECK_TRUE(result.truncated);
CHECK_FALSE(result.terminated);
CHECK(dst[8] != 0);
bool are_equal = etl::equal(src, src + 9, dst);
CHECK(are_equal);
}
//*************************************************************************
TEST(test_str_n_copy_zero_sized_destination)
{
STR_PTR src = STR("Hello World");
STR_TYPE dst[15];
etl::str_n_copy_result result = etl::str_n_copy(src, 0, dst);
CHECK_EQUAL(0, result.count);
CHECK_TRUE(result.truncated);
CHECK_FALSE(result.terminated);
}
};
}

View File

@ -180,6 +180,20 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
Text text(shorter_text.c_str());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{

View File

@ -251,6 +251,21 @@ namespace
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_shorter_string)
{
TextSTD compare_text(shorter_text.c_str());
TextBuffer buffer{0};
Text text(shorter_text.c_str(), buffer.data(), buffer.size());
CHECK(!text.empty());
bool is_equal = Equal(compare_text, text);
CHECK(is_equal);
CHECK_FALSE(text.is_truncated());
}
//*************************************************************************
TEST_FIXTURE(SetupFixture, test_constructor_char_pointer_excess)
{