1
+ #ifndef DICE_TEMPLATELIBRARY_CONSTSTRING_HPP
2
+ #define DICE_TEMPLATELIBRARY_CONSTSTRING_HPP
3
+
4
+ #include < cassert>
5
+ #include < cstring>
6
+ #include < memory>
7
+ #include < ostream>
8
+ #include < string_view>
9
+ #include < utility>
10
+
11
+ namespace dice ::template_library {
12
+
13
+ /* *
14
+ * A constant-size (i.e. non-growing), heap-allocated string type
15
+ * Behaves like std::string except that it cannot grow and therefore occupies 1 less word in memory (it has no capacity field).
16
+ */
17
+ template <typename Char, typename Traits = std::char_traits<Char>, typename Allocator = std::allocator<Char>>
18
+ struct basic_static_string {
19
+ using value_type = Char;
20
+ using traits_type = Traits;
21
+ using allocator_type = Allocator;
22
+ using size_type = size_t ;
23
+ using different_type = std::ptrdiff_t ;
24
+ using pointer = typename std::allocator_traits<allocator_type>::pointer;
25
+ using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
26
+ using view_type = std::basic_string_view<Char, Traits>;
27
+ using iterator = value_type *;
28
+ using const_iterator = value_type const *;
29
+ using reverse_iterator = std::reverse_iterator<iterator>;
30
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
31
+
32
+ private:
33
+ pointer data_;
34
+ size_type size_;
35
+ [[no_unique_address]] allocator_type alloc_;
36
+
37
+ static constexpr void swap_data (basic_static_string &a, basic_static_string &b) noexcept {
38
+ using std::swap;
39
+ swap (a.data_ , b.data_ );
40
+ swap (a.size_ , b.size_ );
41
+ }
42
+
43
+ void copy_assign_from_other (basic_static_string const &other) {
44
+ if (size_ != 0 ) {
45
+ std::allocator_traits<allocator_type>::deallocate (alloc_, data_, size_);
46
+ }
47
+
48
+ size_ = other.size_ ;
49
+
50
+ if (size_ != 0 ) {
51
+ data_ = std::allocator_traits<allocator_type>::allocate (alloc_, size_);
52
+ std::memcpy (std::to_address (data_), std::to_address (other.data_ ), size_);
53
+ } else {
54
+ data_ = nullptr ;
55
+ }
56
+ }
57
+
58
+ public:
59
+ constexpr basic_static_string (allocator_type const &alloc = allocator_type{}) noexcept
60
+ : data_{nullptr }, size_{0 }, alloc_{alloc} {
61
+ }
62
+
63
+ explicit basic_static_string (view_type const sv, allocator_type const &alloc = allocator_type{})
64
+ : size_{sv.size ()}, alloc_{alloc} {
65
+
66
+ if (sv.empty ()) {
67
+ data_ = nullptr ;
68
+ return ;
69
+ }
70
+
71
+ data_ = std::allocator_traits<allocator_type>::allocate (alloc_, size_);
72
+ std::memcpy (std::to_address (data_), sv.data (), size_);
73
+ }
74
+
75
+ basic_static_string (basic_static_string const &other) : basic_static_string{static_cast <view_type>(other), other.alloc_ } {
76
+ }
77
+
78
+ basic_static_string &operator =(basic_static_string const &other) {
79
+ if (this == &other) {
80
+ return *this ;
81
+ }
82
+
83
+ if constexpr (std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value) {
84
+ alloc_ = other.alloc_ ;
85
+ }
86
+
87
+ copy_assign_from_other (other);
88
+ return *this ;
89
+ }
90
+
91
+ constexpr basic_static_string (basic_static_string &&other) noexcept : data_{std::exchange (other.data_ , nullptr )},
92
+ size_{std::exchange (other.size_ , 0 )},
93
+ alloc_{std::move (other.alloc_ )} {
94
+ }
95
+
96
+ basic_static_string &operator =(basic_static_string &&other) noexcept (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
97
+ || std::allocator_traits<allocator_type>::is_always_equal::value) {
98
+ assert (this != &other);
99
+
100
+ if constexpr (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value) {
101
+ swap (*this , other);
102
+ return *this ;
103
+ } else if constexpr (std::allocator_traits<allocator_type>::is_always_equal::value) {
104
+ swap_data (*this , other);
105
+ return *this ;
106
+ } else {
107
+ if (alloc_ == other.alloc_ ) [[likely]] {
108
+ swap_data (*this , other);
109
+ return *this ;
110
+ }
111
+
112
+ // alloc_ != other.alloc_ and not allowed to propagate, need to copy
113
+ copy_assign_from_other (other);
114
+ return *this ;
115
+ }
116
+ }
117
+
118
+ ~basic_static_string () {
119
+ if (data_ != nullptr ) {
120
+ std::allocator_traits<allocator_type>::deallocate (alloc_, data_, size_);
121
+ }
122
+ }
123
+
124
+ operator view_type () const noexcept {
125
+ return {std::to_address (data_), size_};
126
+ }
127
+
128
+ [[nodiscard]] const_pointer data () const noexcept {
129
+ return data_;
130
+ }
131
+ [[nodiscard]] pointer data () noexcept {
132
+ return data_;
133
+ }
134
+
135
+ [[nodiscard]] bool empty () const noexcept {
136
+ return size_ == 0 ;
137
+ }
138
+
139
+ [[nodiscard]] size_type size () const noexcept {
140
+ return size_;
141
+ }
142
+
143
+ [[nodiscard]] value_type operator [](size_type const ix) const noexcept {
144
+ assert (ix < size ());
145
+ return std::to_address (data_)[ix];
146
+ }
147
+
148
+ [[nodiscard]] value_type &operator [](size_type const ix) noexcept {
149
+ assert (ix < size ());
150
+ return std::to_address (data_)[ix];
151
+ }
152
+
153
+ [[nodiscard]] value_type front () const noexcept {
154
+ assert (size () > 0 );
155
+ return (*this )[0 ];
156
+ }
157
+
158
+ [[nodiscard]] value_type &front () noexcept {
159
+ assert (size () > 0 );
160
+ return (*this )[0 ];
161
+ }
162
+
163
+ [[nodiscard]] value_type back () const noexcept {
164
+ assert (size () > 0 );
165
+ return (*this )[size () - 1 ];
166
+ }
167
+
168
+ [[nodiscard]] value_type &back () noexcept {
169
+ assert (size () > 0 );
170
+ return (*this )[size () - 1 ];
171
+ }
172
+
173
+ [[nodiscard]] const_iterator begin () const noexcept {
174
+ return std::to_address (data_);
175
+ }
176
+ [[nodiscard]] const_iterator end () const noexcept {
177
+ return std::to_address (data_) + size_;
178
+ }
179
+
180
+ [[nodiscard]] const_iterator cbegin () const noexcept {
181
+ return begin ();
182
+ }
183
+ [[nodiscard]] const_iterator cend () const noexcept {
184
+ return end ();
185
+ }
186
+
187
+ [[nodiscard]] iterator begin () noexcept {
188
+ return std::to_address (data_);
189
+ }
190
+ [[nodiscard]] iterator end () noexcept {
191
+ return std::to_address (data_) + size_;
192
+ }
193
+
194
+ [[nodiscard]] const_reverse_iterator rbegin () const noexcept {
195
+ return const_reverse_iterator{end ()};
196
+ }
197
+ [[nodiscard]] const_reverse_iterator rend () const noexcept {
198
+ return const_reverse_iterator{begin ()};
199
+ }
200
+
201
+ [[nodiscard]] const_reverse_iterator crbegin () const noexcept {
202
+ return rbegin ();
203
+ }
204
+ [[nodiscard]] const_reverse_iterator crend () const noexcept {
205
+ return rend ();
206
+ }
207
+
208
+ [[nodiscard]] reverse_iterator rbegin () noexcept {
209
+ return reverse_iterator{end ()};
210
+ }
211
+ [[nodiscard]] reverse_iterator rend () noexcept {
212
+ return reverse_iterator{begin ()};
213
+ }
214
+
215
+ friend void swap (basic_static_string &a, basic_static_string &b) noexcept {
216
+ using std::swap;
217
+ swap (a.data_ , b.data_ );
218
+ swap (a.size_ , b.size_ );
219
+ swap (a.alloc_ , b.alloc_ );
220
+ }
221
+
222
+ bool operator ==(basic_static_string const &other) const noexcept {
223
+ return static_cast <view_type>(*this ) == static_cast <view_type>(other);
224
+ }
225
+
226
+ auto operator <=>(basic_static_string const &other) const noexcept {
227
+ return static_cast <view_type>(*this ) <=> static_cast <view_type>(other);
228
+ }
229
+
230
+ friend bool operator ==(basic_static_string const &self, view_type const other) noexcept {
231
+ return static_cast <view_type>(self) == other;
232
+ }
233
+
234
+ friend auto operator <=>(basic_static_string const &self, view_type const other) noexcept {
235
+ return static_cast <view_type>(self) <=> other;
236
+ }
237
+ };
238
+
239
+ template <typename Char, typename CharTraits, typename Allocator>
240
+ std::basic_ostream<Char, CharTraits> &operator <<(std::basic_ostream<Char, CharTraits> &os, basic_static_string<Char, CharTraits, Allocator> const &str) {
241
+ os << static_cast <std::basic_string_view<Char, CharTraits>>(str);
242
+ return os;
243
+ }
244
+
245
+ using static_string = basic_static_string<char >;
246
+ using static_wstring = basic_static_string<wchar_t >;
247
+ using static_u8string = basic_static_string<char8_t >;
248
+ using static_u16string = basic_static_string<char16_t >;
249
+ using static_u32string = basic_static_string<char32_t >;
250
+
251
+ } // namespace dice::template_library
252
+
253
+ #endif // DICE_TEMPLATELIBRARY_CONSTSTRING_HPP
0 commit comments