Skip to content

Commit

Permalink
Improved async_string with more methods and std::format support (#1536)
Browse files Browse the repository at this point in the history
* Improved async_string with more methods and std::format support

* Update async_string.hpp

* resize and reserve
  • Loading branch information
stephenberry authored Jan 2, 2025
1 parent e652b11 commit 4e4446e
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 47 deletions.
1 change: 1 addition & 0 deletions include/glaze/concepts/container_concepts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <ranges>
#include <utility>
#include <vector>
#include <version>

// Over time we want most concepts to use the nomenclature:
// is_
Expand Down
133 changes: 105 additions & 28 deletions include/glaze/thread/async_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <string>
#include <string_view>
#include <utility>
#include <version>

#include "glaze/core/common.hpp"

Expand All @@ -18,9 +19,11 @@ namespace glz
{
struct async_string
{
private:
std::string str;
mutable std::shared_mutex mutex;

public:
async_string() = default;
async_string(const char* s) : str(s) {}
async_string(const std::string& s) : str(s) {}
Expand Down Expand Up @@ -98,8 +101,13 @@ namespace glz
proxy(std::string& p, std::unique_lock<std::shared_mutex>&& lock) noexcept : ptr{&p}, lock(std::move(lock)) {}

std::string* operator->() noexcept { return ptr; }
const std::string* operator->() const noexcept { return ptr; }

std::string& operator*() noexcept { return *ptr; }
const std::string& operator*() const noexcept { return *ptr; }

std::string& value() noexcept { return *ptr; }
const std::string& value() const noexcept { return *ptr; }
};

proxy write() { return {str, std::unique_lock{mutex}}; }
Expand All @@ -117,6 +125,8 @@ namespace glz
const std::string* operator->() const noexcept { return ptr; }

const std::string& operator*() const noexcept { return *ptr; }

const std::string& value() const noexcept { return *ptr; }
};

const_proxy read() const { return {str, std::shared_lock{mutex}}; }
Expand Down Expand Up @@ -159,17 +169,19 @@ namespace glz
str.pop_back();
}

async_string& append(const std::string& s)
template <class RHS>
requires(std::same_as<std::remove_cvref_t<RHS>, std::string>)
async_string& append(RHS&& s)
{
std::unique_lock lock(mutex);
str.append(s);
str.append(std::forward<RHS>(s));
return *this;
}

async_string& append(const char* s)
async_string& append(const char* s, size_t count)
{
std::unique_lock lock(mutex);
str.append(s);
str.append(s, count);
return *this;
}

Expand All @@ -179,10 +191,21 @@ namespace glz
str.append(sv);
return *this;
}

template <class RHS>
requires(std::same_as<std::remove_cvref_t<RHS>, async_string>)
async_string& append(RHS&& other)
{
std::unique_lock lock(mutex, std::defer_lock);
std::shared_lock lock2(other.mutex, std::defer_lock);
std::lock(lock, lock2);
str.append(other.str);
return *this;
}

async_string& operator+=(const std::string& s) { return append(s); }

async_string& operator+=(const char* s) { return append(s); }
template <class RHS>
requires(std::same_as<std::remove_cvref_t<RHS>, std::string>)
async_string& operator+=(RHS&& s) { return append(std::forward<RHS>(s)); }

async_string& operator+=(const std::string_view& sv) { return append(sv); }

Expand All @@ -192,6 +215,23 @@ namespace glz
str += c;
return *this;
}

void reserve(size_t count) {
std::unique_lock lock(mutex);
str.reserve(count);
}

void resize(size_t count)
{
std::unique_lock lock(mutex);
str.resize(count);
}

void resize(size_t count, const char ch)
{
std::unique_lock lock(mutex);
str.resize(count, ch);
}

// Element access
char at(size_t pos) const
Expand Down Expand Up @@ -220,34 +260,52 @@ namespace glz

int compare(const async_string& other) const
{
std::shared_lock lock1(mutex, std::defer_lock);
std::shared_lock lock2(other.mutex, std::defer_lock);
std::lock(lock1, lock2);
std::scoped_lock lock{mutex, other.mutex};
return str.compare(other.str);
}

bool starts_with(const std::string_view other) const
{
std::shared_lock lock{mutex};
return str.starts_with(other);
}

bool ends_with(const std::string_view other) const
{
std::shared_lock lock{mutex};
return str.ends_with(other);
}

std::string substr(size_t pos = 0, size_t len = std::string::npos) const
{
std::shared_lock lock{mutex};
return str.substr(pos, len);
}

// Obtain a copy
std::string string() const
friend bool operator==(const async_string& lhs, const std::string_view rhs)
{
std::shared_lock lock(mutex);
return str;
std::scoped_lock lock{lhs.mutex};
return lhs.str == rhs;
}

friend bool operator==(const async_string& lhs, const async_string& rhs)
template <class RHS>
requires(std::same_as<std::remove_cvref_t<RHS>, async_string>)
friend bool operator==(const async_string& lhs, RHS&& rhs)
{
std::shared_lock lock1(lhs.mutex, std::defer_lock);
std::shared_lock lock2(rhs.mutex, std::defer_lock);
std::lock(lock1, lock2);
std::scoped_lock lock{lhs.mutex, rhs.mutex};
return lhs.str == rhs.str;
}

friend bool operator!=(const async_string& lhs, const std::string_view rhs) { return !(lhs == rhs); }

friend bool operator!=(const async_string& lhs, const async_string& rhs) { return !(lhs == rhs); }
template <class RHS>
requires(std::same_as<std::remove_cvref_t<RHS>, async_string>)
friend bool operator!=(const async_string& lhs, RHS&& rhs) { return !(lhs == rhs); }

friend bool operator<(const async_string& lhs, const async_string& rhs)
{
std::shared_lock lock1(lhs.mutex, std::defer_lock);
std::shared_lock lock2(rhs.mutex, std::defer_lock);
std::lock(lock1, lock2);
std::scoped_lock lock{lhs.mutex, rhs.mutex};
return lhs.str < rhs.str;
}

Expand All @@ -260,9 +318,7 @@ namespace glz
void swap(async_string& other)
{
if (this == &other) return;
std::unique_lock lock1(mutex, std::defer_lock);
std::unique_lock lock2(other.mutex, std::defer_lock);
std::lock(lock1, lock2);
std::scoped_lock lock{mutex, other.mutex};
str.swap(other.str);
}

Expand All @@ -279,8 +335,8 @@ namespace glz::detail
template <auto Opts>
static void op(auto&& value, is_context auto&& ctx, auto&& it, auto&& end) noexcept
{
std::shared_lock lock{value.mutex};
read<Format>::template op<Opts>(value.str, ctx, it, end);
auto proxy = value.write();
read<Format>::template op<Opts>(*proxy, ctx, it, end);
}
};

Expand All @@ -290,8 +346,29 @@ namespace glz::detail
template <auto Opts>
static void op(auto&& value, is_context auto&& ctx, auto&&... args) noexcept
{
std::unique_lock lock{value.mutex};
write<Format>::template op<Opts>(value.str, ctx, args...);
auto proxy = value.read();
write<Format>::template op<Opts>(*proxy, ctx, args...);
}
};
}

// Allow formatting via std::format
#ifdef __cpp_lib_format
#include <format>
namespace std
{
template <>
struct formatter<glz::async_string>
{
std::formatter<std::string> formatter;

constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { return formatter.parse(ctx); }

template <class FormatContext>
auto format(const glz::async_string& s, FormatContext& ctx) const -> decltype(ctx.out())
{
return formatter.format(*s.read(), ctx);
}
};
}
#endif
Loading

0 comments on commit 4e4446e

Please sign in to comment.