summaryrefslogtreecommitdiff
path: root/asl/strings/string_builder.hpp
blob: b983bc9ed398428cfcf35358b20f82b3999be9ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#pragma once

#include "asl/containers/buffer.hpp"
#include "asl/strings/string.hpp"
#include "asl/strings/string_view.hpp"
#include "asl/formatting/format.hpp"
#include "asl/io/writer.hpp"

namespace asl
{

template<allocator Allocator = DefaultAllocator>
class StringBuilder
{
    buffer<char, Allocator> m_buffer;

public:
    constexpr StringBuilder() requires default_constructible<Allocator> = default;
    explicit constexpr StringBuilder(Allocator allocator) : m_buffer{ASL_MOVE(allocator)} {}

    constexpr ~StringBuilder() = default;

    constexpr StringBuilder(const StringBuilder&) requires copy_constructible<Allocator> = default;
    constexpr StringBuilder(StringBuilder&&) = default;

    constexpr StringBuilder& operator=(const StringBuilder&) requires copy_assignable<Allocator> = default;
    constexpr StringBuilder& operator=(StringBuilder&&) = default;

    constexpr string_view as_string_view() const
    {
        auto span = m_buffer.as_span();
        return string_view{span.data(), span.size()};
    }

    void reset()
    {
        m_buffer.clear();
    }

    auto push(this auto&& self, string_view sv) -> decltype(self)
        requires (!is_const<un_ref_t<decltype(self)>>)
    {
        isize_t old_size = self.m_buffer.size();
        self.m_buffer.resize_zero(old_size + sv.size());
        // NOLINTNEXTLINE(*-pointer-arithmetic)
        asl::memcpy(self.m_buffer.data() + old_size, sv.data(), sv.size());
        return ASL_FWD(self);
    }

    auto push(this auto&& self, char c) -> decltype(self)
        requires (!is_const<un_ref_t<decltype(self)>>)
    {
        self.m_buffer.push(c);
        return ASL_FWD(self);
    }

    string<Allocator> finish() &&
    {
        return string<Allocator>{ASL_MOVE(m_buffer)};
    }

    template<allocator StringAllocator = Allocator>
    string<StringAllocator> as_string() const
        requires default_constructible<StringAllocator>
    {
        return string<StringAllocator>{as_string_view()};
    }

    template<allocator StringAllocator = Allocator>
    string<StringAllocator> as_string(Allocator allocator) const
    {
        return string<StringAllocator>{as_string_view(), ASL_MOVE(allocator)};
    }
};

StringBuilder() -> StringBuilder<>;

template<typename Allocator = DefaultAllocator>
class StringWriter : public asl::Writer
{
    StringBuilder<Allocator> m_builder;

public:
    constexpr StringWriter() requires default_constructible<Allocator> = default;
    explicit constexpr StringWriter(Allocator allocator) : m_builder{ASL_MOVE(allocator)} {}

    constexpr ~StringWriter() override = default;

    constexpr StringWriter(const StringWriter&) requires copy_constructible<Allocator> = default;
    constexpr StringWriter(StringWriter&&) = default;

    constexpr StringWriter& operator=(const StringWriter&) requires copy_assignable<Allocator> = default;
    constexpr StringWriter& operator=(StringWriter&&) = default;

    void write(span<const byte> str) override
    {
        m_builder.push(string_view{reinterpret_cast<const char*>(str.data()), str.size()});
    }

    constexpr string_view as_string_view() const
    {
        return m_builder.as_string_view();
    }

    string<Allocator> finish() &&
    {
        return ASL_MOVE(m_builder).finish();
    }

    template<allocator StringAllocator = Allocator>
    string<StringAllocator> as_string() const
        requires default_constructible<StringAllocator>
    {
        return m_builder.as_string();
    }

    template<allocator StringAllocator = Allocator>
    string<StringAllocator> as_string(Allocator allocator) const
    {
        return m_builder.as_string(ASL_MOVE(allocator));
    }
};

StringWriter() -> StringWriter<>;

template<allocator Allocator = DefaultAllocator>
string<Allocator> format_to_string(string_view fmt, const formattable auto&... args)
    requires default_constructible<Allocator>
{
    StringWriter writer{};
    format(&writer, fmt, args...);
    return ASL_MOVE(writer).finish();
}

template<allocator Allocator = DefaultAllocator>
string<Allocator> format_to_string(Allocator allocator, string_view fmt, const formattable auto&... args)
{
    StringWriter writer{ASL_MOVE(allocator)};
    format(&writer, fmt, args...);
    return ASL_MOVE(writer).finish();
}

} // namespace asl