From e5acc1eaa7e342b64ccfaaef5ea502cd623e41d5 Mon Sep 17 00:00:00 2001 From: Steven Le Rouzic Date: Thu, 23 Jan 2025 00:17:27 +0100 Subject: Add string_builder --- asl/BUILD.bazel | 2 + asl/buffer.hpp | 31 ++++++++++--- asl/string.hpp | 7 +++ asl/string_builder.hpp | 89 ++++++++++++++++++++++++++++++++++++++ asl/tests/buffer_tests.cpp | 18 ++++++++ asl/tests/string_builder_tests.cpp | 23 ++++++++++ 6 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 asl/string_builder.hpp create mode 100644 asl/tests/string_builder_tests.cpp (limited to 'asl') diff --git a/asl/BUILD.bazel b/asl/BUILD.bazel index b5bb68f..2d82f4b 100644 --- a/asl/BUILD.bazel +++ b/asl/BUILD.bazel @@ -26,6 +26,7 @@ cc_library( "status.hpp", "status_or.hpp", "string.hpp", + "string_builder.hpp", "string_view.hpp", "utility.hpp", ], @@ -71,6 +72,7 @@ cc_library( "status", "status_or", "string", + "string_builder", "string_view", "utility", ]] diff --git a/asl/buffer.hpp b/asl/buffer.hpp index aea1cb6..b714e5a 100644 --- a/asl/buffer.hpp +++ b/asl/buffer.hpp @@ -88,11 +88,11 @@ private: constexpr T* push_uninit() { isize_t sz = size(); - resize_uninit(sz + 1); + resize_uninit_inner(sz + 1); return data() + sz; } - constexpr void resize_uninit(isize_t new_size) + constexpr void resize_uninit_inner(isize_t new_size) { isize_t old_size = size(); if (!trivially_destructible && new_size < old_size) @@ -142,7 +142,7 @@ private: { isize_t other_n = other.size(); isize_t this_n = size(); - resize_uninit(other_n); + resize_uninit_inner(other_n); if (other_n <= this_n) { relocate_assign_n(data(), other.data(), other_n); @@ -175,7 +175,7 @@ private: isize_t this_size = size(); isize_t new_size = to_copy.size(); - resize_uninit(to_copy.size()); + resize_uninit_inner(to_copy.size()); ASL_ASSERT(capacity() >= new_size); ASL_ASSERT(size() == to_copy.size()); @@ -197,8 +197,8 @@ private: ASL_ASSERT(new_size >= 0); isize_t old_size = size(); - resize_uninit(new_size); - + resize_uninit_inner(new_size); + T* data_ptr = data(); T* end = data_ptr + new_size; @@ -343,6 +343,25 @@ public: store_size_encoded(encode_size_heap(current_size)); } + constexpr void resize_uninit(isize_t new_size) + requires trivially_default_constructible && trivially_destructible + { + reserve_capacity(new_size); + set_size(new_size); + } + + constexpr void resize_zero(isize_t new_size) + requires trivially_default_constructible && trivially_destructible + { + isize_t old_size = size(); + resize_uninit(new_size); + + if (new_size > old_size) + { + memzero(data() + old_size, (new_size - old_size) * size_of); + } + } + void resize(isize_t new_size) requires default_constructible { diff --git a/asl/string.hpp b/asl/string.hpp index fbeffe5..4750cd5 100644 --- a/asl/string.hpp +++ b/asl/string.hpp @@ -11,6 +11,13 @@ class string { buffer m_buffer; + explicit constexpr string(buffer&& buffer) : + m_buffer{ASL_MOVE(buffer)} + {} + + template + friend class string_builder; + public: constexpr string() requires default_constructible = default; explicit constexpr string(Allocator allocator) : m_buffer{ASL_MOVE(allocator)} {} diff --git a/asl/string_builder.hpp b/asl/string_builder.hpp new file mode 100644 index 0000000..378ec48 --- /dev/null +++ b/asl/string_builder.hpp @@ -0,0 +1,89 @@ +#pragma once + +#include "asl/buffer.hpp" +#include "asl/string.hpp" +#include "asl/string_view.hpp" + +namespace asl +{ + +template +class string_builder +{ + buffer m_buffer; + +public: + constexpr string_builder() requires default_constructible = default; + explicit constexpr string_builder(Allocator allocator) : m_buffer{ASL_MOVE(allocator)} {} + + constexpr ~string_builder() = default; + + constexpr string_builder(const string_builder&) requires copy_constructible = default; + constexpr string_builder(string_builder&&) = default; + + constexpr string_builder& operator=(const string_builder&) requires copy_assignable = default; + constexpr string_builder& operator=(string_builder&&) = 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(); + } + + // @Todo(C++23) Deducing this + + string_builder& push(string_view sv) & + { + isize_t old_size = m_buffer.size(); + m_buffer.resize_zero(old_size + sv.size()); + asl::memcpy(m_buffer.data() + old_size, sv.data(), sv.size()); + return *this; + } + + string_builder&& push(string_view sv) && + { + isize_t old_size = m_buffer.size(); + m_buffer.resize_zero(old_size + sv.size()); + asl::memcpy(m_buffer.data() + old_size, sv.data(), sv.size()); + return ASL_MOVE(*this); + } + + string_builder& push(char c) & + { + m_buffer.push(c); + return *this; + } + + string_builder&& push(char c) && + { + m_buffer.push(c); + return ASL_MOVE(*this); + } + + string finish() && + { + return string{ASL_MOVE(m_buffer)}; + } + + template + string as_string() + requires default_constructible + { + return string{as_string_view()}; + } + + template + string as_string(Allocator allocator) + { + return string{as_string_view(), ASL_MOVE(allocator)}; + } +}; + +string_builder() -> string_builder<>; + +} // namespace asl diff --git a/asl/tests/buffer_tests.cpp b/asl/tests/buffer_tests.cpp index 20acad5..dfa0bcb 100644 --- a/asl/tests/buffer_tests.cpp +++ b/asl/tests/buffer_tests.cpp @@ -583,3 +583,21 @@ ASL_TEST(resize) ASL_TEST_EXPECT(buf[1] == 6); } +ASL_TEST(resize_zero) +{ + asl::buffer buf; + for (int i = 0; i < 100; ++i) + { + buf.push(i); + } + + buf.resize_zero(200); + ASL_TEST_ASSERT(buf.size() == 200); + + for (int i = 0; i < 100; ++i) + { + ASL_TEST_EXPECT(buf[i] == i); + ASL_TEST_EXPECT(buf[100 + i] == 0); + } +} + diff --git a/asl/tests/string_builder_tests.cpp b/asl/tests/string_builder_tests.cpp new file mode 100644 index 0000000..9bb25bd --- /dev/null +++ b/asl/tests/string_builder_tests.cpp @@ -0,0 +1,23 @@ +#include "asl/string_builder.hpp" +#include "asl/testing/testing.hpp" + +ASL_TEST(string_builder) +{ + asl::string_builder b; + b.push('a'); + b.push("bcdef"); + b.push('g'); + + ASL_TEST_EXPECT(b.as_string_view() == "abcdefg"); + + asl::string s = b.as_string(); + ASL_TEST_EXPECT(s == "abcdefg"); +} + +ASL_TEST(string_builder_rvalue) +{ + asl::string s = asl::string_builder{}.push('a').push("bcdef").push('g').finish(); + + ASL_TEST_EXPECT(s == "abcdefg"); +} + -- cgit