#pragma once
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <fmt/core.h>
#include <fmt/format.h>
#include <limits>
#include <new>
#include <string>
#include <string_view>
#include "memory_resource.hpp"
#include "pointer.hpp"
namespace ddwaf {
class owned_object;
class dynamic_string {
public:
using size_type = uint32_t;
dynamic_string() = default;
explicit dynamic_string(size_type capacity,
nonnull_ptr<memory::memory_resource> alloc = memory::get_default_resource())
: alloc_(alloc)
{
ensure_spare_capacity(capacity);
}
dynamic_string(const char *str, size_type size,
nonnull_ptr<memory::memory_resource> alloc = memory::get_default_resource())
: alloc_(alloc)
{
ensure_spare_capacity(size);
if (size != 0) {
memcpy(buffer_, str, size);
size_ = size;
}
}
dynamic_string(std::string_view str,
nonnull_ptr<memory::memory_resource> alloc = memory::get_default_resource())
: dynamic_string(str.data(), str.size(), alloc)
{}
dynamic_string(
char *ptr, size_type size, size_type capacity, nonnull_ptr<memory::memory_resource> alloc)
: alloc_(alloc), buffer_(ptr), size_(size), capacity_(capacity)
{}
dynamic_string(const char *str) = delete;
template <typename T>
dynamic_string(
T str, nonnull_ptr<memory::memory_resource> alloc = memory::get_default_resource())
requires std::is_convertible_v<T, std::string_view>
: dynamic_string(std::string_view{str}, alloc)
{}
~dynamic_string()
{
if (buffer_ != nullptr) {
alloc_->deallocate(buffer_, capacity_, alignof(char));
buffer_ = nullptr;
size_ = capacity_ = 0;
}
}
dynamic_string(const dynamic_string &str) : dynamic_string(str.data(), str.size(), str.alloc())
{}
dynamic_string &operator=(const dynamic_string &str)
{
if (this == &str) {
return *this;
}
return (*this = dynamic_string(str.data(), str.size(), str.alloc()));
}
dynamic_string(dynamic_string &&other) noexcept
: alloc_(other.alloc_), buffer_(other.buffer_), size_(other.size_),
capacity_(other.capacity_)
{
other.buffer_ = nullptr;
other.size_ = other.capacity_ = 0;
}
dynamic_string &operator=(dynamic_string &&other) noexcept
{
if (buffer_ != nullptr) {
alloc_->deallocate(buffer_, capacity_, alignof(char));
}
alloc_ = other.alloc_;
buffer_ = other.buffer_;
size_ = other.size_;
capacity_ = other.capacity_;
other.buffer_ = nullptr;
other.size_ = other.capacity_ = 0;
return *this;
}
owned_object to_object(nonnull_ptr<memory::memory_resource> alloc);
[[nodiscard]] size_type size() const noexcept { return size_; }
[[nodiscard]] bool empty() const noexcept { return size_ == 0; }
[[nodiscard]] size_type capacity() const noexcept { return capacity_; }
[[nodiscard]] const char *data() const noexcept { return buffer_; }
[[nodiscard]] nonnull_ptr<memory::memory_resource> alloc() const noexcept { return alloc_; }
char &operator[](size_type pos) { return buffer_[pos]; }
void append(std::string_view str)
{
ensure_spare_capacity(str.size());
if (!str.empty()) [[likely]] {
memcpy(&buffer_[size_], str.data(), str.size());
size_ += str.size();
}
}
void append(char c)
{
ensure_spare_capacity(1);
buffer_[size_++] = c;
}
void resize(size_type count)
{
if (count > size_) {
ensure_spare_capacity(count - size_);
}
size_ = count;
}
void resize(size_type count, char c)
{
if (count > size_) {
ensure_spare_capacity(count - size_);
memset(&buffer_[size_], c, count - size_);
}
size_ = count;
}
void reserve(size_type count) { ensure_spare_capacity(count); }
void clear() { size_ = 0; }
operator std::string_view() const noexcept { return {buffer_, size_}; }
explicit operator std::string() const { return {buffer_, size_}; }
bool operator==(const dynamic_string &other) const noexcept
{
return size_ == other.size_ && (size_ == 0 || memcmp(buffer_, other.buffer_, size_) == 0);
}
protected:
void ensure_spare_capacity(size_type at_least)
{
if (at_least > (capacity_ - size_)) {
auto required_mem = at_least - (capacity_ - size_);
auto available_mem = std::numeric_limits<size_type>::max() - capacity_;
if (available_mem < required_mem) {
throw std::bad_alloc{};
}
auto new_capacity =
capacity_ + std::min(std::max(capacity_, required_mem), available_mem);
char *new_buffer = static_cast<char *>(alloc_->allocate(new_capacity, alignof(char)));
if (buffer_ != nullptr) {
memcpy(new_buffer, buffer_, size_);
alloc_->deallocate(buffer_, capacity_, alignof(char));
}
buffer_ = new_buffer;
capacity_ = new_capacity;
}
}
nonnull_ptr<memory::memory_resource> alloc_{memory::get_default_resource()};
char *buffer_{nullptr};
size_type size_{0};
size_type capacity_{0};
};
template <> struct fmt::formatter<dynamic_string> : fmt::formatter<std::string_view> {
template <typename FormatContext> auto format(const dynamic_string &d, FormatContext &ctx) const
{
return fmt::formatter<std::string_view>::format(std::string_view{d.data(), d.size()}, ctx);
}
};
}