/src/stdplus/include/stdplus/str/buf.hpp
Line | Count | Source |
1 | | #pragma once |
2 | | |
3 | | #include <bit> |
4 | | #include <cstdint> |
5 | | #include <limits> |
6 | | #include <memory> |
7 | | #include <string_view> |
8 | | |
9 | | namespace stdplus |
10 | | { |
11 | | |
12 | | namespace detail |
13 | | { |
14 | | |
15 | | template <typename CharT, std::size_t BufLen, auto Endian = std::endian::native> |
16 | | union StrBufStore; |
17 | | |
18 | | template <typename CharT, std::size_t BufLen> |
19 | | union StrBufStore<CharT, BufLen, std::endian::little> |
20 | | { |
21 | | struct Inl |
22 | | { |
23 | | CharT ptr[BufLen]; |
24 | | std::make_unsigned_t<CharT> len; |
25 | | static_assert(BufLen <= std::numeric_limits<decltype(len)>::max() / 2); |
26 | | } inl; |
27 | | struct Dyn |
28 | | { |
29 | | std::uint8_t rsvd[offsetof(Inl, len) + 1 - sizeof(CharT*) - |
30 | | sizeof(std::size_t) * 2]; |
31 | | CharT* ptr; |
32 | | std::size_t cap; |
33 | | std::size_t len; |
34 | | } dyn; |
35 | | static_assert(offsetof(Inl, len) + sizeof(Inl::len) == |
36 | | offsetof(Dyn, len) + sizeof(Dyn::len)); |
37 | | }; |
38 | | |
39 | | template <typename CharT, std::size_t BufLen> |
40 | | union StrBufStore<CharT, BufLen, std::endian::big> |
41 | | { |
42 | | struct Inl |
43 | | { |
44 | | std::make_unsigned_t<CharT> len; |
45 | | CharT ptr[BufLen]; |
46 | | static_assert(BufLen <= std::numeric_limits<decltype(len)>::max() / 2); |
47 | | } inl; |
48 | | struct Dyn |
49 | | { |
50 | | std::size_t len; |
51 | | std::size_t cap; |
52 | | CharT* ptr; |
53 | | } dyn; |
54 | | }; |
55 | | |
56 | | template <typename CharT, std::size_t ObjSize, typename Allocator> |
57 | | struct StrBufAS : Allocator |
58 | | { |
59 | | static inline constexpr std::size_t buf_len = |
60 | | (ObjSize - |
61 | | sizeof(std::declval<detail::StrBufStore<CharT, 63>>().inl.len) - |
62 | | (std::is_empty_v<Allocator> ? 0 : sizeof(Allocator))) / |
63 | | sizeof(CharT); |
64 | | using Store = detail::StrBufStore<CharT, buf_len>; |
65 | | static_assert(sizeof(std::declval<Store>().inl) >= |
66 | | sizeof(std::declval<Store>().dyn)); |
67 | | |
68 | | static inline constexpr auto dyn_mask = |
69 | | std::size_t{1} << ((sizeof(std::size_t) << 3) - 1); |
70 | | static inline constexpr auto inl_mask = |
71 | | decltype(Store::Inl::len){1} << ((sizeof(Store::Inl::len) << 3) - 1); |
72 | | |
73 | | Store store; |
74 | | |
75 | | constexpr bool isDyn() const noexcept |
76 | 90.0k | { |
77 | 90.0k | if (std::is_constant_evaluated()) |
78 | 0 | { |
79 | 0 | return true; |
80 | 0 | } |
81 | 90.0k | return store.inl.len & inl_mask; |
82 | 90.0k | } |
83 | | |
84 | | constexpr std::size_t dynLen() const noexcept |
85 | 0 | { |
86 | 0 | return store.dyn.len & ~dyn_mask; |
87 | 0 | } |
88 | | |
89 | | constexpr void dynLen(std::size_t len) noexcept |
90 | 0 | { |
91 | 0 | store.dyn.len = len | dyn_mask; |
92 | 0 | } |
93 | | |
94 | | constexpr auto inlLen() const noexcept |
95 | 87.2k | { |
96 | 87.2k | return store.inl.len; |
97 | 87.2k | } |
98 | | |
99 | | constexpr void inlLen(decltype(Store::Inl::len) len) noexcept |
100 | 87.2k | { |
101 | 87.2k | store.inl.len = len; |
102 | 87.2k | } |
103 | | |
104 | | constexpr CharT* allocate(std::size_t n) |
105 | 0 | { |
106 | 0 | auto ptr = static_cast<Allocator&>(*this).allocate(n); |
107 | 0 | if (std::is_constant_evaluated()) |
108 | 0 | { |
109 | 0 | for (std::size_t i = 0; i < n; ++i) |
110 | 0 | { |
111 | 0 | std::construct_at(ptr + i); |
112 | 0 | } |
113 | 0 | } |
114 | 0 | return ptr; |
115 | 0 | } |
116 | | |
117 | | constexpr void zeroInit() noexcept |
118 | 1.39k | { |
119 | 1.39k | if (std::is_constant_evaluated()) |
120 | 0 | { |
121 | 0 | store.dyn.ptr = allocate(buf_len); |
122 | 0 | store.dyn.cap = buf_len; |
123 | 0 | dynLen(0); |
124 | 0 | } |
125 | 1.39k | else |
126 | 1.39k | { |
127 | 1.39k | inlLen(0); |
128 | 1.39k | } |
129 | 1.39k | } |
130 | | |
131 | 1.39k | constexpr StrBufAS(Allocator&& a) noexcept : Allocator(std::move(a)) |
132 | 1.39k | { |
133 | 1.39k | if (std::is_constant_evaluated()) |
134 | 0 | { |
135 | 0 | std::construct_at(&store.dyn); |
136 | 0 | } |
137 | 1.39k | zeroInit(); |
138 | 1.39k | } |
139 | | |
140 | | constexpr StrBufAS& operator=(Allocator&& a) noexcept |
141 | | { |
142 | | *static_cast<Allocator*>(this) = a; |
143 | | return *this; |
144 | | } |
145 | | }; |
146 | | |
147 | | } // namespace detail |
148 | | |
149 | | template <typename CharT, std::size_t ObjSize = 128, |
150 | | typename Allocator = std::allocator<CharT>> |
151 | | class BasicStrBuf |
152 | | { |
153 | | private: |
154 | | detail::StrBufAS<CharT, ObjSize, Allocator> as; |
155 | | |
156 | | template <bool assign> |
157 | | constexpr void inlcopy(const BasicStrBuf& other) noexcept |
158 | | { |
159 | | const auto optr = other.as.store.inl.ptr; |
160 | | const auto olen = other.as.inlLen(); |
161 | | if (assign || std::is_constant_evaluated()) |
162 | | { |
163 | | if (as.isDyn()) |
164 | | { |
165 | | as.dynLen(olen); |
166 | | std::copy(optr, optr + olen, as.store.dyn.ptr); |
167 | | return; |
168 | | } |
169 | | } |
170 | | as.store.inl.len = other.as.store.inl.len; |
171 | | std::copy(optr, optr + olen, as.store.inl.ptr); |
172 | | } |
173 | | |
174 | | template <bool assign> |
175 | | constexpr void move(BasicStrBuf&& other) noexcept |
176 | | { |
177 | | if (!other.as.isDyn()) |
178 | | { |
179 | | inlcopy<assign>(other); |
180 | | other.as.zeroInit(); |
181 | | return; |
182 | | } |
183 | | if (assign || std::is_constant_evaluated()) |
184 | | { |
185 | | if (as.isDyn()) |
186 | | { |
187 | | as.deallocate(as.store.dyn.ptr, as.store.dyn.cap); |
188 | | } |
189 | | } |
190 | | as.store.dyn = other.as.store.dyn; |
191 | | other.as.zeroInit(); |
192 | | } |
193 | | |
194 | | template <bool assign> |
195 | | constexpr void copy(const BasicStrBuf& other) |
196 | | { |
197 | | if (!other.as.isDyn()) |
198 | | { |
199 | | inlcopy<assign>(other); |
200 | | return; |
201 | | } |
202 | | const auto optr = other.as.store.dyn.ptr; |
203 | | const std::size_t olen = other.as.dynLen(); |
204 | | if (assign || std::is_constant_evaluated()) |
205 | | { |
206 | | if (as.isDyn()) |
207 | | { |
208 | | if (olen <= as.store.dyn.cap) |
209 | | { |
210 | | as.store.dyn.len = other.as.store.dyn.len; |
211 | | std::copy(optr, optr + olen, as.store.dyn.ptr); |
212 | | return; |
213 | | } |
214 | | else |
215 | | { |
216 | | as.deallocate(as.store.dyn.ptr, as.store.dyn.cap); |
217 | | } |
218 | | } |
219 | | } |
220 | | if (olen <= as.buf_len) |
221 | | { |
222 | | std::copy(optr, optr + olen, as.store.inl.ptr); |
223 | | as.inlLen(olen); |
224 | | return; |
225 | | } |
226 | | as.store.dyn.cap = other.as.store.dyn.cap; |
227 | | as.store.dyn.ptr = as.allocate(as.store.dyn.cap); |
228 | | as.store.dyn.len = other.as.store.dyn.len; |
229 | | std::copy(optr, optr + olen, as.store.dyn.ptr); |
230 | | } |
231 | | |
232 | | public: |
233 | | using value_type = CharT; |
234 | | |
235 | 1.39k | constexpr BasicStrBuf() noexcept : as({}) {} |
236 | | |
237 | | constexpr BasicStrBuf(BasicStrBuf&& other) noexcept : |
238 | | as(static_cast<Allocator&&>(other.as)) |
239 | | { |
240 | | move</*assign=*/false>(std::move(other)); |
241 | | } |
242 | | |
243 | | constexpr BasicStrBuf(const BasicStrBuf& other) : |
244 | | as(std::allocator_traits<Allocator>:: |
245 | | select_on_container_copy_construction( |
246 | | static_cast<const Allocator&>(other.as))) |
247 | | { |
248 | | copy</*assign=*/false>(other); |
249 | | } |
250 | | |
251 | | constexpr BasicStrBuf& operator=(BasicStrBuf&& other) noexcept |
252 | | { |
253 | | if (this != &other) |
254 | | { |
255 | | if constexpr (typename std::allocator_traits<Allocator>:: |
256 | | propagate_on_container_move_assignment()) |
257 | | { |
258 | | as = static_cast<Allocator&&>(other.as); |
259 | | } |
260 | | move</*assign=*/true>(std::move(other)); |
261 | | } |
262 | | return *this; |
263 | | } |
264 | | |
265 | | constexpr BasicStrBuf& operator=(const BasicStrBuf& other) |
266 | | { |
267 | | if (this != &other) |
268 | | { |
269 | | if constexpr (typename std::allocator_traits<Allocator>:: |
270 | | propagate_on_container_copy_assignment()) |
271 | | { |
272 | | as = static_cast<const Allocator&>(other.as); |
273 | | } |
274 | | copy</*assign=*/true>(other); |
275 | | } |
276 | | return *this; |
277 | | } |
278 | | |
279 | | constexpr ~BasicStrBuf() |
280 | 1.39k | { |
281 | 1.39k | if (as.isDyn()) |
282 | 0 | { |
283 | 0 | as.deallocate(as.store.dyn.ptr, as.store.dyn.cap); |
284 | 0 | } |
285 | 1.39k | } |
286 | | |
287 | | constexpr std::size_t size() const noexcept |
288 | 1.39k | { |
289 | 1.39k | return as.isDyn() ? as.dynLen() : as.inlLen(); |
290 | 1.39k | } |
291 | | |
292 | | constexpr operator std::basic_string_view<CharT>() const noexcept |
293 | 1.39k | { |
294 | 1.39k | return std::basic_string_view<CharT>(begin(), size()); |
295 | 1.39k | } |
296 | | |
297 | | constexpr CharT* append(std::size_t amt) |
298 | 85.8k | { |
299 | 85.8k | if (!as.isDyn()) |
300 | 85.8k | { |
301 | 85.8k | const std::size_t oldlen = as.inlLen(); |
302 | 85.8k | const std::size_t newlen = oldlen + amt; |
303 | 85.8k | if (newlen <= as.buf_len) |
304 | 85.8k | { |
305 | 85.8k | as.inlLen(newlen); |
306 | 85.8k | return as.store.inl.ptr + oldlen; |
307 | 85.8k | } |
308 | 0 | const std::size_t newcap = newlen + (newlen >> 1); |
309 | 0 | const auto ptr = as.allocate(newcap); |
310 | 0 | std::copy(as.store.inl.ptr, as.store.inl.ptr + oldlen, ptr); |
311 | |
|
312 | 0 | as.store.dyn.ptr = ptr; |
313 | 0 | as.store.dyn.cap = newcap; |
314 | 0 | as.dynLen(newlen); |
315 | 0 | return ptr + oldlen; |
316 | 85.8k | } |
317 | 0 | const std::size_t oldlen = as.dynLen(); |
318 | 0 | const std::size_t newlen = oldlen + amt; |
319 | 0 | if (newlen > as.store.dyn.cap) |
320 | 0 | { |
321 | 0 | const std::size_t newcap = newlen + (newlen >> 1); |
322 | 0 | const auto ptr = as.allocate(newcap); |
323 | 0 | std::copy(as.store.dyn.ptr, as.store.dyn.ptr + oldlen, ptr); |
324 | 0 | as.deallocate(as.store.dyn.ptr, as.store.dyn.cap); |
325 | |
|
326 | 0 | as.store.dyn.ptr = ptr; |
327 | 0 | as.store.dyn.cap = newcap; |
328 | 0 | } |
329 | 0 | as.dynLen(newlen); |
330 | 0 | return as.store.dyn.ptr + oldlen; |
331 | 85.8k | } |
332 | | |
333 | | constexpr void append(const CharT* begin, const CharT* end) |
334 | | { |
335 | | std::copy(begin, end, append(end - begin)); |
336 | | } |
337 | | |
338 | | constexpr void push_back(CharT c) noexcept |
339 | 85.8k | { |
340 | 85.8k | *append(1) = c; |
341 | 85.8k | } |
342 | | |
343 | | constexpr void shrink(std::size_t amt) noexcept |
344 | | { |
345 | | if (as.isDyn()) |
346 | | { |
347 | | as.store.dyn.len -= amt; |
348 | | } |
349 | | else |
350 | | { |
351 | | as.store.inl.len -= amt; |
352 | | } |
353 | | } |
354 | | |
355 | | constexpr void clear() noexcept |
356 | | { |
357 | | if (as.isDyn()) |
358 | | { |
359 | | as.dynLen(0); |
360 | | } |
361 | | else |
362 | | { |
363 | | as.inlLen(0); |
364 | | } |
365 | | } |
366 | | |
367 | | constexpr CharT* data() noexcept |
368 | | { |
369 | | return as.isDyn() ? as.store.dyn.ptr : as.store.inl.ptr; |
370 | | } |
371 | | |
372 | | constexpr const CharT* data() const noexcept |
373 | 1.39k | { |
374 | 1.39k | return as.isDyn() ? as.store.dyn.ptr : as.store.inl.ptr; |
375 | 1.39k | } |
376 | | |
377 | | constexpr CharT* begin() noexcept |
378 | | { |
379 | | return data(); |
380 | | } |
381 | | |
382 | | constexpr const CharT* begin() const noexcept |
383 | 1.39k | { |
384 | 1.39k | return data(); |
385 | 1.39k | } |
386 | | |
387 | | constexpr CharT* end() noexcept |
388 | | { |
389 | | return begin() + size(); |
390 | | } |
391 | | |
392 | | constexpr const CharT* end() const noexcept |
393 | | { |
394 | | return begin() + size(); |
395 | | } |
396 | | |
397 | | constexpr const CharT* cbegin() const noexcept |
398 | | { |
399 | | return begin(); |
400 | | } |
401 | | |
402 | | constexpr const CharT* cend() const noexcept |
403 | | { |
404 | | return end(); |
405 | | } |
406 | | |
407 | | constexpr bool operator==( |
408 | | std::basic_string_view<CharT> other) const noexcept |
409 | | { |
410 | | return std::basic_string_view<CharT>{*this} == other; |
411 | | } |
412 | | }; |
413 | | |
414 | | static_assert(sizeof(BasicStrBuf<char, 128>) == 128); |
415 | | static_assert(sizeof(BasicStrBuf<wchar_t, 256>) == 256); |
416 | | |
417 | | using StrBuf = BasicStrBuf<char>; |
418 | | using WStrBuf = BasicStrBuf<wchar_t>; |
419 | | |
420 | | } // namespace stdplus |