Small algorithm improvements to avoid allocations
TL depends on cppcoro now
This commit is contained in:
@@ -51,6 +51,7 @@ endforeach()
|
|||||||
|
|
||||||
add_library(TL STATIC ${SRC} ${HEADERS} ${NATVIS})
|
add_library(TL STATIC ${SRC} ${HEADERS} ${NATVIS})
|
||||||
target_include_directories(TL PUBLIC "include")
|
target_include_directories(TL PUBLIC "include")
|
||||||
|
target_include_directories(TL PUBLIC "${PROJECT_SOURCE_DIR}/../cppcoro/include")
|
||||||
target_link_libraries(TL EASTL)
|
target_link_libraries(TL EASTL)
|
||||||
|
|
||||||
#if(BUILD_SHARED_LIBS)
|
#if(BUILD_SHARED_LIBS)
|
||||||
|
|||||||
@@ -105,49 +105,7 @@ inline void to_upper_ascii(string& str) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class String>
|
template <class String>
|
||||||
tl::vector<String> split_on_any(const String& str, const char* separators, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
cppcoro::generator<const String&> split_on_any(const String& str, char sep, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
||||||
{
|
|
||||||
return split_on_any(str, String(separators), token_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String, class Delim>
|
|
||||||
tl::vector<String> split_on_any(const String& str, const Delim& separators, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
|
||||||
{
|
|
||||||
tl::vector<String> dst;
|
|
||||||
dst.reserve(32);
|
|
||||||
split_on_any(str,
|
|
||||||
separators,
|
|
||||||
[&dst](const String& token)
|
|
||||||
{
|
|
||||||
dst.push_back(token);
|
|
||||||
},
|
|
||||||
token_policy);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String, class Delim>
|
|
||||||
tl::vector<String> split_on_all(const String& str, const Delim& separator, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
|
||||||
{
|
|
||||||
tl::vector<String> dst;
|
|
||||||
dst.reserve(32);
|
|
||||||
split_on_all(str,
|
|
||||||
separator,
|
|
||||||
[&dst](const String& token)
|
|
||||||
{
|
|
||||||
dst.push_back(token);
|
|
||||||
},
|
|
||||||
token_policy);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String, class Func>
|
|
||||||
void split_on_any(const String& str, const char* delimiters, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
|
||||||
{
|
|
||||||
return split_on_any(str, String(delimiters), functor, token_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String, class Func>
|
|
||||||
void split_on_any(const String& str, char delimiter, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
|
||||||
{
|
{
|
||||||
String token;
|
String token;
|
||||||
const char* cstr = str.data();
|
const char* cstr = str.data();
|
||||||
@@ -155,35 +113,39 @@ void split_on_any(const String& str, char delimiter, const Func& functor, empty_
|
|||||||
const size_t endOffset = str.size();
|
const size_t endOffset = str.size();
|
||||||
while (offset <= endOffset)
|
while (offset <= endOffset)
|
||||||
{
|
{
|
||||||
const char* nextCstr = static_cast<const char*>(::memchr(cstr + offset, delimiter, endOffset - offset));
|
const char* nextCstr = static_cast<const char*>(::memchr(cstr + offset, sep, endOffset - offset));
|
||||||
const size_t nextOffset = nextCstr ? nextCstr - cstr : endOffset;
|
const size_t nextOffset = nextCstr ? nextCstr - cstr : endOffset;
|
||||||
|
|
||||||
if (offset < nextOffset || token_policy == empty_token_policy::keep)
|
if (offset < nextOffset || token_policy == empty_token_policy::keep)
|
||||||
{
|
{
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(cstr + offset, cstr + nextOffset);
|
token.append(cstr + offset, cstr + nextOffset);
|
||||||
functor(token);
|
co_yield token;
|
||||||
}
|
}
|
||||||
offset = nextOffset + 1;
|
offset = nextOffset + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class String, class Delim, class Func>
|
template <class String>
|
||||||
void split_on_any(const String& str, const Delim& delimiters, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
cppcoro::generator<const String&> split_on_any(const String& str, tl::string_view sep, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
||||||
{
|
{
|
||||||
const size_t k_delims_size = delimiters.size();
|
const size_t sep_size = sep.size();
|
||||||
if (k_delims_size == 1)
|
if (sep_size == 1)
|
||||||
return split_on_any(str, delimiters[0], functor, token_policy);
|
|
||||||
|
|
||||||
if (k_delims_size == 0)
|
|
||||||
{
|
{
|
||||||
TL_PLAIN_FAIL("'delimiters' cannot be empty");
|
for (auto const& s: split_on_any(str, sep[0], token_policy))
|
||||||
return;
|
co_yield s;
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sep_size == 0)
|
||||||
|
{
|
||||||
|
TL_PLAIN_FAIL("'sep' cannot be empty");
|
||||||
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String token;
|
String token;
|
||||||
const char* cstr = str.data();
|
const char* cstr = str.data();
|
||||||
const char* delimsStr = delimiters.data();
|
const char* sepStr = sep.data();
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
const size_t endOffset = str.size();
|
const size_t endOffset = str.size();
|
||||||
while (offset <= endOffset)
|
while (offset <= endOffset)
|
||||||
@@ -191,7 +153,7 @@ void split_on_any(const String& str, const Delim& delimiters, const Func& functo
|
|||||||
size_t nextOffset = offset;
|
size_t nextOffset = offset;
|
||||||
for (; nextOffset < endOffset; ++nextOffset)
|
for (; nextOffset < endOffset; ++nextOffset)
|
||||||
{
|
{
|
||||||
if (::memchr(delimsStr, cstr[nextOffset], k_delims_size) != nullptr)
|
if (::memchr(sepStr, cstr[nextOffset], sep_size) != nullptr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,47 +161,47 @@ void split_on_any(const String& str, const Delim& delimiters, const Func& functo
|
|||||||
{
|
{
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(cstr + offset, cstr + nextOffset);
|
token.append(cstr + offset, cstr + nextOffset);
|
||||||
functor(token);
|
co_yield token;
|
||||||
}
|
}
|
||||||
offset = nextOffset + 1;
|
offset = nextOffset + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class String, class Delim, class Func>
|
template <class String>
|
||||||
void split_on_all(const String& str, const Delim& sep, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
cppcoro::generator<const String&> split_on_all(const String& str, tl::string_view sep, empty_token_policy token_policy = empty_token_policy::discard) noexcept
|
||||||
{
|
{
|
||||||
String separator(sep);
|
if (sep.empty())
|
||||||
if (separator.empty())
|
|
||||||
{
|
{
|
||||||
TL_PLAIN_FAIL("'delimiters' cannot be empty");
|
TL_PLAIN_FAIL("'sep' cannot be empty");
|
||||||
return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t p0 = 0;
|
size_t p0 = 0;
|
||||||
size_t p1 = 0;
|
size_t p1 = 0;
|
||||||
|
const String empty_token;
|
||||||
String token;
|
String token;
|
||||||
while ((p1 = str.find(separator, p0)) != String::npos)
|
while ((p1 = str.find(sep, p0)) != String::npos)
|
||||||
{
|
{
|
||||||
if (p1 > p0)
|
if (p1 > p0)
|
||||||
{
|
{
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(str, p0, p1 - p0);
|
token.append(str, p0, p1 - p0);
|
||||||
functor(token);
|
co_yield token;
|
||||||
}
|
}
|
||||||
else if (token_policy == empty_token_policy::keep)
|
else if (token_policy == empty_token_policy::keep)
|
||||||
functor(String{});
|
co_yield empty_token;
|
||||||
|
|
||||||
p0 = p1 + separator.size();
|
p0 = p1 + sep.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str.size() > p0)
|
if (str.size() > p0)
|
||||||
{
|
{
|
||||||
token.clear();
|
token.clear();
|
||||||
token.append(str, p0, String::npos);
|
token.append(str, p0, String::npos);
|
||||||
functor(token);
|
co_yield token;
|
||||||
}
|
}
|
||||||
else if (token_policy == empty_token_policy::keep)
|
else if (token_policy == empty_token_policy::keep)
|
||||||
functor(String{});
|
co_yield empty_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class String>
|
template <class String>
|
||||||
@@ -541,13 +503,13 @@ size_t replace_first(const string& text, const string& target, const string& rep
|
|||||||
namespace detail
|
namespace detail
|
||||||
{
|
{
|
||||||
template <class Container, class String>
|
template <class Container, class String>
|
||||||
String join_worker(const Container& container, const String& delimiter) noexcept
|
String join_worker(const Container& container, const String& sep) noexcept
|
||||||
{
|
{
|
||||||
String dst;
|
String dst;
|
||||||
//reserve some space based on some heuristics:
|
//reserve some space based on some heuristics:
|
||||||
// We'll have size - 1 delimiters
|
// We'll have size - 1 seps
|
||||||
// And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant.
|
// And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant.
|
||||||
dst.reserve((delimiter.size() + 2) * container.size());
|
dst.reserve((sep.size() + 2) * container.size());
|
||||||
|
|
||||||
auto end = std::end(container);
|
auto end = std::end(container);
|
||||||
auto it = std::begin(container);
|
auto it = std::begin(container);
|
||||||
@@ -558,19 +520,19 @@ String join_worker(const Container& container, const String& delimiter) noexcept
|
|||||||
if (it == end)
|
if (it == end)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
dst += delimiter;
|
dst += sep;
|
||||||
}
|
}
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Container>
|
template <class Container>
|
||||||
string join_worker(const Container& container, const string& delimiter) noexcept
|
string join_worker(const Container& container, const string& sep) noexcept
|
||||||
{
|
{
|
||||||
eastl::string buffer;
|
eastl::string buffer;
|
||||||
//reserve some space based on some heuristics:
|
//reserve some space based on some heuristics:
|
||||||
// We'll have size - 1 delimiters
|
// We'll have size - 1 seps
|
||||||
// And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant.
|
// And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant.
|
||||||
buffer.reserve((delimiter.size() + 2) * container.size());
|
buffer.reserve((sep.size() + 2) * container.size());
|
||||||
|
|
||||||
auto end = std::end(container);
|
auto end = std::end(container);
|
||||||
auto it = std::begin(container);
|
auto it = std::begin(container);
|
||||||
@@ -582,41 +544,41 @@ string join_worker(const Container& container, const string& delimiter) noexcept
|
|||||||
if (it == end)
|
if (it == end)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
buffer.append(delimiter.data(), delimiter.size());
|
buffer.append(sep.data(), sep.size());
|
||||||
}
|
}
|
||||||
return string(buffer);
|
return string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
//joiner used when the Delim and String are of the same type
|
//joiner used when the Sep and String are of the same type
|
||||||
template <class Container, class Delim, class String>
|
template <class Container, class Sep, class String>
|
||||||
std::enable_if_t<
|
std::enable_if_t<
|
||||||
std::is_same_v<
|
std::is_same_v<
|
||||||
std::remove_const_t<std::remove_reference_t<String>>,
|
std::remove_const_t<std::remove_reference_t<String>>,
|
||||||
std::remove_const_t<std::remove_reference_t<Delim>>>,
|
std::remove_const_t<std::remove_reference_t<Sep>>>,
|
||||||
String> join(const Container& container, const Delim& delimiter) noexcept
|
String> join(const Container& container, const Sep& sep) noexcept
|
||||||
{
|
{
|
||||||
return join_worker(container, delimiter);
|
return join_worker(container, sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
//joiner used when the Delim and String are different types
|
//joiner used when the Sep and String are different types
|
||||||
template <class Container, class Delim, class String>
|
template <class Container, class Sep, class String>
|
||||||
std::enable_if_t<
|
std::enable_if_t<
|
||||||
!std::is_same_v<
|
!std::is_same_v<
|
||||||
std::remove_const_t<std::remove_reference_t<String>>,
|
std::remove_const_t<std::remove_reference_t<String>>,
|
||||||
std::remove_const_t<std::remove_reference_t<Delim>>>,
|
std::remove_const_t<std::remove_reference_t<Sep>>>,
|
||||||
String> join(const Container& container, const Delim& delimiter) noexcept
|
String> join(const Container& container, const Sep& sep) noexcept
|
||||||
{
|
{
|
||||||
return join_worker(container, String(delimiter));
|
return join_worker(container, String(sep));
|
||||||
}
|
}
|
||||||
} //namespace detail
|
} //namespace detail
|
||||||
|
|
||||||
template <class Container, class Delim, class String>
|
template <class Container, class Sep, class String>
|
||||||
String join(const Container& container, const Delim& delimiter) noexcept
|
String join(const Container& container, const Sep& sep) noexcept
|
||||||
{
|
{
|
||||||
if (container.empty())
|
if (container.empty())
|
||||||
return String();
|
return String();
|
||||||
|
|
||||||
return detail::join<Container, Delim, String>(container, delimiter);
|
return detail::join<Container, Sep, String>(container, sep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -113,7 +113,7 @@ extern thread_local int32_t s_ptr_checking_disabled;
|
|||||||
#define CB_LENT_DEC_REF() \
|
#define CB_LENT_DEC_REF() \
|
||||||
if (m_cb) [[likely]] \
|
if (m_cb) [[likely]] \
|
||||||
{ \
|
{ \
|
||||||
bool is_dangling = m_cb->unique_count.load(tl::memory_order_acquire) == 0; \
|
const bool is_dangling = m_cb->unique_count.load(tl::memory_order_acquire) == 0; \
|
||||||
if (is_dangling && is_ptr_checking_enabled()) [[unlikely]] \
|
if (is_dangling && is_ptr_checking_enabled()) [[unlikely]] \
|
||||||
TL_FAIL("lent: {} dangling lent ptrs detected", m_cb->lent_count.load(tl::memory_order_acquire)); \
|
TL_FAIL("lent: {} dangling lent ptrs detected", m_cb->lent_count.load(tl::memory_order_acquire)); \
|
||||||
if (m_cb->lent_count.fetch_sub(1, tl::memory_order_acquire) == 1) /* last one? cleanup */ \
|
if (m_cb->lent_count.fetch_sub(1, tl::memory_order_acquire) == 1) /* last one? cleanup */ \
|
||||||
@@ -144,7 +144,7 @@ extern thread_local int32_t s_ptr_checking_disabled;
|
|||||||
#define CB_UNIQUE_DEC_REF() \
|
#define CB_UNIQUE_DEC_REF() \
|
||||||
if (m_cb && m_cb->unique_count.fetch_sub(1, tl::memory_order_acquire) == 1) [[likely]] \
|
if (m_cb && m_cb->unique_count.fetch_sub(1, tl::memory_order_acquire) == 1) [[likely]] \
|
||||||
{ \
|
{ \
|
||||||
int32_t count = m_cb->lent_count.load(tl::memory_order_acquire); \
|
const int32_t count = m_cb->lent_count.load(tl::memory_order_acquire); \
|
||||||
if (count == 0) [[likely]] \
|
if (count == 0) [[likely]] \
|
||||||
delete m_cb; \
|
delete m_cb; \
|
||||||
else if (is_ptr_checking_enabled()) [[likely]] \
|
else if (is_ptr_checking_enabled()) [[likely]] \
|
||||||
|
|||||||
Reference in New Issue
Block a user